Initial revision
[apps/madtty.git] / rote.h
1 /* ROTE - Our Own Terminal Emulation library 
2  * Copyright (c) 2004 Bruno T. C. de Oliveira
3  * All rights reserved
4  *
5  * 2004-08-25
6  */
7
8 #ifndef btco_ROTE_rote_h
9 #define btco_ROTE_rote_h
10
11 #include <ncurses.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15
16 /* Color codes: 0 = black, 1 = red, 2 = green, 3 = yellow, 4 = blue,
17  *              5 = magenta, 6 = cyan, 7 = white. 
18  *
19  * An 'attribute' as used in this library means an 8-bit value that conveys
20  * a foreground color code, a background color code, and the bold
21  * and blink bits. Each cell in the virtual terminal screen is associated
22  * with an attribute that specifies its appearance. The bits of an attribute,
23  * from most significant to least significant, are 
24  *
25  *  bit:      7 6 5 4 3 2 1 0
26  *  content:  S F F F H B B B
27  *            | `-,-' | `-,-'
28  *            |   |   |   |
29  *            |   |   |   `----- 3-bit background color code (0 - 7)
30  *            |   |   `--------- blink bit (if on, text is blinking)
31  *            |   `------------- 3-bit foreground color code (0 - 7)
32  *            `----------------- bold bit
33  *
34  * It is however recommended that you use the provided macros rather
35  * than dealing with this format directly.
36  *
37  * Sometimes we will call the 'SFFF' nibble above the 'extended
38  * foreground color code', and the 'HBBB' nibble the 'extended background
39  * color code'. So the extended color codes are just the regular
40  * color codes except that they have an additional bit (the most significant
41  * bit) indicating bold/blink.
42  */
43
44 /* retrieve attribute fields */
45 #define ROTE_ATTR_BG(attr)              ((attr) & 0x07)
46 #define ROTE_ATTR_FG(attr)              (((attr) & 0x70) >> 4)
47
48 /* retrieve 'extended' color codes (see above for info) */
49 #define ROTE_ATTR_XBG(attr)             ((attr) & 0x0F)
50 #define ROTE_ATTR_XFG(attr)             (((attr) & 0xF0) >> 4)
51
52 /* set attribute fields. This requires attr to be an lvalue, and it will
53  * be evaluated more than once. Use with care. */
54 #define ROTE_ATTR_MOD_BG(attr, newbg)    attr &= 0xF8, attr |= (newbg)
55 #define ROTE_ATTR_MOD_FG(attr, newfg)    attr &= 0x8F, attr |= ((newfg) << 4)
56 #define ROTE_ATTR_MOD_XBG(attr, newxbg)  attr &= 0xF0, attr |= (newxbg)
57 #define ROTE_ATTR_MOD_XFG(attr, newxfg)  attr &= 0x0F, attr |= ((newxfg) << 4)
58 #define ROTE_ATTR_MOD_BOLD(attr, boldbit) \
59                                attr &= 0x7F, attr |= (boldbit)?0x80:0x00
60 #define ROTE_ATTR_MOD_BLINK(attr, blinkbit) \
61                                attr &= 0xF7, attr |= (boldbit)?0x08:0x00
62
63 /* these return non-zero for 'yes', zero for 'no'. Don't rely on them being 
64  * any more specific than that (e.g. being exactly 1 for 'yes' or whatever). */
65 #define ROTE_ATTR_BOLD(attr)            ((attr) & 0x80)
66 #define ROTE_ATTR_BLINK(attr)           ((attr) & 0x08)
67
68 /* Represents each of the text cells in the terminal screen */
69 typedef struct RoteCell_ {
70    unsigned char ch;    /* >= 32, that is, control characters are not
71                          * allowed to be on the virtual screen */
72
73    unsigned char attr;  /* a color attribute, as described previously */
74 } RoteCell;
75
76 /* Declaration of opaque rote_Term_Private structure */
77 typedef struct RoteTermPrivate_ RoteTermPrivate;
78
79 /* Represents a virtual terminal. You may directly access the fields
80  * of this structure, but please pay close attention to the fields
81  * marked read-only or with special usage notes. */
82 typedef struct RoteTerm_ {
83    int rows, cols;              /* terminal dimensions, READ-ONLY. You
84                                  * can't resize the terminal by changing
85                                  * this (a segfault is about all you will 
86                                  * accomplish). */
87
88    RoteCell **cells;            /* matrix of cells. This
89                                  * matrix is indexed as cell[row][column]
90                                  * where 0 <= row < rows and
91                                  *       0 <= col < cols
92                                  *
93                                  * You may freely modify the contents of
94                                  * the cells.
95                                  */
96
97    int crow, ccol;              /* cursor coordinates. READ-ONLY. */
98
99    unsigned char curattr;       /* current attribute, that is the attribute
100                                  * that will be used for newly inserted
101                                  * characters */
102
103    pid_t childpid;              /* pid of the child process running in the
104                                  * terminal; 0 for none. This is READ-ONLY. */
105
106    RoteTermPrivate *pd;         /* private state data */
107
108    /* --- dirtiness flags: the following flags will be raised when the
109     * corresponding items are modified. They can only be unset by YOU
110     * (when, for example, you redraw the term or something) --- */
111    bool curpos_dirty;           /* whether cursor location has changed */
112    bool *line_dirty;            /* whether each row is dirty  */
113    /* --- end dirtiness flags */
114 } RoteTerm;
115
116 /* Creates a new virtual terminal with the given dimensions. You
117  * must destroy it with rote_vt_destroy after you are done with it.
118  * The terminal will be initially blank and the cursor will
119  * be at the top-left corner. 
120  *
121  * Returns NULL on error.
122  */
123 RoteTerm *rote_vt_create(int rows, int cols);
124
125 /* Destroys a virtual terminal previously created with
126  * rote_vt_create. If rt == NULL, does nothing. */
127 void rote_vt_destroy(RoteTerm *rt);
128
129 /* Starts a forked process in the terminal. The <command> parameter
130  * is a shell command to execute (it will be interpreted by '/bin/sh -c') 
131  * Returns the pid of the forked process. 
132  *
133  * Some useful reminders: If you want to be notified when child processes exit,
134  * you should handle the SIGCHLD signal.  If, on the other hand, you want to
135  * ignore exitting child processes, you should set the SIGCHLD handler to
136  * SIG_IGN to prevent child processes from hanging around the system as 'zombie
137  * processes'. 
138  *
139  * Continuing to write to a RoteTerm whose child process has died does not
140  * accomplish a lot, but is not an error and should not cause your program
141  * to crash or block indefinitely or anything of that sort :-)
142  * If, however, you want to be tidy and inform the RoteTerm that its
143  * child has died, call rote_vt_forsake_child when appropriate.
144  *
145  * If there is an error, returns -1. Notice that passing an invalid
146  * command will not cause an error at this level: the shell will try
147  * to execute the command and will exit with status 127. You can catch
148  * that by installing a SIGCHLD handler if you want.
149  */
150 pid_t rote_vt_forkpty(RoteTerm *rt, const char *command);
151
152 /* Disconnects the RoteTerm from its forked child process. This function
153  * should be called when the child process dies or something of the sort.
154  * It is not strictly necessary to call this function, but it is
155  * certainly tidy. */
156 void rote_vt_forsake_child(RoteTerm *rt);
157
158 /* Does some data plumbing, that is, sees if the sub process has
159  * something to write to the terminal, and if so, write it. If you
160  * called rote_vt_fork to start a forked process, you must call
161  * this function regularly to update the terminal. 
162  *
163  * This function will not block, that is, if there is no data to be
164  * read from the child process it will return immediately. */
165 void rote_vt_update(RoteTerm *rt);
166
167 /* Puts data into the terminal: if there is a forked process running,
168  * the data will be sent to it. If there is no forked process,
169  * the data will simply be injected into the terminal (as in
170  * rote_vt_inject) */
171 void rote_vt_write(RoteTerm *rt, const char *data, int length);
172
173 /* Inject data into the terminal. <data> needs NOT be 0-terminated:
174  * its length is solely determined by the <length> parameter. Please
175  * notice that this writes directly to the terminal, that is,
176  * this function does NOT send the data to the forked process
177  * running in the terminal (if any). For that, you might want
178  * to use rote_vt_write.
179  */
180 void rote_vt_inject(RoteTerm *rt, const char *data, int length);
181
182 /* Paints the virtual terminal screen on the given window, putting
183  * the top-left corner at the given position. The cur_set_attr
184  * function must set the curses attributes given a Rote attribute
185  * byte. It should, for example, do wattrset(win, COLOR_PAIR(n)) where
186  * n is the colorpair appropriate for the attribute and such.
187  *
188  * If you pass NULL for cur_set_attr, the default implementation will
189  * set the color pair given by (bg * 8 + 7 - fg), which seems to be
190  * a common mapping, and the bold and blink attributes will be mapped 
191  * to A_BOLD and A_BLINK.
192  *
193  * At the end of the function, the cursor will be left where the virtual 
194  * cursor of the terminal is supposed to be.
195  *
196  * This function does not call wrefresh(win); you have to do that yourself.
197  * This function automatically calls rote_vt_update prior to drawing
198  * so that the drawn contents are accurate.
199  */
200 void rote_vt_draw(RoteTerm *rt, WINDOW *win, int startrow, int startcol,
201                   void (*cur_set_attr)(WINDOW *win, unsigned char attr));
202
203 /* Indicates to the terminal that the given key has been pressed.
204  * This will cause the terminal to rote_vt_write() the appropriate
205  * escape sequence for that key (that is, the escape sequence
206  * that the linux text-mode console would produce for it). The argument,
207  * keycode, must be a CURSES EXTENDED KEYCODE, the ones you get
208  * when you use keypad(somewin, TRUE) (see man page). */
209 void rote_vt_keypress(RoteTerm *rt, int keycode);
210
211 /* Takes a snapshot of the current contents of the terminal and
212  * saves them to a dynamically allocated buffer. Returns a pointer
213  * to the newly created buffer, which you can pass to
214  * rote_vt_restore_snapshot. Caller is responsible for free()'ing when
215  * the snapshot is no longer needed. */
216 void *rote_vt_take_snapshot(RoteTerm *rt);
217
218 /* Restores a snapshot previously taken with rote_vt_take_snapshot.
219  * This function does NOT free() the passed buffer */
220 void rote_vt_restore_snapshot(RoteTerm *rt, void *snapbuf);
221
222 /* Declaration of custom escape sequence callback type. See the
223  * rote_vt_add_es_handler function for more info */
224 typedef int (*rote_es_handler_t)(RoteTerm *rt, const char *es);
225
226 /* Installs a custom escape sequence handler for the given RoteTerm.
227  * The handler will be called by the library every time it tries to
228  * recognize an escape sequence; depending on the return value of the
229  * handler, it will proceed in a different manner. See the description
230  * of the possible return values (ROTE_HANDLERESULT_* constants) below
231  * for more info.
232  *
233  * This handler will be called EACH TIME THE ESCAPE SEQUENCE BUFFER
234  * RECEIVES A CHARACTER. Therefore, it must execute speedily in order
235  * not to create too heavy a performance penalty. In particular, the
236  * writer of the handler should take care to quickly test for invalid
237  * or incomplete escape sequences before trying to do more elaborate
238  * parsing.
239  *
240  * The handler will NOT be called with an empty escape sequence (i.e.
241  * one in which only the initial ESC was received).
242  *
243  * The custom handler receives the terminal it pertains to and the
244  * escape sequence as a string (without the initial escape character).
245  *
246  * The handler may of course modify the terminal as it sees fit, taking 
247  * care not to corrupt it of course (in particular, it should appropriately 
248  * raise the line_dirty[] and curpos_dirty flags to indicate what it has 
249  * changed).
250  */
251 void rote_vt_install_handler(RoteTerm *rt, rote_es_handler_t handler);
252                             
253 /* Possible return values for the custom handler function and their
254  * meanings: */
255 #define ROTE_HANDLERESULT_OK 0      /* means escape sequence was handled */
256
257 #define ROTE_HANDLERESULT_NOTYET 1  /* means the escape sequence was not
258                                      * recognized yet, but there is hope that
259                                      * it still will once more characters
260                                      * arrive (i.e. it is not yet complete).
261                                      *
262                                      * The library will thus continue collecting
263                                      * characters and calling the handler as
264                                      * each character arrives until
265                                      * either OK or NOWAY is returned.
266                                      */
267
268 #define ROTE_HANDLERESULT_NOWAY 2   /* means the escape sequence was not
269                                      * recognized, and there is no chance
270                                      * that it will even if more characters
271                                      * are added to it. */
272
273 #endif
274