[asterisk-commits] rizzo: branch group/video_console r92978 - /team/group/video_console/channels/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Dec 14 10:26:17 CST 2007


Author: rizzo
Date: Fri Dec 14 10:26:17 2007
New Revision: 92978

URL: http://svn.digium.com/view/asterisk?view=rev&rev=92978
Log:
Implement a better solution for keypad 'skins'.

We use whatever image format we like for the skin, and the mapping
of regions to events is done through a series of entries of this
kind:

	keymap_entry => event shape x0 y0 x1 y1 h
	...

which can go either in oss.conf/alsa.conf or in the COMMENT section
of the image used for the skin (we simply grep for strings, so
the code is rather format-agnostic).

'event' is typically a key available on the skin, 'shape' is
either 'circle' or 'rect' and indicates the shape of the region
(rect is a rectangle, circle is actually an ellipse, i might probably
implement an alias for that), x0 y0 x1 y1 are the coordinates of
the base or main diameter, respectively, and h is the height
or the second diameter of the shape.

We can represent most reasonable shapes in this way, and should
we need more, nothing prevents us from mapping multiple regions to
the same event, or introducing a 'triangle' shape.

note to self: this code is now ready to be moved to
trunk, not compiled in by default.


Modified:
    team/group/video_console/channels/chan_oss.c
    team/group/video_console/channels/console_video.c

Modified: team/group/video_console/channels/chan_oss.c
URL: http://svn.digium.com/view/asterisk/team/group/video_console/channels/chan_oss.c?view=diff&rev=92978&r1=92977&r2=92978
==============================================================================
--- team/group/video_console/channels/chan_oss.c (original)
+++ team/group/video_console/channels/chan_oss.c Fri Dec 14 10:26:17 2007
@@ -63,6 +63,7 @@
 #include "asterisk/pbx.h"
 #include "asterisk/config.h"
 #include "asterisk/cli.h"
+#include "asterisk/file.h"	/* mmap */
 #include "asterisk/utils.h"
 #include "asterisk/causes.h"
 #include "asterisk/endian.h"

Modified: team/group/video_console/channels/console_video.c
URL: http://svn.digium.com/view/asterisk/team/group/video_console/channels/console_video.c?view=diff&rev=92978&r1=92977&r2=92978
==============================================================================
--- team/group/video_console/channels/console_video.c (original)
+++ team/group/video_console/channels/console_video.c Fri Dec 14 10:26:17 2007
@@ -252,6 +252,13 @@
 
 #define GUI_BUFFER_LEN 256			/* buffer lenght used for input buffers */
 
+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;
+};
+
 /*! \brief info related to the gui: button status, mouse coords, etc. */
 struct gui_info {
 	char			inbuf[GUI_BUFFER_LEN];	/* buffer for to-dial buffer */
@@ -267,7 +274,8 @@
 #endif
 	int			outfd;		/* fd for output */
 	SDL_Surface		*keypad;	/* the pixmap for the keypad */
-	SDL_Surface		*keypad_bg;	/* the keypad background, mapping locations to keys */
+	int kp_size, kp_used;
+	struct keypad_entry *kp;
 };
 
 /*
@@ -1844,9 +1852,7 @@
 	}
 	if (env->gui.keypad)
 		SDL_FreeSurface(env->gui.keypad);
-	if (env->gui.keypad_bg)
-		SDL_FreeSurface(env->gui.keypad_bg);
-	env->gui.keypad = env->gui.keypad_bg = NULL;
+	env->gui.keypad = NULL;
 	SDL_Quit();
 	env->screen = NULL; /* XXX check reference */
 	bzero(env->win, sizeof(env->win));
@@ -2327,34 +2333,9 @@
 #endif
 }
 
