diff options
Diffstat (limited to 'channels/console_gui.c')
-rw-r--r-- | channels/console_gui.c | 580 |
1 files changed, 517 insertions, 63 deletions
diff --git a/channels/console_gui.c b/channels/console_gui.c index 699e6b1fa..a228c3ce7 100644 --- a/channels/console_gui.c +++ b/channels/console_gui.c @@ -7,30 +7,72 @@ /* * GUI layout, structure and management -For the GUI we use SDL to create a large surface (gui->screen) -containing tree sections: remote video on the left, local video -on the right, and the keypad with all controls and text windows -in the center. -The central section is built using an image for the skin, fonts and -other GUI elements. Comments embedded in the image to indicate to -what function each area is mapped to. +For the GUI we use SDL to create a large surface (gui->screen) with 4 areas: +remote video on the left, local video on the right, keypad with all controls +and text windows in the center, and source device thumbnails on the top. +The top row is not displayed if no devices are specified in the config file. + + ________________________________________________________________ + | ______ ______ ______ ______ ______ ______ ______ | + | | tn.1 | | tn.2 | | tn.3 | | tn.4 | | tn.5 | | tn.6 | | tn.7 | | + | |______| |______| |______| |______| |______| |______| |______| | + | ______ ______ ______ ______ ______ ______ ______ | + | |______| |______| |______| |______| |______| |______| |______| | + | _________________ __________________ _________________ | + | | | | | | | | + | | | | | | | | + | | | | | | | | + | | remote video | | | | local video | | + | | | | | | ______ | | + | | | | keypad | | | PIP || | + | | | | | | |______|| | + | |_________________| | | |_________________| | + | | | | + | | | | + | |__________________| | + |________________________________________________________________| + + +The central section is built using an image (jpg, png, maybe gif too) +for the skin, and other GUI elements. Comments embedded in the image +indicate to what function each area is mapped to. +Another image (png with transparency) is used for the font. Mouse and keyboard events are detected on the whole surface, and handled differently according to their location: - +- center/right click on the local/remote window are used to resize + the corresponding window; +- clicks on the thumbnail start/stop sources and select them as + primary or secondary video sources; - drag on the local video window are used to move the captured - area (in the case of X11 grabber) or the picture-in-picture - location (in case of camera included on the X11 grab). -- click on the keypad are mapped to the corresponding key; + area (in the case of X11 grabber) or the picture-in-picture position; +- keystrokes on the keypad are mapped to the corresponding key; + keystrokes are used as keypad functions, or as text input + if we are in text-input mode. - drag on some keypad areas (sliders etc.) are mapped to the corresponding functions; -- keystrokes are used as keypad functions, or as text input - if we are in text-input mode. Configuration options control the appeareance of the gui: - keypad = /tmp/phone.jpg ; the skin - keypad_font = /tmp/font.ttf ; the font to use for output (XXX deprecated) + keypad = /tmp/kpad2.jpg ; the skin + keypad_font = /tmp/font.png ; the font to use for output + +For future implementation, intresting features can be the following: +- freeze of the video coming from our remote party +- save of the whole SDL window as a picture +- oudio output device switching + +The audio switching feature should allow changing the device +or switching to a recorded message for audio sent to remote party. +The selection of the device should happen clicking on a marker in the layout. +For this reason above the thumbnails row in the layout we would like a new row, +the elements composing the row could be message boards, reporting the name of the +device or the path of the message to be played. + +For video input freeze and entire window capture, we define 2 new key types, +those should be activated pressing the buttons on the keypad, associated with +new regions inside the keypad pictureas comments + * */ @@ -42,13 +84,15 @@ Configuration options control the appeareance of the gui: #include "asterisk/utils.h" /* ast_calloc and ast_realloc */ #include <math.h> /* sqrt */ -/* We use 3 'windows' in the GUI */ -enum { WIN_LOCAL, WIN_REMOTE, WIN_KEYPAD, WIN_MAX }; +/* We use a maximum of 12 'windows' in the GUI */ +enum { WIN_LOCAL, WIN_REMOTE, WIN_KEYPAD, WIN_SRC1, + WIN_SRC2, WIN_SRC3, WIN_SRC4, WIN_SRC5, + WIN_SRC6, WIN_SRC7, WIN_SRC8, WIN_SRC9, WIN_MAX }; #ifndef HAVE_SDL /* stubs if we don't have any sdl */ static void show_frame(struct video_desc *env, int out) {} static void sdl_setup(struct video_desc *env) {} -static struct gui_info *cleanup_sdl(struct gui_info *gui) { return NULL; } +static struct gui_info *cleanup_sdl(struct gui_info* g, int n) { return NULL; } static void eventhandler(struct video_desc *env, const char *caption) {} static int keypad_cfg_read(struct gui_info *gui, const char *val) { return 0; } @@ -65,6 +109,9 @@ static int keypad_cfg_read(struct gui_info *gui, const char *val) { return 0; } #include <X11/Xlib.h> #endif +#define BORDER 5 /* border around our windows */ +#define SRC_MSG_BD_H 20 /* height of the message board below those windows */ + enum kp_type { KP_NONE, KP_RECT, KP_CIRCLE }; struct keypad_entry { int c; /* corresponding character */ @@ -75,11 +122,18 @@ struct keypad_entry { /* our representation of a displayed window. SDL can only do one main * window so we map everything within that one */ -struct display_window { +struct display_window { SDL_Overlay *bmp; SDL_Rect rect; /* location of the window */ }; +/* each thumbnail message board has a rectangle associated for the geometry, + * and a board structure, we include these two elements in a singole structure */ +struct thumb_bd { + SDL_Rect rect; /* the rect for geometry and background */ + struct board *board; /* the board */ +}; + struct gui_info { enum kb_output kb_output; /* where the keyboard output goes */ struct drag_info drag; /* info on the window are we dragging */ @@ -92,9 +146,11 @@ struct gui_info { SDL_Surface *font; /* font to be used */ SDL_Rect font_rects[96]; /* only printable chars */ - /* each board has two rectangles, + /* each of the following board has two rectangles, * [0] is the geometry relative to the keypad, * [1] is the geometry relative to the whole screen + * we do not use the thumb_bd for these boards because here we need + * 2 rectangles for geometry */ SDL_Rect kp_msg[2]; /* incoming msg, relative to kpad */ struct board *bd_msg; @@ -105,6 +161,12 @@ struct gui_info { SDL_Rect kp_dialed[2]; /* dialed number */ struct board *bd_dialed; + /* other boards are one associated with the source windows + * above the keypad in the layout, we only have the geometry + * relative to the whole screen + */ + struct thumb_bd thumb_bd_array[MAX_VIDEO_SOURCES]; + /* variable-size array mapping keypad regions to functions */ int kp_size, kp_used; struct keypad_entry *kp; @@ -115,7 +177,7 @@ struct gui_info { /*! \brief free the resources in struct gui_info and the descriptor itself. * Return NULL so we can assign the value back to the descriptor in case. */ -static struct gui_info *cleanup_sdl(struct gui_info *gui) +static struct gui_info *cleanup_sdl(struct gui_info *gui, int device_num) { int i; @@ -142,11 +204,43 @@ static struct gui_info *cleanup_sdl(struct gui_info *gui) SDL_FreeYUVOverlay(gui->win[i].bmp); } bzero(gui, sizeof(gui)); + + /* deallocates the space allocated for the keypad message boards */ + if (gui->bd_dialed) + delete_board(gui->bd_dialed); + if (gui->bd_msg) + delete_board(gui->bd_msg); + + /* deallocates the space allocated for the thumbnail message boards */ + for (i = 0; i < device_num; i++) { + if (gui->thumb_bd_array[i].board) /* may be useless */ + delete_board(gui->thumb_bd_array[i].board); + } + ast_free(gui); SDL_Quit(); return NULL; } +/* messages to be displayed in the sources message boards + * below the source windows + */ + +/* costants defined to describe status of devices */ +#define IS_PRIMARY 1 +#define IS_SECONDARY 2 +#define IS_ON 4 + +char* src_msgs[] = { + " OFF", + "1 OFF", + " 2 OFF", + "1+2 OFF", + " ON", + "1 ON", + " 2 ON", + "1+2 ON", +}; /* * Display video frames (from local or remote stream) using the SDL library. * - Set the video mode to use the resolution specified by the codec context @@ -171,7 +265,7 @@ static void show_frame(struct video_desc *env, int out) b_in = &env->enc_in; b_out = &env->loc_dpy; p_in = NULL; - } else { + } else if (out == WIN_REMOTE) { /* copy input format from the decoding context */ AVCodecContext *c; if (env->in == NULL) /* XXX should not happen - decoder not ready */ @@ -184,7 +278,14 @@ static void show_frame(struct video_desc *env, int out) b_out = &env->rem_dpy; p_in = (AVPicture *)env->in->d_frame; - } + } else { + int i = out-WIN_SRC1; + b_in = env->out.devices[i].dev_buf; + if (b_in == NULL) + return; + p_in = NULL; + b_out = &env->src_dpy[i]; + } bmp = gui->win[out].bmp; SDL_LockYUVOverlay(bmp); /* output picture info - this is sdl, YUV420P */ @@ -236,6 +337,21 @@ enum skin_area { KEY_DIALED = 203, /* area for dialed numbers */ KEY_EDIT = 204, /* area for editing user input */ +#ifdef notyet /* XXX for future implementation */ + KEY_AUDIO_SRCS = 210, + /*indexes between 210 and 219 (or more) have been reserved for the "keys" + associated with the audio device markers, clicking on these markers + will change the source device for audio output */ + + KEY_FREEZE = 220, /* freeze the incoming video */ + KEY_CAPTURE = 221, /* capture the whole SDL window as a picture */ +#endif + /* video source switching key(s) */ + KEY_PIP = 230, + /*indexes between 231 and 239 have been reserved for the "keys" + associated with the device thumbnails, clicking on these pictures + will change the source device for primary or secondary (PiP) video output*/ + KEY_SRCS_WIN = 231, /* till 239 */ /* areas outside the keypad - simulated */ KEY_OUT_OF_KEYPAD = 241, KEY_REM_DPY = 242, @@ -270,15 +386,16 @@ static char *keypad_toggle(struct video_desc *env, int index) ast_log(LOG_WARNING, "keypad_toggle(%i) called\n", index); switch (index) { - case KEY_SENDVIDEO: + case KEY_SENDVIDEO: /* send or do not send video */ env->out.sendvideo = !env->out.sendvideo; break; -#ifdef notyet - case KEY_MUTE: { - struct chan_oss_pvt *o = find_desc(oss_active); - o->mute = !o->mute; - } + case KEY_PIP: /* enable or disable Picture in Picture */ + env->out.picture_in_picture = !env->out.picture_in_picture; + break; + case KEY_MUTE: /* send or do not send audio */ + ast_cli_command(env->gui->outfd, "console mute toggle"); break; +#ifdef notyet case KEY_AUTOANSWER: { struct chan_oss_pvt *o = find_desc(oss_active); o->autoanswer = !o->autoanswer; @@ -357,6 +474,130 @@ static void set_drag(struct drag_info *drag, int x, int y, enum drag_window win) drag->drag_window = win; } +static int update_device_info(struct video_desc *env, int i) +{ + reset_board(env->gui->thumb_bd_array[i].board); + print_message(env->gui->thumb_bd_array[i].board, + src_msgs[env->out.devices[i].status_index]); + return 0; +} + +/*! \brief Changes the video output (local video) source, controlling if + * it is already using that video device, + * and switching the correct fields of env->out. + * grabbers are always open and saved in the device table. + * The secondary or the primary device can be changed, + * according to the "button" parameter: + * the primary device is changed if button = SDL_BUTTON_LEFT; + * the secondary device is changed if button = not SDL_BUTTON_LEFT; + * + * the correct message boards of the sources are also updated + * with the new status + * + * \param env = pointer to the video environment descriptor + * \param index = index of the device the caller wants to use are primary or secondary device + * \param button = button clicked on the mouse + * + * returns 0 on success, + * returns 1 on error + */ +static int switch_video_out(struct video_desc *env, int index, Uint8 button) +{ + int *p; /* pointer to the index of the device to select */ + + if (index >= env->out.device_num) { + ast_log(LOG_WARNING, "no devices\n"); + return 1; + } + /* select primary or secondary */ + p = (button == SDL_BUTTON_LEFT) ? &env->out.device_primary : + &env->out.device_secondary; + /* controls if the device is already selected */ + if (index == *p) { + ast_log(LOG_WARNING, "device %s already selected\n", env->out.devices[index].name); + return 0; + } + ast_log(LOG_WARNING, "switching to %s...\n", env->out.devices[index].name); + /* already open */ + if (env->out.devices[index].grabber) { + /* we also have to update the messages in the source + message boards below the source windows */ + /* first we update the board of the previous source */ + if (p == &env->out.device_primary) + env->out.devices[*p].status_index &= ~IS_PRIMARY; + else + env->out.devices[*p].status_index &= ~IS_SECONDARY; + update_device_info(env, *p); + /* update the index used as primary or secondary */ + *p = index; + ast_log(LOG_WARNING, "done\n"); + /* then we update the board of the new primary or secondary source */ + if (p == &env->out.device_primary) + env->out.devices[*p].status_index |= IS_PRIMARY; + else + env->out.devices[*p].status_index |= IS_SECONDARY; + update_device_info(env, *p); + return 0; + } + /* device is off, just do nothing */ + ast_log(LOG_WARNING, "device is down\n"); + return 1; +} + +/*! \brief tries to switch the state of a device from on to off or off to on + * we also have to update the status of the device and the correct message board + * + * \param index = the device that must be turned on or off + * \param env = pointer to the video environment descriptor + * + * returns: + * - 0 on falure switching from off to on + * - 1 on success in switching from off to on + * - 2 on success in switching from on to off +*/ +static int turn_on_off(int index, struct video_desc *env) +{ + struct video_device *p = &env->out.devices[index]; + + if (index >= env->out.device_num) { + ast_log(LOG_WARNING, "no devices\n"); + return 0; + } + + if (!p->grabber) { /* device off */ + void *g_data; /* result of grabber_open() */ + struct grab_desc *g; + int i; + + /* see if the device can be used by one of the existing drivers */ + for (i = 0; (g = console_grabbers[i]); i++) { + /* try open the device */ + g_data = g->open(p->name, &env->out.loc_src_geometry, env->out.fps); + if (!g_data) /* no luck, try the next driver */ + continue; + p->grabber = g; + p->grabber_data = g_data; + /* update the status of the source */ + p->status_index |= IS_ON; + /* print the new message in the message board */ + update_device_info(env, index); + return 1; /* open succeded */ + } + return 0; /* failure */ + } else { + /* the grabber must be closed */ + p->grabber_data = p->grabber->close(p->grabber_data); + p->grabber = NULL; + /* dev_buf is already freed by grabber->close() */ + p->dev_buf = NULL; + /* update the status of the source */ + p->status_index &= ~IS_ON; + /* print the new message in the message board */ + update_device_info(env, index); + return 2; /* closed */ + } +} + /* * Handle SDL_MOUSEBUTTONDOWN type, finding the palette * index value and calling the right callback. @@ -367,29 +608,83 @@ static void handle_mousedown(struct video_desc *env, SDL_MouseButtonEvent button { uint8_t index = KEY_OUT_OF_KEYPAD; /* the key or region of the display we clicked on */ struct gui_info *gui = env->gui; + + int i; /* integer variable used as iterator */ + int x; /* integer variable usable as a container */ + + /* total width of source device thumbnails */ + int src_wins_tot_w = env->out.device_num*(SRC_WIN_W+BORDER)+BORDER; + + /* x coordinate of the center of the keypad */ + int x0 = MAX(env->rem_dpy.w+gui->keypad->w/2+2*BORDER, src_wins_tot_w/2); + #if 0 ast_log(LOG_WARNING, "event %d %d have %d/%d regions at %p\n", button.x, button.y, gui->kp_used, gui->kp_size, gui->kp); #endif /* for each mousedown we end previous drag */ gui->drag.drag_window = DRAG_NONE; - + /* define keypad boundary */ - if (button.x < env->rem_dpy.w) - index = KEY_REM_DPY; /* click on remote video */ - else if (button.x > env->rem_dpy.w + gui->keypad->w) - index = KEY_LOC_DPY; /* click on local video */ - else if (button.y > gui->keypad->h) - index = KEY_OUT_OF_KEYPAD; /* click outside the keypad */ - else if (gui->kp) { - int i; - for (i = 0; i < gui->kp_used; i++) { - if (kp_match_area(&gui->kp[i], button.x - env->rem_dpy.w, button.y)) { - index = gui->kp[i].c; - break; + /* XXX this should be extended for clicks on different audio device markers */ + if (button.y >= (env->out.device_num ? SRC_WIN_H+2*BORDER+SRC_MSG_BD_H : 0)) { + /* if control reaches this point this means that the clicked point is + below the row of the additional sources windows*/ + /* adjust the y coordinate as if additional devices windows were not present */ + button.y -= (env->out.device_num ? SRC_WIN_H+2*BORDER+SRC_MSG_BD_H : 0); + if (button.y < BORDER) + index = KEY_OUT_OF_KEYPAD; + else if (button.y >= MAX(MAX(env->rem_dpy.h, env->loc_dpy.h), gui->keypad->h)) + index = KEY_OUT_OF_KEYPAD; + else if (button.x < x0 - gui->keypad->w/2 - BORDER - env->rem_dpy.w) + index = KEY_OUT_OF_KEYPAD; + else if (button.x < x0 - gui->keypad->w/2 - BORDER) + index = KEY_REM_DPY; + else if (button.x < x0 - gui->keypad->w/2) + index = KEY_OUT_OF_KEYPAD; + else if (button.x >= x0 + gui->keypad->w/2 + BORDER + env->loc_dpy.w) + index = KEY_OUT_OF_KEYPAD; + else if (button.x >= x0 + gui->keypad->w/2 + BORDER) + index = KEY_LOC_DPY; + else if (button.x >= x0 + gui->keypad->w/2) + index = KEY_OUT_OF_KEYPAD; + else if (gui->kp) { + /* we have to calculate the first coordinate + inside the keypad before calling the kp_match_area*/ + int x_keypad = button.x - (x0 - gui->keypad->w/2); + /* find the key clicked (if one was clicked) */ + for (i = 0; i < gui->kp_used; i++) { + if (kp_match_area(&gui->kp[i],x_keypad, button.y - BORDER)) { + index = gui->kp[i].c; + break; + } } } + } else if (button.y < BORDER) { + index = KEY_OUT_OF_KEYPAD; + } else { /* we are in the thumbnail area */ + x = x0 - src_wins_tot_w/2 + BORDER; + if (button.y >= BORDER + SRC_WIN_H) + index = KEY_OUT_OF_KEYPAD; + else if (button.x < x) + index = KEY_OUT_OF_KEYPAD; + else if (button.x < x + src_wins_tot_w - BORDER) { + /* note that the additional device windows + are numbered from left to right + starting from 0, with a maximum of 8, the index associated on a click is: + KEY_SRCS_WIN + number_of_the_window */ + for (i = 1; i <= env->out.device_num; i++) { + if (button.x < x+i*(SRC_WIN_W+BORDER)-BORDER) { + index = KEY_SRCS_WIN+i-1; + break; + } else if (button.x < x+i*(SRC_WIN_W+BORDER)) { + index = KEY_OUT_OF_KEYPAD; + break; + } + } + } else + index = KEY_OUT_OF_KEYPAD; } /* exec the function */ @@ -397,6 +692,37 @@ static void handle_mousedown(struct video_desc *env, SDL_MouseButtonEvent button keypad_digit(env, index); return; } + + else if (index >= KEY_SRCS_WIN && index < KEY_SRCS_WIN+env->out.device_num) { + index -= KEY_SRCS_WIN; /* index of the window, equal to the device index in the table */ + /* if one of the additional device windows is clicked with + left or right mouse button, we have to switch to that device */ + if (button.button == SDL_BUTTON_RIGHT || button.button == SDL_BUTTON_LEFT) { + switch_video_out(env, index, button.button); + return; + } + /* turn on or off the devices selectively with other mouse buttons */ + else { + int ret = turn_on_off(index, env); + /* print a message according to what happened */ + if (!ret) + ast_log(LOG_WARNING, "unable to turn on device %s\n", + env->out.devices[index].name); + else if (ret == 1) + ast_log(LOG_WARNING, "device %s changed state to on\n", + env->out.devices[index].name); + else if (ret == 2) + ast_log(LOG_WARNING, "device %s changed state to off\n", + env->out.devices[index].name); + return; + } + } + + /* XXX for future implementation + else if (click on audio source marker) + change audio source device + */ + switch (index) { /* answer/close function */ case KEY_PICK_UP: @@ -407,9 +733,10 @@ static void handle_mousedown(struct video_desc *env, SDL_MouseButtonEvent button break; /* other functions */ - case KEY_MUTE: + case KEY_MUTE: /* send or not send the audio */ case KEY_AUTOANSWER: - case KEY_SENDVIDEO: + case KEY_SENDVIDEO: /* send or not send the video */ + case KEY_PIP: /* activate/deactivate picture in picture mode */ keypad_toggle(env, index); break; @@ -418,6 +745,13 @@ static void handle_mousedown(struct video_desc *env, SDL_MouseButtonEvent button case KEY_REMOTEVIDEO: break; +#ifdef notyet /* XXX for future implementations */ + case KEY_FREEZE: + break + case KEY_CAPTURE: + break; +#endif + case KEY_MESSAGEBOARD: if (button.button == SDL_BUTTON_LEFT) set_drag(&gui->drag, button.x, button.y, DRAG_MESSAGE); @@ -427,8 +761,26 @@ static void handle_mousedown(struct video_desc *env, SDL_MouseButtonEvent button case KEY_LOC_DPY: case KEY_REM_DPY: if (button.button == SDL_BUTTON_LEFT) { - if (index == KEY_LOC_DPY) + /* values used to find the position of the picture in picture (if present) */ + int pip_loc_x = (double)env->out.pip_x/env->enc_in.w * env->loc_dpy.w; + int pip_loc_y = (double)env->out.pip_y/env->enc_in.h * env->loc_dpy.h; + /* check if picture in picture is active and the click was on it */ + if (index == KEY_LOC_DPY && env->out.picture_in_picture && + button.x >= x0+gui->keypad->w/2+BORDER+pip_loc_x && + button.x < x0+gui->keypad->w/2+BORDER+pip_loc_x+env->loc_dpy.w/3 && + button.y >= BORDER+pip_loc_y && + button.y < BORDER+pip_loc_y+env->loc_dpy.h/3) { + /* set the y cordinate to his previous value */ + button.y += (env->out.device_num ? SRC_WIN_H+2*BORDER+SRC_MSG_BD_H : 0); + /* starts dragging the picture inside the picture */ + set_drag(&gui->drag, button.x, button.y, DRAG_PIP); + } + else if (index == KEY_LOC_DPY) { + /* set the y cordinate to his previous value */ + button.y += (env->out.device_num ? SRC_WIN_H+2*BORDER+SRC_MSG_BD_H : 0); + /* click in the local display, but not on the PiP */ set_drag(&gui->drag, button.x, button.y, DRAG_LOCAL); + } break; } else { char buf[128]; @@ -437,9 +789,21 @@ static void handle_mousedown(struct video_desc *env, SDL_MouseButtonEvent button fb->w, fb->h); video_geom(fb, buf); sdl_setup(env); + /* writes messages in the source boards, those can be + modified during the execution, because of the events + this must be done here, otherwise the status of sources will not be + shown after sdl_setup */ + for (i = 0; i < env->out.device_num; i++) { + update_device_info(env, i); + } + /* we also have to refresh other boards, + to avoid messages to disappear after video resize */ + print_message(gui->bd_msg, " \b"); + print_message(gui->bd_dialed, " \b"); } break; case KEY_OUT_OF_KEYPAD: + ast_log(LOG_WARNING, "nothing clicked, coordinates: %d, %d\n", button.x, button.y); break; case KEY_DIGIT_BACKGROUND: @@ -524,7 +888,7 @@ static void handle_keyboard_input(struct video_desc *env, SDL_keysym *ks) return; } -static void grabber_move(struct video_out_desc *, int dx, int dy); +static void grabber_move(struct video_device *, int dx, int dy); int compute_drag(int *start, int end, int magnifier); int compute_drag(int *start, int end, int magnifier) @@ -539,6 +903,33 @@ int compute_drag(int *start, int end, int magnifier) return delta; } +/*! \brief This function moves the picture in picture, + * controlling the limits of the containing buffer + * to avoid problems deriving from going through the limits. + * + * \param env = pointer to the descriptor of the video environment + * \param dx = the variation of the x position + * \param dy = the variation of the y position +*/ +static void pip_move(struct video_desc* env, int dx, int dy) { + int new_pip_x = env->out.pip_x+dx; + int new_pip_y = env->out.pip_y+dy; + /* going beyond the left borders */ + if (new_pip_x < 0) + new_pip_x = 0; + /* going beyond the right borders */ + else if (new_pip_x > env->enc_in.w - env->enc_in.w/3) + new_pip_x = env->enc_in.w - env->enc_in.w/3; + /* going beyond the top borders */ + if (new_pip_y < 0) + new_pip_y = 0; + /* going beyond the bottom borders */ + else if (new_pip_y > env->enc_in.h - env->enc_in.h/3) + new_pip_y = env->enc_in.h - env->enc_in.h/3; + env->out.pip_x = new_pip_x; + env->out.pip_y = new_pip_y; +} + /* * I am seeing some kind of deadlock or stall around * SDL_PumpEvents() while moving the window on a remote X server @@ -593,11 +984,24 @@ static void eventhandler(struct video_desc *env, const char *caption) case SDL_MOUSEMOTION: case SDL_MOUSEBUTTONUP: - if (drag->drag_window == DRAG_LOCAL) { + if (drag->drag_window == DRAG_LOCAL && env->out.device_num) { /* move the capture source */ int dx = compute_drag(&drag->x_start, ev[i].motion.x, 3); int dy = compute_drag(&drag->y_start, ev[i].motion.y, 3); - grabber_move(&env->out, dx, dy); + grabber_move(&env->out.devices[env->out.device_primary], dx, dy); + } else if (drag->drag_window == DRAG_PIP) { + /* move the PiP image inside the frames of the enc_in buffers */ + int dx = ev[i].motion.x - drag->x_start; + int dy = ev[i].motion.y - drag->y_start; + /* dx and dy value are directly applied to env->out.pip_x and + env->out.pip_y, so they must work as if the format was cif */ + dx = (double)dx*env->enc_in.w/env->loc_dpy.w; + dy = (double)dy*env->enc_in.h/env->loc_dpy.h; + /* sets starts to a new value */ + drag->x_start = ev[i].motion.x; + drag->y_start = ev[i].motion.y; + /* ast_log(LOG_WARNING, "moving: %d, %d\n", dx, dy); */ + pip_move(env, dx, dy); } else if (drag->drag_window == DRAG_MESSAGE) { /* scroll up/down the window */ int dy = compute_drag(&drag->y_start, ev[i].motion.y, 1); @@ -808,7 +1212,15 @@ static void sdl_setup(struct video_desc *env) const SDL_VideoInfo *info; int kp_w = 0, kp_h = 0; /* keypad width and height */ struct gui_info *gui = env->gui; - + + /* Some helper variables used for filling the SDL window */ + int x0; /* the x coordinate of the center of the keypad */ + int x1; /* userful for calculating of the size of the parent window */ + int y0; /* y coordinate of the keypad, the remote window and the local window */ + int src_wins_tot_w; /* total width of the source windows */ + int i; + int x; /* useful for the creation of the source windows; */ + #ifdef HAVE_X11 const char *e = getenv("SDL_WINDOWID"); @@ -830,6 +1242,8 @@ static void sdl_setup(struct video_desc *env) * initialize the SDL environment. We have one large window * with local and remote video, and a keypad. * At the moment we arrange them statically, as follows: + * - top row: thumbnails for local video sources; + * - next row: message boards for local video sources * - on the left, the remote video; * - on the center, the keypad * - on the right, the local video @@ -869,13 +1283,23 @@ static void sdl_setup(struct video_desc *env) kp_h = gui->keypad->h; } } - /* XXX same for other boards */ -#define BORDER 5 /* border around our windows */ - maxw = env->rem_dpy.w + env->loc_dpy.w + kp_w; - maxh = MAX( MAX(env->rem_dpy.h, env->loc_dpy.h), kp_h); - maxw += 4 * BORDER; - maxh += 2 * BORDER; - + + /* total width of the thumbnails */ + src_wins_tot_w = env->out.device_num*(SRC_WIN_W+BORDER)+BORDER; + + /* x coordinate of the center of the keypad */ + x0 = MAX(env->rem_dpy.w+kp_w/2+2*BORDER, src_wins_tot_w/2); + + /* from center of the keypad to right border */ + x1 = MAX(env->loc_dpy.w+kp_w/2+2*BORDER, src_wins_tot_w/2); + + /* total width of the SDL window to create */ + maxw = x0+x1; + + /* total height of the mother window to create */ + maxh = MAX( MAX(env->rem_dpy.h, env->loc_dpy.h), kp_h)+2*BORDER; + maxh += env->out.device_num ? (2*BORDER+SRC_WIN_H+SRC_MSG_BD_H) : 0; + gui->screen = SDL_SetVideoMode(maxw, maxh, depth, 0); if (!gui->screen) { ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n"); @@ -974,24 +1398,53 @@ static void sdl_setup(struct video_desc *env) } } while (0); #endif /* HAVE_X11 */ + + y0 = env->out.device_num ? (3*BORDER+SRC_WIN_H+SRC_MSG_BD_H) : BORDER; + SDL_WM_SetCaption("Asterisk console Video Output", NULL); + + /* intialize the windows for local and remote video */ if (set_win(gui->screen, &gui->win[WIN_REMOTE], dpy_fmt, - env->rem_dpy.w, env->rem_dpy.h, BORDER, BORDER)) + env->rem_dpy.w, env->rem_dpy.h, x0-kp_w/2-BORDER-env->rem_dpy.w, y0)) goto no_sdl; if (set_win(gui->screen, &gui->win[WIN_LOCAL], dpy_fmt, env->loc_dpy.w, env->loc_dpy.h, - 3*BORDER+env->rem_dpy.w + kp_w, BORDER)) + x0+kp_w/2+BORDER, y0)) goto no_sdl; + + /* initialize device_num source windows (thumbnails) and boards + (for a maximum of 9 additional windows and boards) */ + x = x0 - src_wins_tot_w/2 + BORDER; + for (i = 0; i < env->out.device_num; i++){ + struct thumb_bd *p = &gui->thumb_bd_array[i]; + if (set_win(gui->screen, &gui->win[i+WIN_SRC1], dpy_fmt, + SRC_WIN_W, SRC_WIN_H, x+i*(BORDER+SRC_WIN_W), BORDER)) + goto no_sdl; + /* set geometry for the rect for the message board of the device */ + p->rect.w = SRC_WIN_W; + p->rect.h = SRC_MSG_BD_H; + p->rect.x = x+i*(BORDER+SRC_WIN_W); + p->rect.y = 2*BORDER+SRC_WIN_H; + /* the white color is used as background */ + SDL_FillRect(gui->screen, &p->rect, + SDL_MapRGB(gui->screen->format, 255, 255, 255)); + /* if necessary, initialize boards for the sources */ + if (!p->board) + p->board = + board_setup(gui->screen, &p->rect, + gui->font, gui->font_rects); + /* update board rect */ + SDL_UpdateRect(gui->screen, p->rect.x, p->rect.y, p->rect.w, p->rect.h); + } /* display the skin, but do not free it as we need it later to - * restore text areas and maybe sliders too. - */ + restore text areas and maybe sliders too */ if (gui->keypad) { struct SDL_Rect *dest = &gui->win[WIN_KEYPAD].rect; struct SDL_Rect *src = (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) ? & gui->kp_rect : NULL; /* set the coordinates of the keypad relative to the main screen */ - dest->x = 2*BORDER + env->rem_dpy.w; - dest->y = BORDER; + dest->x = x0-kp_w/2; + dest->y = y0; dest->w = kp_w; dest->h = kp_h; SDL_BlitSurface(gui->keypad, src, gui->screen, dest); @@ -1003,7 +1456,7 @@ static void sdl_setup(struct video_desc *env) no_sdl: /* free resources in case of errors */ - env->gui = cleanup_sdl(gui); + env->gui = cleanup_sdl(gui, env->out.device_num); } /* @@ -1043,6 +1496,7 @@ static int kp_match_area(const struct keypad_entry *e, int x, int y) struct _s_k { const char *s; int k; }; static struct _s_k gui_key_map[] = { + {"PIP", KEY_PIP}, {"PICK_UP", KEY_PICK_UP }, {"PICKUP", KEY_PICK_UP }, {"HANG_UP", KEY_HANG_UP }, |