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