resolve a load-time problem avoiding a call to console_do_answer.
[asterisk/asterisk.git] / channels / console_gui.c
1 /*
2  * GUI for console video.
3  * The routines here are in charge of loading the keypad and handling events.
4  * $Revision$
5  */
6
7 #include "asterisk.h"
8 #include "console_video.h"
9 #include "asterisk/lock.h"
10 #include "asterisk/frame.h"
11 #include "asterisk/utils.h"     /* ast_calloc and ast_realloc */
12 #include <math.h>               /* sqrt */
13
14 /* we support 3 regions in the GUI */
15 enum { WIN_LOCAL, WIN_REMOTE, WIN_KEYPAD, WIN_MAX };
16
17 #ifndef HAVE_SDL
18 /* stubs if we don't have any sdl */
19 static void show_frame(struct video_desc *env, int out) {}
20 static void sdl_setup(struct video_desc *env)           {}
21 static struct gui_info *cleanup_sdl(struct gui_info *gui)       { return NULL; }
22 static void eventhandler(struct video_desc *env, const char *caption)   {}
23 static int keypad_cfg_read(struct gui_info *gui, const char *val)       { return 0; }
24
25 #else /* HAVE_SDL, the real rendering code */
26
27 #include <SDL/SDL.h>
28 #ifdef HAVE_SDL_IMAGE
29 #include <SDL/SDL_image.h>      /* for loading images */
30 #endif
31 #ifdef HAVE_SDL_TTF
32 #include <SDL/SDL_ttf.h>        /* render text on sdl surfaces */
33 #endif
34
35 enum kp_type { KP_NONE, KP_RECT, KP_CIRCLE };
36 struct keypad_entry {
37         int c;  /* corresponding character */
38         int x0, y0, x1, y1, h;  /* arguments */
39         enum kp_type type;
40 };
41
42 /* our representation of a displayed window. SDL can only do one main
43  * window so we map everything within that one
44  */
45 struct display_window   {   
46         SDL_Overlay     *bmp;
47         SDL_Rect        rect;   /* location of the window */
48 };
49 #define GUI_BUFFER_LEN 256                      /* buffer lenght used for input buffers */
50
51 struct keypad_entry;    /* defined in console_gui.c */
52
53 /*! \brief info related to the gui: button status, mouse coords, etc. */
54 struct gui_info {
55         char                    inbuf[GUI_BUFFER_LEN];  /* buffer for to-dial buffer */
56         int                     inbuf_pos;      /* next free position in inbuf */
57         char                    msgbuf[GUI_BUFFER_LEN]; /* buffer for text-message buffer */
58         int                     msgbuf_pos;     /* next free position in msgbuf */
59         int                     text_mode;      /* switch to-dial and text-message mode */
60         int                     drag_mode;      /* switch phone and drag-source mode */
61         int                     x_drag;         /* x coordinate where the drag starts */
62         int                     y_drag;         /* y coordinate where the drag starts */
63 #ifdef HAVE_SDL_TTF
64         TTF_Font                *font;          /* font to be used */ 
65 #endif
66         /* support for display. */
67         SDL_Surface             *screen;        /* the main window */
68
69         int                     outfd;          /* fd for output */
70         SDL_Surface             *keypad;        /* the pixmap for the keypad */
71         int kp_size, kp_used;
72         struct keypad_entry *kp;
73
74         struct display_window   win[WIN_MAX];
75 };
76
77 /*! \brief free the resources in struct gui_info and the descriptor itself.
78  *  Return NULL so we can assign the value back to the descriptor in case.
79  */
80 static struct gui_info *cleanup_sdl(struct gui_info *gui)
81 {
82         int i;
83
84         if (gui == NULL)
85                 return NULL;
86
87 #ifdef HAVE_SDL_TTF
88         /* unload font file */ 
89         if (gui->font) {
90                 TTF_CloseFont(gui->font);
91                 gui->font = NULL; 
92         }
93
94         /* uninitialize SDL_ttf library */
95         if ( TTF_WasInit() )
96                 TTF_Quit();
97 #endif
98         if (gui->outfd > -1)
99                 close(gui->outfd);
100         if (gui->keypad)
101                 SDL_FreeSurface(gui->keypad);
102         gui->keypad = NULL;
103         if (gui->kp)
104                 ast_free(gui->kp);
105
106         /* uninitialize the SDL environment */
107         for (i = 0; i < WIN_MAX; i++) {
108                 if (gui->win[i].bmp)
109                         SDL_FreeYUVOverlay(gui->win[i].bmp);
110         }
111         bzero(gui, sizeof(gui));
112         ast_free(gui);
113         SDL_Quit();
114         return NULL;
115 }
116
117 /*
118  * Display video frames (from local or remote stream) using the SDL library.
119  * - Set the video mode to use the resolution specified by the codec context
120  * - Create a YUV Overlay to copy the frame into it;
121  * - After the frame is copied into the overlay, display it
122  *
123  * The size is taken from the configuration.
124  *
125  * 'out' is 0 for remote video, 1 for the local video
126  */
127 static void show_frame(struct video_desc *env, int out)
128 {
129         AVPicture *p_in, p_out;
130         struct fbuf_t *b_in, *b_out;
131         SDL_Overlay *bmp;
132         struct gui_info *gui = env->gui;
133
134         if (!gui)
135                 return;
136
137         if (out == WIN_LOCAL) { /* webcam/x11 to sdl */
138                 b_in = &env->enc_in;
139                 b_out = &env->loc_dpy;
140                 p_in = NULL;
141         } else {
142                 /* copy input format from the decoding context */
143                 AVCodecContext *c;
144                 if (env->in == NULL)    /* XXX should not happen - decoder not ready */
145                         return;
146                 c = env->in->dec_ctx;
147                 b_in = &env->in->dec_out;
148                 b_in->pix_fmt = c->pix_fmt;
149                 b_in->w = c->width;
150                 b_in->h = c->height;
151
152                 b_out = &env->rem_dpy;
153                 p_in = (AVPicture *)env->in->d_frame;
154         }
155         bmp = gui->win[out].bmp;
156         SDL_LockYUVOverlay(bmp);
157         /* output picture info - this is sdl, YUV420P */
158         bzero(&p_out, sizeof(p_out));
159         p_out.data[0] = bmp->pixels[0];
160         p_out.data[1] = bmp->pixels[1];
161         p_out.data[2] = bmp->pixels[2];
162         p_out.linesize[0] = bmp->pitches[0];
163         p_out.linesize[1] = bmp->pitches[1];
164         p_out.linesize[2] = bmp->pitches[2];
165
166         my_scale(b_in, p_in, b_out, &p_out);
167
168         /* lock to protect access to Xlib by different threads. */
169         SDL_DisplayYUVOverlay(bmp, &gui->win[out].rect);
170         SDL_UnlockYUVOverlay(bmp);
171 }
172
173 /*
174  * GUI layout, structure and management
175  *
176
177 For the GUI we use SDL to create a large surface (gui->screen)
178 containing tree sections: remote video on the left, local video
179 on the right, and the keypad with all controls and text windows
180 in the center.
181 The central section is built using two images: one is the skin,
182 the other one is a mask where the sensitive areas of the skin
183 are colored in different grayscale levels according to their
184 functions. The mapping between colors and function is defined
185 in the 'enum pixel_value' below.
186
187 Mouse and keyboard events are detected on the whole surface, and
188 handled differently according to their location, as follows:
189
190 - drag on the local video window are used to move the captured
191   area (in the case of X11 grabber) or the picture-in-picture
192   location (in case of camera included on the X11 grab).
193 - click on the keypad are mapped to the corresponding key;
194 - drag on some keypad areas (sliders etc.) are mapped to the
195   corresponding functions;
196 - keystrokes are used as keypad functions, or as text input
197   if we are in text-input mode.
198
199 To manage these behavior we use two status variables,
200 that defines if keyboard events should be redirect to dialing functions
201 or to write message functions, and if mouse events should be used
202 to implement keypad functionalities or to drag the capture device.
203
204 Configuration options control the appeareance of the gui:
205
206     keypad = /tmp/phone.jpg             ; the keypad on the screen
207     keypad_font = /tmp/font.ttf         ; the font to use for output
208
209  *
210  */
211
212 /* enumerate for the pixel value. 0..127 correspond to ascii chars */
213 enum pixel_value {
214         /* answer/close functions */
215         KEY_PICK_UP = 128,
216         KEY_HANG_UP = 129,
217
218         /* other functions */
219         KEY_MUTE = 130,
220         KEY_AUTOANSWER = 131,
221         KEY_SENDVIDEO = 132,
222         KEY_LOCALVIDEO = 133,
223         KEY_REMOTEVIDEO = 134,
224         KEY_WRITEMESSAGE = 135,
225         KEY_GUI_CLOSE = 136,            /* close gui */
226
227         /* other areas within the keypad */
228         KEY_DIGIT_BACKGROUND = 255,
229
230         /* areas outside the keypad - simulated */
231         KEY_OUT_OF_KEYPAD = 251,
232         KEY_REM_DPY = 252,
233         KEY_LOC_DPY = 253,
234 };
235
236 /*
237  * Handlers for the various keypad functions
238  */
239
240 /*! \brief append a character, or reset if '\0' */
241 static void append_char(char *str, int *str_pos, const char c)
242 {
243         int i = *str_pos;
244         if (c == '\0')
245                 i = 0;
246         else if (i < GUI_BUFFER_LEN - 1)
247                 str[i++] = c;
248         else
249                 i = GUI_BUFFER_LEN - 1; /* unnecessary, i think */
250         str = '\0';
251         *str_pos = i;
252 }
253
254 static void append_string(char *str, int *str_pos, const char *s)
255 {
256         while (*s)
257                 append_char(str, str_pos, *s++);
258 }
259
260 /* accumulate digits, possibly call dial if in connected mode */
261 static void keypad_digit(struct video_desc *env, int digit)
262 {       
263         if (env->owner) {               /* we have a call, send the digit */
264                 struct ast_frame f = { AST_FRAME_DTMF, 0 };
265
266                 f.subclass = digit;
267                 ast_queue_frame(env->owner, &f);
268         } else {                /* no call, accumulate digits */
269                 append_char(env->gui->inbuf, &env->gui->inbuf_pos, digit);
270         }
271 }
272
273 /* this is a wrapper for actions that are available through the cli */
274 /* TODO append arg to command and send the resulting string as cli command */
275 static void keypad_send_command(struct video_desc *env, char *command)
276 {       
277         ast_log(LOG_WARNING, "keypad_send_command(%s) called\n", command);
278         ast_cli_command(env->gui->outfd, command);
279         return;
280 }
281
282 /* function used to toggle on/off the status of some variables */
283 static char *keypad_toggle(struct video_desc *env, int index)
284 {
285         ast_log(LOG_WARNING, "keypad_toggle(%i) called\n", index);
286
287         switch (index) {
288         case KEY_SENDVIDEO:
289                 env->out.sendvideo = !env->out.sendvideo;
290                 break;
291 #ifdef notyet
292         case KEY_MUTE: {
293                 struct chan_oss_pvt *o = find_desc(oss_active);
294                 o->mute = !o->mute;
295                 }
296                 break;
297         case KEY_AUTOANSWER: {
298                 struct chan_oss_pvt *o = find_desc(oss_active);
299                 o->autoanswer = !o->autoanswer;
300                 }
301                 break;
302 #endif
303         }
304         return NULL;
305 }
306
307 char *console_do_answer(int fd);
308 /*
309  * Function called when the pick up button is pressed
310  * perform actions according the channel status:
311  *
312  *  - if no one is calling us and no digits was pressed,
313  *    the operation have no effects,
314  *  - if someone is calling us we answer to the call.
315  *  - if we have no call in progress and we pressed some
316  *    digit, send the digit to the console.
317  */
318 static void keypad_pick_up(struct video_desc *env)
319 {
320         struct gui_info *gui = env->gui;
321
322         ast_log(LOG_WARNING, "keypad_pick_up called\n");
323
324         if (env->owner) { /* someone is calling us, just answer */
325                 ast_cli_command(gui->outfd, "console answer");
326         } else if (gui->inbuf_pos) { /* we have someone to call */
327                 ast_cli_command(gui->outfd, gui->inbuf);
328         }
329         append_char(gui->inbuf, &gui->inbuf_pos, '\0'); /* clear buffer */
330         append_string(gui->inbuf, &gui->inbuf_pos, "console dial ");
331 }
332
333 #if 0 /* still unused */
334 /*
335  * As an alternative to SDL_TTF, we can simply load the font from
336  * an image and blit characters on the background of the GUI.
337  *
338  * To generate a font we can use the 'fly' command with the
339  * following script (3 lines with 32 chars each)
340  
341 size 320,64
342 name font.png
343 transparent 0,0,0
344 string 255,255,255,  0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
345 string 255,255,255,  0,20,giant,@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
346 string 255,255,255,  0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
347 end
348
349  */
350
351 /* Print given text on the gui */
352 static int gui_output(struct video_desc *env, const char *text)
353 {
354 #ifndef HAVE_SDL_TTF
355         return 1;       /* error, not supported */
356 #else
357         int x = 30, y = 20;     /* XXX change */
358         SDL_Surface *output = NULL;
359         SDL_Color color = {0, 0, 0};    /* text color */
360         struct gui_info *gui = env->gui;
361         SDL_Rect dest = {gui->win[WIN_KEYPAD].rect.x + x, y};
362
363         /* clean surface each rewrite */
364         SDL_BlitSurface(gui->keypad, NULL, gui->screen, &gui->win[WIN_KEYPAD].rect);
365
366         output = TTF_RenderText_Solid(gui->font, text, color);
367         if (output == NULL) {
368                 ast_log(LOG_WARNING, "Cannot render text on gui - %s\n", TTF_GetError());
369                 return 1;
370         }
371
372         SDL_BlitSurface(output, NULL, gui->screen, &dest);
373         
374         SDL_UpdateRects(gui->keypad, 1, &gui->win[WIN_KEYPAD].rect);
375         SDL_FreeSurface(output);
376         return 0;       /* success */
377 #endif
378 }
379 #endif 
380
381 static int video_geom(struct fbuf_t *b, const char *s);
382 static void sdl_setup(struct video_desc *env);
383 static int kp_match_area(const struct keypad_entry *e, int x, int y);
384
385 /*
386  * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
387  * index value and calling the right callback.
388  *
389  * x, y are referred to the upper left corner of the main SDL window.
390  */
391 static void handle_button_event(struct video_desc *env, SDL_MouseButtonEvent button)
392 {
393         uint8_t index = KEY_OUT_OF_KEYPAD;      /* the key or region of the display we clicked on */
394         struct gui_info *gui = env->gui;
395
396 #if 0
397         ast_log(LOG_WARNING, "event %d %d have %d/%d regions at %p\n",
398                 button.x, button.y, gui->kp_used, gui->kp_size, gui->kp);
399 #endif
400         /* for each click we come back in normal mode */
401         gui->text_mode = 0;
402
403         /* define keypad boundary */
404         if (button.x < env->rem_dpy.w)
405                 index = KEY_REM_DPY; /* click on remote video */
406         else if (button.x > env->rem_dpy.w + gui->keypad->w)
407                 index = KEY_LOC_DPY; /* click on local video */
408         else if (button.y > gui->keypad->h)
409                 index = KEY_OUT_OF_KEYPAD; /* click outside the keypad */
410         else if (gui->kp) {
411                 int i;
412                 for (i = 0; i < gui->kp_used; i++) {
413                         if (kp_match_area(&gui->kp[i], button.x - env->rem_dpy.w, button.y)) {
414                                 index = gui->kp[i].c;
415                                 break;
416                         }
417                 }
418         }
419
420         /* exec the function */
421         if (index < 128) {      /* surely clicked on the keypad, don't care which key */
422                 keypad_digit(env, index);
423                 return;
424         }
425         switch (index) {
426         /* answer/close function */
427         case KEY_PICK_UP:
428                 keypad_pick_up(env);
429                 break;
430         case KEY_HANG_UP:
431                 ast_cli_command(gui->outfd, "console hangup");
432                 break;
433
434         /* other functions */
435         case KEY_MUTE:
436         case KEY_AUTOANSWER:
437         case KEY_SENDVIDEO:
438                 keypad_toggle(env, index);
439                 break;
440
441         case KEY_LOCALVIDEO:
442                 break;
443         case KEY_REMOTEVIDEO:
444                 break;
445         case KEY_WRITEMESSAGE:
446                 /* goes in text-mode */
447                 env->gui->text_mode = 1;
448                 break;
449
450
451         /* press outside the keypad. right increases size, center decreases, left drags */
452         case KEY_LOC_DPY:
453         case KEY_REM_DPY:
454                 if (button.button == SDL_BUTTON_LEFT) {
455                         if (index == KEY_LOC_DPY) {
456                                 /* store points where the drag start
457                                 * and switch in drag mode */
458                                 env->gui->x_drag = button.x;
459                                 env->gui->y_drag = button.y;
460                                 env->gui->drag_mode = 1;
461                         }
462                         break;
463                 } else {
464                         char buf[128];
465                         struct fbuf_t *fb = index == KEY_LOC_DPY ? &env->loc_dpy : &env->rem_dpy;
466                         sprintf(buf, "%c%dx%d", button.button == SDL_BUTTON_RIGHT ? '>' : '<',
467                                 fb->w, fb->h);
468                         video_geom(fb, buf);
469                         sdl_setup(env);
470                 }
471                 break;
472         case KEY_OUT_OF_KEYPAD:
473                 break;
474
475         case KEY_DIGIT_BACKGROUND:
476                 break;
477         default:
478                 ast_log(LOG_WARNING, "function not yet defined %i\n", index);
479         }
480 }
481
482 /*
483  * Handle SDL_KEYDOWN type event, put the key pressed
484  * in the dial buffer or in the text-message buffer,
485  * depending on the text_mode variable value.
486  *
487  * key is the SDLKey structure corresponding to the key pressed.
488  */
489 static void handle_keyboard_input(struct video_desc *env, SDLKey key)
490 {
491         struct gui_info *gui = env->gui;
492         if (gui->text_mode) {
493                 /* append in the text-message buffer */
494                 if (key == SDLK_RETURN) {
495                         /* send the text message and return in normal mode */
496                         gui->text_mode = 0;
497                         keypad_send_command(env, "send text");
498                 } else {
499                         /* accumulate the key in the message buffer */
500                         append_char(gui->msgbuf, &gui->msgbuf_pos, key);
501                 }
502         }
503         else {
504                 /* append in the dial buffer */
505                 append_char(gui->inbuf, &gui->inbuf_pos, key);
506         }
507
508         return;
509 }
510
511 /* implement superlinear acceleration on the movement */
512 static int move_accel(int delta)
513 {
514         int d1 = delta*delta / 100;
515         return (delta > 0) ? delta + d1 : delta - d1;
516 }
517
518 static void grabber_move(struct video_out_desc *, int dx, int dy);
519 /*
520  * Move the source of the captured video.
521  *
522  * x_final_drag and y_final_drag are the coordinates where the drag ends,
523  * start coordinares are in the gui_info structure.
524  */
525 static void move_capture_source(struct video_desc *env, int x_final_drag, int y_final_drag)
526 {
527         int dx, dy;
528
529         /* move the origin */
530 #define POLARITY -1             /* +1 or -1 depending on the desired direction */
531         dx = POLARITY*move_accel(x_final_drag - env->gui->x_drag) * 3;
532         dy = POLARITY*move_accel(y_final_drag - env->gui->y_drag) * 3;
533 #undef POLARITY
534         env->gui->x_drag = x_final_drag;        /* update origin */
535         env->gui->y_drag = y_final_drag;
536
537         grabber_move(&env->out, dx, dy);
538         return;
539 }
540
541 /*
542  * I am seeing some kind of deadlock or stall around
543  * SDL_PumpEvents() while moving the window on a remote X server
544  * (both xfree-4.4.0 and xorg 7.2)
545  * and windowmaker. It is unclear what causes it.
546  */
547
548 /*! \brief refresh the screen, and also grab a bunch of events.
549  */
550 static void eventhandler(struct video_desc *env, const char *caption)
551 {
552         struct gui_info *gui = env->gui;
553 #define N_EVENTS        32
554         int i, n;
555         SDL_Event ev[N_EVENTS];
556
557         if (!gui)
558                 return;
559         if (caption)
560                 SDL_WM_SetCaption(caption, NULL);
561
562 #define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
563         while ( (n = SDL_PeepEvents(ev, N_EVENTS, SDL_GETEVENT, SDL_ALLEVENTS)) > 0) {
564                 for (i = 0; i < n; i++) {
565 #if 0
566                         ast_log(LOG_WARNING, "------ event %d at %d %d\n",
567                                 ev[i].type,  ev[i].button.x,  ev[i].button.y);
568 #endif
569                         switch (ev[i].type) {
570                         case SDL_KEYDOWN:
571                                 handle_keyboard_input(env, ev[i].key.keysym.sym);
572                                 break;
573                         case SDL_MOUSEMOTION:
574                                 if (gui->drag_mode != 0)
575                                         move_capture_source(env, ev[i].motion.x, ev[i].motion.y);
576                                 break;
577                         case SDL_MOUSEBUTTONDOWN:
578                                 handle_button_event(env, ev[i].button);
579                                 break;
580                         case SDL_MOUSEBUTTONUP:
581                                 if (gui->drag_mode != 0) {
582                                         move_capture_source(env, ev[i].button.x, ev[i].button.y);
583                                         gui->drag_mode = 0;
584                                 }
585                                 break;
586                         }
587
588                 }
589         }
590         if (1) {
591                 struct timeval b, a = ast_tvnow();
592                 int i;
593                 //SDL_Lock_EventThread();
594                 SDL_PumpEvents();
595                 b = ast_tvnow();
596                 i = ast_tvdiff_ms(b, a);
597                 if (i > 3)
598                         fprintf(stderr, "-------- SDL_PumpEvents took %dms\n", i);
599                 //SDL_Unlock_EventThread();
600         }
601 }
602
603 static SDL_Surface *get_keypad(const char *file)
604 {
605         SDL_Surface *temp;
606  
607 #ifdef HAVE_SDL_IMAGE
608         temp = IMG_Load(file);
609 #else
610         temp = SDL_LoadBMP(file);
611 #endif
612         if (temp == NULL)
613                 fprintf(stderr, "Unable to load image %s: %s\n",
614                         file, SDL_GetError());
615         return temp;
616 }
617
618 static void keypad_setup(struct gui_info *gui, const char *kp_file);
619
620 /* TODO: consistency checks, check for bpp, widht and height */
621 /* Init the mask image used to grab the action. */
622 static struct gui_info *gui_init(const char *keypad_file)
623 {
624         struct gui_info *gui = ast_calloc(1, sizeof(*gui));
625
626         if (gui == NULL)
627                 return NULL;
628         /* initialize keypad status */
629         gui->text_mode = 0;
630         gui->drag_mode = 0;
631         gui->outfd = -1;
632
633         /* initialize keyboard buffer */
634         append_char(gui->inbuf, &gui->inbuf_pos, '\0');
635         append_string(gui->inbuf, &gui->inbuf_pos, "console dial ");
636         append_char(gui->msgbuf, &gui->msgbuf_pos, '\0');
637
638         keypad_setup(gui, keypad_file);
639         if (gui->keypad == NULL)        /* no keypad, we are done */
640                 return gui;
641 #ifdef HAVE_SDL_TTF
642         /* Initialize SDL_ttf library and load font */
643         if (TTF_Init() == -1) {
644                 ast_log(LOG_WARNING, "Unable to init SDL_ttf, no output available\n");
645                 goto error;
646         }
647
648 #define GUI_FONTSIZE 28
649         gui->font = TTF_OpenFont( env->keypad_font, GUI_FONTSIZE);
650         if (!gui->font) {
651                 ast_log(LOG_WARNING, "Unable to load font %s, no output available\n", env->keypad_font);
652                 goto error;
653         }
654         ast_log(LOG_WARNING, "Loaded font %s\n", env->keypad_font);
655 #endif
656
657         gui->outfd = open ("/dev/null", O_WRONLY);      /* discard output, temporary */
658         if (gui->outfd < 0) {
659                 ast_log(LOG_WARNING, "Unable output fd\n");
660                 goto error;
661         }
662         return gui;
663
664 error:
665         ast_free(gui);
666         return NULL;
667 }
668
669 /* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
670 static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
671         int w, int h, int x, int y)
672 {
673         win->bmp = SDL_CreateYUVOverlay(w, h, fmt, screen);
674         if (win->bmp == NULL)
675                 return -1;      /* error */
676         win->rect.x = x;
677         win->rect.y = y;
678         win->rect.w = w;
679         win->rect.h = h;
680         return 0;
681 }
682
683 static int keypad_cfg_read(struct gui_info *gui, const char *val);
684
685 static void keypad_setup(struct gui_info *gui, const char *kp_file)
686 {
687         FILE *fd;
688         char buf[1024];
689         const char region[] = "region";
690         int reg_len = strlen(region);
691         int in_comment = 0;
692
693         if (gui->keypad)
694                 return;
695         gui->keypad = get_keypad(kp_file);
696         if (!gui->keypad)
697                 return;
698         /* now try to read the keymap from the file. */
699         fd = fopen(kp_file, "r");
700         if (fd == NULL) {
701                 ast_log(LOG_WARNING, "fail to open %s\n", kp_file);
702                 return;
703         }
704         /*
705          * If the keypad image has a comment field, try to read
706          * the button location from there. The block must start with
707          * a comment (or empty) line, and continue with entries like:
708          *      region = token shape x0 y0 x1 y1 h
709          *      ...
710          * (basically, lines have the same format as config file entries).
711          * You can add it to a jpeg file using wrjpgcom
712          */
713         while (fgets(buf, sizeof(buf), fd)) {
714                 char *s;
715
716                 if (!strstr(buf, region)) { /* no keyword yet */
717                         if (!in_comment)        /* still waiting for initial comment block */
718                                 continue;
719                         else
720                                 break;
721                 }
722                 if (!in_comment) {      /* first keyword, reset previous entries */
723                         keypad_cfg_read(gui, "reset");
724                         in_comment = 1;
725                 }
726                 s = ast_skip_blanks(buf);
727                 ast_trim_blanks(s);
728                 if (memcmp(s, region, reg_len))
729                         break;  /* keyword not found */
730                 s = ast_skip_blanks(s + reg_len); /* space between token and '=' */
731                 if (*s++ != '=')        /* missing separator */
732                         break;
733                 if (*s == '>')  /* skip '>' if present */
734                         s++;
735                 keypad_cfg_read(gui, ast_skip_blanks(s));
736         }
737         fclose(fd);
738 }
739
740 /*! \brief [re]set the main sdl window, useful in case of resize.
741  * We can tell the first from subsequent calls from the value of
742  * env->gui, which is NULL the first time.
743  */
744 static void sdl_setup(struct video_desc *env)
745 {
746         int dpy_fmt = SDL_IYUV_OVERLAY; /* YV12 causes flicker in SDL */
747         int depth, maxw, maxh;
748         const SDL_VideoInfo *info;
749         int kp_w = 0, kp_h = 0; /* keypad width and height */
750         struct gui_info *gui = env->gui;
751
752         /*
753          * initialize the SDL environment. We have one large window
754          * with local and remote video, and a keypad.
755          * At the moment we arrange them statically, as follows:
756          * - on the left, the remote video;
757          * - on the center, the keypad
758          * - on the right, the local video
759          * We need to read in the skin for the keypad before creating the main
760          * SDL window, because the size is only known here.
761          */
762
763         if (gui == NULL && SDL_Init(SDL_INIT_VIDEO)) {
764                 ast_log(LOG_WARNING, "Could not initialize SDL - %s\n",
765                         SDL_GetError());
766                 /* again not fatal, just we won't display anything */
767                 return;
768         }
769         info = SDL_GetVideoInfo();
770         /* We want at least 16bpp to support YUV overlays.
771          * E.g with SDL_VIDEODRIVER = aalib the default is 8
772          */
773         depth = info->vfmt->BitsPerPixel;
774         if (depth < 16)
775                 depth = 16;
776         if (!gui)
777                 env->gui = gui = gui_init(env->keypad_file);
778         if (!gui)
779                 goto no_sdl;
780
781         if (gui->keypad) {
782                 kp_w = gui->keypad->w;
783                 kp_h = gui->keypad->h;
784         }
785 #define BORDER  5       /* border around our windows */
786         maxw = env->rem_dpy.w + env->loc_dpy.w + kp_w;
787         maxh = MAX( MAX(env->rem_dpy.h, env->loc_dpy.h), kp_h);
788         maxw += 4 * BORDER;
789         maxh += 2 * BORDER;
790         gui->screen = SDL_SetVideoMode(maxw, maxh, depth, 0);
791         if (!gui->screen) {
792                 ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
793                 goto no_sdl;
794         }
795
796         SDL_WM_SetCaption("Asterisk console Video Output", NULL);
797         if (set_win(gui->screen, &gui->win[WIN_REMOTE], dpy_fmt,
798                         env->rem_dpy.w, env->rem_dpy.h, BORDER, BORDER))
799                 goto no_sdl;
800         if (set_win(gui->screen, &gui->win[WIN_LOCAL], dpy_fmt,
801                         env->loc_dpy.w, env->loc_dpy.h,
802                         3*BORDER+env->rem_dpy.w + kp_w, BORDER))
803                 goto no_sdl;
804
805         /* display the skin, but do not free it as we need it later to
806          * restore text areas and maybe sliders too.
807          */
808         if (gui->keypad) {
809                 struct SDL_Rect *dest = &gui->win[WIN_KEYPAD].rect;
810                 dest->x = 2*BORDER + env->rem_dpy.w;
811                 dest->y = BORDER;
812                 dest->w = gui->keypad->w;
813                 dest->h = gui->keypad->h;
814                 SDL_BlitSurface(gui->keypad, NULL, gui->screen, dest);
815                 SDL_UpdateRects(gui->screen, 1, dest);
816         }
817         return;
818
819 no_sdl:
820         /* free resources in case of errors */
821         env->gui = cleanup_sdl(gui);
822 }
823
824 /*
825  * Functions to determine if a point is within a region. Return 1 if success.
826  * First rotate the point, with
827  *      x' =  (x - x0) * cos A + (y - y0) * sin A
828  *      y' = -(x - x0) * sin A + (y - y0) * cos A
829  * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
830  *      l = sqrt( (x1-x0)^2 + (y1-y0)^2
831  * Then determine inclusion by simple comparisons i.e.:
832  *      rectangle: x >= 0 && x < l && y >= 0 && y < h
833  *      ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
834  */
835 static int kp_match_area(const struct keypad_entry *e, int x, int y)
836 {
837         double xp, dx = (e->x1 - e->x0);
838         double yp, dy = (e->y1 - e->y0);
839         double l = sqrt(dx*dx + dy*dy);
840         int ret = 0;
841
842         if (l > 1) { /* large enough */
843                 xp = ((x - e->x0)*dx + (y - e->y0)*dy)/l;
844                 yp = (-(x - e->x0)*dy + (y - e->y0)*dx)/l;
845                 if (e->type == KP_RECT) {
846                         ret = (xp >= 0 && xp < l && yp >=0 && yp < l);
847                 } else if (e->type == KP_CIRCLE) {
848                         dx = xp*xp/(l*l) + yp*yp/(e->h*e->h);
849                         ret = (dx < 1);
850                 }
851         }
852 #if 0
853         ast_log(LOG_WARNING, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
854                 ret, e->c, x, y, e->type, e->x0, e->y0, e->x1, e->y1, e->h);
855 #endif
856         return ret;
857 }
858
859 struct _s_k { const char *s; int k; };
860 static struct _s_k gui_key_map[] = {
861         {"PICK_UP",     KEY_PICK_UP },
862         {"PICKUP",      KEY_PICK_UP },
863         {"HANG_UP",     KEY_HANG_UP },
864         {"HANGUP",      KEY_HANG_UP },
865         {"MUTE",        KEY_MUTE },
866         {"AUTOANSWER",  KEY_AUTOANSWER },
867         {"SENDVIDEO",   KEY_SENDVIDEO },
868         {"LOCALVIDEO",  KEY_LOCALVIDEO },
869         {"REMOTEVIDEO", KEY_REMOTEVIDEO },
870         {"WRITEMESSAGE", KEY_WRITEMESSAGE },
871         {"GUI_CLOSE",   KEY_GUI_CLOSE },
872         {NULL, 0 } };
873
874 /*! \brief read a keypad entry line in the format
875  *      reset
876  *      token circle xc yc diameter
877  *      token circle xc yc x1 y1 h      # ellipse, main diameter and height
878  *      token rect x0 y0 x1 y1 h        # rectangle with main side and eight
879  * token is the token to be returned, either a character or a symbol
880  * as KEY_* above
881  * Return 1 on success, 0 on error.
882  */
883 static int keypad_cfg_read(struct gui_info *gui, const char *val)
884 {
885         struct keypad_entry e;
886         char s1[16], s2[16];
887         int i, ret = 0;
888
889         if (gui == NULL || val == NULL)
890                 return 0;
891
892         bzero(&e, sizeof(e));
893         i = sscanf(val, "%14s %14s %d %d %d %d %d",
894                 s1, s2, &e.x0, &e.y0, &e.x1, &e.y1, &e.h);
895
896         switch (i) {
897         default:
898                 break;
899         case 1: /* only "reset" is allowed */
900                 if (strcasecmp(s1, "reset"))    /* invalid */
901                         break;
902                 if (gui->kp) {
903                         gui->kp_used = 0;
904                 }
905                 break;
906         case 5: /* token circle xc yc diameter */
907                 if (strcasecmp(s2, "circle"))   /* invalid */
908                         break;
909                 e.h = e.x1;
910                 e.y1 = e.y0;    /* map radius in x1 y1 */
911                 e.x1 = e.x0 + e.h;      /* map radius in x1 y1 */
912                 e.x0 = e.x0 - e.h;      /* map radius in x1 y1 */
913                 /* fallthrough */
914
915         case 7: /* token circle|rect x0 y0 x1 y1 h */
916                 if (e.x1 < e.x0 || e.h <= 0) {
917                         ast_log(LOG_WARNING, "error in coordinates\n");
918                         e.type = 0;
919                         break;
920                 }
921                 if (!strcasecmp(s2, "circle")) {
922                         /* for a circle we specify the diameter but store center and radii */
923                         e.type = KP_CIRCLE;
924                         e.x0 = (e.x1 + e.x0) / 2;
925                         e.y0 = (e.y1 + e.y0) / 2;
926                         e.h = e.h / 2;
927                 } else if (!strcasecmp(s2, "rect")) {
928                         e.type = KP_RECT;
929                 } else
930                         break;
931                 ret = 1;
932         }
933         // ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
934         if (ret == 0)
935                 return 0;
936         /* map the string into token to be returned */
937         i = atoi(s1);
938         if (i > 0 || s1[1] == '\0')     /* numbers or single characters */
939                 e.c = (i > 9) ? i : s1[0];
940         else {
941                 struct _s_k *p;
942                 for (p = gui_key_map; p->s; p++) {
943                         if (!strcasecmp(p->s, s1)) {
944                                 e.c = p->k;
945                                 break;
946                         }
947                 }
948         }
949         if (e.c == 0) {
950                 ast_log(LOG_WARNING, "missing token\n");
951                 return 0;
952         }
953         if (gui->kp_size == 0) {
954                 gui->kp = ast_calloc(10, sizeof(e));
955                 if (gui->kp == NULL) {
956                         ast_log(LOG_WARNING, "cannot allocate kp");
957                         return 0;
958                 }
959                 gui->kp_size = 10;
960         }
961         if (gui->kp_size == gui->kp_used) { /* must allocate */
962                 struct keypad_entry *a = ast_realloc(gui->kp, sizeof(e)*(gui->kp_size+10));
963                 if (a == NULL) {
964                         ast_log(LOG_WARNING, "cannot reallocate kp");
965                         return 0;
966                 }
967                 gui->kp = a;
968                 gui->kp_size += 10;
969         }
970         if (gui->kp_size == gui->kp_used)
971                 return 0;
972         gui->kp[gui->kp_used++] = e;
973         // ast_log(LOG_WARNING, "now %d regions\n", gui->kp_used);
974         return 1;
975 }
976 #endif  /* HAVE_SDL */