2 * GUI for console video.
3 * The routines here are in charge of loading the keypad and handling events.
8 * GUI layout, structure and management
10 For the GUI we use SDL to create a large surface (gui->screen)
11 containing tree sections: remote video on the left, local video
12 on the right, and the keypad with all controls and text windows
14 The central section is built using an image for the skin, fonts and
15 other GUI elements. Comments embedded in the image to indicate to
16 what function each area is mapped to.
18 Mouse and keyboard events are detected on the whole surface, and
19 handled differently according to their location:
21 - drag on the local video window are used to move the captured
22 area (in the case of X11 grabber) or the picture-in-picture
23 location (in case of camera included on the X11 grab).
24 - click on the keypad are mapped to the corresponding key;
25 - drag on some keypad areas (sliders etc.) are mapped to the
26 corresponding functions;
27 - keystrokes are used as keypad functions, or as text input
28 if we are in text-input mode.
30 Configuration options control the appeareance of the gui:
32 keypad = /tmp/phone.jpg ; the skin
33 keypad_font = /tmp/font.ttf ; the font to use for output (XXX deprecated)
39 #include "console_video.h"
40 #include "asterisk/lock.h"
41 #include "asterisk/frame.h"
42 #include "asterisk/utils.h" /* ast_calloc and ast_realloc */
43 #include <math.h> /* sqrt */
45 /* We use 3 'windows' in the GUI */
46 enum { WIN_LOCAL, WIN_REMOTE, WIN_KEYPAD, WIN_MAX };
48 #ifndef HAVE_SDL /* stubs if we don't have any sdl */
49 static void show_frame(struct video_desc *env, int out) {}
50 static void sdl_setup(struct video_desc *env) {}
51 static struct gui_info *cleanup_sdl(struct gui_info *gui) { return NULL; }
52 static void eventhandler(struct video_desc *env, const char *caption) {}
53 static int keypad_cfg_read(struct gui_info *gui, const char *val) { return 0; }
55 #else /* HAVE_SDL, the real rendering code */
58 #include <SDL/SDL_syswm.h>
60 #include <SDL/SDL_image.h> /* for loading images */
64 /* Need to hook into X for SDL_WINDOWID handling */
68 enum kp_type { KP_NONE, KP_RECT, KP_CIRCLE };
70 int c; /* corresponding character */
71 int x0, y0, x1, y1, h; /* arguments */
75 /* our representation of a displayed window. SDL can only do one main
76 * window so we map everything within that one
78 struct display_window {
80 SDL_Rect rect; /* location of the window */
84 enum kb_output kb_output; /* where the keyboard output goes */
85 struct drag_info drag; /* info on the window are we dragging */
86 /* support for display. */
87 SDL_Surface *screen; /* the main window */
89 int outfd; /* fd for output */
90 SDL_Surface *keypad; /* the skin for the keypad */
91 SDL_Rect kp_rect; /* portion of the skin to display - default all */
92 SDL_Surface *font; /* font to be used */
93 SDL_Rect font_rects[96]; /* only printable chars */
95 /* each board has two rectangles,
96 * [0] is the geometry relative to the keypad,
97 * [1] is the geometry relative to the whole screen
99 SDL_Rect kp_msg[2]; /* incoming msg, relative to kpad */
100 struct board *bd_msg;
102 SDL_Rect kp_edit[2]; /* edit user input */
103 struct board *bd_edit;
105 SDL_Rect kp_dialed[2]; /* dialed number */
106 struct board *bd_dialed;
108 /* variable-size array mapping keypad regions to functions */
109 int kp_size, kp_used;
110 struct keypad_entry *kp;
112 struct display_window win[WIN_MAX];
115 /*! \brief free the resources in struct gui_info and the descriptor itself.
116 * Return NULL so we can assign the value back to the descriptor in case.
118 static struct gui_info *cleanup_sdl(struct gui_info *gui)
125 /* unload font file */
127 SDL_FreeSurface(gui->font);
134 SDL_FreeSurface(gui->keypad);
139 /* uninitialize the SDL environment */
140 for (i = 0; i < WIN_MAX; i++) {
142 SDL_FreeYUVOverlay(gui->win[i].bmp);
144 bzero(gui, sizeof(gui));
151 * Display video frames (from local or remote stream) using the SDL library.
152 * - Set the video mode to use the resolution specified by the codec context
153 * - Create a YUV Overlay to copy the frame into it;
154 * - After the frame is copied into the overlay, display it
156 * The size is taken from the configuration.
158 * 'out' is 0 for remote video, 1 for the local video
160 static void show_frame(struct video_desc *env, int out)
162 AVPicture *p_in, p_out;
163 struct fbuf_t *b_in, *b_out;
165 struct gui_info *gui = env->gui;
170 if (out == WIN_LOCAL) { /* webcam/x11 to sdl */
172 b_out = &env->loc_dpy;
175 /* copy input format from the decoding context */
177 if (env->in == NULL) /* XXX should not happen - decoder not ready */
179 c = env->in->dec_ctx;
180 b_in = &env->in->dec_out;
181 b_in->pix_fmt = c->pix_fmt;
185 b_out = &env->rem_dpy;
186 p_in = (AVPicture *)env->in->d_frame;
188 bmp = gui->win[out].bmp;
189 SDL_LockYUVOverlay(bmp);
190 /* output picture info - this is sdl, YUV420P */
191 bzero(&p_out, sizeof(p_out));
192 p_out.data[0] = bmp->pixels[0];
193 p_out.data[1] = bmp->pixels[1];
194 p_out.data[2] = bmp->pixels[2];
195 p_out.linesize[0] = bmp->pitches[0];
196 p_out.linesize[1] = bmp->pitches[1];
197 p_out.linesize[2] = bmp->pitches[2];
199 my_scale(b_in, p_in, b_out, &p_out);
201 /* lock to protect access to Xlib by different threads. */
202 SDL_DisplayYUVOverlay(bmp, &gui->win[out].rect);
203 SDL_UnlockYUVOverlay(bmp);
207 * Identifiers for regions of the main window.
208 * Values between 0 and 127 correspond to ASCII characters.
209 * The corresponding strings to be used in the skin comment section
210 * are defined in gui_key_map.
213 /* answer/close functions */
218 KEY_AUTOANSWER = 131,
220 KEY_LOCALVIDEO = 133,
221 KEY_REMOTEVIDEO = 134,
224 /* sensitive areas for the various text windows */
225 KEY_MESSAGEBOARD = 140,
226 KEY_DIALEDBOARD = 141,
229 KEY_GUI_CLOSE = 199, /* close gui */
230 /* regions of the skin - displayed area, fonts, etc.
231 * XXX NOTE these are not sensitive areas.
233 KEY_KEYPAD = 200, /* the keypad - default to the whole image */
234 KEY_FONT = 201, /* the font. Maybe not really useful */
235 KEY_MESSAGE = 202, /* area for incoming messages */
236 KEY_DIALED = 203, /* area for dialed numbers */
237 KEY_EDIT = 204, /* area for editing user input */
239 /* areas outside the keypad - simulated */
240 KEY_OUT_OF_KEYPAD = 241,
243 KEY_RESET = 253, /* the 'reset' keyword */
244 KEY_NONE = 254, /* invalid area */
245 KEY_DIGIT_BACKGROUND = 255, /* other areas within the keypad */
249 * Handlers for the various keypad functions
252 /* accumulate digits, possibly call dial if in connected mode */
253 static void keypad_digit(struct video_desc *env, int digit)
255 if (env->owner) { /* we have a call, send the digit */
256 struct ast_frame f = { AST_FRAME_DTMF, 0 };
259 ast_queue_frame(env->owner, &f);
260 } else { /* no call, accumulate digits */
261 char buf[2] = { digit, '\0' };
262 if (env->gui->bd_msg) /* XXX not strictly necessary ... */
263 print_message(env->gui->bd_msg, buf);
267 /* function used to toggle on/off the status of some variables */
268 static char *keypad_toggle(struct video_desc *env, int index)
270 ast_log(LOG_WARNING, "keypad_toggle(%i) called\n", index);
274 env->out.sendvideo = !env->out.sendvideo;
278 struct chan_oss_pvt *o = find_desc(oss_active);
282 case KEY_AUTOANSWER: {
283 struct chan_oss_pvt *o = find_desc(oss_active);
284 o->autoanswer = !o->autoanswer;
292 char *console_do_answer(int fd);
294 * Function called when the pick up button is pressed
295 * perform actions according the channel status:
297 * - if no one is calling us and no digits was pressed,
298 * the operation have no effects,
299 * - if someone is calling us we answer to the call.
300 * - if we have no call in progress and we pressed some
301 * digit, send the digit to the console.
303 static void keypad_pick_up(struct video_desc *env)
305 struct gui_info *gui = env->gui;
307 ast_log(LOG_WARNING, "keypad_pick_up called\n");
309 if (env->owner) { /* someone is calling us, just answer */
310 ast_cli_command(gui->outfd, "console answer");
311 } else { /* we have someone to call */
313 const char *who = ast_skip_blanks(read_message(gui->bd_msg));
314 buf[sizeof(buf) - 1] = '\0';
315 snprintf(buf, sizeof(buf), "console dial %s", who);
316 ast_log(LOG_WARNING, "doing <%s>\n", buf);
317 print_message(gui->bd_dialed, "\n");
318 print_message(gui->bd_dialed, who);
319 reset_board(gui->bd_msg);
320 ast_cli_command(gui->outfd, buf);
324 #if 0 /* still unused */
326 * As an alternative to SDL_TTF, we can simply load the font from
327 * an image and blit characters on the background of the GUI.
329 * To generate a font we can use the 'fly' command with the
330 * following script (3 lines with 32 chars each)
335 string 255,255,255, 0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
336 string 255,255,255, 0,20,giant,@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
337 string 255,255,255, 0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
342 /* Print given text on the gui */
343 static int gui_output(struct video_desc *env, const char *text)
345 return 1; /* error, not supported */
349 static int video_geom(struct fbuf_t *b, const char *s);
350 static void sdl_setup(struct video_desc *env);
351 static int kp_match_area(const struct keypad_entry *e, int x, int y);
353 static void set_drag(struct drag_info *drag, int x, int y, enum drag_window win)
357 drag->drag_window = win;
361 * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
362 * index value and calling the right callback.
364 * x, y are referred to the upper left corner of the main SDL window.
366 static void handle_mousedown(struct video_desc *env, SDL_MouseButtonEvent button)
368 uint8_t index = KEY_OUT_OF_KEYPAD; /* the key or region of the display we clicked on */
369 struct gui_info *gui = env->gui;
372 ast_log(LOG_WARNING, "event %d %d have %d/%d regions at %p\n",
373 button.x, button.y, gui->kp_used, gui->kp_size, gui->kp);
375 /* for each mousedown we end previous drag */
376 gui->drag.drag_window = DRAG_NONE;
378 /* define keypad boundary */
379 if (button.x < env->rem_dpy.w)
380 index = KEY_REM_DPY; /* click on remote video */
381 else if (button.x > env->rem_dpy.w + gui->keypad->w)
382 index = KEY_LOC_DPY; /* click on local video */
383 else if (button.y > gui->keypad->h)
384 index = KEY_OUT_OF_KEYPAD; /* click outside the keypad */
387 for (i = 0; i < gui->kp_used; i++) {
388 if (kp_match_area(&gui->kp[i], button.x - env->rem_dpy.w, button.y)) {
389 index = gui->kp[i].c;
395 /* exec the function */
396 if (index < 128) { /* surely clicked on the keypad, don't care which key */
397 keypad_digit(env, index);
401 /* answer/close function */
406 ast_cli_command(gui->outfd, "console hangup");
409 /* other functions */
413 keypad_toggle(env, index);
418 case KEY_REMOTEVIDEO:
421 case KEY_MESSAGEBOARD:
422 if (button.button == SDL_BUTTON_LEFT)
423 set_drag(&gui->drag, button.x, button.y, DRAG_MESSAGE);
426 /* press outside the keypad. right increases size, center decreases, left drags */
429 if (button.button == SDL_BUTTON_LEFT) {
430 if (index == KEY_LOC_DPY)
431 set_drag(&gui->drag, button.x, button.y, DRAG_LOCAL);
435 struct fbuf_t *fb = index == KEY_LOC_DPY ? &env->loc_dpy : &env->rem_dpy;
436 sprintf(buf, "%c%dx%d", button.button == SDL_BUTTON_RIGHT ? '>' : '<',
442 case KEY_OUT_OF_KEYPAD:
445 case KEY_DIGIT_BACKGROUND:
448 ast_log(LOG_WARNING, "function not yet defined %i\n", index);
453 * Handle SDL_KEYDOWN type event, put the key pressed
454 * in the dial buffer or in the text-message buffer,
455 * depending on the text_mode variable value.
457 * key is the SDLKey structure corresponding to the key pressed.
458 * Note that SDL returns modifiers (ctrl, shift, alt) as independent
459 * information so the key itself is not enough and we need to
460 * use a translation table, below - one line per entry,
461 * plain, shift, ctrl, ... using the first char as key.
463 static const char *us_kbd_map[] = {
464 "`~", "1!", "2@", "3#", "4$", "5%", "6^",
465 "7&", "8*", "9(", "0)", "-_", "=+", "[{",
466 "]}", "\\|", ";:", "'\"", ",<", ".>", "/?",
471 static char map_key(SDL_keysym *ks)
473 const char *s, **p = us_kbd_map;
476 if (c == '\r') /* map cr into lf */
478 if (c >= SDLK_NUMLOCK && c <= SDLK_COMPOSE)
479 return 0; /* only a modifier */
482 while ((s = *p) && s[0] != c)
484 if (s) { /* see if we have a modifier and a chance to use it */
485 int l = strlen(s), mod = 0;
487 mod |= (ks->mod & KMOD_SHIFT) ? 1 : 0;
489 mod |= (ks->mod & KMOD_CTRL) ? 2 : 0;
491 mod |= (ks->mod & KMOD_ALT) ? 4 : 0;
494 if (ks->mod & (KMOD_CAPS|KMOD_SHIFT) && c >= 'a' && c <='z')
499 static void handle_keyboard_input(struct video_desc *env, SDL_keysym *ks)
501 char buf[2] = { map_key(ks), '\0' };
502 struct gui_info *gui = env->gui;
503 if (buf[0] == 0) /* modifier ? */
505 switch (gui->kb_output) {
508 case KO_INPUT: /* to be completed */
512 print_message(gui->bd_msg, buf);
513 if (buf[0] == '\r' || buf[0] == '\n') {
519 case KO_DIALED: /* to be completed */
526 static void grabber_move(struct video_out_desc *, int dx, int dy);
528 int compute_drag(int *start, int end, int magnifier);
529 int compute_drag(int *start, int end, int magnifier)
531 int delta = end - *start;
533 /* add a small quadratic term */
534 delta += delta * delta * (delta > 0 ? 1 : -1 )/100;
535 delta *= POLARITY * magnifier;
542 * I am seeing some kind of deadlock or stall around
543 * SDL_PumpEvents() while moving the window on a remote X server
544 * (both xfree-4.4.0 and xorg 7.2)
545 * and windowmaker. It is unclear what causes it.
548 /*! \brief refresh the screen, and also grab a bunch of events.
550 static void eventhandler(struct video_desc *env, const char *caption)
552 struct gui_info *gui = env->gui;
553 struct drag_info *drag;
556 SDL_Event ev[N_EVENTS];
562 SDL_WM_SetCaption(caption, NULL);
564 #define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
565 while ( (n = SDL_PeepEvents(ev, N_EVENTS, SDL_GETEVENT, SDL_ALLEVENTS)) > 0) {
566 for (i = 0; i < n; i++) {
568 ast_log(LOG_WARNING, "------ event %d at %d %d\n",
569 ev[i].type, ev[i].button.x, ev[i].button.y);
571 switch (ev[i].type) {
573 ast_log(LOG_WARNING, "------ event %d at %d %d\n",
574 ev[i].type, ev[i].button.x, ev[i].button.y);
577 case SDL_ACTIVEEVENT:
578 if (ev[i].active.gain == 0 && ev[i].active.state & SDL_APPACTIVE) {
579 ast_log(LOG_WARNING, "/* somebody has killed us ? */");
580 ast_cli_command(gui->outfd, "stop now");
584 case SDL_KEYUP: /* ignore, for the time being */
588 handle_keyboard_input(env, &ev[i].key.keysym);
591 case SDL_MOUSEMOTION:
592 case SDL_MOUSEBUTTONUP:
593 if (drag->drag_window == DRAG_LOCAL) {
594 /* move the capture source */
595 int dx = compute_drag(&drag->x_start, ev[i].motion.x, 3);
596 int dy = compute_drag(&drag->y_start, ev[i].motion.y, 3);
597 grabber_move(&env->out, dx, dy);
598 } else if (drag->drag_window == DRAG_MESSAGE) {
599 /* scroll up/down the window */
600 int dy = compute_drag(&drag->y_start, ev[i].motion.y, 1);
601 move_message_board(gui->bd_msg, dy);
603 if (ev[i].type == SDL_MOUSEBUTTONUP)
604 drag->drag_window = DRAG_NONE;
606 case SDL_MOUSEBUTTONDOWN:
607 handle_mousedown(env, ev[i].button);
613 struct timeval b, a = ast_tvnow();
615 //SDL_Lock_EventThread();
618 i = ast_tvdiff_ms(b, a);
620 fprintf(stderr, "-------- SDL_PumpEvents took %dms\n", i);
621 //SDL_Unlock_EventThread();
625 static SDL_Surface *load_image(const char *file)
629 #ifdef HAVE_SDL_IMAGE
630 temp = IMG_Load(file);
632 temp = SDL_LoadBMP(file);
635 fprintf(stderr, "Unable to load image %s: %s\n",
636 file, SDL_GetError());
640 static void keypad_setup(struct gui_info *gui, const char *kp_file);
642 /* TODO: consistency checks, check for bpp, widht and height */
643 /* Init the mask image used to grab the action. */
644 static struct gui_info *gui_init(const char *keypad_file, const char *font)
646 struct gui_info *gui = ast_calloc(1, sizeof(*gui));
650 /* initialize keypad status */
651 gui->kb_output = KO_MESSAGE; /* XXX temp */
652 gui->drag.drag_window = DRAG_NONE;
655 keypad_setup(gui, keypad_file);
656 if (gui->keypad == NULL) /* no keypad, we are done */
659 if (!ast_strlen_zero(font)) {
663 gui->font = load_image(font);
665 ast_log(LOG_WARNING, "Unable to load font %s, no output available\n", font);
668 ast_log(LOG_WARNING, "Loaded font %s\n", font);
669 /* XXX hardwired constants - 3 rows of 32 chars */
673 for (i = 0; i < 96; r++, i++) {
674 r->x = (i % 32 ) * FONT_W;
675 r->y = (i / 32 ) * FONT_H;
681 gui->outfd = open ("/dev/null", O_WRONLY); /* discard output, temporary */
682 if (gui->outfd < 0) {
683 ast_log(LOG_WARNING, "Unable output fd\n");
693 /* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
694 static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
695 int w, int h, int x, int y)
697 win->bmp = SDL_CreateYUVOverlay(w, h, fmt, screen);
698 if (win->bmp == NULL)
699 return -1; /* error */
707 static int keypad_cfg_read(struct gui_info *gui, const char *val);
709 static void keypad_setup(struct gui_info *gui, const char *kp_file)
713 const char region[] = "region";
714 int reg_len = strlen(region);
719 gui->keypad = load_image(kp_file);
722 /* now try to read the keymap from the file. */
723 fd = fopen(kp_file, "r");
725 ast_log(LOG_WARNING, "fail to open %s\n", kp_file);
729 * If the keypad image has a comment field, try to read
730 * the button location from there. The block must start with
731 * a comment (or empty) line, and continue with entries like:
732 * region = token shape x0 y0 x1 y1 h
734 * (basically, lines have the same format as config file entries).
735 * You can add it to a jpeg file using wrjpgcom
737 while (fgets(buf, sizeof(buf), fd)) {
740 if (!strstr(buf, region)) { /* no keyword yet */
741 if (!in_comment) /* still waiting for initial comment block */
746 if (!in_comment) { /* first keyword, reset previous entries */
747 keypad_cfg_read(gui, "reset");
750 s = ast_skip_blanks(buf);
752 if (memcmp(s, region, reg_len))
753 break; /* keyword not found */
754 s = ast_skip_blanks(s + reg_len); /* space between token and '=' */
755 if (*s++ != '=') /* missing separator */
757 if (*s == '>') /* skip '>' if present */
759 keypad_cfg_read(gui, ast_skip_blanks(s));
764 struct board *board_setup(SDL_Surface *screen, SDL_Rect *dest,
765 SDL_Surface *font, SDL_Rect *font_rects);
767 /*! \brief initialize the boards we have in the keypad */
768 static void init_board(struct gui_info *gui, struct board **dst, SDL_Rect *r, int dx, int dy)
770 if (r[0].w == 0 || r[0].h == 0)
771 return; /* not available */
772 r[1] = r[0]; /* copy geometry */
773 r[1].x += dx; /* add offset of main window */
775 if (*dst == NULL) { /* initial call */
776 *dst = board_setup(gui->screen, &r[1], gui->font, gui->font_rects);
784 * SDL is not very robust on error handling, so we need to trap ourselves
785 * at least the most obvious failure conditions, e.g. a bad SDL_WINDOWID.
786 * As of sdl-1.2.13, SDL_SetVideoMode crashes with bad parameters, so
787 * we need to do the explicit X calls to make sure the window is correct.
788 * And around these calls, we must trap X errors.
790 static int my_x_handler(Display *d, XErrorEvent *e)
792 ast_log(LOG_WARNING, "%s error_code %d\n", __FUNCTION__, e->error_code);
795 #endif /* HAVE_X11 */
797 /*! \brief [re]set the main sdl window, useful in case of resize.
798 * We can tell the first from subsequent calls from the value of
799 * env->gui, which is NULL the first time.
801 static void sdl_setup(struct video_desc *env)
803 int dpy_fmt = SDL_IYUV_OVERLAY; /* YV12 causes flicker in SDL */
804 int depth, maxw, maxh;
805 const SDL_VideoInfo *info;
806 int kp_w = 0, kp_h = 0; /* keypad width and height */
807 struct gui_info *gui = env->gui;
810 const char *e = getenv("SDL_WINDOWID");
812 if (!ast_strlen_zero(e)) {
814 int (*old_x_handler)(Display *d, XErrorEvent *e) = XSetErrorHandler(my_x_handler);
815 Display *d = XOpenDisplay(getenv("DISPLAY"));
817 int success = w ? XGetWindowAttributes(d, w, &a) : 0;
819 XSetErrorHandler(old_x_handler);
821 ast_log(LOG_WARNING, "%s error in window\n", __FUNCTION__);
827 * initialize the SDL environment. We have one large window
828 * with local and remote video, and a keypad.
829 * At the moment we arrange them statically, as follows:
830 * - on the left, the remote video;
831 * - on the center, the keypad
832 * - on the right, the local video
833 * We need to read in the skin for the keypad before creating the main
834 * SDL window, because the size is only known here.
837 if (gui == NULL && SDL_Init(SDL_INIT_VIDEO)) {
838 ast_log(LOG_WARNING, "Could not initialize SDL - %s\n",
840 /* again not fatal, just we won't display anything */
843 info = SDL_GetVideoInfo();
844 /* We want at least 16bpp to support YUV overlays.
845 * E.g with SDL_VIDEODRIVER = aalib the default is 8
847 if (!info || !info->vfmt) {
848 ast_log(LOG_WARNING, "Bad SDL_GetVideoInfo - %s\n",
852 depth = info->vfmt->BitsPerPixel;
856 env->gui = gui = gui_init(env->keypad_file, env->keypad_font);
861 if (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) {
862 kp_w = gui->kp_rect.w;
863 kp_h = gui->kp_rect.h;
865 kp_w = gui->keypad->w;
866 kp_h = gui->keypad->h;
869 /* XXX same for other boards */
870 #define BORDER 5 /* border around our windows */
871 maxw = env->rem_dpy.w + env->loc_dpy.w + kp_w;
872 maxh = MAX( MAX(env->rem_dpy.h, env->loc_dpy.h), kp_h);
876 gui->screen = SDL_SetVideoMode(maxw, maxh, depth, 0);
878 ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
884 * Annoying as it may be, if SDL_WINDOWID is set, SDL does
885 * not grab keyboard/mouse events or expose or other stuff,
886 * and it does not handle resize either.
887 * So we need to implement workarounds here.
890 /* First, handle the event mask */
891 XWindowAttributes attr;
894 Display *SDL_Display;
897 const char *e = getenv("SDL_WINDOWID");
898 if (ast_strlen_zero(e)) /* no external window, don't bother doing this */
900 SDL_VERSION(&info.version); /* it is important to set the version */
901 if (SDL_GetWMInfo(&info) != 1) {
902 fprintf(stderr, "no wm info\n");
905 SDL_Display = info.info.x11.display;
906 if (SDL_Display == NULL)
908 win = info.info.x11.window;
911 * A list of events we want.
912 * Leave ResizeRedirectMask to the parent.
914 want = KeyPressMask | KeyReleaseMask | ButtonPressMask |
915 ButtonReleaseMask | EnterWindowMask |
916 LeaveWindowMask | PointerMotionMask |
918 Button2MotionMask | Button3MotionMask |
919 Button4MotionMask | Button5MotionMask |
920 ButtonMotionMask | KeymapStateMask |
921 ExposureMask | VisibilityChangeMask |
922 StructureNotifyMask | /* ResizeRedirectMask | */
923 SubstructureNotifyMask | SubstructureRedirectMask |
924 FocusChangeMask | PropertyChangeMask |
925 ColormapChangeMask | OwnerGrabButtonMask;
927 bzero(&attr, sizeof(attr));
928 XGetWindowAttributes(SDL_Display, win, &attr);
930 /* the following events can be delivered only to one client.
931 * So check which ones are going to someone else, and drop
932 * them from our request.
935 /* ev are the events for a single recipient */
936 long ev = ButtonPressMask | ResizeRedirectMask |
937 SubstructureRedirectMask;
938 ev &= (attr.all_event_masks & ~attr.your_event_mask);
939 /* now ev contains 1 for single-recipient events owned by others.
940 * We must clear those bits in 'want'
941 * and then add the bits in 'attr.your_event_mask' to 'want'
944 want |= attr.your_event_mask;
946 XSelectInput(SDL_Display, win, want);
948 /* Second, handle resize.
949 * We do part of the things that X11Resize does,
950 * but also generate a ConfigureNotify event so
951 * the owner of the window has a chance to do something
954 XResizeWindow(SDL_Display, win, maxw, maxh);
956 XConfigureEvent ce = {
957 .type = ConfigureNotify,
959 .send_event = 1, /* TRUE */
960 .display = SDL_Display,
969 .override_redirect = 0 };
970 XSendEvent(SDL_Display, win, 1 /* TRUE */, StructureNotifyMask, (XEvent *)&ce);
973 #endif /* HAVE_X11 */
974 SDL_WM_SetCaption("Asterisk console Video Output", NULL);
975 if (set_win(gui->screen, &gui->win[WIN_REMOTE], dpy_fmt,
976 env->rem_dpy.w, env->rem_dpy.h, BORDER, BORDER))
978 if (set_win(gui->screen, &gui->win[WIN_LOCAL], dpy_fmt,
979 env->loc_dpy.w, env->loc_dpy.h,
980 3*BORDER+env->rem_dpy.w + kp_w, BORDER))
983 /* display the skin, but do not free it as we need it later to
984 * restore text areas and maybe sliders too.
987 struct SDL_Rect *dest = &gui->win[WIN_KEYPAD].rect;
988 struct SDL_Rect *src = (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) ? & gui->kp_rect : NULL;
989 /* set the coordinates of the keypad relative to the main screen */
990 dest->x = 2*BORDER + env->rem_dpy.w;
994 SDL_BlitSurface(gui->keypad, src, gui->screen, dest);
995 init_board(gui, &gui->bd_msg, gui->kp_msg, dest->x, dest->y);
996 init_board(gui, &gui->bd_dialed, gui->kp_dialed, dest->x, dest->y);
997 SDL_UpdateRects(gui->screen, 1, dest);
1002 /* free resources in case of errors */
1003 env->gui = cleanup_sdl(gui);
1007 * Functions to determine if a point is within a region. Return 1 if success.
1008 * First rotate the point, with
1009 * x' = (x - x0) * cos A + (y - y0) * sin A
1010 * y' = -(x - x0) * sin A + (y - y0) * cos A
1011 * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
1012 * l = sqrt( (x1-x0)^2 + (y1-y0)^2
1013 * Then determine inclusion by simple comparisons i.e.:
1014 * rectangle: x >= 0 && x < l && y >= 0 && y < h
1015 * ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
1017 static int kp_match_area(const struct keypad_entry *e, int x, int y)
1019 double xp, dx = (e->x1 - e->x0);
1020 double yp, dy = (e->y1 - e->y0);
1021 double l = sqrt(dx*dx + dy*dy);
1024 if (l > 1) { /* large enough */
1025 xp = ((x - e->x0)*dx + (y - e->y0)*dy)/l;
1026 yp = (-(x - e->x0)*dy + (y - e->y0)*dx)/l;
1027 if (e->type == KP_RECT) {
1028 ret = (xp >= 0 && xp < l && yp >=0 && yp < l);
1029 } else if (e->type == KP_CIRCLE) {
1030 dx = xp*xp/(l*l) + yp*yp/(e->h*e->h);
1035 ast_log(LOG_WARNING, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
1036 ret, e->c, x, y, e->type, e->x0, e->y0, e->x1, e->y1, e->h);
1041 struct _s_k { const char *s; int k; };
1042 static struct _s_k gui_key_map[] = {
1043 {"PICK_UP", KEY_PICK_UP },
1044 {"PICKUP", KEY_PICK_UP },
1045 {"HANG_UP", KEY_HANG_UP },
1046 {"HANGUP", KEY_HANG_UP },
1047 {"MUTE", KEY_MUTE },
1048 {"FLASH", KEY_FLASH },
1049 {"AUTOANSWER", KEY_AUTOANSWER },
1050 {"SENDVIDEO", KEY_SENDVIDEO },
1051 {"LOCALVIDEO", KEY_LOCALVIDEO },
1052 {"REMOTEVIDEO", KEY_REMOTEVIDEO },
1053 {"GUI_CLOSE", KEY_GUI_CLOSE },
1054 {"MESSAGEBOARD", KEY_MESSAGEBOARD },
1055 {"DIALEDBOARD", KEY_DIALEDBOARD },
1056 {"EDITBOARD", KEY_EDITBOARD },
1057 {"KEYPAD", KEY_KEYPAD }, /* x0 y0 w h - active area of the keypad */
1058 {"MESSAGE", KEY_MESSAGE }, /* x0 y0 w h - incoming messages */
1059 {"DIALED", KEY_DIALED }, /* x0 y0 w h - dialed number */
1060 {"EDIT", KEY_EDIT }, /* x0 y0 w h - edit user input */
1061 {"FONT", KEY_FONT }, /* x0 yo w h rows cols - location and format of the font */
1064 static int gui_map_token(const char *s)
1066 /* map the string into token to be returned */
1069 if (i > 0 || s[1] == '\0') /* numbers or single characters */
1070 return (i > 9) ? i : s[0];
1071 for (p = gui_key_map; p->s; p++) {
1072 if (!strcasecmp(p->s, s))
1075 return KEY_NONE; /* not found */
1078 /*! \brief read a keypad entry line in the format
1080 * token circle xc yc diameter
1081 * token circle xc yc x1 y1 h # ellipse, main diameter and height
1082 * token rect x0 y0 x1 y1 h # rectangle with main side and eight
1083 * token is the token to be returned, either a character or a symbol
1085 * Return 1 on success, 0 on error.
1087 static int keypad_cfg_read(struct gui_info *gui, const char *val)
1089 struct keypad_entry e;
1091 char s1[16], s2[16];
1092 int i, ret = 0; /* default, error */
1094 if (gui == NULL || val == NULL)
1097 s1[0] = s2[0] = '\0';
1098 bzero(&e, sizeof(e));
1099 i = sscanf(val, "%14s %14s %d %d %d %d %d",
1100 s1, s2, &e.x0, &e.y0, &e.x1, &e.y1, &e.h);
1102 e.c = gui_map_token(s1);
1103 if (e.c == KEY_NONE)
1104 return 0; /* nothing found */
1108 case 1: /* only "reset" is allowed */
1109 if (e.c != KEY_RESET)
1115 if (e.c == KEY_KEYPAD) /* active keypad area */
1117 else if (e.c == KEY_MESSAGE)
1119 else if (e.c == KEY_DIALED)
1121 else if (e.c == KEY_EDIT)
1130 if (strcasecmp(s2, "circle")) /* invalid */
1132 /* token circle xc yc diameter */
1134 e.y1 = e.y0; /* map radius in x1 y1 */
1135 e.x1 = e.x0 + e.h; /* map radius in x1 y1 */
1136 e.x0 = e.x0 - e.h; /* map radius in x1 y1 */
1140 if (e.c == KEY_FONT) { /* font - x0 y0 w h rows cols */
1141 ast_log(LOG_WARNING, "font not supported yet\n");
1144 /* token circle|rect x0 y0 x1 y1 h */
1145 if (e.x1 < e.x0 || e.h <= 0) {
1146 ast_log(LOG_WARNING, "error in coordinates\n");
1150 if (!strcasecmp(s2, "circle")) {
1151 /* for a circle we specify the diameter but store center and radii */
1153 e.x0 = (e.x1 + e.x0) / 2;
1154 e.y0 = (e.y1 + e.y0) / 2;
1156 } else if (!strcasecmp(s2, "rect")) {
1162 // ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
1165 if (gui->kp_size == 0) {
1166 gui->kp = ast_calloc(10, sizeof(e));
1167 if (gui->kp == NULL) {
1168 ast_log(LOG_WARNING, "cannot allocate kp");
1173 if (gui->kp_size == gui->kp_used) { /* must allocate */
1174 struct keypad_entry *a = ast_realloc(gui->kp, sizeof(e)*(gui->kp_size+10));
1176 ast_log(LOG_WARNING, "cannot reallocate kp");
1182 if (gui->kp_size == gui->kp_used)
1184 gui->kp[gui->kp_used++] = e;
1185 // ast_log(LOG_WARNING, "now %d regions\n", gui->kp_used);
1188 #endif /* HAVE_SDL */