[PATCH] xRandr strikes back

Hardening rdp.effort at gmail.com
Sun Mar 10 07:53:30 PDT 2013


This patch adds a wlrandr extension. It is useful to test
mode switching. The patch provides the weston-switch-mode
utility that can be use quite the same way as xrandr to
change graphical modes. For now only the DRM backend supports
mode switching, but other may follow.
---
 clients/Makefile.am   |   10 ++
 clients/switch-mode.c |  300 +++++++++++++++++++++++++++++++++++++++++++++++++
 protocol/Makefile.am  |    3 +-
 protocol/wlrandr.xml  |   62 ++++++++++
 src/Makefile.am       |    5 +
 src/compositor.c      |    1 +
 src/compositor.h      |    3 +
 src/wlrandr.c         |  105 +++++++++++++++++
 8 files changed, 488 insertions(+), 1 deletion(-)
 create mode 100644 clients/switch-mode.c
 create mode 100644 protocol/wlrandr.xml
 create mode 100644 src/wlrandr.c

diff --git a/clients/Makefile.am b/clients/Makefile.am
index 8c9bcd4..c71b94f 100644
--- a/clients/Makefile.am
+++ b/clients/Makefile.am
@@ -1,5 +1,6 @@
 bin_PROGRAMS =					\
 	weston-info				\
+	weston-switch-mode		\
 	$(terminal)
 
 noinst_PROGRAMS =				\
@@ -160,6 +161,13 @@ weston_info_SOURCES =				\
 	../shared/os-compatibility.h
 weston_info_LDADD = $(WESTON_INFO_LIBS)
 
+weston_switch_mode_SOURCES =				\
+	switch-mode.c				\
+	../shared/option-parser.c		\
+	wlrandr-client-protocol.h		\
+	wlrandr-protocol.c
+weston_switch_mode_LDADD = $(WESTON_INFO_LIBS)
+
 weston_desktop_shell_SOURCES =			\
 	desktop-shell.c				\
 	desktop-shell-client-protocol.h		\
@@ -173,6 +181,8 @@ weston_tablet_shell_SOURCES =			\
 weston_tablet_shell_LDADD = libtoytoolkit.la
 
 BUILT_SOURCES =					\
+	wlrandr-client-protocol.h		\
+	wlrandr-protocol.c		\
 	screenshooter-client-protocol.h		\
 	screenshooter-protocol.c		\
 	text-cursor-position-client-protocol.h	\
