source: branches/r9/pkgs/system/curses.c @ 4919

Last change on this file since 4919 was 4916, checked in by gah, 9 years ago
File size: 9.7 KB
Line 
1/*
2 * ----------------------------------------------------------------------
3 *  RpCurses - interface to the ncurses library for Rappture apps
4 *
5 *  Provides access to curses functions, so that tty-based programs
6 *  can write out status info to a screen in the manner of "top" or
7 *  "sftp".
8 *
9 *  if {[Rappture::curses isatty]} {
10 *    Rappture::curses start
11 *    Rappture::curses puts ?-at y x? ?-bold? ?-clear? ?-dim? ?-reverse? <str>
12 *    Rappture::curses refresh
13 *    Rappture::curses stop
14 *  }
15 * 
16 * ======================================================================
17 *  AUTHOR:  Michael McLennan, Purdue University
18 *  Copyright (c) 2004-2012  HUBzero Foundation, LLC
19 *
20 *  See the file "license.terms" for information on usage and
21 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
22 * ======================================================================
23 */
24#include "config.h"
25#include "tcl.h"
26#include <string.h>
27#include <ncurses.h>
28#include <unistd.h>
29
30static CONST char *commandNames[] = {
31    "beep", "clear", "flash", "getch",
32    "isatty", "puts", "refresh", "screensize",
33    "start", "stop", (char*)NULL
34};
35
36enum command {
37    COMMAND_BEEP, COMMAND_CLEAR, COMMAND_FLASH, COMMAND_GETCH,
38    COMMAND_ISATTY, COMMAND_PUTS, COMMAND_REFRESH, COMMAND_SCREENSIZE,
39    COMMAND_START, COMMAND_STOP
40};
41
42/*
43 * Forward declarations
44 */
45static int              CursesObjCmd _ANSI_ARGS_((ClientData clientData,
46                            Tcl_Interp *interp, int objc,
47                            Tcl_Obj *CONST objv[]));
48
49/*
50 * ------------------------------------------------------------------------
51 *  RpCurses_Init --
52 *
53 *  Invoked when the Rappture GUI library is being initialized
54 *  to install the "curses" command into the interpreter.
55 *
56 *  Returns TCL_OK if successful, or TCL_ERROR (along with an error
57 *  message in the interp) if anything goes wrong.
58 * ------------------------------------------------------------------------
59 */
60int
61RpCurses_Init(interp)
62    Tcl_Interp *interp;         /* interpreter being initialized */
63{
64    /* install the command */
65    Tcl_CreateObjCommand(interp, "Rappture::curses", CursesObjCmd,
66        NULL, NULL);
67
68    return TCL_OK;
69}
70
71/*
72 * ----------------------------------------------------------------------
73 * CursesObjCmd()
74 *
75 * Called whenever the "curses" command is invoked to access ncurses
76 * library functions.
77 * ----------------------------------------------------------------------
78 */
79static int
80CursesObjCmd(clientData, interp, objc, objv)
81    ClientData clientData;        /* not used */
82    Tcl_Interp *interp;           /* current interpreter */
83    int objc;                     /* number of command arguments */
84    Tcl_Obj *CONST objv[];        /* command argument objects */
85{
86    int result = TCL_OK;
87    int pos, cmdToken;
88    char *opt;
89   
90    if (objc < 2) {
91        Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
92        return TCL_ERROR;
93    }
94
95    /*
96     * Look up the command option and map it to an enumerated value.
97     */
98    result = Tcl_GetIndexFromObj(interp, objv[1], commandNames,
99            "option", 0, &cmdToken);
100
101    if (result != TCL_OK) {
102        return result;
103    }
104
105    switch (cmdToken) {
106        case COMMAND_PUTS: {
107            int flags = 0;  /* attribute flags -- bold, dim, reverse, etc */
108            int clear = 0;  /* non-zero => clear to EOL before printing */
109            int y, x;
110
111            pos = 2;
112            while (pos < objc) {
113                opt = Tcl_GetStringFromObj(objv[pos], (int*)NULL);
114                if (*opt != '-') {
115                    break;
116                }
117                else if (strcmp(opt,"-at") == 0) {
118                    if (objc-pos < 2) {
119                        Tcl_WrongNumArgs(interp, 2, objv, "-at y x string");
120                        return TCL_ERROR;
121                    }
122                    if (Tcl_GetIntFromObj(interp, objv[pos+1], &y) != TCL_OK) {
123                        return TCL_ERROR;
124                    }
125                    if (Tcl_GetIntFromObj(interp, objv[pos+2], &x) != TCL_OK) {
126                        return TCL_ERROR;
127                    }
128                    move(y,x);
129                    pos += 3;
130                }
131                else if (strcmp(opt,"-bold") == 0) {
132                    flags |= A_BOLD;
133                    pos++;
134                }
135                else if (strcmp(opt,"-dim") == 0) {
136                    flags |= A_DIM;
137                    pos++;
138                }
139                else if (strcmp(opt,"-reverse") == 0) {
140                    flags |= A_REVERSE;
141                    pos++;
142                }
143                else if (strcmp(opt,"-underline") == 0) {
144                    flags |= A_UNDERLINE;
145                    pos++;
146                }
147                else if (strcmp(opt,"-clear") == 0) {
148                    clear = 1;
149                    pos++;
150                }
151                else if (strcmp(opt,"--") == 0) {
152                    pos++;
153                    break;
154                }
155                else {
156                    Tcl_AppendResult(interp, "bad option \"", opt,
157                        "\": should be -at, -bold, -clear, -dim, -reverse, -underline", (char*)NULL);
158                    return TCL_ERROR;
159                }
160            }
161            if (pos >= objc) {
162                Tcl_WrongNumArgs(interp, 2, objv, "?-at y x? ?-bold? ?-clear? ?-dim? ?-reverse? ?-underline? string");
163                return TCL_ERROR;
164            }
165
166            if (clear) clrtoeol();
167            if (flags != 0) attron(flags);
168            printw(Tcl_GetStringFromObj(objv[pos],(int*)NULL));
169            if (flags != 0) attroff(flags);
170            break;
171        }
172
173        case COMMAND_GETCH: {
174            int c;
175            char *opt, str[10];
176            int tdelay = 100;  /* time delay for blocking in ms */
177
178            pos = 2;
179            while (pos < objc) {
180                opt = Tcl_GetStringFromObj(objv[pos], (int*)NULL);
181                if (*opt != '-') {
182                    break;
183                }
184                else if (strcmp(opt,"-wait") == 0) {
185                    if (objc-pos < 1) {
186                        Tcl_WrongNumArgs(interp, 2, objv, "-wait ms");
187                        return TCL_ERROR;
188                    }
189                    opt = Tcl_GetStringFromObj(objv[pos+1], (int*)NULL);
190                    if (strcmp(opt,"forever") == 0) {
191                        tdelay = -1;
192                    }
193                    else if (Tcl_GetIntFromObj(interp, objv[pos+1], &tdelay) != TCL_OK) {
194                        return TCL_ERROR;
195                    }
196                    pos += 2;
197                }
198                else {
199                    Tcl_AppendResult(interp, "bad option \"", opt,
200                        "\": should be -wait", (char*)NULL);
201                    return TCL_ERROR;
202                }
203            }
204            if (pos < objc) {
205                Tcl_WrongNumArgs(interp, 2, objv, "?-wait ms?");
206            }
207
208            timeout(tdelay);
209            c = getch();
210
211            /*
212             * BLECH! Some systems don't use the proper termcap info, so
213             *   they don't map special keys the way they should.  Instead,
214             *   they send escape sequences for arrows, such as ^[A.
215             *   Look for this and map to the proper key value here.
216             */
217            if (c == 0x1b) {  /* escape key */
218                timeout(0);   /* look for the next key */
219                c = getch();
220                if (c == 'O' || c == '[') {
221                    c = getch();
222                    if (c == 'A') {
223                        c = KEY_UP;
224                    } else if (c == 'B') {
225                        c = KEY_DOWN;
226                    } else if (c == 'C') {
227                        c = KEY_LEFT;
228                    } else if (c == 'D') {
229                        c = KEY_RIGHT;
230                    } else {
231                        c = 0x1b;
232                    }
233                } else {
234                    c = 0x1b;
235                }
236            }
237
238            if (c == KEY_UP) {
239                Tcl_SetObjResult(interp, Tcl_NewStringObj("up",-1));
240            } else if (c == KEY_DOWN) {
241                Tcl_SetObjResult(interp, Tcl_NewStringObj("down",-1));
242            } else if (c == KEY_LEFT) {
243                Tcl_SetObjResult(interp, Tcl_NewStringObj("left",-1));
244            } else if (c == KEY_RIGHT) {
245                Tcl_SetObjResult(interp, Tcl_NewStringObj("right",-1));
246            } else if (c != ERR) {
247                str[0] = c;
248                str[1] = '\0';
249                Tcl_SetObjResult(interp, Tcl_NewStringObj(str,1));
250            }
251            break;
252        }
253
254        case COMMAND_ISATTY: {
255            Tcl_SetObjResult(interp, Tcl_NewBooleanObj(isatty(0) == 1));
256            break;
257        }
258
259        case COMMAND_CLEAR: {
260            clear();
261            break;
262        }
263
264        case COMMAND_REFRESH: {
265            refresh();
266            break;
267        }
268
269        case COMMAND_SCREENSIZE: {
270            int rows,cols;
271            Tcl_Obj *listPtr;
272
273            getmaxyx(stdscr,rows,cols);
274            listPtr = Tcl_NewListObj(0, (Tcl_Obj**)NULL);
275            Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewIntObj(rows));
276            Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewIntObj(cols));
277            Tcl_SetObjResult(interp, listPtr);
278            break;
279        }
280
281        case COMMAND_BEEP: {
282            beep();
283            break;
284        }
285
286        case COMMAND_FLASH: {
287            flash();
288            break;
289        }
290
291        case COMMAND_START: {
292            initscr();
293            raw();      /* can press keys without Enter to control things */
294            noecho();   /* don't see the control keypresses */
295            break;
296        }
297
298        case COMMAND_STOP: {
299            endwin();
300            break;
301        }
302    }
303    return result;
304}
Note: See TracBrowser for help on using the repository browser.