-/*
- * Map the x, y coordinates to a keypad event according to
- * the mask.
- */
-static uint8_t map_click(struct SDL_Surface *keymask, int x, int y)
-{
-	uint8_t *pixel = keymask->pixels;	/* pixel data pointer */
-	uint8_t pixel_value;			/* palette index */
-	int has_palette = 0;
-	SDL_Color col1;
-	SDL_Color *col = &col1;
-
-	SDL_LockSurface(keymask);	/* Do we really need to lock the surface ? */
-	pixel_value = pixel[(y*keymask->pitch) + x];	/* pitch takes care of alignment */
-	SDL_UnlockSurface(keymask);
-	if (keymask->format->palette && keymask->format->BitsPerPixel <= 8) {
-		has_palette = 1;
-		col = &keymask->format->palette->colors[pixel_value];
-	}
-
-	ast_log(LOG_WARNING, "pixel %d %d bpp %d palette %d value %d colors %d %d %d\n",
-		x, y, keymask->format->BitsPerPixel,
-		has_palette, pixel_value, col->r, col->g, col->b);
-
-        return pixel_value;
-}
 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);
 
 /*
  * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
@@ -2364,7 +2345,7 @@
  */
 static void handle_button_event(struct video_desc *env, SDL_MouseButtonEvent button)
 {
-	uint8_t index;	/* the key or region of the display we clicked on */
+	uint8_t index = KEY_OUT_OF_KEYPAD;	/* the key or region of the display we clicked on */
 
 	/* for each click we come back in normal mode */
 	env->gui.text_mode = 0;
@@ -2376,8 +2357,15 @@
 		index = KEY_LOC_DPY; /* click on local video */
 	else if (button.y > env->out.keypad_dpy.h)
 		index = KEY_OUT_OF_KEYPAD; /* click outside the keypad */
-	else
-		index = map_click(env->gui.keypad_bg, button.x - env->in.rem_dpy.w, button.y);
+	else if (env->gui.kp) {
+		int i;
+		for (i = 0; i < env->gui.kp_used; i++) {
+			if (kp_match_area(&env->gui.kp[i], button.x - env->in.rem_dpy.w, button.y)) {
+				index = env->gui.kp[i].c;
+				break;
+			}
+		}
+	}
 
 	/* exec the function */
 	if (index < 128) {	/* surely clicked on the keypad, don't care which key */
@@ -2598,11 +2586,6 @@
 	env->out.loc_src.x = 0;
 	env->out.loc_src.y = 0;
 
-	/* Load the keypad mask image */
-	env->gui.keypad_bg = get_keypad(env->keypad_mask);
-	if (env->gui.keypad_bg == NULL)
-		return -1;
-
 	/* initialize keyboard buffer */
 	append_char(env->gui.inbuf, &env->gui.inbuf_pos, '\0');
 	append_char(env->gui.msgbuf, &env->gui.msgbuf_pos, '\0');
@@ -2628,9 +2611,6 @@
 		ast_log(LOG_WARNING, "Unable output fd\n");
 		return -1;
 	}
-
-	ast_log(LOG_WARNING,"w %i, h %i, pitch %i\n", \
-		env->gui.keypad_bg->w, env->gui.keypad_bg->h, env->gui.keypad_bg->pitch);
 
 	return 0;
 }
@@ -2857,6 +2837,7 @@
 	ast_pthread_create_background(&env->vthread, NULL, video_thread, env);
 }
 
+static int keypad_cfg_read(struct gui_info *gui, const char *val);
 /* [re]set the main sdl window, useful in case of resize */
 static void sdl_setup(struct video_desc *env)
 {
@@ -2876,8 +2857,75 @@
 	if (!env->gui.keypad)
 		env->gui.keypad = get_keypad(env->keypad_file);
 	if (env->gui.keypad) {
+		int fd = -1;
+		void *p = NULL;
+		off_t l = 0;
+
 		env->out.keypad_dpy.w = env->gui.keypad->w;
 		env->out.keypad_dpy.h = env->gui.keypad->h;
+		/*
+		 * If the keypad image has a comment field, try to read
+		 * the button location from there. The block must be
+		 *	keypad_entry = token shape x0 y0 x1 y1 h
+		 *	...
+		 * (basically, lines have the same format as config file entries.
+		 * same as the keypad_entry.
+		 * You can add it to a jpeg file using wrjpgcom
+		 */
+		do { /* only once, in fact */
+			const unsigned char *s, *e;
+
+			fd = open(env->keypad_file, O_RDONLY);
+			if (fd < 0) {
+				ast_log(LOG_WARNING, "fail to open %s\n", env->keypad_file);
+				break;
+			}
+			l = lseek(fd, 0, SEEK_END);
+			if (l <= 0) {
+				ast_log(LOG_WARNING, "fail to lseek %s\n", env->keypad_file);
+				break;
+			}
+			p = mmap(NULL, l, PROT_READ, 0, fd, 0);
+			if (p == NULL) {
+				ast_log(LOG_WARNING, "fail to mmap %s size %ld\n", env->keypad_file, (long)l);
+				break;
+			}
+			e = (const unsigned char *)p + l;
+			for (s = p; s < e - 20 ; s++) {
+				if (!memcmp(s, "keypad_entry", 12)) { /* keyword found */
+					ast_log(LOG_WARNING, "found entry\n");
+					break;
+				}
+			}
+			for ( ;s < e - 20; s++) {
+				char buf[256];
+				const unsigned char *s1;
+				if (index(" \t\r\n", *s))	/* ignore blanks */
+					continue;
+				if (*s > 127)	/* likely end of comment */
+					break;
+				if (memcmp(s, "keypad_entry", 12)) /* keyword not found */
+					break;
+				s += 12;
+				l = MIN(sizeof(buf), e - s);
+				ast_copy_string(buf, s, l);
+				s1 = ast_skip_blanks(buf);	/* between token and '=' */
+				if (*s1++ != '=')	/* missing separator */
+					break;
+				if (*s1 == '>')	/* skip => */
+					s1++;
+				keypad_cfg_read(&env->gui, ast_skip_blanks(s1));
+				/* now wait for a newline */
+				s1 = s;
+				while (s1 < e - 20 && !index("\r\n", *s1) && *s1 < 128)
+					s1++;
+				s = s1;
+			}
+		} while (0);
+		if (p)
+			munmap(p, l);
+		if (fd >= 0)
+			close(fd);
 	}
 #define BORDER	5	/* border around our windows */
 	maxw = env->in.rem_dpy.w + env->out.loc_dpy.w + env->out.keypad_dpy.w;
@@ -2981,6 +3029,157 @@
 	return 0;
 }
 
