- testable console framework implementing mucurses SCREEN struct
[people/xl0/gpxe.git] / src / core / curses.c
1 #include <curses.h>
2 #include <malloc.h>
3 #include <stddef.h>
4 #include <vsprintf.h>
5 #include <string.h>
6
7 /** @file
8  *
9  * MuCurses: lightweight xcurses implementation for PXE ROMs
10  *
11  */
12
13 #define WRAP 0
14 #define NOWRAP 1
15
16 unsigned short _COLS;
17 unsigned short _LINES;
18 unsigned short _COLOURS;
19 unsigned int *_COLOUR_PAIRS; /* basically this is an array, but as its
20                                length is determined only when initscr
21                                is run, I can only think to make it a
22                                pointer and malloc the array into being
23                                ... */
24
25 struct cursor_pos {
26         unsigned int y, x;
27 };
28
29 struct _softlabel {
30         /* Format of soft label 
31            0: left justify
32            1: centre justify
33            2: right justify
34          */
35         int fmt;
36         // label string
37         char *label;
38 };
39
40 struct _softlabelkeys {
41         struct _softlabel fkeys[12];
42         attr_t attrs;
43         unsigned int fmt;
44         unsigned int maxlablen;
45 };
46
47 struct _softlabelkeys *slks;
48
49 WINDOW _stdscr = {
50         .attrs = A_DEFAULT,
51         .ori_y = 0,
52         .ori_x = 0,
53         .curs_y = 0,
54         .curs_x = 0,
55         .scr = curscr,
56 };
57
58 /*
59  *  Primitives
60  */
61
62 /**
63  * Write a single character rendition to a window
64  *
65  * @v *win      window in which to write
66  * @v ch        character rendition to write
67  * @v wrap      wrap "switch"
68  */
69 static void _wputch ( WINDOW *win, chtype ch, int wrap ) {
70         /* make sure we set the screen cursor to the right position
71            first! */
72         win->scr->movetoyx( win->scr, win->ori_y + win->curs_y,
73                                       win->ori_x + win->curs_x );
74         win->scr->putc(win->scr, ch);
75         if ( ++(win->curs_x) == win->width ) {
76                 if ( wrap == WRAP ) {
77                         win->curs_x = 0;
78                         /* specification says we should really scroll,
79                            but we have no buffer to scroll with, so we
80                            can only overwrite back at the beginning of
81                            the window */
82                         if ( ++(win->curs_y) == win->height )
83                                 win->curs_y = 0;
84                 } else {
85                         (win->curs_x)--;
86                 }
87         }
88 }
89
90 /**
91  * Write a chtype string to a window
92  *
93  * @v *win      window in which to write
94  * @v *chstr    chtype string
95  * @v wrap      wrap "switch"
96  * @v n         write at most n chtypes
97  */
98 static void _wputchstr ( WINDOW *win, const chtype *chstr, int wrap, int n ) {
99         for ( ; *chstr && n-- ; chstr++ ) {
100                 _wputch(win,*chstr,wrap);
101         }
102 }
103
104 /**
105  * Write a standard c-style string to a window
106  *
107  * @v *win      window in which to write
108  * @v *str      string
109  * @v wrap      wrap "switch"
110  * @v n         write at most n chars from *str
111  */
112 static void _wputstr ( WINDOW *win, const char *str, int wrap, int n ) {
113         for ( ; *str && n-- ; str++ ) {
114                 _wputch( win, *str | win->attrs, wrap );
115         }
116 }
117
118 /**
119  * Restore cursor position from encoded backup variable
120  *
121  * @v *win      window on which to operate
122  * @v *pos      pointer to struct in which original cursor position is stored
123  */
124 static void _restore_curs_pos ( WINDOW *win, struct cursor_pos *pos ){
125         win->curs_y = pos->y;
126         win->curs_x = pos->x;
127         win->scr->movetoyx ( win->scr, win->curs_y, win->curs_x );
128 }
129
130 /**
131  * Store cursor position for later restoration
132  *
133  * @v *win      window on which to operate
134  * @v *pos      pointer to struct in which to store cursor position
135  */
136 static void _store_curs_pos ( WINDOW *win, struct cursor_pos *pos ) {
137         pos->y = win->curs_y;
138         pos->x = win->curs_x;
139 }
140
141 /**
142  * Move a window's cursor to the specified position
143  *
144  * @v *win      window to be operated on
145  * @v y         Y position
146  * @v x         X position
147  * @ret rc      return status code
148  */
149 int wmove ( WINDOW *win, int y, int x ) {
150         /* chech for out-of-bounds errors */
151         if ( ( ( (unsigned)x - win->ori_x ) > win->width ) ||
152              ( ( (unsigned)y - win->ori_y ) > win->height ) ) {
153                 return ERR;
154         }
155
156         win->curs_y = y;
157         win->curs_x = x;
158         win->scr->movetoyx( win->scr, win->ori_y + win->curs_y, 
159                                       win->ori_x + win->curs_x );
160         return OK;
161 }
162
163
164 /**
165  * get terminal baud rate
166  *
167  * @ret bps     return baud rate in bits per second
168  */
169 int baudrate ( void ) {
170         return OK;
171 }
172
173 /**
174  * Audible (or visual) signal
175  *
176  * @ret rc      return status code
177  */
178 int beep ( void ) {
179         printf("\a");
180         return OK;
181 }
182
183 /**
184  * Draw borders from single-byte characters and renditions around a
185  * window
186  *
187  * @v *win      window to be bordered
188  * @v verch     vertical chtype
189  * @v horch     horizontal chtype
190  * @ret rc      return status code
191  */
192 int box ( WINDOW *win, chtype verch, chtype horch ) {
193         int corner = '+' | win->attrs; /* default corner character */
194         return wborder( win, verch, verch, horch, horch,
195                         corner, corner, corner, corner );
196 }
197
198 /**
199  * Indicates whether the underlying terminal device is capable of
200  * having colours redefined
201  *
202  * @ret bool    returns boolean
203  */
204 bool can_change_colour ( void ) {
205         return (bool)TRUE;
206 }
207
208 /**
209  * Identify the RGB components of a given colour value
210  *
211  * @v colour    colour value
212  * @v *red      address to store red component
213  * @v *green    address to store green component
214  * @v *blue     address to store blue component
215  * @ret rc      return status code
216  */
217 int colour_content ( short colour, short *red, short *green, short *blue ) {
218         /* we do not have a particularly large range of colours (3
219            primary, 3 secondary and black), so let's just put in a
220            basic switch... */
221         switch(colour) {
222         case COLOUR_BLACK:
223                 *red = 0; *green = 0; *blue = 0;
224                 break;
225         case COLOUR_BLUE:
226                 *red = 0; *green = 0; *blue = 1000;
227                 break;
228         case COLOUR_GREEN:
229                 *red = 0; *green = 1000; *blue = 0;
230                 break;
231         case COLOUR_CYAN:
232                 *red = 0; *green = 1000; *blue = 1000;
233                 break;
234         case COLOUR_RED:
235                 *red = 1000; *green = 0; *blue = 0;
236                 break;
237         case COLOUR_MAGENTA:
238                 *red = 1000; *green = 0; *blue = 1000;
239                 break;
240         case COLOUR_YELLOW:
241                 *red = 1000; *green = 1000; *blue = 0;
242                 break;
243         }
244         return OK;
245 }
246
247 /**
248  * Delete a window
249  *
250  * @v *win      pointer to window being deleted
251  * @ret rc      return status code
252  */
253 int delwin ( WINDOW *win ) {
254         if ( win == NULL )
255                 goto err;
256         /* must free descendants first, but I haven't implemented descendants yet
257            ... */
258         free(win);
259         return OK;
260  err:
261         return ERR;
262 }
263
264 /**
265  * Get the background rendition attributes for a window
266  *
267  * @v *win      subject window
268  * @ret ch      chtype rendition representation
269  */
270 inline chtype getbkgd ( WINDOW *win ) {
271         return win->attrs;
272 }
273
274 /**
275  * Initialise console environment
276  *
277  * @ret *win    return pointer to stdscr
278  */
279 WINDOW *initscr ( void ) {
280         /* determine console size */
281         /* initialise screen */
282         stdscr->width = 80;
283         stdscr->height = 25;
284         /* set previously unknown window attributes */
285         /* refresh screen */
286         return stdscr;
287 }
288
289 /**
290  * Create new WINDOW
291  *
292  * @v nlines    number of lines
293  * @v ncols     number of columns
294  * @v begin_y   column origin
295  * @v begin_x   line origin
296  * @ret *win    return pointer to new window
297  */
298 WINDOW *newwin ( int nlines, int ncols, int begin_y, int begin_x ) {
299         WINDOW *win = calloc( 1, sizeof(WINDOW) );
300         win->ori_y = begin_y;
301         win->ori_x = begin_x;
302         win->height = nlines;
303         win->width = ncols;
304         win->scr = stdscr->scr;
305         win->parent = NULL;
306         win->child = NULL;
307         return win;
308 }
309
310 /**
311  * Return the attribute used for the soft function keys
312  *
313  * @ret attrs   the current attributes of the soft function keys
314  */
315 attr_t slk_attr ( void ) {
316         return ( slks == NULL ? 0 : slks->attrs );
317 }
318
319 /**
320  * Turn off soft function key attributes
321  *
322  * @v attrs     attribute bit mask
323  * @ret rc      return status code
324  */
325 int slk_attroff ( const chtype attrs ) {
326         if ( slks == NULL ) 
327                 return ERR;
328         slks->attrs &= ~( attrs & A_ATTRIBUTES );
329         return OK;
330 }
331
332 /**
333  * Turn on soft function key attributes
334  *
335  * @v attrs     attribute bit mask
336  * @ret rc      return status code
337  */
338 int slk_attron ( const chtype attrs ) {
339         if ( slks == NULL )
340                 return ERR;
341         slks->attrs |= ( attrs & A_ATTRIBUTES );
342         return OK;
343 }
344
345 /**
346  * Set soft function key attributes
347  *
348  * @v attrs     attribute bit mask
349  * @ret rc      return status code
350  */
351 int slk_attrset ( const chtype attrs ) {
352         if ( slks == NULL ) 
353                 return ERR;
354         slks->attrs = ( attrs & A_ATTRIBUTES );
355         return OK;
356 }
357
358 /**
359  * Turn off soft function key attributes
360  *
361  * @v attrs     attribute bit mask
362  * @v *opts     undefined (for future implementation)
363  * @ret rc      return status code
364  */
365 int slk_attr_off ( const attr_t attrs, void *opts __unused ) {
366         return slk_attroff( attrs );
367 }
368
369 /**
370  * Turn on soft function key attributes
371  *
372  * @v attrs     attribute bit mask
373  * @v *opts     undefined (for future implementation)
374  * @ret rc      return status code
375  */
376 int slk_attr_on ( attr_t attrs, void *opts __unused ) {
377         return slk_attron( attrs );
378 }
379
380 /**
381  * Set soft function key attributes
382  *
383  * @v attrs                     attribute bit mask
384  * @v colour_pair_number        colour pair integer
385  * @v *opts                     undefined (for future implementation)
386  * @ret rc                      return status code
387  */
388 int slk_attr_set ( const attr_t attrs, short colour_pair_number,
389                    void *opts __unused ) {
390         if ( slks == NULL ) 
391                 return ERR;
392
393         if ( ( unsigned short )colour_pair_number > COLORS )
394                 return ERR;
395
396         slks->attrs = ( (unsigned short)colour_pair_number << CPAIR_SHIFT ) |
397                 ( attrs & A_ATTRIBUTES );
398         return OK;
399 }
400
401 /**
402  * Clear the soft function key labels from the screen
403  *
404  * @ret rc      return status code
405  */
406 int slk_clear ( void ) {
407         if ( slks == NULL )
408                 return ERR;
409
410         wmove(stdscr,stdscr->height-1,0);
411         wclrtoeol(stdscr);
412         return 0;
413 }
414
415 /**
416  * Initialise the soft function keys
417  *
418  * @v fmt       format of keys
419  * @ret rc      return status code
420  */
421 int slk_init ( int fmt ) {
422         if ( (unsigned)fmt > 3 ) {
423                 return ERR;
424         }
425
426         slks = malloc(sizeof(struct _softlabelkeys));
427         slks->attrs = A_DEFAULT;
428         slks->fmt = fmt;
429         slks->maxlablen = 5;
430         return OK;
431 }
432
433 /**
434  * Return the label for the specified soft key
435  *
436  * @v labnum    soft key identifier
437  * @ret label   return label
438  */
439 char* slk_label ( int labnum ) {
440         if ( slks == NULL ) 
441                 return NULL;
442
443         return slks->fkeys[labnum].label;
444 }
445
446 /**
447  * Restore soft function key labels to the screen
448  *
449  * @ret rc      return status code
450  */
451 int slk_restore ( void ) {
452         if ( slks == NULL ) 
453                 return ERR;
454
455         return OK;
456 }
457
458 /**
459  * Configure specified soft key
460  *
461  * @v labnum    soft label position to configure
462  * @v *label    string to use as soft key label
463  * @v fmt       justification format of label
464  * @ret rc      return status code
465  */
466 int slk_set ( int labnum, const char *label, int fmt ) {
467         if ( slks == NULL ) 
468                 return ERR;
469         if ( labnum == 0 || (unsigned)labnum > 12 )
470                 return ERR;
471         if ( (unsigned)fmt >= 3 )
472                 return ERR;
473         if ( strlen(label) > slks->maxlablen )
474                 return ERR;
475
476         strcpy( slks->fkeys[labnum].label, label );
477         slks->fkeys[labnum].fmt = fmt;
478
479         return OK;
480 }
481
482 struct printw_context {
483         struct printf_context ctx;
484         WINDOW *win;
485 };
486
487 static void _printw_handler ( struct printf_context *ctx, unsigned int c ) {
488         struct printw_context *wctx =
489                 container_of ( ctx, struct printw_context, ctx );
490
491         _wputch( wctx->win, c | wctx->win->attrs, WRAP );
492 }
493
494 /**
495  * Print formatted output in a window
496  *
497  * @v *win      subject window
498  * @v *fmt      formatted string
499  * @v varglist  argument list
500  * @ret rc      return status code
501  */
502 int vw_printw ( WINDOW *win, const char *fmt, va_list varglist ) {
503         struct printw_context wctx = {
504                 .win = win,
505                 .ctx = { .handler = _printw_handler, },
506         };
507
508         vcprintf ( &(wctx.ctx), fmt, varglist );
509         return OK;
510 }
511
512 /**
513  * Add a single-byte character and rendition to a window and advance
514  * the cursor
515  *
516  * @v *win      window to be rendered in
517  * @v ch        character to be added at cursor
518  * @ret rc      return status code
519  */
520 int waddch ( WINDOW *win, const chtype ch ) {
521         _wputch( win, ch, WRAP );
522         return OK;
523 }
524
525 /**
526  * Add string of single-byte characters and renditions to a window
527  *
528  * @v *win      window to be rendered in
529  * @v *chstr    pointer to first chtype in "string"
530  * @v n         max number of chars from chstr to render
531  * @ret rc      return status code
532  */
533 int waddchnstr ( WINDOW *win, const chtype *chstr, int n ) {
534         struct cursor_pos pos;  
535
536         _store_curs_pos( win, &pos );
537         _wputchstr( win, chstr, NOWRAP, n );
538         _restore_curs_pos( win, &pos );
539         return OK;
540 }
541
542 /**
543  * Add string of single-byte characters to a window
544  *
545  * @v *win      window to be rendered in
546  * @v *str      standard c-style string
547  * @v n         max number of chars from string to render
548  * @ret rc      return status code
549  */
550 int waddnstr ( WINDOW *win, const char *str, int n ) {
551         _wputstr( win, str, WRAP, n );
552         return OK;
553 }
554
555 /**
556  * Turn off attributes in a window
557  *
558  * @v win       subject window
559  * @v attrs     attributes to enable
560  * @ret rc      return status code
561  */
562 int wattroff ( WINDOW *win, int attrs ) {
563         win->attrs &= ~attrs;
564         return OK;
565 }
566
567 /**
568  * Turn on attributes in a window
569  *
570  * @v win       subject window
571  * @v attrs     attributes to enable
572  * @ret rc      return status code
573  */
574 int wattron ( WINDOW *win, int attrs ) {
575         win->attrs |= attrs;
576         return OK;
577 }
578
579 /**
580  * Set attributes in a window
581  *
582  * @v win       subject window
583  * @v attrs     attributes to enable
584  * @ret rc      return status code
585  */
586 int wattrset ( WINDOW *win, int attrs ) {
587         win->attrs = ( attrs | ( win->attrs & A_COLOR ) );
588         return OK;
589 }
590
591 /**
592  * Get attributes and colour pair information
593  *
594  * @v *win      window to obtain information from
595  * @v *attrs    address in which to store attributes
596  * @v *pair     address in which to store colour pair
597  * @v *opts     undefined (for future implementation)
598  * @ret rc      return status cude
599  */
600 int wattr_get ( WINDOW *win, attr_t *attrs, short *pair, 
601                 void *opts __unused ) {
602         *attrs = win->attrs & A_ATTRIBUTES;
603         *pair = (short)(( win->attrs & A_COLOR ) >> CPAIR_SHIFT);
604         return OK;
605 }
606
607 /**
608  * Turn off attributes in a window
609  *
610  * @v *win      subject window
611  * @v attrs     attributes to toggle
612  * @v *opts     undefined (for future implementation)
613  * @ret rc      return status code
614  */
615 int wattr_off ( WINDOW *win, attr_t attrs, 
616                 void *opts __unused ) {
617         wattroff( win, attrs );
618         return OK;
619 }
620
621 /**
622  * Turn on attributes in a window
623  *
624  * @v *win      subject window
625  * @v attrs     attributes to toggle
626  * @v *opts     undefined (for future implementation)
627  * @ret rc      return status code
628  */
629 int wattr_on ( WINDOW *win, attr_t attrs, 
630                void *opts __unused ) {
631         wattron( win, attrs );
632         return OK;
633 }
634
635 /**
636  * Set attributes and colour pair information in a window
637  *
638  * @v *win      subject window
639  * @v attrs     attributes to set
640  * @v cpair     colour pair to set
641  * @v *opts     undefined (for future implementation)
642  * @ret rc      return status code
643  */
644 int wattr_set ( WINDOW *win, attr_t attrs, short cpair, 
645                 void *opts __unused ) {
646         wattrset( win, attrs | ( ( (unsigned short)cpair ) << CPAIR_SHIFT ) );
647         return OK;
648 }
649
650 /**
651  * Draw borders from single-byte characters and renditions around a
652  * window
653  *
654  * @v *win      window to be bordered
655  * @v ls        left side
656  * @v rs        right side
657  * @v ts        top
658  * @v bs        bottom
659  * @v tl        top left corner
660  * @v tr        top right corner
661  * @v bl        bottom left corner
662  * @v br        bottom right corner
663  * @ret rc      return status code
664  */
665 int wborder ( WINDOW *win, chtype ls, chtype rs,
666               chtype ts, chtype bs, chtype tl,
667               chtype tr, chtype bl, chtype br ) {
668
669         wmove(win,0,0);
670
671         _wputch(win,tl,WRAP);
672         while ( ( win->width - 1 ) - win->curs_x ) {
673                 _wputch(win,ts,WRAP);
674         }
675         _wputch(win,tr,WRAP);
676
677         while ( ( win->height - 1 ) - win->curs_y ) {
678                 _wputch(win,ls,WRAP);
679                 wmove(win,win->curs_y,(win->width)-1);
680                 _wputch(win,rs,WRAP);
681         }
682
683         _wputch(win,bl,WRAP);
684         while ( ( win->width -1 ) - win->curs_x ) {
685                 _wputch(win,bs,WRAP);
686         }
687         _wputch(win,br,NOWRAP); /* do not wrap last char to leave
688                                    cursor in last position */
689
690         return OK;
691 }
692
693 /**
694  * Clear a window to the bottom
695  *
696  * @v *win      subject window
697  * @ret rc      return status code
698  */
699 int wclrtobot ( WINDOW *win ) {
700         struct cursor_pos pos;
701
702         _store_curs_pos( win, &pos );
703         do {
704                 _wputch( win, (unsigned)' ', WRAP );
705         } while ( win->curs_y + win->curs_x );
706         _restore_curs_pos( win, &pos );
707
708         return OK;
709 }
710
711 /**
712  * Clear a window to the end of the current line
713  *
714  * @v *win      subject window
715  * @ret rc      return status code
716  */
717 int wclrtoeol ( WINDOW *win ) {
718         struct cursor_pos pos;
719
720         _store_curs_pos( win, &pos );
721         while ( ( win->curs_y - pos.y ) == 0 ) {
722                 _wputch( win, (unsigned)' ', WRAP );
723         }
724         _restore_curs_pos( win, &pos );
725
726         return OK;
727 }
728
729 /**
730  * Set colour pair for a window
731  *
732  * @v *win                      subject window
733  * @v colour_pair_number        colour pair integer
734  * @v *opts                     undefined (for future implementation)
735  * @ret rc                      return status code
736  */
737 int wcolour_set ( WINDOW *win, short colour_pair_number, 
738                   void *opts __unused ) {
739         if ( ( unsigned short )colour_pair_number > COLORS )
740                 return ERR;
741
742         win->attrs = ( (unsigned short)colour_pair_number << CPAIR_SHIFT ) |
743                 ( win->attrs & A_ATTRIBUTES );
744         return OK;
745 }
746
747 /**
748  * Delete character under the cursor in a window
749  *
750  * @v *win      subject window
751  * @ret rc      return status code
752  */
753 int wdelch ( WINDOW *win ) {
754         struct cursor_pos pos;
755
756         _store_curs_pos( win, &pos );
757         _wputch( win, (unsigned)' ', NOWRAP );
758         _restore_curs_pos( win, &pos );
759
760         return OK;
761 }
762
763 /**
764  * Delete line under a window's cursor
765  *
766  * @v *win      subject window
767  * @ret rc      return status code
768  */
769 int wdeleteln ( WINDOW *win ) {
770         /* let's just set the cursor to the beginning of the line and
771            let wclrtoeol do the work :) */
772         wmove( win, win->curs_y, 0 );
773         wclrtoeol( win );
774         return OK;
775 }
776
777 /**
778  * Create a horizontal line in a window
779  *
780  * @v *win      subject window
781  * @v ch        rendition and character
782  * @v n         max number of chars (wide) to render
783  * @ret rc      return status code
784  */
785 int whline ( WINDOW *win, chtype ch, int n ) {
786         struct cursor_pos pos;
787
788         _store_curs_pos ( win, &pos );
789         while ( ( win->curs_x - win->width ) && n-- ) {
790                 _wputch ( win, ch, NOWRAP );
791         }
792         _restore_curs_pos ( win, &pos );
793
794         return OK;
795 }
796
797 /**
798  * Print formatted output to a window
799  *
800  * @v *win      subject window
801  * @v *fmt      formatted string
802  * @v ...       string arguments
803  * @ret rc      return status code
804  */
805 int wprintw ( WINDOW *win, const char *fmt, ... ) {
806         va_list args;
807         int i;
808
809         va_start ( args, fmt );
810         i = vw_printw ( win, fmt, args );
811         va_end ( args );
812         return i;
813 }
814
815 /**
816  * Create a vertical line in a window
817  *
818  * @v *win      subject window
819  * @v ch        rendition and character
820  * @v n         max number of lines to render
821  * @ret rc      return status code
822  */
823 int wvline ( WINDOW *win, chtype ch, int n ) {
824         struct cursor_pos pos;
825
826         _store_curs_pos ( win, &pos );
827         while ( ( win->curs_y - win->height ) && n-- ) {
828                 _wputch ( win, ch, NOWRAP );
829                 wmove( win, ++(win->curs_y), pos.x);
830         }
831         _restore_curs_pos ( win, &pos );
832
833         return OK;
834 }