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