call directly the cli command to implement hangup.
[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 /* accumulate digits, possibly call dial if in connected mode */
255 static void keypad_digit(struct video_desc *env, int digit)
256 {       
257         if (env->owner) {               /* we have a call, send the digit */
258                 struct ast_frame f = { AST_FRAME_DTMF, 0 };
259
260                 f.subclass = digit;
261                 ast_queue_frame(env->owner, &f);
262         } else {                /* no call, accumulate digits */
263                 append_char(env->gui->inbuf, &env->gui->inbuf_pos, digit);
264         }
265 }
266
267 /* this is a wrapper for actions that are available through the cli */
268 /* TODO append arg to command and send the resulting string as cli command */
269 static void keypad_send_command(struct video_desc *env, char *command)
270 {       
271         ast_log(LOG_WARNING, "keypad_send_command(%s) called\n", command);
272         ast_cli_command(env->gui->outfd, command);
273         return;
274 }
275
276 /* function used to toggle on/off the status of some variables */
277 static char *keypad_toggle(struct video_desc *env, int index)
278 {
279         ast_log(LOG_WARNING, "keypad_toggle(%i) called\n", index);
280
281         switch (index) {
282         case KEY_SENDVIDEO:
283                 env->out.sendvideo = !env->out.sendvideo;
284                 break;
285 #ifdef notyet
286         case KEY_MUTE: {
287                 struct chan_oss_pvt *o = find_desc(oss_active);
288                 o->mute = !o->mute;
289                 }
290                 break;
291         case KEY_AUTOANSWER: {
292                 struct chan_oss_pvt *o = find_desc(oss_active);
293                 o->autoanswer = !o->autoanswer;
294                 }
295                 break;
296 #endif
297         }
298         return NULL;
299 }
300
301 char *console_do_answer(int fd);
302 /*
303  * Function called when the pick up button is pressed
304  * perform actions according the channel status:
305  *
306  *  - if no one is calling us and no digits was pressed,
307  *    the operation have no effects,
308  *  - if someone is calling us we answer to the call.
309  *  - if we have no call in progress and we pressed some
310  *    digit, send the digit to the console.
311  */
312 static void keypad_pick_up(struct video_desc *env)
313 {
314         struct gui_info *gui = env->gui;
315
316         ast_log(LOG_WARNING, "keypad_pick_up called\n");
317
318         if (env->owner) { /* someone is calling us, just answer */
319                 console_do_answer(-1);
320         } else if (gui->inbuf_pos) { /* we have someone to call */
321                 ast_cli_command(gui->outfd, gui->inbuf);
322         }
323
324         append_char(gui->inbuf, &gui->inbuf_pos, '\0'); /* clear buffer */
325 }
326
327 #if 0 /* still unused */
328 /*
329  * As an alternative to SDL_TTF, we can simply load the font from
330  * an image and blit characters on the background of the GUI.
331  *
332  * To generate a font we can use the 'fly' command with the
333  * following script (3 lines with 32 chars each)
334  
335 size 320,64
336 name font.png
337 transparent 0,0,0
338 string 255,255,255,  0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
339 string 255,255,255,  0,20,giant,@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
340 string 255,255,255,  0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
341 end
342
343  */
344
345 /* Print given text on the gui */
346 static int gui_output(struct video_desc *env, const char *text)
347 {
348 #ifndef HAVE_SDL_TTF
349         return 1;       /* error, not supported */
350 #else
351         int x = 30, y = 20;     /* XXX change */
352         SDL_Surface *output = NULL;
353         SDL_Color color = {0, 0, 0};    /* text color */
354         struct gui_info *gui = env->gui;
355         SDL_Rect dest = {gui->win[WIN_KEYPAD].rect.x + x, y};
356
357         /* clean surface each rewrite */
358         SDL_BlitSurface(gui->keypad, NULL, gui->screen, &gui->win[WIN_KEYPAD].rect);
359
360         output = TTF_RenderText_Solid(gui->font, text, color);
361         if (output == NULL) {
362                 ast_log(LOG_WARNING, "Cannot render text on gui - %s\n", TTF_GetError());
363                 return 1;
364         }
365
366         SDL_BlitSurface(output, NULL, gui->screen, &dest);
367         
368         SDL_UpdateRects(gui->keypad, 1, &gui->win[WIN_KEYPAD].rect);
369         SDL_FreeSurface(output);
370         return 0;       /* success */
371 #endif
372 }
373 #endif 
374
375 static int video_geom(struct fbuf_t *b, const char *s);
376 static void sdl_setup(struct video_desc *env);
377 static int kp_match_area(const struct keypad_entry *e, int x, int y);
378
379 /*
380  * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
381  * index value and calling the right callback.
382  *
383  * x, y are referred to the upper left corner of the main SDL window.
384  */
385 static void handle_button_event(struct video_desc *env, SDL_MouseButtonEvent button)
386 {
387         uint8_t index = KEY_OUT_OF_KEYPAD;      /* the key or region of the display we clicked on */
388         struct gui_info *gui = env->gui;
389
390 #if 0
391         ast_log(LOG_WARNING, "event %d %d have %d/%d regions at %p\n",
392                 button.x, button.y, gui->kp_used, gui->kp_size, gui->kp);
393 #endif
394         /* for each click we come back in normal mode */
395         gui->text_mode = 0;
396
397         /* define keypad boundary */
398         if (button.x < env->rem_dpy.w)
399                 index = KEY_REM_DPY; /* click on remote video */
400         else if (button.x > env->rem_dpy.w + gui->keypad->w)
401                 index = KEY_LOC_DPY; /* click on local video */
402         else if (button.y > gui->keypad->h)
403                 index = KEY_OUT_OF_KEYPAD; /* click outside the keypad */
404         else if (gui->kp) {
405                 int i;
406                 for (i = 0; i < gui->kp_used; i++) {
407                         if (kp_match_area(&gui->kp[i], button.x - env->rem_dpy.w, button.y)) {
408                                 index = gui->kp[i].c;
409                                 break;
410                         }
411                 }
412         }
413
414         /* exec the function */
415         if (index < 128) {      /* surely clicked on the keypad, don't care which key */
416                 keypad_digit(env, index);
417                 return;
418         }
419         switch (index) {
420         /* answer/close function */
421         case KEY_PICK_UP:
422                 keypad_pick_up(env);
423                 break;
424         case KEY_HANG_UP:
425                 ast_cli_command(gui->outfd, "console hangup");
426                 break;
427
428         /* other functions */
429         case KEY_MUTE:
430         case KEY_AUTOANSWER:
431         case KEY_SENDVIDEO:
432                 keypad_toggle(env, index);
433                 break;
434
435         case KEY_LOCALVIDEO:
436                 break;
437         case KEY_REMOTEVIDEO:
438                 break;
439         case KEY_WRITEMESSAGE:
440                 /* goes in text-mode */
441                 env->gui->text_mode = 1;
442                 break;
443
444
445         /* press outside the keypad. right increases size, center decreases, left drags */
446         case KEY_LOC_DPY:
447         case KEY_REM_DPY:
448                 if (button.button == SDL_BUTTON_LEFT) {
449                         if (index == KEY_LOC_DPY) {
450                                 /* store points where the drag start
451                                 * and switch in drag mode */
452                                 env->gui->x_drag = button.x;
453                                 env->gui->y_drag = button.y;
454                                 env->gui->drag_mode = 1;
455                         }
456                         break;
457                 } else {
458                         char buf[128];
459                         struct fbuf_t *fb = index == KEY_LOC_DPY ? &env->loc_dpy : &env->rem_dpy;
460                         sprintf(buf, "%c%dx%d", button.button == SDL_BUTTON_RIGHT ? '>' : '<',
461                                 fb->w, fb->h);
462                         video_geom(fb, buf);
463                         sdl_setup(env);
464                 }
465                 break;
466         case KEY_OUT_OF_KEYPAD:
467                 break;
468
469         case KEY_DIGIT_BACKGROUND:
470                 break;
471         default:
472                 ast_log(LOG_WARNING, "function not yet defined %i\n", index);
473         }
474 }
475
476 /*
477  * Handle SDL_KEYDOWN type event, put the key pressed
478  * in the dial buffer or in the text-message buffer,
479  * depending on the text_mode variable value.
480  *
481  * key is the SDLKey structure corresponding to the key pressed.
482  */
483 static void handle_keyboard_input(struct video_desc *env, SDLKey key)
484 {
485         struct gui_info *gui = env->gui;
486         if (gui->text_mode) {
487                 /* append in the text-message buffer */
488                 if (key == SDLK_RETURN) {
489                         /* send the text message and return in normal mode */
490                         gui->text_mode = 0;
491                         keypad_send_command(env, "send text");
492                 } else {
493                         /* accumulate the key in the message buffer */
494                         append_char(gui->msgbuf, &gui->msgbuf_pos, key);
495                 }
496         }
497         else {
498                 /* append in the dial buffer */
499                 append_char(gui->inbuf, &gui->inbuf_pos, key);
500         }
501
502         return;
503 }
504
505 /* implement superlinear acceleration on the movement */
506 static int move_accel(int delta)
507 {
508         int d1 = delta*delta / 100;
509         return (delta > 0) ? delta + d1 : delta - d1;
510 }
511
512 static void grabber_move(struct video_out_desc *, int dx, int dy);
513 /*
514  * Move the source of the captured video.
515  *
516  * x_final_drag and y_final_drag are the coordinates where the drag ends,
517  * start coordinares are in the gui_info structure.
518  */
519 static void move_capture_source(struct video_desc *env, int x_final_drag, int y_final_drag)
520 {
521         int dx, dy;
522
523         /* move the origin */
524 #define POLARITY -1             /* +1 or -1 depending on the desired direction */
525         dx = POLARITY*move_accel(x_final_drag - env->gui->x_drag) * 3;
526         dy = POLARITY*move_accel(y_final_drag - env->gui->y_drag) * 3;
527 #undef POLARITY
528         env->gui->x_drag = x_final_drag;        /* update origin */
529         env->gui->y_drag = y_final_drag;
530
531         grabber_move(&env->out, dx, dy);
532         return;
533 }
534
535 /*
536  * I am seeing some kind of deadlock or stall around
537  * SDL_PumpEvents() while moving the window on a remote X server
538  * (both xfree-4.4.0 and xorg 7.2)
539  * and windowmaker. It is unclear what causes it.
540  */
541
542 /*! \brief refresh the screen, and also grab a bunch of events.
543  */
544 static void eventhandler(struct video_desc *env, const char *caption)
545 {
546         struct gui_info *gui = env->gui;
547 #define N_EVENTS        32
548         int i, n;
549         SDL_Event ev[N_EVENTS];
550
551         if (!gui)
552                 return;
553         if (caption)
554                 SDL_WM_SetCaption(caption, NULL);
555
556 #define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
557         while ( (n = SDL_PeepEvents(ev, N_EVENTS, SDL_GETEVENT, SDL_ALLEVENTS)) > 0) {
558                 for (i = 0; i < n; i++) {
559 #if 0
560                         ast_log(LOG_WARNING, "------ event %d at %d %d\n",
561                                 ev[i].type,  ev[i].button.x,  ev[i].button.y);
562 #endif
563                         switch (ev[i].type) {
564                         case SDL_KEYDOWN:
565                                 handle_keyboard_input(env, ev[i].key.keysym.sym);
566                                 break;
567                         case SDL_MOUSEMOTION:
568                                 if (gui->drag_mode != 0)
569                                         move_capture_source(env, ev[i].motion.x, ev[i].motion.y);
570                                 break;
571                         case SDL_MOUSEBUTTONDOWN:
572                                 handle_button_event(env, ev[i].button);
573                                 break;
574                         case SDL_MOUSEBUTTONUP:
575                                 if (gui->drag_mode != 0) {
576                                         move_capture_source(env, ev[i].button.x, ev[i].button.y);
577                                         gui->drag_mode = 0;
578                                 }
579                                 break;
580                         }
581
582                 }
583         }
584         if (1) {
585                 struct timeval b, a = ast_tvnow();
586                 int i;
587                 //SDL_Lock_EventThread();
588                 SDL_PumpEvents();
589                 b = ast_tvnow();
590                 i = ast_tvdiff_ms(b, a);
591                 if (i > 3)
592                         fprintf(stderr, "-------- SDL_PumpEvents took %dms\n", i);
593                 //SDL_Unlock_EventThread();
594         }
595 }
596
597 static SDL_Surface *get_keypad(const char *file)
598 {
599         SDL_Surface *temp;
600  
601 #ifdef HAVE_SDL_IMAGE
602         temp = IMG_Load(file);
603 #else
604         temp = SDL_LoadBMP(file);
605 #endif
606         if (temp == NULL)
607                 fprintf(stderr, "Unable to load image %s: %s\n",
608                         file, SDL_GetError());
609         return temp;
610 }
611
612 static void keypad_setup(struct gui_info *gui, const char *kp_file);
613
614 /* TODO: consistency checks, check for bpp, widht and height */
615 /* Init the mask image used to grab the action. */
616 static struct gui_info *gui_init(const char *keypad_file)
617 {
618         struct gui_info *gui = ast_calloc(1, sizeof(*gui));
619
620         if (gui == NULL)
621                 return NULL;
622         /* initialize keypad status */
623         gui->text_mode = 0;
624         gui->drag_mode = 0;
625         gui->outfd = -1;
626
627         /* initialize keyboard buffer */
628         append_char(gui->inbuf, &gui->inbuf_pos, '\0');
629         append_char(gui->msgbuf, &gui->msgbuf_pos, '\0');
630
631         keypad_setup(gui, keypad_file);
632         if (gui->keypad == NULL)        /* no keypad, we are done */
633                 return gui;
634 #ifdef HAVE_SDL_TTF
635         /* Initialize SDL_ttf library and load font */
636         if (TTF_Init() == -1) {
637                 ast_log(LOG_WARNING, "Unable to init SDL_ttf, no output available\n");
638                 goto error;
639         }
640
641 #define GUI_FONTSIZE 28
642         gui->font = TTF_OpenFont( env->keypad_font, GUI_FONTSIZE);
643         if (!gui->font) {
644                 ast_log(LOG_WARNING, "Unable to load font %s, no output available\n", env->keypad_font);
645                 goto error;
646         }
647         ast_log(LOG_WARNING, "Loaded font %s\n", env->keypad_font);
648 #endif
649
650         gui->outfd = open ("/dev/null", O_WRONLY);      /* discard output, temporary */
651         if (gui->outfd < 0) {
652                 ast_log(LOG_WARNING, "Unable output fd\n");
653                 goto error;
654         }
655         return gui;
656
657 error:
658         ast_free(gui);
659         return NULL;
660 }
661
662 /* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
663 static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
664         int w, int h, int x, int y)
665 {
666         win->bmp = SDL_CreateYUVOverlay(w, h, fmt, screen);
667         if (win->bmp == NULL)
668                 return -1;      /* error */
669         win->rect.x = x;
670         win->rect.y = y;
671         win->rect.w = w;
672         win->rect.h = h;
673         return 0;
674 }
675
676 static int keypad_cfg_read(struct gui_info *gui, const char *val);
677
678 static void keypad_setup(struct gui_info *gui, const char *kp_file)
679 {
680         FILE *fd;
681         char buf[1024];
682         const char region[] = "region";
683         int reg_len = strlen(region);
684         int in_comment = 0;
685
686         if (gui->keypad)
687                 return;
688         gui->keypad = get_keypad(kp_file);
689         if (!gui->keypad)
690                 return;
691         /* now try to read the keymap from the file. */
692         fd = fopen(kp_file, "r");
693         if (fd == NULL) {
694                 ast_log(LOG_WARNING, "fail to open %s\n", kp_file);
695                 return;
696         }
697         /*
698          * If the keypad image has a comment field, try to read
699          * the button location from there. The block must start with
700          * a comment (or empty) line, and continue with entries like:
701          *      region = token shape x0 y0 x1 y1 h
702          *      ...
703          * (basically, lines have the same format as config file entries).
704          * You can add it to a jpeg file using wrjpgcom
705          */
706         while (fgets(buf, sizeof(buf), fd)) {
707                 char *s;
708
709                 if (!strstr(buf, region)) { /* no keyword yet */
710                         if (!in_comment)        /* still waiting for initial comment block */
711                                 continue;
712                         else
713                                 break;
714                 }
715                 if (!in_comment) {      /* first keyword, reset previous entries */
716                         keypad_cfg_read(gui, "reset");
717                         in_comment = 1;
718                 }
719                 s = ast_skip_blanks(buf);
720                 ast_trim_blanks(s);
721                 if (memcmp(s, region, reg_len))
722                         break;  /* keyword not found */
723                 s = ast_skip_blanks(s + reg_len); /* space between token and '=' */
724                 if (*s++ != '=')        /* missing separator */
725                         break;
726                 if (*s == '>')  /* skip '>' if present */
727                         s++;
728                 keypad_cfg_read(gui, ast_skip_blanks(s));
729         }
730         fclose(fd);
731 }
732
733 /*! \brief [re]set the main sdl window, useful in case of resize.
734  * We can tell the first from subsequent calls from the value of
735  * env->gui, which is NULL the first time.
736  */
737 static void sdl_setup(struct video_desc *env)
738 {
739         int dpy_fmt = SDL_IYUV_OVERLAY; /* YV12 causes flicker in SDL */
740         int depth, maxw, maxh;
741         const SDL_VideoInfo *info;
742         int kp_w = 0, kp_h = 0; /* keypad width and height */
743         struct gui_info *gui = env->gui;
744
745         /*
746          * initialize the SDL environment. We have one large window
747          * with local and remote video, and a keypad.
748          * At the moment we arrange them statically, as follows:
749          * - on the left, the remote video;
750          * - on the center, the keypad
751          * - on the right, the local video
752          * We need to read in the skin for the keypad before creating the main
753          * SDL window, because the size is only known here.
754          */
755
756         if (gui == NULL && SDL_Init(SDL_INIT_VIDEO)) {
757                 ast_log(LOG_WARNING, "Could not initialize SDL - %s\n",
758                         SDL_GetError());
759                 /* again not fatal, just we won't display anything */
760                 return;
761         }
762         info = SDL_GetVideoInfo();
763         /* We want at least 16bpp to support YUV overlays.
764          * E.g with SDL_VIDEODRIVER = aalib the default is 8
765          */
766         depth = info->vfmt->BitsPerPixel;
767         if (depth < 16)
768                 depth = 16;
769         if (!gui)
770                 env->gui = gui = gui_init(env->keypad_file);
771         if (!gui)
772                 goto no_sdl;
773
774         if (gui->keypad) {
775                 kp_w = gui->keypad->w;
776                 kp_h = gui->keypad->h;
777         }
778 #define BORDER  5       /* border around our windows */
779         maxw = env->rem_dpy.w + env->loc_dpy.w + kp_w;
780         maxh = MAX( MAX(env->rem_dpy.h, env->loc_dpy.h), kp_h);
781         maxw += 4 * BORDER;
782         maxh += 2 * BORDER;
783         gui->screen = SDL_SetVideoMode(maxw, maxh, depth, 0);
784         if (!gui->screen) {
785                 ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
786                 goto no_sdl;
787         }
788
789         SDL_WM_SetCaption("Asterisk console Video Output", NULL);
790         if (set_win(gui->screen, &gui->win[WIN_REMOTE], dpy_fmt,
791                         env->rem_dpy.w, env->rem_dpy.h, BORDER, BORDER))
792                 goto no_sdl;
793         if (set_win(gui->screen, &gui->win[WIN_LOCAL], dpy_fmt,
794                         env->loc_dpy.w, env->loc_dpy.h,
795                         3*BORDER+env->rem_dpy.w + kp_w, BORDER))
796                 goto no_sdl;
797
798         /* display the skin, but do not free it as we need it later to
799          * restore text areas and maybe sliders too.
800          */
801         if (gui->keypad) {
802                 struct SDL_Rect *dest = &gui->win[WIN_KEYPAD].rect;
803                 dest->x = 2*BORDER + env->rem_dpy.w;
804                 dest->y = BORDER;
805                 dest->w = gui->keypad->w;
806                 dest->h = gui->keypad->h;
807                 SDL_BlitSurface(gui->keypad, NULL, gui->screen, dest);
808                 SDL_UpdateRects(gui->screen, 1, dest);
809         }
810         return;
811
812 no_sdl:
813         /* free resources in case of errors */
814         env->gui = cleanup_sdl(gui);
815 }
816
817 /*
818  * Functions to determine if a point is within a region. Return 1 if success.
819  * First rotate the point, with
820  *      x' =  (x - x0) * cos A + (y - y0) * sin A
821  *      y' = -(x - x0) * sin A + (y - y0) * cos A
822  * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
823  *      l = sqrt( (x1-x0)^2 + (y1-y0)^2
824  * Then determine inclusion by simple comparisons i.e.:
825  *      rectangle: x >= 0 && x < l && y >= 0 && y < h
826  *      ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
827  */
828 static int kp_match_area(const struct keypad_entry *e, int x, int y)
829 {
830         double xp, dx = (e->x1 - e->x0);
831         double yp, dy = (e->y1 - e->y0);
832         double l = sqrt(dx*dx + dy*dy);
833         int ret = 0;
834
835         if (l > 1) { /* large enough */
836                 xp = ((x - e->x0)*dx + (y - e->y0)*dy)/l;
837                 yp = (-(x - e->x0)*dy + (y - e->y0)*dx)/l;
838                 if (e->type == KP_RECT) {
839                         ret = (xp >= 0 && xp < l && yp >=0 && yp < l);
840                 } else if (e->type == KP_CIRCLE) {
841                         dx = xp*xp/(l*l) + yp*yp/(e->h*e->h);
842                         ret = (dx < 1);
843                 }
844         }
845 #if 0
846         ast_log(LOG_WARNING, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
847                 ret, e->c, x, y, e->type, e->x0, e->y0, e->x1, e->y1, e->h);
848 #endif
849         return ret;
850 }
851
852 struct _s_k { const char *s; int k; };
853 static struct _s_k gui_key_map[] = {
854         {"PICK_UP",     KEY_PICK_UP },
855         {"PICKUP",      KEY_PICK_UP },
856         {"HANG_UP",     KEY_HANG_UP },
857         {"HANGUP",      KEY_HANG_UP },
858         {"MUTE",        KEY_MUTE },
859         {"AUTOANSWER",  KEY_AUTOANSWER },
860         {"SENDVIDEO",   KEY_SENDVIDEO },
861         {"LOCALVIDEO",  KEY_LOCALVIDEO },
862         {"REMOTEVIDEO", KEY_REMOTEVIDEO },
863         {"WRITEMESSAGE", KEY_WRITEMESSAGE },
864         {"GUI_CLOSE",   KEY_GUI_CLOSE },
865         {NULL, 0 } };
866
867 /*! \brief read a keypad entry line in the format
868  *      reset
869  *      token circle xc yc diameter
870  *      token circle xc yc x1 y1 h      # ellipse, main diameter and height
871  *      token rect x0 y0 x1 y1 h        # rectangle with main side and eight
872  * token is the token to be returned, either a character or a symbol
873  * as KEY_* above
874  * Return 1 on success, 0 on error.
875  */
876 static int keypad_cfg_read(struct gui_info *gui, const char *val)
877 {
878         struct keypad_entry e;
879         char s1[16], s2[16];
880         int i, ret = 0;
881
882         if (gui == NULL || val == NULL)
883                 return 0;
884
885         bzero(&e, sizeof(e));
886         i = sscanf(val, "%14s %14s %d %d %d %d %d",
887                 s1, s2, &e.x0, &e.y0, &e.x1, &e.y1, &e.h);
888
889         switch (i) {
890         default:
891                 break;
892         case 1: /* only "reset" is allowed */
893                 if (strcasecmp(s1, "reset"))    /* invalid */
894                         break;
895                 if (gui->kp) {
896                         gui->kp_used = 0;
897                 }
898                 break;
899         case 5: /* token circle xc yc diameter */
900                 if (strcasecmp(s2, "circle"))   /* invalid */
901                         break;
902                 e.h = e.x1;
903                 e.y1 = e.y0;    /* map radius in x1 y1 */
904                 e.x1 = e.x0 + e.h;      /* map radius in x1 y1 */
905                 e.x0 = e.x0 - e.h;      /* map radius in x1 y1 */
906                 /* fallthrough */
907
908         case 7: /* token circle|rect x0 y0 x1 y1 h */
909                 if (e.x1 < e.x0 || e.h <= 0) {
910                         ast_log(LOG_WARNING, "error in coordinates\n");
911                         e.type = 0;
912                         break;
913                 }
914                 if (!strcasecmp(s2, "circle")) {
915                         /* for a circle we specify the diameter but store center and radii */
916                         e.type = KP_CIRCLE;
917                         e.x0 = (e.x1 + e.x0) / 2;
918                         e.y0 = (e.y1 + e.y0) / 2;
919                         e.h = e.h / 2;
920                 } else if (!strcasecmp(s2, "rect")) {
921                         e.type = KP_RECT;
922                 } else
923                         break;
924                 ret = 1;
925         }
926         // ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
927         if (ret == 0)
928                 return 0;
929         /* map the string into token to be returned */
930         i = atoi(s1);
931         if (i > 0 || s1[1] == '\0')     /* numbers or single characters */
932                 e.c = (i > 9) ? i : s1[0];
933         else {
934                 struct _s_k *p;
935                 for (p = gui_key_map; p->s; p++) {
936                         if (!strcasecmp(p->s, s1)) {
937                                 e.c = p->k;
938                                 break;
939                         }
940                 }
941         }
942         if (e.c == 0) {
943                 ast_log(LOG_WARNING, "missing token\n");
944                 return 0;
945         }
946         if (gui->kp_size == 0) {
947                 gui->kp = ast_calloc(10, sizeof(e));
948                 if (gui->kp == NULL) {
949                         ast_log(LOG_WARNING, "cannot allocate kp");
950                         return 0;
951                 }
952                 gui->kp_size = 10;
953         }
954         if (gui->kp_size == gui->kp_used) { /* must allocate */
955                 struct keypad_entry *a = ast_realloc(gui->kp, sizeof(e)*(gui->kp_size+10));
956                 if (a == NULL) {
957                         ast_log(LOG_WARNING, "cannot reallocate kp");
958                         return 0;
959                 }
960                 gui->kp = a;
961                 gui->kp_size += 10;
962         }
963         if (gui->kp_size == gui->kp_used)
964                 return 0;
965         gui->kp[gui->kp_used++] = e;
966         // ast_log(LOG_WARNING, "now %d regions\n", gui->kp_used);
967         return 1;
968 }
969 #endif  /* HAVE_SDL */