On Mon, Dec 20, 2010 at 10:05:42AM -0800, ext Keith Packard wrote:
> On Mon, 20 Dec 2010 18:05:38 +0200, [email protected] wrote:
> > Rather than continue my attempts to hack around the issue of incorrect
> > ClipNotifys during window redirection changes, I decided to tackle the
> > issue in more proper manner.
> > 
> > This series will remove the internal MapWindow+UnmapWindow cycle and
> > replace it with a single ValidateTree+HandleExposures pass through
> > the affected windows.
> 
> Thanks! As you might imagine, the whole unmap/map adventure was a short
> cut to ensure that all of the regions ended up recomputed correctly:
> 
>  * compRedirectWindow
>    + Redirected window has anything formerly obscured now exposed
>    + Underlying windows are exposed where the redirected window was
>      covering
> 
>  * compFreeClientWindow
>    + Un-Redirected window gets painted from backing pixmap
>    + Underlying window clip lists get updated
>    
> Do you have any small test cases that verify that these are working for
> both manual and automatic redirect in each direction? I'm concerned that
> the validation code won't 'just work' in all cases...

Yeah. I'm not 100% sold on that myself yet. miValidateTree & co. don't
seem to sink in without some conscious effort.

I've attached an ugly test app I was using. Just ignore/rip out the xv
stuff etc. You can run it, for example, like so:
'xvredirect -F -d -s -b 10`

You can input 'm'/'a'/'u' for manual/automatic/unredirected redirection
for the middle sibling. 'M'/'A'/'U' to do the same for the top level
window. It will print the expose events it receives.

-- 
Ville Syrjälä
/*
 */

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xvlib.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/Xcomposite.h>
#include <math.h>

#define WIDTH	400
#define HEIGHT	400

#define MAX(a, b) ((a) > (b) ? (a) : (b))

static void set_nodecorations_motif(Display *dpy, Window win)
{
	struct {
		unsigned long   flags;
		unsigned long   functions;
		unsigned long   decorations;
		long            inputMode;
		unsigned long   status;
	} hints = {
		.flags = 2,
		.decorations = 0,
	};
	Atom XA_MOTIF_WM_HINTS = XInternAtom(dpy, "_MOTIF_WM_HINTS", True);

	if (XA_MOTIF_WM_HINTS == None)
		return;

	XChangeProperty(dpy, win, XA_MOTIF_WM_HINTS, XA_MOTIF_WM_HINTS, 32,
			PropModeReplace, (unsigned char *) &hints, 5);
}

static void set_fs_ewmh(Display *dpy, Window win)
{
 	Atom XA_NET_WM_STATE = XInternAtom(dpy, "_NET_WM_STATE", True);
 	Atom XA_NET_WM_STATE_FULLSCREEN = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", True);

	if (XA_NET_WM_STATE == None || XA_NET_WM_STATE_FULLSCREEN == None)
		return;

	XChangeProperty(dpy, win, XA_NET_WM_STATE, XA_ATOM, 32, PropModeReplace,
			(unsigned char *) &XA_NET_WM_STATE_FULLSCREEN, 1);
}

static void
draw_rect(unsigned short rgb[HEIGHT][WIDTH],
	  int x0, int y0, int sz, unsigned short color)
{
	int x, y = y0;

	for (x = x0; x < x0+sz; x++) {
		rgb[y][x] = color;
	}

	for (y++; y < y0+sz-1; y++) {
		rgb[y][x0     ] = color;
		rgb[y][x0+sz-1] = color;
	}

	for (x = x0; x < x0+sz; x++) {
		rgb[y][x] = color;
	}
}

static void
draw_circle (unsigned short rgb[HEIGHT][WIDTH],
	     int x0, int y0, int sz, unsigned short color)
{
	int x, y;

	for (x = 0; x < sz; x++) {
		y = sqrt(sz * sz - x * x) + 0.5;

		rgb[y0 + sz - y][x0 + sz + x] = color;
		rgb[y0 + sz - y][x0 + sz - x] = color;
		rgb[y0 + sz + y][x0 + sz + x] = color;
		rgb[y0 + sz + y][x0 + sz - x] = color;
	}
	for (y = 0; y < sz; y++) {
		x = sqrt(sz * sz - y * y) + 0.5;

		rgb[y0 + sz - y][x0 + sz + x] = color;
		rgb[y0 + sz - y][x0 + sz - x] = color;
		rgb[y0 + sz + y][x0 + sz + x] = color;
		rgb[y0 + sz + y][x0 + sz - x] = color;
	}
}

