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