2 * GUI for console video.
3 * The routines here are in charge of loading the keypad and handling events.
9 #include "console_video.h"
10 #include "asterisk/lock.h"
11 #include "asterisk/frame.h"
12 #include "asterisk/utils.h" /* ast_calloc and ast_realloc */
13 #include <math.h> /* sqrt */
15 /* we support 3 regions in the GUI */
16 enum { WIN_LOCAL, WIN_REMOTE, WIN_KEYPAD, WIN_MAX };
19 /* stubs if we don't have any sdl */
20 static void show_frame(struct video_desc *env, int out) {}
21 static void sdl_setup(struct video_desc *env) {}
22 static struct gui_info *cleanup_sdl(struct gui_info *gui) { return NULL; }
23 static void eventhandler(struct video_desc *env, const char *caption) {}
24 static int keypad_cfg_read(struct gui_info *gui, const char *val) { return 0; }
26 #else /* HAVE_SDL, the real rendering code */
30 #include <SDL/SDL_image.h> /* for loading images */
33 #include <SDL/SDL_ttf.h> /* render text on sdl surfaces */
36 enum kp_type { KP_NONE, KP_RECT, KP_CIRCLE };
38 int c; /* corresponding character */
39 int x0, y0, x1, y1, h; /* arguments */
43 /* our representation of a displayed window. SDL can only do one main
44 * window so we map everything within that one
46 struct display_window {
48 SDL_Rect rect; /* location of the window */
50 #define GUI_BUFFER_LEN 256 /* buffer lenght used for input buffers */
52 struct keypad_entry; /* defined in console_gui.c */
54 /*! \brief info related to the gui: button status, mouse coords, etc. */
56 char inbuf[GUI_BUFFER_LEN]; /* buffer for to-dial buffer */
57 int inbuf_pos; /* next free position in inbuf */
58 char msgbuf[GUI_BUFFER_LEN]; /* buffer for text-message buffer */
59 int msgbuf_pos; /* next free position in msgbuf */
60 int text_mode; /* switch to-dial and text-message mode */
61 int drag_mode; /* switch phone and drag-source mode */
62 int x_drag; /* x coordinate where the drag starts */
63 int y_drag; /* y coordinate where the drag starts */
65 TTF_Font *font; /* font to be used */
67 /* support for display. */
68 SDL_Surface *screen; /* the main window */
70 int outfd; /* fd for output */
71 SDL_Surface *keypad; /* the pixmap for the keypad */
73 struct keypad_entry *kp;
75 struct display_window win[WIN_MAX];
78 /*! \brief free the resources in struct gui_info and the descriptor itself.
79 * Return NULL so we can assign the value back to the descriptor in case.
81 static struct gui_info *cleanup_sdl(struct gui_info *gui)
89 /* unload font file */
91 TTF_CloseFont(gui->font);
95 /* uninitialize SDL_ttf library */
102 SDL_FreeSurface(gui->keypad);
107 /* uninitialize the SDL environment */
108 for (i = 0; i < WIN_MAX; i++) {
110 SDL_FreeYUVOverlay(gui->win[i].bmp);
112 bzero(gui, sizeof(gui));
119 * Display video frames (from local or remote stream) using the SDL library.
120 * - Set the video mode to use the resolution specified by the codec context
121 * - Create a YUV Overlay to copy the frame into it;
122 * - After the frame is copied into the overlay, display it
124 * The size is taken from the configuration.
126 * 'out' is 0 for remote video, 1 for the local video
128 static void show_frame(struct video_desc *env, int out)
130 AVPicture *p_in, p_out;
131 struct fbuf_t *b_in, *b_out;
133 struct gui_info *gui = env->gui;
138 if (out == WIN_LOCAL) { /* webcam/x11 to sdl */
140 b_out = &env->loc_dpy;
143 /* copy input format from the decoding context */
145 if (env->in == NULL) /* XXX should not happen - decoder not ready */
147 c = env->in->dec_ctx;
148 b_in = &env->in->dec_out;
149 b_in->pix_fmt = c->pix_fmt;
153 b_out = &env->rem_dpy;
154 p_in = (AVPicture *)env->in->d_frame;
156 bmp = gui->win[out].bmp;
157 SDL_LockYUVOverlay(bmp);
158 /* output picture info - this is sdl, YUV420P */
159 bzero(&p_out, sizeof(p_out));
160 p_out.data[0] = bmp->pixels[0];
161 p_out.data[1] = bmp->pixels[1];
162 p_out.data[2] = bmp->pixels[2];
163 p_out.linesize[0] = bmp->pitches[0];
164 p_out.linesize[1] = bmp->pitches[1];
165 p_out.linesize[2] = bmp->pitches[2];
167 my_scale(b_in, p_in, b_out, &p_out);
169 /* lock to protect access to Xlib by different threads. */
170 SDL_DisplayYUVOverlay(bmp, &gui->win[out].rect);
171 SDL_UnlockYUVOverlay(bmp);
175 * GUI layout, structure and management
178 For the GUI we use SDL to create a large surface (gui->screen)
179 containing tree sections: remote video on the left, local video
180 on the right, and the keypad with all controls and text windows
182 The central section is built using two images: one is the skin,
183 the other one is a mask where the sensitive areas of the skin
184 are colored in different grayscale levels according to their
185 functions. The mapping between colors and function is defined
186 in the 'enum pixel_value' below.
188 Mouse and keyboard events are detected on the whole surface, and
189 handled differently according to their location, as follows:
191 - drag on the local video window are used to move the captured
192 area (in the case of X11 grabber) or the picture-in-picture
193 location (in case of camera included on the X11 grab).
194 - click on the keypad are mapped to the corresponding key;
195 - drag on some keypad areas (sliders etc.) are mapped to the
196 corresponding functions;
197 - keystrokes are used as keypad functions, or as text input
198 if we are in text-input mode.
200 To manage these behavior we use two status variables,
201 that defines if keyboard events should be redirect to dialing functions
202 or to write message functions, and if mouse events should be used
203 to implement keypad functionalities or to drag the capture device.
205 Configuration options control the appeareance of the gui:
207 keypad = /tmp/phone.jpg ; the keypad on the screen
208 keypad_font = /tmp/font.ttf ; the font to use for output
213 /* enumerate for the pixel value. 0..127 correspond to ascii chars */
215 /* answer/close functions */
219 /* other functions */
221 KEY_AUTOANSWER = 131,
223 KEY_LOCALVIDEO = 133,
224 KEY_REMOTEVIDEO = 134,
225 KEY_WRITEMESSAGE = 135,
226 KEY_GUI_CLOSE = 136, /* close gui */
228 /* other areas within the keypad */
229 KEY_DIGIT_BACKGROUND = 255,
231 /* areas outside the keypad - simulated */
232 KEY_OUT_OF_KEYPAD = 251,
238 * Handlers for the various keypad functions
241 /*! \brief append a character, or reset if '\0' */
242 static void append_char(char *str, int *str_pos, const char c)
247 else if (i < GUI_BUFFER_LEN - 1)
250 i = GUI_BUFFER_LEN - 1; /* unnecessary, i think */
255 /* accumulate digits, possibly call dial if in connected mode */
256 static void keypad_digit(struct video_desc *env, int digit)
258 if (env->owner) { /* we have a call, send the digit */
259 struct ast_frame f = { AST_FRAME_DTMF, 0 };
262 ast_queue_frame(env->owner, &f);
263 } else { /* no call, accumulate digits */
264 append_char(env->gui->inbuf, &env->gui->inbuf_pos, digit);
268 /* this is a wrapper for actions that are available through the cli */
269 /* TODO append arg to command and send the resulting string as cli command */
270 static void keypad_send_command(struct video_desc *env, char *command)
272 ast_log(LOG_WARNING, "keypad_send_command(%s) called\n", command);
273 ast_cli_command(env->gui->outfd, command);
277 /* function used to toggle on/off the status of some variables */
278 static char *keypad_toggle(struct video_desc *env, int index)
280 ast_log(LOG_WARNING, "keypad_toggle(%i) called\n", index);
284 env->out.sendvideo = !env->out.sendvideo;
288 struct chan_oss_pvt *o = find_desc(oss_active);
292 case KEY_AUTOANSWER: {
293 struct chan_oss_pvt *o = find_desc(oss_active);
294 o->autoanswer = !o->autoanswer;
302 char *console_do_answer(int fd);
304 * Function called when the pick up button is pressed
305 * perform actions according the channel status:
307 * - if no one is calling us and no digits was pressed,
308 * the operation have no effects,
309 * - if someone is calling us we answer to the call.
310 * - if we have no call in progress and we pressed some
311 * digit, send the digit to the console.
313 static void keypad_pick_up(struct video_desc *env)
315 struct gui_info *gui = env->gui;
317 ast_log(LOG_WARNING, "keypad_pick_up called\n");
319 if (env->owner) { /* someone is calling us, just answer */
320 console_do_answer(-1);
321 } else if (gui->inbuf_pos) { /* we have someone to call */
322 ast_cli_command(gui->outfd, gui->inbuf);
325 append_char(gui->inbuf, &gui->inbuf_pos, '\0'); /* clear buffer */
328 #if 0 /* still unused */
330 * As an alternative to SDL_TTF, we can simply load the font from
331 * an image and blit characters on the background of the GUI.
333 * To generate a font we can use the 'fly' command with the
334 * following script (3 lines with 32 chars each)
339 string 255,255,255, 0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
340 string 255,255,255, 0,20,giant,@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
341 string 255,255,255, 0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
346 /* Print given text on the gui */
347 static int gui_output(struct video_desc *env, const char *text)
350 return 1; /* error, not supported */
352 int x = 30, y = 20; /* XXX change */
353 SDL_Surface *output = NULL;
354 SDL_Color color = {0, 0, 0}; /* text color */
355 struct gui_info *gui = env->gui;
356 SDL_Rect dest = {gui->win[WIN_KEYPAD].rect.x + x, y};
358 /* clean surface each rewrite */
359 SDL_BlitSurface(gui->keypad, NULL, gui->screen, &gui->win[WIN_KEYPAD].rect);
361 output = TTF_RenderText_Solid(gui->font, text, color);
362 if (output == NULL) {
363 ast_log(LOG_WARNING, "Cannot render text on gui - %s\n", TTF_GetError());
367 SDL_BlitSurface(output, NULL, gui->screen, &dest);
369 SDL_UpdateRects(gui->keypad, 1, &gui->win[WIN_KEYPAD].rect);
370 SDL_FreeSurface(output);
371 return 0; /* success */
376 static int video_geom(struct fbuf_t *b, const char *s);
377 static void sdl_setup(struct video_desc *env);
378 static int kp_match_area(const struct keypad_entry *e, int x, int y);
381 * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
382 * index value and calling the right callback.
384 * x, y are referred to the upper left corner of the main SDL window.
386 static void handle_button_event(struct video_desc *env, SDL_MouseButtonEvent button)
388 uint8_t index = KEY_OUT_OF_KEYPAD; /* the key or region of the display we clicked on */
389 struct gui_info *gui = env->gui;
392 ast_log(LOG_WARNING, "event %d %d have %d/%d regions at %p\n",
393 button.x, button.y, gui->kp_used, gui->kp_size, gui->kp);
395 /* for each click we come back in normal mode */
398 /* define keypad boundary */
399 if (button.x < env->rem_dpy.w)
400 index = KEY_REM_DPY; /* click on remote video */
401 else if (button.x > env->rem_dpy.w + gui->keypad->w)
402 index = KEY_LOC_DPY; /* click on local video */
403 else if (button.y > gui->keypad->h)
404 index = KEY_OUT_OF_KEYPAD; /* click outside the keypad */
407 for (i = 0; i < gui->kp_used; i++) {
408 if (kp_match_area(&gui->kp[i], button.x - env->rem_dpy.w, button.y)) {
409 index = gui->kp[i].c;
415 /* exec the function */
416 if (index < 128) { /* surely clicked on the keypad, don't care which key */
417 keypad_digit(env, index);
421 /* answer/close function */
426 keypad_send_command(env, "console hangup");
429 /* other functions */
433 keypad_toggle(env, index);
438 case KEY_REMOTEVIDEO:
440 case KEY_WRITEMESSAGE:
441 /* goes in text-mode */
442 env->gui->text_mode = 1;
446 /* press outside the keypad. right increases size, center decreases, left drags */
449 if (button.button == SDL_BUTTON_LEFT) {
450 if (index == KEY_LOC_DPY) {
451 /* store points where the drag start
452 * and switch in drag mode */
453 env->gui->x_drag = button.x;
454 env->gui->y_drag = button.y;
455 env->gui->drag_mode = 1;
460 struct fbuf_t *fb = index == KEY_LOC_DPY ? &env->loc_dpy : &env->rem_dpy;
461 sprintf(buf, "%c%dx%d", button.button == SDL_BUTTON_RIGHT ? '>' : '<',
467 case KEY_OUT_OF_KEYPAD:
470 case KEY_DIGIT_BACKGROUND:
473 ast_log(LOG_WARNING, "function not yet defined %i\n", index);
478 * Handle SDL_KEYDOWN type event, put the key pressed
479 * in the dial buffer or in the text-message buffer,
480 * depending on the text_mode variable value.
482 * key is the SDLKey structure corresponding to the key pressed.
484 static void handle_keyboard_input(struct video_desc *env, SDLKey key)
486 struct gui_info *gui = env->gui;
487 if (gui->text_mode) {
488 /* append in the text-message buffer */
489 if (key == SDLK_RETURN) {
490 /* send the text message and return in normal mode */
492 keypad_send_command(env, "send text");
494 /* accumulate the key in the message buffer */
495 append_char(gui->msgbuf, &gui->msgbuf_pos, key);
499 /* append in the dial buffer */
500 append_char(gui->inbuf, &gui->inbuf_pos, key);
506 /* implement superlinear acceleration on the movement */
507 static int move_accel(int delta)
509 int d1 = delta*delta / 100;
510 return (delta > 0) ? delta + d1 : delta - d1;
513 static void grabber_move(struct video_out_desc *, int dx, int dy);
515 * Move the source of the captured video.
517 * x_final_drag and y_final_drag are the coordinates where the drag ends,
518 * start coordinares are in the gui_info structure.
520 static void move_capture_source(struct video_desc *env, int x_final_drag, int y_final_drag)
524 /* move the origin */
525 #define POLARITY -1 /* +1 or -1 depending on the desired direction */
526 dx = POLARITY*move_accel(x_final_drag - env->gui->x_drag) * 3;
527 dy = POLARITY*move_accel(y_final_drag - env->gui->y_drag) * 3;
529 env->gui->x_drag = x_final_drag; /* update origin */
530 env->gui->y_drag = y_final_drag;
532 grabber_move(&env->out, dx, dy);
537 * I am seeing some kind of deadlock or stall around
538 * SDL_PumpEvents() while moving the window on a remote X server
539 * (both xfree-4.4.0 and xorg 7.2)
540 * and windowmaker. It is unclear what causes it.
543 /*! \brief refresh the screen, and also grab a bunch of events.
545 static void eventhandler(struct video_desc *env, const char *caption)
547 struct gui_info *gui = env->gui;
550 SDL_Event ev[N_EVENTS];
555 SDL_WM_SetCaption(caption, NULL);
557 #define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
558 while ( (n = SDL_PeepEvents(ev, N_EVENTS, SDL_GETEVENT, SDL_ALLEVENTS)) > 0) {
559 for (i = 0; i < n; i++) {
561 ast_log(LOG_WARNING, "------ event %d at %d %d\n",
562 ev[i].type, ev[i].button.x, ev[i].button.y);
564 switch (ev[i].type) {
566 handle_keyboard_input(env, ev[i].key.keysym.sym);
568 case SDL_MOUSEMOTION:
569 if (gui->drag_mode != 0)
570 move_capture_source(env, ev[i].motion.x, ev[i].motion.y);
572 case SDL_MOUSEBUTTONDOWN:
573 handle_button_event(env, ev[i].button);
575 case SDL_MOUSEBUTTONUP:
576 if (gui->drag_mode != 0) {
577 move_capture_source(env, ev[i].button.x, ev[i].button.y);
586 struct timeval b, a = ast_tvnow();
588 //SDL_Lock_EventThread();
591 i = ast_tvdiff_ms(b, a);
593 fprintf(stderr, "-------- SDL_PumpEvents took %dms\n", i);
594 //SDL_Unlock_EventThread();
598 static SDL_Surface *get_keypad(const char *file)
602 #ifdef HAVE_SDL_IMAGE
603 temp = IMG_Load(file);
605 temp = SDL_LoadBMP(file);
608 fprintf(stderr, "Unable to load image %s: %s\n",
609 file, SDL_GetError());
613 static void keypad_setup(struct gui_info *gui, const char *kp_file);
615 /* TODO: consistency checks, check for bpp, widht and height */
616 /* Init the mask image used to grab the action. */
617 static struct gui_info *gui_init(const char *keypad_file)
619 struct gui_info *gui = ast_calloc(1, sizeof(*gui));
623 /* initialize keypad status */
628 /* initialize keyboard buffer */
629 append_char(gui->inbuf, &gui->inbuf_pos, '\0');
630 append_char(gui->msgbuf, &gui->msgbuf_pos, '\0');
632 keypad_setup(gui, keypad_file);
633 if (gui->keypad == NULL) /* no keypad, we are done */
636 /* Initialize SDL_ttf library and load font */
637 if (TTF_Init() == -1) {
638 ast_log(LOG_WARNING, "Unable to init SDL_ttf, no output available\n");
642 #define GUI_FONTSIZE 28
643 gui->font = TTF_OpenFont( env->keypad_font, GUI_FONTSIZE);
645 ast_log(LOG_WARNING, "Unable to load font %s, no output available\n", env->keypad_font);
648 ast_log(LOG_WARNING, "Loaded font %s\n", env->keypad_font);
651 gui->outfd = open ("/dev/null", O_WRONLY); /* discard output, temporary */
652 if (gui->outfd < 0) {
653 ast_log(LOG_WARNING, "Unable output fd\n");
663 /* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
664 static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
665 int w, int h, int x, int y)
667 win->bmp = SDL_CreateYUVOverlay(w, h, fmt, screen);
668 if (win->bmp == NULL)
669 return -1; /* error */
677 static int keypad_cfg_read(struct gui_info *gui, const char *val);
679 static void keypad_setup(struct gui_info *gui, const char *kp_file)
683 const char region[] = "region";
684 int reg_len = strlen(region);
689 gui->keypad = get_keypad(kp_file);
692 /* now try to read the keymap from the file. */
693 fd = fopen(kp_file, "r");
695 ast_log(LOG_WARNING, "fail to open %s\n", kp_file);
699 * If the keypad image has a comment field, try to read
700 * the button location from there. The block must start with
701 * a comment (or empty) line, and continue with entries like:
702 * region = token shape x0 y0 x1 y1 h
704 * (basically, lines have the same format as config file entries).
705 * You can add it to a jpeg file using wrjpgcom
707 while (fgets(buf, sizeof(buf), fd)) {
710 if (!strstr(buf, region)) { /* no keyword yet */
711 if (!in_comment) /* still waiting for initial comment block */
716 if (!in_comment) { /* first keyword, reset previous entries */
717 keypad_cfg_read(gui, "reset");
720 s = ast_skip_blanks(buf);
722 if (memcmp(s, region, reg_len))
723 break; /* keyword not found */
724 s = ast_skip_blanks(s + reg_len); /* space between token and '=' */
725 if (*s++ != '=') /* missing separator */
727 if (*s == '>') /* skip '>' if present */
729 keypad_cfg_read(gui, ast_skip_blanks(s));
734 /*! \brief [re]set the main sdl window, useful in case of resize.
735 * We can tell the first from subsequent calls from the value of
736 * env->gui, which is NULL the first time.
738 static void sdl_setup(struct video_desc *env)
740 int dpy_fmt = SDL_IYUV_OVERLAY; /* YV12 causes flicker in SDL */
741 int depth, maxw, maxh;
742 const SDL_VideoInfo *info;
743 int kp_w = 0, kp_h = 0; /* keypad width and height */
744 struct gui_info *gui = env->gui;
747 * initialize the SDL environment. We have one large window
748 * with local and remote video, and a keypad.
749 * At the moment we arrange them statically, as follows:
750 * - on the left, the remote video;
751 * - on the center, the keypad
752 * - on the right, the local video
753 * We need to read in the skin for the keypad before creating the main
754 * SDL window, because the size is only known here.
757 if (gui == NULL && SDL_Init(SDL_INIT_VIDEO)) {
758 ast_log(LOG_WARNING, "Could not initialize SDL - %s\n",
760 /* again not fatal, just we won't display anything */
763 info = SDL_GetVideoInfo();
764 /* We want at least 16bpp to support YUV overlays.
765 * E.g with SDL_VIDEODRIVER = aalib the default is 8
767 depth = info->vfmt->BitsPerPixel;
771 env->gui = gui = gui_init(env->keypad_file);
776 kp_w = gui->keypad->w;
777 kp_h = gui->keypad->h;
779 #define BORDER 5 /* border around our windows */
780 maxw = env->rem_dpy.w + env->loc_dpy.w + kp_w;
781 maxh = MAX( MAX(env->rem_dpy.h, env->loc_dpy.h), kp_h);
784 gui->screen = SDL_SetVideoMode(maxw, maxh, depth, 0);
786 ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
790 SDL_WM_SetCaption("Asterisk console Video Output", NULL);
791 if (set_win(gui->screen, &gui->win[WIN_REMOTE], dpy_fmt,
792 env->rem_dpy.w, env->rem_dpy.h, BORDER, BORDER))
794 if (set_win(gui->screen, &gui->win[WIN_LOCAL], dpy_fmt,
795 env->loc_dpy.w, env->loc_dpy.h,
796 3*BORDER+env->rem_dpy.w + kp_w, BORDER))
799 /* display the skin, but do not free it as we need it later to
800 * restore text areas and maybe sliders too.
803 struct SDL_Rect *dest = &gui->win[WIN_KEYPAD].rect;
804 dest->x = 2*BORDER + env->rem_dpy.w;
806 dest->w = gui->keypad->w;
807 dest->h = gui->keypad->h;
808 SDL_BlitSurface(gui->keypad, NULL, gui->screen, dest);
809 SDL_UpdateRects(gui->screen, 1, dest);
814 /* free resources in case of errors */
815 env->gui = cleanup_sdl(gui);
819 * Functions to determine if a point is within a region. Return 1 if success.
820 * First rotate the point, with
821 * x' = (x - x0) * cos A + (y - y0) * sin A
822 * y' = -(x - x0) * sin A + (y - y0) * cos A
823 * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
824 * l = sqrt( (x1-x0)^2 + (y1-y0)^2
825 * Then determine inclusion by simple comparisons i.e.:
826 * rectangle: x >= 0 && x < l && y >= 0 && y < h
827 * ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
829 static int kp_match_area(const struct keypad_entry *e, int x, int y)
831 double xp, dx = (e->x1 - e->x0);
832 double yp, dy = (e->y1 - e->y0);
833 double l = sqrt(dx*dx + dy*dy);
836 if (l > 1) { /* large enough */
837 xp = ((x - e->x0)*dx + (y - e->y0)*dy)/l;
838 yp = (-(x - e->x0)*dy + (y - e->y0)*dx)/l;
839 if (e->type == KP_RECT) {
840 ret = (xp >= 0 && xp < l && yp >=0 && yp < l);
841 } else if (e->type == KP_CIRCLE) {
842 dx = xp*xp/(l*l) + yp*yp/(e->h*e->h);
847 ast_log(LOG_WARNING, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
848 ret, e->c, x, y, e->type, e->x0, e->y0, e->x1, e->y1, e->h);
853 struct _s_k { const char *s; int k; };
854 static struct _s_k gui_key_map[] = {
855 {"PICK_UP", KEY_PICK_UP },
856 {"PICKUP", KEY_PICK_UP },
857 {"HANG_UP", KEY_HANG_UP },
858 {"HANGUP", KEY_HANG_UP },
860 {"AUTOANSWER", KEY_AUTOANSWER },
861 {"SENDVIDEO", KEY_SENDVIDEO },
862 {"LOCALVIDEO", KEY_LOCALVIDEO },
863 {"REMOTEVIDEO", KEY_REMOTEVIDEO },
864 {"WRITEMESSAGE", KEY_WRITEMESSAGE },
865 {"GUI_CLOSE", KEY_GUI_CLOSE },
868 /*! \brief read a keypad entry line in the format
870 * token circle xc yc diameter
871 * token circle xc yc x1 y1 h # ellipse, main diameter and height
872 * token rect x0 y0 x1 y1 h # rectangle with main side and eight
873 * token is the token to be returned, either a character or a symbol
875 * Return 1 on success, 0 on error.
877 static int keypad_cfg_read(struct gui_info *gui, const char *val)
879 struct keypad_entry e;
883 if (gui == NULL || val == NULL)
886 bzero(&e, sizeof(e));
887 i = sscanf(val, "%14s %14s %d %d %d %d %d",
888 s1, s2, &e.x0, &e.y0, &e.x1, &e.y1, &e.h);
893 case 1: /* only "reset" is allowed */
894 if (strcasecmp(s1, "reset")) /* invalid */
900 case 5: /* token circle xc yc diameter */
901 if (strcasecmp(s2, "circle")) /* invalid */
904 e.y1 = e.y0; /* map radius in x1 y1 */
905 e.x1 = e.x0 + e.h; /* map radius in x1 y1 */
906 e.x0 = e.x0 - e.h; /* map radius in x1 y1 */
909 case 7: /* token circle|rect x0 y0 x1 y1 h */
910 if (e.x1 < e.x0 || e.h <= 0) {
911 ast_log(LOG_WARNING, "error in coordinates\n");
915 if (!strcasecmp(s2, "circle")) {
916 /* for a circle we specify the diameter but store center and radii */
918 e.x0 = (e.x1 + e.x0) / 2;
919 e.y0 = (e.y1 + e.y0) / 2;
921 } else if (!strcasecmp(s2, "rect")) {
927 // ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
930 /* map the string into token to be returned */
932 if (i > 0 || s1[1] == '\0') /* numbers or single characters */
933 e.c = (i > 9) ? i : s1[0];
936 for (p = gui_key_map; p->s; p++) {
937 if (!strcasecmp(p->s, s1)) {
944 ast_log(LOG_WARNING, "missing token\n");
947 if (gui->kp_size == 0) {
948 gui->kp = ast_calloc(10, sizeof(e));
949 if (gui->kp == NULL) {
950 ast_log(LOG_WARNING, "cannot allocate kp");
955 if (gui->kp_size == gui->kp_used) { /* must allocate */
956 struct keypad_entry *a = ast_realloc(gui->kp, sizeof(e)*(gui->kp_size+10));
958 ast_log(LOG_WARNING, "cannot reallocate kp");
964 if (gui->kp_size == gui->kp_used)
966 gui->kp[gui->kp_used++] = e;
967 // ast_log(LOG_WARNING, "now %d regions\n", gui->kp_used);
970 #endif /* HAVE_SDL */