- fixes to _wputch to get positioning and wrap working properly
[gpxe.git] / src / core / curses.c
1 #include <curses.h>
2 #include <malloc.h>
3 #include <stddef.h>
4 #include <vsprintf.h>
5
6 /** @file
7  *
8  * MuCurses: lightweight xcurses implementation for PXE ROMs
9  *
10  */
11
12 #define WRAP 0
13 #define NOWRAP 1
14
15 unsigned short _COLS;
16 unsigned short _LINES;
17 unsigned short _COLOURS;
18 unsigned int *_COLOUR_PAIRS; /* basically this is an array, but as its
19                                length is determined only when initscr
20                                is run, I can only think to make it a
21                                pointer and malloc the array into being
22                                ... */
23
24 struct cursor_pos {
25         unsigned int y, x;
26 };
27
28 WINDOW _stdscr = {
29         .attrs = A_DEFAULT,
30         .ori_y = 0,
31         .ori_x = 0,
32         .curs_y = 0,
33         .curs_x = 0,
34         .scr = curscr,
35 };
36
37 /*
38  *  Primitives
39  */
40
41 /**
42  * Write a single character rendition to a window
43  *
44  * @v *win      window in which to write
45  * @v ch        character rendition to write
46  * @v wrap      wrap "switch"
47  */
48 static void _wputch ( WINDOW *win, chtype ch, int wrap ) {
49         /* make sure we set the screen cursor to the right position
50            first! */
51         win->scr->movetoyx( win->scr, win->ori_y + win->curs_y,
52                                       win->ori_x + win->curs_x );
53         win->scr->putc(win->scr, ch);
54         if ( ++(win->curs_x) == win->width ) {
55                 if ( wrap == WRAP ) {
56                         win->curs_x = 0;
57                         /* specification says we should really scroll,
58                            but we have no buffer to scroll with, so we
59                            can only overwrite back at the beginning of
60                            the window */
61                         if ( ++(win->curs_y) == win->height )
62                                 win->curs_y = 0;
63                 } else {
64                         (win->curs_x)--;
65                 }
66         }
67 }
68
69 /**
70  * Write a chtype string to a window
71  *
72  * @v *win      window in which to write
73  * @v *chstr    chtype string
74  * @v wrap      wrap "switch"
75  * @v n         write at most n chtypes
76  */
77 static void _wputchstr ( WINDOW *win, const chtype *chstr, int wrap, int n ) {
78         for ( ; *chstr && n-- ; chstr++ ) {
79                 _wputch(win,*chstr,wrap);
80         }
81 }
82
83 /**
84  * Write a standard c-style string to a window
85  *
86  * @v *win      window in which to write
87  * @v *str      string
88  * @v wrap      wrap "switch"
89  * @v n         write at most n chars from *str
90  */
91 static void _wputstr ( WINDOW *win, const char *str, int wrap, int n ) {
92         for ( ; *str && n-- ; str++ ) {
93                 _wputch( win, *str | win->attrs, wrap );
94         }
95 }
96
97 /**
98  * Restore cursor position from encoded backup variable
99  *
100  * @v *win      window on which to operate
101  * @v *pos      pointer to struct in which original cursor position is stored
102  */
103 static void _restore_curs_pos ( WINDOW *win, struct cursor_pos *pos ){
104         win->curs_y = pos->y;
105         win->curs_x = pos->x;
106 }
107
108 /**
109  * Store cursor position for later restoration
110  *
111  * @v *win      window on which to operate
112  * @v *pos      pointer to struct in which to store cursor position
113  */
114 static void _store_curs_pos ( WINDOW *win, struct cursor_pos *pos ) {
115         pos->y = win->curs_y;
116         pos->x = win->curs_x;
117 }
118
119 /**
120  * Move a window's cursor to the specified position
121  *
122  * @v *win      window to be operated on
123  * @v y         Y position
124  * @v x         X position
125  * @ret rc      return status code
126  */
127 int wmove ( WINDOW *win, int y, int x ) {
128         /* chech for out-of-bounds errors */
129         if ( ( ( (unsigned)x - win->ori_x ) > win->width ) ||
130              ( ( (unsigned)y - win->ori_y ) > win->height ) ) {
131                 return ERR;
132         }
133
134         win->curs_y = y;
135         win->curs_x = x;
136         win->scr->movetoyx( win->scr, win->ori_y + win->curs_y, 
137                                       win->ori_x + win->curs_x );
138         return OK;
139 }
140
141
142 /**
143  * get terminal baud rate
144  *
145  * @ret bps     return baud rate in bits per second
146  */
147 int baudrate ( void ) {
148         return 0;
149 }
150
151 /**
152  * Audible (or visual) signal
153  *
154  * @ret rc      return status code
155  */
156 int beep ( void ) {
157         printf("\a");
158         return OK;
159 }
160
161 /**
162  * Draw borders from single-byte characters and renditions around a
163  * window
164  *
165  * @v *win      window to be bordered
166  * @v verch     vertical chtype
167  * @v horch     horizontal chtype
168  * @ret rc      return status code
169  */
170 int box ( WINDOW *win, chtype verch, chtype horch ) {
171         int corner = '+' | win->attrs; /* default corner character */
172         return wborder( win, verch, verch, horch, horch,
173                         corner, corner, corner, corner );
174 }
175
176 /**
177  * Indicates whether the underlying terminal device is capable of
178  * having colours redefined
179  *
180  * @ret bool    returns boolean
181  */
182 bool can_change_colour ( void ) {
183         return (bool)TRUE;
184 }
185
186 /**
187  * Identify the RGB components of a given colour value
188  *
189  * @v colour    colour value
190  * @v *red      address to store red component
191  * @v *green    address to store green component
192  * @v *blue     address to store blue component
193  * @ret rc      return status code
194  */
195 int colour_content ( short colour, short *red, short *green, short *blue ) {
196         /* we do not have a particularly large range of colours (3
197            primary, 3 secondary and black), so let's just put in a
198            basic switch... */
199         switch(colour) {
200         case COLOUR_BLACK:
201                 *red = 0; *green = 0; *blue = 0;
202                 break;
203         case COLOUR_BLUE:
204                 *red = 0; *green = 0; *blue = 1000;
205                 break;
206         case COLOUR_GREEN:
207                 *red = 0; *green = 1000; *blue = 0;
208                 break;
209         case COLOUR_CYAN:
210                 *red = 0; *green = 1000; *blue = 1000;
211                 break;
212         case COLOUR_RED:
213                 *red = 1000; *green = 0; *blue = 0;
214                 break;
215         case COLOUR_MAGENTA:
216                 *red = 1000; *green = 0; *blue = 1000;
217                 break;
218         case COLOUR_YELLOW:
219                 *red = 1000; *green = 1000; *blue = 0;
220                 break;
221         }
222         return OK;
223 }
224
225 /**
226  * Delete a window
227  *
228  * @v *win      pointer to window being deleted
229  * @ret rc      return status code
230  */
231 int delwin ( WINDOW *win ) {
232         if ( win == NULL )
233                 goto err;
234         /* must free descendants first, but I haven't implemented descendants yet
235            ... */
236         free(win);
237         return OK;
238  err:
239         return ERR;
240 }
241
242 /**
243  * Get the background rendition attributes for a window
244  *
245  * @v *win      subject window
246  * @ret ch      chtype rendition representation
247  */
248 inline chtype getbkgd ( WINDOW *win ) {
249         return win->attrs;
250 }
251
252 /**
253  * Initialise console environment
254  *
255  * @ret *win    return pointer to stdscr
256  */
257 WINDOW *initscr ( void ) {
258         /* determine console size */
259         /* initialise screen */
260         stdscr->width = 80;
261         stdscr->height = 25;
262         /* set previously unknown window attributes */
263         /* refresh screen */
264         return stdscr;
265 }
266
267 /**
268  * Create new WINDOW
269  *
270  * @v nlines    number of lines
271  * @v ncols     number of columns
272  * @v begin_y   column origin
273  * @v begin_x   line origin
274  * @ret *win    return pointer to new window
275  */
276 WINDOW *newwin ( int nlines, int ncols, int begin_y, int begin_x ) {
277         WINDOW *win = calloc( 1, sizeof(WINDOW) );
278         win->ori_y = begin_y;
279         win->ori_x = begin_x;
280         win->height = nlines;
281         win->width = ncols;
282         win->scr = stdscr->scr;
283         win->parent = NULL;
284         win->child = NULL;
285         return win;
286 }
287
288 /**
289  * Add a single-byte character and rendition to a window and advance
290  * the cursor
291  *
292  * @v *win      window to be rendered in
293  * @v ch        character to be added at cursor
294  * @ret rc      return status code
295  */
296 int waddch ( WINDOW *win, const chtype ch ) {
297         _wputch( win, ch, WRAP );
298         return OK;
299 }
300
301 /**
302  * Add string of single-byte characters and renditions to a window
303  *
304  * @v *win      window to be rendered in
305  * @v *chstr    pointer to first chtype in "string"
306  * @v n         max number of chars from chstr to render
307  * @ret rc      return status code
308  */
309 int waddchnstr ( WINDOW *win, const chtype *chstr, int n ) {
310         struct cursor_pos pos;  
311
312         _store_curs_pos( win, &pos );
313         _wputchstr( win, chstr, NOWRAP, n );
314         _restore_curs_pos( win, &pos );
315         return OK;
316 }
317
318 /**
319  * Add string of single-byte characters to a window
320  *
321  * @v *win      window to be rendered in
322  * @v *str      standard c-style string
323  * @v n         max number of chars from string to render
324  * @ret rc      return status code
325  */
326 int waddnstr ( WINDOW *win, const char *str, int n ) {
327         _wputstr( win, str, WRAP, n );
328         return OK;
329 }
330
331 /**
332  * Turn off attributes in a window
333  *
334  * @v win       subject window
335  * @v attrs     attributes to enable
336  * @ret rc      return status code
337  */
338 int wattroff ( WINDOW *win, int attrs ) {
339         win->attrs &= ~attrs;
340         return 0;
341 }
342
343 /**
344  * Turn on attributes in a window
345  *
346  * @v win       subject window
347  * @v attrs     attributes to enable
348  * @ret rc      return status code
349  */
350 int wattron ( WINDOW *win, int attrs ) {
351         win->attrs |= attrs;
352         return OK;
353 }
354
355 /**
356  * Set attributes in a window
357  *
358  * @v win       subject window
359  * @v attrs     attributes to enable
360  * @ret rc      return status code
361  */
362 int wattrset ( WINDOW *win, int attrs ) {
363         win->attrs = ( attrs | ( win->attrs & A_COLOR ) );
364         return OK;
365 }
366
367 /**
368  * Get attributes and colour pair information
369  *
370  * @v *win      window to obtain information from
371  * @v *attrs    address in which to store attributes
372  * @v *pair     address in which to store colour pair
373  * @v *opts     undefined (for future implementation)
374  * @ret rc      return status cude
375  */
376 int wattr_get ( WINDOW *win, attr_t *attrs, short *pair, void *opts ) {
377         *attrs = win->attrs & A_ATTRIBUTES;
378         *pair = (short)(( win->attrs & A_COLOR ) >> CPAIR_SHIFT);
379         return OK;
380 }
381
382 /**
383  * Turn off attributes in a window
384  *
385  * @v *win      subject window
386  * @v attrs     attributes to toggle
387  * @v *opts     undefined (for future implementation)
388  * @ret rc      return status code
389  */
390 int wattr_off ( WINDOW *win, attr_t attrs, void *opts ) {
391         wattroff( win, attrs );
392         return OK;
393 }
394
395 /**
396  * Turn on attributes in a window
397  *
398  * @v *win      subject window
399  * @v attrs     attributes to toggle
400  * @v *opts     undefined (for future implementation)
401  * @ret rc      return status code
402  */
403 int wattr_on ( WINDOW *win, attr_t attrs, void *opts ) {
404         wattron( win, attrs );
405         return OK;
406 }
407
408 /**
409  * Set attributes and colour pair information in a window
410  *
411  * @v *win      subject window
412  * @v attrs     attributes to set
413  * @v cpair     colour pair to set
414  * @v *opts     undefined (for future implementation)
415  * @ret rc      return status code
416  */
417 int wattr_set ( WINDOW *win, attr_t attrs, short cpair, void *opts ) {
418         wattrset( win, attrs | ( ( (unsigned short)cpair ) << CPAIR_SHIFT ) );
419         return OK;
420 }
421
422 /**
423  * Draw borders from single-byte characters and renditions around a
424  * window
425  *
426  * @v *win      window to be bordered
427  * @v ls        left side
428  * @v rs        right side
429  * @v ts        top
430  * @v bs        bottom
431  * @v tl        top left corner
432  * @v tr        top right corner
433  * @v bl        bottom left corner
434  * @v br        bottom right corner
435  * @ret rc      return status code
436  */
437 int wborder ( WINDOW *win, chtype ls, chtype rs,
438               chtype ts, chtype bs, chtype tl,
439               chtype tr, chtype bl, chtype br ) {
440
441         wmove(win,0,0);
442
443         _wputch(win,tl,WRAP);
444         while ( ( win->width - 1 ) - win->curs_x ) {
445                 _wputch(win,ts,WRAP);
446         }
447         _wputch(win,tr,WRAP);
448
449         while ( ( win->height - 1 ) - win->curs_y ) {
450                 _wputch(win,ls,WRAP);
451                 wmove(win,win->curs_y,(win->width)-1);
452                 _wputch(win,rs,WRAP);
453         }
454
455         _wputch(win,bl,WRAP);
456         while ( ( win->width -1 ) - win->curs_x ) {
457                 _wputch(win,bs,WRAP);
458         }
459         _wputch(win,br,NOWRAP); /* do not wrap last char to leave
460                                    cursor in last position */
461
462         return OK;
463 }
464
465 /**
466  * Clear a window to the bottom
467  *
468  * @v *win      subject window
469  * @ret rc      return status code
470  */
471 int wclrtobot ( WINDOW *win ) {
472         struct cursor_pos pos;
473
474         _store_curs_pos( win, &pos );
475         while ( win->curs_y + win->curs_x ) {
476                 _wputch( win, (unsigned)' ', WRAP );
477         }
478         _restore_curs_pos( win, &pos );
479
480         return OK;
481 }
482
483 /**
484  * Clear a window to the end of the current line
485  *
486  * @v *win      subject window
487  * @ret rc      return status code
488  */
489 int wclrtoeol ( WINDOW *win ) {
490         struct cursor_pos pos;
491
492         _store_curs_pos( win, &pos );
493         while ( ( win->curs_y - pos.y ) == 0 ) {
494                 _wputch( win, (unsigned)' ', WRAP );
495         }
496         _restore_curs_pos( win, &pos );
497
498         return OK;
499 }
500
501 /**
502  * Set colour pair for a window
503  *
504  * @v *win                      subject window
505  * @v colour_pair_number        colour pair integer
506  * @v *opts                     undefined (for future implementation)
507  * @ret rc                      return status code
508  */
509 int wcolour_set ( WINDOW *win, short colour_pair_number, void *opts ) {
510         if ( ( unsigned short )colour_pair_number > COLORS )
511                 return ERR;
512
513         win->attrs = ( (unsigned short)colour_pair_number << CPAIR_SHIFT ) |
514                 ( win->attrs & A_ATTRIBUTES );
515         return OK;
516 }
517
518 /**
519  * Delete character under the cursor in a window
520  *
521  * @v *win      subject window
522  * @ret rc      return status code
523  */
524 int wdelch ( WINDOW *win ) {
525         struct cursor_pos pos;
526
527         _store_curs_pos( win, &pos );
528         _wputch( win, (unsigned)' ', NOWRAP );
529         _restore_curs_pos( win, &pos );
530
531         return OK;
532 }
533
534 /**
535  * Delete line under a window's cursor
536  *
537  * @v *win      subject window
538  * @ret rc      return status code
539  */
540 int wdeleteln ( WINDOW *win ) {
541         /* let's just set the cursor to the beginning of the line and
542            let wclrtoeol do the work :) */
543         wmove( win, win->curs_y, 0 );
544         wclrtoeol( win );
545         return OK;
546 }