xf86-video-intel: tools/virtual.c

Chris Wilson ickle at kemper.freedesktop.org
Sun Sep 1 08:09:21 PDT 2013


 tools/virtual.c |  453 ++++++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 357 insertions(+), 96 deletions(-)

New commits:
commit a41acee7282f007ed8e589395597f8aea3b22361
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Sep 1 13:56:18 2013 +0100

    intel-virtual-output: Register as a singleton
    
    If the user tries to use multiple commands to combine multiple displays,
    reroute those to the first instance.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/tools/virtual.c b/tools/virtual.c
index 42771ca..9bbd390 100644
--- a/tools/virtual.c
+++ b/tools/virtual.c
@@ -23,6 +23,7 @@
  */
 
 #include <X11/Xlib.h>
+#include <X11/Xatom.h>
 
 #include <X11/Xlibint.h>
 #include <X11/extensions/XShm.h>
@@ -42,6 +43,7 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
@@ -133,10 +135,15 @@ struct context {
 	struct display *display;
 	struct clone *clones;
 	struct pollfd *pfd;
+#define timer pfd[0].fd
 	Display *record;
 	int nclone;
 	int ndisplay;
 	int nfd;
+
+	Atom singleton;
+	char command[1024];
+	int command_continuation;
 };
 
 static int xlib_vendor_is_xorg(Display *dpy)
@@ -166,7 +173,6 @@ can_use_shm(Display *dpy,
 	    int *shm_pixmap)
 {
 	XShmSegmentInfo shm;
-	int (*old_handler)(Display *display, XErrorEvent *event);
 	Status success;
 	XExtCodes *codes;
 	int major, minor, has_shm, has_pixmap;
@@ -187,14 +193,12 @@ can_use_shm(Display *dpy,
 		return 0;
 	}
 
-	_x_error_occurred = 0;
-
 	XSync(dpy, False);
-	old_handler = XSetErrorHandler(_check_error_handler);
+	_x_error_occurred = 0;
 
 	success = XShmAttach(dpy, &shm);
-	XSync(dpy, False);
 
+	XSync(dpy, False);
 	has_shm = success && _x_error_occurred == 0;
 
 	codes = XInitExtension(dpy, SHMNAME);
@@ -231,17 +235,14 @@ can_use_shm(Display *dpy,
 		e.offset = 0;
 
 		XSendEvent(dpy, e.drawable, False, 0, (XEvent *)&e);
-		XSync(dpy, False);
 
+		XSync(dpy, False);
 		has_pixmap = _x_error_occurred == 0;
 	}
 
 	if (success)
 		XShmDetach(dpy, &shm);
 
-	XSync(dpy, False);
-	XSetErrorHandler(old_handler);
-
 	shmctl(shm.shmid, IPC_RMID, NULL);
 	shmdt(shm.shmaddr);
 
@@ -310,11 +311,17 @@ static int clone_update_modes(struct clone *clone)
 	clone->dst.rr_crtc = from_info->crtc;
 
 	/* Clear all current UserModes on the output, including any active ones */
-	if (to_info->crtc)
+	if (to_info->crtc) {
+		DBG(("%s(%s-%s): disabling active CRTC\n", __func__,
+		     DisplayString(clone->src.dpy), clone->src.name));
 		XRRSetCrtcConfig(clone->src.dpy, to_res, to_info->crtc, CurrentTime,
 				0, 0, None, RR_Rotate_0, NULL, 0);
-	for (i = 0; i < to_info->nmode; i++)
+	}
+	for (i = 0; i < to_info->nmode; i++) {
+		DBG(("%s(%s-%s): deleting mode %ld\n", __func__,
+		     DisplayString(clone->src.dpy), clone->src.name, (long)to_info->modes[i]));
 		XRRDeleteOutputMode(clone->src.dpy, clone->src.rr_output, to_info->modes[i]);
+	}
 
 	clone->src.rr_crtc = 0;
 
@@ -1163,28 +1170,6 @@ static int record_mouse(struct context *ctx)
 	return ConnectionNumber(dpy);
 }
 
