add support for textareas, used for various dialog windows on the gui.
[asterisk/asterisk.git] / channels / console_board.c
1 /* 
2  * Message board implementation.
3  *
4  * A message board is a section of an sdl screen where
5  * messages can be printed, like on a terminal window.
6  * The text is stored in a buffer
7  * of fixed size (rows and cols). A portion of the buffer is
8  * visible on the screen, and the visible window can be moved up and
9  * down by dragging.
10  * 
11  * TODO: font dynamic allocation
12  *
13  * OLD: The physical section displayed on the screen is defined
14  * as keypad element, (the name is defined in the `region' variable
15  * so the board geometry can be read from the skin or from the
16  * configuration file.
17  *
18  * OLD: To define a message board:
19  *  - declare a board in the gui_info structure;
20  *  - define a region name in the keypad skin and update
21  *    the gui_key_map list;
22  *  - add and manage focus events on its.
23  *
24  */
25
26 #include "asterisk.h"   /* ast_strdupa */
27 #include "asterisk/utils.h"     /* ast_strdupa */
28
29 #ifndef HAVE_SDL
30 /* nothing */
31 #else
32 #include <SDL/SDL.h>
33
34 #define GUI_BUFFER_LEN 256                      /* buffer lenght used for input buffers */
35
36 /* Fonts characterization, TODO, read from file */
37 #define FONT_H 20                       /* char height, pixels */
38 #define FONT_W 9                        /* char width, pixels */
39
40 struct board {
41         int             kb_output;      /* identity of the board */
42         /* pointer to the destination surface (on the keypad window) */
43         SDL_Surface     *screen;        /* the main screen */
44         SDL_Rect        *p_rect;        /* where to write on the main screen */
45         SDL_Surface     *blank;         /* original content of the window */
46
47         int     v_h;    /* virtual text height, in lines */
48         int     v_w;    /* virtual text width, in lines (probably same as p_w) */
49         int     p_h;    /* physical (displayed) text height, in lines
50                          * XXX p_h * FONT_H = pixel_height */
51         int     p_w;    /* physical (displayed) text width, in characters
52                          * XXX p_w * FONT_W = pixel_width */
53
54         int     cur_col; /* print position (free character) on the last line */
55         int     cur_line;       /* first (or last ?) virtual line displayed,
56                                          * 0 is the line at the bottom, 1 is the one above,...
57                                          */
58
59         SDL_Surface     *font;          /* points to a surface in the gui structure */
60         SDL_Rect        *font_rects;    /* pointer to the font rects */
61         char            *text;
62                                 /* text buffer, v_h * v_w char.
63                                  * We make sure the buffer is always full,
64                                  * print on some position on the last line,
65                                  * and scroll up when appending new text
66                                  */
67 };
68
69 /*! \brief Initialize the board.
70  * return 0 on success, 1 on error
71  * TODO, if this is done at reload time,
72  * free resources before allocate new ones
73  * TODO: resource deallocation in case of error.
74  * TODO: move the font load at gui_initialization
75  * TODO: deallocation of the message history
76  */
77 struct board *board_setup(SDL_Surface *screen, SDL_Rect *dest,
78         SDL_Surface *font, SDL_Rect *font_rects);
79 struct board *board_setup(SDL_Surface *screen, SDL_Rect *dest,
80         SDL_Surface *font, SDL_Rect *font_rects)
81 {
82         struct board *b = ast_calloc(1, sizeof (*b));
83         SDL_Rect br;
84
85         if (b == NULL)
86                 return NULL;
87         /* font, points to the gui structure */
88         b->font = font;
89         b->font_rects = font_rects;
90
91         /* Destination rectangle on the screen - reference is the whole screen */
92         b->p_rect = dest;
93         b->screen = screen;
94
95         /* compute physical sizes */
96         b->p_h = b->p_rect->h/FONT_H;
97         b->p_w = b->p_rect->w/FONT_W;
98
99         /* virtual sizes */
100         b->v_h = b->p_h * 10; /* XXX 10 times larger */
101         b->v_w = b->p_w;        /* same width */
102
103         br.h = b->p_h * FONT_H; /* pixel sizes of the background */
104         br.w = b->p_w * FONT_W;
105         br.x = br.y = 0;
106         
107         b->text = ast_calloc(b->v_w*b->v_h + 1, 1);
108         if (b->text == NULL) {
109                 ast_log(LOG_WARNING, "Unable to allocate board history memory.\n");
110                 ast_free(b);
111                 return NULL;
112         }
113         memset(b->text, ' ', b->v_w * b->v_h);  /* fill with spaces */
114
115         /* XXX make a copy of the original, for cleaning up */
116         b->blank = SDL_CreateRGBSurface(screen->flags, br.w, br.h,
117                 screen->format->BitsPerPixel,
118                 screen->format->Rmask, screen->format->Gmask,
119                 screen->format->Bmask, screen->format->Amask);
120
121         if (b->blank == NULL) { 
122                 ast_log(LOG_WARNING, "Unable to allocate board virtual screen: %s\n",
123                                 SDL_GetError());
124                 ast_free(b->text);
125                 ast_free(b);
126                 return NULL;
127         }
128         SDL_BlitSurface(screen, b->p_rect, b->blank, &br);
129
130         /* Set color key, if not alpha channel present */
131         //colorkey = SDL_MapRGB(b->board_surface->format, 0, 0, 0);
132         //SDL_SetColorKey(b->board_surface, SDL_SRCCOLORKEY, colorkey);
133
134         b->cur_col = 0;         /* current print column */
135         b->cur_line = 0;        /* last line displayed */
136
137         ast_log(LOG_WARNING, "Message board %dx%d@%d,%d successfully initialized\n",
138                 b->p_rect->w, b->p_rect->h,
139                 b->p_rect->x, b->p_rect->y);
140         return b;
141 }
142
143 #if 0
144
145 /*! \brief Remap and blit the virtual surface on the physical surface */
146 static void blit_on_screen(struct gui_info *gui, struct board *b)
147 {
148         /* Blit a section of the virtual board on the main surface */
149         SDL_Rect mapped_rect;   /* coordinates related to the main surface */
150
151         mapped_rect.x = 0;
152         mapped_rect.y = b->rendering_offset;
153         mapped_rect.w = b->p_rect.w;
154         mapped_rect.h = b->p_rect.h;
155
156         /* Clean the surface (print backgroud) */
157         // This sould be done in the main loop
158         // SDL_BlitSurface(gui->keypad, NULL, gui->screen, &b->p_rect);
159         SDL_BlitSurface(gui->keypad, NULL, gui->screen, &b->p_rect);
160
161         /* Blit the virtual surface on the main surface */
162         SDL_BlitSurface(b->v_board, &mapped_rect, gui->screen, &b->p_rect);
163
164         /* Update the keypad screen, should be done in the main loop */
165         SDL_UpdateRects(gui->screen, 1, &gui->message_board.p_rect);
166         //SDL_UpdateRects(gui->screen, 1, &gui->message_board.p_rect);
167 }
168
169 #endif /* notyet */
170
171 /* Render the text on the board surface.
172  * The first line to render is the one at v_h - p_h - cur_line,
173  * the size is p_h * p_w.
174  * XXX we assume here that p_w = v_w.
175  */
176 static void render_board(struct board *b)
177 {
178         int first_row = b->v_h - b->p_h - b->cur_line;
179         int first_char = b->v_w * first_row;
180         int last_char = first_char + b->p_h * b->v_w;
181         int i, col;
182         SDL_Rect dst;
183
184         /* top left char on the physical surface */
185         dst.w = FONT_W;
186         dst.h = FONT_H;
187         dst.x = b->p_rect->x;
188         dst.y = b->p_rect->y;
189
190
191         /* clean the surface board */
192         SDL_BlitSurface(b->blank, NULL, b->screen, b->p_rect);
193
194         /* blit all characters */
195         for (i = first_char, col = 0; i <  last_char; i++) {
196                 int c = b->text[i] - 32;
197                 SDL_BlitSurface(b->font, &b->font_rects[c], b->screen, &dst);
198                 /* point dst to next char position */
199                 dst.x += dst.w;
200                 col++;
201                 if (col >= b->v_w) { /* next row */
202                         dst.x = b->p_rect->x;
203                         dst.y += dst.h;
204                         col = 0;
205                 }
206         }
207         /* Update the written portion of the keypad on the screen */
208         SDL_UpdateRects(b->screen, 1, b->p_rect);
209 }
210
211 /* Store the message on the history board
212  * and blit on screen if required.
213  * XXX now easy. only regular chars
214  */
215 int print_message(struct board *b, const char *s);
216 int print_message(struct board *b, const char *s)
217 {
218         int i, l, row, col;
219         char *dst;
220
221         if (ast_strlen_zero(s))
222                 return 0;
223
224         l = strlen(s);
225         row = 0;
226         col = b->cur_col;
227         /* First, only check how much space we need.
228          * Starting from the current print position, we move
229          * it forward and down (if necessary) according to input
230          * characters (including newlines, tabs, backspaces...).
231          * At the end, row tells us how many rows to scroll, and
232          * col (ignored) is the final print position.
233          */
234         for (i = 0; i < l; i++) {
235                 switch (s[i]) {
236                 case '\r':
237                         col = 0;
238                         break;
239                 case '\n':
240                         col = 0;
241                         row++;
242                         break;
243                 case '\b':
244                         if (col > 0)
245                                 col--;
246                         break;
247                 default:
248                         if (s[i] < 32) /* signed, so take up to 127 */
249                                 break;
250                         col++;
251                         if (col >= b->v_w) {
252                                 col -= b->v_w;
253                                 row++;
254                         }
255                         break;
256                 }
257         }
258         /* scroll the text window */
259         if (row > 0) { /* need to scroll by 'row' rows */
260                 memcpy(b->text, b->text + row * b->v_w, b->v_w * (b->v_h - row));
261                 /* clean the destination area */
262                 dst = b->text + b->v_w * (b->v_h - row - 1) + b->cur_col;
263                 memset(dst, ' ', b->v_w - b->cur_col + b->v_w * row);
264         }
265         /* now do the actual printing. The print position is 'row' lines up
266          * from the bottom of the buffer, start at the same 'cur_col' as before.
267          * dst points to the beginning of the current line.
268          */
269         dst = b->text + b->v_w * (b->v_h - row - 1); /* start of current line */
270         col = b->cur_col;
271         for (i = 0; i < l; i++) {
272                 switch (s[i]) {
273                 case '\r':
274                         col = 0;
275                         break;
276                 case '\n':      /* move to beginning of next line */
277                         col = 0;
278                         dst += b->v_w;
279                         break;
280                 case '\b':      /* one char back */
281                         if (col > 0)
282                                 col--;
283                         break;
284                 default:
285                         if (s[i] < 32) /* signed, so take up to 127 */
286                                 break;  /* non printable */
287                         dst[col] = s[i];        /* store character */
288                         col++;
289                         if (col >= b->v_w) {
290                                 col -= b->v_w;
291                                 dst += b->v_w;
292                         }
293                         break;
294                 }
295         }
296         b->cur_col = col;
297         /* everything is printed now, must do the rendering */
298         //board_dump(b);
299         render_board(b);
300         return 1;
301 }
302
303 #if 0
304 /*! \brief refresh the screen, and also grab a bunch of events.
305  */
306 static int scroll_message(...)
307 {
308 if moving up, scroll text up;
309     if (gui->message_board.screen_cur > 0)
310         gui->message_board.screen_cur--;
311 otherwise scroll text down.
312     if ((b->screen_cur + b->p_line) < b->board_next) {
313         gui->message_board.screen_cur++;
314 #endif /* notyet */
315
316 #endif /* HAVE_SDL */