static void fatal(const char *name)
{

	printf("Usage: %s "
	       "[-c|--child]"
	       "[-d|--deepchild]"
	       "[-s|--siblings]"
	       "[-C|--clip <WxH|WxH+X+Y>]"
	       "[-F|--fullscreen]"
	       "[-r|--reverse]"
	       "[-g|--geometry <WxH|WxH+X+Y>]"
	       "\n", name);

	exit(1);

}

static void set_wm_props(Display *dpy, Window win, const char *name,
			 int argc, char *argv[], bool fullscreen)
{
	XSizeHints sizeHints;
	XWMHints wmHints;
	XTextProperty wm_name, icon_name;

	sizeHints.flags = 0;

	wmHints.flags = InputHint;
	wmHints.input = True;

	wm_name.value = (unsigned char *) name;
	wm_name.encoding = XA_STRING;
	wm_name.format = 8;
	wm_name.nitems = strlen (wm_name.value) + 1;

	icon_name = wm_name;

	XSetWMProperties(dpy, win,
			 &wm_name, &icon_name,
			 argv, argc,
			 &sizeHints, &wmHints, 0);

	if (fullscreen) {
		set_nodecorations_motif(dpy, win);
		set_fs_ewmh(dpy, win);
	}
}

static void print_redir(const char *str, int redir)
{
	const char *name;

	switch (redir) {
	case CompositeRedirectAutomatic:
		name = "Automatic";
		break;
	case CompositeRedirectManual:
		name = "Manual";
		break;
	case -1:
		name = "Direct";
		break;
	default:
		assert(0);
	}

	printf("%s redirection mode is %s\n", str, name);
}