-static int timer(int hz)
-{
-	struct itimerspec it;
-	int fd;
-
-	fd = timerfd_create(CLOCK_MONOTONIC_COARSE, TFD_NONBLOCK);
-	if (fd < 0)
-		fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
-	if (fd < 0)
-		return -ETIME;
-
-	it.it_interval.tv_sec = 0;
-	it.it_interval.tv_nsec = 1000000000 / hz;
-	it.it_value = it.it_interval;
-	if (timerfd_settime(fd, 0, &it, NULL) < 0) {
-		close(fd);
-		return -ETIME;
-	}
-
-	return fd;
-}
-
 static int bad_visual(Visual *visual, int depth)
 {
 	switch (depth) {
@@ -1450,7 +1435,6 @@ static int bumblebee_open(struct context *ctx)
 {
 	char buf[256];
 	struct sockaddr_un addr;
-	Display *dpy;
 	int fd, len;
 
 	fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
@@ -1483,13 +1467,7 @@ static int bumblebee_open(struct context *ctx)
 	while (isspace(buf[--len]))
 		buf[len] = '\0';
 
-	dpy = XOpenDisplay(buf+7);
-	if (dpy == NULL) {
-		fprintf(stderr, "Unable to connect to bumblebee Xserver on %s\n", buf+7);
-		return -ECONNREFUSED;
-	}
-
-	return add_display(ctx, dpy);
+	return display_open(ctx, buf+7);
 
 err:
 	fprintf(stderr, "Unable to connect to bumblebee\n");
@@ -1513,11 +1491,33 @@ static int display_init_damage(struct display *display)
 	return 0;
 }
 
+static int timerfd(int hz)
+{
+	struct itimerspec it;
+	int fd;
+
+	fd = timerfd_create(CLOCK_MONOTONIC_COARSE, TFD_NONBLOCK);
+	if (fd < 0)
+		fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
+	if (fd < 0)
+		return -ETIME;
+
+	it.it_interval.tv_sec = 0;
+	it.it_interval.tv_nsec = 1000000000 / hz;
+	it.it_value = it.it_interval;
+	if (timerfd_settime(fd, 0, &it, NULL) < 0) {
+		close(fd);
+		return -ETIME;
+	}
+
+	return fd;
+}
+
 static int context_init(struct context *ctx)
 {
 	memset(ctx, 0, sizeof(*ctx));
 
-	ctx->pfd = malloc(sizeof(struct pollfd));
+	ctx->pfd = malloc(2*sizeof(struct pollfd));
 	if (ctx->pfd == NULL)
 		return -ENOMEM;
 
@@ -1529,9 +1529,37 @@ static int context_init(struct context *ctx)
 	if (ctx->display == NULL)
 		return -ENOMEM;
 
+	ctx->pfd[0].fd = timerfd(60);
+	if (ctx->pfd[0].fd < 0)
+		return ctx->pfd[0].fd;
+
+	ctx->pfd[0].events = 0;
+	ctx->pfd[0].revents = 0;
+	ctx->nfd++;
+
 	return 0;
 }
 
+static void context_build_lists(struct context *ctx)
+{
+	int n, m;
+
+	for (n = 1; n < ctx->ndisplay; n++) {
+		struct display *d = &ctx->display[n];
+
+		d->clone = NULL;
+		for (m = 0; m < ctx->nclone; m++) {
+			struct clone *c = &ctx->clones[m];
+
+			if (c->dst.display != d)
+				continue;
+
+			c->next = d->clone;
+			d->clone = c;
+		}
+	}
+}
+
 static int add_fd(struct context *ctx, int fd)
 {
 	if (fd < 0)
@@ -1545,6 +1573,7 @@ static int add_fd(struct context *ctx, int fd)
 
 	ctx->pfd[ctx->nfd].fd = fd;
 	ctx->pfd[ctx->nfd].events = POLLIN;
+	ctx->pfd[ctx->nfd].revents = 0;
 	ctx->nfd++;
 	return 0;
 }
@@ -1573,9 +1602,14 @@ static struct clone *add_clone(struct context *ctx)
 	return memset(&ctx->clones[ctx->nclone++], 0, sizeof(struct clone));
 }
 
+static struct display *last_display(struct context *ctx)
+{
+	return &ctx->display[ctx->ndisplay-1];
+}
+
 static int last_display_add_clones(struct context *ctx)
 {
-	struct display *display = &ctx->display[ctx->ndisplay-1];
+	struct display *display = last_display(ctx);
 	XRRScreenResources *res;
 	char buf[80];
 	int i, ret;
@@ -1627,48 +1661,248 @@ static int last_display_add_clones(struct context *ctx)
 	return 0;
 }
 
-static void display_flush(struct display *display)
+static int last_display_clone(struct context *ctx, int fd)
 {
-	display_flush_cursor(display);
+	if (fd < 0)
+		goto err;
 
-	if (!display->flush)
-		return;
+	fd = add_fd(ctx, fd);
+	if (fd < 0)
+		goto err;
 
-	DBG(("%s(%s)\n", __func__, DisplayString(display->dpy)));
+	fd = last_display_add_clones(ctx);
+	if (fd)
+		goto err;
 
-	XFlush(display->dpy);
-	display->flush = 0;
+err:
+	context_build_lists(ctx);
+	return fd;
 }
 
-static void context_build_lists(struct context *ctx)
+static int first_display_has_singleton(struct context *ctx)
 {
-	int n, m;
+	struct display *display = ctx->display;
+	unsigned long nitems, bytes;
+	unsigned char *prop;
+	int format;
+	Atom type;
+
+	ctx->singleton = XInternAtom(display->dpy, "intel-virtual-output-singleton", False);
+
+	XGetWindowProperty(display->dpy, display->root, ctx->singleton,
+			   0, 0, 0, AnyPropertyType, &type, &format, &nitems, &bytes, &prop);
+	DBG(("%s: singleton registered? %d\n", DisplayString(display->dpy), type != None));
+	return type != None;
+}
 
-	for (n = 1; n < ctx->ndisplay; n++) {
-		struct display *d = &ctx->display[n];
+static int first_display_wait_for_ack(struct context *ctx, int timeout, int id)
+{
+	struct display *display = ctx->display;
+	struct pollfd pfd;
+	char expect[5];
 
-		for (m = 0; m < ctx->nclone; m++) {
-			struct clone *c = &ctx->clones[m];
+	sprintf(expect, "%04xR", id);
+	DBG(("%s: wait for act '%c%c%c%c%c'\n",
+	     DisplayString(display->dpy),
+	     expect[0], expect[1], expect[2], expect[3], expect[4]));
 
-			if (c->dst.display != d)
+	XFlush(display->dpy);
+
+	pfd.fd = ConnectionNumber(display->dpy);
+	pfd.events = POLLIN;
+	do {
+		if (poll(&pfd, 1, timeout) <= 0)
+			return -ETIME;
+
+		while (XPending(display->dpy)) {
+			XEvent e;
+			XClientMessageEvent *cme;
+
+			XNextEvent(display->dpy, &e);
+			DBG(("%s: reading event type %d\n", DisplayString(display->dpy), e.type));
+
+			if (e.type != ClientMessage)
 				continue;
 
-			c->next = d->clone;
-			d->clone = c;
+			cme = (XClientMessageEvent *)&e;
+			if (cme->message_type != ctx->singleton)
+				continue;
+			if (cme->format != 8)
+				continue;
+
+			DBG(("%s: client message '%c%c%c%c%c'\n",
+			     DisplayString(display->dpy),
+			     cme->data.b[0],
+			     cme->data.b[1],
+			     cme->data.b[2],
+			     cme->data.b[3],
+			     cme->data.b[4]));
+			if (memcmp(cme->data.b, expect, 5))
+				continue;
+
+			return -atoi(cme->data.b + 5);
+		}
+	} while(1);
+}
+
+#if defined(__GNUC__) && (__GNUC__ > 3)
+__attribute__((format(gnu_printf, 3, 4)))
+#endif
+static int first_display_send_command(struct context *ctx, int timeout,
+				      const char *format,
+				      ...)
+{
+	struct display *display = ctx->display;
+	char buf[1024], *b;
+	int len, id;
+	va_list va;
+
+	id = rand() & 0xffff;
+	sprintf(buf, "%04x", id);
+	va_start(va, format);
+	len = vsnprintf(buf+4, sizeof(buf)-4, format, va)+5;
+	va_end(va);
+	assert(len < sizeof(buf));
+
+	DBG(("%s: send command '%s'\n", DisplayString(display->dpy), buf));
+
+	b = buf;
+	while (len) {
+		XClientMessageEvent msg;
+		int n = len;
+		if (n > sizeof(msg.data.b))
+			n = sizeof(msg.data.b);
+		len -= n;
+
+		msg.type = ClientMessage;
+		msg.serial = 0;
+		msg.message_type = ctx->singleton;
+		msg.format = 8;
+		memcpy(msg.data.b, b, n);
+		b += n;
+
+		XSendEvent(display->dpy, display->root, False, PropertyChangeMask, (XEvent *)&msg);
+	}
+
+	return first_display_wait_for_ack(ctx, timeout, id);
+}
+
+static void first_display_reply(struct context *ctx, int result)
+{
+	struct display *display = ctx->display;
+	XClientMessageEvent msg;
+
+	sprintf(msg.data.b, "%c%c%c%cR%d",
+	     ctx->command[0],
+	     ctx->command[1],
+	     ctx->command[2],
+	     ctx->command[3],
+	     -result);
+
+	DBG(("%s: send reply '%s'\n", DisplayString(display->dpy), msg.data.b));
+
+	msg.type = ClientMessage;
+	msg.serial = 0;
+	msg.message_type = ctx->singleton;
+	msg.format = 8;
+
+	XSendEvent(display->dpy, display->root, False, PropertyChangeMask, (XEvent *)&msg);
+	XFlush(display->dpy);
+}
+
+static void first_display_handle_command(struct context *ctx,
+					 const char *msg)
+{
+	int len;
+
+	DBG(("client message!\n"));
+
+	for (len = 0; len < 20 && msg[len]; len++)
+		;
+
+	if (ctx->command_continuation + len > sizeof(ctx->command)) {
+		ctx->command_continuation = 0;
+		return;
+	}
+
+	memcpy(ctx->command + ctx->command_continuation, msg, len);
+	ctx->command_continuation += len;
+
+	if (len < 20) {
+		ctx->command[ctx->command_continuation] = 0;
+		DBG(("client command complete! '%s'\n", ctx->command));
+		switch (ctx->command[4]) {
+		case 'B':
+			first_display_reply(ctx, last_display_clone(ctx, bumblebee_open(ctx)));
+			break;
+		case 'C':
+			first_display_reply(ctx, last_display_clone(ctx, display_open(ctx, ctx->command + 5)));
+			break;
+		case 'P':
+			first_display_reply(ctx, 0);
+			break;
+		case 'R':
+			break;
 		}
+		ctx->command_continuation = 0;
+		return;
 	}
 }
 
+static int first_display_register_as_singleton(struct context *ctx)
+{
+	struct display *display = ctx->display;
+	struct pollfd pfd;
+
+	XChangeProperty(display->dpy, display->root, ctx->singleton,
+			XA_STRING, 8, PropModeReplace, (unsigned char *)".", 1);
+	XFlush(display->dpy);
+
+	/* And eat the notify (presuming that it is ours!) */
+
+	pfd.fd = ConnectionNumber(display->dpy);
+	pfd.events = POLLIN;
+	do {
+		if (poll(&pfd, 1, 1000) <= 0) {
+			fprintf(stderr, "Failed to register as singleton\n");
+			return EBUSY;
+		}
+
+		while (XPending(display->dpy)) {
+			XPropertyEvent pe;
+
+			XNextEvent(display->dpy, (XEvent *)&pe);
+			DBG(("%s: reading event type %d\n", DisplayString(display->dpy), pe.type));
+
+			if (pe.type == PropertyNotify &&
+			    pe.atom == ctx->singleton)
+				return 0;
+		}
+	} while(1);
+}
+
+static void display_flush(struct display *display)
+{
+	display_flush_cursor(display);
+
+	if (!display->flush)
+		return;
+
+	DBG(("%s(%s)\n", __func__, DisplayString(display->dpy)));
+
+	XFlush(display->dpy);
+	display->flush = 0;
+}
+
 int main(int argc, char **argv)
 {
 	struct context ctx;
-	int (*old_handler)(Display *display, XErrorEvent *event);
 	const char *src_name = NULL;
 	uint64_t count;
 	int enable_timer = 0;
-	int i, ret, daemonize = 1, bumblebee = 0;
+	int i, ret, daemonize = 1, bumblebee = 0, singleton = 1;
 
-	while ((i = getopt(argc, argv, "bd:fh")) != -1) {
+	while ((i = getopt(argc, argv, "bd:fhS")) != -1) {
 		switch (i) {
 		case 'd':
 			src_name = optarg;
@@ -1679,6 +1913,9 @@ int main(int argc, char **argv)
 		case 'b':
 			bumblebee = 1;
 			break;
+		case 'S':
+			singleton = 0;
+			break;
 		case 'h':
 		default:
 			usage(argv[0]);
@@ -1690,10 +1927,42 @@ int main(int argc, char **argv)
 	if (ret)
 		return -ret;
 
+	XSetErrorHandler(_check_error_handler);
+
 	ret = add_fd(&ctx, display_open(&ctx, src_name));
 	if (ret)
 		return -ret;
 
+	if (singleton) {
+		XSelectInput(ctx.display->dpy, ctx.display->root, PropertyChangeMask);
+		if (first_display_has_singleton(&ctx)) {
+			DBG(("%s: pinging singleton\n", DisplayString(ctx.display->dpy)));
+			ret = first_display_send_command(&ctx, 2000, "P");
+			if (ret) {
+				if (ret != -ETIME)
+					return -ret;
+				DBG(("No reply from singleton; assuming control\n"));
+			} else {
+				DBG(("%s: singleton active, sending open commands\n", DisplayString(ctx.display->dpy)));
+				if (optind == argc || bumblebee) {
+					ret = first_display_send_command(&ctx, 5000, "B");
+					if (ret && ret != -EBUSY)
+						return -ret;
+				}
+				for (i = optind; i < argc; i++) {
+					ret = first_display_send_command(&ctx, 5000, "C%s", argv[i]);
+					if (ret && ret != -EBUSY)
+						return -ret;
+				}
+
+				return 0;
+			}
+		}
+		ret = first_display_register_as_singleton(&ctx);
+		if (ret)
+			return ret;
+	}
+
 	ret = display_init_damage(ctx.display);
 	if (ret)
 		return ret;
@@ -1701,11 +1970,8 @@ int main(int argc, char **argv)
 	XRRSelectInput(ctx.display->dpy, ctx.display->root, RRScreenChangeNotifyMask);
 	XFixesSelectCursorInput(ctx.display->dpy, ctx.display->root, XFixesDisplayCursorNotifyMask);
 
-	XSync(ctx.display->dpy, False);
-	old_handler = XSetErrorHandler(_check_error_handler);
-
 	if (optind == argc || bumblebee) {
-		ret = add_fd(&ctx, bumblebee_open(&ctx));
+		ret = last_display_clone(&ctx, bumblebee_open(&ctx));
 		if (ret) {
 			if (!bumblebee) {
 				usage(argv[0]);
@@ -1713,44 +1979,23 @@ int main(int argc, char **argv)
 			}
 			return -ret;
 		}
-
-		ret = last_display_add_clones(&ctx);
-		if (ret)
-			return -ret;
 	}
 
 	for (i = optind; i < argc; i++) {
-		ret = add_fd(&ctx, display_open(&ctx, argv[i]));
+		ret = last_display_clone(&ctx, display_open(&ctx, argv[i]));
 		if (ret) {
 			if (ret == -EBUSY)
 				continue;
-
 			return -ret;
 		}
-
-		ret = last_display_add_clones(&ctx);
-		if (ret)
-			return -ret;
 	}
 
-	context_build_lists(&ctx);
-
-	XSync(ctx.display->dpy, False);
-	XSetErrorHandler(old_handler);
-
 	ret = add_fd(&ctx, record_mouse(&ctx));
 	if (ret) {
 		fprintf(stderr, "XTEST extension not supported by display \"%s\"\n", DisplayString(ctx.display[0].dpy));
 		return -ret;
 	}
 
-	ret = add_fd(&ctx, timer(60));
-	if (ret) {
-		fprintf(stderr, "Failed to setup timer\n");
-		return -ret;
-	}
-	ctx.nfd--; /* we only conditionally poll the timer */
-
 	if (daemonize && daemon(0, 0))
 		return EINVAL;
 
@@ -1758,11 +2003,11 @@ int main(int argc, char **argv)
 		XEvent e;
 		int reconfigure = 0;
 
-		ret = poll(ctx.pfd, ctx.nfd + enable_timer, -1);
+		ret = poll(ctx.pfd + !enable_timer, ctx.nfd - !enable_timer, -1);
 		if (ret <= 0)
 			break;
 
-		if (ctx.pfd[0].revents) {
+		if (ctx.pfd[1].revents) {
 			int damaged = 0;
 
 			do {
@@ -1773,7 +2018,7 @@ int main(int argc, char **argv)
 					for (i = 0; i < ctx.nclone; i++)
 						clone_damage(&ctx.clones[i], &de->area);
 					if (!enable_timer)
-						enable_timer = read(ctx.pfd[ctx.nfd].fd, &count, sizeof(count)) > 0;
+						enable_timer = read(ctx.timer, &count, sizeof(count)) > 0;
 					damaged++;
 				} else if (e.type == ctx.display->xfixes_event + XFixesCursorNotify) {
 					XFixesCursorImage *cur;
@@ -1789,11 +2034,27 @@ int main(int argc, char **argv)
 				} else if (e.type == ctx.display->rr_event + RRScreenChangeNotify) {
 					reconfigure = 1;
 					if (!enable_timer)
-						enable_timer = read(ctx.pfd[ctx.nfd].fd, &count, sizeof(count)) > 0;
+						enable_timer = read(ctx.timer, &count, sizeof(count)) > 0;
+				} else if (e.type == PropertyNotify) {
+					XPropertyEvent *pe = (XPropertyEvent *)&e;
+					if (pe->atom == ctx.singleton) {
+						DBG(("lost control of singleton\n"));
+						return 0;
+					}
+				} else if (e.type == ClientMessage) {
+					XClientMessageEvent *cme;
+
+					cme = (XClientMessageEvent *)&e;
+					if (cme->message_type != ctx.singleton)
+						continue;
+					if (cme->format != 8)
+						continue;
+
+					first_display_handle_command(&ctx, cme->data.b);
 				} else {
 					DBG(("unknown event %d\n", e.type));
 				}
-			} while (XPending(ctx.display->dpy) || poll(ctx.pfd, 1, 0) > 0);
+			} while (XPending(ctx.display->dpy) || poll(&ctx.pfd[1], 1, 0) > 0);
 
 			if (damaged)
 				XDamageSubtract(ctx.display->dpy, ctx.display->damage, None, None);
@@ -1801,7 +2062,7 @@ int main(int argc, char **argv)
 		}
 
 		for (i = 1; ret && i < ctx.ndisplay; i++) {
-			if (ctx.pfd[i].revents == 0)
+			if (ctx.pfd[i+1].revents == 0)
 				continue;
 
 			do {
@@ -1820,7 +2081,7 @@ int main(int argc, char **argv)
 						}
 					}
 				}
-			} while (XPending(ctx.display[i].dpy) || poll(&ctx.pfd[i], 1, 0) > 0);
+			} while (XPending(ctx.display[i].dpy) || poll(&ctx.pfd[i+1], 1, 0) > 0);
 
 			ret--;
 		}
@@ -1831,7 +2092,7 @@ int main(int argc, char **argv)
 		for (i = 0; i < ctx.nclone; i++)
 			clone_update(&ctx.clones[i]);
 
-		if (enable_timer && read(ctx.pfd[ctx.nfd].fd, &count, sizeof(count)) > 0 && count > 0) {
+		if (enable_timer && read(ctx.timer, &count, sizeof(count)) > 0 && count > 0) {
 			ret = 0;
 			for (i = 0; i < ctx.nclone; i++)
 				ret |= clone_paint(&ctx.clones[i]);


More information about the xorg-commit mailing list