1 /*
2 Copyright (c) 2003 Bruno T. C. de Oliveira
3 
4 LICENSE INFORMATION:
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 General Public License for more details.
14 
15 You should have received a copy of the GNU General Public
16 License along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 Copyright (c) 2002 Bruno T. C. de Oliveira
19 
20 INFORMA��ES DE LICEN�A:
21 Este programa � um software de livre distribui��o; voc� pode
22 redistribu�-lo e/ou modific�-lo sob os termos da GNU General
23 Public License, conforme publicado pela Free Software Foundation,
24 pela vers�o 2 da licen�a ou qualquer vers�o posterior.
25 
26 Este programa � distribu�do na esperan�a de que ele ser� �til
27 aos seus usu�rios, por�m, SEM QUAISQUER GARANTIAS; sem sequer
28 a garantia impl�cita de COMERCIABILIDADE ou DE ADEQUA��O A
29 QUALQUER FINALIDADE ESPEC�FICA. Consulte a GNU General Public
30 License para obter mais detalhes (uma c�pia acompanha este
31 programa, armazenada no arquivo COPYING).
32 */
33 
34 #include "kurses.h"
35 #include "util.h"
36 #include <ncurses.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 
41 /* mapping between color codes and curses pairs and attributes:
42  *
43  * color pair depends on the three least significant bits of the foreground
44  * and background. If a pair is (fg, bg),
45  *      let FG = fg % 8 (i.e. the three least significant bits)
46  *      let BG = bg % 8
47  *
48  * the curses pair for it will be BG * 8 + (7 - FG)
49  *
50  * Valid curses pairs are 1..63, but the color (7, 0) maps to pair number 0.
51  * This is no problem, because (7, 0) is the default color so it needs
52  * no curses color pair.
53  *
54  * After the color pair is selected, the most significant bit of
55  * fg and bg are used to activate A_BOLD and A_BLINK attributes.
56  */
57 
58 /* --------- internal data and declarations of private functions ---------- */
59 
60 /* current foreground and background colors */
61 static int cur_fg, cur_bg;
62 
63 /* Given a pair of color codes, returns the corresponding curses pair */
64 static int kurses_pair_for(int fg, int bg);
65 
66 /* node type for the saved screen linked list (see below) */
67 struct SavedScreenNode_ {
68    WINDOW *win;  /* the curses window that holds the screenshot */
69    struct SavedScreenNode_* next;
70 };
71 typedef struct SavedScreenNode_ SavedScreenNode;
72 
73 /* Stack of saved screens, implemented as a linked list */
74 SavedScreenNode *sss_top; /* sss = 'saved screen stack' */
75 
76 /* -------------- implementation of public functions ---------------------- */
77 
kurses_init()78 void kurses_init() {
79    int fg, bg;
80 
81    initscr();               /* enter screen-oriented mode */
82    raw();                   /* don't treat Ctrl+C, etc. as special. */
83    noecho();                /* don't echo typed characters back to terminal */
84    keypad(stdscr, TRUE);    /* convert ugly escape sequences to more
85                              * palatable curses KEY_* codes */
86 
87    cur_fg = 7;
88    cur_bg = 0;
89    sss_top = NULL;
90 
91    if (!has_colors()) {
92       fprintf(stderr, "*** Fatal error: Terminal has no color support.\n");
93       exit(1);
94    }
95 
96    start_color();
97 
98    /* initialize all curses pairs */
99    for (fg = 0; fg < 8; fg++) for (bg = 0; bg < 8; bg++) {
100       int pair = kurses_pair_for(fg, bg);
101       if (!pair) continue; /* don't try to initialize pair 0,
102                             * that corresponds to color (7, 0). */
103 
104       init_pair(pair, fg, bg);
105    }
106 
107    clear();
108    refresh();
109 }
110 
kurses_finalize()111 void kurses_finalize() {
112    clear();
113    refresh();
114    endwin();
115 }
116 
kurses_color(int fg,int bg)117 void kurses_color(int fg, int bg) {
118    int pair;
119 
120    if (fg < 0) fg = cur_fg;
121    if (bg < 0) bg = cur_bg;
122 
123    /* see what color pair corresponds to fg, bg */
124    pair = kurses_pair_for(fg, bg);
125 
126    /* if that maps to 0, just reset attribute; otherwise set color pair */
127    if (pair) attrset(COLOR_PAIR(pair));
128    else      attrset(A_NORMAL);
129 
130    /* now turn on A_BOLD and A_BLINK attribute as indicated by the most
131     * significant bit of fg and bg, respectively */
132    if (fg & 0x08) attron(A_BOLD);
133    if (bg & 0x08) attron(A_BLINK);
134 }
135 
136 /* Same as kurses_color(attr >> 4, attr & 0x0F) */
kurses_color_at(int attr)137 void kurses_color_at(int attr) {
138    kurses_color(attr >> 4, attr & 0x0F);
139 }
140 
kurses_width()141 int kurses_width() {
142    int maxy, maxx;
143    getmaxyx(stdscr, maxy, maxx);
144    return maxx;
145 }
146 
kurses_height()147 int kurses_height() {
148    int maxy, maxx;
149    getmaxyx(stdscr, maxy, maxx);
150    return maxy;
151 }
152 
kurses_pos_valid(int x,int y)153 int kurses_pos_valid(int x, int y) {
154    return (x >= 0 && x < kurses_width() &&
155            y >= 0 && y < kurses_height());
156 }
157 
kurses_move(int x,int y)158 int kurses_move(int x, int y) {
159    if (!kurses_pos_valid(x, y)) return 0;
160    move(y, x);
161    return 1;
162 }
163 
kurses_line_input(char * buf,int buf_size)164 int kurses_line_input(char *buf, int buf_size) {
165    return kurses_line_input_ex(buf, buf_size, NULL, 0);
166 }
167 
kurses_line_input_ex(char * buf,int buf_size,int * skeys,int flags)168 int kurses_line_input_ex(char *buf, int buf_size, int *skeys, int flags) {
169    int pos = 0;
170    int ch;
171    const int *p;
172 
173    while (1) {
174       refresh();
175       ch = getch();
176       if (ch < 0) continue;
177 
178       /* first check if character is in skeys */
179       if ( (p = skeys) ) while (*p) if (*(p++) == ch) return -ch;
180 
181       /* proceed to normal handling */
182       switch (ch) {
183          case 10: buf[pos] = 0; return 1; /* confirm input */
184          case 3:
185          case 7:
186          case 27: *buf = 0; return 0; /* cancel input */
187          case KEY_BACKSPACE: case 8: case 127: /* backspace */
188             if (pos > 0) { --pos; addch('\b'); addch(' '); addch('\b'); }
189             break;
190          case 'U' - 0x40: /* kill */
191             while (pos--) { addch('\b'); addch(' '); addch('\b'); }
192             pos = 0;
193             break;
194          default:
195             if (pos <= buf_size - 2 && printable_char(ch)) {
196                /* buffer not full (remember to reserve a byte for the NULL),
197                 * and character is not a control character: append it */
198                buf[pos++] = ch;
199                addch((flags & KURSES_LI_HIDE) ? '*' : ch);
200             }
201       }
202    }
203 }
204 
draw_window(int x0,int y0,int w,int h,const char * title)205 void draw_window(int x0, int y0, int w, int h, const char *title) {
206    int x, y, x1, y1;
207    int gr_ch[3][3];
208 
209    gr_ch[0][0]=ACS_ULCORNER, gr_ch[0][1]=ACS_HLINE, gr_ch[0][2]=ACS_URCORNER;
210    gr_ch[1][0]=ACS_VLINE,    gr_ch[1][1]=' ',       gr_ch[1][2]=ACS_VLINE;
211    gr_ch[2][0]=ACS_LLCORNER, gr_ch[2][1]=ACS_HLINE, gr_ch[2][2]=ACS_LRCORNER;
212 
213    x1 = x0 + w - 1;
214    y1 = y0 + h - 1;
215 
216    for (y = y0; y < y0 + h; y++)
217       for (x = x0; x < x0 + w; x++)
218          if (!kurses_move(x, y)) continue;
219          else addch(gr_ch [(y == y0) ? 0 : (y == y1) ? 2 : 1]
220                           [(x == x0) ? 0 : (x == x1) ? 2 : 1]);
221 
222    if (title) if (kurses_move(x0 + w/2 - strlen(title) / 2, y0)) addstr(title);
223    kurses_move(x0 + 1, y0 + 1);
224 }
225 
draw_centered_window(int w,int h,const char * title,int * x,int * y)226 void draw_centered_window(int w, int h, const char *title, int *x, int *y) {
227    int x0 = (kurses_width() - w) / 2;
228    int y0 = (kurses_height() - h) / 2;
229    if (x) *x = x0 + 1;
230    if (y) *y = y0 + 1;
231 
232    draw_window(x0, y0, w, h, title);
233 }
234 
draw_hline(int x0,int y0,int w,int left_endpt,int interim,int right_endpt)235 void draw_hline(int x0, int y0, int w,
236                 int left_endpt, int interim, int right_endpt)
237 {
238    int x;
239    if (!kurses_move(x0, y0)) return;
240    for (x = x0; x < x0 + w; x++)
241       addch( (x == x0) ? left_endpt :
242              (x == x0 + w - 1) ? right_endpt : interim);
243 }
244 
draw_vline(int x0,int y0,int h,int upper_endpt,int interim,int lower_endpt)245 void draw_vline(int x0, int y0, int h,
246                 int upper_endpt, int interim, int lower_endpt) {
247    int y;
248    if (!kurses_move(x0, y0)) return;
249    for (y = y0; y < y0 + h; y++) {
250       kurses_move(x0, y);
251       addch( (y == y0) ? upper_endpt :
252              (y == y0 + h - 1) ? lower_endpt : interim);
253    }
254 }
255 
push_screen(void)256 void push_screen(void) {
257    SavedScreenNode *n = zalloc(sizeof(SavedScreenNode));
258    n->win = newwin(0,0,0,0);
259    overwrite(stdscr, n->win);
260    n->next = sss_top;
261 
262    sss_top = n;
263 }
264 
restore_screen(void)265 void restore_screen(void) {
266    if (!sss_top) {
267       /* if there are no saved screens, just clear the screen and return.
268        * Doesn't make much sense, but beats doing nothing or segfaulting. */
269       erase();
270       return;
271    }
272 
273    overwrite(sss_top->win, stdscr);
274    touchwin(stdscr);
275 }
276 
pop_screen(void)277 void pop_screen(void) {
278    SavedScreenNode *newtop;
279    if (!sss_top) return;
280 
281    newtop = sss_top->next;
282    free(sss_top);
283    sss_top = newtop;
284 }
285 
286 /* -------------- implementation of private functions --------------------- */
kurses_pair_for(int fg,int bg)287 static int kurses_pair_for(int fg, int bg) {
288    fg &= 0x07; bg &= 0x07;   /* only keep the three least significant bits */
289    return (bg << 3) + (7 - fg);
290 }
291 
292 
293 
294