diff --git a/clients/switch-mode.c b/clients/switch-mode.c
new file mode 100644
index 0000000..7603fff
--- /dev/null
+++ b/clients/switch-mode.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright © 2013 Hardening <rdp.effort at gmail.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <wayland-client.h>
+#include "wlrandr-client-protocol.h"
+#include "../shared/config-parser.h"
+#include "../shared/os-compatibility.h"
+
+#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
+
+static struct wl_list output_list;
+static struct wlrandr *wlRandr;
+int mode_switched;
+uint32_t switch_result;
+
+struct randr_mode {
+	int width, height, refresh, flags;
+	struct wl_list link;
+};
+
+struct randr_output {
+	struct wl_output *output;
+	int transform;
+	char *make;
+	char *model;
+	struct wl_list modes;
+
+	struct wl_list link;
+};
+
+
+
+static void
+switch_mode_done(void *data, struct wlrandr *wlrander, uint32_t result)
+{
+	mode_switched = 1;
+	switch_result = result;
+}
+
+static const struct wlrandr_listener wlrander_listener = {
+		switch_mode_done
+};
+
+static void
+display_handle_geometry(void *data,
+			struct wl_output *wl_output,
+			int x,
+			int y,
+			int physical_width,
+			int physical_height,
+			int subpixel,
+			const char *make,
+			const char *model,
+			int transform)
+{
+	struct randr_output *output;
+	output = wl_output_get_user_data(wl_output);
+	output->make = strdup(make);
+	output->model = strdup(model);
+	output->transform = transform;
+}
+
+static void
+display_handle_mode(void *data,
+		    struct wl_output *wl_output,
+		    uint32_t flags,
+		    int width,
+		    int height,
+		    int refresh)
+{
+	struct randr_output *output;
+	struct randr_mode *mode;
+
+	output = wl_output_get_user_data(wl_output);
+	mode = malloc(sizeof *mode);
+	mode->width = width;
+	mode->height = height;
+	mode->refresh = refresh;
+	mode->flags = flags;
+	wl_list_insert(&output->modes, &mode->link);
+}
+
+static const struct wl_output_listener output_listener = {
+	display_handle_geometry,
+	display_handle_mode
+};
+
+
+static void
+handle_global(void *data, struct wl_registry *registry,
+	      uint32_t name, const char *interface, uint32_t version)
+{
+	static struct randr_output *output;
+
+	if (strcmp(interface, "wl_output") == 0) {
+		output = calloc(sizeof *output, 1);
+		output->output = wl_registry_bind(registry, name, &wl_output_interface, 1);
+		wl_list_init(&output->modes);
+		wl_list_insert(&output_list, &output->link);
+		wl_output_add_listener(output->output, &output_listener, output);
+	} else if (strcmp(interface, "wlrandr") == 0) {
+		wlRandr = wl_registry_bind(registry, name, &wlrandr_interface, 1);
+	}
+}
+
+static void
+handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
+{
+	/* XXX: unimplemented */
+}
+
+static const struct wl_registry_listener registry_listener = {
+	handle_global,
+	handle_global_remove
+};
+
+static int
+parse_mode(const char *mode, int *w, int *h)
+{
+	/* parses a mode with the format <width>x<height> */
+	const char *ptr;
+	ptr = strchr(mode, 'x');
+	if(!ptr)
+		return -1;
+	*w = strtoul(mode, NULL, 0);
+	if(!*w || *w > 5000)
+		return -1;
+
+	ptr++;
+	if(!*ptr)
+		return -1;
+	*h = strtoul(ptr, NULL, 0);
+	if(!*h || *h > 5000)
+		return -1;
+	return 0;
+}
+
+static void
+usage(char *argv[]) {
+	fprintf(stderr, "usage: %s [options]\n", argv[0]);
+	fprintf(stderr, "  where options are:\n");
+	fprintf(stderr, "  --mode <width>x<height>\n");
+	fprintf(stderr, "  --refresh <rate> or -r <rate> or --rate <rate>\n");
+	fprintf(stderr, "  --output <output>\n");
+}
+
+static struct randr_output *
+find_output(const char *name) {
+	struct randr_output *ret;
+
+	wl_list_for_each(ret, &output_list, link) {
+		if(strcmp(ret->model, name) == 0)
+			return ret;
+	}
+	return 0;
+}
+
+static void
+print_available_modes(struct randr_output *output) {
+	struct randr_mode *mode;
+	fprintf(stderr, "%s %s:\n", output->make, output->model);
+	wl_list_for_each(mode, &output->modes, link) {
+		fprintf(stderr, "  %s%dx%d\t%d\n", (mode->flags & WL_OUTPUT_MODE_CURRENT) ? "*" : "",
+				mode->width, mode->height, mode->refresh);
+	}
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct wl_display *display;
+	struct wl_registry *registry;
+	struct randr_output *output;
+	struct randr_mode *mode;
+	int mode_found;
+
+	char *mode_string= NULL;
+	char *output_name = NULL;
+	int width, height, refresh = -1;
+
+	const struct weston_option options[] = {
+		{ WESTON_OPTION_STRING,  "mode", 0, &mode_string },
+		{ WESTON_OPTION_STRING,  "output", 0, &output_name },
+		{ WESTON_OPTION_UNSIGNED_INTEGER,  "rate", 0, &refresh },
+		{ WESTON_OPTION_UNSIGNED_INTEGER,  "refresh", 0, &refresh }
+	};
+
+	if(parse_options(options, ARRAY_LENGTH(options), &argc, argv) < 0) {
+		usage(argv);
+		exit(EXIT_FAILURE);
+	}
+
+	display = wl_display_connect(NULL);
+	if (display == NULL) {
+		fprintf(stderr, "failed to create display: %m\n");
+		exit(EXIT_FAILURE);
+	}
+
+	wl_list_init(&output_list);
+	registry = wl_display_get_registry(display);
+	wl_registry_add_listener(registry, &registry_listener, NULL);
+	wl_display_dispatch(display);
+	wl_display_roundtrip(display);
+	if (wlRandr == NULL) {
+		fprintf(stderr, "display doesn't support wlRandr\n");
+		return -1;
+	}
+
+	wlrandr_add_listener(wlRandr, &wlrander_listener, wlRandr);
+
+	if(output_name) {
+		output = find_output(output_name);
+		if(!output) {
+			fprintf(stderr, "output %s not found\n", output_name);
+			exit(EXIT_FAILURE);
+		}
+	} else {
+		if(wl_list_length(&output_list) != 1) {
+			fprintf(stderr, "multiple output detected, you should specify the "
+					"target output with --output <output>\n");
+			exit(EXIT_FAILURE);
+		}
+
+		output = wl_container_of(output_list.next, output, link);
+	}
+
+	if(!mode_string) {
+		print_available_modes(output);
+		exit(EXIT_SUCCESS);
+	}
+
+	if(parse_mode(argv[1], &width, &height) < 0) {
+		fprintf(stderr, "invalid mode %s", mode_string);
+		usage(argv);
+		exit(EXIT_FAILURE);
+	}
+
+	mode_found = 0;
+	wl_list_for_each(mode, &output->modes, link) {
+		if(mode->width == width && mode->height == height) {
+			if(refresh < 0 || mode->refresh == refresh) {
+				mode_found = 1;
+				break;
+			}
+		}
+	}
+
+	if(!mode_found) {
+		fprintf(stderr, "mode %dx%d not available\n", width, height);
+		exit(EXIT_FAILURE);
+	}
+
+	if(mode->flags & WL_OUTPUT_MODE_CURRENT) {
+		fprintf(stderr, "mode %dx%d is already the current mode\n", width, height);
+		exit(0);
+	}
+
+	if(refresh < 0)
+		refresh = 0;
+	wlrandr_switch_mode(wlRandr, output->output, width, height, refresh);
+	mode_switched = 0;
+	while (!mode_switched)
+		wl_display_roundtrip(display);
+
+	switch(switch_result) {
+	case WLRANDR_SWITCH_MODE_RESULT_FAILED:
+		fprintf(stderr, "something failed in weston during mode switch\n");
+		break;
+	case WLRANDR_SWITCH_MODE_RESULT_NOT_AVAILABLE:
+		fprintf(stderr, "mode not available in weston\n");
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
diff --git a/protocol/Makefile.am b/protocol/Makefile.am
index 8c1803c..1454909 100644
--- a/protocol/Makefile.am
+++ b/protocol/Makefile.am
@@ -6,4 +6,5 @@ EXTRA_DIST =					\
 	text.xml				\
 	input-method.xml			\
 	workspaces.xml				\
-	wayland-test.xml
+	wayland-test.xml			\
+	wlrandr.xml
diff --git a/protocol/wlrandr.xml b/protocol/wlrandr.xml
new file mode 100644
index 0000000..192ba24
--- /dev/null
+++ b/protocol/wlrandr.xml
@@ -0,0 +1,62 @@
+<protocol name="wlrandr">
+  <copyright>
+    Copyright © 2013 Hardening, rdp.effort at gmail.com
+
+    Permission to use, copy, modify, distribute, and sell this
+    software and its documentation for any purpose is hereby granted
+    without fee, provided that the above copyright notice appear in
+    all copies and that both that copyright notice and this permission
+    notice appear in supporting documentation, and that the name of
+    the copyright holders not be used in advertising or publicity
+    pertaining to distribution of the software without specific,
+    written prior permission.  The copyright holders make no
+    representations about the suitability of this software for any
+    purpose.  It is provided "as is" without express or implied
+    warranty.
+
+    THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+    FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+    THIS SOFTWARE.
+  </copyright>
+
+
+  <interface name="wlrandr" version="1">
+    <description summary="xRandr for weston">
+    This is a port of the xRandr extension for weston
+    </description>
+  
+    <request name="switch_mode">
+      <description summary="switch mode">
+        Asks weston to switch graphical mode for the given output.
+      </description>
+    
+      <arg name="output" type="object" interface="wl_output"/>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+      <arg name="refresh" type="int"/>      
+    </request>
+    
+    <enum name="switch_mode_result">
+      <description summary="switch mode result">
+        The result of a switch_mode request
+      </description>
+    	
+    	<entry name="ok" value="0"
+    		summary="the mode switch completed successfully"/>
+    	<entry name="failed" value="1"
+    		summary="something failed during mode switch (internal error)"/>
+    	<entry name="not_available" value="2"
+    		summary="the requested mode was not available"/>
+    </enum>
+    
+    <event name="done">
+    	<arg name='result' type='uint'/>
+    </event>
+  </interface>
+
+</protocol>
diff --git a/src/Makefile.am b/src/Makefile.am
index 2c93a7b..2410327 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -34,6 +34,9 @@ weston_SOURCES =				\
 	input-method-server-protocol.h		\
 	workspaces-protocol.c			\
 	workspaces-server-protocol.h		\
+	wlrandr.c 				\
+	wlrandr-protocol.c			\
+	wlrandr-server-protocol.h		\
 	bindings.c				\
 	animation.c				\
 	gl-renderer.h				\
@@ -252,6 +255,8 @@ BUILT_SOURCES =					\
 	input-method-server-protocol.h		\
 	workspaces-server-protocol.h		\
 	workspaces-protocol.c			\
+	wlrandr-server-protocol.h		\
+	wlrandr-protocol.c			\
 	git-version.h
 
 CLEANFILES = $(BUILT_SOURCES)
diff --git a/src/compositor.c b/src/compositor.c
index a2860fd..c372968 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -3075,6 +3075,7 @@ weston_compositor_init(struct weston_compositor *ec,
 	ec->ping_handler = NULL;
 
 	screenshooter_create(ec);
+	wlrandr_create(ec);
 	text_cursor_position_notifier_create(ec);
 	text_backend_init(ec);
 
diff --git a/src/compositor.h b/src/compositor.h
index 4a0c1e3..3f56c73 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -775,6 +775,9 @@ tty_activate_vt(struct tty *tty, int vt);
 void
 screenshooter_create(struct weston_compositor *ec);
 
+void
+wlrandr_create(struct weston_compositor *ec);
+
 struct clipboard *
 clipboard_create(struct weston_seat *seat);
 
diff --git a/src/wlrandr.c b/src/wlrandr.c
new file mode 100644
index 0000000..a1950a2
--- /dev/null
+++ b/src/wlrandr.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright © 2013 Hardening <rdp.effort at gmail.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "compositor.h"
+#include "wlrandr-server-protocol.h"
+
+struct wlrandr_impl {
+	struct wl_object base;
+	struct weston_compositor *ec;
+	struct wl_global *global;
+	struct wl_listener destroy_listener;
+};
+
+static void
+wlrandr_switch_mode(struct wl_client *client, struct wl_resource *resource,
+			    struct wl_resource *output_resource,
+			    int32_t width, int32_t height, int32_t refresh) {
+	struct weston_output *o = (struct weston_output *)output_resource->data;
+	struct weston_mode mode;
+
+	weston_log("switching to %dx%d@%d\n", width, height, refresh);
+	mode.width = width;
+	mode.height = height;
+	mode.refresh = refresh;
+
+	int ret = weston_output_switch_mode(o, &mode);
+	if(ret < 0) {
+		switch(ret) {
+		case -ENOENT:
+			wlrandr_send_done(resource, WLRANDR_SWITCH_MODE_RESULT_NOT_AVAILABLE);
+			break;
+		default:
+			/* generic error */
+			wlrandr_send_done(resource, WLRANDR_SWITCH_MODE_RESULT_FAILED);
+			break;
+		}
+	} else {
+		wlrandr_send_done(resource, WLRANDR_SWITCH_MODE_RESULT_OK);
+	}
+}
+
+struct wlrandr_interface wlrandr_implementation = {
+	wlrandr_switch_mode
+};
+
+static void
+bind_wlrandr(struct wl_client *client,
+	     void *data, uint32_t version, uint32_t id)
+{
+	wl_client_add_object(client, &wlrandr_interface, &wlrandr_implementation,
+			id, data);
+}
+
+static void
+wlrandr_destroy(struct wl_listener *listener, void *data)
+{
+	struct wlrandr_impl *randr =
+		container_of(listener, struct wlrandr_impl, destroy_listener);
+
+	wl_display_remove_global(randr->ec->wl_display, randr->global);
+	free(randr);
+}
+
+
+void
+wlrandr_create(struct weston_compositor *ec)
+{
+	struct wlrandr_impl *randr;
+
+	randr = malloc(sizeof *randr);
+	if (randr == NULL)
+		return;
+
+	randr->base.interface = &wlrandr_interface;
+	randr->base.implementation = (void(**)(void))&wlrandr_implementation;
+	randr->ec = ec;
+	randr->global = wl_display_add_global(ec->wl_display, &wlrandr_interface,
+						randr, bind_wlrandr);
+
+	randr->destroy_listener.notify = wlrandr_destroy;
+	wl_signal_add(&ec->destroy_signal, &randr->destroy_listener);
+}
-- 
1.7.10.4



More information about the wayland-devel mailing list