source: branches/uq/lang/tcl/src/RpCurses.c @ 5679

Last change on this file since 5679 was 5679, checked in by ldelgass, 9 years ago

Full merge 1.3 branch to uq branch to sync. Fixed partial subdirectory merge
by removing mergeinfo from lang/python/Rappture directory.

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