mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 18:55:19 +00:00 
			
		
		
		
	git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@97533 65c4cc65-6c06-0410-ace0-fbb531ad65f3
		
			
				
	
	
		
			1037 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1037 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * GUI for console video.
 | |
|  * The routines here are in charge of loading the keypad and handling events.
 | |
|  * $Revision$
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * 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.
 | |
| 
 | |
| Mouse and keyboard events are detected on the whole surface, and
 | |
| handled differently according to their location:
 | |
| 
 | |
| - 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;
 | |
| - 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)
 | |
| 
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "asterisk.h"
 | |
| #include "console_video.h"
 | |
| #include "asterisk/lock.h"
 | |
| #include "asterisk/frame.h"
 | |
| #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 };
 | |
| 
 | |
| #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 void eventhandler(struct video_desc *env, const char *caption)	{}
 | |
| static int keypad_cfg_read(struct gui_info *gui, const char *val)	{ return 0; }
 | |
| 
 | |
| #else /* HAVE_SDL, the real rendering code */
 | |
| 
 | |
| #include <SDL/SDL.h>
 | |
| #ifdef HAVE_SDL_IMAGE
 | |
| #include <SDL/SDL_image.h>      /* for loading images */
 | |
| #endif
 | |
| 
 | |
| enum kp_type { KP_NONE, KP_RECT, KP_CIRCLE };
 | |
| struct keypad_entry {
 | |
|         int c;  /* corresponding character */
 | |
|         int x0, y0, x1, y1, h;  /* arguments */
 | |
|         enum kp_type type;
 | |
| };
 | |
| 
 | |
| /* our representation of a displayed window. SDL can only do one main
 | |
|  * window so we map everything within that one
 | |
|  */
 | |
| struct display_window   {   
 | |
| 	SDL_Overlay	*bmp;
 | |
| 	SDL_Rect	rect;	/* location of the window */
 | |
| };
 | |
| 
 | |
| struct gui_info {
 | |
| 	enum kb_output		kb_output;	/* where the keyboard output goes */
 | |
| 	struct drag_info	drag;		/* info on the window are we dragging */
 | |
| 	/* support for display. */
 | |
| 	SDL_Surface             *screen;	/* the main window */
 | |
| 
 | |
| 	int			outfd;		/* fd for output */
 | |
| 	SDL_Surface		*keypad;	/* the skin for the keypad */
 | |
| 	SDL_Rect		kp_rect;	/* portion of the skin to display - default all */
 | |
| 	SDL_Surface		*font;		/* font to be used */ 
 | |
| 	SDL_Rect		font_rects[96];	/* only printable chars */
 | |
| 
 | |
| 	/* each board has two rectangles,
 | |
| 	 * [0] is the geometry relative to the keypad,
 | |
| 	 * [1] is the geometry relative to the whole screen
 | |
| 	 */
 | |
| 	SDL_Rect		kp_msg[2];		/* incoming msg, relative to kpad */
 | |
| 	struct board		*bd_msg;
 | |
| 
 | |
| 	SDL_Rect		kp_edit[2];	/* edit user input */
 | |
| 	struct board		*bd_edit;
 | |
| 
 | |
| 	SDL_Rect		kp_dialed[2];	/* dialed number */
 | |
| 	struct board		*bd_dialed;
 | |
| 
 | |
| 	/* variable-size array mapping keypad regions to functions */
 | |
| 	int kp_size, kp_used;
 | |
| 	struct keypad_entry *kp;
 | |
| 
 | |
| 	struct display_window   win[WIN_MAX];
 | |
| };
 | |
| 
 | |
| /*! \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)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	if (gui == NULL)
 | |
| 		return NULL;
 | |
| 
 | |
| 	/* unload font file */ 
 | |
| 	if (gui->font) {
 | |
| 		SDL_FreeSurface(gui->font);
 | |
| 		gui->font = NULL; 
 | |
| 	}
 | |
| 
 | |
| 	if (gui->outfd > -1)
 | |
| 		close(gui->outfd);
 | |
| 	if (gui->keypad)
 | |
| 		SDL_FreeSurface(gui->keypad);
 | |
| 	gui->keypad = NULL;
 | |
| 	if (gui->kp)
 | |
| 		ast_free(gui->kp);
 | |
| 
 | |
| 	/* uninitialize the SDL environment */
 | |
| 	for (i = 0; i < WIN_MAX; i++) {
 | |
| 		if (gui->win[i].bmp)
 | |
| 			SDL_FreeYUVOverlay(gui->win[i].bmp);
 | |
| 	}
 | |
| 	bzero(gui, sizeof(gui));
 | |
| 	ast_free(gui);
 | |
| 	SDL_Quit();
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 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
 | |
|  * - Create a YUV Overlay to copy the frame into it;
 | |
|  * - After the frame is copied into the overlay, display it
 | |
|  *
 | |
|  * The size is taken from the configuration.
 | |
|  *
 | |
|  * 'out' is 0 for remote video, 1 for the local video
 | |
|  */
 | |
| static void show_frame(struct video_desc *env, int out)
 | |
