Functions implemented in this application 1) Query output mode list 2) Update properties of output (transform, scale, mode setting) 3) Position of output (leftof and rightof are supported.) 4) Custom a mode for output. 5) Delete mode of output. 6) Combination of above 2-5 in one shot.
Note: Does not support operations on multiple outputs in one time. More details, please run "weston-wrandr -h" Signed-off-by: Quanxian Wang <quanxian.w...@intel.com> --- clients/Makefile.am | 9 + clients/wrandr.c | 862 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 871 insertions(+) create mode 100644 clients/wrandr.c diff --git a/clients/Makefile.am b/clients/Makefile.am index 4f8d4a6..757dba3 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -60,6 +60,7 @@ libexec_PROGRAMS = \ weston-desktop-shell \ weston-screenshooter \ $(screensaver) \ + weston-wrandr \ weston-keyboard \ weston-simple-im @@ -101,6 +102,12 @@ libtoytoolkit_la_LIBADD = \ weston_flower_SOURCES = flower.c weston_flower_LDADD = libtoytoolkit.la +weston_wrandr_SOURCES = \ + wrandr.c \ + randr-protocol.c \ + randr-client-protocol.h +weston_wrandr_LDADD = libtoytoolkit.la $(CLIENT_LIBS) + weston_screenshooter_SOURCES = \ screenshot.c \ screenshooter-protocol.c \ @@ -211,6 +218,8 @@ BUILT_SOURCES = \ text-cursor-position-protocol.c \ text-protocol.c \ text-client-protocol.h \ + randr-protocol.c \ + randr-client-protocol.h \ input-method-protocol.c \ input-method-client-protocol.h \ desktop-shell-client-protocol.h \ diff --git a/clients/wrandr.c b/clients/wrandr.c new file mode 100644 index 0000000..86e58a1 --- /dev/null +++ b/clients/wrandr.c @@ -0,0 +1,862 @@ +/* + * Copyright © 2014 Quanxian Wang + * Copyright © 2014 Intel Corporation + + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <assert.h> +#include <signal.h> + +#include "randr-client-protocol.h" +#include <wayland-client.h> +#include <wayland-server.h> +#include "../shared/config-parser.h" + +#ifndef HZ +#define HZ 1000 +#endif + +#define ARRAY_LENGTH(a) (sizeof(a) / sizeof(a)[0]) + +enum randr_status { + RANDR_EXIT = 0, + RANDR_COMMIT = 1, +}; + +static int running = 1; + +struct output; +struct mode; + +struct randr { + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct weston_randr *randr; + struct wl_list output_list; + struct wl_output *loutput; + struct wl_output *routput; + struct output *output; + struct wl_output *wayland_output; + struct mode *delmode; + struct mode *mode; + struct { + int clock; + int hdisplay; + int hsync_start; + int hsync_end; + int htotal; + int vdisplay; + int vsync_start; + int vsync_end; + int vtotal; + char hsync[128]; + char vsync[128]; + } newmode; + struct wrandr_callback *callback; +}; + +struct mode { + struct wl_list link; + int height; + int width; + int refresh; + uint32_t flags; +}; + +struct output { + struct wl_list link; + struct wl_list mode_list; + struct randr *randr; + struct wl_output *output; + char *name; + int x; + int y; + int physical_width; + int physical_height; + int subpixel; + char *make; + char *model; + int transform; + int scale; + int server_output_id; +}; + +struct argument { + char *output; + char *leftof; + char *rightof; + char *mode; + char *newmode; + char *delmode; + int query; + int scale; + int transform; + int help; +}; + +static void +delete_argument(struct argument *argument) +{ + if (argument->output) + free(argument->output); + + if (argument->leftof) + free(argument->leftof); + + if (argument->rightof) + free(argument->rightof); + + if (argument->mode) + free(argument->mode); + + if (argument->newmode) + free(argument->newmode); + + if (argument->delmode) + free(argument->delmode); +} + +#define RESULT_MASK 0x3 + +static void +print_line(int flags, int results, int flag, char *name) +{ + if (!(flags & 1<<flag)) + return; + + printf("%s:", name); + switch (results>>(flag * 2) & RESULT_MASK) { + case WRANDR_CALLBACK_RESULT_SUCCESS: + printf("SUCCESS!\n"); + break; + case WRANDR_CALLBACK_RESULT_FAIL: + printf("FAIL!\n"); + break; + case WRANDR_CALLBACK_RESULT_NOACT: + printf("Same as current, no change!\n"); + break; + } +} + +static void +randr_done(void *data, + struct wrandr_callback *callback, + uint32_t flags, + uint32_t results) +{ + print_line(flags, results, + WRANDR_CALLBACK_ACTION_TRANSFORM, "TRANSFORM"); + print_line(flags, results, WRANDR_CALLBACK_ACTION_MODE, "MODE"); + print_line(flags, results, WRANDR_CALLBACK_ACTION_SCALE, "SCALE"); + print_line(flags, results, WRANDR_CALLBACK_ACTION_MOVE, "MOVE"); + print_line(flags, results, WRANDR_CALLBACK_ACTION_NEWMODE, "NEWMODE"); + print_line(flags, results, WRANDR_CALLBACK_ACTION_DELMODE, "DELMODE"); + + running = 0; +} + +static const struct wrandr_callback_listener wrandr_listener = { + randr_done +}; + +static void +output_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 output *output = data; + + output->output = wl_output; + output->x = x; + output->y = y; + output->physical_height = physical_height; + output->physical_width = physical_width; + output->subpixel = subpixel; + output->make = strdup(make); + output->model = strdup(model); + output->transform = transform; +} + +static void +output_handle_done(void *data, + struct wl_output *wl_output) +{ +} + +static void +output_handle_name(void *data, + struct wl_output *wl_output, + const char *name) +{ + struct output *output = data; + if (name) + output->name = strdup(name); +} + +static void +output_handle_scale(void *data, + struct wl_output *wl_output, + int32_t scale) +{ + struct output *output = data; + + output->scale = scale; +} + +static void +output_handle_mode(void *data, + struct wl_output *wl_output, + uint32_t flags, + int width, + int height, + int refresh) +{ + struct output *output = data; + struct mode *mode; + + wl_list_for_each(mode, &output->mode_list, link) { + if (mode->width == width && + mode->height == height && + mode->refresh == refresh) { + if (flags != mode->flags) + mode->flags = flags; + return; + } + } + + mode = (struct mode *)malloc(sizeof(*mode)); + if (!mode) + return; + + mode->width = width; + mode->height = height; + mode->refresh = refresh; + mode->flags = flags; + + wl_list_insert(output->mode_list.prev, &mode->link); +} + +static const struct wl_output_listener output_listener = { + output_handle_geometry, + output_handle_mode, + output_handle_done, + output_handle_scale, + output_handle_name +}; + +static void +randr_add_output(struct randr *randr, uint32_t id) +{ + struct output *output; + + output = (struct output *)malloc(sizeof(*output)); + if (!output) + return; + + output->randr = randr; + output->scale = 1; + output->name = NULL; + output->server_output_id = id; + output->output = wl_registry_bind(randr->registry, + id, + &wl_output_interface, + 2); + + wl_list_init(&output->mode_list); + wl_list_insert(randr->output_list.prev, &output->link); + wl_output_add_listener(output->output, &output_listener, output); +} + +static void +mode_destroy(struct mode *mode) +{ + wl_list_remove(&mode->link); + free(mode); +} + +static void +output_destroy(struct output *output) +{ + struct mode *mode; + + wl_list_for_each(mode, &output->mode_list, link) + mode_destroy(mode); + + wl_list_remove(&output->link); + + free(output->name); + free(output->make); + free(output->model); + free(output); +} + +static void +randr_destroy_output(struct randr *randr) +{ + struct output *output; + + wl_list_for_each(output, &randr->output_list, link) + output_destroy(output); +} + +static void +registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, + const char *interface, uint32_t version) +{ + struct randr *randr = data; + + if (strcmp(interface, "weston_randr") == 0) { + randr->randr = wl_registry_bind(registry, id, + &weston_randr_interface, + 1); + } else if (strcmp(interface, "wl_compositor") == 0) { + randr->compositor = wl_registry_bind(registry, id, + &wl_compositor_interface, + 3); + } else if (strcmp(interface, "wl_output") == 0) { + randr_add_output(randr, id); + } +} + +static void +registry_handle_global_remove(void *data, + struct wl_registry *registry, + uint32_t name) +{ + struct randr *randr = data; + + randr_destroy_output(randr); +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global, + registry_handle_global_remove +}; + +static void +transform_usage(void) +{ + fprintf(stderr, + " -R (0-7)\n" + " 0: TRANSFORM NORMAL\n" + " 1: TRANSFORM 90\n" + " 2: TRANSFORM 180\n" + " 3: TRANSFORM 270\n" + " 4: TRANSFORM FLIP 0\n" + " 5: TRANSFORM FLIP 90\n" + " 6: TRANSFORM FLIP 180\n" + " 7: TRANSFORM FLIP 270\n"); +} + +static void +newmode_format(void) +{ + fprintf(stderr, + "\t\nNewmode Format:" + "\t--newmode='d,d,d,d,d,d,d,d,d,s,s'" + "(d:digit, s:string)\n\n" + "\tclock: Pixel clock (Mhz)\n\n" + "\thdisplay: Pixel number of one horticontal line(Hz)\n\n" + "\thsync_start: Start of horizontal sync signal(Hz)\n\n" + "\thsycn_end: End of horizontal sync signal(Hz)\n\n" + "\thtotal: Whole cycle of a horizontal line(Hz)\n\n" + "\tvdisplay: Pixel number of one vertical line(Hz)\n\n" + "\tvsync_start: Start of vertical sync signal(Hz)\n\n" + "\tvsycn_end: End of vertical sync signal(Hz)\n\n" + "\tvtotal: Whole cycle of a vertical line(Hz)\n\n" + "\thsync: Polarity of the horizontal sync pulses\n" + "\t\tvalue:(+-)hsync\n" + "\tvsync: Polarity of the vertical sync pulses\n" + "\t\tvsync value:(+-)vsync\n"); +} +static void +usage(int error_code) +{ + fprintf(stderr, "Usage: weston-randr [OPTIONS]\n\n" + " --leftof \tleft output\n" + " --rightof \tright output\n" + " --output \ttarget output\n" + " --newmode \tcustome one mode\n" + " --delmode \tdelete a mode like 800x600@58 or number\n" + " -q|--query \tquery all outputs\n" + " --mode \tset mode like 800x600@60 or number" + " --scale \tthe value of scale" + "(Firstly use -q to get mode list and find number).\n" + " --transform \toutput trasform(0-7)\n" + " -h|--help \tThis help text\n\n"); + + transform_usage(); + newmode_format(); + exit(error_code); +} + +static void +randr_init(struct randr *randr) +{ + wl_list_init(&randr->output_list); + randr->display = wl_display_connect(NULL); + assert(randr->display); + + randr->registry = wl_display_get_registry(randr->display); + wl_registry_add_listener(randr->registry, + ®istry_listener, randr); + + wl_display_dispatch(randr->display); +} + +static void +randr_query(struct randr *randr) +{ + struct output *output; + struct mode *mode; + char *state; + int i; + + wl_list_for_each(output, &randr->output_list, link) { + printf("%s\n", output->name); + i = 1; + + wl_list_for_each(mode, &output->mode_list, link) { + state = ""; + if (mode->flags & WL_OUTPUT_MODE_CURRENT) + state = "(current)"; + else if (mode->flags & WL_OUTPUT_MODE_PREFERRED) + state = "(preferred)"; + + printf("%d)%dx%d@%d%s\n", + i++, + mode->width, + mode->height, + (mode->refresh + HZ/2)/HZ, + state); + } + printf("\n"); + } +} + +static int +get_newmode(char *str, struct randr *randr) +{ + if (sscanf(str, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%6s,%6s", + &randr->newmode.clock, + &randr->newmode.hdisplay, + &randr->newmode.hsync_start, + &randr->newmode.hsync_end, + &randr->newmode.htotal, + &randr->newmode.vdisplay, + &randr->newmode.vsync_start, + &randr->newmode.vsync_end, + &randr->newmode.vtotal, + randr->newmode.hsync, + randr->newmode.vsync) == 11) { + + if ((strcmp(randr->newmode.hsync, "+hsync") != 0 && + strcmp(randr->newmode.hsync, "-hsync") != 0) || + (strcmp(randr->newmode.vsync, "+vsync") != 0 && + strcmp(randr->newmode.vsync, "-vsync") != 0)) { + fprintf(stderr, + "Invalid format of hsync and vsync\n" + "hsync:%s, vsync:%s\n", + randr->newmode.hsync, + randr->newmode.vsync); + + return WRANDR_CALLBACK_RESULT_FAIL; + } + return WRANDR_CALLBACK_RESULT_SUCCESS; + } + return WRANDR_CALLBACK_RESULT_FAIL; +} + +static struct mode * +found_mode(struct output *output, + const char *modestr) +{ + int width = -1, height = -1, refresh = -1; + int num = -1; + int i = 0; + struct mode *mode; + + if (sscanf(modestr, "%dx%d@%d", &width, &height, &refresh) != 3) { + if (sscanf(modestr, "%dx%d", &width, &height) != 2) { + if (sscanf(modestr, "%d", &num) != 1) { + printf("Error formatting for mode.\n"); + return NULL; + } + } + } + + if (num >= 0) { + wl_list_for_each(mode, &output->mode_list, link) { + i++; + if (i == num) + return mode; + } + } else if (width > 0 && height > 0) + wl_list_for_each(mode, &output->mode_list, link) { + if (mode->width == width && mode->height == height) + if (refresh <= 0 || mode->refresh == refresh) + return mode; + } + + return NULL; +} + +static void +randr_printmode(struct output *output) +{ + struct mode *mode; + char *state; + int i = 1; + + printf("%s\n", output->name); + wl_list_for_each(mode, &output->mode_list, link) { + if (mode->flags & WL_OUTPUT_MODE_CURRENT) + state = "(current)"; + else if (mode->flags & WL_OUTPUT_MODE_PREFERRED) + state = "(preferred)"; + else + state = ""; + printf("%d)%dx%d@%d%s\n", + i++, + mode->width, + mode->height, + (mode->refresh + HZ/2)/HZ, + state); + } +} + +static void +randr_commit(struct randr *randr, + struct wl_output *wayland_output) +{ + weston_randr_commit(randr->randr, + wayland_output); +} + +static void +get_output_handle(struct randr *randr, + struct argument *argument) +{ + struct output *output; + + /* Get all output handles. */ + wl_list_for_each(output, &randr->output_list, link) { + if (argument->leftof && + !strcmp(output->name, argument->leftof)) { + randr->loutput = output->output; + } + + if (argument->rightof && + !strcmp(output->name, argument->rightof)) { + randr->routput = output->output; + } + + if (argument->output && + !strcmp(output->name, argument->output)) { + randr->output = output; + randr->wayland_output = output->output; + } + } +} + +static int +verify_params(struct randr *randr, + struct argument *argument) +{ + int ret = WRANDR_CALLBACK_RESULT_SUCCESS; + /* Verify output paramerters */ + if (argument->output && !randr->wayland_output) { + printf("%s does not exists or not connected.\n", + argument->output); + ret = WRANDR_CALLBACK_RESULT_FAIL; + } + + if (argument->newmode) { + if (get_newmode(argument->newmode, randr) == + WRANDR_CALLBACK_RESULT_FAIL) { + printf("Invalid newmode format!\n"); + newmode_format(); + ret = WRANDR_CALLBACK_RESULT_FAIL; + } + } + + if (argument->delmode) { + randr->delmode = found_mode(randr->output, argument->delmode); + if (!randr->delmode) { + printf("%s not exists\n", argument->delmode); + ret = WRANDR_CALLBACK_RESULT_FAIL; + } + } + + if (argument->leftof) { + if (!randr->loutput) { + printf("%s not exists\n", argument->leftof); + ret = WRANDR_CALLBACK_RESULT_FAIL; + } + } + + if (argument->rightof) { + if (!randr->routput) { + printf("%s not exists\n", argument->rightof); + ret = WRANDR_CALLBACK_RESULT_FAIL; + } + } + + if (argument->mode) { + randr->mode = found_mode(randr->output, argument->mode); + if (randr->mode) { + if (randr->mode->flags & WL_OUTPUT_MODE_CURRENT) { + printf("%dx%d@%d has been the current.\n", + randr->mode->width, + randr->mode->height, + randr->mode->refresh); + + ret = WRANDR_CALLBACK_RESULT_FAIL; + } + } else { + printf("%s not exists\n", argument->mode); + ret = WRANDR_CALLBACK_RESULT_FAIL; + } + } + + if (argument->scale != -1) { + if (argument->scale <= 0) { + printf("scale %d should be great than 0.\n", + argument->scale); + ret = WRANDR_CALLBACK_RESULT_FAIL; + } + + if (argument->scale == randr->output->scale) { + printf("scale %d is the same as current.\n", + argument->scale); + ret = WRANDR_CALLBACK_RESULT_FAIL; + } + } + + if (argument->transform != -1) { + if (argument->transform >= 0 && argument->transform <= 7) { + if (argument->transform == randr->output->transform) { + printf("transform %d is the same as current.\n", + argument->transform); + ret = WRANDR_CALLBACK_RESULT_FAIL; + } + } else { + transform_usage(); + ret = WRANDR_CALLBACK_RESULT_FAIL; + } + } + + return ret; +} + +static void +query_output(struct randr *randr, + struct argument *argument) +{ + /* Query mode */ + if (argument->query > 0) { + if (randr->output) + randr_printmode(randr->output); + else + randr_query(randr); + } +} + +static int +del_new_mode(struct randr *randr, + struct argument *argument) +{ + int ret = RANDR_EXIT; + + /* Delete mode */ + if (argument->delmode) { + weston_randr_del_mode(randr->randr, + randr->wayland_output, + randr->delmode->width, + randr->delmode->height, + randr->delmode->refresh); + ret = RANDR_COMMIT; + } + + /* New mode */ + if (argument->newmode) { + weston_randr_new_mode(randr->randr, + randr->wayland_output, + randr->newmode.clock, + randr->newmode.hdisplay, + randr->newmode.hsync_start, + randr->newmode.hsync_end, + randr->newmode.htotal, + randr->newmode.vdisplay, + randr->newmode.vsync_start, + randr->newmode.vsync_end, + randr->newmode.vtotal, + randr->newmode.hsync, + randr->newmode.vsync); + + ret = RANDR_COMMIT; + } + + return ret; +} + +static int +output_modeset(struct randr *randr, + struct argument *argument) +{ + int ret = RANDR_EXIT; + + /* Single action for output. */ + if (randr->mode) { + weston_randr_set_mode(randr->randr, + randr->wayland_output, + randr->mode->width, + randr->mode->height, + randr->mode->refresh); + ret = RANDR_COMMIT; + } + + if (argument->scale != -1) { + weston_randr_set_scale(randr->randr, + randr->wayland_output, + argument->scale); + ret = RANDR_COMMIT; + } + + if (argument->transform >= 0) { + weston_randr_set_transform(randr->randr, + randr->wayland_output, + argument->transform); + ret = RANDR_COMMIT; + + } + + if (randr->loutput) { + weston_randr_move(randr->randr, + randr->wayland_output, + randr->loutput, + WESTON_RANDR_MOVE_LEFTOF); + ret = RANDR_COMMIT; + } + + if (randr->routput) { + weston_randr_move(randr->randr, + randr->wayland_output, + randr->routput, + WESTON_RANDR_MOVE_RIGHTOF); + ret = RANDR_COMMIT; + } + + return ret; +} + +int +main(int argc, char **argv) +{ + struct randr randr = { 0 }; + struct argument argument = {NULL, NULL, NULL, NULL, + NULL, NULL, -1, -1, -1, -1}; + int ret = 0; + int commit = 0; + + const struct weston_option randr_options[] = { + { WESTON_OPTION_STRING, "output", 0, &argument.output}, + { WESTON_OPTION_STRING, "mode", 0, &argument.mode}, + { WESTON_OPTION_STRING, "newmode", 0, &argument.newmode}, + { WESTON_OPTION_STRING, "delmode", 0, &argument.delmode}, + { WESTON_OPTION_STRING, "leftof", 0, &argument.leftof}, + { WESTON_OPTION_STRING, "rightof", 0, &argument.rightof}, + { WESTON_OPTION_BOOLEAN, "query", 'q', &argument.query}, + { WESTON_OPTION_INTEGER, "scale", 0, &argument.scale}, + { WESTON_OPTION_INTEGER, "transform", 0, &argument.transform}, + { WESTON_OPTION_BOOLEAN, "help", 'h', &argument.help }, + }; + + parse_options(randr_options, ARRAY_LENGTH(randr_options), &argc, argv); + + if (argument.help > 0) + usage(EXIT_SUCCESS); + + randr_init(&randr); + + wl_display_roundtrip(randr.display); + + /* Check if weston_randr is enable or not. */ + if (!randr.randr) { + printf("weston_randr is not available in compositor.\n"); + goto exit; + } + + get_output_handle(&randr, &argument); + + if (verify_params(&randr, &argument) == + WRANDR_CALLBACK_RESULT_FAIL) + goto exit; + + query_output(&randr, &argument); + + if (!randr.output) + goto exit; + + randr.callback = + weston_randr_start(randr.randr, randr.wayland_output); + wrandr_callback_add_listener(randr.callback, + &wrandr_listener, &randr); + + if (del_new_mode(&randr, &argument) == RANDR_COMMIT) + commit += 1; + + if (del_new_mode(&randr, &argument) == RANDR_COMMIT) + commit += 1; + + if (output_modeset(&randr, &argument) == RANDR_COMMIT) + commit += 1; + + if (commit > 0) { + randr_commit(&randr, + randr.wayland_output); + } else + goto exit; + + while (running && ret != -1) + ret = wl_display_dispatch(randr.display); + +exit: + delete_argument(&argument); + wl_registry_destroy(randr.registry); + wl_display_flush(randr.display); + wl_display_disconnect(randr.display); + + return 0; +} -- 1.8.1.2 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel