Thu Jul 9 13:40:33 2009

Asterisk developer's documentation


console_gui.c

Go to the documentation of this file.
00001 /*
00002  * GUI for console video.
00003  * The routines here are in charge of loading the keypad and handling events.
00004  * $Revision: 147809 $
00005  */
00006 
00007 /*
00008  * GUI layout, structure and management
00009  
00010 For the GUI we use SDL to create a large surface (gui->screen)
00011 containing tree sections: remote video on the left, local video
00012 on the right, and the keypad with all controls and text windows
00013 in the center.
00014 The central section is built using an image for the skin, fonts and
00015 other GUI elements.  Comments embedded in the image to indicate to
00016 what function each area is mapped to.
00017 
00018 Mouse and keyboard events are detected on the whole surface, and
00019 handled differently according to their location:
00020 
00021 - drag on the local video window are used to move the captured
00022   area (in the case of X11 grabber) or the picture-in-picture
00023   location (in case of camera included on the X11 grab).
00024 - click on the keypad are mapped to the corresponding key;
00025 - drag on some keypad areas (sliders etc.) are mapped to the
00026   corresponding functions;
00027 - keystrokes are used as keypad functions, or as text input
00028   if we are in text-input mode.
00029 
00030 Configuration options control the appeareance of the gui:
00031 
00032     keypad = /tmp/phone.jpg   ; the skin
00033     keypad_font = /tmp/font.ttf  ; the font to use for output (XXX deprecated)
00034 
00035  *
00036  */
00037 
00038 #include "asterisk.h"
00039 #include "console_video.h"
00040 #include "asterisk/lock.h"
00041 #include "asterisk/frame.h"
00042 #include "asterisk/utils.h"   /* ast_calloc and ast_realloc */
00043 #include <math.h>    /* sqrt */
00044 
00045 /* We use 3 'windows' in the GUI */
00046 enum { WIN_LOCAL, WIN_REMOTE, WIN_KEYPAD, WIN_MAX };
00047 
00048 #ifndef HAVE_SDL  /* stubs if we don't have any sdl */
00049 static void show_frame(struct video_desc *env, int out)  {}
00050 static void sdl_setup(struct video_desc *env)      {}
00051 static struct gui_info *cleanup_sdl(struct gui_info *gui)   { return NULL; }
00052 static void eventhandler(struct video_desc *env, const char *caption)   {}
00053 static int keypad_cfg_read(struct gui_info *gui, const char *val) { return 0; }
00054 
00055 #else /* HAVE_SDL, the real rendering code */
00056 
00057 #include <SDL/SDL.h>
00058 #ifdef HAVE_SDL_IMAGE
00059 #include <SDL/SDL_image.h>      /* for loading images */
00060 #endif
00061 
00062 enum kp_type { KP_NONE, KP_RECT, KP_CIRCLE };
00063 struct keypad_entry {
00064         int c;  /* corresponding character */
00065         int x0, y0, x1, y1, h;  /* arguments */
00066         enum kp_type type;
00067 };
00068 
00069 /* our representation of a displayed window. SDL can only do one main
00070  * window so we map everything within that one
00071  */
00072 struct display_window   {   
00073    SDL_Overlay *bmp;
00074    SDL_Rect rect; /* location of the window */
00075 };
00076 
00077 struct gui_info {
00078    enum kb_output    kb_output;  /* where the keyboard output goes */
00079    struct drag_info  drag;    /* info on the window are we dragging */
00080    /* support for display. */
00081    SDL_Surface             *screen; /* the main window */
00082 
00083    int         outfd;      /* fd for output */
00084    SDL_Surface    *keypad; /* the skin for the keypad */
00085    SDL_Rect    kp_rect; /* portion of the skin to display - default all */
00086    SDL_Surface    *font;      /* font to be used */ 
00087    SDL_Rect    font_rects[96];   /* only printable chars */
00088 
00089    /* each board has two rectangles,
00090     * [0] is the geometry relative to the keypad,
00091     * [1] is the geometry relative to the whole screen
00092     */
00093    SDL_Rect    kp_msg[2];     /* incoming msg, relative to kpad */
00094    struct board      *bd_msg;
00095 
00096    SDL_Rect    kp_edit[2]; /* edit user input */
00097    struct board      *bd_edit;
00098 
00099    SDL_Rect    kp_dialed[2];  /* dialed number */
00100    struct board      *bd_dialed;
00101 
00102    /* variable-size array mapping keypad regions to functions */
00103    int kp_size, kp_used;
00104    struct keypad_entry *kp;
00105 
00106    struct display_window   win[WIN_MAX];
00107 };
00108 
00109 /*! \brief free the resources in struct gui_info and the descriptor itself.
00110  *  Return NULL so we can assign the value back to the descriptor in case.
00111  */
00112 static struct gui_info *cleanup_sdl(struct gui_info *gui)
00113 {
00114    int i;
00115 
00116    if (gui == NULL)
00117       return NULL;
00118 
00119    /* unload font file */ 
00120    if (gui->font) {
00121       SDL_FreeSurface(gui->font);
00122       gui->font = NULL; 
00123    }
00124 
00125    if (gui->outfd > -1)
00126       close(gui->outfd);
00127    if (gui->keypad)
00128       SDL_FreeSurface(gui->keypad);
00129    gui->keypad = NULL;
00130    if (gui->kp)
00131       ast_free(gui->kp);
00132 
00133    /* uninitialize the SDL environment */
00134    for (i = 0; i < WIN_MAX; i++) {
00135       if (gui->win[i].bmp)
00136          SDL_FreeYUVOverlay(gui->win[i].bmp);
00137    }
00138    memset(gui, '\0', sizeof(gui));
00139    ast_free(gui);
00140    SDL_Quit();
00141    return NULL;
00142 }
00143 
00144 /*
00145  * Display video frames (from local or remote stream) using the SDL library.
00146  * - Set the video mode to use the resolution specified by the codec context
00147  * - Create a YUV Overlay to copy the frame into it;
00148  * - After the frame is copied into the overlay, display it
00149  *
00150  * The size is taken from the configuration.
00151  *
00152  * 'out' is 0 for remote video, 1 for the local video
00153  */
00154 static void show_frame(struct video_desc *env, int out)
00155 {
00156    AVPicture *p_in, p_out;
00157    struct fbuf_t *b_in, *b_out;
00158    SDL_Overlay *bmp;
00159    struct gui_info *gui = env->gui;
00160 
00161    if (!gui)
00162       return;
00163 
00164    if (out == WIN_LOCAL) { /* webcam/x11 to sdl */
00165       b_in = &env->enc_in;
00166       b_out = &env->loc_dpy;
00167       p_in = NULL;
00168    } else {
00169       /* copy input format from the decoding context */
00170       AVCodecContext *c;
00171       if (env->in == NULL) /* XXX should not happen - decoder not ready */
00172          return;
00173       c = env->in->dec_ctx;
00174       b_in = &env->in->dec_out;
00175                 b_in->pix_fmt = c->pix_fmt;
00176                 b_in->w = c->width;
00177                 b_in->h = c->height;
00178 
00179       b_out = &env->rem_dpy;
00180       p_in = (AVPicture *)env->in->d_frame;
00181    }
00182    bmp = gui->win[out].bmp;
00183    SDL_LockYUVOverlay(bmp);
00184    /* output picture info - this is sdl, YUV420P */
00185    memset(&p_out, '\0', sizeof(p_out));
00186    p_out.data[0] = bmp->pixels[0];
00187    p_out.data[1] = bmp->pixels[1];
00188    p_out.data[2] = bmp->pixels[2];
00189    p_out.linesize[0] = bmp->pitches[0];
00190    p_out.linesize[1] = bmp->pitches[1];
00191    p_out.linesize[2] = bmp->pitches[2];
00192 
00193    my_scale(b_in, p_in, b_out, &p_out);
00194 
00195    /* lock to protect access to Xlib by different threads. */
00196    SDL_DisplayYUVOverlay(bmp, &gui->win[out].rect);
00197    SDL_UnlockYUVOverlay(bmp);
00198 }
00199 
00200 /*
00201  * Identifiers for regions of the main window.
00202  * Values between 0 and 127 correspond to ASCII characters.
00203  * The corresponding strings to be used in the skin comment section
00204  * are defined in gui_key_map.
00205  */
00206 enum skin_area {
00207    /* answer/close functions */
00208    KEY_PICK_UP = 128,
00209    KEY_HANG_UP = 129,
00210 
00211    KEY_MUTE = 130,
00212    KEY_AUTOANSWER = 131,
00213    KEY_SENDVIDEO = 132,
00214    KEY_LOCALVIDEO = 133,
00215    KEY_REMOTEVIDEO = 134,
00216    KEY_FLASH = 136,
00217 
00218    /* sensitive areas for the various text windows */
00219    KEY_MESSAGEBOARD = 140,
00220    KEY_DIALEDBOARD = 141,
00221    KEY_EDITBOARD = 142,
00222 
00223    KEY_GUI_CLOSE = 199,    /* close gui */
00224    /* regions of the skin - displayed area, fonts, etc.
00225     * XXX NOTE these are not sensitive areas.
00226     */
00227    KEY_KEYPAD = 200,    /* the keypad - default to the whole image */
00228    KEY_FONT = 201,      /* the font. Maybe not really useful */
00229    KEY_MESSAGE = 202,   /* area for incoming messages */
00230    KEY_DIALED = 203, /* area for dialed numbers */
00231    KEY_EDIT = 204,      /* area for editing user input */
00232 
00233    /* areas outside the keypad - simulated */
00234    KEY_OUT_OF_KEYPAD = 241,
00235    KEY_REM_DPY = 242,
00236    KEY_LOC_DPY = 243,
00237    KEY_RESET = 253,     /* the 'reset' keyword */
00238    KEY_NONE = 254,         /* invalid area */
00239    KEY_DIGIT_BACKGROUND = 255,   /* other areas within the keypad */
00240 };
00241 
00242 /*
00243  * Handlers for the various keypad functions
00244  */
00245 
00246 /* accumulate digits, possibly call dial if in connected mode */
00247 static void keypad_digit(struct video_desc *env, int digit)
00248 {  
00249    if (env->owner) {    /* we have a call, send the digit */
00250       struct ast_frame f = { AST_FRAME_DTMF, 0 };
00251 
00252       f.subclass = digit;
00253       ast_queue_frame(env->owner, &f);
00254    } else {    /* no call, accumulate digits */
00255       char buf[2] = { digit, '\0' };
00256       if (env->gui->bd_msg) /* XXX not strictly necessary ... */
00257          print_message(env->gui->bd_msg, buf);
00258    }
00259 }
00260 
00261 /* function used to toggle on/off the status of some variables */
00262 static char *keypad_toggle(struct video_desc *env, int index)
00263 {
00264    ast_log(LOG_WARNING, "keypad_toggle(%i) called\n", index);
00265 
00266    switch (index) {
00267    case KEY_SENDVIDEO:
00268       env->out.sendvideo = !env->out.sendvideo;
00269       break;
00270 #ifdef notyet
00271    case KEY_MUTE: {
00272       struct chan_oss_pvt *o = find_desc(oss_active);
00273       o->mute = !o->mute;
00274       }
00275       break;
00276    case KEY_AUTOANSWER: {
00277       struct chan_oss_pvt *o = find_desc(oss_active);
00278       o->autoanswer = !o->autoanswer;
00279       }
00280       break;
00281 #endif
00282    }
00283    return NULL;
00284 }
00285 
00286 char *console_do_answer(int fd);
00287 /*
00288  * Function called when the pick up button is pressed
00289  * perform actions according the channel status:
00290  *
00291  *  - if no one is calling us and no digits was pressed,
00292  *    the operation have no effects,
00293  *  - if someone is calling us we answer to the call.
00294  *  - if we have no call in progress and we pressed some
00295  *    digit, send the digit to the console.
00296  */
00297 static void keypad_pick_up(struct video_desc *env)
00298 {
00299    struct gui_info *gui = env->gui;
00300 
00301    ast_log(LOG_WARNING, "keypad_pick_up called\n");
00302 
00303    if (env->owner) { /* someone is calling us, just answer */
00304       ast_cli_command(gui->outfd, "console answer");
00305    } else { /* we have someone to call */
00306       char buf[160];
00307       const char *who = ast_skip_blanks(read_message(gui->bd_msg));
00308       buf[sizeof(buf) - 1] = '\0';
00309       snprintf(buf, sizeof(buf), "console dial %s", who);
00310       ast_log(LOG_WARNING, "doing <%s>\n", buf);
00311       print_message(gui->bd_dialed, "\n");
00312       print_message(gui->bd_dialed, who);
00313       reset_board(gui->bd_msg);
00314       ast_cli_command(gui->outfd, buf);
00315    }
00316 }
00317 
00318 #if 0 /* still unused */
00319 /*
00320  * As an alternative to SDL_TTF, we can simply load the font from
00321  * an image and blit characters on the background of the GUI.
00322  *
00323  * To generate a font we can use the 'fly' command with the
00324  * following script (3 lines with 32 chars each)
00325  
00326 size 320,64
00327 name font.png
00328 transparent 0,0,0
00329 string 255,255,255,  0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
00330 string 255,255,255,  0,20,giant,@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
00331 string 255,255,255,  0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
00332 end
00333 
00334  */
00335 
00336 /* Print given text on the gui */
00337 static int gui_output(struct video_desc *env, const char *text)
00338 {
00339    return 1;   /* error, not supported */
00340 }
00341 #endif 
00342 
00343 static int video_geom(struct fbuf_t *b, const char *s);
00344 static void sdl_setup(struct video_desc *env);
00345 static int kp_match_area(const struct keypad_entry *e, int x, int y);
00346 
00347 static void set_drag(struct drag_info *drag, int x, int y, enum drag_window win)
00348 {
00349    drag->x_start = x;
00350    drag->y_start = y;
00351    drag->drag_window = win;
00352 }
00353 
00354 /*
00355  * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
00356  * index value and calling the right callback.
00357  *
00358  * x, y are referred to the upper left corner of the main SDL window.
00359  */
00360 static void handle_mousedown(struct video_desc *env, SDL_MouseButtonEvent button)
00361 {
00362    uint8_t index = KEY_OUT_OF_KEYPAD;  /* the key or region of the display we clicked on */
00363    struct gui_info *gui = env->gui;
00364 
00365 #if 0
00366    ast_log(LOG_WARNING, "event %d %d have %d/%d regions at %p\n",
00367       button.x, button.y, gui->kp_used, gui->kp_size, gui->kp);
00368 #endif
00369    /* for each mousedown we end previous drag */
00370    gui->drag.drag_window = DRAG_NONE;
00371 
00372    /* define keypad boundary */
00373    if (button.x < env->rem_dpy.w)
00374       index = KEY_REM_DPY; /* click on remote video */
00375    else if (button.x > env->rem_dpy.w + gui->keypad->w)
00376       index = KEY_LOC_DPY; /* click on local video */
00377    else if (button.y > gui->keypad->h)
00378       index = KEY_OUT_OF_KEYPAD; /* click outside the keypad */
00379    else if (gui->kp) {
00380       int i;
00381       for (i = 0; i < gui->kp_used; i++) {
00382          if (kp_match_area(&gui->kp[i], button.x - env->rem_dpy.w, button.y)) {
00383             index = gui->kp[i].c;
00384             break;
00385          }
00386       }
00387    }
00388 
00389    /* exec the function */
00390    if (index < 128) {   /* surely clicked on the keypad, don't care which key */
00391       keypad_digit(env, index);
00392       return;
00393    }
00394    switch (index) {
00395    /* answer/close function */
00396    case KEY_PICK_UP:
00397       keypad_pick_up(env);
00398       break;
00399    case KEY_HANG_UP:
00400       ast_cli_command(gui->outfd, "console hangup");
00401       break;
00402 
00403    /* other functions */
00404    case KEY_MUTE:
00405    case KEY_AUTOANSWER:
00406    case KEY_SENDVIDEO:
00407       keypad_toggle(env, index);
00408       break;
00409 
00410    case KEY_LOCALVIDEO:
00411       break;
00412    case KEY_REMOTEVIDEO:
00413       break;
00414 
00415    case KEY_MESSAGEBOARD:
00416       if (button.button == SDL_BUTTON_LEFT)
00417          set_drag(&gui->drag, button.x, button.y, DRAG_MESSAGE);
00418       break;
00419 
00420    /* press outside the keypad. right increases size, center decreases, left drags */
00421    case KEY_LOC_DPY:
00422    case KEY_REM_DPY:
00423       if (button.button == SDL_BUTTON_LEFT) {
00424          if (index == KEY_LOC_DPY)
00425             set_drag(&gui->drag, button.x, button.y, DRAG_LOCAL);
00426          break;
00427       } else {
00428          char buf[128];
00429          struct fbuf_t *fb = index == KEY_LOC_DPY ? &env->loc_dpy : &env->rem_dpy;
00430          sprintf(buf, "%c%dx%d", button.button == SDL_BUTTON_RIGHT ? '>' : '<',
00431             fb->w, fb->h);
00432          video_geom(fb, buf);
00433          sdl_setup(env);
00434       }
00435       break;
00436    case KEY_OUT_OF_KEYPAD:
00437       break;
00438 
00439    case KEY_DIGIT_BACKGROUND:
00440       break;
00441    default:
00442       ast_log(LOG_WARNING, "function not yet defined %i\n", index);
00443    }
00444 }
00445 
00446 /*
00447  * Handle SDL_KEYDOWN type event, put the key pressed
00448  * in the dial buffer or in the text-message buffer,
00449  * depending on the text_mode variable value.
00450  *
00451  * key is the SDLKey structure corresponding to the key pressed.
00452  * Note that SDL returns modifiers (ctrl, shift, alt) as independent
00453  * information so the key itself is not enough and we need to
00454  * use a translation table, below - one line per entry,
00455  * plain, shift, ctrl, ... using the first char as key.
00456  */
00457 static const char *us_kbd_map[] = {
00458    "`~", "1!", "2@", "3#", "4$", "5%", "6^",
00459    "7&", "8*", "9(", "0)", "-_", "=+", "[{",
00460    "]}", "\\|", ";:", "'\"", ",<", ".>", "/?",
00461    "jJ\n",
00462    NULL
00463 };
00464 
00465 static char map_key(SDL_keysym *ks)
00466 {
00467    const char *s, **p = us_kbd_map;
00468    int c = ks->sym;
00469 
00470    if (c == '\r') /* map cr into lf */
00471       c = '\n';
00472    if (c >= SDLK_NUMLOCK && c <= SDLK_COMPOSE)
00473       return 0;   /* only a modifier */
00474    if (ks->mod == 0)
00475       return c;
00476    while ((s = *p) && s[0] != c)
00477       p++;
00478    if (s) { /* see if we have a modifier and a chance to use it */
00479       int l = strlen(s), mod = 0;
00480       if (l > 1)
00481          mod |= (ks->mod & KMOD_SHIFT) ? 1 : 0;
00482       if (l > 2 + mod)
00483          mod |= (ks->mod & KMOD_CTRL) ? 2 : 0;
00484       if (l > 4 + mod)
00485          mod |= (ks->mod & KMOD_ALT) ? 4 : 0;
00486       c = s[mod];
00487    }
00488    if (ks->mod & (KMOD_CAPS|KMOD_SHIFT) && c >= 'a' && c <='z')
00489       c += 'A' - 'a';
00490    return c;
00491 }
00492 
00493 static void handle_keyboard_input(struct video_desc *env, SDL_keysym *ks)
00494 {
00495    char buf[2] = { map_key(ks), '\0' };
00496    struct gui_info *gui = env->gui;
00497    if (buf[0] == 0)  /* modifier ? */
00498       return;
00499    switch (gui->kb_output) {
00500    default:
00501       break;
00502    case KO_INPUT: /* to be completed */
00503       break;
00504    case KO_MESSAGE:
00505       if (gui->bd_msg) {
00506          print_message(gui->bd_msg, buf);
00507          if (buf[0] == '\r' || buf[0] == '\n') {
00508             keypad_pick_up(env);
00509          }
00510       }
00511       break;
00512 
00513    case KO_DIALED: /* to be completed */
00514       break;
00515    }
00516 
00517    return;
00518 }
00519 
00520 static void grabber_move(struct video_out_desc *, int dx, int dy);
00521 
00522 int compute_drag(int *start, int end, int magnifier);
00523 int compute_drag(int *start, int end, int magnifier)
00524 {
00525    int delta = end - *start;
00526 #define POLARITY -1
00527    /* add a small quadratic term */
00528    delta += delta * delta * (delta > 0 ? 1 : -1 )/100;
00529    delta *= POLARITY * magnifier;
00530 #undef POLARITY
00531    *start = end;
00532    return delta;
00533 }
00534 
00535 /*
00536  * I am seeing some kind of deadlock or stall around
00537  * SDL_PumpEvents() while moving the window on a remote X server
00538  * (both xfree-4.4.0 and xorg 7.2)
00539  * and windowmaker. It is unclear what causes it.
00540  */
00541 
00542 /*! \brief refresh the screen, and also grab a bunch of events.
00543  */
00544 static void eventhandler(struct video_desc *env, const char *caption)
00545 {
00546    struct gui_info *gui = env->gui;
00547    struct drag_info *drag;
00548 #define N_EVENTS  32
00549    int i, n;
00550    SDL_Event ev[N_EVENTS];
00551 
00552    if (!gui)
00553       return;
00554    drag = &gui->drag;
00555    if (caption)
00556       SDL_WM_SetCaption(caption, NULL);
00557 
00558 #define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
00559    while ( (n = SDL_PeepEvents(ev, N_EVENTS, SDL_GETEVENT, SDL_ALLEVENTS)) > 0) {
00560       for (i = 0; i < n; i++) {
00561 #if 0
00562          ast_log(LOG_WARNING, "------ event %d at %d %d\n",
00563             ev[i].type,  ev[i].button.x,  ev[i].button.y);
00564 #endif
00565          switch (ev[i].type) {
00566          case SDL_KEYDOWN:
00567             handle_keyboard_input(env, &ev[i].key.keysym);
00568             break;
00569          case SDL_MOUSEMOTION:
00570          case SDL_MOUSEBUTTONUP:
00571             if (drag->drag_window == DRAG_LOCAL) {
00572                /* move the capture source */
00573                int dx = compute_drag(&drag->x_start, ev[i].motion.x, 3);
00574                int dy = compute_drag(&drag->y_start, ev[i].motion.y, 3);
00575                grabber_move(&env->out, dx, dy);
00576             } else if (drag->drag_window == DRAG_MESSAGE) {
00577                /* scroll up/down the window */
00578                int dy = compute_drag(&drag->y_start, ev[i].motion.y, 1);
00579                move_message_board(gui->bd_msg, dy);
00580             }
00581             if (ev[i].type == SDL_MOUSEBUTTONUP)
00582                drag->drag_window = DRAG_NONE;
00583             break;
00584          case SDL_MOUSEBUTTONDOWN:
00585             handle_mousedown(env, ev[i].button);
00586             break;
00587          }
00588       }
00589    }
00590    if (1) {
00591       struct timeval b, a = ast_tvnow();
00592       int i;
00593       //SDL_Lock_EventThread();
00594       SDL_PumpEvents();
00595       b = ast_tvnow();
00596       i = ast_tvdiff_ms(b, a);
00597       if (i > 3)
00598          fprintf(stderr, "-------- SDL_PumpEvents took %dms\n", i);
00599       //SDL_Unlock_EventThread();
00600    }
00601 }
00602 
00603 static SDL_Surface *load_image(const char *file)
00604 {
00605    SDL_Surface *temp;
00606  
00607 #ifdef HAVE_SDL_IMAGE
00608    temp = IMG_Load(file);
00609 #else
00610    temp = SDL_LoadBMP(file);
00611 #endif
00612    if (temp == NULL)
00613       fprintf(stderr, "Unable to load image %s: %s\n",
00614          file, SDL_GetError());
00615    return temp;
00616 }
00617 
00618 static void keypad_setup(struct gui_info *gui, const char *kp_file);
00619 
00620 /* TODO: consistency checks, check for bpp, widht and height */
00621 /* Init the mask image used to grab the action. */
00622 static struct gui_info *gui_init(const char *keypad_file, const char *font)
00623 {
00624    struct gui_info *gui = ast_calloc(1, sizeof(*gui));
00625 
00626    if (gui == NULL)
00627       return NULL;
00628    /* initialize keypad status */
00629    gui->kb_output = KO_MESSAGE;  /* XXX temp */
00630    gui->drag.drag_window = DRAG_NONE;
00631    gui->outfd = -1;
00632 
00633    keypad_setup(gui, keypad_file);
00634    if (gui->keypad == NULL)   /* no keypad, we are done */
00635       return gui;
00636    /* XXX load image */
00637    if (!ast_strlen_zero(font)) {
00638       int i;
00639       SDL_Rect *r;
00640 
00641       gui->font = load_image(font);
00642       if (!gui->font) {
00643          ast_log(LOG_WARNING, "Unable to load font %s, no output available\n", font);
00644          goto error;
00645       }
00646       ast_log(LOG_WARNING, "Loaded font %s\n", font);
00647       /* XXX hardwired constants - 3 rows of 32 chars */
00648       r = gui->font_rects;
00649 #define FONT_H 20
00650 #define FONT_W 9
00651       for (i = 0; i < 96; r++, i++) {
00652                   r->x = (i % 32 ) * FONT_W;
00653                   r->y = (i / 32 ) * FONT_H;
00654                   r->w = FONT_W;
00655                   r->h = FONT_H;
00656       }
00657    }
00658 
00659    gui->outfd = open ("/dev/null", O_WRONLY);   /* discard output, temporary */
00660    if (gui->outfd < 0) {
00661       ast_log(LOG_WARNING, "Unable output fd\n");
00662       goto error;
00663    }
00664    return gui;
00665 
00666 error:
00667    ast_free(gui);
00668    return NULL;
00669 }
00670 
00671 /* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
00672 static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
00673    int w, int h, int x, int y)
00674 {
00675    win->bmp = SDL_CreateYUVOverlay(w, h, fmt, screen);
00676    if (win->bmp == NULL)
00677       return -1;  /* error */
00678    win->rect.x = x;
00679    win->rect.y = y;
00680    win->rect.w = w;
00681    win->rect.h = h;
00682    return 0;
00683 }
00684 
00685 static int keypad_cfg_read(struct gui_info *gui, const char *val);
00686 
00687 static void keypad_setup(struct gui_info *gui, const char *kp_file)
00688 {
00689    FILE *fd;
00690    char buf[1024];
00691    const char region[] = "region";
00692    int reg_len = strlen(region);
00693    int in_comment = 0;
00694 
00695    if (gui->keypad)
00696       return;
00697    gui->keypad = load_image(kp_file);
00698    if (!gui->keypad)
00699       return;
00700    /* now try to read the keymap from the file. */
00701    fd = fopen(kp_file, "r");
00702    if (fd == NULL) {
00703       ast_log(LOG_WARNING, "fail to open %s\n", kp_file);
00704       return;
00705    }
00706    /*
00707     * If the keypad image has a comment field, try to read
00708     * the button location from there. The block must start with
00709     * a comment (or empty) line, and continue with entries like:
00710     * region = token shape x0 y0 x1 y1 h
00711     * ...
00712     * (basically, lines have the same format as config file entries).
00713     * You can add it to a jpeg file using wrjpgcom
00714     */
00715    while (fgets(buf, sizeof(buf), fd)) {
00716       char *s;
00717 
00718       if (!strstr(buf, region)) { /* no keyword yet */
00719          if (!in_comment)  /* still waiting for initial comment block */
00720             continue;
00721          else
00722             break;
00723       }
00724       if (!in_comment) {   /* first keyword, reset previous entries */
00725          keypad_cfg_read(gui, "reset");
00726          in_comment = 1;
00727       }
00728       s = ast_skip_blanks(buf);
00729       ast_trim_blanks(s);
00730       if (memcmp(s, region, reg_len))
00731          break;   /* keyword not found */
00732       s = ast_skip_blanks(s + reg_len); /* space between token and '=' */
00733       if (*s++ != '=')  /* missing separator */
00734          break;
00735       if (*s == '>') /* skip '>' if present */
00736          s++;
00737       keypad_cfg_read(gui, ast_skip_blanks(s));
00738    }
00739    fclose(fd);
00740 }
00741 
00742 struct board *board_setup(SDL_Surface *screen, SDL_Rect *dest,
00743    SDL_Surface *font, SDL_Rect *font_rects);
00744 
00745 /*! \brief initialize the boards we have in the keypad */
00746 static void init_board(struct gui_info *gui, struct board **dst, SDL_Rect *r, int dx, int dy)
00747 {
00748    if (r[0].w == 0 || r[0].h == 0)
00749       return;  /* not available */
00750    r[1] = r[0];   /* copy geometry */
00751    r[1].x += dx;  /* add offset of main window */
00752    r[1].y += dy;
00753    if (*dst == NULL) {  /* initial call */
00754       *dst = board_setup(gui->screen, &r[1], gui->font, gui->font_rects);
00755    } else {
00756       /* call a refresh */
00757    }
00758 }
00759 
00760 /*! \brief [re]set the main sdl window, useful in case of resize.
00761  * We can tell the first from subsequent calls from the value of
00762  * env->gui, which is NULL the first time.
00763  */
00764 static void sdl_setup(struct video_desc *env)
00765 {
00766    int dpy_fmt = SDL_IYUV_OVERLAY;  /* YV12 causes flicker in SDL */
00767    int depth, maxw, maxh;
00768    const SDL_VideoInfo *info;
00769    int kp_w = 0, kp_h = 0; /* keypad width and height */
00770    struct gui_info *gui = env->gui;
00771 
00772    /*
00773     * initialize the SDL environment. We have one large window
00774     * with local and remote video, and a keypad.
00775     * At the moment we arrange them statically, as follows:
00776     * - on the left, the remote video;
00777     * - on the center, the keypad
00778     * - on the right, the local video
00779     * We need to read in the skin for the keypad before creating the main
00780     * SDL window, because the size is only known here.
00781     */
00782 
00783    if (gui == NULL && SDL_Init(SDL_INIT_VIDEO)) {
00784       ast_log(LOG_WARNING, "Could not initialize SDL - %s\n",
00785                         SDL_GetError());
00786                 /* again not fatal, just we won't display anything */
00787       return;
00788    }
00789    info = SDL_GetVideoInfo();
00790    /* We want at least 16bpp to support YUV overlays.
00791     * E.g with SDL_VIDEODRIVER = aalib the default is 8
00792     */
00793    depth = info->vfmt->BitsPerPixel;
00794    if (depth < 16)
00795       depth = 16;
00796    if (!gui)
00797       env->gui = gui = gui_init(env->keypad_file, env->keypad_font);
00798    if (!gui)
00799       goto no_sdl;
00800 
00801    if (gui->keypad) {
00802       if (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) {
00803          kp_w = gui->kp_rect.w;
00804          kp_h = gui->kp_rect.h;
00805       } else {
00806          kp_w = gui->keypad->w;
00807          kp_h = gui->keypad->h;
00808       }
00809    }
00810    /* XXX same for other boards */
00811 #define BORDER 5  /* border around our windows */
00812    maxw = env->rem_dpy.w + env->loc_dpy.w + kp_w;
00813    maxh = MAX( MAX(env->rem_dpy.h, env->loc_dpy.h), kp_h);
00814    maxw += 4 * BORDER;
00815    maxh += 2 * BORDER;
00816    gui->screen = SDL_SetVideoMode(maxw, maxh, depth, 0);
00817    if (!gui->screen) {
00818       ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
00819       goto no_sdl;
00820    }
00821 
00822    SDL_WM_SetCaption("Asterisk console Video Output", NULL);
00823    if (set_win(gui->screen, &gui->win[WIN_REMOTE], dpy_fmt,
00824          env->rem_dpy.w, env->rem_dpy.h, BORDER, BORDER))
00825       goto no_sdl;
00826    if (set_win(gui->screen, &gui->win[WIN_LOCAL], dpy_fmt,
00827          env->loc_dpy.w, env->loc_dpy.h,
00828          3*BORDER+env->rem_dpy.w + kp_w, BORDER))
00829       goto no_sdl;
00830 
00831    /* display the skin, but do not free it as we need it later to
00832     * restore text areas and maybe sliders too.
00833     */
00834    if (gui->keypad) {
00835       struct SDL_Rect *dest = &gui->win[WIN_KEYPAD].rect;
00836       struct SDL_Rect *src = (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) ? & gui->kp_rect : NULL;
00837       /* set the coordinates of the keypad relative to the main screen */
00838       dest->x = 2*BORDER + env->rem_dpy.w;
00839       dest->y = BORDER;
00840       dest->w = kp_w;
00841       dest->h = kp_h;
00842       SDL_BlitSurface(gui->keypad, src, gui->screen, dest);
00843       init_board(gui, &gui->bd_msg, gui->kp_msg, dest->x, dest->y);
00844       init_board(gui, &gui->bd_dialed, gui->kp_dialed, dest->x, dest->y);
00845       SDL_UpdateRects(gui->screen, 1, dest);
00846    }
00847    return;
00848 
00849 no_sdl:
00850    /* free resources in case of errors */
00851    env->gui = cleanup_sdl(gui);
00852 }
00853 
00854 /*
00855  * Functions to determine if a point is within a region. Return 1 if success.
00856  * First rotate the point, with
00857  * x' =  (x - x0) * cos A + (y - y0) * sin A
00858  * y' = -(x - x0) * sin A + (y - y0) * cos A
00859  * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
00860  * l = sqrt( (x1-x0)^2 + (y1-y0)^2
00861  * Then determine inclusion by simple comparisons i.e.:
00862  * rectangle: x >= 0 && x < l && y >= 0 && y < h
00863  * ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
00864  */
00865 static int kp_match_area(const struct keypad_entry *e, int x, int y)
00866 {
00867    double xp, dx = (e->x1 - e->x0);
00868    double yp, dy = (e->y1 - e->y0);
00869    double l = sqrt(dx*dx + dy*dy);
00870    int ret = 0;
00871 
00872    if (l > 1) { /* large enough */
00873       xp = ((x - e->x0)*dx + (y - e->y0)*dy)/l;
00874       yp = (-(x - e->x0)*dy + (y - e->y0)*dx)/l;
00875       if (e->type == KP_RECT) {
00876          ret = (xp >= 0 && xp < l && yp >=0 && yp < l);
00877       } else if (e->type == KP_CIRCLE) {
00878          dx = xp*xp/(l*l) + yp*yp/(e->h*e->h);
00879          ret = (dx < 1);
00880       }
00881    }
00882 #if 0
00883    ast_log(LOG_WARNING, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
00884       ret, e->c, x, y, e->type, e->x0, e->y0, e->x1, e->y1, e->h);
00885 #endif
00886    return ret;
00887 }
00888 
00889 struct _s_k { const char *s; int k; };
00890 static struct _s_k gui_key_map[] = {
00891    {"PICK_UP", KEY_PICK_UP },
00892    {"PICKUP",  KEY_PICK_UP },
00893         {"HANG_UP",  KEY_HANG_UP },
00894         {"HANGUP",   KEY_HANG_UP },
00895         {"MUTE",  KEY_MUTE },
00896         {"FLASH", KEY_FLASH },
00897         {"AUTOANSWER",  KEY_AUTOANSWER },
00898         {"SENDVIDEO",   KEY_SENDVIDEO },
00899         {"LOCALVIDEO",  KEY_LOCALVIDEO },
00900         {"REMOTEVIDEO", KEY_REMOTEVIDEO },
00901         {"GUI_CLOSE",   KEY_GUI_CLOSE },
00902         {"MESSAGEBOARD",   KEY_MESSAGEBOARD },
00903         {"DIALEDBOARD", KEY_DIALEDBOARD },
00904         {"EDITBOARD",   KEY_EDITBOARD },
00905         {"KEYPAD",   KEY_KEYPAD },  /* x0 y0 w h - active area of the keypad */
00906         {"MESSAGE",  KEY_MESSAGE }, /* x0 y0 w h - incoming messages */
00907         {"DIALED",   KEY_DIALED },  /* x0 y0 w h - dialed number */
00908         {"EDIT",  KEY_EDIT }, /* x0 y0 w h - edit user input */
00909         {"FONT",  KEY_FONT }, /* x0 yo w h rows cols - location and format of the font */
00910         {NULL, 0 } };
00911 
00912 static int gui_map_token(const char *s)
00913 {
00914    /* map the string into token to be returned */
00915    int i = atoi(s);
00916    struct _s_k *p;
00917    if (i > 0 || s[1] == '\0') /* numbers or single characters */
00918       return (i > 9) ? i : s[0];
00919    for (p = gui_key_map; p->s; p++) {
00920       if (!strcasecmp(p->s, s))
00921          return p->k;
00922    }
00923    return KEY_NONE;  /* not found */
00924 }
00925 
00926 /*! \brief read a keypad entry line in the format
00927  * reset
00928  * token circle xc yc diameter
00929  * token circle xc yc x1 y1 h # ellipse, main diameter and height
00930  * token rect x0 y0 x1 y1 h   # rectangle with main side and eight
00931  * token is the token to be returned, either a character or a symbol
00932  * as KEY_* above
00933  * Return 1 on success, 0 on error.
00934  */
00935 static int keypad_cfg_read(struct gui_info *gui, const char *val)
00936 {
00937    struct keypad_entry e;
00938    SDL_Rect *r = NULL;
00939    char s1[16], s2[16];
00940    int i, ret = 0; /* default, error */
00941 
00942    if (gui == NULL || val == NULL)
00943       return 0;
00944 
00945    s1[0] = s2[0] = '\0';
00946    memset(&e, '\0', sizeof(e));
00947    i = sscanf(val, "%14s %14s %d %d %d %d %d",
00948                 s1, s2, &e.x0, &e.y0, &e.x1, &e.y1, &e.h);
00949 
00950    e.c = gui_map_token(s1);
00951    if (e.c == KEY_NONE)
00952       return 0;   /* nothing found */
00953    switch (i) {
00954    default:
00955       break;
00956    case 1:  /* only "reset" is allowed */
00957       if (e.c != KEY_RESET)
00958          break;
00959       if (gui->kp)
00960          gui->kp_used = 0;
00961       break;
00962    case 5:
00963       if (e.c == KEY_KEYPAD)  /* active keypad area */
00964          r = &gui->kp_rect;
00965       else if (e.c == KEY_MESSAGE)
00966          r = gui->kp_msg;
00967       else if (e.c == KEY_DIALED)
00968          r = gui->kp_dialed;
00969       else if (e.c == KEY_EDIT)
00970          r = gui->kp_edit;
00971       if (r) {
00972          r->x = atoi(s2);
00973          r->y = e.x0;
00974          r->w = e.y0;
00975          r->h = e.x1;
00976          break;
00977       }
00978       if (strcasecmp(s2, "circle")) /* invalid */
00979          break;
00980       /* token circle xc yc diameter */
00981       e.h = e.x1;
00982       e.y1 = e.y0;   /* map radius in x1 y1 */
00983       e.x1 = e.x0 + e.h;   /* map radius in x1 y1 */
00984       e.x0 = e.x0 - e.h;   /* map radius in x1 y1 */
00985       /* fallthrough */
00986 
00987    case 7:
00988       if (e.c == KEY_FONT) {  /* font - x0 y0 w h rows cols */
00989          ast_log(LOG_WARNING, "font not supported yet\n");
00990          break;
00991       }
00992       /* token circle|rect x0 y0 x1 y1 h */
00993       if (e.x1 < e.x0 || e.h <= 0) {
00994          ast_log(LOG_WARNING, "error in coordinates\n");
00995          e.type = 0;
00996          break;
00997       }
00998       if (!strcasecmp(s2, "circle")) {
00999          /* for a circle we specify the diameter but store center and radii */
01000          e.type = KP_CIRCLE;
01001          e.x0 = (e.x1 + e.x0) / 2;
01002          e.y0 = (e.y1 + e.y0) / 2;
01003          e.h = e.h / 2;
01004       } else if (!strcasecmp(s2, "rect")) {
01005          e.type = KP_RECT;
01006       } else
01007          break;
01008       ret = 1;
01009    }
01010    // ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
01011    if (ret == 0)
01012       return 0;
01013    if (gui->kp_size == 0) {
01014       gui->kp = ast_calloc(10, sizeof(e));
01015       if (gui->kp == NULL) {
01016          ast_log(LOG_WARNING, "cannot allocate kp");
01017          return 0;
01018       }
01019       gui->kp_size = 10;
01020    }
01021    if (gui->kp_size == gui->kp_used) { /* must allocate */
01022       struct keypad_entry *a = ast_realloc(gui->kp, sizeof(e)*(gui->kp_size+10));
01023       if (a == NULL) {
01024          ast_log(LOG_WARNING, "cannot reallocate kp");
01025          return 0;
01026       }
01027       gui->kp = a;
01028       gui->kp_size += 10;
01029    }
01030    if (gui->kp_size == gui->kp_used)
01031       return 0;
01032    gui->kp[gui->kp_used++] = e;
01033    // ast_log(LOG_WARNING, "now %d regions\n", gui->kp_used);
01034    return 1;
01035 }
01036 #endif   /* HAVE_SDL */

Generated on Thu Jul 9 13:40:33 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7