[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, ®istry_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