2 * GUI for console video.
3 * The routines here are in charge of loading the keypad and handling events.
8 #include "console_video.h"
9 #include "asterisk/lock.h"
10 #include "asterisk/frame.h"
11 #include "asterisk/utils.h" /* ast_calloc and ast_realloc */
12 #include <math.h> /* sqrt */
14 enum kp_type { KP_NONE, KP_RECT, KP_CIRCLE };
16 int c; /* corresponding character */
17 int x0, y0, x1, y1, h; /* arguments */
21 #define GUI_BUFFER_LEN 256 /* buffer lenght used for input buffers */
23 struct keypad_entry; /* defined in console_gui.c */
25 /*! \brief info related to the gui: button status, mouse coords, etc. */
27 char inbuf[GUI_BUFFER_LEN]; /* buffer for to-dial buffer */
28 int inbuf_pos; /* next free position in inbuf */
29 char msgbuf[GUI_BUFFER_LEN]; /* buffer for text-message buffer */
30 int msgbuf_pos; /* next free position in msgbuf */
31 int text_mode; /* switch to-dial and text-message mode */
32 int drag_mode; /* switch phone and drag-source mode */
33 int x_drag; /* x coordinate where the drag starts */
34 int y_drag; /* y coordinate where the drag starts */
36 TTF_Font *font; /* font to be used */
38 int outfd; /* fd for output */
39 SDL_Surface *keypad; /* the pixmap for the keypad */
41 struct keypad_entry *kp;
44 static void cleanup_sdl(struct video_desc *env)
47 struct gui_info *gui = env->gui;
51 /* unload font file */
53 TTF_CloseFont(gui->font);
57 /* uninitialize SDL_ttf library */
62 SDL_FreeSurface(gui->keypad);
64 /* XXX free the keys entries */
69 /* uninitialize the SDL environment */
70 for (i = 0; i < WIN_MAX; i++) {
72 SDL_FreeYUVOverlay(env->win[i].bmp);
75 env->screen = NULL; /* XXX check reference */
76 bzero(env->win, sizeof(env->win));
78 ast_mutex_destroy(&(env->in.dec_in_lock));
82 * Display video frames (from local or remote stream) using the SDL library.
83 * - Set the video mode to use the resolution specified by the codec context
84 * - Create a YUV Overlay to copy the frame into it;
85 * - After the frame is copied into the overlay, display it
87 * The size is taken from the configuration.
89 * 'out' is 0 for remote video, 1 for the local video
91 static void show_frame(struct video_desc *env, int out)
93 AVPicture *p_in, p_out;
94 struct fbuf_t *b_in, *b_out;
100 if (out == WIN_LOCAL) { /* webcam/x11 to sdl */
101 b_in = &env->out.enc_in;
102 b_out = &env->out.loc_dpy;
105 /* copy input format from the decoding context */
106 AVCodecContext *c = env->in.dec_ctx;
107 b_in = &env->in.dec_out;
108 b_in->pix_fmt = c->pix_fmt;
112 b_out = &env->in.rem_dpy;
113 p_in = (AVPicture *)env->in.d_frame;
115 bmp = env->win[out].bmp;
116 SDL_LockYUVOverlay(bmp);
117 /* output picture info - this is sdl, YUV420P */
118 bzero(&p_out, sizeof(p_out));
119 p_out.data[0] = bmp->pixels[0];
120 p_out.data[1] = bmp->pixels[1];
121 p_out.data[2] = bmp->pixels[2];
122 p_out.linesize[0] = bmp->pitches[0];
123 p_out.linesize[1] = bmp->pitches[1];
124 p_out.linesize[2] = bmp->pitches[2];
126 my_scale(b_in, p_in, b_out, &p_out);
128 /* lock to protect access to Xlib by different threads. */
129 SDL_DisplayYUVOverlay(bmp, &env->win[out].rect);
130 SDL_UnlockYUVOverlay(bmp);
134 * GUI layout, structure and management
137 For the GUI we use SDL to create a large surface (env->screen)
138 containing tree sections: remote video on the left, local video
139 on the right, and the keypad with all controls and text windows
141 The central section is built using two images: one is the skin,
142 the other one is a mask where the sensitive areas of the skin
143 are colored in different grayscale levels according to their
144 functions. The mapping between colors and function is defined
145 in the 'enum pixel_value' below.
147 Mouse and keyboard events are detected on the whole surface, and
148 handled differently according to their location, as follows:
150 - drag on the local video window are used to move the captured
151 area (in the case of X11 grabber) or the picture-in-picture
152 location (in case of camera included on the X11 grab).
153 - click on the keypad are mapped to the corresponding key;
154 - drag on some keypad areas (sliders etc.) are mapped to the
155 corresponding functions;
156 - keystrokes are used as keypad functions, or as text input
157 if we are in text-input mode.
159 To manage these behavior we use two status variables,
160 that defines if keyboard events should be redirect to dialing functions
161 or to write message functions, and if mouse events should be used
162 to implement keypad functionalities or to drag the capture device.
164 Configuration options control the appeareance of the gui:
166 keypad = /tmp/phone.jpg ; the keypad on the screen
167 keypad_font = /tmp/font.ttf ; the font to use for output
172 /* enumerate for the pixel value. 0..127 correspond to ascii chars */
174 /* answer/close functions */
178 /* other functions */
180 KEY_AUTOANSWER = 131,
182 KEY_LOCALVIDEO = 133,
183 KEY_REMOTEVIDEO = 134,
184 KEY_WRITEMESSAGE = 135,
185 KEY_GUI_CLOSE = 136, /* close gui */
187 /* other areas within the keypad */
188 KEY_DIGIT_BACKGROUND = 255,
190 /* areas outside the keypad - simulated */
191 KEY_OUT_OF_KEYPAD = 251,
197 * Handlers for the various keypad functions
200 /*! \brief append a character, or reset if '\0' */
201 static void append_char(char *str, int *str_pos, const char c)
206 else if (i < GUI_BUFFER_LEN - 1)
209 i = GUI_BUFFER_LEN - 1; /* unnecessary, i think */
214 /* accumulate digits, possibly call dial if in connected mode */
215 static void keypad_digit(struct video_desc *env, int digit)
217 if (env->owner) { /* we have a call, send the digit */
218 struct ast_frame f = { AST_FRAME_DTMF, 0 };
221 ast_queue_frame(env->owner, &f);
222 } else { /* no call, accumulate digits */
223 append_char(env->gui->inbuf, &env->gui->inbuf_pos, digit);
227 /* this is a wrapper for actions that are available through the cli */
228 /* TODO append arg to command and send the resulting string as cli command */
229 static void keypad_send_command(struct video_desc *env, char *command)
231 ast_log(LOG_WARNING, "keypad_send_command(%s) called\n", command);
232 ast_cli_command(env->gui->outfd, command);
236 /* function used to toggle on/off the status of some variables */
237 static char *keypad_toggle(struct video_desc *env, int index)
239 ast_log(LOG_WARNING, "keypad_toggle(%i) called\n", index);
243 env->out.sendvideo = !env->out.sendvideo;
247 struct chan_oss_pvt *o = find_desc(oss_active);
251 case KEY_AUTOANSWER: {
252 struct chan_oss_pvt *o = find_desc(oss_active);
253 o->autoanswer = !o->autoanswer;
261 char *console_do_answer(int fd);
263 * Function called when the pick up button is pressed
264 * perform actions according the channel status:
266 * - if no one is calling us and no digits was pressed,
267 * the operation have no effects,
268 * - if someone is calling us we answer to the call.
269 * - if we have no call in progress and we pressed some
270 * digit, send the digit to the console.
272 static void keypad_pick_up(struct video_desc *env)
274 struct gui_info *gui = env->gui;
276 ast_log(LOG_WARNING, "keypad_pick_up called\n");
278 if (env->owner) { /* someone is calling us, just answer */
279 console_do_answer(-1);
280 } else if (gui->inbuf_pos) { /* we have someone to call */
281 ast_cli_command(gui->outfd, gui->inbuf);
284 append_char(gui->inbuf, &gui->inbuf_pos, '\0'); /* clear buffer */
287 #if 0 /* still unused */
289 * As an alternative to SDL_TTF, we can simply load the font from
290 * an image and blit characters on the background of the GUI.
292 * To generate a font we can use the 'fly' command with the
293 * following script (3 lines with 32 chars each)
298 string 255,255,255, 0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
299 string 255,255,255, 0,20,giant,@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
300 string 255,255,255, 0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
305 /* Print given text on the gui */
306 static int gui_output(struct video_desc *env, const char *text)
309 return 1; /* error, not supported */
311 int x = 30, y = 20; /* XXX change */
312 SDL_Surface *output = NULL;
313 SDL_Color color = {0, 0, 0}; /* text color */
314 SDL_Rect dest = {env->win[WIN_KEYPAD].rect.x + x, y};
315 struct gui_info *gui = env->gui;
317 /* clean surface each rewrite */
318 SDL_BlitSurface(gui->keypad, NULL, env->screen, &env->win[WIN_KEYPAD].rect);
320 output = TTF_RenderText_Solid(gui->font, text, color);
321 if (output == NULL) {
322 ast_log(LOG_WARNING, "Cannot render text on gui - %s\n", TTF_GetError());
326 SDL_BlitSurface(output, NULL, env->screen, &dest);
328 SDL_UpdateRects(gui->keypad, 1, &env->win[WIN_KEYPAD].rect);
329 SDL_FreeSurface(output);
330 return 0; /* success */
335 static int video_geom(struct fbuf_t *b, const char *s);
336 static void sdl_setup(struct video_desc *env);
337 static int kp_match_area(const struct keypad_entry *e, int x, int y);
340 * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
341 * index value and calling the right callback.
343 * x, y are referred to the upper left corner of the main SDL window.
345 static void handle_button_event(struct video_desc *env, SDL_MouseButtonEvent button)
347 uint8_t index = KEY_OUT_OF_KEYPAD; /* the key or region of the display we clicked on */
350 ast_log(LOG_WARNING, "event %d %d have %d/%d regions at %p\n",
351 button.x, button.y, env->gui->kp_used, env->gui->kp_size, env->gui->kp);
353 /* for each click we come back in normal mode */
354 env->gui->text_mode = 0;
356 /* define keypad boundary */
357 if (button.x < env->in.rem_dpy.w)
358 index = KEY_REM_DPY; /* click on remote video */
359 else if (button.x > env->in.rem_dpy.w + env->out.keypad_dpy.w)
360 index = KEY_LOC_DPY; /* click on local video */
361 else if (button.y > env->out.keypad_dpy.h)
362 index = KEY_OUT_OF_KEYPAD; /* click outside the keypad */
363 else if (env->gui->kp) {
365 for (i = 0; i < env->gui->kp_used; i++) {
366 if (kp_match_area(&env->gui->kp[i], button.x - env->in.rem_dpy.w, button.y)) {
367 index = env->gui->kp[i].c;
373 /* exec the function */
374 if (index < 128) { /* surely clicked on the keypad, don't care which key */
375 keypad_digit(env, index);
379 /* answer/close function */
384 keypad_send_command(env, "console hangup");
387 /* other functions */
391 keypad_toggle(env, index);
396 case KEY_REMOTEVIDEO:
398 case KEY_WRITEMESSAGE:
399 /* goes in text-mode */
400 env->gui->text_mode = 1;
404 /* press outside the keypad. right increases size, center decreases, left drags */
407 if (button.button == SDL_BUTTON_LEFT) {
408 if (index == KEY_LOC_DPY) {
409 /* store points where the drag start
410 * and switch in drag mode */
411 env->gui->x_drag = button.x;
412 env->gui->y_drag = button.y;
413 env->gui->drag_mode = 1;
418 struct fbuf_t *fb = index == KEY_LOC_DPY ? &env->out.loc_dpy : &env->in.rem_dpy;
419 sprintf(buf, "%c%dx%d", button.button == SDL_BUTTON_RIGHT ? '>' : '<',
425 case KEY_OUT_OF_KEYPAD:
431 case KEY_DIGIT_BACKGROUND:
434 ast_log(LOG_WARNING, "function not yet defined %i\n", index);
439 * Handle SDL_KEYDOWN type event, put the key pressed
440 * in the dial buffer or in the text-message buffer,
441 * depending on the text_mode variable value.
443 * key is the SDLKey structure corresponding to the key pressed.
445 static void handle_keyboard_input(struct video_desc *env, SDLKey key)
447 if (env->gui->text_mode) {
448 /* append in the text-message buffer */
449 if (key == SDLK_RETURN) {
450 /* send the text message and return in normal mode */
451 env->gui->text_mode = 0;
452 keypad_send_command(env, "send text");
454 /* accumulate the key in the message buffer */
455 append_char(env->gui->msgbuf, &env->gui->msgbuf_pos, key);
459 /* append in the dial buffer */
460 append_char(env->gui->inbuf, &env->gui->inbuf_pos, key);
467 * Check if the grab point is inside the X screen.
469 * x represent the new grab value
470 * limit represent the upper value to use
472 static int boundary_checks(int x, int limit)
474 return (x <= 0) ? 0 : (x > limit ? limit : x);
477 /* implement superlinear acceleration on the movement */
478 static int move_accel(int delta)
480 int d1 = delta*delta / 100;
481 return (delta > 0) ? delta + d1 : delta - d1;
485 * Move the source of the captured video.
487 * x_final_drag and y_final_drag are the coordinates where the drag ends,
488 * start coordinares are in the gui_info structure.
490 static void move_capture_source(struct video_desc *env, int x_final_drag, int y_final_drag)
492 int new_x, new_y; /* new coordinates for grabbing local video */
493 int x = env->out.loc_src.x; /* old value */
494 int y = env->out.loc_src.y; /* old value */
496 /* move the origin */
497 #define POLARITY -1 /* +1 or -1 depending on the desired direction */
498 new_x = x + POLARITY*move_accel(x_final_drag - env->gui->x_drag) * 3;
499 new_y = y + POLARITY*move_accel(y_final_drag - env->gui->y_drag) * 3;
501 env->gui->x_drag = x_final_drag; /* update origin */
502 env->gui->y_drag = y_final_drag;
504 /* check boundary and let the source to grab from the new points */
505 env->out.loc_src.x = boundary_checks(new_x, env->out.screen_width - env->out.loc_src.w);
506 env->out.loc_src.y = boundary_checks(new_y, env->out.screen_height - env->out.loc_src.h);
511 * I am seeing some kind of deadlock or stall around
512 * SDL_PumpEvents() while moving the window on a remote X server
513 * (both xfree-4.4.0 and xorg 7.2)
514 * and windowmaker. It is unclear what causes it.
517 /* grab a bunch of events */
518 static void eventhandler(struct video_desc *env)
522 SDL_Event ev[N_EVENTS];
524 #define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
525 while ( (n = SDL_PeepEvents(ev, N_EVENTS, SDL_GETEVENT, SDL_ALLEVENTS)) > 0) {
526 for (i = 0; i < n; i++) {
528 ast_log(LOG_WARNING, "------ event %d at %d %d\n",
529 ev[i].type, ev[i].button.x, ev[i].button.y);
531 switch (ev[i].type) {
533 handle_keyboard_input(env, ev[i].key.keysym.sym);
535 case SDL_MOUSEMOTION:
536 if (env->gui->drag_mode != 0)
537 move_capture_source(env, ev[i].motion.x, ev[i].motion.y);
539 case SDL_MOUSEBUTTONDOWN:
540 handle_button_event(env, ev[i].button);
542 case SDL_MOUSEBUTTONUP:
543 if (env->gui->drag_mode != 0) {
544 move_capture_source(env, ev[i].button.x, ev[i].button.y);
545 env->gui->drag_mode = 0;
553 struct timeval b, a = ast_tvnow();
555 //SDL_Lock_EventThread();
558 i = ast_tvdiff_ms(b, a);
560 fprintf(stderr, "-------- SDL_PumpEvents took %dms\n", i);
561 //SDL_Unlock_EventThread();
565 static SDL_Surface *get_keypad(const char *file)
569 #ifdef HAVE_SDL_IMAGE
570 temp = IMG_Load(file);
572 temp = SDL_LoadBMP(file);
575 fprintf(stderr, "Unable to load image %s: %s\n",
576 file, SDL_GetError());
580 /* TODO: consistency checks, check for bpp, widht and height */
581 /* Init the mask image used to grab the action. */
582 static struct gui_info *gui_init(struct video_desc *env)
584 struct gui_info *gui = ast_calloc(1, sizeof(*gui));
588 /* initialize keypad status */
592 /* initialize grab coordinates */
593 env->out.loc_src.x = 0;
594 env->out.loc_src.y = 0;
596 /* initialize keyboard buffer */
597 append_char(gui->inbuf, &gui->inbuf_pos, '\0');
598 append_char(gui->msgbuf, &gui->msgbuf_pos, '\0');
601 /* Initialize SDL_ttf library and load font */
602 if (TTF_Init() == -1) {
603 ast_log(LOG_WARNING, "Unable to init SDL_ttf, no output available\n");
607 #define GUI_FONTSIZE 28
608 gui->font = TTF_OpenFont( env->keypad_font, GUI_FONTSIZE);
610 ast_log(LOG_WARNING, "Unable to load font %s, no output available\n", env->keypad_font);
613 ast_log(LOG_WARNING, "Loaded font %s\n", env->keypad_font);
616 gui->outfd = open ("/dev/null", O_WRONLY); /* discard output, temporary */
617 if (gui->outfd < 0) {
618 ast_log(LOG_WARNING, "Unable output fd\n");
628 /* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
629 static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
630 int w, int h, int x, int y)
632 win->bmp = SDL_CreateYUVOverlay(w, h, fmt, screen);
633 if (win->bmp == NULL)
634 return -1; /* error */
642 static int keypad_cfg_read(struct gui_info *gui, const char *val);
644 static void keypad_setup(struct video_desc *env)
650 if (env->gui->keypad)
652 env->gui->keypad = get_keypad(env->keypad_file);
653 if (!env->gui->keypad)
656 env->out.keypad_dpy.w = env->gui->keypad->w;
657 env->out.keypad_dpy.h = env->gui->keypad->h;
659 * If the keypad image has a comment field, try to read
660 * the button location from there. The block must be
661 * keypad_entry = token shape x0 y0 x1 y1 h
663 * (basically, lines have the same format as config file entries.
664 * same as the keypad_entry.
665 * You can add it to a jpeg file using wrjpgcom
667 do { /* only once, in fact */
668 const char region[] = "region";
669 int reg_len = strlen(region);
670 const unsigned char *s, *e;
672 fd = open(env->keypad_file, O_RDONLY);
674 ast_log(LOG_WARNING, "fail to open %s\n", env->keypad_file);
677 l = lseek(fd, 0, SEEK_END);
679 ast_log(LOG_WARNING, "fail to lseek %s\n", env->keypad_file);
682 p = mmap(NULL, l, PROT_READ, 0, fd, 0);
684 ast_log(LOG_WARNING, "fail to mmap %s size %ld\n", env->keypad_file, (long)l);
687 e = (const unsigned char *)p + l;
688 for (s = p; s < e - 20 ; s++) {
689 if (!memcmp(s, region, reg_len)) { /* keyword found */
690 /* reset previous entries */
691 keypad_cfg_read(env->gui, "reset");
695 for ( ;s < e - 20; s++) {
697 const unsigned char *s1;
698 if (index(" \t\r\n", *s)) /* ignore blanks */
700 if (*s > 127) /* likely end of comment */
702 if (memcmp(s, region, reg_len)) /* keyword not found */
705 l = MIN(sizeof(buf), e - s);
706 ast_copy_string(buf, s, l);
707 s1 = ast_skip_blanks(buf); /* between token and '=' */
708 if (*s1++ != '=') /* missing separator */
710 if (*s1 == '>') /* skip => */
712 keypad_cfg_read(env->gui, ast_skip_blanks(s1));
713 /* now wait for a newline */
715 while (s1 < e - 20 && !index("\r\n", *s1) && *s1 < 128)
726 /* [re]set the main sdl window, useful in case of resize */
727 static void sdl_setup(struct video_desc *env)
729 int dpy_fmt = SDL_IYUV_OVERLAY; /* YV12 causes flicker in SDL */
730 int depth, maxw, maxh;
731 const SDL_VideoInfo *info = SDL_GetVideoInfo();
733 /* We want at least 16bpp to support YUV overlays.
734 * E.g with SDL_VIDEODRIVER = aalib the default is 8
736 depth = info->vfmt->BitsPerPixel;
740 * initialize the SDL environment. We have one large window
741 * with local and remote video, and a keypad.
742 * At the moment we arrange them statically, as follows:
743 * - on the left, the remote video;
744 * - on the center, the keypad
745 * - on the right, the local video
746 * We need to read in the skin for the keypad before creating the main
747 * SDL window, because the size is only known here.
750 env->gui = gui_init(env);
751 ast_log(LOG_WARNING, "gui_init returned %p\n", env->gui);
754 ast_log(LOG_WARNING, "keypad_setup returned %p %d\n",
755 env->gui->keypad, env->gui->kp_used);
757 #define BORDER 5 /* border around our windows */
758 maxw = env->in.rem_dpy.w + env->out.loc_dpy.w + env->out.keypad_dpy.w;
759 maxh = MAX( MAX(env->in.rem_dpy.h, env->out.loc_dpy.h), env->out.keypad_dpy.h);
762 env->screen = SDL_SetVideoMode(maxw, maxh, depth, 0);
764 ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
768 SDL_WM_SetCaption("Asterisk console Video Output", NULL);
769 if (set_win(env->screen, &env->win[WIN_REMOTE], dpy_fmt,
770 env->in.rem_dpy.w, env->in.rem_dpy.h, BORDER, BORDER))
772 if (set_win(env->screen, &env->win[WIN_LOCAL], dpy_fmt,
773 env->out.loc_dpy.w, env->out.loc_dpy.h,
774 3*BORDER+env->in.rem_dpy.w + env->out.keypad_dpy.w, BORDER))
777 /* display the skin, but do not free it as we need it later to
778 * restore text areas and maybe sliders too.
780 if (env->gui && env->gui->keypad) {
781 struct SDL_Rect *dest = &env->win[WIN_KEYPAD].rect;
782 dest->x = 2*BORDER + env->in.rem_dpy.w;
784 dest->w = env->gui->keypad->w;
785 dest->h = env->gui->keypad->h;
786 SDL_BlitSurface(env->gui->keypad, NULL, env->screen, dest);
787 SDL_UpdateRects(env->screen, 1, dest);
789 env->in.dec_in_cur = &env->in.dec_in[0];
790 env->in.dec_in_dpy = NULL; /* nothing to display */
794 if (env->sdl_ok == 0) /* free resources in case of errors */
799 * Functions to determine if a point is within a region. Return 1 if success.
800 * First rotate the point, with
801 * x' = (x - x0) * cos A + (y - y0) * sin A
802 * y' = -(x - x0) * sin A + (y - y0) * cos A
803 * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
804 * l = sqrt( (x1-x0)^2 + (y1-y0)^2
805 * Then determine inclusion by simple comparisons i.e.:
806 * rectangle: x >= 0 && x < l && y >= 0 && y < h
807 * ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
809 static int kp_match_area(const struct keypad_entry *e, int x, int y)
811 double xp, dx = (e->x1 - e->x0);
812 double yp, dy = (e->y1 - e->y0);
813 double l = sqrt(dx*dx + dy*dy);
816 if (l > 1) { /* large enough */
817 xp = ((x - e->x0)*dx + (y - e->y0)*dy)/l;
818 yp = (-(x - e->x0)*dy + (y - e->y0)*dx)/l;
819 if (e->type == KP_RECT) {
820 ret = (xp >= 0 && xp < l && yp >=0 && yp < l);
821 } else if (e->type == KP_CIRCLE) {
822 dx = xp*xp/(l*l) + yp*yp/(e->h*e->h);
827 ast_log(LOG_WARNING, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
828 ret, e->c, x, y, e->type, e->x0, e->y0, e->x1, e->y1, e->h);
833 struct _s_k { const char *s; int k; };
834 static struct _s_k gui_key_map[] = {
835 {"PICK_UP", KEY_PICK_UP },
836 {"PICKUP", KEY_PICK_UP },
837 {"HANG_UP", KEY_HANG_UP },
838 {"HANGUP", KEY_HANG_UP },
840 {"AUTOANSWER", KEY_AUTOANSWER },
841 {"SENDVIDEO", KEY_SENDVIDEO },
842 {"LOCALVIDEO", KEY_LOCALVIDEO },
843 {"REMOTEVIDEO", KEY_REMOTEVIDEO },
844 {"WRITEMESSAGE", KEY_WRITEMESSAGE },
845 {"GUI_CLOSE", KEY_GUI_CLOSE },
848 /*! \brief read a keypad entry line in the format
850 * token circle xc yc diameter
851 * token circle xc yc x1 y1 h # ellipse, main diameter and height
852 * token rect x0 y0 x1 y1 h # rectangle with main side and eight
853 * token is the token to be returned, either a character or a symbol
855 * Return 1 on success, 0 on error.
857 static int keypad_cfg_read(struct gui_info *gui, const char *val)
859 struct keypad_entry e;
863 if (gui == NULL || val == NULL)
866 bzero(&e, sizeof(e));
867 i = sscanf(val, "%14s %14s %d %d %d %d %d",
868 s1, s2, &e.x0, &e.y0, &e.x1, &e.y1, &e.h);
873 case 1: /* only "reset" is allowed */
874 if (strcasecmp(s1, "reset")) /* invalid */
880 case 5: /* token circle xc yc diameter */
881 if (strcasecmp(s2, "circle")) /* invalid */
884 e.y1 = e.y0; /* map radius in x1 y1 */
885 e.x1 = e.x0 + e.h; /* map radius in x1 y1 */
886 e.x0 = e.x0 - e.h; /* map radius in x1 y1 */
889 case 7: /* token circle|rect x0 y0 x1 y1 h */
890 if (e.x1 < e.x0 || e.h <= 0) {
891 ast_log(LOG_WARNING, "error in coordinates\n");
895 if (!strcasecmp(s2, "circle")) {
896 /* for a circle we specify the diameter but store center and radii */
898 e.x0 = (e.x1 + e.x0) / 2;
899 e.y0 = (e.y1 + e.y0) / 2;
901 } else if (!strcasecmp(s2, "rect")) {
907 // ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
910 /* map the string into token to be returned */
912 if (i > 0 || s1[1] == '\0') /* numbers or single characters */
913 e.c = (i > 9) ? i : s1[0];
916 for (p = gui_key_map; p->s; p++) {
917 if (!strcasecmp(p->s, s1)) {
924 ast_log(LOG_WARNING, "missing token\n");
927 if (gui->kp_size == 0) {
928 gui->kp = ast_calloc(10, sizeof(e));
929 if (gui->kp == NULL) {
930 ast_log(LOG_WARNING, "cannot allocate kp");
935 if (gui->kp_size == gui->kp_used) { /* must allocate */
936 struct keypad_entry *a = ast_realloc(gui->kp, sizeof(e)*(gui->kp_size+10));
938 ast_log(LOG_WARNING, "cannot reallocate kp");
944 if (gui->kp_size == gui->kp_used)
946 gui->kp[gui->kp_used++] = e;
947 ast_log(LOG_WARNING, "now %d regions\n", gui->kp_used);