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 */
59 #include <SDL/SDL_image.h> /* for loading images */
62 enum kp_type { KP_NONE, KP_RECT, KP_CIRCLE };
64 int c; /* corresponding character */
65 int x0, y0, x1, y1, h; /* arguments */
69 /* our representation of a displayed window. SDL can only do one main
70 * window so we map everything within that one
72 struct display_window {
74 SDL_Rect rect; /* location of the window */
78 enum kb_output kb_output; /* where the keyboard output goes */
79 struct drag_info drag; /* info on the window are we dragging */
80 /* support for display. */
81 SDL_Surface *screen; /* the main window */
83 int outfd; /* fd for output */
84 SDL_Surface *keypad; /* the skin for the keypad */
85 SDL_Rect kp_rect; /* portion of the skin to display - default all */
86 SDL_Surface *font; /* font to be used */
87 SDL_Rect font_rects[96]; /* only printable chars */
89 /* each board has two rectangles,
90 * [0] is the geometry relative to the keypad,
91 * [1] is the geometry relative to the whole screen
93 SDL_Rect kp_msg[2]; /* incoming msg, relative to kpad */
96 SDL_Rect kp_edit[2]; /* edit user input */
97 struct board *bd_edit;
99 SDL_Rect kp_dialed[2]; /* dialed number */
100 struct board *bd_dialed;
102 /* variable-size array mapping keypad regions to functions */
103 int kp_size, kp_used;
104 struct keypad_entry *kp;
106 struct display_window win[WIN_MAX];
109 /*! \brief free the resources in struct gui_info and the descriptor itself.
110 * Return NULL so we can assign the value back to the descriptor in case.
112 static struct gui_info *cleanup_sdl(struct gui_info *gui)
119 /* unload font file */
121 SDL_FreeSurface(gui->font);
128 SDL_FreeSurface(gui->keypad);
133 /* uninitialize the SDL environment */
134 for (i = 0; i < WIN_MAX; i++) {
136 SDL_FreeYUVOverlay(gui->win[i].bmp);
138 bzero(gui, sizeof(gui));
145 * Display video frames (from local or remote stream) using the SDL library.
146 * - Set the video mode to use the resolution specified by the codec context
147 * - Create a YUV Overlay to copy the frame into it;
148 * - After the frame is copied into the overlay, display it
150 * The size is taken from the configuration.
152 * 'out' is 0 for remote video, 1 for the local video
154 static void show_frame(struct video_desc *env, int out)
156 AVPicture *p_in, p_out;
157 struct fbuf_t *b_in, *b_out;
159 struct gui_info *gui = env->gui;
164 if (out == WIN_LOCAL) { /* webcam/x11 to sdl */
166 b_out = &env->loc_dpy;
169 /* copy input format from the decoding context */
171 if (env->in == NULL) /* XXX should not happen - decoder not ready */
173 c = env->in->dec_ctx;
174 b_in = &env->in->dec_out;
175 b_in->pix_fmt = c->pix_fmt;
179 b_out = &env->rem_dpy;
180 p_in = (AVPicture *)env->in->d_frame;
182 bmp = gui->win[out].bmp;
183 SDL_LockYUVOverlay(bmp);
184 /* output picture info - this is sdl, YUV420P */
185 bzero(&p_out, sizeof(p_out));
186 p_out.data[0] = bmp->pixels[0];
187 p_out.data[1] = bmp->pixels[1];
188 p_out.data[2] = bmp->pixels[2];
189 p_out.linesize[0] = bmp->pitches[0];
190 p_out.linesize[1] = bmp->pitches[1];
191 p_out.linesize[2] = bmp->pitches[2];
193 my_scale(b_in, p_in, b_out, &p_out);
195 /* lock to protect access to Xlib by different threads. */
196 SDL_DisplayYUVOverlay(bmp, &gui->win[out].rect);
197 SDL_UnlockYUVOverlay(bmp);
201 * Identifiers for regions of the main window.
202 * Values between 0 and 127 correspond to ASCII characters.
203 * The corresponding strings to be used in the skin comment section
204 * are defined in gui_key_map.
207 /* answer/close functions */
212 KEY_AUTOANSWER = 131,
214 KEY_LOCALVIDEO = 133,
215 KEY_REMOTEVIDEO = 134,
218 /* sensitive areas for the various text windows */
219 KEY_MESSAGEBOARD = 140,
220 KEY_DIALEDBOARD = 141,
223 KEY_GUI_CLOSE = 199, /* close gui */
224 /* regions of the skin - displayed area, fonts, etc.
225 * XXX NOTE these are not sensitive areas.
227 KEY_KEYPAD = 200, /* the keypad - default to the whole image */
228 KEY_FONT = 201, /* the font. Maybe not really useful */
229 KEY_MESSAGE = 202, /* area for incoming messages */
230 KEY_DIALED = 203, /* area for dialed numbers */
231 KEY_EDIT = 204, /* area for editing user input */
233 /* areas outside the keypad - simulated */
234 KEY_OUT_OF_KEYPAD = 241,
237 KEY_RESET = 253, /* the 'reset' keyword */
238 KEY_NONE = 254, /* invalid area */
239 KEY_DIGIT_BACKGROUND = 255, /* other areas within the keypad */
243 * Handlers for the various keypad functions
246 /* accumulate digits, possibly call dial if in connected mode */
247 static void keypad_digit(struct video_desc *env, int digit)
249 if (env->owner) { /* we have a call, send the digit */
250 struct ast_frame f = { AST_FRAME_DTMF, 0 };
253 ast_queue_frame(env->owner, &f);
254 } else { /* no call, accumulate digits */
255 char buf[2] = { digit, '\0' };
256 if (env->gui->bd_msg) /* XXX not strictly necessary ... */
257 print_message(env->gui->bd_msg, buf);
261 /* function used to toggle on/off the status of some variables */
262 static char *keypad_toggle(struct video_desc *env, int index)
264 ast_log(LOG_WARNING, "keypad_toggle(%i) called\n", index);
268 env->out.sendvideo = !env->out.sendvideo;
272 struct chan_oss_pvt *o = find_desc(oss_active);
276 case KEY_AUTOANSWER: {
277 struct chan_oss_pvt *o = find_desc(oss_active);
278 o->autoanswer = !o->autoanswer;
286 char *console_do_answer(int fd);
288 * Function called when the pick up button is pressed
289 * perform actions according the channel status:
291 * - if no one is calling us and no digits was pressed,
292 * the operation have no effects,
293 * - if someone is calling us we answer to the call.
294 * - if we have no call in progress and we pressed some
295 * digit, send the digit to the console.
297 static void keypad_pick_up(struct video_desc *env)
299 struct gui_info *gui = env->gui;
301 ast_log(LOG_WARNING, "keypad_pick_up called\n");
303 if (env->owner) { /* someone is calling us, just answer */
304 ast_cli_command(gui->outfd, "console answer");
305 } else { /* we have someone to call */
307 const char *who = ast_skip_blanks(read_message(gui->bd_msg));
308 buf[sizeof(buf) - 1] = '\0';
309 snprintf(buf, sizeof(buf) - 1, "console dial %s", who);
310 ast_log(LOG_WARNING, "doing <%s>\n", buf);
311 print_message(gui->bd_dialed, "\n");
312 print_message(gui->bd_dialed, who);
313 reset_board(gui->bd_msg);
314 ast_cli_command(gui->outfd, buf);
318 #if 0 /* still unused */
320 * As an alternative to SDL_TTF, we can simply load the font from
321 * an image and blit characters on the background of the GUI.
323 * To generate a font we can use the 'fly' command with the
324 * following script (3 lines with 32 chars each)
329 string 255,255,255, 0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
330 string 255,255,255, 0,20,giant,@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
331 string 255,255,255, 0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
336 /* Print given text on the gui */
337 static int gui_output(struct video_desc *env, const char *text)
339 return 1; /* error, not supported */
343 static int video_geom(struct fbuf_t *b, const char *s);
344 static void sdl_setup(struct video_desc *env);
345 static int kp_match_area(const struct keypad_entry *e, int x, int y);
347 static void set_drag(struct drag_info *drag, int x, int y, enum drag_window win)
351 drag->drag_window = win;
355 * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
356 * index value and calling the right callback.
358 * x, y are referred to the upper left corner of the main SDL window.
360 static void handle_mousedown(struct video_desc *env, SDL_MouseButtonEvent button)
362 uint8_t index = KEY_OUT_OF_KEYPAD; /* the key or region of the display we clicked on */
363 struct gui_info *gui = env->gui;
366 ast_log(LOG_WARNING, "event %d %d have %d/%d regions at %p\n",
367 button.x, button.y, gui->kp_used, gui->kp_size, gui->kp);
369 /* for each mousedown we end previous drag */
370 gui->drag.drag_window = DRAG_NONE;
372 /* define keypad boundary */
373 if (button.x < env->rem_dpy.w)
374 index = KEY_REM_DPY; /* click on remote video */
375 else if (button.x > env->rem_dpy.w + gui->keypad->w)
376 index = KEY_LOC_DPY; /* click on local video */
377 else if (button.y > gui->keypad->h)
378 index = KEY_OUT_OF_KEYPAD; /* click outside the keypad */
381 for (i = 0; i < gui->kp_used; i++) {
382 if (kp_match_area(&gui->kp[i], button.x - env->rem_dpy.w, button.y)) {
383 index = gui->kp[i].c;
389 /* exec the function */
390 if (index < 128) { /* surely clicked on the keypad, don't care which key */
391 keypad_digit(env, index);
395 /* answer/close function */
400 ast_cli_command(gui->outfd, "console hangup");
403 /* other functions */
407 keypad_toggle(env, index);
412 case KEY_REMOTEVIDEO:
415 case KEY_MESSAGEBOARD:
416 if (button.button == SDL_BUTTON_LEFT)
417 set_drag(&gui->drag, button.x, button.y, DRAG_MESSAGE);
420 /* press outside the keypad. right increases size, center decreases, left drags */
423 if (button.button == SDL_BUTTON_LEFT) {
424 if (index == KEY_LOC_DPY)
425 set_drag(&gui->drag, button.x, button.y, DRAG_LOCAL);
429 struct fbuf_t *fb = index == KEY_LOC_DPY ? &env->loc_dpy : &env->rem_dpy;
430 sprintf(buf, "%c%dx%d", button.button == SDL_BUTTON_RIGHT ? '>' : '<',
436 case KEY_OUT_OF_KEYPAD:
439 case KEY_DIGIT_BACKGROUND:
442 ast_log(LOG_WARNING, "function not yet defined %i\n", index);
447 * Handle SDL_KEYDOWN type event, put the key pressed
448 * in the dial buffer or in the text-message buffer,
449 * depending on the text_mode variable value.
451 * key is the SDLKey structure corresponding to the key pressed.
452 * Note that SDL returns modifiers (ctrl, shift, alt) as independent
453 * information so the key itself is not enough and we need to
454 * use a translation table, below - one line per entry,
455 * plain, shift, ctrl, ... using the first char as key.
457 static const char *us_kbd_map[] = {
458 "`~", "1!", "2@", "3#", "4$", "5%", "6^",
459 "7&", "8*", "9(", "0)", "-_", "=+", "[{",
460 "]}", "\\|", ";:", "'\"", ",<", ".>", "/?",
465 static const char map_key(SDL_keysym *ks)
467 const char *s, **p = us_kbd_map;
470 if (c == '\r') /* map cr into lf */
472 if (c >= SDLK_NUMLOCK && c <= SDLK_COMPOSE)
473 return 0; /* only a modifier */
476 while ((s = *p) && s[0] != c)
478 if (s) { /* see if we have a modifier and a chance to use it */
479 int l = strlen(s), mod = 0;
481 mod |= (ks->mod & KMOD_SHIFT) ? 1 : 0;
483 mod |= (ks->mod & KMOD_CTRL) ? 2 : 0;
485 mod |= (ks->mod & KMOD_ALT) ? 4 : 0;
488 if (ks->mod & (KMOD_CAPS|KMOD_SHIFT) && c >= 'a' && c <='z')
493 static void handle_keyboard_input(struct video_desc *env, SDL_keysym *ks)
495 char buf[2] = { map_key(ks), '\0' };
496 struct gui_info *gui = env->gui;
497 if (buf[0] == 0) /* modifier ? */
499 switch (gui->kb_output) {
502 case KO_INPUT: /* to be completed */
506 print_message(gui->bd_msg, buf);
507 if (buf[0] == '\r' || buf[0] == '\n') {
513 case KO_DIALED: /* to be completed */
520 static void grabber_move(struct video_out_desc *, int dx, int dy);
522 int compute_drag(int *start, int end, int magnifier);
523 int compute_drag(int *start, int end, int magnifier)
525 int delta = end - *start;
527 /* add a small quadratic term */
528 delta += delta * delta * (delta > 0 ? 1 : -1 )/100;
529 delta *= POLARITY * magnifier;
536 * I am seeing some kind of deadlock or stall around
537 * SDL_PumpEvents() while moving the window on a remote X server
538 * (both xfree-4.4.0 and xorg 7.2)
539 * and windowmaker. It is unclear what causes it.
542 /*! \brief refresh the screen, and also grab a bunch of events.
544 static void eventhandler(struct video_desc *env, const char *caption)
546 struct gui_info *gui = env->gui;
547 struct drag_info *drag;
550 SDL_Event ev[N_EVENTS];
556 SDL_WM_SetCaption(caption, NULL);
558 #define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
559 while ( (n = SDL_PeepEvents(ev, N_EVENTS, SDL_GETEVENT, SDL_ALLEVENTS)) > 0) {
560 for (i = 0; i < n; i++) {
562 ast_log(LOG_WARNING, "------ event %d at %d %d\n",
563 ev[i].type, ev[i].button.x, ev[i].button.y);
565 switch (ev[i].type) {
567 handle_keyboard_input(env, &ev[i].key.keysym);
569 case SDL_MOUSEMOTION:
570 case SDL_MOUSEBUTTONUP:
571 if (drag->drag_window == DRAG_LOCAL) {
572 /* move the capture source */
573 int dx = compute_drag(&drag->x_start, ev[i].motion.x, 3);
574 int dy = compute_drag(&drag->y_start, ev[i].motion.y, 3);
575 grabber_move(&env->out, dx, dy);
576 } else if (drag->drag_window == DRAG_MESSAGE) {
577 /* scroll up/down the window */
578 int dy = compute_drag(&drag->y_start, ev[i].motion.y, 1);
579 move_message_board(gui->bd_msg, dy);
581 if (ev[i].type == SDL_MOUSEBUTTONUP)
582 drag->drag_window = DRAG_NONE;
584 case SDL_MOUSEBUTTONDOWN:
585 handle_mousedown(env, ev[i].button);
591 struct timeval b, a = ast_tvnow();
593 //SDL_Lock_EventThread();
596 i = ast_tvdiff_ms(b, a);
598 fprintf(stderr, "-------- SDL_PumpEvents took %dms\n", i);
599 //SDL_Unlock_EventThread();
603 static SDL_Surface *load_image(const char *file)
607 #ifdef HAVE_SDL_IMAGE
608 temp = IMG_Load(file);
610 temp = SDL_LoadBMP(file);
613 fprintf(stderr, "Unable to load image %s: %s\n",
614 file, SDL_GetError());
618 static void keypad_setup(struct gui_info *gui, const char *kp_file);
620 /* TODO: consistency checks, check for bpp, widht and height */
621 /* Init the mask image used to grab the action. */
622 static struct gui_info *gui_init(const char *keypad_file, const char *font)
624 struct gui_info *gui = ast_calloc(1, sizeof(*gui));
628 /* initialize keypad status */
629 gui->kb_output = KO_MESSAGE; /* XXX temp */
630 gui->drag.drag_window = DRAG_NONE;
633 keypad_setup(gui, keypad_file);
634 if (gui->keypad == NULL) /* no keypad, we are done */
637 if (!ast_strlen_zero(font)) {
641 gui->font = load_image(font);
643 ast_log(LOG_WARNING, "Unable to load font %s, no output available\n", font);
646 ast_log(LOG_WARNING, "Loaded font %s\n", font);
647 /* XXX hardwired constants - 3 rows of 32 chars */
651 for (i = 0; i < 96; r++, i++) {
652 r->x = (i % 32 ) * FONT_W;
653 r->y = (i / 32 ) * FONT_H;
659 gui->outfd = open ("/dev/null", O_WRONLY); /* discard output, temporary */
660 if (gui->outfd < 0) {
661 ast_log(LOG_WARNING, "Unable output fd\n");
671 /* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
672 static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
673 int w, int h, int x, int y)
675 win->bmp = SDL_CreateYUVOverlay(w, h, fmt, screen);
676 if (win->bmp == NULL)
677 return -1; /* error */
685 static int keypad_cfg_read(struct gui_info *gui, const char *val);
687 static void keypad_setup(struct gui_info *gui, const char *kp_file)
691 const char region[] = "region";
692 int reg_len = strlen(region);
697 gui->keypad = load_image(kp_file);
700 /* now try to read the keymap from the file. */
701 fd = fopen(kp_file, "r");
703 ast_log(LOG_WARNING, "fail to open %s\n", kp_file);
707 * If the keypad image has a comment field, try to read
708 * the button location from there. The block must start with
709 * a comment (or empty) line, and continue with entries like:
710 * region = token shape x0 y0 x1 y1 h
712 * (basically, lines have the same format as config file entries).
713 * You can add it to a jpeg file using wrjpgcom
715 while (fgets(buf, sizeof(buf), fd)) {
718 if (!strstr(buf, region)) { /* no keyword yet */
719 if (!in_comment) /* still waiting for initial comment block */
724 if (!in_comment) { /* first keyword, reset previous entries */
725 keypad_cfg_read(gui, "reset");
728 s = ast_skip_blanks(buf);
730 if (memcmp(s, region, reg_len))
731 break; /* keyword not found */
732 s = ast_skip_blanks(s + reg_len); /* space between token and '=' */
733 if (*s++ != '=') /* missing separator */
735 if (*s == '>') /* skip '>' if present */
737 keypad_cfg_read(gui, ast_skip_blanks(s));
742 struct board *board_setup(SDL_Surface *screen, SDL_Rect *dest,
743 SDL_Surface *font, SDL_Rect *font_rects);
745 /*! \brief initialize the boards we have in the keypad */
746 static void init_board(struct gui_info *gui, struct board **dst, SDL_Rect *r, int dx, int dy)
748 if (r[0].w == 0 || r[0].h == 0)
749 return; /* not available */
750 r[1] = r[0]; /* copy geometry */
751 r[1].x += dx; /* add offset of main window */
753 if (*dst == NULL) { /* initial call */
754 *dst = board_setup(gui->screen, &r[1], gui->font, gui->font_rects);
760 /*! \brief [re]set the main sdl window, useful in case of resize.
761 * We can tell the first from subsequent calls from the value of
762 * env->gui, which is NULL the first time.
764 static void sdl_setup(struct video_desc *env)
766 int dpy_fmt = SDL_IYUV_OVERLAY; /* YV12 causes flicker in SDL */
767 int depth, maxw, maxh;
768 const SDL_VideoInfo *info;
769 int kp_w = 0, kp_h = 0; /* keypad width and height */
770 struct gui_info *gui = env->gui;
773 * initialize the SDL environment. We have one large window
774 * with local and remote video, and a keypad.
775 * At the moment we arrange them statically, as follows:
776 * - on the left, the remote video;
777 * - on the center, the keypad
778 * - on the right, the local video
779 * We need to read in the skin for the keypad before creating the main
780 * SDL window, because the size is only known here.
783 if (gui == NULL && SDL_Init(SDL_INIT_VIDEO)) {
784 ast_log(LOG_WARNING, "Could not initialize SDL - %s\n",
786 /* again not fatal, just we won't display anything */
789 info = SDL_GetVideoInfo();
790 /* We want at least 16bpp to support YUV overlays.
791 * E.g with SDL_VIDEODRIVER = aalib the default is 8
793 depth = info->vfmt->BitsPerPixel;
797 env->gui = gui = gui_init(env->keypad_file, env->keypad_font);
802 if (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) {
803 kp_w = gui->kp_rect.w;
804 kp_h = gui->kp_rect.h;
806 kp_w = gui->keypad->w;
807 kp_h = gui->keypad->h;
810 /* XXX same for other boards */
811 #define BORDER 5 /* border around our windows */
812 maxw = env->rem_dpy.w + env->loc_dpy.w + kp_w;
813 maxh = MAX( MAX(env->rem_dpy.h, env->loc_dpy.h), kp_h);
816 gui->screen = SDL_SetVideoMode(maxw, maxh, depth, 0);
818 ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
822 SDL_WM_SetCaption("Asterisk console Video Output", NULL);
823 if (set_win(gui->screen, &gui->win[WIN_REMOTE], dpy_fmt,
824 env->rem_dpy.w, env->rem_dpy.h, BORDER, BORDER))
826 if (set_win(gui->screen, &gui->win[WIN_LOCAL], dpy_fmt,
827 env->loc_dpy.w, env->loc_dpy.h,
828 3*BORDER+env->rem_dpy.w + kp_w, BORDER))
831 /* display the skin, but do not free it as we need it later to
832 * restore text areas and maybe sliders too.
835 struct SDL_Rect *dest = &gui->win[WIN_KEYPAD].rect;
836 struct SDL_Rect *src = (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) ? & gui->kp_rect : NULL;
837 /* set the coordinates of the keypad relative to the main screen */
838 dest->x = 2*BORDER + env->rem_dpy.w;
842 SDL_BlitSurface(gui->keypad, src, gui->screen, dest);
843 init_board(gui, &gui->bd_msg, gui->kp_msg, dest->x, dest->y);
844 init_board(gui, &gui->bd_dialed, gui->kp_dialed, dest->x, dest->y);
845 SDL_UpdateRects(gui->screen, 1, dest);
850 /* free resources in case of errors */
851 env->gui = cleanup_sdl(gui);
855 * Functions to determine if a point is within a region. Return 1 if success.
856 * First rotate the point, with
857 * x' = (x - x0) * cos A + (y - y0) * sin A
858 * y' = -(x - x0) * sin A + (y - y0) * cos A
859 * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
860 * l = sqrt( (x1-x0)^2 + (y1-y0)^2
861 * Then determine inclusion by simple comparisons i.e.:
862 * rectangle: x >= 0 && x < l && y >= 0 && y < h
863 * ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
865 static int kp_match_area(const struct keypad_entry *e, int x, int y)
867 double xp, dx = (e->x1 - e->x0);
868 double yp, dy = (e->y1 - e->y0);
869 double l = sqrt(dx*dx + dy*dy);
872 if (l > 1) { /* large enough */
873 xp = ((x - e->x0)*dx + (y - e->y0)*dy)/l;
874 yp = (-(x - e->x0)*dy + (y - e->y0)*dx)/l;
875 if (e->type == KP_RECT) {
876 ret = (xp >= 0 && xp < l && yp >=0 && yp < l);
877 } else if (e->type == KP_CIRCLE) {
878 dx = xp*xp/(l*l) + yp*yp/(e->h*e->h);
883 ast_log(LOG_WARNING, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
884 ret, e->c, x, y, e->type, e->x0, e->y0, e->x1, e->y1, e->h);
889 struct _s_k { const char *s; int k; };
890 static struct _s_k gui_key_map[] = {
891 {"PICK_UP", KEY_PICK_UP },
892 {"PICKUP", KEY_PICK_UP },
893 {"HANG_UP", KEY_HANG_UP },
894 {"HANGUP", KEY_HANG_UP },
896 {"FLASH", KEY_FLASH },
897 {"AUTOANSWER", KEY_AUTOANSWER },
898 {"SENDVIDEO", KEY_SENDVIDEO },
899 {"LOCALVIDEO", KEY_LOCALVIDEO },
900 {"REMOTEVIDEO", KEY_REMOTEVIDEO },
901 {"GUI_CLOSE", KEY_GUI_CLOSE },
902 {"MESSAGEBOARD", KEY_MESSAGEBOARD },
903 {"DIALEDBOARD", KEY_DIALEDBOARD },
904 {"EDITBOARD", KEY_EDITBOARD },
905 {"KEYPAD", KEY_KEYPAD }, /* x0 y0 w h - active area of the keypad */
906 {"MESSAGE", KEY_MESSAGE }, /* x0 y0 w h - incoming messages */
907 {"DIALED", KEY_DIALED }, /* x0 y0 w h - dialed number */
908 {"EDIT", KEY_EDIT }, /* x0 y0 w h - edit user input */
909 {"FONT", KEY_FONT }, /* x0 yo w h rows cols - location and format of the font */
912 static int gui_map_token(const char *s)
914 /* map the string into token to be returned */
917 if (i > 0 || s[1] == '\0') /* numbers or single characters */
918 return (i > 9) ? i : s[0];
919 for (p = gui_key_map; p->s; p++) {
920 if (!strcasecmp(p->s, s))
923 return KEY_NONE; /* not found */
926 /*! \brief read a keypad entry line in the format
928 * token circle xc yc diameter
929 * token circle xc yc x1 y1 h # ellipse, main diameter and height
930 * token rect x0 y0 x1 y1 h # rectangle with main side and eight
931 * token is the token to be returned, either a character or a symbol
933 * Return 1 on success, 0 on error.
935 static int keypad_cfg_read(struct gui_info *gui, const char *val)
937 struct keypad_entry e;
940 int i, ret = 0; /* default, error */
942 if (gui == NULL || val == NULL)
945 s1[0] = s2[0] = '\0';
946 bzero(&e, sizeof(e));
947 i = sscanf(val, "%14s %14s %d %d %d %d %d",
948 s1, s2, &e.x0, &e.y0, &e.x1, &e.y1, &e.h);
950 e.c = gui_map_token(s1);
952 return 0; /* nothing found */
956 case 1: /* only "reset" is allowed */
957 if (e.c != KEY_RESET)
963 if (e.c == KEY_KEYPAD) /* active keypad area */
965 else if (e.c == KEY_MESSAGE)
967 else if (e.c == KEY_DIALED)
969 else if (e.c == KEY_EDIT)
978 if (strcasecmp(s2, "circle")) /* invalid */
980 /* token circle xc yc diameter */
982 e.y1 = e.y0; /* map radius in x1 y1 */
983 e.x1 = e.x0 + e.h; /* map radius in x1 y1 */
984 e.x0 = e.x0 - e.h; /* map radius in x1 y1 */
988 if (e.c == KEY_FONT) { /* font - x0 y0 w h rows cols */
989 ast_log(LOG_WARNING, "font not supported yet\n");
992 /* token circle|rect x0 y0 x1 y1 h */
993 if (e.x1 < e.x0 || e.h <= 0) {
994 ast_log(LOG_WARNING, "error in coordinates\n");
998 if (!strcasecmp(s2, "circle")) {
999 /* for a circle we specify the diameter but store center and radii */
1001 e.x0 = (e.x1 + e.x0) / 2;
1002 e.y0 = (e.y1 + e.y0) / 2;
1004 } else if (!strcasecmp(s2, "rect")) {
1010 // ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
1013 if (gui->kp_size == 0) {
1014 gui->kp = ast_calloc(10, sizeof(e));
1015 if (gui->kp == NULL) {
1016 ast_log(LOG_WARNING, "cannot allocate kp");
1021 if (gui->kp_size == gui->kp_used) { /* must allocate */
1022 struct keypad_entry *a = ast_realloc(gui->kp, sizeof(e)*(gui->kp_size+10));
1024 ast_log(LOG_WARNING, "cannot reallocate kp");
1030 if (gui->kp_size == gui->kp_used)
1032 gui->kp[gui->kp_used++] = e;
1033 // ast_log(LOG_WARNING, "now %d regions\n", gui->kp_used);
1036 #endif /* HAVE_SDL */