| {
 | |
| 	AVPicture *p_in, p_out;
 | |
| 	struct fbuf_t *b_in, *b_out;
 | |
| 	SDL_Overlay *bmp;
 | |
| 	struct gui_info *gui = env->gui;
 | |
| 
 | |
| 	if (!gui)
 | |
| 		return;
 | |
| 
 | |
| 	if (out == WIN_LOCAL) {	/* webcam/x11 to sdl */
 | |
| 		b_in = &env->enc_in;
 | |
| 		b_out = &env->loc_dpy;
 | |
| 		p_in = NULL;
 | |
| 	} else {
 | |
| 		/* copy input format from the decoding context */
 | |
| 		AVCodecContext *c;
 | |
| 		if (env->in == NULL)	/* XXX should not happen - decoder not ready */
 | |
| 			return;
 | |
| 		c = env->in->dec_ctx;
 | |
| 		b_in = &env->in->dec_out;
 | |
|                 b_in->pix_fmt = c->pix_fmt;
 | |
|                 b_in->w = c->width;
 | |
|                 b_in->h = c->height;
 | |
| 
 | |
| 		b_out = &env->rem_dpy;
 | |
| 		p_in = (AVPicture *)env->in->d_frame;
 | |
| 	}
 | |
| 	bmp = gui->win[out].bmp;
 | |
| 	SDL_LockYUVOverlay(bmp);
 | |
| 	/* output picture info - this is sdl, YUV420P */
 | |
| 	bzero(&p_out, sizeof(p_out));
 | |
| 	p_out.data[0] = bmp->pixels[0];
 | |
| 	p_out.data[1] = bmp->pixels[1];
 | |
| 	p_out.data[2] = bmp->pixels[2];
 | |
| 	p_out.linesize[0] = bmp->pitches[0];
 | |
| 	p_out.linesize[1] = bmp->pitches[1];
 | |
| 	p_out.linesize[2] = bmp->pitches[2];
 | |
| 
 | |
| 	my_scale(b_in, p_in, b_out, &p_out);
 | |
| 
 | |
| 	/* lock to protect access to Xlib by different threads. */
 | |
| 	SDL_DisplayYUVOverlay(bmp, &gui->win[out].rect);
 | |
| 	SDL_UnlockYUVOverlay(bmp);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Identifiers for regions of the main window.
 | |
|  * Values between 0 and 127 correspond to ASCII characters.
 | |
|  * The corresponding strings to be used in the skin comment section
 | |
|  * are defined in gui_key_map.
 | |
|  */
 | |
| enum skin_area {
 | |
| 	/* answer/close functions */
 | |
| 	KEY_PICK_UP = 128,
 | |
| 	KEY_HANG_UP = 129,
 | |
| 
 | |
| 	KEY_MUTE = 130,
 | |
| 	KEY_AUTOANSWER = 131,
 | |
| 	KEY_SENDVIDEO = 132,
 | |
| 	KEY_LOCALVIDEO = 133,
 | |
| 	KEY_REMOTEVIDEO = 134,
 | |
| 	KEY_FLASH = 136,
 | |
| 
 | |
| 	/* sensitive areas for the various text windows */
 | |
| 	KEY_MESSAGEBOARD = 140,
 | |
| 	KEY_DIALEDBOARD = 141,
 | |
| 	KEY_EDITBOARD = 142,
 | |
| 
 | |
| 	KEY_GUI_CLOSE = 199,		/* close gui */
 | |
| 	/* regions of the skin - displayed area, fonts, etc.
 | |
| 	 * XXX NOTE these are not sensitive areas.
 | |
| 	 */
 | |
| 	KEY_KEYPAD = 200,		/* the keypad - default to the whole image */
 | |
| 	KEY_FONT = 201,		/* the font. Maybe not really useful */
 | |
| 	KEY_MESSAGE = 202,	/* area for incoming messages */
 | |
| 	KEY_DIALED = 203,	/* area for dialed numbers */
 | |
| 	KEY_EDIT = 204,		/* area for editing user input */
 | |
| 
 | |
| 	/* areas outside the keypad - simulated */
 | |
| 	KEY_OUT_OF_KEYPAD = 241,
 | |
| 	KEY_REM_DPY = 242,
 | |
| 	KEY_LOC_DPY = 243,
 | |
| 	KEY_RESET = 253,		/* the 'reset' keyword */
 | |
| 	KEY_NONE = 254,			/* invalid area */
 | |
| 	KEY_DIGIT_BACKGROUND = 255,	/* other areas within the keypad */
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * Handlers for the various keypad functions
 | |
|  */
 | |
| 
 | |
| /* accumulate digits, possibly call dial if in connected mode */
 | |
| static void keypad_digit(struct video_desc *env, int digit)
 | |
| {	
 | |
| 	if (env->owner) {		/* we have a call, send the digit */
 | |
| 		struct ast_frame f = { AST_FRAME_DTMF, 0 };
 | |
| 
 | |
| 		f.subclass = digit;
 | |
| 		ast_queue_frame(env->owner, &f);
 | |
| 	} else {		/* no call, accumulate digits */
 | |
| 		char buf[2] = { digit, '\0' };
 | |
| 		if (env->gui->bd_msg) /* XXX not strictly necessary ... */
 | |
| 			print_message(env->gui->bd_msg, buf);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* function used to toggle on/off the status of some variables */
 | |
| 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:
 | |
| 		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;
 | |
| 		}
 | |
| 		break;
 | |
| 	case KEY_AUTOANSWER: {
 | |
| 		struct chan_oss_pvt *o = find_desc(oss_active);
 | |
| 		o->autoanswer = !o->autoanswer;
 | |
| 		}
 | |
| 		break;
 | |
| #endif
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| char *console_do_answer(int fd);
 | |
| /*
 | |
|  * Function called when the pick up button is pressed
 | |
|  * perform actions according the channel status:
 | |
|  *
 | |
|  *  - if no one is calling us and no digits was pressed,
 | |
|  *    the operation have no effects,
 | |
|  *  - if someone is calling us we answer to the call.
 | |
|  *  - if we have no call in progress and we pressed some
 | |
|  *    digit, send the digit to the console.
 | |
|  */
 | |
| static void keypad_pick_up(struct video_desc *env)
 | |
| {
 | |
| 	struct gui_info *gui = env->gui;
 | |
| 
 | |
| 	ast_log(LOG_WARNING, "keypad_pick_up called\n");
 | |
| 
 | |
| 	if (env->owner) { /* someone is calling us, just answer */
 | |
| 		ast_cli_command(gui->outfd, "console answer");
 | |
| 	} else { /* we have someone to call */
 | |
| 		char buf[160];
 | |
| 		const char *who = ast_skip_blanks(read_message(gui->bd_msg));
 | |
| 		buf[sizeof(buf) - 1] = '\0';
 | |
| 		snprintf(buf, sizeof(buf) - 1, "console dial %s", who);
 | |
| 		ast_log(LOG_WARNING, "doing <%s>\n", buf);
 | |
| 		print_message(gui->bd_dialed, "\n");
 | |
| 		print_message(gui->bd_dialed, who);
 | |
| 		reset_board(gui->bd_msg);
 | |
| 		ast_cli_command(gui->outfd, buf);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| #if 0 /* still unused */
 | |
| /*
 | |
|  * As an alternative to SDL_TTF, we can simply load the font from
 | |
|  * an image and blit characters on the background of the GUI.
 | |
|  *
 | |
|  * To generate a font we can use the 'fly' command with the
 | |
|  * following script (3 lines with 32 chars each)
 | |
|  
 | |
| size 320,64
 | |
| name font.png
 | |
| transparent 0,0,0
 | |
| string 255,255,255,  0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
 | |
| string 255,255,255,  0,20,giant,@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
 | |
| string 255,255,255,  0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
 | |
| end
 | |
| 
 | |
|  */
 | |
| 
 | |
| /* Print given text on the gui */
 | |
| static int gui_output(struct video_desc *env, const char *text)
 | |
| {
 | |
| 	return 1;	/* error, not supported */
 | |
| }
 | |
| #endif 
 | |
| 
 | |
| static int video_geom(struct fbuf_t *b, const char *s);
 | |
| static void sdl_setup(struct video_desc *env);
 | |
| static int kp_match_area(const struct keypad_entry *e, int x, int y);
 | |
| 
 | |
| static void set_drag(struct drag_info *drag, int x, int y, enum drag_window win)
 | |
| {
 | |
| 	drag->x_start = x;
 | |
| 	drag->y_start = y;
 | |
| 	drag->drag_window = win;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
 | |
|  * index value and calling the right callback.
 | |
|  *
 | |
|  * x, y are referred to the upper left corner of the main SDL window.
 | |
|  */
 | |
| 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;
 | |
| 
 | |
| #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;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* exec the function */
 | |
| 	if (index < 128) {	/* surely clicked on the keypad, don't care which key */
 | |
| 		keypad_digit(env, index);
 | |
| 		return;
 | |
| 	}
 | |
| 	switch (index) {
 | |
| 	/* answer/close function */
 | |
| 	case KEY_PICK_UP:
 | |
| 		keypad_pick_up(env);
 | |
| 		break;
 | |
| 	case KEY_HANG_UP:
 | |
| 		ast_cli_command(gui->outfd, "console hangup");
 | |
| 		break;
 | |
| 
 | |
| 	/* other functions */
 | |
| 	case KEY_MUTE:
 | |
| 	case KEY_AUTOANSWER:
 | |
| 	case KEY_SENDVIDEO:
 | |
| 		keypad_toggle(env, index);
 | |
| 		break;
 | |
| 
 | |
| 	case KEY_LOCALVIDEO:
 | |
| 		break;
 | |
| 	case KEY_REMOTEVIDEO:
 | |
| 		break;
 | |
| 
 | |
| 	case KEY_MESSAGEBOARD:
 | |
| 		if (button.button == SDL_BUTTON_LEFT)
 | |
| 			set_drag(&gui->drag, button.x, button.y, DRAG_MESSAGE);
 | |
| 		break;
 | |
| 
 | |
| 	/* press outside the keypad. right increases size, center decreases, left drags */
 | |
| 	case KEY_LOC_DPY:
 | |
| 	case KEY_REM_DPY:
 | |
| 		if (button.button == SDL_BUTTON_LEFT) {
 | |
| 			if (index == KEY_LOC_DPY)
 | |
| 				set_drag(&gui->drag, button.x, button.y, DRAG_LOCAL);
 | |
| 			break;
 | |
| 		} else {
 | |
| 			char buf[128];
 | |
| 			struct fbuf_t *fb = index == KEY_LOC_DPY ? &env->loc_dpy : &env->rem_dpy;
 | |
| 			sprintf(buf, "%c%dx%d", button.button == SDL_BUTTON_RIGHT ? '>' : '<',
 | |
| 				fb->w, fb->h);
 | |
| 			video_geom(fb, buf);
 | |
| 			sdl_setup(env);
 | |
| 		}
 | |
| 		break;
 | |
| 	case KEY_OUT_OF_KEYPAD:
 | |
| 		break;
 | |
| 
 | |
| 	case KEY_DIGIT_BACKGROUND:
 | |
| 		break;
 | |
| 	default:
 | |
| 		ast_log(LOG_WARNING, "function not yet defined %i\n", index);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Handle SDL_KEYDOWN type event, put the key pressed
 | |
|  * in the dial buffer or in the text-message buffer,
 | |
|  * depending on the text_mode variable value.
 | |
|  *
 | |
|  * key is the SDLKey structure corresponding to the key pressed.
 | |
|  * Note that SDL returns modifiers (ctrl, shift, alt) as independent
 | |
|  * information so the key itself is not enough and we need to
 | |
|  * use a translation table, below - one line per entry,
 | |
|  * plain, shift, ctrl, ... using the first char as key.
 | |
|  */
 | |
| static const char *us_kbd_map[] = {
 | |
| 	"`~", "1!", "2@", "3#", "4$", "5%", "6^",
 | |
| 	"7&", "8*", "9(", "0)", "-_", "=+", "[{",
 | |
| 	"]}", "\\|", ";:", "'\"", ",<", ".>", "/?",
 | |
| 	"jJ\n",
 | |
| 	NULL
 | |
| };
 | |
| 
 | |
| static char map_key(SDL_keysym *ks)
 | |
| {
 | |
| 	const char *s, **p = us_kbd_map;
 | |
| 	int c = ks->sym;
 | |
| 
 | |
| 	if (c == '\r')	/* map cr into lf */
 | |
| 		c = '\n';
 | |
| 	if (c >= SDLK_NUMLOCK && c <= SDLK_COMPOSE)
 | |
| 		return 0;	/* only a modifier */
 | |
| 	if (ks->mod == 0)
 | |
| 		return c;
 | |
| 	while ((s = *p) && s[0] != c)
 | |
| 		p++;
 | |
| 	if (s) { /* see if we have a modifier and a chance to use it */
 | |
| 		int l = strlen(s), mod = 0;
 | |
| 		if (l > 1)
 | |
| 			mod |= (ks->mod & KMOD_SHIFT) ? 1 : 0;
 | |
| 		if (l > 2 + mod)
 | |
| 			mod |= (ks->mod & KMOD_CTRL) ? 2 : 0;
 | |
| 		if (l > 4 + mod)
 | |
| 			mod |= (ks->mod & KMOD_ALT) ? 4 : 0;
 | |
| 		c = s[mod];
 | |
| 	}
 | |
| 	if (ks->mod & (KMOD_CAPS|KMOD_SHIFT) && c >= 'a' && c <='z')
 | |
| 		c += 'A' - 'a';
 | |
| 	return c;
 | |
| }
 | |
| 
 | |
| static void handle_keyboard_input(struct video_desc *env, SDL_keysym *ks)
 | |
| {
 | |
| 	char buf[2] = { map_key(ks), '\0' };
 | |
| 	struct gui_info *gui = env->gui;
 | |
| 	if (buf[0] == 0)	/* modifier ? */
 | |
| 		return;
 | |
| 	switch (gui->kb_output) {
 | |
| 	default:
 | |
| 		break;
 | |
| 	case KO_INPUT:	/* to be completed */
 | |
| 		break;
 | |
| 	case KO_MESSAGE:
 | |
| 		if (gui->bd_msg) {
 | |
| 			print_message(gui->bd_msg, buf);
 | |
| 			if (buf[0] == '\r' || buf[0] == '\n') {
 | |
| 				keypad_pick_up(env);
 | |
| 			}
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case KO_DIALED: /* to be completed */
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| static void grabber_move(struct video_out_desc *, int dx, int dy);
 | |
| 
 | |
| int compute_drag(int *start, int end, int magnifier);
 | |
| int compute_drag(int *start, int end, int magnifier)
 | |
| {
 | |
| 	int delta = end - *start;
 | |
| #define POLARITY -1
 | |
| 	/* add a small quadratic term */
 | |
| 	delta += delta * delta * (delta > 0 ? 1 : -1 )/100;
 | |
| 	delta *= POLARITY * magnifier;
 | |
| #undef POLARITY
 | |
| 	*start = end;
 | |
| 	return delta;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * I am seeing some kind of deadlock or stall around
 | |
|  * SDL_PumpEvents() while moving the window on a remote X server
 | |
|  * (both xfree-4.4.0 and xorg 7.2)
 | |
|  * and windowmaker. It is unclear what causes it.
 | |
|  */
 | |
| 
 | |
| /*! \brief refresh the screen, and also grab a bunch of events.
 | |
|  */
 | |
| static void eventhandler(struct video_desc *env, const char *caption)
 | |
| {
 | |
| 	struct gui_info *gui = env->gui;
 | |
| 	struct drag_info *drag;
 | |
| #define N_EVENTS	32
 | |
| 	int i, n;
 | |
| 	SDL_Event ev[N_EVENTS];
 | |
| 
 | |
| 	if (!gui)
 | |
| 		return;
 | |
| 	drag = &gui->drag;
 | |
| 	if (caption)
 | |
| 		SDL_WM_SetCaption(caption, NULL);
 | |
| 
 | |
| #define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
 | |
| 	while ( (n = SDL_PeepEvents(ev, N_EVENTS, SDL_GETEVENT, SDL_ALLEVENTS)) > 0) {
 | |
| 		for (i = 0; i < n; i++) {
 | |
| #if 0
 | |
| 			ast_log(LOG_WARNING, "------ event %d at %d %d\n",
 | |
| 				ev[i].type,  ev[i].button.x,  ev[i].button.y);
 | |
| #endif
 | |
| 			switch (ev[i].type) {
 | |
| 			case SDL_KEYDOWN:
 | |
| 				handle_keyboard_input(env, &ev[i].key.keysym);
 | |
| 				break;
 | |
| 			case SDL_MOUSEMOTION:
 | |
| 			case SDL_MOUSEBUTTONUP:
 | |
| 				if (drag->drag_window == DRAG_LOCAL) {
 | |
| 					/* 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);
 | |
| 				} else if (drag->drag_window == DRAG_MESSAGE) {
 | |
| 					/* scroll up/down the window */
 | |
| 					int dy = compute_drag(&drag->y_start, ev[i].motion.y, 1);
 | |
| 					move_message_board(gui->bd_msg, dy);
 | |
| 				}
 | |
| 				if (ev[i].type == SDL_MOUSEBUTTONUP)
 | |
| 					drag->drag_window = DRAG_NONE;
 | |
| 				break;
 | |
| 			case SDL_MOUSEBUTTONDOWN:
 | |
| 				handle_mousedown(env, ev[i].button);
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	if (1) {
 | |
| 		struct timeval b, a = ast_tvnow();
 | |
| 		int i;
 | |
| 		//SDL_Lock_EventThread();
 | |
| 		SDL_PumpEvents();
 | |
| 		b = ast_tvnow();
 | |
| 		i = ast_tvdiff_ms(b, a);
 | |
| 		if (i > 3)
 | |
| 			fprintf(stderr, "-------- SDL_PumpEvents took %dms\n", i);
 | |
| 		//SDL_Unlock_EventThread();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static SDL_Surface *load_image(const char *file)
 | |
| {
 | |
| 	SDL_Surface *temp;
 | |
|  
 | |
| #ifdef HAVE_SDL_IMAGE
 | |
| 	temp = IMG_Load(file);
 | |
| #else
 | |
| 	temp = SDL_LoadBMP(file);
 | |
| #endif
 | |
| 	if (temp == NULL)
 | |
| 		fprintf(stderr, "Unable to load image %s: %s\n",
 | |
| 			file, SDL_GetError());
 | |
| 	return temp;
 | |
| }
 | |
| 
 | |
| static void keypad_setup(struct gui_info *gui, const char *kp_file);
 | |
| 
 | |
| /* TODO: consistency checks, check for bpp, widht and height */
 | |
| /* Init the mask image used to grab the action. */
 | |
| static struct gui_info *gui_init(const char *keypad_file, const char *font)
 | |
| {
 | |
| 	struct gui_info *gui = ast_calloc(1, sizeof(*gui));
 | |
| 
 | |
| 	if (gui == NULL)
 | |
| 		return NULL;
 | |
| 	/* initialize keypad status */
 | |
| 	gui->kb_output = KO_MESSAGE;	/* XXX temp */
 | |
| 	gui->drag.drag_window = DRAG_NONE;
 | |
| 	gui->outfd = -1;
 | |
| 
 | |
| 	keypad_setup(gui, keypad_file);
 | |
| 	if (gui->keypad == NULL)	/* no keypad, we are done */
 | |
| 		return gui;
 | |
| 	/* XXX load image */
 | |
| 	if (!ast_strlen_zero(font)) {
 | |
| 		int i;
 | |
| 		SDL_Rect *r;
 | |
| 
 | |
| 		gui->font = load_image(font);
 | |
| 		if (!gui->font) {
 | |
| 			ast_log(LOG_WARNING, "Unable to load font %s, no output available\n", font);
 | |
| 			goto error;
 | |
| 		}
 | |
| 		ast_log(LOG_WARNING, "Loaded font %s\n", font);
 | |
| 		/* XXX hardwired constants - 3 rows of 32 chars */
 | |
| 		r = gui->font_rects;
 | |
| #define FONT_H 20
 | |
| #define FONT_W 9
 | |
| 		for (i = 0; i < 96; r++, i++) {
 | |
|                 	r->x = (i % 32 ) * FONT_W;
 | |
|                 	r->y = (i / 32 ) * FONT_H;
 | |
|                 	r->w = FONT_W;
 | |
|                 	r->h = FONT_H;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	gui->outfd = open ("/dev/null", O_WRONLY);	/* discard output, temporary */
 | |
| 	if (gui->outfd < 0) {
 | |
| 		ast_log(LOG_WARNING, "Unable output fd\n");
 | |
| 		goto error;
 | |
| 	}
 | |
| 	return gui;
 | |
| 
 | |
| error:
 | |
| 	ast_free(gui);
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| /* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
 | |
| static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
 | |
| 	int w, int h, int x, int y)
 | |
| {
 | |
| 	win->bmp = SDL_CreateYUVOverlay(w, h, fmt, screen);
 | |
| 	if (win->bmp == NULL)
 | |
| 		return -1;	/* error */
 | |
| 	win->rect.x = x;
 | |
| 	win->rect.y = y;
 | |
| 	win->rect.w = w;
 | |
| 	win->rect.h = h;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int keypad_cfg_read(struct gui_info *gui, const char *val);
 | |
| 
 | |
| static void keypad_setup(struct gui_info *gui, const char *kp_file)
 | |
| {
 | |
| 	FILE *fd;
 | |
| 	char buf[1024];
 | |
| 	const char region[] = "region";
 | |
| 	int reg_len = strlen(region);
 | |
| 	int in_comment = 0;
 | |
| 
 | |
| 	if (gui->keypad)
 | |
| 		return;
 | |
| 	gui->keypad = load_image(kp_file);
 | |
| 	if (!gui->keypad)
 | |
| 		return;
 | |
| 	/* now try to read the keymap from the file. */
 | |
| 	fd = fopen(kp_file, "r");
 | |
| 	if (fd == NULL) {
 | |
| 		ast_log(LOG_WARNING, "fail to open %s\n", kp_file);
 | |
| 		return;
 | |
| 	}
 | |
| 	/*
 | |
| 	 * If the keypad image has a comment field, try to read
 | |
| 	 * the button location from there. The block must start with
 | |
| 	 * a comment (or empty) line, and continue with entries like:
 | |
| 	 *	region = token shape x0 y0 x1 y1 h
 | |
| 	 *	...
 | |
| 	 * (basically, lines have the same format as config file entries).
 | |
| 	 * You can add it to a jpeg file using wrjpgcom
 | |
| 	 */
 | |
| 	while (fgets(buf, sizeof(buf), fd)) {
 | |
| 		char *s;
 | |
| 
 | |
| 		if (!strstr(buf, region)) { /* no keyword yet */
 | |
| 			if (!in_comment)	/* still waiting for initial comment block */
 | |
| 				continue;
 | |
| 			else
 | |
| 				break;
 | |
| 		}
 | |
| 		if (!in_comment) {	/* first keyword, reset previous entries */
 | |
| 			keypad_cfg_read(gui, "reset");
 | |
| 			in_comment = 1;
 | |
| 		}
 | |
| 		s = ast_skip_blanks(buf);
 | |
| 		ast_trim_blanks(s);
 | |
| 		if (memcmp(s, region, reg_len))
 | |
| 			break;	/* keyword not found */
 | |
| 		s = ast_skip_blanks(s + reg_len); /* space between token and '=' */
 | |
| 		if (*s++ != '=')	/* missing separator */
 | |
| 			break;
 | |
| 		if (*s == '>')	/* skip '>' if present */
 | |
| 			s++;
 | |
| 		keypad_cfg_read(gui, ast_skip_blanks(s));
 | |
| 	}
 | |
| 	fclose(fd);
 | |
| }
 | |
| 
 | |
| struct board *board_setup(SDL_Surface *screen, SDL_Rect *dest,
 | |
| 	SDL_Surface *font, SDL_Rect *font_rects);
 | |
| 
 | |
| /*! \brief initialize the boards we have in the keypad */
 | |
| static void init_board(struct gui_info *gui, struct board **dst, SDL_Rect *r, int dx, int dy)
 | |
| {
 | |
| 	if (r[0].w == 0 || r[0].h == 0)
 | |
| 		return;	/* not available */
 | |
| 	r[1] = r[0];	/* copy geometry */
 | |
| 	r[1].x += dx;	/* add offset of main window */
 | |
| 	r[1].y += dy;
 | |
| 	if (*dst == NULL) {	/* initial call */
 | |
| 		*dst = board_setup(gui->screen, &r[1], gui->font, gui->font_rects);
 | |
| 	} else {
 | |
| 		/* call a refresh */
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*! \brief [re]set the main sdl window, useful in case of resize.
 | |
|  * We can tell the first from subsequent calls from the value of
 | |
|  * env->gui, which is NULL the first time.
 | |
|  */
 | |
| static void sdl_setup(struct video_desc *env)
 | |
| {
 | |
| 	int dpy_fmt = SDL_IYUV_OVERLAY;	/* YV12 causes flicker in SDL */
 | |
| 	int depth, maxw, maxh;
 | |
| 	const SDL_VideoInfo *info;
 | |
| 	int kp_w = 0, kp_h = 0;	/* keypad width and height */
 | |
| 	struct gui_info *gui = env->gui;
 | |
| 
 | |
| 	/*
 | |
| 	 * 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:
 | |
| 	 * - on the left, the remote video;
 | |
| 	 * - on the center, the keypad
 | |
| 	 * - on the right, the local video
 | |
| 	 * We need to read in the skin for the keypad before creating the main
 | |
| 	 * SDL window, because the size is only known here.
 | |
| 	 */
 | |
| 
 | |
| 	if (gui == NULL && SDL_Init(SDL_INIT_VIDEO)) {
 | |
|  		ast_log(LOG_WARNING, "Could not initialize SDL - %s\n",
 | |
|                         SDL_GetError());
 | |
|                 /* again not fatal, just we won't display anything */
 | |
| 		return;
 | |
| 	}
 | |
| 	info = SDL_GetVideoInfo();
 | |
| 	/* We want at least 16bpp to support YUV overlays.
 | |
| 	 * E.g with SDL_VIDEODRIVER = aalib the default is 8
 | |
| 	 */
 | |
| 	depth = info->vfmt->BitsPerPixel;
 | |
| 	if (depth < 16)
 | |
| 		depth = 16;
 | |
| 	if (!gui)
 | |
| 		env->gui = gui = gui_init(env->keypad_file, env->keypad_font);
 | |
| 	if (!gui)
 | |
| 		goto no_sdl;
 | |
| 
 | |
| 	if (gui->keypad) {
 | |
| 		if (gui->kp_rect.w > 0 && gui->kp_rect.h > 0) {
 | |
| 			kp_w = gui->kp_rect.w;
 | |
| 			kp_h = gui->kp_rect.h;
 | |
| 		} else {
 | |
| 			kp_w = gui->keypad->w;
 | |
| 			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;
 | |
| 	gui->screen = SDL_SetVideoMode(maxw, maxh, depth, 0);
 | |
| 	if (!gui->screen) {
 | |
| 		ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
 | |
| 		goto no_sdl;
 | |
| 	}
 | |
| 
 | |
| 	SDL_WM_SetCaption("Asterisk console Video Output", NULL);
 | |
| 	if (set_win(gui->screen, &gui->win[WIN_REMOTE], dpy_fmt,
 | |
| 			env->rem_dpy.w, env->rem_dpy.h, BORDER, BORDER))
 | |
| 		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))
 | |
| 		goto no_sdl;
 | |
| 
 | |
| 	/* display the skin, but do not free it as we need it later to
 | |
| 	 * 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->w = kp_w;
 | |
| 		dest->h = kp_h;
 | |
| 		SDL_BlitSurface(gui->keypad, src, gui->screen, dest);
 | |
| 		init_board(gui, &gui->bd_msg, gui->kp_msg, dest->x, dest->y);
 | |
| 		init_board(gui, &gui->bd_dialed, gui->kp_dialed, dest->x, dest->y);
 | |
| 		SDL_UpdateRects(gui->screen, 1, dest);
 | |
| 	}
 | |
| 	return;
 | |
| 
 | |
| no_sdl:
 | |
| 	/* free resources in case of errors */
 | |
| 	env->gui = cleanup_sdl(gui);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Functions to determine if a point is within a region. Return 1 if success.
 | |
|  * First rotate the point, with
 | |
|  *	x' =  (x - x0) * cos A + (y - y0) * sin A
 | |
|  *	y' = -(x - x0) * sin A + (y - y0) * cos A
 | |
|  * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
 | |
|  *	l = sqrt( (x1-x0)^2 + (y1-y0)^2
 | |
|  * Then determine inclusion by simple comparisons i.e.:
 | |
|  *	rectangle: x >= 0 && x < l && y >= 0 && y < h
 | |
|  *	ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
 | |
|  */
 | |
| static int kp_match_area(const struct keypad_entry *e, int x, int y)
 | |
| {
 | |
| 	double xp, dx = (e->x1 - e->x0);
 | |
| 	double yp, dy = (e->y1 - e->y0);
 | |
| 	double l = sqrt(dx*dx + dy*dy);
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (l > 1) { /* large enough */
 | |
| 		xp = ((x - e->x0)*dx + (y - e->y0)*dy)/l;
 | |
| 		yp = (-(x - e->x0)*dy + (y - e->y0)*dx)/l;
 | |
| 		if (e->type == KP_RECT) {
 | |
| 			ret = (xp >= 0 && xp < l && yp >=0 && yp < l);
 | |
| 		} else if (e->type == KP_CIRCLE) {
 | |
| 			dx = xp*xp/(l*l) + yp*yp/(e->h*e->h);
 | |
| 			ret = (dx < 1);
 | |
| 		}
 | |
| 	}
 | |
| #if 0
 | |
| 	ast_log(LOG_WARNING, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
 | |
| 		ret, e->c, x, y, e->type, e->x0, e->y0, e->x1, e->y1, e->h);
 | |
| #endif
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| struct _s_k { const char *s; int k; };
 | |
| static struct _s_k gui_key_map[] = {
 | |
| 	{"PICK_UP",	KEY_PICK_UP },
 | |
| 	{"PICKUP",	KEY_PICK_UP },
 | |
|         {"HANG_UP",	KEY_HANG_UP },
 | |
|         {"HANGUP",	KEY_HANG_UP },
 | |
|         {"MUTE",	KEY_MUTE },
 | |
|         {"FLASH",	KEY_FLASH },
 | |
|         {"AUTOANSWER",	KEY_AUTOANSWER },
 | |
|         {"SENDVIDEO",	KEY_SENDVIDEO },
 | |
|         {"LOCALVIDEO",	KEY_LOCALVIDEO },
 | |
|         {"REMOTEVIDEO",	KEY_REMOTEVIDEO },
 | |
|         {"GUI_CLOSE",	KEY_GUI_CLOSE },
 | |
|         {"MESSAGEBOARD",	KEY_MESSAGEBOARD },
 | |
|         {"DIALEDBOARD",	KEY_DIALEDBOARD },
 | |
|         {"EDITBOARD",	KEY_EDITBOARD },
 | |
|         {"KEYPAD",	KEY_KEYPAD },	/* x0 y0 w h - active area of the keypad */
 | |
|         {"MESSAGE",	KEY_MESSAGE },	/* x0 y0 w h - incoming messages */
 | |
|         {"DIALED",	KEY_DIALED },	/* x0 y0 w h - dialed number */
 | |
|         {"EDIT",	KEY_EDIT },	/* x0 y0 w h - edit user input */
 | |
|         {"FONT",	KEY_FONT },	/* x0 yo w h rows cols - location and format of the font */
 | |
|         {NULL, 0 } };
 | |
| 
 | |
| static int gui_map_token(const char *s)
 | |
| {
 | |
| 	/* map the string into token to be returned */
 | |
| 	int i = atoi(s);
 | |
| 	struct _s_k *p;
 | |
| 	if (i > 0 || s[1] == '\0')	/* numbers or single characters */
 | |
| 		return (i > 9) ? i : s[0];
 | |
| 	for (p = gui_key_map; p->s; p++) {
 | |
| 		if (!strcasecmp(p->s, s))
 | |
| 			return p->k;
 | |
| 	}
 | |
| 	return KEY_NONE;	/* not found */
 | |
| }
 | |
| 
 | |
| /*! \brief read a keypad entry line in the format
 | |
|  *	reset
 | |
|  *	token circle xc yc diameter
 | |
|  *	token circle xc yc x1 y1 h	# ellipse, main diameter and height
 | |
|  *	token rect x0 y0 x1 y1 h	# rectangle with main side and eight
 | |
|  * token is the token to be returned, either a character or a symbol
 | |
|  * as KEY_* above
 | |
|  * Return 1 on success, 0 on error.
 | |
|  */
 | |
| static int keypad_cfg_read(struct gui_info *gui, const char *val)
 | |
| {
 | |
| 	struct keypad_entry e;
 | |
| 	SDL_Rect *r = NULL;
 | |
| 	char s1[16], s2[16];
 | |
| 	int i, ret = 0; /* default, error */
 | |
| 
 | |
| 	if (gui == NULL || val == NULL)
 | |
| 		return 0;
 | |
| 
 | |
| 	s1[0] = s2[0] = '\0';
 | |
| 	bzero(&e, sizeof(e));
 | |
| 	i = sscanf(val, "%14s %14s %d %d %d %d %d",
 | |
|                 s1, s2, &e.x0, &e.y0, &e.x1, &e.y1, &e.h);
 | |
| 
 | |
| 	e.c = gui_map_token(s1);
 | |
| 	if (e.c == KEY_NONE)
 | |
| 		return 0;	/* nothing found */
 | |
| 	switch (i) {
 | |
| 	default:
 | |
| 		break;
 | |
| 	case 1:	/* only "reset" is allowed */
 | |
| 		if (e.c != KEY_RESET)
 | |
| 			break;
 | |
| 		if (gui->kp)
 | |
| 			gui->kp_used = 0;
 | |
| 		break;
 | |
| 	case 5:
 | |
| 		if (e.c == KEY_KEYPAD)	/* active keypad area */
 | |
| 			r = &gui->kp_rect;
 | |
| 		else if (e.c == KEY_MESSAGE)
 | |
| 			r = gui->kp_msg;
 | |
| 		else if (e.c == KEY_DIALED)
 | |
| 			r = gui->kp_dialed;
 | |
| 		else if (e.c == KEY_EDIT)
 | |
| 			r = gui->kp_edit;
 | |
| 		if (r) {
 | |
| 			r->x = atoi(s2);
 | |
| 			r->y = e.x0;
 | |
| 			r->w = e.y0;
 | |
| 			r->h = e.x1;
 | |
| 			break;
 | |
| 		}
 | |
| 		if (strcasecmp(s2, "circle"))	/* invalid */
 | |
| 			break;
 | |
| 		/* token circle xc yc diameter */
 | |
| 		e.h = e.x1;
 | |
| 		e.y1 = e.y0;	/* map radius in x1 y1 */
 | |
| 		e.x1 = e.x0 + e.h;	/* map radius in x1 y1 */
 | |
| 		e.x0 = e.x0 - e.h;	/* map radius in x1 y1 */
 | |
| 		/* fallthrough */
 | |
| 
 | |
| 	case 7:
 | |
| 		if (e.c == KEY_FONT) {	/* font - x0 y0 w h rows cols */
 | |
| 			ast_log(LOG_WARNING, "font not supported yet\n");
 | |
| 			break;
 | |
| 		}
 | |
| 		/* token circle|rect x0 y0 x1 y1 h */
 | |
| 		if (e.x1 < e.x0 || e.h <= 0) {
 | |
| 			ast_log(LOG_WARNING, "error in coordinates\n");
 | |
| 			e.type = 0;
 | |
| 			break;
 | |
| 		}
 | |
| 		if (!strcasecmp(s2, "circle")) {
 | |
| 			/* for a circle we specify the diameter but store center and radii */
 | |
| 			e.type = KP_CIRCLE;
 | |
| 			e.x0 = (e.x1 + e.x0) / 2;
 | |
| 			e.y0 = (e.y1 + e.y0) / 2;
 | |
| 			e.h = e.h / 2;
 | |
| 		} else if (!strcasecmp(s2, "rect")) {
 | |
| 			e.type = KP_RECT;
 | |
| 		} else
 | |
| 			break;
 | |
| 		ret = 1;
 | |
| 	}
 | |
| 	// ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
 | |
| 	if (ret == 0)
 | |
| 		return 0;
 | |
| 	if (gui->kp_size == 0) {
 | |
| 		gui->kp = ast_calloc(10, sizeof(e));
 | |
| 		if (gui->kp == NULL) {
 | |
| 			ast_log(LOG_WARNING, "cannot allocate kp");
 | |
| 			return 0;
 | |
| 		}
 | |
| 		gui->kp_size = 10;
 | |
| 	}
 | |
| 	if (gui->kp_size == gui->kp_used) { /* must allocate */
 | |
| 		struct keypad_entry *a = ast_realloc(gui->kp, sizeof(e)*(gui->kp_size+10));
 | |
| 		if (a == NULL) {
 | |
| 			ast_log(LOG_WARNING, "cannot reallocate kp");
 | |
| 			return 0;
 | |
| 		}
 | |
| 		gui->kp = a;
 | |
| 		gui->kp_size += 10;
 | |
| 	}
 | |
| 	if (gui->kp_size == gui->kp_used)
 | |
| 		return 0;
 | |
| 	gui->kp[gui->kp_used++] = e;
 | |
| 	// ast_log(LOG_WARNING, "now %d regions\n", gui->kp_used);
 | |
| 	return 1;
 | |
| }
 | |
| #endif	/* HAVE_SDL */
 |