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