do not die on SDL_ACTIVEEVENT reporting lost focus.
[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 /*
8  * GUI layout, structure and management
9  
10 For the GUI we use SDL to create a large surface (gui->screen)
11 containing tree sections: remote video on the left, local video
12 on the right, and the keypad with all controls and text windows
13 in the center.
14 The central section is built using an image for the skin, fonts and
15 other GUI elements.  Comments embedded in the image to indicate to
16 what function each area is mapped to.
17
18 Mouse and keyboard events are detected on the whole surface, and
19 handled differently according to their location:
20
21 - drag on the local video window are used to move the captured
22   area (in the case of X11 grabber) or the picture-in-picture
23   location (in case of camera included on the X11 grab).
24 - click on the keypad are mapped to the corresponding key;
25 - drag on some keypad areas (sliders etc.) are mapped to the
26   corresponding functions;
27 - keystrokes are used as keypad functions, or as text input
28   if we are in text-input mode.
29
30 Configuration options control the appeareance of the gui:
31
32     keypad = /tmp/phone.jpg     ; the skin
33     keypad_font = /tmp/font.ttf ; the font to use for output (XXX deprecated)
34
35  *
36  */
37
38 #include "asterisk.h"
39 #include "console_video.h"
40 #include "asterisk/lock.h"
41 #include "asterisk/frame.h"
42 #include "asterisk/utils.h"     /* ast_calloc and ast_realloc */
43 #include <math.h>               /* sqrt */
44
45 /* We use 3 'windows' in the GUI */
46 enum { WIN_LOCAL, WIN_REMOTE, WIN_KEYPAD, WIN_MAX };
47
48 #ifndef HAVE_SDL        /* stubs if we don't have any sdl */
49 static void show_frame(struct video_desc *env, int out) {}
50 static void sdl_setup(struct video_desc *env)           {}
51 static struct gui_info *cleanup_sdl(struct gui_info *gui)       { return NULL; }
52 static void eventhandler(struct video_desc *env, const char *caption)   {}
53 static int keypad_cfg_read(struct gui_info *gui, const char *val)       { return 0; }
54
55 #else /* HAVE_SDL, the real rendering code */
56
57 #include <SDL/SDL.h>
58 #include <SDL/SDL_syswm.h>
59 #ifdef HAVE_SDL_IMAGE
60 #include <SDL/SDL_image.h>      /* for loading images */
61 #endif
62
63 #ifdef HAVE_X11
64 /* Need to hook into X for SDL_WINDOWID handling */
65 #include <X11/Xlib.h>
66 #endif
67
68 enum kp_type { KP_NONE, KP_RECT, KP_CIRCLE };
69 struct keypad_entry {
70         int c;  /* corresponding character */
71         int x0, y0, x1, y1, h;  /* arguments */
72         enum kp_type type;
73 };
74
75 /* our representation of a displayed window. SDL can only do one main
76  * window so we map everything within that one
77  */
78 struct display_window   {   
79         SDL_Overlay     *bmp;
80         SDL_Rect        rect;   /* location of the window */
81 };
82
83 struct gui_info {
84         enum kb_output          kb_output;      /* where the keyboard output goes */
85         struct drag_info        drag;           /* info on the window are we dragging */
86         /* support for display. */
87         SDL_Surface             *screen;        /* the main window */
88
89         int                     outfd;          /* fd for output */
90         SDL_Surface             *keypad;        /* the skin for the keypad */
91         SDL_Rect                kp_rect;        /* portion of the skin to display - default all */
92         SDL_Surface             *font;          /* font to be used */ 
93         SDL_Rect                font_rects[96]; /* only printable chars */
94
95         /* each board has two rectangles,
96          * [0] is the geometry relative to the keypad,
97          * [1] is the geometry relative to the whole screen
98          */
99         SDL_Rect                kp_msg[2];              /* incoming msg, relative to kpad */
100         struct board            *bd_msg;
101
102         SDL_Rect                kp_edit[2];     /* edit user input */
103         struct board            *bd_edit;
104
105         SDL_Rect                kp_dialed[2];   /* dialed number */
106         struct board            *bd_dialed;
107
108         /* variable-size array mapping keypad regions to functions */
109         int kp_size, kp_used;
110         struct keypad_entry *kp;
111
112         struct display_window   win[WIN_MAX];
113 };
114
115 /*! \brief free the resources in struct gui_info and the descriptor itself.
116  *  Return NULL so we can assign the value back to the descriptor in case.
117  */
118 static struct gui_info *cleanup_sdl(struct gui_info *gui)
119 {
120         int i;
121
122         if (gui == NULL)
123                 return NULL;
124
125         /* unload font file */ 
126         if (gui->font) {
127                 SDL_FreeSurface(gui->font);
128                 gui->font = NULL; 
129         }
130
131         if (gui->outfd > -1)
132                 close(gui->outfd);
133         if (gui->keypad)
134                 SDL_FreeSurface(gui->keypad);
135         gui->keypad = NULL;
136         if (gui->kp)
137                 ast_free(gui->kp);
138
139         /* uninitialize the SDL environment */
140         for (i = 0; i < WIN_MAX; i++) {
141                 if (gui->win[i].bmp)
142                         SDL_FreeYUVOverlay(gui->win[i].bmp);
143         }
144         bzero(gui, sizeof(gui));
145         ast_free(gui);
146         SDL_Quit();
147         return NULL;
148 }
149
150 /*
151  * Display video frames (from local or remote stream) using the SDL library.
152  * - Set the video mode to use the resolution specified by the codec context
153  * - Create a YUV Overlay to copy the frame into it;
154  * - After the frame is copied into the overlay, display it
155  *
156  * The size is taken from the configuration.
157  *
158  * 'out' is 0 for remote video, 1 for the local video
159  */
160 static void show_frame(struct video_desc *env, int out)
161 {
162         AVPicture *p_in, p_out;
163         struct fbuf_t *b_in, *b_out;
164         SDL_Overlay *bmp;
165         struct gui_info *gui = env->gui;
166
167         if (!gui)
168                 return;
169
170         if (out == WIN_LOCAL) { /* webcam/x11 to sdl */
171                 b_in = &env->enc_in;
172                 b_out = &env->loc_dpy;
173                 p_in = NULL;
174         } else {
175                 /* copy input format from the decoding context */
176                 AVCodecContext *c;
177                 if (env->in == NULL)    /* XXX should not happen - decoder not ready */
178                         return;
179                 c = env->in->dec_ctx;
180                 b_in = &env->in->dec_out;
181                 b_in->pix_fmt = c->pix_fmt;
182                 b_in->w = c->width;
183                 b_in->h = c->height;
184
185                 b_out = &env->rem_dpy;
186                 p_in = (AVPicture *)env->in->d_frame;
187         }
188         bmp = gui->win[out].bmp;
189         SDL_LockYUVOverlay(bmp);
190         /* output picture info - this is sdl, YUV420P */
191         bzero(&p_out, sizeof(p_out));
192         p_out.data[0] = bmp->pixels[0];
193         p_out.data[1] = bmp->pixels[1];
194         p_out.data[2] = bmp->pixels[2];
195         p_out.linesize[0] = bmp->pitches[0];
196         p_out.linesize[1] = bmp->pitches[1];
197         p_out.linesize[2] = bmp->pitches[2];
198
199         my_scale(b_in, p_in, b_out, &p_out);
200
201         /* lock to protect access to Xlib by different threads. */
202         SDL_DisplayYUVOverlay(bmp, &gui->win[out].rect);
203         SDL_UnlockYUVOverlay(bmp);
204 }
205
206 /*
207  * Identifiers for regions of the main window.
208  * Values between 0 and 127 correspond to ASCII characters.
209  * The corresponding strings to be used in the skin comment section
210  * are defined in gui_key_map.
211  */
212 enum skin_area {
213         /* answer/close functions */
214         KEY_PICK_UP = 128,
215         KEY_HANG_UP = 129,
216
217         KEY_MUTE = 130,
218         KEY_AUTOANSWER = 131,
219         KEY_SENDVIDEO = 132,
220         KEY_LOCALVIDEO = 133,
221         KEY_REMOTEVIDEO = 134,
222         KEY_FLASH = 136,
223
224         /* sensitive areas for the various text windows */
225         KEY_MESSAGEBOARD = 140,
226         KEY_DIALEDBOARD = 141,
227         KEY_EDITBOARD = 142,
228
229         KEY_GUI_CLOSE = 199,            /* close gui */
230         /* regions of the skin - displayed area, fonts, etc.
231          * XXX NOTE these are not sensitive areas.
232          */
233         KEY_KEYPAD = 200,               /* the keypad - default to the whole image */
234         KEY_FONT = 201,         /* the font. Maybe not really useful */
235         KEY_MESSAGE = 202,      /* area for incoming messages */
236         KEY_DIALED = 203,       /* area for dialed numbers */
237         KEY_EDIT = 204,         /* area for editing user input */
238
239         /* areas outside the keypad - simulated */
240         KEY_OUT_OF_KEYPAD = 241,
241         KEY_REM_DPY = 242,
242         KEY_LOC_DPY = 243,
243         KEY_RESET = 253,                /* the 'reset' keyword */
244         KEY_NONE = 254,                 /* invalid area */
245         KEY_DIGIT_BACKGROUND = 255,     /* other areas within the keypad */
246 };
247
248 /*
249  * Handlers for the various keypad functions
250  */
251
252 /* accumulate digits, possibly call dial if in connected mode */
253 static void keypad_digit(struct video_desc *env, int digit)
254 {       
255         if (env->owner) {               /* we have a call, send the digit */
256                 struct ast_frame f = { AST_FRAME_DTMF, 0 };
257
258                 f.subclass = digit;
259                 ast_queue_frame(env->owner, &f);
260         } else {                /* no call, accumulate digits */
261                 char buf[2] = { digit, '\0' };
262                 if (env->gui->bd_msg) /* XXX not strictly necessary ... */
263                         print_message(env->gui->bd_msg, buf);
264         }
265 }
266
267 /* function used to toggle on/off the status of some variables */
268 static char *keypad_toggle(struct video_desc *env, int index)
269 {
270         ast_log(LOG_WARNING, "keypad_toggle(%i) called\n", index);
271
272         switch (index) {
273         case KEY_SENDVIDEO:
274                 env->out.sendvideo = !env->out.sendvideo;
275                 break;
276 #ifdef notyet
277         case KEY_MUTE: {
278                 struct chan_oss_pvt *o = find_desc(oss_active);
279                 o->mute = !o->mute;
280                 }
281                 break;
282         case KEY_AUTOANSWER: {
283                 struct chan_oss_pvt *o = find_desc(oss_active);
284                 o->autoanswer = !o->autoanswer;
285                 }
286                 break;
287 #endif
288         }
289         return NULL;
290 }
291
292 char *console_do_answer(int fd);
293 /*
294  * Function called when the pick up button is pressed
295  * perform actions according the channel status:
296  *
297  *  - if no one is calling us and no digits was pressed,
298  *    the operation have no effects,
299  *  - if someone is calling us we answer to the call.
300  *  - if we have no call in progress and we pressed some
301  *    digit, send the digit to the console.
302  */
303 static void keypad_pick_up(struct video_desc *env)
304 {
305         struct gui_info *gui = env->gui;
306
307         ast_log(LOG_WARNING, "keypad_pick_up called\n");
308
309         if (env->owner) { /* someone is calling us, just answer */
310                 ast_cli_command(gui->outfd, "console answer");
311         } else { /* we have someone to call */
312                 char buf[160];
313                 const char *who = ast_skip_blanks(read_message(gui->bd_msg));
314                 buf[sizeof(buf) - 1] = '\0';
315                 snprintf(buf, sizeof(buf), "console dial %s", who);
316                 ast_log(LOG_WARNING, "doing <%s>\n", buf);
317                 print_message(gui->bd_dialed, "\n");
318                 print_message(gui->bd_dialed, who);
319                 reset_board(gui->bd_msg);
320                 ast_cli_command(gui->outfd, buf);
321         }
322 }
323
324 #if 0 /* still unused */
325 /*
326  * As an alternative to SDL_TTF, we can simply load the font from
327  * an image and blit characters on the background of the GUI.
328  *
329  * To generate a font we can use the 'fly' command with the
330  * following script (3 lines with 32 chars each)
331  
332 size 320,64
333 name font.png
334 transparent 0,0,0
335 string 255,255,255,  0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
336 string 255,255,255,  0,20,giant,@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
337 string 255,255,255,  0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
338 end
339
340  */
341
342 /* Print given text on the gui */
343 static int gui_output(struct video_desc *env, const char *text)
344 {
345         return 1;       /* error, not supported */
346 }
347 #endif 
348
349 static int video_geom(struct fbuf_t *b, const char *s);
350 static void sdl_setup(struct video_desc *env);
351 static int kp_match_area(const struct keypad_entry *e, int x, int y);
352
353 static void set_drag(struct drag_info *drag, int x, int y, enum drag_window win)
354 {
355         drag->x_start = x;
356         drag->y_start = y;
357         drag->drag_window = win;
358 }
359
360 /*
361  * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
362  * index value and calling the right callback.
363  *
364  * x, y are referred to the upper left corner of the main SDL window.
365  */
366 static void handle_mousedown(struct video_desc *env, SDL_MouseButtonEvent button)
367 {
368         uint8_t index = KEY_OUT_OF_KEYPAD;      /* the key or region of the display we clicked on */
369         struct gui_info *gui = env->gui;
370
371 #if 0
372         ast_log(LOG_WARNING, "event %d %d have %d/%d regions at %p\n",
373                 button.x, button.y, gui->kp_used, gui->kp_size, gui->kp);
374 #endif
375         /* for each mousedown we end previous drag */
376         gui->drag.drag_window = DRAG_NONE;
377
378         /* define keypad boundary */
379         if (button.x < env->rem_dpy.w)
380                 index = KEY_REM_DPY; /* click on remote video */
381         else if (button.x > env->rem_dpy.w + gui->keypad->w)
382                 index = KEY_LOC_DPY; /* click on local video */
383         else if (button.y > gui->keypad->h)
384                 index = KEY_OUT_OF_KEYPAD; /* click outside the keypad */
385         else if (gui->kp) {
386                 int i;
387                 for (i = 0; i < gui->kp_used; i++) {
388                         if (kp_match_area(&gui->kp[i], button.x - env->rem_dpy.w, button.y)) {
389                                 index = gui->kp[i].c;
390                                 break;
391                         }
392                 }
393         }
394
395         /* exec the function */
396         if (index < 128) {      /* surely clicked on the keypad, don't care which key */
397                 keypad_digit(env, index);
398                 return;
399         }
400         switch (index) {
401         /* answer/close function */
402         case KEY_PICK_UP:
403                 keypad_pick_up(env);
404                 break;
405         case KEY_HANG_UP:
406                 ast_cli_command(gui->outfd, "console hangup");
407                 break;
408
409         /* other functions */
410         case KEY_MUTE:
411         case KEY_AUTOANSWER:
412         case KEY_SENDVIDEO:
413                 keypad_toggle(env, index);
414                 break;
415
416         case KEY_LOCALVIDEO:
417                 break;
418         case KEY_REMOTEVIDEO:
419                 break;
420
421         case KEY_MESSAGEBOARD:
422                 if (button.button == SDL_BUTTON_LEFT)
423                         set_drag(&gui->drag, button.x, button.y, DRAG_MESSAGE);
424                 break;
425
426         /* press outside the keypad. right increases size, center decreases, left drags */
427         case KEY_LOC_DPY:
428         case KEY_REM_DPY:
429                 if (button.button == SDL_BUTTON_LEFT) {
430                         if (index == KEY_LOC_DPY)
431                                 set_drag(&gui->drag, button.x, button.y, DRAG_LOCAL);
432                         break;
433                 } else {
434                         char buf[128];
435                         struct fbuf_t *fb = index == KEY_LOC_DPY ? &env->loc_dpy : &env->rem_dpy;
436                         sprintf(buf, "%c%dx%d", button.button == SDL_BUTTON_RIGHT ? '>' : '<',
437                                 fb->w, fb->h);
438                         video_geom(fb, buf);
439                         sdl_setup(env);
440                 }
441                 break;
442         case KEY_OUT_OF_KEYPAD:
443                 break;
444
445         case KEY_DIGIT_BACKGROUND:
446                 break;
447         default:
448                 ast_log(LOG_WARNING, "function not yet defined %i\n", index);
449         }
450 }
451
452 /*
453  * Handle SDL_KEYDOWN type event, put the key pressed
454  * in the dial buffer or in the text-message buffer,
455  * depending on the text_mode variable value.
456  *
457  * key is the SDLKey structure corresponding to the key pressed.
458  * Note that SDL returns modifiers (ctrl, shift, alt) as independent
459  * information so the key itself is not enough and we need to
460  * use a translation table, below - one line per entry,
461  * plain, shift, ctrl, ... using the first char as key.
462  */
463 static const char *us_kbd_map[] = {
464         "`~", "1!", "2@", "3#", "4$", "5%", "6^",
465         "7&", "8*", "9(", "0)", "-_", "=+", "[{",
466         "]}", "\\|", ";:", "'\"", ",<", ".>", "/?",
467         "jJ\n",
468         NULL
469 };
470
471 static char map_key(SDL_keysym *ks)
472 {
473         const char *s, **p = us_kbd_map;
474         int c = ks->sym;
475
476         if (c == '\r')  /* map cr into lf */
477                 c = '\n';
478         if (c >= SDLK_NUMLOCK && c <= SDLK_COMPOSE)
479                 return 0;       /* only a modifier */
480         if (ks->mod == 0)
481                 return c;
482         while ((s = *p) && s[0] != c)
483                 p++;
484         if (s) { /* see if we have a modifier and a chance to use it */
485                 int l = strlen(s), mod = 0;
486                 if (l > 1)
487                         mod |= (ks->mod & KMOD_SHIFT) ? 1 : 0;
488                 if (l > 2 + mod)
489                         mod |= (ks->mod & KMOD_CTRL) ? 2 : 0;
490                 if (l > 4 + mod)
491                         mod |= (ks->mod & KMOD_ALT) ? 4 : 0;
492                 c = s[mod];
493         }
494         if (ks->mod & (KMOD_CAPS|KMOD_SHIFT) && c >= 'a' && c <='z')
495                 c += 'A' - 'a';
496         return c;
497 }
498
499 static void handle_keyboard_input(struct video_desc *env, SDL_keysym *ks)
500 {
501         char buf[2] = { map_key(ks), '\0' };
502         struct gui_info *gui = env->gui;
503         if (buf[0] == 0)        /* modifier ? */
504                 return;
505         switch (gui->kb_output) {
506         default:
507                 break;
508         case KO_INPUT:  /* to be completed */
509                 break;
510         case KO_MESSAGE:
511                 if (gui->bd_msg) {
512                         print_message(gui->bd_msg, buf);
513                         if (buf[0] == '\r' || buf[0] == '\n') {
514                                 keypad_pick_up(env);
515                         }
516                 }
517                 break;
518
519         case KO_DIALED: /* to be completed */
520                 break;
521         }
522
523         return;
524 }
525
526 static void grabber_move(struct video_out_desc *, int dx, int dy);
527
528 int compute_drag(int *start, int end, int magnifier);
529 int compute_drag(int *start, int end, int magnifier)
530 {
531         int delta = end - *start;
532 #define POLARITY -1
533         /* add a small quadratic term */
534         delta += delta * delta * (delta > 0 ? 1 : -1 )/100;
535         delta *= POLARITY * magnifier;
536 #undef POLARITY
537         *start = end;
538         return delta;
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         struct drag_info *drag;
554 #define N_EVENTS        32
555         int i, n;
556         SDL_Event ev[N_EVENTS];
557
558         if (!gui)
559                 return;
560         drag = &gui->drag;
561         if (caption)
562                 SDL_WM_SetCaption(caption, NULL);
563
564 #define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
565         while ( (n = SDL_PeepEvents(ev, N_EVENTS, SDL_GETEVENT, SDL_ALLEVENTS)) > 0) {
566                 for (i = 0; i < n; i++) {
567 #if 0
568                         ast_log(LOG_WARNING, "------ event %d at %d %d\n",
569                                 ev[i].type,  ev[i].button.x,  ev[i].button.y);
570 #endif
571                         switch (ev[i].type) {
572                         default:
573                                 ast_log(LOG_WARNING, "------ event %d at %d %d\n",
574                                         ev[i].type,  ev[i].button.x,  ev[i].button.y);
575                                 break;
576
577                         case SDL_ACTIVEEVENT:
578 #if 0 /* do not react, we don't want to die because the window is minimized */
579                                 if (ev[i].active.gain == 0 && ev[i].active.state & SDL_APPACTIVE) {
580                                         ast_log(LOG_WARNING, "/* somebody has killed us ? */");
581                                         ast_cli_command(gui->outfd, "stop now");
582                                 }
583 #endif
584                                 break;
585
586                         case SDL_KEYUP: /* ignore, for the time being */
587                                 break;
588
589                         case SDL_KEYDOWN:
590                                 handle_keyboard_input(env, &ev[i].key.keysym);
591                                 break;
592
593                         case SDL_MOUSEMOTION:
594                         case SDL_MOUSEBUTTONUP:
595                                 if (drag->drag_window == DRAG_LOCAL) {
596                                         /* move the capture source */
597                                         int dx = compute_drag(&drag->x_start, ev[i].motion.x, 3);
598                                         int dy = compute_drag(&drag->y_start, ev[i].motion.y, 3);
599                                         grabber_move(&env->out, dx, dy);
600                                 } else if (drag->drag_window == DRAG_MESSAGE) {
601                                         /* scroll up/down the window */
602                                         int dy = compute_drag(&drag->y_start, ev[i].motion.y, 1);
603                                         move_message_board(gui->bd_msg, dy);
604                                 }
605                                 if (ev[i].type == SDL_MOUSEBUTTONUP)
606                                         drag->drag_window = DRAG_NONE;
607                                 break;
608                         case SDL_MOUSEBUTTONDOWN:
609                                 handle_mousedown(env, ev[i].button);
610                                 break;
611                         }
612                 }
613         }
614         if (1) {
615                 struct timeval b, a = ast_tvnow();
616                 int i;
617                 //SDL_Lock_EventThread();
618                 SDL_PumpEvents();
619                 b = ast_tvnow();
620                 i = ast_tvdiff_ms(b, a);
621                 if (i > 3)
622                         fprintf(stderr, "-------- SDL_PumpEvents took %dms\n", i);
623                 //SDL_Unlock_EventThread();
624         }
625 }
626
627 static SDL_Surface *load_image(const char *file)
628 {
629         SDL_Surface *temp;
630  
631 #ifdef HAVE_SDL_IMAGE
632         temp = IMG_Load(file);
633 #else
634         temp = SDL_LoadBMP(file);
635 #endif
636         if (temp == NULL)
637                 fprintf(stderr, "Unable to load image %s: %s\n",
638                         file, SDL_GetError());
639         return temp;
640 }
641
642 static void keypad_setup(struct gui_info *gui, const char *kp_file);
643
644 /* TODO: consistency checks, check for bpp, widht and height */
645 /* Init the mask image used to grab the action. */
646 static struct gui_info *gui_init(const char *keypad_file, const char *font)
647 {
648         struct gui_info *gui = ast_calloc(1, sizeof(*gui));
649
650         if (gui == NULL)
651                 return NULL;
652         /* initialize keypad status */
653         gui->kb_output = KO_MESSAGE;    /* XXX temp */
654         gui->drag.drag_window = DRAG_NONE;
655         gui->outfd = -1;
656
657         keypad_setup(gui, keypad_file);
658         if (gui->keypad == NULL)        /* no keypad, we are done */
659                 return gui;
660         /* XXX load image */
661         if (!ast_strlen_zero(font)) {
662                 int i;
663                 SDL_Rect *r;
664
665                 gui->font = load_image(font);
666                 if (!gui->font) {
667                         ast_log(LOG_WARNING, "Unable to load font %s, no output available\n", font);
668                         goto error;
669                 }
670                 ast_log(LOG_WARNING, "Loaded font %s\n", font);
671                 /* XXX hardwired constants - 3 rows of 32 chars */
672                 r = gui->font_rects;
673 #define FONT_H 20
674 #define FONT_W 9
675                 for (i = 0; i < 96; r++, i++) {
676                         r->x = (i % 32 ) * FONT_W;
677                         r->y = (i / 32 ) * FONT_H;
678                         r->w = FONT_W;
679                         r->h = FONT_H;
680                 }
681         }
682
683         gui->outfd = open ("/dev/null", O_WRONLY);      /* discard output, temporary */
684         if (gui->outfd < 0) {
685                 ast_log(LOG_WARNING, "Unable output fd\n");
686                 goto error;
687         }
688         return gui;
689
690 error:
691         ast_free(gui);
692         return NULL;
693 }
694
695 /* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
696 static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
697         int w, int h, int x, int y)
698 {
699         win->bmp = SDL_CreateYUVOverlay(w, h, fmt, screen);
700         if (win->bmp == NULL)
701                 return -1;      /* error */
702         win->rect.x = x;
703         win->rect.y = y;
704         win->rect.w = w;
705         win->rect.h = h;
706         return 0;
707 }
708
709 static int keypad_cfg_read(struct gui_info *gui, const char *val);
710
711 static void keypad_setup(struct gui_info *gui, const char *kp_file)
712 {
713         FILE *fd;
714         char buf[1024];
715         const char region[] = "region";
716         int reg_len = strlen(region);
717         int in_comment = 0;
718
719         if (gui->keypad)
720                 return;
721         gui->keypad = load_image(kp_file);
722         if (!gui->keypad)
723                 return;
724         /* now try to read the keymap from the file. */
725         fd = fopen(kp_file, "r");
726         if (fd == NULL) {
727                 ast_log(LOG_WARNING, "fail to open %s\n", kp_file);
728                 return;
729         }
730         /*
731          * If the keypad image has a comment field, try to read
732          * the button location from there. The block must start with
733          * a comment (or empty) line, and continue with entries like:
734          *      region = token shape x0 y0 x1 y1 h
735          *      ...
736          * (basically, lines have the same format as config file entries).
737          * You can add it to a jpeg file using wrjpgcom
738          */
739         while (fgets(buf, sizeof(buf), fd)) {
740                 char *s;
741
742                 if (!strstr(buf, region)) { /* no keyword yet */
743                         if (!in_comment)        /* still waiting for initial comment block */
744                                 continue;
745                         else
746                                 break;
747                 }
748                 if (!in_comment) {      /* first keyword, reset previous entries */
749                         keypad_cfg_read(gui, "reset");
750                         in_comment = 1;
751                 }
752                 s = ast_skip_blanks(buf);
753                 ast_trim_blanks(s);
754                 if (memcmp(s, region, reg_len))
755                         break;  /* keyword not found */
756                 s = ast_skip_blanks(s + reg_len); /* space between token and '=' */
757                 if (*s++ != '=')        /* missing separator */
758                         break;
759                 if (*s == '>')  /* skip '>' if present */
760                         s++;
761                 keypad_cfg_read(gui, ast_skip_blanks(s));
762         }
763         fclose(fd);
764 }
765
766 struct board *board_setup(SDL_Surface *screen, SDL_Rect *dest,
767         SDL_Surface *font, SDL_Rect *font_rects);
768
769 /*! \brief initialize the boards we have in the keypad */
770 static void init_board(struct gui_info *gui, struct board **dst, SDL_Rect *r, int dx, int dy)
771 {
772         if (r[0].w == 0 || r[0].h == 0)
773                 return; /* not available */
774         r[1] = r[0];    /* copy geometry */
775         r[1].x += dx;   /* add offset of main window */
776         r[1].y += dy;
777         if (*dst == NULL) {     /* initial call */
778                 *dst = board_setup(gui->screen, &r[1], gui->font, gui->font_rects);
779         } else {
780                 /* call a refresh */
781         }
782 }
783
784 #ifdef HAVE_X11
785 /*
786  * SDL is not very robust on error handling, so we need to trap ourselves
787  * at least the most obvious failure conditions, e.g. a bad SDL_WINDOWID.
788  * As of sdl-1.2.13, SDL_SetVideoMode crashes with bad parameters, so
789  * we need to do the explicit X calls to make sure the window is correct.
790  * And around these calls, we must trap X errors.
791  */
792 static int my_x_handler(Display *d, XErrorEvent *e)
793 {
794         ast_log(LOG_WARNING, "%s error_code %d\n", __FUNCTION__, e->error_code);
795         return 0;
796 }
797 #endif /* HAVE_X11 */
798
799 /*! \brief [re]set the main sdl window, useful in case of resize.
800  * We can tell the first from subsequent calls from the value of
801  * env->gui, which is NULL the first time.
802  */
803 static void sdl_setup(struct video_desc *env)
804 {
805         int dpy_fmt = SDL_IYUV_OVERLAY; /* YV12 causes flicker in SDL */
806         int depth, maxw, maxh;
807         const SDL_VideoInfo *info;
808         int kp_w = 0, kp_h = 0; /* keypad width and height */
809         struct gui_info *gui = env->gui;
810
811 #ifdef HAVE_X11
812         const char *e = getenv("SDL_WINDOWID");
813
814         if (!ast_strlen_zero(e)) {
815                 XWindowAttributes a;
816                 int (*old_x_handler)(Display *d, XErrorEvent *e) = XSetErrorHandler(my_x_handler);
817                 Display *d = XOpenDisplay(getenv("DISPLAY"));
818                 long w = atol(e);
819                 int success = w ? XGetWindowAttributes(d, w, &a) : 0;
820
821                 XSetErrorHandler(old_x_handler);
822                 if (!success) {
823                         ast_log(LOG_WARNING, "%s error in window\n", __FUNCTION__);
824                         return;
825                 }
826         }       
827 #endif
828         /*
829          * initialize the SDL environment. We have one large window
830          * with local and remote video, and a keypad.
831          * At the moment we arrange them statically, as follows:
832          * - on the left, the remote video;
833          * - on the center, the keypad
834          * - on the right, the local video
835          * We need to read in the skin for the keypad before creating the main
836          * SDL window, because the size is only known here.
837          */
838
839         if (gui == NULL && SDL_Init(SDL_INIT_VIDEO)) {
840                 ast_log(LOG_WARNING, "Could not initialize SDL - %s\n",
841                         SDL_GetError());
842                 /* again not fatal, just we won't display anything */
843                 return;
844         }
845         info = SDL_GetVideoInfo();
846         /* We want at least 16bpp to support YUV overlays.
847          * E.g with SDL_VIDEODRIVER = aalib the default is 8
848          */
849         if (!info || !info->vfmt) {
850                 ast_log(LOG_WARNING, "Bad SDL_GetVideoInfo - %s\n",
851                         SDL_GetError());
852                 return;
853         }
854         depth = info->vfmt->BitsPerPixel;
855         if (depth < 16)
856                 depth = 16;
857         if (!gui)
858                 env->gui = gui = gui_init(env->keypad_file, env->keypad_font);
859         if (!gui)
860                 goto no_sdl;
861
862         if (gui->keypad) {
863                 if (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) {
864                         kp_w = gui->kp_rect.w;
865                         kp_h = gui->kp_rect.h;
866                 } else {
867                         kp_w = gui->keypad->w;
868                         kp_h = gui->keypad->h;
869                 }
870         }
871         /* XXX same for other boards */
872 #define BORDER  5       /* border around our windows */
873         maxw = env->rem_dpy.w + env->loc_dpy.w + kp_w;
874         maxh = MAX( MAX(env->rem_dpy.h, env->loc_dpy.h), kp_h);
875         maxw += 4 * BORDER;
876         maxh += 2 * BORDER;
877
878         gui->screen = SDL_SetVideoMode(maxw, maxh, depth, 0);
879         if (!gui->screen) {
880                 ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
881                 goto no_sdl;
882         }
883
884 #ifdef HAVE_X11
885         /*
886          * Annoying as it may be, if SDL_WINDOWID is set, SDL does
887          * not grab keyboard/mouse events or expose or other stuff,
888          * and it does not handle resize either.
889          * So we need to implement workarounds here.
890          */
891     do {
892         /* First, handle the event mask */
893         XWindowAttributes attr;
894         long want;
895         SDL_SysWMinfo info;
896         Display *SDL_Display;
897         Window win;
898
899         const char *e = getenv("SDL_WINDOWID");
900         if (ast_strlen_zero(e))  /* no external window, don't bother doing this */
901                 break;
902         SDL_VERSION(&info.version); /* it is important to set the version */
903         if (SDL_GetWMInfo(&info) != 1) {
904                 fprintf(stderr, "no wm info\n");
905                 break;
906         }
907         SDL_Display = info.info.x11.display;
908         if (SDL_Display == NULL)
909                 break;
910         win = info.info.x11.window;
911
912         /*
913          * A list of events we want.
914          * Leave ResizeRedirectMask to the parent.
915          */
916         want = KeyPressMask | KeyReleaseMask | ButtonPressMask |
917                            ButtonReleaseMask | EnterWindowMask |
918                            LeaveWindowMask | PointerMotionMask |
919                            Button1MotionMask |
920                            Button2MotionMask | Button3MotionMask |
921                            Button4MotionMask | Button5MotionMask |
922                            ButtonMotionMask | KeymapStateMask |
923                            ExposureMask | VisibilityChangeMask |
924                            StructureNotifyMask | /* ResizeRedirectMask | */
925                            SubstructureNotifyMask | SubstructureRedirectMask |
926                            FocusChangeMask | PropertyChangeMask |
927                            ColormapChangeMask | OwnerGrabButtonMask;
928
929         bzero(&attr, sizeof(attr));
930         XGetWindowAttributes(SDL_Display, win, &attr);
931
932         /* the following events can be delivered only to one client.
933          * So check which ones are going to someone else, and drop
934          * them from our request.
935          */
936         {
937         /* ev are the events for a single recipient */
938         long ev = ButtonPressMask | ResizeRedirectMask |
939                         SubstructureRedirectMask;
940         ev &= (attr.all_event_masks & ~attr.your_event_mask);
941         /* now ev contains 1 for single-recipient events owned by others.
942          * We must clear those bits in 'want'
943          * and then add the bits in 'attr.your_event_mask' to 'want'
944          */
945         want &= ~ev;
946         want |= attr.your_event_mask;
947         }
948         XSelectInput(SDL_Display, win, want);
949
950         /* Second, handle resize.
951          * We do part of the things that X11Resize does,
952          * but also generate a ConfigureNotify event so
953          * the owner of the window has a chance to do something
954          * with it.
955          */
956         XResizeWindow(SDL_Display, win, maxw, maxh);
957         {
958         XConfigureEvent ce = {
959                 .type = ConfigureNotify,
960                 .serial = 0,
961                 .send_event = 1,        /* TRUE */
962                 .display = SDL_Display,
963                 .event = win,
964                 .window = win,
965                 .x = 0,
966                 .y = 0,
967                 .width = maxw,
968                 .height = maxh,
969                 .border_width = 0,
970                 .above = 0,
971                 .override_redirect = 0 };
972         XSendEvent(SDL_Display, win, 1 /* TRUE */, StructureNotifyMask, (XEvent *)&ce);
973         }
974     } while (0);
975 #endif /* HAVE_X11 */
976         SDL_WM_SetCaption("Asterisk console Video Output", NULL);
977         if (set_win(gui->screen, &gui->win[WIN_REMOTE], dpy_fmt,
978                         env->rem_dpy.w, env->rem_dpy.h, BORDER, BORDER))
979                 goto no_sdl;
980         if (set_win(gui->screen, &gui->win[WIN_LOCAL], dpy_fmt,
981                         env->loc_dpy.w, env->loc_dpy.h,
982                         3*BORDER+env->rem_dpy.w + kp_w, BORDER))
983                 goto no_sdl;
984
985         /* display the skin, but do not free it as we need it later to
986          * restore text areas and maybe sliders too.
987          */
988         if (gui->keypad) {
989                 struct SDL_Rect *dest = &gui->win[WIN_KEYPAD].rect;
990                 struct SDL_Rect *src = (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) ? & gui->kp_rect : NULL;
991                 /* set the coordinates of the keypad relative to the main screen */
992                 dest->x = 2*BORDER + env->rem_dpy.w;
993                 dest->y = BORDER;
994                 dest->w = kp_w;
995                 dest->h = kp_h;
996                 SDL_BlitSurface(gui->keypad, src, gui->screen, dest);
997                 init_board(gui, &gui->bd_msg, gui->kp_msg, dest->x, dest->y);
998                 init_board(gui, &gui->bd_dialed, gui->kp_dialed, dest->x, dest->y);
999                 SDL_UpdateRects(gui->screen, 1, dest);
1000         }
1001         return;
1002
1003 no_sdl:
1004         /* free resources in case of errors */
1005         env->gui = cleanup_sdl(gui);
1006 }
1007
1008 /*
1009  * Functions to determine if a point is within a region. Return 1 if success.
1010  * First rotate the point, with
1011  *      x' =  (x - x0) * cos A + (y - y0) * sin A
1012  *      y' = -(x - x0) * sin A + (y - y0) * cos A
1013  * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
1014  *      l = sqrt( (x1-x0)^2 + (y1-y0)^2
1015  * Then determine inclusion by simple comparisons i.e.:
1016  *      rectangle: x >= 0 && x < l && y >= 0 && y < h
1017  *      ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
1018  */
1019 static int kp_match_area(const struct keypad_entry *e, int x, int y)
1020 {
1021         double xp, dx = (e->x1 - e->x0);
1022         double yp, dy = (e->y1 - e->y0);
1023         double l = sqrt(dx*dx + dy*dy);
1024         int ret = 0;
1025
1026         if (l > 1) { /* large enough */
1027                 xp = ((x - e->x0)*dx + (y - e->y0)*dy)/l;
1028                 yp = (-(x - e->x0)*dy + (y - e->y0)*dx)/l;
1029                 if (e->type == KP_RECT) {
1030                         ret = (xp >= 0 && xp < l && yp >=0 && yp < l);
1031                 } else if (e->type == KP_CIRCLE) {
1032                         dx = xp*xp/(l*l) + yp*yp/(e->h*e->h);
1033                         ret = (dx < 1);
1034                 }
1035         }
1036 #if 0
1037         ast_log(LOG_WARNING, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
1038                 ret, e->c, x, y, e->type, e->x0, e->y0, e->x1, e->y1, e->h);
1039 #endif
1040         return ret;
1041 }
1042
1043 struct _s_k { const char *s; int k; };
1044 static struct _s_k gui_key_map[] = {
1045         {"PICK_UP",     KEY_PICK_UP },
1046         {"PICKUP",      KEY_PICK_UP },
1047         {"HANG_UP",     KEY_HANG_UP },
1048         {"HANGUP",      KEY_HANG_UP },
1049         {"MUTE",        KEY_MUTE },
1050         {"FLASH",       KEY_FLASH },
1051         {"AUTOANSWER",  KEY_AUTOANSWER },
1052         {"SENDVIDEO",   KEY_SENDVIDEO },
1053         {"LOCALVIDEO",  KEY_LOCALVIDEO },
1054         {"REMOTEVIDEO", KEY_REMOTEVIDEO },
1055         {"GUI_CLOSE",   KEY_GUI_CLOSE },
1056         {"MESSAGEBOARD",        KEY_MESSAGEBOARD },
1057         {"DIALEDBOARD", KEY_DIALEDBOARD },
1058         {"EDITBOARD",   KEY_EDITBOARD },
1059         {"KEYPAD",      KEY_KEYPAD },   /* x0 y0 w h - active area of the keypad */
1060         {"MESSAGE",     KEY_MESSAGE },  /* x0 y0 w h - incoming messages */
1061         {"DIALED",      KEY_DIALED },   /* x0 y0 w h - dialed number */
1062         {"EDIT",        KEY_EDIT },     /* x0 y0 w h - edit user input */
1063         {"FONT",        KEY_FONT },     /* x0 yo w h rows cols - location and format of the font */
1064         {NULL, 0 } };
1065
1066 static int gui_map_token(const char *s)
1067 {
1068         /* map the string into token to be returned */
1069         int i = atoi(s);
1070         struct _s_k *p;
1071         if (i > 0 || s[1] == '\0')      /* numbers or single characters */
1072                 return (i > 9) ? i : s[0];
1073         for (p = gui_key_map; p->s; p++) {
1074                 if (!strcasecmp(p->s, s))
1075                         return p->k;
1076         }
1077         return KEY_NONE;        /* not found */
1078 }
1079
1080 /*! \brief read a keypad entry line in the format
1081  *      reset
1082  *      token circle xc yc diameter
1083  *      token circle xc yc x1 y1 h      # ellipse, main diameter and height
1084  *      token rect x0 y0 x1 y1 h        # rectangle with main side and eight
1085  * token is the token to be returned, either a character or a symbol
1086  * as KEY_* above
1087  * Return 1 on success, 0 on error.
1088  */
1089 static int keypad_cfg_read(struct gui_info *gui, const char *val)
1090 {
1091         struct keypad_entry e;
1092         SDL_Rect *r = NULL;
1093         char s1[16], s2[16];
1094         int i, ret = 0; /* default, error */
1095
1096         if (gui == NULL || val == NULL)
1097                 return 0;
1098
1099         s1[0] = s2[0] = '\0';
1100         bzero(&e, sizeof(e));
1101         i = sscanf(val, "%14s %14s %d %d %d %d %d",
1102                 s1, s2, &e.x0, &e.y0, &e.x1, &e.y1, &e.h);
1103
1104         e.c = gui_map_token(s1);
1105         if (e.c == KEY_NONE)
1106                 return 0;       /* nothing found */
1107         switch (i) {
1108         default:
1109                 break;
1110         case 1: /* only "reset" is allowed */
1111                 if (e.c != KEY_RESET)
1112                         break;
1113                 if (gui->kp)
1114                         gui->kp_used = 0;
1115                 break;
1116         case 5:
1117                 if (e.c == KEY_KEYPAD)  /* active keypad area */
1118                         r = &gui->kp_rect;
1119                 else if (e.c == KEY_MESSAGE)
1120                         r = gui->kp_msg;
1121                 else if (e.c == KEY_DIALED)
1122                         r = gui->kp_dialed;
1123                 else if (e.c == KEY_EDIT)
1124                         r = gui->kp_edit;
1125                 if (r) {
1126                         r->x = atoi(s2);
1127                         r->y = e.x0;
1128                         r->w = e.y0;
1129                         r->h = e.x1;
1130                         break;
1131                 }
1132                 if (strcasecmp(s2, "circle"))   /* invalid */
1133                         break;
1134                 /* token circle xc yc diameter */
1135                 e.h = e.x1;
1136                 e.y1 = e.y0;    /* map radius in x1 y1 */
1137                 e.x1 = e.x0 + e.h;      /* map radius in x1 y1 */
1138                 e.x0 = e.x0 - e.h;      /* map radius in x1 y1 */
1139                 /* fallthrough */
1140
1141         case 7:
1142                 if (e.c == KEY_FONT) {  /* font - x0 y0 w h rows cols */
1143                         ast_log(LOG_WARNING, "font not supported yet\n");
1144                         break;
1145                 }
1146                 /* token circle|rect x0 y0 x1 y1 h */
1147                 if (e.x1 < e.x0 || e.h <= 0) {
1148                         ast_log(LOG_WARNING, "error in coordinates\n");
1149                         e.type = 0;
1150                         break;
1151                 }
1152                 if (!strcasecmp(s2, "circle")) {
1153                         /* for a circle we specify the diameter but store center and radii */
1154                         e.type = KP_CIRCLE;
1155                         e.x0 = (e.x1 + e.x0) / 2;
1156                         e.y0 = (e.y1 + e.y0) / 2;
1157                         e.h = e.h / 2;
1158                 } else if (!strcasecmp(s2, "rect")) {
1159                         e.type = KP_RECT;
1160                 } else
1161                         break;
1162                 ret = 1;
1163         }
1164         // ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
1165         if (ret == 0)
1166                 return 0;
1167         if (gui->kp_size == 0) {
1168                 gui->kp = ast_calloc(10, sizeof(e));
1169                 if (gui->kp == NULL) {
1170                         ast_log(LOG_WARNING, "cannot allocate kp");
1171                         return 0;
1172                 }
1173                 gui->kp_size = 10;
1174         }
1175         if (gui->kp_size == gui->kp_used) { /* must allocate */
1176                 struct keypad_entry *a = ast_realloc(gui->kp, sizeof(e)*(gui->kp_size+10));
1177                 if (a == NULL) {
1178                         ast_log(LOG_WARNING, "cannot reallocate kp");
1179                         return 0;
1180                 }
1181                 gui->kp = a;
1182                 gui->kp_size += 10;
1183         }
1184         if (gui->kp_size == gui->kp_used)
1185                 return 0;
1186         gui->kp[gui->kp_used++] = e;
1187         // ast_log(LOG_WARNING, "now %d regions\n", gui->kp_used);
1188         return 1;
1189 }
1190 #endif  /* HAVE_SDL */