int
main (int argc, char **argv)
{
	Display *dpy;
	int num_adaptors;
	XvAdaptorInfo *adaptor_info;
	int screen;
	Window root, win, parent, toplevel, sib;
	int a;
	XvPortID port;
	XvImageFormatValues *formats, *format = NULL;
	int nformats;
	int f;
	XSetWindowAttributes wattr;
	XEvent event;
	GC gc;
	XvImage *image;
	int p;
	void *data;
	XRectangle clip = {};
	bool clip_set = false;
	int x, y;
	unsigned int width, height;
	unsigned int border, depth;
	bool fullscreen = false;
	bool child = false;
	bool deepchild = false;
	bool siblings = false;
	bool reverse = false;
	int i;
	XWindowChanges c;


	dpy = XOpenDisplay (NULL);
	screen = DefaultScreen (dpy);
	root = RootWindow (dpy, screen);

	XGetGeometry(dpy, root, &root, &x, &y, &width, &height, &border, &depth);

	for (i = 1; i < argc; ) {
		if (!strcmp(argv[i], "-F") || !strcmp(argv[i], "--fullscreen")) {
			fullscreen = true;
			i++;
			continue;
		} else if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--bordr")) {
			if (i + 1 >= argc)
				fatal(argv[0]);
			i++;

			if (sscanf(argv[i], "%u", &border) == 1) {
				i++;
				continue;
			}

			fatal(argv[0]);
		} else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--child")) {
			child = true;
			i++;
			continue;
		} else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--deepchild")) {
			child = true;
			deepchild = true;
			i++;
			continue;
		} else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--siblings")) {
			siblings = true;
			i++;
			continue;
		} else if (!strcmp(argv[i], "-C") || !strcmp(argv[i], "--clip")) {
			if (i + 1 >= argc)
				fatal(argv[0]);
			i++;

			if (sscanf(argv[i], "%ux%u+%d+%d", &clip.width, &clip.height,
				   &clip.x, &clip.y) == 4) {
				clip_set = true;
				i++;
				continue;
			}
			clip.x = 0;
			clip.y = 0;

			if (sscanf(argv[i], "%ux%u", &clip.width, &clip.height) == 2) {
				clip_set = true;
				i++;
				continue;
			}

			fatal(argv[0]);
		} else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--reverse")) {
			reverse = true;
			i++;
			continue;
		} else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--geometry")) {
			if (i + 1 >= argc)
				fatal(argv[0]);
			i++;

			if (sscanf(argv[i], "%ux%u+%d+%d", &width, &height, &x, &y) == 4) {
				i++;
				continue;
			}
			x = 0;
			y = 0;

			if (sscanf(argv[i], "%ux%u", &width, &height) == 2) {
				i++;
				continue;
			}

			fatal(argv[0]);
		} else
			fatal(argv[0]);
	}

	XvQueryAdaptors(dpy, root, &num_adaptors, &adaptor_info);
	port = None;
	for (a = 0; a < num_adaptors && port == None; a++) {
		printf ("Adaptor \"%s\" has %lu ports\n",
			adaptor_info[a].name,
			adaptor_info[a].num_ports);
		for (p = 0; p < adaptor_info[a].num_ports; p++) {
			if (XvGrabPort(dpy, adaptor_info[a].base_id + p, CurrentTime) == Success) {
				port = adaptor_info[a].base_id + p;
				break;
			}
		}
	}
	if (port == None) {
		fprintf (stderr, "No port available on %s\n",
			 adaptor_info[a].name);
		return 1;
	}
	printf ("port %u\n", (unsigned int) port);

	formats = XvListImageFormats (dpy, port, &nformats);
	for (f = 0; f < nformats; f++) {
		if (formats[f].type != XvRGB)
			continue;
		if (formats[f].format != XvPacked)
			continue;
		if (formats[f].bits_per_pixel != 16)
			continue;
		if (formats[f].depth != 16)
			continue;
		if (formats[f].num_planes != 1)
			continue;
		if (formats[f].scanline_order != XvTopToBottom)
			continue;
		if (strcmp (formats[f].component_order, "RGB") != 0)
			continue;
		format = &formats[f];
		break;
	}
	if (!format) {
		formats = XvListImageFormats (dpy, port, &nformats);
		for (f = 0; f < nformats; f++) {
			if (formats[f].type != XvYUV)
				continue;
			if (formats[f].format != XvPacked)
				continue;
			if (formats[f].bits_per_pixel != 16)
				continue;
			if (formats[f].num_planes != 1)
				continue;
			if (formats[f].scanline_order != XvTopToBottom)
				continue;
			if (strcmp (formats[f].component_order, "YUYV") != 0)
				continue;
			format = &formats[f];
			break;
		}
	}

	if (!format) {
		fprintf(stderr, "No suitable format\n");
		return 1;
	}

	int colorkey = 0;
	Atom XA_XV_COLORKEY = XInternAtom(dpy, "XV_COLORKEY", True);
	if (XA_XV_COLORKEY != None)
		XvGetPortAttribute(dpy, port, XA_XV_COLORKEY, &colorkey);
	Atom XA_XV_AUTOPAINT_COLORKEY = XInternAtom(dpy, "XV_AUTOPAINT_COLORKEY", True);
	if (XA_XV_AUTOPAINT_COLORKEY != None)
		XvSetPortAttribute(dpy, port, XA_XV_AUTOPAINT_COLORKEY, 0);

	printf("format = %c%c%c%c\n",
	       (format->id & 0x000000ff),
	       (format->id & 0x0000ff00) >> 8,
	       (format->id & 0x00ff0000) >> 16,
	       (format->id & 0xff000000) >> 24);

	parent = root;

	if (deepchild) {
		width -= border << 1;
		height -= border << 1;

		wattr.event_mask = ExposureMask;
		wattr.background_pixel = 0xfff;
		wattr.border_pixel = WhitePixel(dpy, screen);

		parent = XCreateWindow (dpy, parent, x, y, width, height, border,
					CopyFromParent,
					InputOutput,
					CopyFromParent,
					CWEventMask |
					CWBackPixel |
					CWBorderPixel,
					&wattr);

		set_wm_props(dpy, parent, "xvredirect deepchild", argc, argv, fullscreen);

		XMapWindow(dpy, parent);

		toplevel = parent;
	}

	if (child) {
		width -= border << 1;
		height -= border << 1;

		wattr.event_mask = ExposureMask;
		wattr.background_pixel = 0xfff0;
		wattr.border_pixel = BlackPixel(dpy, screen);

		parent = XCreateWindow (dpy, parent, x, y, width, height, border,
					CopyFromParent,
					InputOutput,
					CopyFromParent,
					CWEventMask |
					CWBackPixel |
					CWBorderPixel,
					&wattr);

		set_wm_props(dpy, parent, "xvredirect child", argc, argv, fullscreen);

		XMapWindow(dpy, parent);

		if (!deepchild)
			toplevel = parent;
	}

	width -= border << 1;
	height -= border << 1;

	wattr.event_mask = ExposureMask;
	wattr.background_pixel = colorkey;
	wattr.border_pixel = colorkey ^ 0xffff;

	win = XCreateWindow (dpy, parent, x, y, width, height, border,
			     CopyFromParent,
			     InputOutput,
			     CopyFromParent,
			     CWEventMask |
			     CWBackPixel |
			     CWBorderPixel,
			     &wattr);

	set_wm_props(dpy, win, "xvredirect video", argc, argv, fullscreen);

	XMapWindow(dpy, win);

	if (!child)
		toplevel = win;

	if (siblings) {
		width = (width >> 1) - (border << 1);
		height = (height >> 1) - (border << 1);

		wattr.event_mask = ExposureMask;
		wattr.background_pixel = 0x1010;
		wattr.border_pixel = 0x1001;

		sib = XCreateWindow (dpy, parent, 0, 0, width, height, border,
				     CopyFromParent,
				     InputOutput,
				     CopyFromParent,
				     CWEventMask |
				     CWBackPixel |
				     CWBorderPixel,
				     &wattr);

		set_wm_props(dpy, sib, "xvredirect top sibling", argc, argv, fullscreen);

		XMapWindow(dpy, sib);

		c.sibling = win;
		c.stack_mode = Above;
		XConfigureWindow(dpy, sib, CWSibling | CWStackMode, &c);

		wattr.event_mask = ExposureMask;
		wattr.background_pixel = 0x0101;
		wattr.border_pixel = 0x0110;

		x = width + (border << 1);
		y = height + (border << 1);

		sib = XCreateWindow (dpy, parent, x, y, width, height, border,
				     CopyFromParent,
				     InputOutput,
				     CopyFromParent,
				     CWEventMask |
				     CWBackPixel |
				     CWBorderPixel,
				     &wattr);

		set_wm_props(dpy, sib, "xvredirect bottom sibling", argc, argv, fullscreen);

		XMapWindow(dpy, sib);

		c.sibling = win;
		c.stack_mode = Below;
		XConfigureWindow(dpy, sib, CWSibling | CWStackMode, &c);

		c.x = width / 4;
		c.y = height / 4;
		c.width = width;
		c.height = height;
		XConfigureWindow(dpy, win, CWX|CWY|CWWidth|CWHeight, &c);
	}

	XSync(dpy, False);

	XGetGeometry(dpy, win, &root, &x, &y, &width, &height, &border, &depth);

	data = malloc(WIDTH * HEIGHT * 2);

	if (reverse) {
		memset(data, 0xff, WIDTH * HEIGHT * 2);
		draw_rect(data, 1, 1, HEIGHT-2, 0x00);
		draw_circle(data, 10, 10, (HEIGHT - 20) / 4, 0x0);
	} else {
		memset(data, 0x00, WIDTH * HEIGHT * 2);
		draw_rect(data, 0, 0, HEIGHT, 0xffff);
		draw_circle(data, 10, 10, (HEIGHT - 20) / 4, 0xffff);
	}

	gc = XCreateGC(dpy, win, 0, NULL);

	if (clip_set)
		XSetClipRectangles(dpy, gc, 0, 0, &clip, 1, Unsorted);

	image = XvCreateImage(dpy, port, format->id,
			      data, WIDTH, HEIGHT);


	int ifd = 0;
	int xfd = ConnectionNumber(dpy);

	Atom XA_XV_ROTATION = XInternAtom(dpy, "XV_ROTATION", True);

	int pos = 32;
	int firsttime = 1;
	int redir = -1;
	int top_redir = -1;

	print_redir("video", redir);
	print_redir("toplevel", top_redir);
	
	for (;;) {
		fd_set fds;
		int r;
		int redraw = firsttime;
		firsttime = 0;

		FD_ZERO(&fds);
		FD_SET(ifd, &fds);
		FD_SET(xfd, &fds);

		if (!redraw) {
			r = select(MAX(ifd, xfd) + 1, &fds, NULL, NULL, NULL);
			if (r == 0)
				continue;
			if (r < 0)
				break;
		}

	again:
		while (XPending(dpy)) {
			if (XNextEvent (dpy, &event) != Success)
				break;

			switch (event.type) {
			case Expose:
				printf("expose %lx (%ux%u+%d+%d) (count=%d)\n",
				       event.xexpose.window,
				       event.xexpose.width,
				       event.xexpose.height,
				       event.xexpose.x,
				       event.xexpose.y,
				       event.xexpose.count);

				redraw = 1;
				break;
			}
		}

	do_redraw:
		if (redraw) {
			printf("redraw %ux%u+%d+%d -> %ux%u+%d+%d\n",
			       WIDTH, HEIGHT, 0, 0,
			       width, height, 0, 0);
			XvPutImage(dpy, port, win, gc, image,
				   0, 0, WIDTH, HEIGHT,
				   0, 0, width, height);
			XSync(dpy, False);
			redraw = 0;
			if (XPending(dpy))
				goto again;
		}

		if (FD_ISSET(ifd, &fds)) {
			char cmd[100];
			int old_rotation = 0;
			int rotation = 0;

			FD_CLR(ifd, &fds);

			r = read(ifd, cmd, sizeof cmd);
			if (r <= 0)
				continue;

			if (XA_XV_ROTATION != None)
				XvGetPortAttribute(dpy, port, XA_XV_ROTATION, &old_rotation);

			switch (cmd[0]) {
			case 'm':
				if (redir < 0) {
					redir = CompositeRedirectManual;
					XCompositeRedirectWindow (dpy, win, redir);
					XSync(dpy, False);
					print_redir("video", redir);
					if (XPending(dpy))
						goto again;
				}
				break;
			case 'a':
				if (redir < 0) {
					redir = CompositeRedirectAutomatic;
					XCompositeRedirectWindow (dpy, win, redir);
					XSync(dpy, False);
					print_redir("video", redir);
					if (XPending(dpy))
						goto again;
				}
				break;
			case 'u':
				if (redir >= 0) {
					XCompositeUnredirectWindow (dpy, win, redir);
					XSync(dpy, False);
					redir = -1;
					print_redir("video", redir);
					if (XPending(dpy))
						goto again;
				}
				break;
			case 'M':
				if (top_redir < 0) {
					top_redir = CompositeRedirectManual;
					XCompositeRedirectWindow (dpy, toplevel, top_redir);
					XSync(dpy, False);
					print_redir("toplevel", top_redir);
					if (XPending(dpy))
						goto again;
				}
				break;
			case 'A':
				if (top_redir < 0) {
					top_redir = CompositeRedirectAutomatic;
					XCompositeRedirectWindow (dpy, toplevel, top_redir);
					XSync(dpy, False);
					print_redir("toplevel", top_redir);
					if (XPending(dpy))
						goto again;
				}
				break;
			case 'U':
				if (top_redir >= 0) {
					XCompositeUnredirectWindow (dpy, toplevel, top_redir);
					XSync(dpy, False);
					top_redir = -1;
					print_redir("toplevel", top_redir);
					if (XPending(dpy))
						goto again;
				}
				break;
			case 's':
				XvStopVideo (dpy, port, win);
				XSync(dpy, False);
				if (XPending(dpy))
					goto again;
				break;
			case 'n':
			case '0':
				rotation = (old_rotation & ~0xf) | RR_Rotate_0;
				break;
			case 'l':
			case '9':
				rotation = (old_rotation & ~0xf) | RR_Rotate_90;
				break;
			case 'i':
			case '1':
				rotation = (old_rotation & ~0xf) | RR_Rotate_180;
				break;
			case 'r':
			case '2':
				rotation = (old_rotation & ~0xf) | RR_Rotate_270;
				break;
			case 'x':
				rotation = old_rotation ^ RR_Reflect_X;
				break;
			case 'y':
				rotation = old_rotation ^ RR_Reflect_Y;
				break;
			case '-':
				pos -= 32;
				if (pos < 0)
					pos = 0;
				break;
			case '+':
				pos += 32;
				if (pos > 854)
					pos = 854;
				break;
			default:
				break;
			}

			if (rotation && XA_XV_ROTATION != None) {
				XvSetPortAttribute(dpy, port, XA_XV_ROTATION, rotation);
				XSync(dpy, False);
				redraw = 1;
				if (XPending(dpy))
					goto again;
				goto do_redraw;
			}
		}
	}

	return 0;
}
_______________________________________________
[email protected]: X.Org development
Archives: http://lists.x.org/archives/xorg-devel
Info: http://lists.x.org/mailman/listinfo/xorg-devel

Reply via email to