import the recent additions for video console into trunk,
[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) with 4 areas:
11 remote video on the left, local video on the right, keypad with all controls
12 and text windows in the center, and source device thumbnails on the top.
13 The top row is not displayed if no devices are specified in the config file.
14
15      ________________________________________________________________
16     |  ______   ______   ______   ______   ______   ______   ______  |
17     | | tn.1 | | tn.2 | | tn.3 | | tn.4 | | tn.5 | | tn.6 | | tn.7 | |
18     | |______| |______| |______| |______| |______| |______| |______| |
19     |  ______   ______   ______   ______   ______   ______   ______  |
20     | |______| |______| |______| |______| |______| |______| |______| |
21     |  _________________    __________________    _________________  |
22     | |                 |  |                  |  |                 | |
23     | |                 |  |                  |  |                 | |
24     | |                 |  |                  |  |                 | |
25     | |   remote video  |  |                  |  |   local video   | |
26     | |                 |  |                  |  |          ______ | |
27     | |                 |  |      keypad      |  |         |  PIP || |
28     | |                 |  |                  |  |         |______|| |
29     | |_________________|  |                  |  |_________________| |
30     |                      |                  |                      |
31     |                      |                  |                      |
32     |                      |__________________|                      |
33     |________________________________________________________________|
34
35
36 The central section is built using an image (jpg, png, maybe gif too)
37 for the skin, and other GUI elements.  Comments embedded in the image
38 indicate to what function each area is mapped to.
39 Another image (png with transparency) is used for the font.
40
41 Mouse and keyboard events are detected on the whole surface, and
42 handled differently according to their location:
43 - center/right click on the local/remote window are used to resize
44   the corresponding window;
45 - clicks on the thumbnail start/stop sources and select them as
46   primary or secondary video sources;
47 - drag on the local video window are used to move the captured
48   area (in the case of X11 grabber) or the picture-in-picture position;
49 - keystrokes on the keypad are mapped to the corresponding key;
50   keystrokes are used as keypad functions, or as text input
51   if we are in text-input mode.
52 - drag on some keypad areas (sliders etc.) are mapped to the
53   corresponding functions;
54
55 Configuration options control the appeareance of the gui:
56
57     keypad = /tmp/kpad2.jpg     ; the skin
58     keypad_font = /tmp/font.png ; the font to use for output
59
60 For future implementation, intresting features can be the following:
61 - freeze of the video coming from our remote party
62 - save of the whole SDL window as a picture
63 - oudio output device switching
64
65 The audio switching feature should allow changing the device
66 or switching to a recorded message for audio sent to remote party.
67 The selection of the device should happen clicking on a marker in the layout.
68 For this reason above the thumbnails row in the layout we would like a new row,
69 the elements composing the row could be message boards, reporting the name of the
70 device or the path of the message to be played.
71
72 For video input freeze and entire window capture, we define 2 new key types,
73 those should be activated pressing the buttons on the keypad, associated with
74 new regions inside the keypad pictureas comments
75
76
77  *
78  */
79
80 #include "asterisk.h"
81 #include "console_video.h"
82 #include "asterisk/lock.h"
83 #include "asterisk/frame.h"
84 #include "asterisk/utils.h"     /* ast_calloc and ast_realloc */
85 #include <math.h>               /* sqrt */
86
87 /* We use a maximum of 12 'windows' in the GUI */
88 enum { WIN_LOCAL, WIN_REMOTE, WIN_KEYPAD, WIN_SRC1,
89         WIN_SRC2, WIN_SRC3, WIN_SRC4, WIN_SRC5,
90         WIN_SRC6, WIN_SRC7, WIN_SRC8, WIN_SRC9, WIN_MAX };
91
92 #ifndef HAVE_SDL        /* stubs if we don't have any sdl */
93 static void show_frame(struct video_desc *env, int out) {}
94 static void sdl_setup(struct video_desc *env)           {}
95 static struct gui_info *cleanup_sdl(struct gui_info* g, int n)  { return NULL; }
96 static void eventhandler(struct video_desc *env, const char *caption)   {}
97 static int keypad_cfg_read(struct gui_info *gui, const char *val)       { return 0; }
98
99 #else /* HAVE_SDL, the real rendering code */
100
101 #include <SDL/SDL.h>
102 #include <SDL/SDL_syswm.h>
103 #ifdef HAVE_SDL_IMAGE
104 #include <SDL/SDL_image.h>      /* for loading images */
105 #endif
106
107 #ifdef HAVE_X11
108 /* Need to hook into X for SDL_WINDOWID handling */
109 #include <X11/Xlib.h>
110 #endif
111
112 #define BORDER  5               /* border around our windows */
113 #define SRC_MSG_BD_H 20         /* height of the message board below those windows */
114
115 enum kp_type { KP_NONE, KP_RECT, KP_CIRCLE };
116 struct keypad_entry {
117         int c;  /* corresponding character */
118         int x0, y0, x1, y1, h;  /* arguments */
119         enum kp_type type;
120 };
121
122 /* our representation of a displayed window. SDL can only do one main
123  * window so we map everything within that one
124  */
125 struct display_window {
126         SDL_Overlay     *bmp;
127         SDL_Rect        rect;   /* location of the window */
128 };
129
130 /* each thumbnail message board has a rectangle associated for the geometry,
131  * and a board structure, we include these two elements in a singole structure */
132 struct thumb_bd {
133         SDL_Rect                rect;           /* the rect for geometry and background */
134         struct board            *board;         /* the board */
135 };
136
137 struct gui_info {
138         enum kb_output          kb_output;      /* where the keyboard output goes */
139         struct drag_info        drag;           /* info on the window are we dragging */
140         /* support for display. */
141         SDL_Surface             *screen;        /* the main window */
142
143         int                     outfd;          /* fd for output */
144         SDL_Surface             *keypad;        /* the skin for the keypad */
145         SDL_Rect                kp_rect;        /* portion of the skin to display - default all */
146         SDL_Surface             *font;          /* font to be used */ 
147         SDL_Rect                font_rects[96]; /* only printable chars */
148
149         /* each of the following board has two rectangles,
150          * [0] is the geometry relative to the keypad,
151          * [1] is the geometry relative to the whole screen
152          * we do not use the thumb_bd for these boards because here we need
153          * 2 rectangles for geometry
154          */
155         SDL_Rect                kp_msg[2];              /* incoming msg, relative to kpad */
156         struct board            *bd_msg;
157
158         SDL_Rect                kp_edit[2];     /* edit user input */
159         struct board            *bd_edit;
160
161         SDL_Rect                kp_dialed[2];   /* dialed number */
162         struct board            *bd_dialed;
163
164         /* other boards are one associated with the source windows
165          * above the keypad in the layout, we only have the geometry
166          * relative to the whole screen
167          */
168         struct thumb_bd         thumb_bd_array[MAX_VIDEO_SOURCES];
169
170         /* variable-size array mapping keypad regions to functions */
171         int kp_size, kp_used;
172         struct keypad_entry *kp;
173
174         struct display_window   win[WIN_MAX];
175 };
176
177 /*! \brief free the resources in struct gui_info and the descriptor itself.
178  *  Return NULL so we can assign the value back to the descriptor in case.
179  */
180 static struct gui_info *cleanup_sdl(struct gui_info *gui, int device_num)
181 {
182         int i;
183
184         if (gui == NULL)
185                 return NULL;
186
187         /* unload font file */ 
188         if (gui->font) {
189                 SDL_FreeSurface(gui->font);
190                 gui->font = NULL; 
191         }
192
193         if (gui->outfd > -1)
194                 close(gui->outfd);
195         if (gui->keypad)
196                 SDL_FreeSurface(gui->keypad);
197         gui->keypad = NULL;
198         if (gui->kp)
199                 ast_free(gui->kp);
200
201         /* uninitialize the SDL environment */
202         for (i = 0; i < WIN_MAX; i++) {
203                 if (gui->win[i].bmp)
204                         SDL_FreeYUVOverlay(gui->win[i].bmp);
205         }
206         bzero(gui, sizeof(gui));
207
208         /* deallocates the space allocated for the keypad message boards */
209         if (gui->bd_dialed)
210                 delete_board(gui->bd_dialed);
211         if (gui->bd_msg)
212                 delete_board(gui->bd_msg);
213
214         /* deallocates the space allocated for the thumbnail message boards */
215         for (i = 0; i < device_num; i++) {
216                 if (gui->thumb_bd_array[i].board) /* may be useless */
217                         delete_board(gui->thumb_bd_array[i].board);
218         }
219         
220         ast_free(gui);
221         SDL_Quit();
222         return NULL;
223 }
224
225 /* messages to be displayed in the sources message boards
226  * below the source windows
227  */
228
229 /* costants defined to describe status of devices */
230 #define IS_PRIMARY 1
231 #define IS_SECONDARY 2
232 #define IS_ON 4
233
234 char* src_msgs[] = {
235         "    OFF",
236         "1   OFF",
237         "  2 OFF",
238         "1+2 OFF",
239         "    ON",
240         "1   ON",
241         "  2 ON",
242         "1+2 ON",
243 };
244 /*
245  * Display video frames (from local or remote stream) using the SDL library.
246  * - Set the video mode to use the resolution specified by the codec context
247  * - Create a YUV Overlay to copy the frame into it;
248  * - After the frame is copied into the overlay, display it
249  *
250  * The size is taken from the configuration.
251  *
252  * 'out' is 0 for remote video, 1 for the local video
253  */
254 static void show_frame(struct video_desc *env, int out)
255 {
256         AVPicture *p_in, p_out;
257         struct fbuf_t *b_in, *b_out;
258         SDL_Overlay *bmp;
259         struct gui_info *gui = env->gui;
260
261         if (!gui)
262                 return;
263
264         if (out == WIN_LOCAL) { /* webcam/x11 to sdl */
265                 b_in = &env->enc_in;
266                 b_out = &env->loc_dpy;
267                 p_in = NULL;
268         } else if (out == WIN_REMOTE) {
269                 /* copy input format from the decoding context */
270                 AVCodecContext *c;
271                 if (env->in == NULL)    /* XXX should not happen - decoder not ready */
272                         return;
273                 c = env->in->dec_ctx;
274                 b_in = &env->in->dec_out;
275                 b_in->pix_fmt = c->pix_fmt;
276                 b_in->w = c->width;
277                 b_in->h = c->height;
278
279                 b_out = &env->rem_dpy;
280                 p_in = (AVPicture *)env->in->d_frame;
281         } else {
282                 int i = out-WIN_SRC1;
283                 b_in = env->out.devices[i].dev_buf;
284                 if (b_in == NULL)
285                         return;
286                 p_in = NULL;
287                 b_out = &env->src_dpy[i];
288         }               
289         bmp = gui->win[out].bmp;
290         SDL_LockYUVOverlay(bmp);
291         /* output picture info - this is sdl, YUV420P */
292         bzero(&p_out, sizeof(p_out));
293         p_out.data[0] = bmp->pixels[0];
294         p_out.data[1] = bmp->pixels[1];
295         p_out.data[2] = bmp->pixels[2];
296         p_out.linesize[0] = bmp->pitches[0];
297         p_out.linesize[1] = bmp->pitches[1];
298         p_out.linesize[2] = bmp->pitches[2];
299
300         my_scale(b_in, p_in, b_out, &p_out);
301
302         /* lock to protect access to Xlib by different threads. */
303         SDL_DisplayYUVOverlay(bmp, &gui->win[out].rect);
304         SDL_UnlockYUVOverlay(bmp);
305 }
306
307 /*
308  * Identifiers for regions of the main window.
309  * Values between 0 and 127 correspond to ASCII characters.
310  * The corresponding strings to be used in the skin comment section
311  * are defined in gui_key_map.
312  */
313 enum skin_area {
314         /* answer/close functions */
315         KEY_PICK_UP = 128,
316         KEY_HANG_UP = 129,
317
318         KEY_MUTE = 130,
319         KEY_AUTOANSWER = 131,
320         KEY_SENDVIDEO = 132,
321         KEY_LOCALVIDEO = 133,
322         KEY_REMOTEVIDEO = 134,
323         KEY_FLASH = 136,
324
325         /* sensitive areas for the various text windows */
326         KEY_MESSAGEBOARD = 140,
327         KEY_DIALEDBOARD = 141,
328         KEY_EDITBOARD = 142,
329
330         KEY_GUI_CLOSE = 199,            /* close gui */
331         /* regions of the skin - displayed area, fonts, etc.
332          * XXX NOTE these are not sensitive areas.
333          */
334         KEY_KEYPAD = 200,               /* the keypad - default to the whole image */
335         KEY_FONT = 201,         /* the font. Maybe not really useful */
336         KEY_MESSAGE = 202,      /* area for incoming messages */
337         KEY_DIALED = 203,       /* area for dialed numbers */
338         KEY_EDIT = 204,         /* area for editing user input */
339
340 #ifdef notyet /* XXX for future implementation */
341         KEY_AUDIO_SRCS = 210,
342         /*indexes between 210 and 219 (or more) have been reserved for the "keys"
343         associated with the audio device markers, clicking on these markers
344         will change the source device for audio output */
345
346         KEY_FREEZE = 220,       /* freeze the incoming video */
347         KEY_CAPTURE = 221,      /* capture the whole SDL window as a picture */
348 #endif
349         /* video source switching key(s) */
350         KEY_PIP = 230,
351         /*indexes between 231 and 239 have been reserved for the "keys"
352         associated with the device thumbnails, clicking on these pictures
353         will change the source device for primary or secondary (PiP) video output*/
354         KEY_SRCS_WIN = 231, /* till 239 */
355         /* areas outside the keypad - simulated */
356         KEY_OUT_OF_KEYPAD = 241,
357         KEY_REM_DPY = 242,
358         KEY_LOC_DPY = 243,
359         KEY_RESET = 253,                /* the 'reset' keyword */
360         KEY_NONE = 254,                 /* invalid area */
361         KEY_DIGIT_BACKGROUND = 255,     /* other areas within the keypad */
362 };
363
364 /*
365  * Handlers for the various keypad functions
366  */
367
368 /* accumulate digits, possibly call dial if in connected mode */
369 static void keypad_digit(struct video_desc *env, int digit)
370 {       
371         if (env->owner) {               /* we have a call, send the digit */
372                 struct ast_frame f = { AST_FRAME_DTMF, 0 };
373
374                 f.subclass = digit;
375                 ast_queue_frame(env->owner, &f);
376         } else {                /* no call, accumulate digits */
377                 char buf[2] = { digit, '\0' };
378                 if (env->gui->bd_msg) /* XXX not strictly necessary ... */
379                         print_message(env->gui->bd_msg, buf);
380         }
381 }
382
383 /* function used to toggle on/off the status of some variables */
384 static char *keypad_toggle(struct video_desc *env, int index)
385 {
386         ast_log(LOG_WARNING, "keypad_toggle(%i) called\n", index);
387
388         switch (index) {
389         case KEY_SENDVIDEO: /* send or do not send video */
390                 env->out.sendvideo = !env->out.sendvideo;
391                 break;
392         case KEY_PIP: /* enable or disable Picture in Picture */
393                 env->out.picture_in_picture = !env->out.picture_in_picture;
394                 break;
395         case KEY_MUTE: /* send or do not send audio */
396                 ast_cli_command(env->gui->outfd, "console mute toggle");
397                 break;
398 #ifdef notyet
399         case KEY_AUTOANSWER: {
400                 struct chan_oss_pvt *o = find_desc(oss_active);
401                 o->autoanswer = !o->autoanswer;
402                 }
403                 break;
404 #endif
405         }
406         return NULL;
407 }
408
409 char *console_do_answer(int fd);
410 /*
411  * Function called when the pick up button is pressed
412  * perform actions according the channel status:
413  *
414  *  - if no one is calling us and no digits was pressed,
415  *    the operation have no effects,
416  *  - if someone is calling us we answer to the call.
417  *  - if we have no call in progress and we pressed some
418  *    digit, send the digit to the console.
419  */
420 static void keypad_pick_up(struct video_desc *env)
421 {
422         struct gui_info *gui = env->gui;
423
424         ast_log(LOG_WARNING, "keypad_pick_up called\n");
425
426         if (env->owner) { /* someone is calling us, just answer */
427                 ast_cli_command(gui->outfd, "console answer");
428         } else { /* we have someone to call */
429                 char buf[160];
430                 const char *who = ast_skip_blanks(read_message(gui->bd_msg));
431                 buf[sizeof(buf) - 1] = '\0';
432                 snprintf(buf, sizeof(buf), "console dial %s", who);
433                 ast_log(LOG_WARNING, "doing <%s>\n", buf);
434                 print_message(gui->bd_dialed, "\n");
435                 print_message(gui->bd_dialed, who);
436                 reset_board(gui->bd_msg);
437                 ast_cli_command(gui->outfd, buf);
438         }
439 }
440
441 #if 0 /* still unused */
442 /*
443  * As an alternative to SDL_TTF, we can simply load the font from
444  * an image and blit characters on the background of the GUI.
445  *
446  * To generate a font we can use the 'fly' command with the
447  * following script (3 lines with 32 chars each)
448  
449 size 320,64
450 name font.png
451 transparent 0,0,0
452 string 255,255,255,  0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
453 string 255,255,255,  0,20,giant,@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
454 string 255,255,255,  0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
455 end
456
457  */
458
459 /* Print given text on the gui */
460 static int gui_output(struct video_desc *env, const char *text)
461 {
462         return 1;       /* error, not supported */
463 }
464 #endif 
465
466 static int video_geom(struct fbuf_t *b, const char *s);
467 static void sdl_setup(struct video_desc *env);
468 static int kp_match_area(const struct keypad_entry *e, int x, int y);
469
470 static void set_drag(struct drag_info *drag, int x, int y, enum drag_window win)
471 {
472         drag->x_start = x;
473         drag->y_start = y;
474         drag->drag_window = win;
475 }
476
477 static int update_device_info(struct video_desc *env, int i)
478 {
479         reset_board(env->gui->thumb_bd_array[i].board);
480         print_message(env->gui->thumb_bd_array[i].board,
481                 src_msgs[env->out.devices[i].status_index]);
482         return 0;
483 }
484
485 /*! \brief Changes the video output (local video) source, controlling if
486  * it is already using that video device, 
487  * and switching the correct fields of env->out.
488  * grabbers are always open and saved in the device table.
489  * The secondary or the primary device can be changed,
490  * according to the "button" parameter:
491  * the primary device is changed if button = SDL_BUTTON_LEFT;
492  * the secondary device is changed if button = not SDL_BUTTON_LEFT;
493  * 
494  * the correct message boards of the sources are also updated
495  * with the new status
496  * 
497  * \param env = pointer to the video environment descriptor
498  * \param index = index of the device the caller wants to use are primary or secondary device
499  * \param button = button clicked on the mouse
500  *
501  * returns 0 on success,
502  * returns 1 on error 
503  */
504 static int switch_video_out(struct video_desc *env, int index, Uint8 button)
505 {
506         int *p; /* pointer to the index of the device to select */
507
508         if (index >= env->out.device_num) {
509                 ast_log(LOG_WARNING, "no devices\n");
510                 return 1;
511         }
512         /* select primary or secondary */
513         p = (button == SDL_BUTTON_LEFT) ? &env->out.device_primary :
514                 &env->out.device_secondary;
515         /* controls if the device is already selected */
516         if (index == *p) {
517                 ast_log(LOG_WARNING, "device %s already selected\n", env->out.devices[index].name);
518                 return 0;
519         }
520         ast_log(LOG_WARNING, "switching to %s...\n", env->out.devices[index].name);
521         /* already open */
522         if (env->out.devices[index].grabber) {
523                 /* we also have to update the messages in the source 
524                 message boards below the source windows */
525                 /* first we update the board of the previous source */
526                 if (p == &env->out.device_primary)
527                         env->out.devices[*p].status_index &= ~IS_PRIMARY;
528                 else
529                         env->out.devices[*p].status_index &= ~IS_SECONDARY;
530                 update_device_info(env, *p);
531                 /* update the index used as primary or secondary */
532                 *p = index;
533                 ast_log(LOG_WARNING, "done\n");
534                 /* then we update the board of the new primary or secondary source */
535                 if (p == &env->out.device_primary)
536                         env->out.devices[*p].status_index |= IS_PRIMARY;
537                 else
538                         env->out.devices[*p].status_index |= IS_SECONDARY;
539                 update_device_info(env, *p);
540                 return 0;
541         }
542         /* device is off, just do nothing */
543         ast_log(LOG_WARNING, "device is down\n");
544         return 1;
545 }
546
547 /*! \brief tries to switch the state of a device from on to off or off to on
548  * we also have to update the status of the device and the correct message board
549  *
550  * \param index = the device that must be turned on or off
551  * \param env = pointer to the video environment descriptor
552  *
553  * returns:
554  * - 0 on falure switching from off to on
555  * - 1 on success in switching from off to on
556  * - 2 on success in switching from on to off
557 */
558 static int turn_on_off(int index, struct video_desc *env)
559 {
560         struct video_device *p = &env->out.devices[index];
561
562         if (index >= env->out.device_num) {
563                 ast_log(LOG_WARNING, "no devices\n");
564                 return 0;
565         }
566
567         if (!p->grabber) { /* device off */
568                 void *g_data; /* result of grabber_open() */
569                 struct grab_desc *g;
570                 int i;
571
572                 /* see if the device can be used by one of the existing drivers */
573                 for (i = 0; (g = console_grabbers[i]); i++) {
574                         /* try open the device */
575                         g_data = g->open(p->name, &env->out.loc_src_geometry, env->out.fps);
576                         if (!g_data)    /* no luck, try the next driver */
577                                 continue;
578                         p->grabber = g;
579                         p->grabber_data = g_data;
580                         /* update the status of the source */
581                         p->status_index |= IS_ON;
582                         /* print the new message in the message board */
583                         update_device_info(env, index);
584                         return 1; /* open succeded */
585                 }
586                 return 0; /* failure */
587         } else {
588                 /* the grabber must be closed */
589                 p->grabber_data = p->grabber->close(p->grabber_data);
590                 p->grabber = NULL;
591                 /* dev_buf is already freed by grabber->close() */
592                 p->dev_buf = NULL;
593                 /* update the status of the source */
594                 p->status_index &= ~IS_ON;
595                 /* print the new message in the message board */
596                 update_device_info(env, index);
597                 return 2; /* closed */
598         }       
599 }
600
601 /*
602  * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
603  * index value and calling the right callback.
604  *
605  * x, y are referred to the upper left corner of the main SDL window.
606  */
607 static void handle_mousedown(struct video_desc *env, SDL_MouseButtonEvent button)
608 {
609         uint8_t index = KEY_OUT_OF_KEYPAD;      /* the key or region of the display we clicked on */
610         struct gui_info *gui = env->gui;
611                 
612         int i; /* integer variable used as iterator */
613
614         int x; /* integer variable usable as a container */
615         
616         /* total width of source device thumbnails */
617         int src_wins_tot_w = env->out.device_num*(SRC_WIN_W+BORDER)+BORDER;
618
619         /* x coordinate of the center of the keypad */
620         int x0 = MAX(env->rem_dpy.w+gui->keypad->w/2+2*BORDER, src_wins_tot_w/2);
621         
622 #if 0
623         ast_log(LOG_WARNING, "event %d %d have %d/%d regions at %p\n",
624                 button.x, button.y, gui->kp_used, gui->kp_size, gui->kp);
625 #endif
626         /* for each mousedown we end previous drag */
627         gui->drag.drag_window = DRAG_NONE;
628         
629         /* define keypad boundary */
630         /* XXX this should be extended for clicks on different audio device markers */
631         if (button.y >= (env->out.device_num ? SRC_WIN_H+2*BORDER+SRC_MSG_BD_H : 0)) {
632                 /* if control reaches this point this means that the clicked point is
633                 below the row of the additional sources windows*/
634                 /* adjust the y coordinate as if additional devices windows were not present */
635                 button.y -= (env->out.device_num ? SRC_WIN_H+2*BORDER+SRC_MSG_BD_H : 0);
636                 if (button.y < BORDER)
637                         index = KEY_OUT_OF_KEYPAD;
638                 else if (button.y >= MAX(MAX(env->rem_dpy.h, env->loc_dpy.h), gui->keypad->h))
639                         index = KEY_OUT_OF_KEYPAD;
640                 else if (button.x < x0 - gui->keypad->w/2 - BORDER - env->rem_dpy.w)
641                         index = KEY_OUT_OF_KEYPAD;
642                 else if (button.x < x0 - gui->keypad->w/2 - BORDER)
643                         index = KEY_REM_DPY;
644                 else if (button.x < x0 - gui->keypad->w/2)
645                         index = KEY_OUT_OF_KEYPAD;
646                 else if (button.x >= x0 + gui->keypad->w/2 + BORDER + env->loc_dpy.w)
647                         index = KEY_OUT_OF_KEYPAD;
648                 else if (button.x >= x0 + gui->keypad->w/2 + BORDER)
649                         index = KEY_LOC_DPY;
650                 else if (button.x >= x0 + gui->keypad->w/2)
651                         index = KEY_OUT_OF_KEYPAD;
652                 else if (gui->kp) {
653                         /* we have to calculate the first coordinate 
654                         inside the keypad before calling the kp_match_area*/
655                         int x_keypad = button.x - (x0 - gui->keypad->w/2);
656                         /* find the key clicked (if one was clicked) */
657                         for (i = 0; i < gui->kp_used; i++) {
658                                 if (kp_match_area(&gui->kp[i],x_keypad, button.y - BORDER)) {
659                                         index = gui->kp[i].c;
660                                         break;
661                                 }
662                         }
663                 }
664         } else if (button.y < BORDER) {
665                 index = KEY_OUT_OF_KEYPAD;
666         } else {  /* we are in the thumbnail area */
667                 x = x0 - src_wins_tot_w/2 + BORDER;
668                 if (button.y >= BORDER + SRC_WIN_H)
669                         index = KEY_OUT_OF_KEYPAD;
670                 else if (button.x < x)
671                         index = KEY_OUT_OF_KEYPAD;
672                 else if (button.x < x + src_wins_tot_w - BORDER) {
673                         /* note that the additional device windows 
674                         are numbered from left to right
675                         starting from 0, with a maximum of 8, the index associated on a click is:
676                         KEY_SRCS_WIN + number_of_the_window */
677                         for (i = 1; i <= env->out.device_num; i++) {
678                                 if (button.x < x+i*(SRC_WIN_W+BORDER)-BORDER) {
679                                         index = KEY_SRCS_WIN+i-1;
680                                         break;
681                                 } else if (button.x < x+i*(SRC_WIN_W+BORDER)) {
682                                         index = KEY_OUT_OF_KEYPAD;
683                                         break;
684                                 }
685                         }
686                 } else
687                         index = KEY_OUT_OF_KEYPAD;
688         }
689
690         /* exec the function */
691         if (index < 128) {      /* surely clicked on the keypad, don't care which key */
692                 keypad_digit(env, index);
693                 return;
694         }
695
696         else if (index >= KEY_SRCS_WIN && index < KEY_SRCS_WIN+env->out.device_num) {
697                 index -= KEY_SRCS_WIN; /* index of the window, equal to the device index in the table */
698                 /* if one of the additional device windows is clicked with
699                 left or right mouse button, we have to switch to that device */
700                 if (button.button == SDL_BUTTON_RIGHT || button.button == SDL_BUTTON_LEFT) {
701                         switch_video_out(env, index, button.button);
702                         return;
703                 }
704                 /* turn on or off the devices selectively with other mouse buttons */
705                 else {
706                         int ret = turn_on_off(index, env);
707                         /* print a message according to what happened */
708                         if (!ret)
709                                 ast_log(LOG_WARNING, "unable to turn on device %s\n",
710                                         env->out.devices[index].name);
711                         else if (ret == 1)
712                                 ast_log(LOG_WARNING, "device %s changed state to on\n",
713                                         env->out.devices[index].name);
714                         else if (ret == 2)
715                                 ast_log(LOG_WARNING, "device %s changed state to off\n",
716                                         env->out.devices[index].name);
717                         return;
718                 }
719         }
720
721         /* XXX for future implementation
722         else if (click on audio source marker)
723                 change audio source device
724         */
725
726         switch (index) {
727         /* answer/close function */
728         case KEY_PICK_UP:
729                 keypad_pick_up(env);
730                 break;
731         case KEY_HANG_UP:
732                 ast_cli_command(gui->outfd, "console hangup");
733                 break;
734
735         /* other functions */
736         case KEY_MUTE: /* send or not send the audio */
737         case KEY_AUTOANSWER:
738         case KEY_SENDVIDEO: /* send or not send the video */
739         case KEY_PIP: /* activate/deactivate picture in picture mode */
740                 keypad_toggle(env, index);
741                 break;
742
743         case KEY_LOCALVIDEO:
744                 break;
745         case KEY_REMOTEVIDEO:
746                 break;
747
748 #ifdef notyet /* XXX for future implementations */
749         case KEY_FREEZE:
750                 break
751         case KEY_CAPTURE:
752                 break;
753 #endif
754
755         case KEY_MESSAGEBOARD:
756                 if (button.button == SDL_BUTTON_LEFT)
757                         set_drag(&gui->drag, button.x, button.y, DRAG_MESSAGE);
758                 break;
759
760         /* press outside the keypad. right increases size, center decreases, left drags */
761         case KEY_LOC_DPY:
762         case KEY_REM_DPY:
763                 if (button.button == SDL_BUTTON_LEFT) {
764                         /* values used to find the position of the picture in picture (if present) */
765                         int pip_loc_x = (double)env->out.pip_x/env->enc_in.w * env->loc_dpy.w;
766                         int pip_loc_y = (double)env->out.pip_y/env->enc_in.h * env->loc_dpy.h;
767                         /* check if picture in picture is active and the click was on it */
768                         if (index == KEY_LOC_DPY && env->out.picture_in_picture &&
769                           button.x >= x0+gui->keypad->w/2+BORDER+pip_loc_x &&
770                           button.x < x0+gui->keypad->w/2+BORDER+pip_loc_x+env->loc_dpy.w/3 &&
771                           button.y >= BORDER+pip_loc_y && 
772                           button.y < BORDER+pip_loc_y+env->loc_dpy.h/3) {
773                                 /* set the y cordinate to his previous value */
774                                 button.y += (env->out.device_num ? SRC_WIN_H+2*BORDER+SRC_MSG_BD_H : 0);
775                                 /* starts dragging the picture inside the picture */
776                                 set_drag(&gui->drag, button.x, button.y, DRAG_PIP);
777                         }
778                         else if (index == KEY_LOC_DPY) {
779                                 /* set the y cordinate to his previous value */
780                                 button.y += (env->out.device_num ? SRC_WIN_H+2*BORDER+SRC_MSG_BD_H : 0);
781                                 /* click in the local display, but not on the PiP */
782                                 set_drag(&gui->drag, button.x, button.y, DRAG_LOCAL);
783                         }
784                         break;
785                 } else {
786                         char buf[128];
787                         struct fbuf_t *fb = index == KEY_LOC_DPY ? &env->loc_dpy : &env->rem_dpy;
788                         sprintf(buf, "%c%dx%d", button.button == SDL_BUTTON_RIGHT ? '>' : '<',
789                                 fb->w, fb->h);
790                         video_geom(fb, buf);
791                         sdl_setup(env);
792                         /* writes messages in the source boards, those can be 
793                         modified during the execution, because of the events 
794                         this must be done here, otherwise the status of sources will not be
795                         shown after sdl_setup */
796                         for (i = 0; i < env->out.device_num; i++) {
797                                 update_device_info(env, i);
798                         }
799                         /* we also have to refresh other boards, 
800                         to avoid messages to disappear after video resize */
801                         print_message(gui->bd_msg, " \b");
802                         print_message(gui->bd_dialed, " \b");
803                 }
804                 break;
805         case KEY_OUT_OF_KEYPAD:
806                 ast_log(LOG_WARNING, "nothing clicked, coordinates: %d, %d\n", button.x, button.y);
807                 break;
808
809         case KEY_DIGIT_BACKGROUND:
810                 break;
811
812         default:
813                 ast_log(LOG_WARNING, "function not yet defined %i\n", index);
814         }
815 }
816
817 /*
818  * Handle SDL_KEYDOWN type event, put the key pressed
819  * in the dial buffer or in the text-message buffer,
820  * depending on the text_mode variable value.
821  *
822  * key is the SDLKey structure corresponding to the key pressed.
823  * Note that SDL returns modifiers (ctrl, shift, alt) as independent
824  * information so the key itself is not enough and we need to
825  * use a translation table, below - one line per entry,
826  * plain, shift, ctrl, ... using the first char as key.
827  */
828 static const char *us_kbd_map[] = {
829         "`~", "1!", "2@", "3#", "4$", "5%", "6^",
830         "7&", "8*", "9(", "0)", "-_", "=+", "[{",
831         "]}", "\\|", ";:", "'\"", ",<", ".>", "/?",
832         "jJ\n",
833         NULL
834 };
835
836 static char map_key(SDL_keysym *ks)
837 {
838         const char *s, **p = us_kbd_map;
839         int c = ks->sym;
840
841         if (c == '\r')  /* map cr into lf */
842                 c = '\n';
843         if (c >= SDLK_NUMLOCK && c <= SDLK_COMPOSE)
844                 return 0;       /* only a modifier */
845         if (ks->mod == 0)
846                 return c;
847         while ((s = *p) && s[0] != c)
848                 p++;
849         if (s) { /* see if we have a modifier and a chance to use it */
850                 int l = strlen(s), mod = 0;
851                 if (l > 1)
852                         mod |= (ks->mod & KMOD_SHIFT) ? 1 : 0;
853                 if (l > 2 + mod)
854                         mod |= (ks->mod & KMOD_CTRL) ? 2 : 0;
855                 if (l > 4 + mod)
856                         mod |= (ks->mod & KMOD_ALT) ? 4 : 0;
857                 c = s[mod];
858         }
859         if (ks->mod & (KMOD_CAPS|KMOD_SHIFT) && c >= 'a' && c <='z')
860                 c += 'A' - 'a';
861         return c;
862 }
863
864 static void handle_keyboard_input(struct video_desc *env, SDL_keysym *ks)
865 {
866         char buf[2] = { map_key(ks), '\0' };
867         struct gui_info *gui = env->gui;
868         if (buf[0] == 0)        /* modifier ? */
869                 return;
870         switch (gui->kb_output) {
871         default:
872                 break;
873         case KO_INPUT:  /* to be completed */
874                 break;
875         case KO_MESSAGE:
876                 if (gui->bd_msg) {
877                         print_message(gui->bd_msg, buf);
878                         if (buf[0] == '\r' || buf[0] == '\n') {
879                                 keypad_pick_up(env);
880                         }
881                 }
882                 break;
883
884         case KO_DIALED: /* to be completed */
885                 break;
886         }
887
888         return;
889 }
890
891 static void grabber_move(struct video_device *, int dx, int dy);
892
893 int compute_drag(int *start, int end, int magnifier);
894 int compute_drag(int *start, int end, int magnifier)
895 {
896         int delta = end - *start;
897 #define POLARITY -1
898         /* add a small quadratic term */
899         delta += delta * delta * (delta > 0 ? 1 : -1 )/100;
900         delta *= POLARITY * magnifier;
901 #undef POLARITY
902         *start = end;
903         return delta;
904 }
905
906 /*! \brief This function moves the picture in picture,
907  * controlling the limits of the containing buffer
908  * to avoid problems deriving from going through the limits.
909  *
910  * \param env = pointer to the descriptor of the video environment
911  * \param dx = the variation of the x position
912  * \param dy = the variation of the y position
913 */
914 static void pip_move(struct video_desc* env, int dx, int dy) {
915         int new_pip_x = env->out.pip_x+dx;
916         int new_pip_y = env->out.pip_y+dy;
917         /* going beyond the left borders */
918         if (new_pip_x < 0)
919                 new_pip_x = 0;
920         /* going beyond the right borders */
921         else if (new_pip_x > env->enc_in.w - env->enc_in.w/3)
922                 new_pip_x = env->enc_in.w - env->enc_in.w/3;
923         /* going beyond the top borders */
924         if (new_pip_y < 0)
925                 new_pip_y = 0;
926         /* going beyond the bottom borders */
927         else if (new_pip_y > env->enc_in.h - env->enc_in.h/3)
928                 new_pip_y = env->enc_in.h - env->enc_in.h/3;
929         env->out.pip_x = new_pip_x;
930         env->out.pip_y = new_pip_y;
931 }
932
933 /*
934  * I am seeing some kind of deadlock or stall around
935  * SDL_PumpEvents() while moving the window on a remote X server
936  * (both xfree-4.4.0 and xorg 7.2)
937  * and windowmaker. It is unclear what causes it.
938  */
939
940 /*! \brief refresh the screen, and also grab a bunch of events.
941  */
942 static void eventhandler(struct video_desc *env, const char *caption)
943 {
944         struct gui_info *gui = env->gui;
945         struct drag_info *drag;
946 #define N_EVENTS        32
947         int i, n;
948         SDL_Event ev[N_EVENTS];
949
950         if (!gui)
951                 return;
952         drag = &gui->drag;
953         if (caption)
954                 SDL_WM_SetCaption(caption, NULL);
955
956 #define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
957         while ( (n = SDL_PeepEvents(ev, N_EVENTS, SDL_GETEVENT, SDL_ALLEVENTS)) > 0) {
958                 for (i = 0; i < n; i++) {
959 #if 0
960                         ast_log(LOG_WARNING, "------ event %d at %d %d\n",
961                                 ev[i].type,  ev[i].button.x,  ev[i].button.y);
962 #endif
963                         switch (ev[i].type) {
964                         default:
965                                 ast_log(LOG_WARNING, "------ event %d at %d %d\n",
966                                         ev[i].type,  ev[i].button.x,  ev[i].button.y);
967                                 break;
968
969                         case SDL_ACTIVEEVENT:
970 #if 0 /* do not react, we don't want to die because the window is minimized */
971                                 if (ev[i].active.gain == 0 && ev[i].active.state & SDL_APPACTIVE) {
972                                         ast_log(LOG_WARNING, "/* somebody has killed us ? */");
973                                         ast_cli_command(gui->outfd, "stop now");
974                                 }
975 #endif
976                                 break;
977
978                         case SDL_KEYUP: /* ignore, for the time being */
979                                 break;
980
981                         case SDL_KEYDOWN:
982                                 handle_keyboard_input(env, &ev[i].key.keysym);
983                                 break;
984
985                         case SDL_MOUSEMOTION:
986                         case SDL_MOUSEBUTTONUP:
987                                 if (drag->drag_window == DRAG_LOCAL && env->out.device_num) {
988                                         /* move the capture source */
989                                         int dx = compute_drag(&drag->x_start, ev[i].motion.x, 3);
990                                         int dy = compute_drag(&drag->y_start, ev[i].motion.y, 3);
991                                         grabber_move(&env->out.devices[env->out.device_primary], dx, dy);
992                                 } else if (drag->drag_window == DRAG_PIP) {
993                                         /* move the PiP image inside the frames of the enc_in buffers */
994                                         int dx = ev[i].motion.x - drag->x_start;
995                                         int dy = ev[i].motion.y - drag->y_start;
996                                         /* dx and dy value are directly applied to env->out.pip_x and
997                                         env->out.pip_y, so they must work as if the format was cif */
998                                         dx = (double)dx*env->enc_in.w/env->loc_dpy.w;
999                                         dy = (double)dy*env->enc_in.h/env->loc_dpy.h;
1000                                         /* sets starts to a new value */
1001                                         drag->x_start = ev[i].motion.x;
1002                                         drag->y_start = ev[i].motion.y;
1003                                         /* ast_log(LOG_WARNING, "moving: %d, %d\n", dx, dy); */
1004                                         pip_move(env, dx, dy);
1005                                 } else if (drag->drag_window == DRAG_MESSAGE) {
1006                                         /* scroll up/down the window */
1007                                         int dy = compute_drag(&drag->y_start, ev[i].motion.y, 1);
1008                                         move_message_board(gui->bd_msg, dy);
1009                                 }
1010                                 if (ev[i].type == SDL_MOUSEBUTTONUP)
1011                                         drag->drag_window = DRAG_NONE;
1012                                 break;
1013                         case SDL_MOUSEBUTTONDOWN:
1014                                 handle_mousedown(env, ev[i].button);
1015                                 break;
1016                         }
1017                 }
1018         }
1019         if (1) {
1020                 struct timeval b, a = ast_tvnow();
1021                 int i;
1022                 //SDL_Lock_EventThread();
1023                 SDL_PumpEvents();
1024                 b = ast_tvnow();
1025                 i = ast_tvdiff_ms(b, a);
1026                 if (i > 3)
1027                         fprintf(stderr, "-------- SDL_PumpEvents took %dms\n", i);
1028                 //SDL_Unlock_EventThread();
1029         }
1030 }
1031
1032 static SDL_Surface *load_image(const char *file)
1033 {
1034         SDL_Surface *temp;
1035  
1036 #ifdef HAVE_SDL_IMAGE
1037         temp = IMG_Load(file);
1038 #else
1039         temp = SDL_LoadBMP(file);
1040 #endif
1041         if (temp == NULL)
1042                 fprintf(stderr, "Unable to load image %s: %s\n",
1043                         file, SDL_GetError());
1044         return temp;
1045 }
1046
1047 static void keypad_setup(struct gui_info *gui, const char *kp_file);
1048
1049 /* TODO: consistency checks, check for bpp, widht and height */
1050 /* Init the mask image used to grab the action. */
1051 static struct gui_info *gui_init(const char *keypad_file, const char *font)
1052 {
1053         struct gui_info *gui = ast_calloc(1, sizeof(*gui));
1054
1055         if (gui == NULL)
1056                 return NULL;
1057         /* initialize keypad status */
1058         gui->kb_output = KO_MESSAGE;    /* XXX temp */
1059         gui->drag.drag_window = DRAG_NONE;
1060         gui->outfd = -1;
1061
1062         keypad_setup(gui, keypad_file);
1063         if (gui->keypad == NULL)        /* no keypad, we are done */
1064                 return gui;
1065         /* XXX load image */
1066         if (!ast_strlen_zero(font)) {
1067                 int i;
1068                 SDL_Rect *r;
1069
1070                 gui->font = load_image(font);
1071                 if (!gui->font) {
1072                         ast_log(LOG_WARNING, "Unable to load font %s, no output available\n", font);
1073                         goto error;
1074                 }
1075                 ast_log(LOG_WARNING, "Loaded font %s\n", font);
1076                 /* XXX hardwired constants - 3 rows of 32 chars */
1077                 r = gui->font_rects;
1078 #define FONT_H 20
1079 #define FONT_W 9
1080                 for (i = 0; i < 96; r++, i++) {
1081                         r->x = (i % 32 ) * FONT_W;
1082                         r->y = (i / 32 ) * FONT_H;
1083                         r->w = FONT_W;
1084                         r->h = FONT_H;
1085                 }
1086         }
1087
1088         gui->outfd = open ("/dev/null", O_WRONLY);      /* discard output, temporary */
1089         if (gui->outfd < 0) {
1090                 ast_log(LOG_WARNING, "Unable output fd\n");
1091                 goto error;
1092         }
1093         return gui;
1094
1095 error:
1096         ast_free(gui);
1097         return NULL;
1098 }
1099
1100 /* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
1101 static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
1102         int w, int h, int x, int y)
1103 {
1104         win->bmp = SDL_CreateYUVOverlay(w, h, fmt, screen);
1105         if (win->bmp == NULL)
1106                 return -1;      /* error */
1107         win->rect.x = x;
1108         win->rect.y = y;
1109         win->rect.w = w;
1110         win->rect.h = h;
1111         return 0;
1112 }
1113
1114 static int keypad_cfg_read(struct gui_info *gui, const char *val);
1115
1116 static void keypad_setup(struct gui_info *gui, const char *kp_file)
1117 {
1118         FILE *fd;
1119         char buf[1024];
1120         const char region[] = "region";
1121         int reg_len = strlen(region);
1122         int in_comment = 0;
1123
1124         if (gui->keypad)
1125                 return;
1126         gui->keypad = load_image(kp_file);
1127         if (!gui->keypad)
1128                 return;
1129         /* now try to read the keymap from the file. */
1130         fd = fopen(kp_file, "r");
1131         if (fd == NULL) {
1132                 ast_log(LOG_WARNING, "fail to open %s\n", kp_file);
1133                 return;
1134         }
1135         /*
1136          * If the keypad image has a comment field, try to read
1137          * the button location from there. The block must start with
1138          * a comment (or empty) line, and continue with entries like:
1139          *      region = token shape x0 y0 x1 y1 h
1140          *      ...
1141          * (basically, lines have the same format as config file entries).
1142          * You can add it to a jpeg file using wrjpgcom
1143          */
1144         while (fgets(buf, sizeof(buf), fd)) {
1145                 char *s;
1146
1147                 if (!strstr(buf, region)) { /* no keyword yet */
1148                         if (!in_comment)        /* still waiting for initial comment block */
1149                                 continue;
1150                         else
1151                                 break;
1152                 }
1153                 if (!in_comment) {      /* first keyword, reset previous entries */
1154                         keypad_cfg_read(gui, "reset");
1155                         in_comment = 1;
1156                 }
1157                 s = ast_skip_blanks(buf);
1158                 ast_trim_blanks(s);
1159                 if (memcmp(s, region, reg_len))
1160                         break;  /* keyword not found */
1161                 s = ast_skip_blanks(s + reg_len); /* space between token and '=' */
1162                 if (*s++ != '=')        /* missing separator */
1163                         break;
1164                 if (*s == '>')  /* skip '>' if present */
1165                         s++;
1166                 keypad_cfg_read(gui, ast_skip_blanks(s));
1167         }
1168         fclose(fd);
1169 }
1170
1171 struct board *board_setup(SDL_Surface *screen, SDL_Rect *dest,
1172         SDL_Surface *font, SDL_Rect *font_rects);
1173
1174 /*! \brief initialize the boards we have in the keypad */
1175 static void init_board(struct gui_info *gui, struct board **dst, SDL_Rect *r, int dx, int dy)
1176 {
1177         if (r[0].w == 0 || r[0].h == 0)
1178                 return; /* not available */
1179         r[1] = r[0];    /* copy geometry */
1180         r[1].x += dx;   /* add offset of main window */
1181         r[1].y += dy;
1182         if (*dst == NULL) {     /* initial call */
1183                 *dst = board_setup(gui->screen, &r[1], gui->font, gui->font_rects);
1184         } else {
1185                 /* call a refresh */
1186         }
1187 }
1188
1189 #ifdef HAVE_X11
1190 /*
1191  * SDL is not very robust on error handling, so we need to trap ourselves
1192  * at least the most obvious failure conditions, e.g. a bad SDL_WINDOWID.
1193  * As of sdl-1.2.13, SDL_SetVideoMode crashes with bad parameters, so
1194  * we need to do the explicit X calls to make sure the window is correct.
1195  * And around these calls, we must trap X errors.
1196  */
1197 static int my_x_handler(Display *d, XErrorEvent *e)
1198 {
1199         ast_log(LOG_WARNING, "%s error_code %d\n", __FUNCTION__, e->error_code);
1200         return 0;
1201 }
1202 #endif /* HAVE_X11 */
1203
1204 /*! \brief [re]set the main sdl window, useful in case of resize.
1205  * We can tell the first from subsequent calls from the value of
1206  * env->gui, which is NULL the first time.
1207  */
1208 static void sdl_setup(struct video_desc *env)
1209 {
1210         int dpy_fmt = SDL_IYUV_OVERLAY; /* YV12 causes flicker in SDL */
1211         int depth, maxw, maxh;
1212         const SDL_VideoInfo *info;
1213         int kp_w = 0, kp_h = 0; /* keypad width and height */
1214         struct gui_info *gui = env->gui;
1215         
1216         /* Some helper variables used for filling the SDL window */
1217         int x0; /* the x coordinate of the center of the keypad */
1218         int x1; /* userful for calculating of the size of the parent window */
1219         int y0; /* y coordinate of the keypad, the remote window and the local window */
1220         int src_wins_tot_w; /* total width of the source windows */
1221         int i;
1222         int x; /* useful for the creation of the source windows; */
1223         
1224 #ifdef HAVE_X11
1225         const char *e = getenv("SDL_WINDOWID");
1226
1227         if (!ast_strlen_zero(e)) {
1228                 XWindowAttributes a;
1229                 int (*old_x_handler)(Display *d, XErrorEvent *e) = XSetErrorHandler(my_x_handler);
1230                 Display *d = XOpenDisplay(getenv("DISPLAY"));
1231                 long w = atol(e);
1232                 int success = w ? XGetWindowAttributes(d, w, &a) : 0;
1233
1234                 XSetErrorHandler(old_x_handler);
1235                 if (!success) {
1236                         ast_log(LOG_WARNING, "%s error in window\n", __FUNCTION__);
1237                         return;
1238                 }
1239         }       
1240 #endif
1241         /*
1242          * initialize the SDL environment. We have one large window
1243          * with local and remote video, and a keypad.
1244          * At the moment we arrange them statically, as follows:
1245          * - top row: thumbnails for local video sources;
1246          * - next row: message boards for local video sources
1247          * - on the left, the remote video;
1248          * - on the center, the keypad
1249          * - on the right, the local video
1250          * We need to read in the skin for the keypad before creating the main
1251          * SDL window, because the size is only known here.
1252          */
1253
1254         if (gui == NULL && SDL_Init(SDL_INIT_VIDEO)) {
1255                 ast_log(LOG_WARNING, "Could not initialize SDL - %s\n",
1256                         SDL_GetError());
1257                 /* again not fatal, just we won't display anything */
1258                 return;
1259         }
1260         info = SDL_GetVideoInfo();
1261         /* We want at least 16bpp to support YUV overlays.
1262          * E.g with SDL_VIDEODRIVER = aalib the default is 8
1263          */
1264         if (!info || !info->vfmt) {
1265                 ast_log(LOG_WARNING, "Bad SDL_GetVideoInfo - %s\n",
1266                         SDL_GetError());
1267                 return;
1268         }
1269         depth = info->vfmt->BitsPerPixel;
1270         if (depth < 16)
1271                 depth = 16;
1272         if (!gui)
1273                 env->gui = gui = gui_init(env->keypad_file, env->keypad_font);
1274         if (!gui)
1275                 goto no_sdl;
1276
1277         if (gui->keypad) {
1278                 if (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) {
1279                         kp_w = gui->kp_rect.w;
1280                         kp_h = gui->kp_rect.h;
1281                 } else {
1282                         kp_w = gui->keypad->w;
1283                         kp_h = gui->keypad->h;
1284                 }
1285         }
1286         
1287         /* total width of the thumbnails */
1288         src_wins_tot_w = env->out.device_num*(SRC_WIN_W+BORDER)+BORDER;
1289         
1290         /* x coordinate of the center of the keypad */
1291         x0 = MAX(env->rem_dpy.w+kp_w/2+2*BORDER, src_wins_tot_w/2);
1292         
1293         /* from center of the keypad to right border */
1294         x1 = MAX(env->loc_dpy.w+kp_w/2+2*BORDER, src_wins_tot_w/2);
1295         
1296         /* total width of the SDL window to create */
1297         maxw = x0+x1;
1298         
1299         /* total height of the mother window to create */
1300         maxh = MAX( MAX(env->rem_dpy.h, env->loc_dpy.h), kp_h)+2*BORDER;
1301         maxh += env->out.device_num ? (2*BORDER+SRC_WIN_H+SRC_MSG_BD_H) : 0;
1302         
1303         gui->screen = SDL_SetVideoMode(maxw, maxh, depth, 0);
1304         if (!gui->screen) {
1305                 ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
1306                 goto no_sdl;
1307         }
1308
1309 #ifdef HAVE_X11
1310         /*
1311          * Annoying as it may be, if SDL_WINDOWID is set, SDL does
1312          * not grab keyboard/mouse events or expose or other stuff,
1313          * and it does not handle resize either.
1314          * So we need to implement workarounds here.
1315          */
1316     do {
1317         /* First, handle the event mask */
1318         XWindowAttributes attr;
1319         long want;
1320         SDL_SysWMinfo info;
1321         Display *SDL_Display;
1322         Window win;
1323
1324         const char *e = getenv("SDL_WINDOWID");
1325         if (ast_strlen_zero(e))  /* no external window, don't bother doing this */
1326                 break;
1327         SDL_VERSION(&info.version); /* it is important to set the version */
1328         if (SDL_GetWMInfo(&info) != 1) {
1329                 fprintf(stderr, "no wm info\n");
1330                 break;
1331         }
1332         SDL_Display = info.info.x11.display;
1333         if (SDL_Display == NULL)
1334                 break;
1335         win = info.info.x11.window;
1336
1337         /*
1338          * A list of events we want.
1339          * Leave ResizeRedirectMask to the parent.
1340          */
1341         want = KeyPressMask | KeyReleaseMask | ButtonPressMask |
1342                            ButtonReleaseMask | EnterWindowMask |
1343                            LeaveWindowMask | PointerMotionMask |
1344                            Button1MotionMask |
1345                            Button2MotionMask | Button3MotionMask |
1346                            Button4MotionMask | Button5MotionMask |
1347                            ButtonMotionMask | KeymapStateMask |
1348                            ExposureMask | VisibilityChangeMask |
1349                            StructureNotifyMask | /* ResizeRedirectMask | */
1350                            SubstructureNotifyMask | SubstructureRedirectMask |
1351                            FocusChangeMask | PropertyChangeMask |
1352                            ColormapChangeMask | OwnerGrabButtonMask;
1353
1354         bzero(&attr, sizeof(attr));
1355         XGetWindowAttributes(SDL_Display, win, &attr);
1356
1357         /* the following events can be delivered only to one client.
1358          * So check which ones are going to someone else, and drop
1359          * them from our request.
1360          */
1361         {
1362         /* ev are the events for a single recipient */
1363         long ev = ButtonPressMask | ResizeRedirectMask |
1364                         SubstructureRedirectMask;
1365         ev &= (attr.all_event_masks & ~attr.your_event_mask);
1366         /* now ev contains 1 for single-recipient events owned by others.
1367          * We must clear those bits in 'want'
1368          * and then add the bits in 'attr.your_event_mask' to 'want'
1369          */
1370         want &= ~ev;
1371         want |= attr.your_event_mask;
1372         }
1373         XSelectInput(SDL_Display, win, want);
1374
1375         /* Second, handle resize.
1376          * We do part of the things that X11Resize does,
1377          * but also generate a ConfigureNotify event so
1378          * the owner of the window has a chance to do something
1379          * with it.
1380          */
1381         XResizeWindow(SDL_Display, win, maxw, maxh);
1382         {
1383         XConfigureEvent ce = {
1384                 .type = ConfigureNotify,
1385                 .serial = 0,
1386                 .send_event = 1,        /* TRUE */
1387                 .display = SDL_Display,
1388                 .event = win,
1389                 .window = win,
1390                 .x = 0,
1391                 .y = 0,
1392                 .width = maxw,
1393                 .height = maxh,
1394                 .border_width = 0,
1395                 .above = 0,
1396                 .override_redirect = 0 };
1397         XSendEvent(SDL_Display, win, 1 /* TRUE */, StructureNotifyMask, (XEvent *)&ce);
1398         }
1399     } while (0);
1400 #endif /* HAVE_X11 */
1401
1402         y0 = env->out.device_num ? (3*BORDER+SRC_WIN_H+SRC_MSG_BD_H) : BORDER;
1403         
1404         SDL_WM_SetCaption("Asterisk console Video Output", NULL);
1405         
1406         /* intialize the windows for local and remote video */
1407         if (set_win(gui->screen, &gui->win[WIN_REMOTE], dpy_fmt,
1408                         env->rem_dpy.w, env->rem_dpy.h, x0-kp_w/2-BORDER-env->rem_dpy.w, y0))
1409                 goto no_sdl;
1410         if (set_win(gui->screen, &gui->win[WIN_LOCAL], dpy_fmt,
1411                         env->loc_dpy.w, env->loc_dpy.h,
1412                         x0+kp_w/2+BORDER, y0))
1413                 goto no_sdl;
1414         
1415         /* initialize device_num source windows (thumbnails) and boards
1416         (for a maximum of 9 additional windows and boards) */
1417         x = x0 - src_wins_tot_w/2 + BORDER;
1418         for (i = 0; i < env->out.device_num; i++){
1419                 struct thumb_bd *p = &gui->thumb_bd_array[i];
1420                 if (set_win(gui->screen, &gui->win[i+WIN_SRC1], dpy_fmt,
1421                         SRC_WIN_W, SRC_WIN_H, x+i*(BORDER+SRC_WIN_W), BORDER))
1422                         goto no_sdl;
1423                 /* set geometry for the rect for the message board of the device */
1424                 p->rect.w = SRC_WIN_W;
1425                 p->rect.h = SRC_MSG_BD_H;
1426                 p->rect.x = x+i*(BORDER+SRC_WIN_W);
1427                 p->rect.y = 2*BORDER+SRC_WIN_H;
1428                 /* the white color is used as background */
1429                 SDL_FillRect(gui->screen, &p->rect,
1430                         SDL_MapRGB(gui->screen->format, 255, 255, 255));
1431                 /* if necessary, initialize boards for the sources */
1432                 if (!p->board)
1433                         p->board =
1434                                 board_setup(gui->screen, &p->rect,
1435                                 gui->font, gui->font_rects);
1436                 /* update board rect */
1437                 SDL_UpdateRect(gui->screen, p->rect.x, p->rect.y, p->rect.w, p->rect.h);
1438         }
1439
1440         /* display the skin, but do not free it as we need it later to
1441         restore text areas and maybe sliders too */
1442         if (gui->keypad) {
1443                 struct SDL_Rect *dest = &gui->win[WIN_KEYPAD].rect;
1444                 struct SDL_Rect *src = (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) ? & gui->kp_rect : NULL;
1445                 /* set the coordinates of the keypad relative to the main screen */
1446                 dest->x = x0-kp_w/2;
1447                 dest->y = y0;
1448                 dest->w = kp_w;
1449                 dest->h = kp_h;
1450                 SDL_BlitSurface(gui->keypad, src, gui->screen, dest);
1451                 init_board(gui, &gui->bd_msg, gui->kp_msg, dest->x, dest->y);
1452                 init_board(gui, &gui->bd_dialed, gui->kp_dialed, dest->x, dest->y);
1453                 SDL_UpdateRects(gui->screen, 1, dest);
1454         }
1455         return;
1456
1457 no_sdl:
1458         /* free resources in case of errors */
1459         env->gui = cleanup_sdl(gui, env->out.device_num);
1460 }
1461
1462 /*
1463  * Functions to determine if a point is within a region. Return 1 if success.
1464  * First rotate the point, with
1465  *      x' =  (x - x0) * cos A + (y - y0) * sin A
1466  *      y' = -(x - x0) * sin A + (y - y0) * cos A
1467  * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
1468  *      l = sqrt( (x1-x0)^2 + (y1-y0)^2
1469  * Then determine inclusion by simple comparisons i.e.:
1470  *      rectangle: x >= 0 && x < l && y >= 0 && y < h
1471  *      ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
1472  */
1473 static int kp_match_area(const struct keypad_entry *e, int x, int y)
1474 {
1475         double xp, dx = (e->x1 - e->x0);
1476         double yp, dy = (e->y1 - e->y0);
1477         double l = sqrt(dx*dx + dy*dy);
1478         int ret = 0;
1479
1480         if (l > 1) { /* large enough */
1481                 xp = ((x - e->x0)*dx + (y - e->y0)*dy)/l;
1482                 yp = (-(x - e->x0)*dy + (y - e->y0)*dx)/l;
1483                 if (e->type == KP_RECT) {
1484                         ret = (xp >= 0 && xp < l && yp >=0 && yp < e->h);
1485                 } else if (e->type == KP_CIRCLE) {
1486                         dx = xp*xp/(l*l) + yp*yp/(e->h*e->h);
1487                         ret = (dx < 1);
1488                 }
1489         }
1490 #if 0
1491         ast_log(LOG_WARNING, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
1492                 ret, e->c, x, y, e->type, e->x0, e->y0, e->x1, e->y1, e->h);
1493 #endif
1494         return ret;
1495 }
1496
1497 struct _s_k { const char *s; int k; };
1498 static struct _s_k gui_key_map[] = {
1499         {"PIP",         KEY_PIP},
1500         {"PICK_UP",     KEY_PICK_UP },
1501         {"PICKUP",      KEY_PICK_UP },
1502         {"HANG_UP",     KEY_HANG_UP },
1503         {"HANGUP",      KEY_HANG_UP },
1504         {"MUTE",        KEY_MUTE },
1505         {"FLASH",       KEY_FLASH },
1506         {"AUTOANSWER",  KEY_AUTOANSWER },
1507         {"SENDVIDEO",   KEY_SENDVIDEO },
1508         {"LOCALVIDEO",  KEY_LOCALVIDEO },
1509         {"REMOTEVIDEO", KEY_REMOTEVIDEO },
1510         {"GUI_CLOSE",   KEY_GUI_CLOSE },
1511         {"MESSAGEBOARD",        KEY_MESSAGEBOARD },
1512         {"DIALEDBOARD", KEY_DIALEDBOARD },
1513         {"EDITBOARD",   KEY_EDITBOARD },
1514         {"KEYPAD",      KEY_KEYPAD },   /* x0 y0 w h - active area of the keypad */
1515         {"MESSAGE",     KEY_MESSAGE },  /* x0 y0 w h - incoming messages */
1516         {"DIALED",      KEY_DIALED },   /* x0 y0 w h - dialed number */
1517         {"EDIT",        KEY_EDIT },     /* x0 y0 w h - edit user input */
1518         {"FONT",        KEY_FONT },     /* x0 yo w h rows cols - location and format of the font */
1519         {NULL, 0 } };
1520
1521 static int gui_map_token(const char *s)
1522 {
1523         /* map the string into token to be returned */
1524         int i = atoi(s);
1525         struct _s_k *p;
1526         if (i > 0 || s[1] == '\0')      /* numbers or single characters */
1527                 return (i > 9) ? i : s[0];
1528         for (p = gui_key_map; p->s; p++) {
1529                 if (!strcasecmp(p->s, s))
1530                         return p->k;
1531         }
1532         return KEY_NONE;        /* not found */
1533 }
1534
1535 /*! \brief read a keypad entry line in the format
1536  *      reset
1537  *      token circle xc yc diameter
1538  *      token circle xc yc x1 y1 h      # ellipse, main diameter and height
1539  *      token rect x0 y0 x1 y1 h        # rectangle with main side and eight
1540  * token is the token to be returned, either a character or a symbol
1541  * as KEY_* above
1542  * Return 1 on success, 0 on error.
1543  */
1544 static int keypad_cfg_read(struct gui_info *gui, const char *val)
1545 {
1546         struct keypad_entry e;
1547         SDL_Rect *r = NULL;
1548         char s1[16], s2[16];
1549         int i, ret = 0; /* default, error */
1550
1551         if (gui == NULL || val == NULL)
1552                 return 0;
1553
1554         s1[0] = s2[0] = '\0';
1555         bzero(&e, sizeof(e));
1556         i = sscanf(val, "%14s %14s %d %d %d %d %d",
1557                 s1, s2, &e.x0, &e.y0, &e.x1, &e.y1, &e.h);
1558
1559         e.c = gui_map_token(s1);
1560         if (e.c == KEY_NONE)
1561                 return 0;       /* nothing found */
1562         switch (i) {
1563         default:
1564                 break;
1565         case 1: /* only "reset" is allowed */
1566                 if (e.c != KEY_RESET)
1567                         break;
1568                 if (gui->kp)
1569                         gui->kp_used = 0;
1570                 break;
1571         case 5:
1572                 if (e.c == KEY_KEYPAD)  /* active keypad area */
1573                         r = &gui->kp_rect;
1574                 else if (e.c == KEY_MESSAGE)
1575                         r = gui->kp_msg;
1576                 else if (e.c == KEY_DIALED)
1577                         r = gui->kp_dialed;
1578                 else if (e.c == KEY_EDIT)
1579                         r = gui->kp_edit;
1580                 if (r) {
1581                         r->x = atoi(s2);
1582                         r->y = e.x0;
1583                         r->w = e.y0;
1584                         r->h = e.x1;
1585                         break;
1586                 }
1587                 if (strcasecmp(s2, "circle"))   /* invalid */
1588                         break;
1589                 /* token circle xc yc diameter */
1590                 e.h = e.x1;
1591                 e.y1 = e.y0;    /* map radius in x1 y1 */
1592                 e.x1 = e.x0 + e.h;      /* map radius in x1 y1 */
1593                 e.x0 = e.x0 - e.h;      /* map radius in x1 y1 */
1594                 /* fallthrough */
1595
1596         case 7:
1597                 if (e.c == KEY_FONT) {  /* font - x0 y0 w h rows cols */
1598                         ast_log(LOG_WARNING, "font not supported yet\n");
1599                         break;
1600                 }
1601                 /* token circle|rect x0 y0 x1 y1 h */
1602                 if (e.x1 < e.x0 || e.h <= 0) {
1603                         ast_log(LOG_WARNING, "error in coordinates\n");
1604                         e.type = 0;
1605                         break;
1606                 }
1607                 if (!strcasecmp(s2, "circle")) {
1608                         /* for a circle we specify the diameter but store center and radii */
1609                         e.type = KP_CIRCLE;
1610                         e.x0 = (e.x1 + e.x0) / 2;
1611                         e.y0 = (e.y1 + e.y0) / 2;
1612                         e.h = e.h / 2;
1613                 } else if (!strcasecmp(s2, "rect")) {
1614                         e.type = KP_RECT;
1615                 } else
1616                         break;
1617                 ret = 1;
1618         }
1619         // ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
1620         if (ret == 0)
1621                 return 0;
1622         if (gui->kp_size == 0) {
1623                 gui->kp = ast_calloc(10, sizeof(e));
1624                 if (gui->kp == NULL) {
1625                         ast_log(LOG_WARNING, "cannot allocate kp");
1626                         return 0;
1627                 }
1628                 gui->kp_size = 10;
1629         }
1630         if (gui->kp_size == gui->kp_used) { /* must allocate */
1631                 struct keypad_entry *a = ast_realloc(gui->kp, sizeof(e)*(gui->kp_size+10));
1632                 if (a == NULL) {
1633                         ast_log(LOG_WARNING, "cannot reallocate kp");
1634                         return 0;
1635                 }
1636                 gui->kp = a;
1637                 gui->kp_size += 10;
1638         }
1639         if (gui->kp_size == gui->kp_used)
1640                 return 0;
1641         gui->kp[gui->kp_used++] = e;
1642         // ast_log(LOG_WARNING, "now %d regions\n", gui->kp_used);
1643         return 1;
1644 }
1645 #endif  /* HAVE_SDL */