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: 369013 $
00005  */
00006 
00007 /*
00008  * GUI layout, structure and management
00009  
00010 For the GUI we use SDL to create a large surface (gui->screen) with 4 areas:
00011 remote video on the left, local video on the right, keypad with all controls
00012 and text windows in the center, and source device thumbnails on the top.
00013 The top row is not displayed if no devices are specified in the config file.
00014 
00015      ________________________________________________________________
00016     |  ______   ______   ______   ______   ______   ______   ______  |
00017     | | tn.1 | | tn.2 | | tn.3 | | tn.4 | | tn.5 | | tn.6 | | tn.7 | |
00018     | |______| |______| |______| |______| |______| |______| |______| |
00019     |  ______   ______   ______   ______   ______   ______   ______  |
00020     | |______| |______| |______| |______| |______| |______| |______| |
00021     |  _________________    __________________    _________________  |
00022     | |                 |  |                  |  |                 | |
00023     | |                 |  |                  |  |                 | |
00024     | |                 |  |                  |  |                 | |
00025     | |   remote video  |  |                  |  |   local video   | |
00026     | |                 |  |                  |  |          ______ | |
00027     | |                 |  |      keypad      |  |         |  PIP || |
00028     | |                 |  |                  |  |         |______|| |
00029     | |_________________|  |                  |  |_________________| |
00030     |                      |                  |                      |
00031     |                      |                  |                      |
00032     |                      |__________________|                      |
00033     |________________________________________________________________|
00034 
00035 
00036 The central section is built using an image (jpg, png, maybe gif too)
00037 for the skin, and other GUI elements.  Comments embedded in the image
00038 indicate to what function each area is mapped to.
00039 Another image (png with transparency) is used for the font.
00040 
00041 Mouse and keyboard events are detected on the whole surface, and
00042 handled differently according to their location:
00043 - center/right click on the local/remote window are used to resize
00044   the corresponding window;
00045 - clicks on the thumbnail start/stop sources and select them as
00046   primary or secondary video sources;
00047 - drag on the local video window are used to move the captured
00048   area (in the case of X11 grabber) or the picture-in-picture position;
00049 - keystrokes on the keypad are mapped to the corresponding key;
00050   keystrokes are used as keypad functions, or as text input
00051   if we are in text-input mode.
00052 - drag on some keypad areas (sliders etc.) are mapped to the
00053   corresponding functions (mute/unmute audio and video,
00054   enable/disable Picture-in-Picture, freeze the incoming video,
00055   dial numbers, pick up or hang up a call, ...)
00056 
00057 Configuration options control the appeareance of the gui:
00058 
00059     keypad = /tmp/kpad2.jpg   ; the skin
00060     keypad_font = /tmp/font.png  ; the font to use for output
00061 
00062 For future implementation, intresting features can be the following:
00063 - save of the whole SDL window as a picture
00064 - audio output device switching
00065 
00066 The audio switching feature should allow changing the device
00067 or switching to a recorded message for audio sent to remote party.
00068 The selection of the device should happen clicking on a marker in the layout.
00069 For this reason above the thumbnails row in the layout we would like a new row,
00070 the elements composing the row could be message boards, reporting the name of the
00071 device or the path of the message to be played.
00072 
00073 For video input freeze and entire window capture, we define 2 new key types,
00074 those should be activated pressing the buttons on the keypad, associated with
00075 new regions inside the keypad pictureas comments
00076 
00077 
00078  *
00079  */
00080 
00081 /*** MODULEINFO
00082    <support_level>extended</support_level>
00083  ***/
00084 
00085 #include "asterisk.h"
00086 #include "console_video.h"
00087 #include "asterisk/lock.h"
00088 #include "asterisk/frame.h"
00089 #include "asterisk/utils.h"   /* ast_calloc and ast_realloc */
00090 #include <math.h>    /* sqrt */
00091 
00092 /* We use a maximum of 12 'windows' in the GUI */
00093 enum { WIN_LOCAL, WIN_REMOTE, WIN_KEYPAD, WIN_SRC1,
00094    WIN_SRC2, WIN_SRC3, WIN_SRC4, WIN_SRC5,
00095    WIN_SRC6, WIN_SRC7, WIN_SRC8, WIN_SRC9, WIN_MAX };
00096 
00097 #ifndef HAVE_SDL  /* stubs if we don't have any sdl */
00098 static void show_frame(struct video_desc *env, int out)  {}
00099 static void sdl_setup(struct video_desc *env)      {}
00100 static struct gui_info *cleanup_sdl(struct gui_info* g, int n) { return NULL; }
00101 static void eventhandler(struct video_desc *env, const char *caption)   {}
00102 static int keypad_cfg_read(struct gui_info *gui, const char *val) { return 0; }
00103 
00104 #else /* HAVE_SDL, the real rendering code */
00105 
00106 #include <SDL/SDL.h>
00107 #include <SDL/SDL_syswm.h>
00108 #ifdef HAVE_SDL_IMAGE
00109 #include <SDL/SDL_image.h>      /* for loading images */
00110 #endif
00111 
00112 #ifdef HAVE_X11
00113 /* Need to hook into X for SDL_WINDOWID handling */
00114 #include <X11/Xlib.h>
00115 #endif
00116 
00117 #define BORDER 5     /* border around our windows */
00118 #define SRC_MSG_BD_H 20    /* height of the message board below those windows */
00119 
00120 enum kp_type { KP_NONE, KP_RECT, KP_CIRCLE };
00121 struct keypad_entry {
00122         int c;  /* corresponding character */
00123         int x0, y0, x1, y1, h;  /* arguments */
00124         enum kp_type type;
00125 };
00126 
00127 /* our representation of a displayed window. SDL can only do one main
00128  * window so we map everything within that one
00129  */
00130 struct display_window {
00131    SDL_Overlay *bmp;
00132    SDL_Rect rect; /* location of the window */
00133 };
00134 
00135 /* each thumbnail message board has a rectangle associated for the geometry,
00136  * and a board structure, we include these two elements in a singole structure */
00137 struct thumb_bd {
00138    SDL_Rect    rect;    /* the rect for geometry and background */
00139    struct board      *board;     /* the board */
00140 };
00141 
00142 struct gui_info {
00143    enum kb_output    kb_output;  /* where the keyboard output goes */
00144    struct drag_info  drag;    /* info on the window are we dragging */
00145    /* support for display. */
00146    SDL_Surface             *screen; /* the main window */
00147 
00148    int         outfd;      /* fd for output */
00149    SDL_Surface    *keypad; /* the skin for the keypad */
00150    SDL_Rect    kp_rect; /* portion of the skin to display - default all */
00151    SDL_Surface    *font;      /* font to be used */ 
00152    SDL_Rect    font_rects[96];   /* only printable chars */
00153 
00154    /* each of the following board has two rectangles,
00155     * [0] is the geometry relative to the keypad,
00156     * [1] is the geometry relative to the whole screen
00157     * we do not use the thumb_bd for these boards because here we need
00158     * 2 rectangles for geometry
00159     */
00160    SDL_Rect    kp_msg[2];     /* incoming msg, relative to kpad */
00161    struct board      *bd_msg;
00162 
00163    SDL_Rect    kp_edit[2]; /* edit user input */
00164    struct board      *bd_edit;
00165 
00166    SDL_Rect    kp_dialed[2];  /* dialed number */
00167    struct board      *bd_dialed;
00168 
00169    /* other boards are one associated with the source windows
00170     * above the keypad in the layout, we only have the geometry
00171     * relative to the whole screen
00172     */
00173    struct thumb_bd      thumb_bd_array[MAX_VIDEO_SOURCES];
00174 
00175    /* variable-size array mapping keypad regions to functions */
00176    int kp_size, kp_used;
00177    struct keypad_entry *kp;
00178 
00179    struct display_window   win[WIN_MAX];
00180 };
00181 
00182 /*! \brief free the resources in struct gui_info and the descriptor itself.
00183  *  Return NULL so we can assign the value back to the descriptor in case.
00184  */
00185 static struct gui_info *cleanup_sdl(struct gui_info *gui, int device_num)
00186 {
00187    int i;
00188 
00189    if (gui == NULL)
00190       return NULL;
00191 
00192    /* unload font file */ 
00193    if (gui->font) {
00194       SDL_FreeSurface(gui->font);
00195       gui->font = NULL; 
00196    }
00197 
00198    if (gui->outfd > -1)
00199       close(gui->outfd);
00200    if (gui->keypad)
00201       SDL_FreeSurface(gui->keypad);
00202    gui->keypad = NULL;
00203    if (gui->kp)
00204       ast_free(gui->kp);
00205 
00206    /* uninitialize the SDL environment */
00207    for (i = 0; i < WIN_MAX; i++) {
00208       if (gui->win[i].bmp)
00209          SDL_FreeYUVOverlay(gui->win[i].bmp);
00210    }
00211    memset(gui, '\0', sizeof(gui));
00212 
00213    /* deallocates the space allocated for the keypad message boards */
00214    if (gui->bd_dialed)
00215       delete_board(gui->bd_dialed);
00216    if (gui->bd_msg)
00217       delete_board(gui->bd_msg);
00218 
00219    /* deallocates the space allocated for the thumbnail message boards */
00220    for (i = 0; i < device_num; i++) {
00221       if (gui->thumb_bd_array[i].board) /* may be useless */
00222          delete_board(gui->thumb_bd_array[i].board);
00223    }
00224    
00225    ast_free(gui);
00226    SDL_Quit();
00227    return NULL;
00228 }
00229 
00230 /* messages to be displayed in the sources message boards
00231  * below the source windows
00232  */
00233 
00234 /* costants defined to describe status of devices */
00235 #define IS_PRIMARY 1
00236 #define IS_SECONDARY 2
00237 #define IS_ON 4
00238 
00239 char* src_msgs[] = {
00240    "    OFF",
00241    "1   OFF",
00242    "  2 OFF",
00243    "1+2 OFF",
00244    "    ON",
00245    "1   ON",
00246    "  2 ON",
00247    "1+2 ON",
00248 };
00249 /*
00250  * Display video frames (from local or remote stream) using the SDL library.
00251  * - Set the video mode to use the resolution specified by the codec context
00252  * - Create a YUV Overlay to copy the frame into it;
00253  * - After the frame is copied into the overlay, display it
00254  *
00255  * The size is taken from the configuration.
00256  *
00257  * 'out' is 0 for remote video, 1 for the local video
00258  */
00259 static void show_frame(struct video_desc *env, int out)
00260 {
00261    AVPicture *p_in, p_out;
00262    struct fbuf_t *b_in, *b_out;
00263    SDL_Overlay *bmp;
00264    struct gui_info *gui = env->gui;
00265 
00266    if (!gui)
00267       return;
00268 
00269    if (out == WIN_LOCAL) { /* webcam/x11 to sdl */
00270       b_in = &env->enc_in;
00271       b_out = &env->loc_dpy;
00272       p_in = NULL;
00273    } else if (out == WIN_REMOTE) {
00274       /* copy input format from the decoding context */
00275       AVCodecContext *c;
00276       if (env->in == NULL) /* XXX should not happen - decoder not ready */
00277          return;
00278       c = env->in->dec_ctx;
00279       b_in = &env->in->dec_out;
00280                 b_in->pix_fmt = c->pix_fmt;
00281                 b_in->w = c->width;
00282                 b_in->h = c->height;
00283 
00284       b_out = &env->rem_dpy;
00285       p_in = (AVPicture *)env->in->d_frame;
00286    } else {
00287       int i = out-WIN_SRC1;
00288       b_in = env->out.devices[i].dev_buf;
00289       if (b_in == NULL)
00290          return;
00291       p_in = NULL;
00292       b_out = &env->src_dpy[i];
00293    }     
00294    bmp = gui->win[out].bmp;
00295    SDL_LockYUVOverlay(bmp);
00296    /* output picture info - this is sdl, YUV420P */
00297    memset(&p_out, '\0', sizeof(p_out));
00298    p_out.data[0] = bmp->pixels[0];
00299    p_out.data[1] = bmp->pixels[1];
00300    p_out.data[2] = bmp->pixels[2];
00301    p_out.linesize[0] = bmp->pitches[0];
00302    p_out.linesize[1] = bmp->pitches[1];
00303    p_out.linesize[2] = bmp->pitches[2];
00304 
00305    my_scale(b_in, p_in, b_out, &p_out);
00306 
00307    /* lock to protect access to Xlib by different threads. */
00308    SDL_DisplayYUVOverlay(bmp, &gui->win[out].rect);
00309    SDL_UnlockYUVOverlay(bmp);
00310 }
00311 
00312 /*
00313  * Identifiers for regions of the main window.
00314  * Values between 0 and 127 correspond to ASCII characters.
00315  * The corresponding strings to be used in the skin comment section
00316  * are defined in gui_key_map.
00317  */
00318 enum skin_area {
00319    /* answer/close functions */
00320    KEY_PICK_UP = 128,
00321    KEY_HANG_UP = 129,
00322 
00323    KEY_MUTE = 130,
00324    KEY_AUTOANSWER = 131,
00325    KEY_SENDVIDEO = 132,
00326    KEY_LOCALVIDEO = 133,
00327    KEY_REMOTEVIDEO = 134,
00328    KEY_FLASH = 136,
00329 
00330    /* sensitive areas for the various text windows */
00331    KEY_MESSAGEBOARD = 140,
00332    KEY_DIALEDBOARD = 141,
00333    KEY_EDITBOARD = 142,
00334 
00335    KEY_GUI_CLOSE = 199,    /* close gui */
00336    /* regions of the skin - displayed area, fonts, etc.
00337     * XXX NOTE these are not sensitive areas.
00338     */
00339    KEY_KEYPAD = 200,    /* the keypad - default to the whole image */
00340    KEY_FONT = 201,      /* the font. Maybe not really useful */
00341    KEY_MESSAGE = 202,   /* area for incoming messages */
00342    KEY_DIALED = 203, /* area for dialed numbers */
00343    KEY_EDIT = 204,      /* area for editing user input */
00344 
00345 #ifdef notyet /* XXX for future implementation */
00346    KEY_AUDIO_SRCS = 210,
00347    /*indexes between 210 and 219 (or more) have been reserved for the "keys"
00348    associated with the audio device markers, clicking on these markers
00349    will change the source device for audio output */
00350 
00351 #endif
00352    /* Keys related to video sources */
00353    KEY_FREEZE = 220, /* freeze the incoming video */
00354    KEY_CAPTURE = 221,   /* capture the whole SDL window as a picture */
00355    KEY_PIP = 230,
00356    /*indexes between 231 and 239 have been reserved for the "keys"
00357    associated with the device thumbnails, clicking on these pictures
00358    will change the source device for primary or secondary (PiP) video output*/
00359    KEY_SRCS_WIN = 231, /* till 239 */
00360    /* areas outside the keypad - simulated */
00361    KEY_OUT_OF_KEYPAD = 241,
00362    KEY_REM_DPY = 242,
00363    KEY_LOC_DPY = 243,
00364    KEY_RESET = 253,     /* the 'reset' keyword */
00365    KEY_NONE = 254,         /* invalid area */
00366    KEY_DIGIT_BACKGROUND = 255,   /* other areas within the keypad */
00367 };
00368 
00369 /*
00370  * Handlers for the various keypad functions
00371  */
00372 
00373 /* accumulate digits, possibly call dial if in connected mode */
00374 static void keypad_digit(struct video_desc *env, int digit)
00375 {  
00376    if (env->owner) {    /* we have a call, send the digit */
00377       struct ast_frame f = { AST_FRAME_DTMF, 0 };
00378 
00379       f.subclass = digit;
00380       ast_queue_frame(env->owner, &f);
00381    } else {    /* no call, accumulate digits */
00382       char buf[2] = { digit, '\0' };
00383       if (env->gui->bd_msg) /* XXX not strictly necessary ... */
00384          print_message(env->gui->bd_msg, buf);
00385    }
00386 }
00387 
00388 /* function used to toggle on/off the status of some variables */
00389 static char *keypad_toggle(struct video_desc *env, int index)
00390 {
00391    ast_log(LOG_WARNING, "keypad_toggle(%i) called\n", index);
00392 
00393    switch (index) {
00394    case KEY_SENDVIDEO: /* send or do not send video */
00395       env->out.sendvideo = !env->out.sendvideo;
00396       break;
00397 
00398    case KEY_PIP: /* enable or disable Picture in Picture */
00399       env->out.picture_in_picture = !env->out.picture_in_picture;
00400       break;
00401 
00402    case KEY_MUTE: /* send or do not send audio */
00403       ast_cli_command(env->gui->outfd, "console mute toggle");
00404       break;
00405 
00406    case KEY_FREEZE: /* freeze/unfreeze the incoming frames */
00407       env->frame_freeze = !env->frame_freeze;
00408       break;
00409 
00410 #ifdef notyet
00411    case KEY_AUTOANSWER: {
00412       struct chan_oss_pvt *o = find_desc(oss_active);
00413       o->autoanswer = !o->autoanswer;
00414       }
00415       break;
00416 #endif
00417    }
00418    return NULL;
00419 }
00420 
00421 char *console_do_answer(int fd);
00422 /*
00423  * Function called when the pick up button is pressed
00424  * perform actions according the channel status:
00425  *
00426  *  - if no one is calling us and no digits was pressed,
00427  *    the operation have no effects,
00428  *  - if someone is calling us we answer to the call.
00429  *  - if we have no call in progress and we pressed some
00430  *    digit, send the digit to the console.
00431  */
00432 static void keypad_pick_up(struct video_desc *env)
00433 {
00434    struct gui_info *gui = env->gui;
00435 
00436    ast_log(LOG_WARNING, "keypad_pick_up called\n");
00437 
00438    if (env->owner) { /* someone is calling us, just answer */
00439       ast_cli_command(gui->outfd, "console answer");
00440    } else { /* we have someone to call */
00441       char buf[160];
00442       const char *who = ast_skip_blanks(read_message(gui->bd_msg));
00443       buf[sizeof(buf) - 1] = '\0';
00444       snprintf(buf, sizeof(buf), "console dial %s", who);
00445       ast_log(LOG_WARNING, "doing <%s>\n", buf);
00446       print_message(gui->bd_dialed, "\n");
00447       print_message(gui->bd_dialed, who);
00448       reset_board(gui->bd_msg);
00449       ast_cli_command(gui->outfd, buf);
00450    }
00451 }
00452 
00453 #if 0 /* still unused */
00454 /*
00455  * As an alternative to SDL_TTF, we can simply load the font from
00456  * an image and blit characters on the background of the GUI.
00457  *
00458  * To generate a font we can use the 'fly' command with the
00459  * following script (3 lines with 32 chars each)
00460  
00461 size 320,64
00462 name font.png
00463 transparent 0,0,0
00464 string 255,255,255,  0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
00465 string 255,255,255,  0,20,giant,@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
00466 string 255,255,255,  0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
00467 end
00468 
00469  */
00470 
00471 /* Print given text on the gui */
00472 static int gui_output(struct video_desc *env, const char *text)
00473 {
00474    return 1;   /* error, not supported */
00475 }
00476 #endif 
00477 
00478 static int video_geom(struct fbuf_t *b, const char *s);
00479 static void sdl_setup(struct video_desc *env);
00480 static int kp_match_area(const struct keypad_entry *e, int x, int y);
00481 
00482 static void set_drag(struct drag_info *drag, int x, int y, enum drag_window win)
00483 {
00484    drag->x_start = x;
00485    drag->y_start = y;
00486    drag->drag_window = win;
00487 }
00488 
00489 static int update_device_info(struct video_desc *env, int i)
00490 {
00491    reset_board(env->gui->thumb_bd_array[i].board);
00492    print_message(env->gui->thumb_bd_array[i].board,
00493       src_msgs[env->out.devices[i].status_index]);
00494    return 0;
00495 }
00496 
00497 /*! \brief Changes the video output (local video) source, controlling if
00498  * it is already using that video device, 
00499  * and switching the correct fields of env->out.
00500  * grabbers are always open and saved in the device table.
00501  * The secondary or the primary device can be changed,
00502  * according to the "button" parameter:
00503  * the primary device is changed if button = SDL_BUTTON_LEFT;
00504  * the secondary device is changed if button = not SDL_BUTTON_LEFT;
00505  * 
00506  * the correct message boards of the sources are also updated
00507  * with the new status
00508  * 
00509  * \param env = pointer to the video environment descriptor
00510  * \param index = index of the device the caller wants to use are primary or secondary device
00511  * \param button = button clicked on the mouse
00512  *
00513  * returns 0 on success,
00514  * returns 1 on error 
00515  */
00516 static int switch_video_out(struct video_desc *env, int index, Uint8 button)
00517 {
00518    int *p; /* pointer to the index of the device to select */
00519 
00520    if (index >= env->out.device_num) {
00521       ast_log(LOG_WARNING, "no devices\n");
00522       return 1;
00523    }
00524    /* select primary or secondary */
00525    p = (button == SDL_BUTTON_LEFT) ? &env->out.device_primary :
00526       &env->out.device_secondary;
00527    /* controls if the device is already selected */
00528    if (index == *p) {
00529       ast_log(LOG_WARNING, "device %s already selected\n", env->out.devices[index].name);
00530       return 0;
00531    }
00532    ast_log(LOG_WARNING, "switching to %s...\n", env->out.devices[index].name);
00533    /* already open */
00534    if (env->out.devices[index].grabber) {
00535       /* we also have to update the messages in the source 
00536       message boards below the source windows */
00537       /* first we update the board of the previous source */
00538       if (p == &env->out.device_primary)
00539          env->out.devices[*p].status_index &= ~IS_PRIMARY;
00540       else
00541          env->out.devices[*p].status_index &= ~IS_SECONDARY;
00542       update_device_info(env, *p);
00543       /* update the index used as primary or secondary */
00544       *p = index;
00545       ast_log(LOG_WARNING, "done\n");
00546       /* then we update the board of the new primary or secondary source */
00547       if (p == &env->out.device_primary)
00548          env->out.devices[*p].status_index |= IS_PRIMARY;
00549       else
00550          env->out.devices[*p].status_index |= IS_SECONDARY;
00551       update_device_info(env, *p);
00552       return 0;
00553    }
00554    /* device is off, just do nothing */
00555    ast_log(LOG_WARNING, "device is down\n");
00556    return 1;
00557 }
00558 
00559 /*! \brief tries to switch the state of a device from on to off or off to on
00560  * we also have to update the status of the device and the correct message board
00561  *
00562  * \param index = the device that must be turned on or off
00563  * \param env = pointer to the video environment descriptor
00564  *
00565  * returns:
00566  * - 0 on falure switching from off to on
00567  * - 1 on success in switching from off to on
00568  * - 2 on success in switching from on to off
00569 */
00570 static int turn_on_off(int index, struct video_desc *env)
00571 {
00572    struct video_device *p = &env->out.devices[index];
00573 
00574    if (index >= env->out.device_num) {
00575       ast_log(LOG_WARNING, "no devices\n");
00576       return 0;
00577    }
00578 
00579    if (!p->grabber) { /* device off */
00580       void *g_data; /* result of grabber_open() */
00581       struct grab_desc *g;
00582       int i;
00583 
00584       /* see if the device can be used by one of the existing drivers */
00585       for (i = 0; (g = console_grabbers[i]); i++) {
00586          /* try open the device */
00587          g_data = g->open(p->name, &env->out.loc_src_geometry, env->out.fps);
00588          if (!g_data)   /* no luck, try the next driver */
00589             continue;
00590          p->grabber = g;
00591          p->grabber_data = g_data;
00592          /* update the status of the source */
00593          p->status_index |= IS_ON;
00594          /* print the new message in the message board */
00595          update_device_info(env, index);
00596          return 1; /* open succeded */
00597       }
00598       return 0; /* failure */
00599    } else {
00600       /* the grabber must be closed */
00601       p->grabber_data = p->grabber->close(p->grabber_data);
00602       p->grabber = NULL;
00603       /* dev_buf is already freed by grabber->close() */
00604       p->dev_buf = NULL;
00605       /* update the status of the source */
00606       p->status_index &= ~IS_ON;
00607       /* print the new message in the message board */
00608       update_device_info(env, index);
00609       return 2; /* closed */
00610    }  
00611 }
00612 
00613 /*
00614  * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
00615  * index value and calling the right callback.
00616  *
00617  * x, y are referred to the upper left corner of the main SDL window.
00618  */
00619 static void handle_mousedown(struct video_desc *env, SDL_MouseButtonEvent button)
00620 {
00621    uint8_t index = KEY_OUT_OF_KEYPAD;  /* the key or region of the display we clicked on */
00622    struct gui_info *gui = env->gui;
00623       
00624    int i; /* integer variable used as iterator */
00625 
00626    int x; /* integer variable usable as a container */
00627    
00628    /* total width of source device thumbnails */
00629    int src_wins_tot_w = env->out.device_num*(SRC_WIN_W+BORDER)+BORDER;
00630 
00631    /* x coordinate of the center of the keypad */
00632    int x0 = MAX(env->rem_dpy.w+gui->keypad->w/2+2*BORDER, src_wins_tot_w/2);
00633    
00634 #if 0
00635    ast_log(LOG_WARNING, "event %d %d have %d/%d regions at %p\n",
00636       button.x, button.y, gui->kp_used, gui->kp_size, gui->kp);
00637 #endif
00638    /* for each mousedown we end previous drag */
00639    gui->drag.drag_window = DRAG_NONE;
00640    
00641    /* define keypad boundary */
00642    /* XXX this should be extended for clicks on different audio device markers */
00643    if (button.y >= (env->out.device_num ? SRC_WIN_H+2*BORDER+SRC_MSG_BD_H : 0)) {
00644       /* if control reaches this point this means that the clicked point is
00645       below the row of the additional sources windows*/
00646       /* adjust the y coordinate as if additional devices windows were not present */
00647       button.y -= (env->out.device_num ? SRC_WIN_H+2*BORDER+SRC_MSG_BD_H : 0);
00648       if (button.y < BORDER)
00649          index = KEY_OUT_OF_KEYPAD;
00650       else if (button.y >= MAX(MAX(env->rem_dpy.h, env->loc_dpy.h), gui->keypad->h))
00651          index = KEY_OUT_OF_KEYPAD;
00652       else if (button.x < x0 - gui->keypad->w/2 - BORDER - env->rem_dpy.w)
00653          index = KEY_OUT_OF_KEYPAD;
00654       else if (button.x < x0 - gui->keypad->w/2 - BORDER)
00655          index = KEY_REM_DPY;
00656       else if (button.x < x0 - gui->keypad->w/2)
00657          index = KEY_OUT_OF_KEYPAD;
00658       else if (button.x >= x0 + gui->keypad->w/2 + BORDER + env->loc_dpy.w)
00659          index = KEY_OUT_OF_KEYPAD;
00660       else if (button.x >= x0 + gui->keypad->w/2 + BORDER)
00661          index = KEY_LOC_DPY;
00662       else if (button.x >= x0 + gui->keypad->w/2)
00663          index = KEY_OUT_OF_KEYPAD;
00664       else if (gui->kp) {
00665          /* we have to calculate the first coordinate 
00666          inside the keypad before calling the kp_match_area*/
00667          int x_keypad = button.x - (x0 - gui->keypad->w/2);
00668          /* find the key clicked (if one was clicked) */
00669          for (i = 0; i < gui->kp_used; i++) {
00670             if (kp_match_area(&gui->kp[i],x_keypad, button.y - BORDER)) {
00671                index = gui->kp[i].c;
00672                break;
00673             }
00674          }
00675       }
00676    } else if (button.y < BORDER) {
00677       index = KEY_OUT_OF_KEYPAD;
00678    } else {  /* we are in the thumbnail area */
00679       x = x0 - src_wins_tot_w/2 + BORDER;
00680       if (button.y >= BORDER + SRC_WIN_H)
00681          index = KEY_OUT_OF_KEYPAD;
00682       else if (button.x < x)
00683          index = KEY_OUT_OF_KEYPAD;
00684       else if (button.x < x + src_wins_tot_w - BORDER) {
00685          /* note that the additional device windows 
00686          are numbered from left to right
00687          starting from 0, with a maximum of 8, the index associated on a click is:
00688          KEY_SRCS_WIN + number_of_the_window */
00689          for (i = 1; i <= env->out.device_num; i++) {
00690             if (button.x < x+i*(SRC_WIN_W+BORDER)-BORDER) {
00691                index = KEY_SRCS_WIN+i-1;
00692                break;
00693             } else if (button.x < x+i*(SRC_WIN_W+BORDER)) {
00694                index = KEY_OUT_OF_KEYPAD;
00695                break;
00696             }
00697          }
00698       } else
00699          index = KEY_OUT_OF_KEYPAD;
00700    }
00701 
00702    /* exec the function */
00703    if (index < 128) {   /* surely clicked on the keypad, don't care which key */
00704       keypad_digit(env, index);
00705       return;
00706    }
00707 
00708    else if (index >= KEY_SRCS_WIN && index < KEY_SRCS_WIN+env->out.device_num) {
00709       index -= KEY_SRCS_WIN; /* index of the window, equal to the device index in the table */
00710       /* if one of the additional device windows is clicked with
00711       left or right mouse button, we have to switch to that device */
00712       if (button.button == SDL_BUTTON_RIGHT || button.button == SDL_BUTTON_LEFT) {
00713          switch_video_out(env, index, button.button);
00714          return;
00715       }
00716       /* turn on or off the devices selectively with other mouse buttons */
00717       else {
00718          int ret = turn_on_off(index, env);
00719          /* print a message according to what happened */
00720          if (!ret)
00721             ast_log(LOG_WARNING, "unable to turn on device %s\n",
00722                env->out.devices[index].name);
00723          else if (ret == 1)
00724             ast_log(LOG_WARNING, "device %s changed state to on\n",
00725                env->out.devices[index].name);
00726          else if (ret == 2)
00727             ast_log(LOG_WARNING, "device %s changed state to off\n",
00728                env->out.devices[index].name);
00729          return;
00730       }
00731    }
00732 
00733    /* XXX for future implementation
00734    else if (click on audio source marker)
00735       change audio source device
00736    */
00737 
00738    switch (index) {
00739    /* answer/close function */
00740    case KEY_PICK_UP:
00741       keypad_pick_up(env);
00742       break;
00743    case KEY_HANG_UP:
00744       ast_cli_command(gui->outfd, "console hangup");
00745       break;
00746 
00747    /* other functions */
00748    case KEY_MUTE: /* send or not send the audio */
00749    case KEY_AUTOANSWER:
00750    case KEY_SENDVIDEO: /* send or not send the video */
00751    case KEY_PIP: /* activate/deactivate picture in picture mode */
00752    case KEY_FREEZE: /* freeze/unfreeze the incoming video */
00753       keypad_toggle(env, index);
00754       break;
00755 
00756    case KEY_LOCALVIDEO:
00757       break;
00758    case KEY_REMOTEVIDEO:
00759       break;
00760 
00761 #ifdef notyet /* XXX for future implementations */
00762    case KEY_CAPTURE:
00763       break;
00764 #endif
00765 
00766    case KEY_MESSAGEBOARD:
00767       if (button.button == SDL_BUTTON_LEFT)
00768          set_drag(&gui->drag, button.x, button.y, DRAG_MESSAGE);
00769       break;
00770 
00771    /* press outside the keypad. right increases size, center decreases, left drags */
00772    case KEY_LOC_DPY:
00773    case KEY_REM_DPY:
00774       if (button.button == SDL_BUTTON_LEFT) {
00775          /* values used to find the position of the picture in picture (if present) */
00776          int pip_loc_x = (double)env->out.pip_x/env->enc_in.w * env->loc_dpy.w;
00777          int pip_loc_y = (double)env->out.pip_y/env->enc_in.h * env->loc_dpy.h;
00778          /* check if picture in picture is active and the click was on it */
00779          if (index == KEY_LOC_DPY && env->out.picture_in_picture &&
00780            button.x >= x0+gui->keypad->w/2+BORDER+pip_loc_x &&
00781            button.x < x0+gui->keypad->w/2+BORDER+pip_loc_x+env->loc_dpy.w/3 &&
00782            button.y >= BORDER+pip_loc_y && 
00783            button.y < BORDER+pip_loc_y+env->loc_dpy.h/3) {
00784             /* set the y cordinate to his previous value */
00785             button.y += (env->out.device_num ? SRC_WIN_H+2*BORDER+SRC_MSG_BD_H : 0);
00786             /* starts dragging the picture inside the picture */
00787             set_drag(&gui->drag, button.x, button.y, DRAG_PIP);
00788          }
00789          else if (index == KEY_LOC_DPY) {
00790             /* set the y cordinate to his previous value */
00791             button.y += (env->out.device_num ? SRC_WIN_H+2*BORDER+SRC_MSG_BD_H : 0);
00792             /* click in the local display, but not on the PiP */
00793             set_drag(&gui->drag, button.x, button.y, DRAG_LOCAL);
00794          }
00795          break;
00796       } else {
00797          char buf[128];
00798          struct fbuf_t *fb = index == KEY_LOC_DPY ? &env->loc_dpy : &env->rem_dpy;
00799          sprintf(buf, "%c%dx%d", button.button == SDL_BUTTON_RIGHT ? '>' : '<',
00800             fb->w, fb->h);
00801          video_geom(fb, buf);
00802          sdl_setup(env);
00803          /* writes messages in the source boards, those can be 
00804          modified during the execution, because of the events 
00805          this must be done here, otherwise the status of sources will not be
00806          shown after sdl_setup */
00807          for (i = 0; i < env->out.device_num; i++) {
00808             update_device_info(env, i);
00809          }
00810          /* we also have to refresh other boards, 
00811          to avoid messages to disappear after video resize */
00812          print_message(gui->bd_msg, " \b");
00813          print_message(gui->bd_dialed, " \b");
00814       }
00815       break;
00816    case KEY_OUT_OF_KEYPAD:
00817       ast_log(LOG_WARNING, "nothing clicked, coordinates: %d, %d\n", button.x, button.y);
00818       break;
00819 
00820    case KEY_DIGIT_BACKGROUND:
00821       break;
00822 
00823    default:
00824       ast_log(LOG_WARNING, "function not yet defined %i\n", index);
00825    }
00826 }
00827 
00828 /*
00829  * Handle SDL_KEYDOWN type event, put the key pressed
00830  * in the dial buffer or in the text-message buffer,
00831  * depending on the text_mode variable value.
00832  *
00833  * key is the SDLKey structure corresponding to the key pressed.
00834  * Note that SDL returns modifiers (ctrl, shift, alt) as independent
00835  * information so the key itself is not enough and we need to
00836  * use a translation table, below - one line per entry,
00837  * plain, shift, ctrl, ... using the first char as key.
00838  */
00839 static const char * const us_kbd_map[] = {
00840    "`~", "1!", "2@", "3#", "4$", "5%", "6^",
00841    "7&", "8*", "9(", "0)", "-_", "=+", "[{",
00842    "]}", "\\|", ";:", "'\"", ",<", ".>", "/?",
00843    "jJ\n",
00844    NULL
00845 };
00846 
00847 static char map_key(SDL_keysym *ks)
00848 {
00849    const char *s, **p = us_kbd_map;
00850    int c = ks->sym;
00851 
00852    if (c == '\r') /* map cr into lf */
00853       c = '\n';
00854    if (c >= SDLK_NUMLOCK && c <= SDLK_COMPOSE)
00855       return 0;   /* only a modifier */
00856    if (ks->mod == 0)
00857       return c;
00858    while ((s = *p) && s[0] != c)
00859       p++;
00860    if (s) { /* see if we have a modifier and a chance to use it */
00861       int l = strlen(s), mod = 0;
00862       if (l > 1)
00863          mod |= (ks->mod & KMOD_SHIFT) ? 1 : 0;
00864       if (l > 2 + mod)
00865          mod |= (ks->mod & KMOD_CTRL) ? 2 : 0;
00866       if (l > 4 + mod)
00867          mod |= (ks->mod & KMOD_ALT) ? 4 : 0;
00868       c = s[mod];
00869    }
00870    if (ks->mod & (KMOD_CAPS|KMOD_SHIFT) && c >= 'a' && c <='z')
00871       c += 'A' - 'a';
00872    return c;
00873 }
00874 
00875 static void handle_keyboard_input(struct video_desc *env, SDL_keysym *ks)
00876 {
00877    char buf[2] = { map_key(ks), '\0' };
00878    struct gui_info *gui = env->gui;
00879    if (buf[0] == 0)  /* modifier ? */
00880       return;
00881    switch (gui->kb_output) {
00882    default:
00883       break;
00884    case KO_INPUT: /* to be completed */
00885       break;
00886    case KO_MESSAGE:
00887       if (gui->bd_msg) {
00888          print_message(gui->bd_msg, buf);
00889          if (buf[0] == '\r' || buf[0] == '\n') {
00890             keypad_pick_up(env);
00891          }
00892       }
00893       break;
00894 
00895    case KO_DIALED: /* to be completed */
00896       break;
00897    }
00898 
00899    return;
00900 }
00901 
00902 static void grabber_move(struct video_device *, int dx, int dy);
00903 
00904 int compute_drag(int *start, int end, int magnifier);
00905 int compute_drag(int *start, int end, int magnifier)
00906 {
00907    int delta = end - *start;
00908 #define POLARITY -1
00909    /* add a small quadratic term */
00910    delta += delta * delta * (delta > 0 ? 1 : -1 )/100;
00911    delta *= POLARITY * magnifier;
00912 #undef POLARITY
00913    *start = end;
00914    return delta;
00915 }
00916 
00917 /*! \brief This function moves the picture in picture,
00918  * controlling the limits of the containing buffer
00919  * to avoid problems deriving from going through the limits.
00920  *
00921  * \param env = pointer to the descriptor of the video environment
00922  * \param dx = the variation of the x position
00923  * \param dy = the variation of the y position
00924 */
00925 static void pip_move(struct video_desc* env, int dx, int dy) {
00926    int new_pip_x = env->out.pip_x+dx;
00927    int new_pip_y = env->out.pip_y+dy;
00928    /* going beyond the left borders */
00929    if (new_pip_x < 0)
00930       new_pip_x = 0;
00931    /* going beyond the right borders */
00932    else if (new_pip_x > env->enc_in.w - env->enc_in.w/3)
00933       new_pip_x = env->enc_in.w - env->enc_in.w/3;
00934    /* going beyond the top borders */
00935    if (new_pip_y < 0)
00936       new_pip_y = 0;
00937    /* going beyond the bottom borders */
00938    else if (new_pip_y > env->enc_in.h - env->enc_in.h/3)
00939       new_pip_y = env->enc_in.h - env->enc_in.h/3;
00940    env->out.pip_x = new_pip_x;
00941    env->out.pip_y = new_pip_y;
00942 }
00943 
00944 /*
00945  * I am seeing some kind of deadlock or stall around
00946  * SDL_PumpEvents() while moving the window on a remote X server
00947  * (both xfree-4.4.0 and xorg 7.2)
00948  * and windowmaker. It is unclear what causes it.
00949  */
00950 
00951 /*! \brief refresh the screen, and also grab a bunch of events.
00952  */
00953 static void eventhandler(struct video_desc *env, const char *caption)
00954 {
00955    struct gui_info *gui = env->gui;
00956    struct drag_info *drag;
00957 #define N_EVENTS  32
00958    int i, n;
00959    SDL_Event ev[N_EVENTS];
00960 
00961    if (!gui)
00962       return;
00963    drag = &gui->drag;
00964    if (caption)
00965       SDL_WM_SetCaption(caption, NULL);
00966 
00967 #define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
00968    while ( (n = SDL_PeepEvents(ev, N_EVENTS, SDL_GETEVENT, SDL_ALLEVENTS)) > 0) {
00969       for (i = 0; i < n; i++) {
00970 #if 0
00971          ast_log(LOG_WARNING, "------ event %d at %d %d\n",
00972             ev[i].type,  ev[i].button.x,  ev[i].button.y);
00973 #endif
00974          switch (ev[i].type) {
00975          default:
00976             ast_log(LOG_WARNING, "------ event %d at %d %d\n",
00977                ev[i].type,  ev[i].button.x,  ev[i].button.y);
00978             break;
00979 
00980          case SDL_ACTIVEEVENT:
00981 #if 0 /* do not react, we don't want to die because the window is minimized */
00982             if (ev[i].active.gain == 0 && ev[i].active.state & SDL_APPACTIVE) {
00983                ast_log(LOG_WARNING, "/* somebody has killed us ? */\n");
00984                ast_cli_command(gui->outfd, "stop now");
00985             }
00986 #endif
00987             break;
00988 
00989          case SDL_KEYUP:   /* ignore, for the time being */
00990             break;
00991 
00992          case SDL_KEYDOWN:
00993             handle_keyboard_input(env, &ev[i].key.keysym);
00994             break;
00995 
00996          case SDL_MOUSEMOTION:
00997          case SDL_MOUSEBUTTONUP:
00998             if (drag->drag_window == DRAG_LOCAL && env->out.device_num) {
00999                /* move the capture source */
01000                int dx = compute_drag(&drag->x_start, ev[i].motion.x, 3);
01001                int dy = compute_drag(&drag->y_start, ev[i].motion.y, 3);
01002                grabber_move(&env->out.devices[env->out.device_primary], dx, dy);
01003             } else if (drag->drag_window == DRAG_PIP) {
01004                /* move the PiP image inside the frames of the enc_in buffers */
01005                int dx = ev[i].motion.x - drag->x_start;
01006                int dy = ev[i].motion.y - drag->y_start;
01007                /* dx and dy value are directly applied to env->out.pip_x and
01008                env->out.pip_y, so they must work as if the format was cif */
01009                dx = (double)dx*env->enc_in.w/env->loc_dpy.w;
01010                dy = (double)dy*env->enc_in.h/env->loc_dpy.h;
01011                /* sets starts to a new value */
01012                drag->x_start = ev[i].motion.x;
01013                drag->y_start = ev[i].motion.y;
01014                /* ast_log(LOG_WARNING, "moving: %d, %d\n", dx, dy); */
01015                pip_move(env, dx, dy);
01016             } else if (drag->drag_window == DRAG_MESSAGE) {
01017                /* scroll up/down the window */
01018                int dy = compute_drag(&drag->y_start, ev[i].motion.y, 1);
01019                move_message_board(gui->bd_msg, dy);
01020             }
01021             if (ev[i].type == SDL_MOUSEBUTTONUP)
01022                drag->drag_window = DRAG_NONE;
01023             break;
01024          case SDL_MOUSEBUTTONDOWN:
01025             handle_mousedown(env, ev[i].button);
01026             break;
01027          }
01028       }
01029    }
01030    if (1) {
01031       struct timeval b, a = ast_tvnow();
01032       int i;
01033       //SDL_Lock_EventThread();
01034       SDL_PumpEvents();
01035       b = ast_tvnow();
01036       i = ast_tvdiff_ms(b, a);
01037       if (i > 3)
01038          fprintf(stderr, "-------- SDL_PumpEvents took %dms\n", i);
01039       //SDL_Unlock_EventThread();
01040    }
01041 }
01042 
01043 static SDL_Surface *load_image(const char *file)
01044 {
01045    SDL_Surface *temp;
01046  
01047 #ifdef HAVE_SDL_IMAGE
01048    temp = IMG_Load(file);
01049 #else
01050    temp = SDL_LoadBMP(file);
01051 #endif
01052    if (temp == NULL)
01053       fprintf(stderr, "Unable to load image %s: %s\n",
01054          file, SDL_GetError());
01055    return temp;
01056 }
01057 
01058 static void keypad_setup(struct gui_info *gui, const char *kp_file);
01059 
01060 /* TODO: consistency checks, check for bpp, widht and height */
01061 /* Init the mask image used to grab the action. */
01062 static struct gui_info *gui_init(const char *keypad_file, const char *font)
01063 {
01064    struct gui_info *gui = ast_calloc(1, sizeof(*gui));
01065 
01066    if (gui == NULL)
01067       return NULL;
01068    /* initialize keypad status */
01069    gui->kb_output = KO_MESSAGE;  /* XXX temp */
01070    gui->drag.drag_window = DRAG_NONE;
01071    gui->outfd = -1;
01072 
01073    keypad_setup(gui, keypad_file);
01074    if (gui->keypad == NULL)   /* no keypad, we are done */
01075       return gui;
01076    /* XXX load image */
01077    if (!ast_strlen_zero(font)) {
01078       int i;
01079       SDL_Rect *r;
01080 
01081       gui->font = load_image(font);
01082       if (!gui->font) {
01083          ast_log(LOG_WARNING, "Unable to load font %s, no output available\n", font);
01084          goto error;
01085       }
01086       ast_log(LOG_WARNING, "Loaded font %s\n", font);
01087       /* XXX hardwired constants - 3 rows of 32 chars */
01088       r = gui->font_rects;
01089 #define FONT_H 20
01090 #define FONT_W 9
01091       for (i = 0; i < 96; r++, i++) {
01092                   r->x = (i % 32 ) * FONT_W;
01093                   r->y = (i / 32 ) * FONT_H;
01094                   r->w = FONT_W;
01095                   r->h = FONT_H;
01096       }
01097    }
01098 
01099    gui->outfd = open ("/dev/null", O_WRONLY);   /* discard output, temporary */
01100    if (gui->outfd < 0) {
01101       ast_log(LOG_WARNING, "Unable output fd\n");
01102       goto error;
01103    }
01104    return gui;
01105 
01106 error:
01107    ast_free(gui);
01108    return NULL;
01109 }
01110 
01111 /* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
01112 static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
01113    int w, int h, int x, int y)
01114 {
01115    win->bmp = SDL_CreateYUVOverlay(w, h, fmt, screen);
01116    if (win->bmp == NULL)
01117       return -1;  /* error */
01118    win->rect.x = x;
01119    win->rect.y = y;
01120    win->rect.w = w;
01121    win->rect.h = h;
01122    return 0;
01123 }
01124 
01125 static int keypad_cfg_read(struct gui_info *gui, const char *val);
01126 
01127 static void keypad_setup(struct gui_info *gui, const char *kp_file)
01128 {
01129    FILE *fd;
01130    char buf[1024];
01131    const char region[] = "region";
01132    int reg_len = strlen(region);
01133    int in_comment = 0;
01134 
01135    if (gui->keypad)
01136       return;
01137    gui->keypad = load_image(kp_file);
01138    if (!gui->keypad)
01139       return;
01140    /* now try to read the keymap from the file. */
01141    fd = fopen(kp_file, "r");
01142    if (fd == NULL) {
01143       ast_log(LOG_WARNING, "fail to open %s\n", kp_file);
01144       return;
01145    }
01146    /*
01147     * If the keypad image has a comment field, try to read
01148     * the button location from there. The block must start with
01149     * a comment (or empty) line, and continue with entries like:
01150     * region = token shape x0 y0 x1 y1 h
01151     * ...
01152     * (basically, lines have the same format as config file entries).
01153     * You can add it to a jpeg file using wrjpgcom
01154     */
01155    while (fgets(buf, sizeof(buf), fd)) {
01156       char *s;
01157 
01158       if (!strstr(buf, region)) { /* no keyword yet */
01159          if (!in_comment)  /* still waiting for initial comment block */
01160             continue;
01161          else
01162             break;
01163       }
01164       if (!in_comment) {   /* first keyword, reset previous entries */
01165          keypad_cfg_read(gui, "reset");
01166          in_comment = 1;
01167       }
01168       s = ast_skip_blanks(buf);
01169       ast_trim_blanks(s);
01170       if (memcmp(s, region, reg_len))
01171          break;   /* keyword not found */
01172       s = ast_skip_blanks(s + reg_len); /* space between token and '=' */
01173       if (*s++ != '=')  /* missing separator */
01174          break;
01175       if (*s == '>') /* skip '>' if present */
01176          s++;
01177       keypad_cfg_read(gui, ast_skip_blanks(s));
01178    }
01179    fclose(fd);
01180 }
01181 
01182 struct board *board_setup(SDL_Surface *screen, SDL_Rect *dest,
01183    SDL_Surface *font, SDL_Rect *font_rects);
01184 
01185 /*! \brief initialize the boards we have in the keypad */
01186 static void init_board(struct gui_info *gui, struct board **dst, SDL_Rect *r, int dx, int dy)
01187 {
01188    if (r[0].w == 0 || r[0].h == 0)
01189       return;  /* not available */
01190    r[1] = r[0];   /* copy geometry */
01191    r[1].x += dx;  /* add offset of main window */
01192    r[1].y += dy;
01193    if (*dst == NULL) {  /* initial call */
01194       *dst = board_setup(gui->screen, &r[1], gui->font, gui->font_rects);
01195    } else {
01196       /* call a refresh */
01197    }
01198 }
01199 
01200 #ifdef HAVE_X11
01201 /*
01202  * SDL is not very robust on error handling, so we need to trap ourselves
01203  * at least the most obvious failure conditions, e.g. a bad SDL_WINDOWID.
01204  * As of sdl-1.2.13, SDL_SetVideoMode crashes with bad parameters, so
01205  * we need to do the explicit X calls to make sure the window is correct.
01206  * And around these calls, we must trap X errors.
01207  */
01208 static int my_x_handler(Display *d, XErrorEvent *e)
01209 {
01210    ast_log(LOG_WARNING, "%s error_code %d\n", __FUNCTION__, e->error_code);
01211    return 0;
01212 }
01213 #endif /* HAVE_X11 */
01214 
01215 /*! \brief [re]set the main sdl window, useful in case of resize.
01216  * We can tell the first from subsequent calls from the value of
01217  * env->gui, which is NULL the first time.
01218  */
01219 static void sdl_setup(struct video_desc *env)
01220 {
01221    int dpy_fmt = SDL_IYUV_OVERLAY;  /* YV12 causes flicker in SDL */
01222    int depth, maxw, maxh;
01223    const SDL_VideoInfo *info;
01224    int kp_w = 0, kp_h = 0; /* keypad width and height */
01225    struct gui_info *gui = env->gui;
01226    
01227    /* Some helper variables used for filling the SDL window */
01228    int x0; /* the x coordinate of the center of the keypad */
01229    int x1; /* userful for calculating of the size of the parent window */
01230    int y0; /* y coordinate of the keypad, the remote window and the local window */
01231    int src_wins_tot_w; /* total width of the source windows */
01232    int i;
01233    int x; /* useful for the creation of the source windows; */
01234    
01235 #ifdef HAVE_X11
01236    const char *e = getenv("SDL_WINDOWID");
01237 
01238    if (!ast_strlen_zero(e)) {
01239       XWindowAttributes a;
01240       int (*old_x_handler)(Display *d, XErrorEvent *e) = XSetErrorHandler(my_x_handler);
01241       Display *d = XOpenDisplay(getenv("DISPLAY"));
01242       long w = atol(e);
01243       int success = w ? XGetWindowAttributes(d, w, &a) : 0;
01244 
01245       XSetErrorHandler(old_x_handler);
01246       if (!success) {
01247          ast_log(LOG_WARNING, "%s error in window\n", __FUNCTION__);
01248          return;
01249       }
01250    }  
01251 #endif
01252    /*
01253     * initialize the SDL environment. We have one large window
01254     * with local and remote video, and a keypad.
01255     * At the moment we arrange them statically, as follows:
01256     * - top row: thumbnails for local video sources;
01257     * - next row: message boards for local video sources
01258     * - on the left, the remote video;
01259     * - on the center, the keypad
01260     * - on the right, the local video
01261     * We need to read in the skin for the keypad before creating the main
01262     * SDL window, because the size is only known here.
01263     */
01264 
01265    if (gui == NULL && SDL_Init(SDL_INIT_VIDEO)) {
01266       ast_log(LOG_WARNING, "Could not initialize SDL - %s\n",
01267                         SDL_GetError());
01268                 /* again not fatal, just we won't display anything */
01269       return;
01270    }
01271    info = SDL_GetVideoInfo();
01272    /* We want at least 16bpp to support YUV overlays.
01273     * E.g with SDL_VIDEODRIVER = aalib the default is 8
01274     */
01275    if (!info || !info->vfmt) {
01276       ast_log(LOG_WARNING, "Bad SDL_GetVideoInfo - %s\n",
01277                         SDL_GetError());
01278       return;
01279    }
01280    depth = info->vfmt->BitsPerPixel;
01281    if (depth < 16)
01282       depth = 16;
01283    if (!gui)
01284       env->gui = gui = gui_init(env->keypad_file, env->keypad_font);
01285    if (!gui)
01286       goto no_sdl;
01287 
01288    if (gui->keypad) {
01289       if (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) {
01290          kp_w = gui->kp_rect.w;
01291          kp_h = gui->kp_rect.h;
01292       } else {
01293          kp_w = gui->keypad->w;
01294          kp_h = gui->keypad->h;
01295       }
01296    }
01297    
01298    /* total width of the thumbnails */
01299    src_wins_tot_w = env->out.device_num*(SRC_WIN_W+BORDER)+BORDER;
01300    
01301    /* x coordinate of the center of the keypad */
01302    x0 = MAX(env->rem_dpy.w+kp_w/2+2*BORDER, src_wins_tot_w/2);
01303    
01304    /* from center of the keypad to right border */
01305    x1 = MAX(env->loc_dpy.w+kp_w/2+2*BORDER, src_wins_tot_w/2);
01306    
01307    /* total width of the SDL window to create */
01308    maxw = x0+x1;
01309    
01310    /* total height of the mother window to create */
01311    maxh = MAX( MAX(env->rem_dpy.h, env->loc_dpy.h), kp_h)+2*BORDER;
01312    maxh += env->out.device_num ? (2*BORDER+SRC_WIN_H+SRC_MSG_BD_H) : 0;
01313    
01314    gui->screen = SDL_SetVideoMode(maxw, maxh, depth, 0);
01315    if (!gui->screen) {
01316       ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
01317       goto no_sdl;
01318    }
01319 
01320 #ifdef HAVE_X11
01321    /*
01322     * Annoying as it may be, if SDL_WINDOWID is set, SDL does
01323     * not grab keyboard/mouse events or expose or other stuff,
01324     * and it does not handle resize either.
01325     * So we need to implement workarounds here.
01326     */
01327     do {
01328    /* First, handle the event mask */
01329    XWindowAttributes attr;
01330         long want;
01331         SDL_SysWMinfo info;
01332    Display *SDL_Display;
01333         Window win;
01334 
01335    const char *e = getenv("SDL_WINDOWID");
01336    if (ast_strlen_zero(e))  /* no external window, don't bother doing this */
01337       break;
01338         SDL_VERSION(&info.version); /* it is important to set the version */
01339         if (SDL_GetWMInfo(&info) != 1) {
01340                 fprintf(stderr, "no wm info\n");
01341                 break;
01342         }
01343    SDL_Display = info.info.x11.display;
01344    if (SDL_Display == NULL)
01345       break;
01346         win = info.info.x11.window;
01347 
01348    /*
01349     * A list of events we want.
01350     * Leave ResizeRedirectMask to the parent.
01351     */
01352         want = KeyPressMask | KeyReleaseMask | ButtonPressMask |
01353                            ButtonReleaseMask | EnterWindowMask |
01354                            LeaveWindowMask | PointerMotionMask |
01355                            Button1MotionMask |
01356                            Button2MotionMask | Button3MotionMask |
01357                            Button4MotionMask | Button5MotionMask |
01358                            ButtonMotionMask | KeymapStateMask |
01359                            ExposureMask | VisibilityChangeMask |
01360                            StructureNotifyMask | /* ResizeRedirectMask | */
01361                            SubstructureNotifyMask | SubstructureRedirectMask |
01362                            FocusChangeMask | PropertyChangeMask |
01363                            ColormapChangeMask | OwnerGrabButtonMask;
01364 
01365         memset(&attr, '\0', sizeof(attr));
01366    XGetWindowAttributes(SDL_Display, win, &attr);
01367 
01368    /* the following events can be delivered only to one client.
01369     * So check which ones are going to someone else, and drop
01370     * them from our request.
01371     */
01372    {
01373    /* ev are the events for a single recipient */
01374    long ev = ButtonPressMask | ResizeRedirectMask |
01375          SubstructureRedirectMask;
01376         ev &= (attr.all_event_masks & ~attr.your_event_mask);
01377    /* now ev contains 1 for single-recipient events owned by others.
01378     * We must clear those bits in 'want'
01379     * and then add the bits in 'attr.your_event_mask' to 'want'
01380     */
01381    want &= ~ev;
01382    want |= attr.your_event_mask;
01383    }
01384    XSelectInput(SDL_Display, win, want);
01385 
01386    /* Second, handle resize.
01387     * We do part of the things that X11Resize does,
01388     * but also generate a ConfigureNotify event so
01389     * the owner of the window has a chance to do something
01390     * with it.
01391     */
01392    XResizeWindow(SDL_Display, win, maxw, maxh);
01393    {
01394    XConfigureEvent ce = {
01395       .type = ConfigureNotify,
01396       .serial = 0,
01397       .send_event = 1,  /* TRUE */
01398       .display = SDL_Display,
01399       .event = win,
01400       .window = win,
01401       .x = 0,
01402       .y = 0,
01403       .width = maxw,
01404       .height = maxh,
01405       .border_width = 0,
01406       .above = 0,
01407       .override_redirect = 0 };
01408    XSendEvent(SDL_Display, win, 1 /* TRUE */, StructureNotifyMask, (XEvent *)&ce);
01409    }
01410     } while (0);
01411 #endif /* HAVE_X11 */
01412 
01413    y0 = env->out.device_num ? (3*BORDER+SRC_WIN_H+SRC_MSG_BD_H) : BORDER;
01414    
01415    SDL_WM_SetCaption("Asterisk console Video Output", NULL);
01416    
01417    /* intialize the windows for local and remote video */
01418    if (set_win(gui->screen, &gui->win[WIN_REMOTE], dpy_fmt,
01419          env->rem_dpy.w, env->rem_dpy.h, x0-kp_w/2-BORDER-env->rem_dpy.w, y0))
01420       goto no_sdl;
01421    /* unfreeze incoming frames if set (to avoid showing nothing) */
01422    env->frame_freeze = 0;
01423 
01424    if (set_win(gui->screen, &gui->win[WIN_LOCAL], dpy_fmt,
01425          env->loc_dpy.w, env->loc_dpy.h,
01426          x0+kp_w/2+BORDER, y0))
01427       goto no_sdl;
01428    
01429    /* initialize device_num source windows (thumbnails) and boards
01430    (for a maximum of 9 additional windows and boards) */
01431    x = x0 - src_wins_tot_w/2 + BORDER;
01432    for (i = 0; i < env->out.device_num; i++){
01433       struct thumb_bd *p = &gui->thumb_bd_array[i];
01434       if (set_win(gui->screen, &gui->win[i+WIN_SRC1], dpy_fmt,
01435          SRC_WIN_W, SRC_WIN_H, x+i*(BORDER+SRC_WIN_W), BORDER))
01436          goto no_sdl;
01437       /* set geometry for the rect for the message board of the device */
01438       p->rect.w = SRC_WIN_W;
01439       p->rect.h = SRC_MSG_BD_H;
01440       p->rect.x = x+i*(BORDER+SRC_WIN_W);
01441       p->rect.y = 2*BORDER+SRC_WIN_H;
01442       /* the white color is used as background */
01443       SDL_FillRect(gui->screen, &p->rect,
01444          SDL_MapRGB(gui->screen->format, 255, 255, 255));
01445       /* if necessary, initialize boards for the sources */
01446       if (!p->board)
01447          p->board =
01448             board_setup(gui->screen, &p->rect,
01449             gui->font, gui->font_rects);
01450       /* update board rect */
01451       SDL_UpdateRect(gui->screen, p->rect.x, p->rect.y, p->rect.w, p->rect.h);
01452    }
01453 
01454    /* display the skin, but do not free it as we need it later to
01455    restore text areas and maybe sliders too */
01456    if (gui->keypad) {
01457       struct SDL_Rect *dest = &gui->win[WIN_KEYPAD].rect;
01458       struct SDL_Rect *src = (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) ? & gui->kp_rect : NULL;
01459       /* set the coordinates of the keypad relative to the main screen */
01460       dest->x = x0-kp_w/2;
01461       dest->y = y0;
01462       dest->w = kp_w;
01463       dest->h = kp_h;
01464       SDL_BlitSurface(gui->keypad, src, gui->screen, dest);
01465       init_board(gui, &gui->bd_msg, gui->kp_msg, dest->x, dest->y);
01466       init_board(gui, &gui->bd_dialed, gui->kp_dialed, dest->x, dest->y);
01467       SDL_UpdateRects(gui->screen, 1, dest);
01468    }
01469    return;
01470 
01471 no_sdl:
01472    /* free resources in case of errors */
01473    env->gui = cleanup_sdl(gui, env->out.device_num);
01474 }
01475 
01476 /*
01477  * Functions to determine if a point is within a region. Return 1 if success.
01478  * First rotate the point, with
01479  * x' =  (x - x0) * cos A + (y - y0) * sin A
01480  * y' = -(x - x0) * sin A + (y - y0) * cos A
01481  * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
01482  * l = sqrt( (x1-x0)^2 + (y1-y0)^2
01483  * Then determine inclusion by simple comparisons i.e.:
01484  * rectangle: x >= 0 && x < l && y >= 0 && y < h
01485  * ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
01486  */
01487 static int kp_match_area(const struct keypad_entry *e, int x, int y)
01488 {
01489    double xp, dx = (e->x1 - e->x0);
01490    double yp, dy = (e->y1 - e->y0);
01491    double l = sqrt(dx*dx + dy*dy);
01492    int ret = 0;
01493 
01494    if (l > 1) { /* large enough */
01495       xp = ((x - e->x0)*dx + (y - e->y0)*dy)/l;
01496       yp = (-(x - e->x0)*dy + (y - e->y0)*dx)/l;
01497       if (e->type == KP_RECT) {
01498          ret = (xp >= 0 && xp < l && yp >=0 && yp < e->h);
01499       } else if (e->type == KP_CIRCLE) {
01500          dx = xp*xp/(l*l) + yp*yp/(e->h*e->h);
01501          ret = (dx < 1);
01502       }
01503    }
01504 #if 0
01505    ast_log(LOG_WARNING, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
01506       ret, e->c, x, y, e->type, e->x0, e->y0, e->x1, e->y1, e->h);
01507 #endif
01508    return ret;
01509 }
01510 
01511 struct _s_k { const char *s; int k; };
01512 static const struct _s_k gui_key_map[] = {
01513    {"FREEZE",  KEY_FREEZE},
01514    {"PIP",     KEY_PIP},
01515    {"PICK_UP", KEY_PICK_UP },
01516    {"PICKUP",  KEY_PICK_UP },
01517         {"HANG_UP",  KEY_HANG_UP },
01518         {"HANGUP",   KEY_HANG_UP },
01519         {"MUTE",  KEY_MUTE },
01520         {"FLASH", KEY_FLASH },
01521         {"AUTOANSWER",  KEY_AUTOANSWER },
01522         {"SENDVIDEO",   KEY_SENDVIDEO },
01523         {"LOCALVIDEO",  KEY_LOCALVIDEO },
01524         {"REMOTEVIDEO", KEY_REMOTEVIDEO },
01525         {"GUI_CLOSE",   KEY_GUI_CLOSE },
01526         {"MESSAGEBOARD",   KEY_MESSAGEBOARD },
01527         {"DIALEDBOARD", KEY_DIALEDBOARD },
01528         {"EDITBOARD",   KEY_EDITBOARD },
01529         {"KEYPAD",   KEY_KEYPAD },  /* x0 y0 w h - active area of the keypad */
01530         {"MESSAGE",  KEY_MESSAGE }, /* x0 y0 w h - incoming messages */
01531         {"DIALED",   KEY_DIALED },  /* x0 y0 w h - dialed number */
01532         {"EDIT",  KEY_EDIT }, /* x0 y0 w h - edit user input */
01533         {"FONT",  KEY_FONT }, /* x0 yo w h rows cols - location and format of the font */
01534         {NULL, 0 } };
01535 
01536 static int gui_map_token(const char *s)
01537 {
01538    /* map the string into token to be returned */
01539    int i = atoi(s);
01540    struct _s_k *p;
01541    if (i > 0 || s[1] == '\0') /* numbers or single characters */
01542       return (i > 9) ? i : s[0];
01543    for (p = gui_key_map; p->s; p++) {
01544       if (!strcasecmp(p->s, s))
01545          return p->k;
01546    }
01547    return KEY_NONE;  /* not found */
01548 }
01549 
01550 /*! \brief read a keypad entry line in the format
01551  * reset
01552  * token circle xc yc diameter
01553  * token circle xc yc x1 y1 h # ellipse, main diameter and height
01554  * token rect x0 y0 x1 y1 h   # rectangle with main side and eight
01555  * token x0 y0 w h         # horizontal rectangle (short format)
01556  *             # this is used e.g. for message boards
01557  * token is the token to be returned, either a character or a symbol
01558  * as KEY_* above
01559  * Return 1 on success, 0 on error.
01560  */
01561 static int keypad_cfg_read(struct gui_info *gui, const char *val)
01562 {
01563    struct keypad_entry e;
01564    SDL_Rect *r = NULL;
01565    char s1[16], s2[16];
01566    int i, ret = 0; /* default, error */
01567 
01568    if (gui == NULL || val == NULL)
01569       return 0;
01570 
01571    s1[0] = s2[0] = '\0';
01572    memset(&e, '\0', sizeof(e));
01573    i = sscanf(val, "%14s %14s %d %d %d %d %d",
01574                 s1, s2, &e.x0, &e.y0, &e.x1, &e.y1, &e.h);
01575 
01576    e.c = gui_map_token(s1);
01577    if (e.c == KEY_NONE)
01578       return 0;   /* nothing found */
01579    switch (i) {
01580    default:
01581       break;
01582    case 1:  /* only "reset" is allowed */
01583       if (e.c != KEY_RESET)
01584          break;
01585       if (gui->kp)
01586          gui->kp_used = 0;
01587       break;
01588    case 5:
01589       if (e.c == KEY_KEYPAD)  /* active keypad area */
01590          r = &gui->kp_rect;
01591       else if (e.c == KEY_MESSAGE)
01592          r = gui->kp_msg;
01593       else if (e.c == KEY_DIALED)
01594          r = gui->kp_dialed;
01595       else if (e.c == KEY_EDIT)
01596          r = gui->kp_edit;
01597       if (r) {
01598          r->x = atoi(s2);  /* this becomes x0 */
01599          r->y = e.x0;      /* this becomes y0 */
01600          r->w = e.y0;      /* this becomes w  */
01601          r->h = e.x1;      /* this becomes h  */
01602          break;
01603       }
01604       if (strcasecmp(s2, "circle")) /* invalid */
01605          break;
01606       /* token circle xc yc diameter */
01607       e.h = e.x1;
01608       e.y1 = e.y0;   /* map radius in x1 y1 */
01609       e.x1 = e.x0 + e.h;   /* map radius in x1 y1 */
01610       e.x0 = e.x0 - e.h;   /* map radius in x1 y1 */
01611       /* fallthrough */
01612 
01613    case 7:
01614       if (e.c == KEY_FONT) {  /* font - x0 y0 w h rows cols */
01615          ast_log(LOG_WARNING, "font not supported yet\n");
01616          break;
01617       }
01618       /* token circle|rect x0 y0 x1 y1 h */
01619       if (e.x1 < e.x0 || e.h <= 0) {
01620          ast_log(LOG_WARNING, "error in coordinates\n");
01621          e.type = 0;
01622          break;
01623       }
01624       if (!strcasecmp(s2, "circle")) {
01625          /* for a circle we specify the diameter but store center and radii */
01626          e.type = KP_CIRCLE;
01627          e.x0 = (e.x1 + e.x0) / 2;
01628          e.y0 = (e.y1 + e.y0) / 2;
01629          e.h = e.h / 2;
01630       } else if (!strcasecmp(s2, "rect")) {
01631          e.type = KP_RECT;
01632       } else
01633          break;
01634       ret = 1;
01635    }
01636    // ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
01637    if (ret == 0)
01638       return 0;
01639    if (gui->kp_size == 0) {
01640       gui->kp = ast_calloc(10, sizeof(e));
01641       if (gui->kp == NULL) {
01642          ast_log(LOG_WARNING, "cannot allocate kp\n");
01643          return 0;
01644       }
01645       gui->kp_size = 10;
01646    }
01647    if (gui->kp_size == gui->kp_used) { /* must allocate */
01648       struct keypad_entry *a = ast_realloc(gui->kp, sizeof(e)*(gui->kp_size+10));
01649       if (a == NULL) {
01650          ast_log(LOG_WARNING, "cannot reallocate kp\n");
01651          return 0;
01652       }
01653       gui->kp = a;
01654       gui->kp_size += 10;
01655    }
01656    if (gui->kp_size == gui->kp_used)
01657       return 0;
01658    gui->kp[gui->kp_used++] = e;
01659    // ast_log(LOG_WARNING, "now %d regions\n", gui->kp_used);
01660    return 1;
01661 }
01662 #endif   /* HAVE_SDL */

Generated on Thu Apr 16 06:27:32 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6