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

Generated on Fri Jun 19 12:09:42 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7