+/*
+ * 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;
+}
+
+/*
+ * 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
+ */
+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 },
+        {"AUTOANSWER",	KEY_AUTOANSWER },
+        {"SENDVIDEO",	KEY_SENDVIDEO },
+        {"LOCALVIDEO",	KEY_LOCALVIDEO },
+        {"REMOTEVIDEO",	KEY_REMOTEVIDEO },
+        {"WRITEMESSAGE", KEY_WRITEMESSAGE },
+        {"GUI_CLOSE",	KEY_GUI_CLOSE },
+        {NULL, 0 } };
+
+static int keypad_cfg_read(struct gui_info *gui, const char *val)
+{
+	struct keypad_entry e;
+	char s1[16], s2[16];
+	int i, ret = 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);
+
+	switch (i) {
+	default:
+		break;
+	case 1:	/* only "reset" is allowed */
+		if (strcasecmp(s1, "reset"))	/* invalid */
+			break;
+		if (gui->kp) {
+			gui->kp_used = 0;
+		}
+		ret = 1;
+		break;
+	case 5: /* token circle xc yc diameter */
+		if (strcasecmp(s2, "circle"))	/* invalid */
+			break;
+		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: /* 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;
+	/* map the string into token to be returned */
+	i = atoi(s1);
+	if (i > 0 || s1[1] == '\0')	/* numbers or single characters */
+		e.c = (i > 9) ? i : s1[0];
+	else {
+		struct _s_k *p;
+		for (p = gui_key_map; p->s; p++) {
+			if (!strcasecmp(p->s, s1)) {
+				e.c = p->k;
+				break;
+			}
+		}
+	}
+	if (e.c == 0) {
+		ast_log(LOG_WARNING, "missing token\n");
+		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;
+	ast_log(LOG_WARNING, "allocated entry %d\n", gui->kp_used);
+	gui->kp[gui->kp_used++] = e;
+	return 1;
+}
+
 /* list of commands supported by the cli.
  * For write operation we use the commands in console_video_config(),
  * for reads we use console_video_cli(). XXX Names should be fixed.
@@ -2988,7 +3187,7 @@
 #define CONSOLE_VIDEO_CMDS				\
 	"console {videodevice|videocodec|sendvideo"	\
 	"|video_size|bitrate|fps|qmin"			\
-	"|keypad|keypad_mask"				\
+	"|keypad|keypad_mask|keypad_entry"		\
 	"}"
 
 /* extend ast_cli with video commands. Called by console_video_config */
@@ -3056,6 +3255,7 @@
         M_F("local_size", video_geom(&env->out.loc_dpy, val))
         M_F("remote_size", video_geom(&env->in.rem_dpy, val))
         M_STR("keypad", env->keypad_file)
+        M_F("keypad_entry", keypad_cfg_read(&env->gui, val))
         M_STR("keypad_mask", env->keypad_mask)
 	M_STR("keypad_font", env->keypad_font)
         M_UINT("fps", env->out.fps)




More information about the asterisk-commits mailing list