1 /* Curses terminal
2 
3    Copyright (c) 1997-2012 Free Software Foundation, Inc.
4 
5    This file is part of GNU Zile.
6 
7    GNU Zile is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3, or (at your option)
10    any later version.
11 
12    GNU Zile is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with GNU Zile; see the file COPYING.  If not, write to the
19    Free Software Foundation, Fifth Floor, 51 Franklin Street, Boston,
20    MA 02111-1301, USA.  */
21 
22 #include <config.h>
23 
24 #include <stdlib.h>
25 #if defined HAVE_NCURSESW_CURSES_H
26 #  include <ncursesw/curses.h>
27 #elif defined HAVE_NCURSESW_H
28 #  include <ncursesw.h>
29 #elif defined HAVE_NCURSES_CURSES_H
30 #  include <ncurses/curses.h>
31 #elif defined HAVE_NCURSES_H
32 #  include <ncurses.h>
33 #elif defined HAVE_CURSES_H
34 #  include <curses.h>
35 #else
36 #  error "SysV or X/Open-compatible Curses header file required"
37 #endif
38 #include <term.h>
39 #include "gl_array_list.h"
40 
41 #include "main.h"
42 #include "extern.h"
43 
44 static gl_list_t key_buf;
45 
46 static char backspace_code = 0177;
47 
48 size_t
term_buf_len(void)49 term_buf_len (void)
50 {
51   return gl_list_size (key_buf);
52 }
53 
54 void
term_move(size_t y,size_t x)55 term_move (size_t y, size_t x)
56 {
57   move ((int) y, (int) x);
58 }
59 
60 void
term_clrtoeol(void)61 term_clrtoeol (void)
62 {
63   clrtoeol ();
64 }
65 
66 void
term_refresh(void)67 term_refresh (void)
68 {
69   refresh ();
70 }
71 
72 void
term_clear(void)73 term_clear (void)
74 {
75   clear ();
76 }
77 
78 void
term_addch(char c)79 term_addch (char c)
80 {
81   addch (c);
82 }
83 
84 void
term_addstr(const char * s)85 term_addstr (const char *s)
86 {
87   addstr (s);
88 }
89 
90 void
term_attrset(size_t attr)91 term_attrset (size_t attr)
92 {
93   int attrs = 0;
94   if (attr & FONT_REVERSE)
95     attrs |= A_REVERSE;
96   if (attr & FONT_UNDERLINE)
97     attrs |= A_UNDERLINE;
98   attrset (attrs);
99 }
100 
101 void
term_beep(void)102 term_beep (void)
103 {
104   beep ();
105 }
106 
107 size_t
term_width(void)108 term_width (void)
109 {
110   return (size_t) COLS;
111 }
112 
113 size_t
term_height(void)114 term_height (void)
115 {
116   return (size_t) LINES;
117 }
118 
119 void
term_init(void)120 term_init (void)
121 {
122   initscr ();
123   noecho ();
124   nonl ();
125   raw ();
126   meta (stdscr, true);
127   intrflush (stdscr, false);
128   keypad (stdscr, true);
129   key_buf = gl_list_create_empty (GL_ARRAY_LIST, NULL, NULL, NULL, true);
130   char *kbs = tigetstr("kbs");
131   if (kbs && strlen (kbs) == 1)
132     backspace_code = *kbs;
133 }
134 
135 void
term_close(void)136 term_close (void)
137 {
138   /* Finish with ncurses. */
139   endwin ();
140 }
141 
142 static _GL_ATTRIBUTE_PURE size_t
codetokey(int c)143 codetokey (int c)
144 {
145   switch (c)
146     {
147     case '\0':			/* C-@ */
148       return KBD_CTRL | '@';
149     case '\1':
150     case '\2':
151     case '\3':
152     case '\4':
153     case '\5':
154     case '\6':
155     case '\7':
156     case '\10':
157     case '\12':
158     case '\13':
159     case '\14':
160     case '\16':
161     case '\17':
162     case '\20':
163     case '\21':
164     case '\22':
165     case '\23':
166     case '\24':
167     case '\25':
168     case '\26':
169     case '\27':
170     case '\30':
171     case '\31':
172     case '\32':			/* C-a ... C-z */
173       return KBD_CTRL | ('a' + c - 1);
174     case '\11':
175       return KBD_TAB;
176     case '\15':
177       return KBD_RET;
178     case '\37':
179       return KBD_CTRL | '_';
180 #ifdef KEY_SUSPEND
181     case KEY_SUSPEND:		/* C-z */
182       return KBD_CTRL | 'z';
183 #endif
184     case '\33':			/* META */
185       return KBD_META;
186     case KEY_PPAGE:		/* PGUP */
187       return KBD_PGUP;
188     case KEY_NPAGE:		/* PGDN */
189       return KBD_PGDN;
190     case KEY_HOME:
191       return KBD_HOME;
192     case KEY_END:
193       return KBD_END;
194     case KEY_DC:		/* DEL */
195       return KBD_DEL;
196     case KEY_BACKSPACE:		/* Backspace or Ctrl-H */
197       return codetokey (backspace_code);
198     case 0177:			/* BS */
199       return KBD_BS;
200     case KEY_IC:		/* INSERT */
201       return KBD_INS;
202     case KEY_LEFT:
203       return KBD_LEFT;
204     case KEY_RIGHT:
205       return KBD_RIGHT;
206     case KEY_UP:
207       return KBD_UP;
208     case KEY_DOWN:
209       return KBD_DOWN;
210     case KEY_F (1):
211       return KBD_F1;
212     case KEY_F (2):
213       return KBD_F2;
214     case KEY_F (3):
215       return KBD_F3;
216     case KEY_F (4):
217       return KBD_F4;
218     case KEY_F (5):
219       return KBD_F5;
220     case KEY_F (6):
221       return KBD_F6;
222     case KEY_F (7):
223       return KBD_F7;
224     case KEY_F (8):
225       return KBD_F8;
226     case KEY_F (9):
227       return KBD_F9;
228     case KEY_F (10):
229       return KBD_F10;
230     case KEY_F (11):
231       return KBD_F11;
232     case KEY_F (12):
233       return KBD_F12;
234     default:
235       if (c > 0xff || c < 0)
236         return KBD_NOKEY;	/* ERR (no key) or undefined behaviour. */
237       return c;
238     }
239 }
240 
241 static size_t
keytocodes(size_t key,int ** codevec)242 keytocodes (size_t key, int ** codevec)
243 {
244   if (key == KBD_NOKEY)
245     return 0;
246 
247   int *p = *codevec = XCALLOC (2, int);
248 
249   if (key & KBD_META)				/* META */
250     *p++ = '\33';
251   key &= ~KBD_META;
252 
253   switch (key)
254     {
255     case KBD_CTRL | '@':			/* C-@ */
256       *p++ = '\0';
257       break;
258     case KBD_CTRL | 'a':
259     case KBD_CTRL | 'b':
260     case KBD_CTRL | 'c':
261     case KBD_CTRL | 'd':
262     case KBD_CTRL | 'e':
263     case KBD_CTRL | 'f':
264     case KBD_CTRL | 'g':
265     case KBD_CTRL | 'h':
266     case KBD_CTRL | 'j':
267     case KBD_CTRL | 'k':
268     case KBD_CTRL | 'l':
269     case KBD_CTRL | 'n':
270     case KBD_CTRL | 'o':
271     case KBD_CTRL | 'p':
272     case KBD_CTRL | 'q':
273     case KBD_CTRL | 'r':
274     case KBD_CTRL | 's':
275     case KBD_CTRL | 't':
276     case KBD_CTRL | 'u':
277     case KBD_CTRL | 'v':
278     case KBD_CTRL | 'w':
279     case KBD_CTRL | 'x':
280     case KBD_CTRL | 'y':
281     case KBD_CTRL | 'z':	/* C-a ... C-z */
282       *p++ = (key & ~KBD_CTRL) + 1 - 'a';
283       break;
284     case KBD_TAB:
285       *p++ = '\11';
286       break;
287     case KBD_RET:
288       *p++ = '\15';
289       break;
290     case KBD_CTRL | '_':
291       *p++ = '\37';
292       break;
293     case KBD_PGUP:		/* PGUP */
294       *p++ = KEY_PPAGE;
295       break;
296     case KBD_PGDN:		/* PGDN */
297       *p++ = KEY_NPAGE;
298       break;
299     case KBD_HOME:
300       *p++ = KEY_HOME;
301       break;
302     case KBD_END:
303       *p++ = KEY_END;
304       break;
305     case KBD_DEL:		/* DEL */
306       *p++ = KEY_DC;
307       break;
308     case KBD_BS:		/* BS */
309       *p++ = 0177;
310       break;
311     case KBD_INS:		/* INSERT */
312       *p++ = KEY_IC;
313       break;
314     case KBD_LEFT:
315       *p++ = KEY_LEFT;
316       break;
317     case KBD_RIGHT:
318       *p++ = KEY_RIGHT;
319       break;
320     case KBD_UP:
321       *p++ = KEY_UP;
322       break;
323     case KBD_DOWN:
324       *p++ = KEY_DOWN;
325       break;
326     case KBD_F1:
327       *p++ = KEY_F (1);
328       break;
329     case KBD_F2:
330       *p++ = KEY_F (2);
331       break;
332     case KBD_F3:
333       *p++ = KEY_F (3);
334       break;
335     case KBD_F4:
336       *p++ = KEY_F (4);
337       break;
338     case KBD_F5:
339       *p++ = KEY_F (5);
340       break;
341     case KBD_F6:
342       *p++ = KEY_F (6);
343       break;
344     case KBD_F7:
345       *p++ = KEY_F (7);
346       break;
347     case KBD_F8:
348       *p++ = KEY_F (8);
349       break;
350     case KBD_F9:
351       *p++ = KEY_F (9);
352       break;
353     case KBD_F10:
354       *p++ = KEY_F (10);
355       break;
356     case KBD_F11:
357       *p++ = KEY_F (11);
358       break;
359     case KBD_F12:
360       *p++ = KEY_F (12);
361       break;
362     default:
363       if ((key & 0xff) == key)
364         *p++ = key;
365       break;
366     }
367 
368   return p - *codevec;
369 }
370 
371 static int
get_char(int delay)372 get_char (int delay)
373 {
374   int c;
375 
376   size_t size = term_buf_len ();
377   if (size > 0)
378     {
379       c = (ptrdiff_t) gl_list_get_at (key_buf, size - 1);
380       gl_list_remove_at (key_buf, size - 1);
381     }
382   else
383     {
384       timeout (delay);
385 
386 #ifdef KEY_RESIZE
387       do {
388 #endif
389         c = getch ();
390 
391 #ifdef KEY_RESIZE
392         if (c == KEY_RESIZE)
393           resize_windows ();
394       } while (c == KEY_RESIZE);
395 #endif
396     }
397 
398   return c;
399 }
400 
401 size_t
term_getkey(int delay)402 term_getkey (int delay)
403 {
404   size_t key = codetokey (get_char (delay));
405   while (key == KBD_META)
406     key = codetokey (get_char (GETKEY_DEFAULT)) | KBD_META;
407   return key;
408 }
409 
410 int
term_getkey_unfiltered(int delay)411 term_getkey_unfiltered (int delay)
412 {
413   keypad (stdscr, false);
414   int key = get_char (delay);
415   keypad (stdscr, true);
416   return key;
417 }
418 
419 void
term_ungetkey(size_t key)420 term_ungetkey (size_t key)
421 {
422   int * codes = NULL;
423   for (size_t i = keytocodes (key, &codes); i > 0; i--)
424     gl_list_add_last (key_buf, (void *)(ptrdiff_t) codes[i - 1]);
425 }
426