Signed-off-by: Quanxian Wang <quanxian.w...@intel.com> --- module/Makefile.am | 3 + module/wrandr/Makefile.am | 32 ++ module/wrandr/wrandr.c | 792 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 827 insertions(+) create mode 100644 module/Makefile.am create mode 100644 module/wrandr/Makefile.am create mode 100644 module/wrandr/wrandr.c
diff --git a/module/Makefile.am b/module/Makefile.am new file mode 100644 index 0000000..1a10e65 --- /dev/null +++ b/module/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = + +SUBDIRS += wrandr diff --git a/module/wrandr/Makefile.am b/module/wrandr/Makefile.am new file mode 100644 index 0000000..b0f2e6b --- /dev/null +++ b/module/wrandr/Makefile.am @@ -0,0 +1,32 @@ +moduledir = $(libdir)/weston +module_LTLIBRARIES = $(wrandr) + +AM_CPPFLAGS = \ + -I$(top_srcdir)/shared \ + -I$(top_srcdir)/src \ + -I$(top_builddir)/src \ + -DDATADIR='"$(datadir)"' \ + -DMODULEDIR='"$(moduledir)"' \ + -DLIBEXECDIR='"$(libexecdir)"' \ + -DIN_WESTON + +if BUILD_WRANDR +wrandr = wrandr.la +wrandr_la_LDFLAGS = -module -avoid-version +wrandr_la_LIBADD = $(COMPOSITOR_LIBS) \ + $(top_srcdir)/shared/libshared.la +wrandr_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS) +wrandr_la_SOURCES = \ + wrandr.c \ + randr-protocol.c \ + randr-server-protocol.h +endif + +BUILT_SOURCES = \ + randr-protocol.c \ + randr-server-protocol.h + +CLEANFILES = $(BUILT_SOURCES) + +wayland_protocoldir = $(top_srcdir)/protocol +include $(top_srcdir)/wayland-scanner.mk diff --git a/module/wrandr/wrandr.c b/module/wrandr/wrandr.c new file mode 100644 index 0000000..b97d101 --- /dev/null +++ b/module/wrandr/wrandr.c @@ -0,0 +1,792 @@ +/* + * Copyright © 2012-2013 Collabora, Ltd. + + * 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 "randr-server-protocol.h" + +#define RESULT_MASK 0x3 + +struct randr_request { + struct wl_list link; + int flags; + struct wl_resource *callback; + + /* Client and output compose unique id + * of these request.*/ + struct wl_client *client; + struct weston_output *output; + + /* output move */ + struct weston_output *loutput; + struct weston_output *routput; + + /* mode set */ + int height; + int width; + int refresh; + + int scale; + int32_t transform; + + 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; + char *vsync; + } newmode; + + struct { + int height; + int width; + int refresh; + } delmode; +}; + +static void +del_request(struct randr_request *request) +{ + wl_list_remove(&request->link); + + if (request->callback) + wl_resource_destroy(request->callback); + + if (request->newmode.hsync) + free(request->newmode.hsync); + + if (request->newmode.vsync) + free(request->newmode.vsync); + + free(request); +} + +static struct randr_request * +get_request(struct wl_resource *resource, + struct wl_client *client, + struct weston_output *output, + int create) +{ + struct wrandr *randr = + wl_resource_get_user_data(resource); + struct randr_request *request; + + wl_list_for_each(request, &randr->pending.request_list, link) + { + if (request->client == client && + request->output == output) + return request; + } + + if (create) { + request = zalloc(sizeof(*request)); + if (request == NULL) { + wl_resource_post_no_memory(resource); + return NULL; + } + memset(request, 0x0, sizeof(*request)); + + request->client = client; + request->output = output; + wl_list_insert(&randr->pending.request_list, &request->link); + return request; + } else + return NULL; +} + +static void +adjust_pointer(struct weston_output *output, + pixman_region32_t *old_output_region) +{ + struct weston_seat *seat; + int32_t x, y; + struct weston_pointer *pointer; + + /* If a pointer falls outside the outputs new geometry, move it to its + * lower-right corner. */ + wl_list_for_each(seat, &output->compositor->seat_list, link) { + pointer = seat->pointer; + if (!pointer) + continue; + + x = wl_fixed_to_int(pointer->x); + y = wl_fixed_to_int(pointer->y); + + if (!pixman_region32_contains_point(old_output_region, + x, y, NULL) || + pixman_region32_contains_point(&output->region, + x, y, NULL)) + continue; + + if (x >= output->x + output->width) + x = output->x + output->width - 1; + if (y >= output->y + output->height) + y = output->y + output->height - 1; + + pointer->x = wl_fixed_from_int(x); + pointer->y = wl_fixed_from_int(y); + } +} + +static void +notify_change(struct weston_output *output, + int mode_changed, + int scale_changed, + int transform_changed) +{ + struct wl_resource *resource; + struct weston_mode *mode = output->current_mode; + + if (!mode_changed && !scale_changed && + !transform_changed) + return; + + /* Notify clients of the changes */ + wl_resource_for_each(resource, + &output->resource_list) { + if (mode_changed) { + wl_output_send_mode(resource, + mode->flags | + WL_OUTPUT_MODE_CURRENT, + mode->width, + mode->height, + mode->refresh); + } + + if (transform_changed) { + wl_output_send_geometry(resource, + output->x, + output->y, + output->mm_width, + output->mm_height, + output->subpixel, + output->make, + output->model, + output->transform); + } + + if (scale_changed && + wl_resource_get_version(resource) >= 2) + wl_output_send_scale(resource, + output->current_scale); + + if (wl_resource_get_version(resource) >= 2) + wl_output_send_done(resource); + } +} + +static void +randr_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); + return; +} + +static struct weston_mode * +randr_found_mode(struct weston_output *output, + int width, int height, int refresh) +{ + struct weston_mode *mode; + + wl_list_for_each(mode, &output->mode_list, link) { + if (width != mode->width || + height != mode->height) + continue; + + if (!refresh || refresh == (int)mode->refresh) + return mode; + } + return NULL; +} + +static void +update_region(struct weston_compositor *ec, + struct weston_output *target_output) +{ + pixman_region32_t old_output_region; + + /* Update output region and transformation matrix. */ + pixman_region32_init(&old_output_region); + pixman_region32_copy(&old_output_region, &target_output->region); + weston_output_transform_scale_init(target_output, + target_output->transform, + target_output->current_scale); + + pixman_region32_init(&target_output->previous_damage); + pixman_region32_init_rect(&target_output->region, + target_output->x, target_output->y, + target_output->width, target_output->height); + + weston_output_update_matrix(target_output); + + adjust_pointer(target_output, &old_output_region); + pixman_region32_fini(&old_output_region); + + /* damage the output region in primary_plane */ + pixman_region32_union(&ec->primary_plane.damage, + &ec->primary_plane.damage, + &target_output->region); + + target_output->dirty = 1; +} + +static int32_t +randr_modeset(struct wrandr *randr, + struct randr_request *request) +{ + struct weston_compositor *ec = randr->compositor; + struct weston_output *output = NULL; + struct weston_mode *mode = NULL; + int move = 0, offset = 0; + uint32_t results = 0; + int32_t result = 0; + uint32_t mode_change = 0, trans_change = 0; + uint32_t scale_change = 0; + uint32_t move_change = 0; + struct weston_output *toutput = request->output; + + /* Mode Setting Operation */ + if (request->width > 0 && request->height > 0) { + mode = randr_found_mode(toutput, + request->width, + request->height, + request->refresh); + + if (!mode) + result = WRANDR_CALLBACK_RESULT_FAIL; + else { + if (!(mode->flags & WL_OUTPUT_MODE_CURRENT) && + toutput->switch_mode) { + result = toutput->switch_mode(toutput, mode); + if (result < 0) + result = WRANDR_CALLBACK_RESULT_FAIL; + else + result = WRANDR_CALLBACK_RESULT_SUCCESS; + } else + result = WRANDR_CALLBACK_RESULT_NOACT; + } + + results |= result<<(WRANDR_CALLBACK_ACTION_MODE * 2); + } + + /* Transform Operation */ + if (request->transform >= 0) { + if ((uint32_t)request->transform != toutput->transform) { + toutput->transform = (uint32_t)request->transform; + result = WRANDR_CALLBACK_RESULT_SUCCESS; + } else + result = WRANDR_CALLBACK_RESULT_NOACT; + + results |= result<<(WRANDR_CALLBACK_ACTION_TRANSFORM * 2); + } + + /* Scale Operation */ + if (request->scale > 0) { + if (request->scale && request->scale != + toutput->current_scale) { + toutput->current_scale = request->scale; + result = WRANDR_CALLBACK_RESULT_SUCCESS; + } else + result = WRANDR_CALLBACK_RESULT_NOACT; + + results |= result<<(WRANDR_CALLBACK_ACTION_SCALE * 2); + } + + /* Move Operation */ + if (request->loutput || request->routput) { + if (request->loutput == toutput || + request->routput == toutput) + result = WRANDR_CALLBACK_RESULT_FAIL; + else { + if (request->loutput && + &toutput->link != + request->loutput->link.prev) { + wl_list_remove(&toutput->link); + wl_list_insert(request->loutput->link.prev, + &toutput->link); + move = 1; + } + + if (request->routput && + &toutput->link != + request->routput->link.next) { + wl_list_remove(&toutput->link); + wl_list_insert(&request->routput->link, + &toutput->link); + move = 1; + } + + if (move != 1) + result = WRANDR_CALLBACK_RESULT_NOACT; + else + result = WRANDR_CALLBACK_RESULT_SUCCESS; + } + results |= result<<(WRANDR_CALLBACK_ACTION_MOVE * 2); + } + + /* Check if any action to be done. */ + mode_change = (results>>(WRANDR_CALLBACK_ACTION_MODE * 2) & + RESULT_MASK) == WRANDR_CALLBACK_RESULT_SUCCESS; + scale_change = (results>>(WRANDR_CALLBACK_ACTION_SCALE * 2) & + RESULT_MASK) == WRANDR_CALLBACK_RESULT_SUCCESS; + trans_change = (results>>(WRANDR_CALLBACK_ACTION_TRANSFORM * 2) & + RESULT_MASK) == WRANDR_CALLBACK_RESULT_SUCCESS; + move_change = (results>>(WRANDR_CALLBACK_ACTION_MOVE * 2) & + RESULT_MASK) == WRANDR_CALLBACK_RESULT_SUCCESS; + + if (!move_change && !scale_change && + !trans_change && !mode_change) + return results; + + update_region(randr->compositor, toutput); + + /* Adjust all outputs. */ + wl_list_for_each(output, &ec->output_list, link) { + weston_output_move(output, offset, 0); + offset += output->width; + pixman_region32_union(&randr->compositor->primary_plane.damage, + &randr->compositor->primary_plane.damage, + &output->region); + } + + /* Notify clients of the changes. */ + notify_change(toutput, mode_change, + scale_change, trans_change); + + return results; +} + +static void +randr_set_scale(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output_resource, + int32_t scale) +{ + struct weston_output *output = + wl_resource_get_user_data(output_resource); + struct randr_request *request; + + request = get_request(resource, client, output, 1); + if (request) { + request->flags |= 1<<WRANDR_CALLBACK_ACTION_SCALE; + request->scale = scale; + } +} + +static void +randr_set_transform(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output_resource, + uint32_t transform) +{ + struct weston_output *output = + wl_resource_get_user_data(output_resource); + struct randr_request *request; + + request = get_request(resource, client, output, 1); + if (request) { + request->flags |= 1<<WRANDR_CALLBACK_ACTION_TRANSFORM; + request->transform = transform % 8; + } +} + +static void +randr_start(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output_resource, + uint32_t callback) +{ + struct weston_output *output = + wl_resource_get_user_data(output_resource); + struct randr_request *request; + + request = get_request(resource, client, output, 1); + if (!request) { + wl_resource_post_no_memory(resource); + return; + } + + request->callback = + wl_resource_create(client, &wrandr_callback_interface, 1, + callback); + if (request->callback == NULL) { + free(request); + wl_resource_post_no_memory(request->callback); + return; + } +} + +static void +randr_set_mode(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output_resource, + int width, int height, int refresh) +{ + struct weston_output *output = + wl_resource_get_user_data(output_resource); + struct randr_request *request; + + request = get_request(resource, client, output, 1); + if (request) { + request->flags |= 1<<WRANDR_CALLBACK_ACTION_MODE; + request->width = width; + request->height = height; + request->refresh = refresh; + } +} + +static void +randr_move(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *target_resource, + struct wl_resource *source_resource, int move) +{ + struct weston_output *output = + wl_resource_get_user_data(target_resource); + struct randr_request *request; + + request = get_request(resource, client, output, 1); + if (request) { + request->flags |= 1<<WRANDR_CALLBACK_ACTION_MOVE; + if (move == WESTON_RANDR_MOVE_LEFTOF) { + request->loutput = + wl_resource_get_user_data(source_resource); + } else { + request->routput = + wl_resource_get_user_data(source_resource); + } + } +} + +static int +output_newmode(struct weston_output *output, + int clock, + int hdisplay, + int hsync_start, + int hsync_end, + int htotal, + int vdisplay, + int vsync_start, + int vsync_end, + int vtotal, + const char *hsync, + const char *vsync) +{ + struct weston_mode *mode = NULL; + struct wl_resource *resource; + uint64_t refresh; + + if ((strcmp(hsync, "+hsync") != 0 && + strcmp(hsync, "-hsync") != 0) || + (strcmp(vsync, "+vsync") != 0 && + strcmp(vsync, "-vsync") != 0)) { + weston_log("Invalid hsync format:%s.\n", hsync); + return WRANDR_CALLBACK_RESULT_FAIL; + } + + /* Calculate higher precision (mHz) refresh rate */ + refresh = (clock * 1000 * 1000000LL / htotal + + vtotal / 2) / vtotal; + + /* Same width, height and refresh in mode list */ + mode = randr_found_mode(output, hdisplay, vdisplay, refresh); + if (mode) + return WRANDR_CALLBACK_RESULT_NOACT; + + if (output->new_mode) { + mode = output->new_mode(output, + clock, + hdisplay, + hsync_start, + hsync_end, + htotal, + vdisplay, + vsync_start, + vsync_end, + vtotal, + hsync, + vsync); + if (mode) { + mode->flags |= WESTON_RANDR_MODE_CUSTOM; + /* Notify clients of the change. */ + wl_resource_for_each(resource, + &output->resource_list) { + wl_output_send_mode(resource, + mode->flags, + mode->width, + mode->height, + mode->refresh); + if (wl_resource_get_version(resource) >= 2) + wl_output_send_done(resource); + } + return WRANDR_CALLBACK_RESULT_SUCCESS; + } + } + + return WRANDR_CALLBACK_RESULT_FAIL; +} + +static void +randr_newmode(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *target_resource, + int clock, + int hdisplay, + int hsync_start, + int hsync_end, + int htotal, + int vdisplay, + int vsync_start, + int vsync_end, + int vtotal, + const char *hsync, + const char *vsync) +{ + struct weston_output *output = + wl_resource_get_user_data(target_resource); + struct randr_request *request; + + request = get_request(resource, client, output, 1); + if (request) { + request->flags |= 1<<WRANDR_CALLBACK_ACTION_NEWMODE; + request->newmode.clock = clock; + request->newmode.hdisplay = hdisplay; + request->newmode.hsync_start = hsync_start; + request->newmode.hsync_end = hsync_end; + request->newmode.htotal = htotal; + request->newmode.vdisplay = vdisplay; + request->newmode.vsync_start = vsync_start; + request->newmode.vsync_end = vsync_end; + request->newmode.vtotal = vtotal; + request->newmode.hsync = strdup(hsync); + request->newmode.vsync = strdup(vsync); + } +} + +static int +output_delmode(struct weston_output *output, + int width, + int height, + int refresh) +{ + struct wl_resource *resource; + struct weston_mode *mode = + randr_found_mode(output, width, height, refresh); + + if (!mode) + return WRANDR_CALLBACK_RESULT_FAIL; + else { + mode->flags |= WESTON_RANDR_MODE_DEL; + /* Notify clients of the change. */ + wl_resource_for_each(resource, &output->resource_list) { + wl_output_send_mode(resource, + mode->flags, + mode->width, + mode->height, + mode->refresh); + if (wl_resource_get_version(resource) >= 2) + wl_output_send_done(resource); + } + + wl_list_remove(&mode->link); + free(mode); + return WRANDR_CALLBACK_RESULT_SUCCESS; + } +} + +static void +randr_delmode(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *target_resource, + int width, + int height, + int refresh) +{ + struct weston_output *output = + wl_resource_get_user_data(target_resource); + struct randr_request *request; + + request = get_request(resource, client, output, 1); + if (request) { + request->flags |= 1<<WRANDR_CALLBACK_ACTION_DELMODE; + request->delmode.width = width; + request->delmode.height = height; + request->delmode.refresh = refresh; + } +} + +static void +randr_commit(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *target_resource) +{ + struct wrandr *randr = wl_resource_get_user_data(resource); + struct weston_output *output = + wl_resource_get_user_data(target_resource); + struct randr_request *request; + int results = 0; + int result = 0; + + /* process pending request for this client */ + request = get_request(resource, client, output, 0); + if (!request) { + weston_log("No action happens when commit?!\n"); + } else { + if (request->flags & 1<<WRANDR_CALLBACK_ACTION_NEWMODE) { + result = output_newmode(output, + request->newmode.clock, + request->newmode.hdisplay, + request->newmode.hsync_start, + request->newmode.hsync_end, + request->newmode.htotal, + request->newmode.vdisplay, + request->newmode.vsync_start, + request->newmode.vsync_end, + request->newmode.vtotal, + request->newmode.hsync, + request->newmode.vsync); + results |= result<<(WRANDR_CALLBACK_ACTION_NEWMODE * 2); + } + + if (request->flags & 1<<WRANDR_CALLBACK_ACTION_DELMODE) { + result = output_delmode(output, + request->delmode.width, + request->delmode.height, + request->delmode.refresh); + results |= result<<(WRANDR_CALLBACK_ACTION_DELMODE * 2); + } + + if (request->width || request->height || + request->scale || request->transform >= 0 || + request->loutput || request->routput) + results |= randr_modeset(randr, request); + } + + wrandr_callback_send_done(request->callback, + request->flags, results); + del_request(request); +} + +static const struct weston_randr_interface randr_interface = { + randr_destroy, + randr_start, + randr_set_mode, + randr_set_transform, + randr_set_scale, + randr_move, + randr_delmode, + randr_newmode, + randr_commit +}; + +static void +bind_randr(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct wrandr *randr = data; + + randr->resource = wl_resource_create(client, + &weston_randr_interface, + 1, id); + + if (randr->resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(randr->resource, + &randr_interface, + randr, NULL); +} + +static void +handle_randr_destroy(struct wl_listener *listener, void *data) +{ + struct wrandr *randr = + container_of(listener, struct wrandr, destroy_listener); + struct randr_request *request; + + if (randr) { + if (randr->global) + wl_global_destroy(randr->global); + + wl_list_for_each(request, &randr->pending.request_list, link) + del_request(request); + + free(randr); + } +} + +static void +weston_randr_init(struct weston_compositor *ec) +{ + /* Initialize randr */ + ec->randr = zalloc(sizeof *(ec->randr)); + if (!ec->randr) { + weston_log("No memory for wrandr in backend compositor.\n"); + return; + } + memset(ec->randr, 0, sizeof *(ec->randr)); + + ec->randr->compositor = ec; + + ec->randr->global = wl_global_create(ec->wl_display, + &weston_randr_interface, 1, + ec->randr, bind_randr); + + if (ec->randr->global == NULL) { + weston_log("Failed to create global weston_randr_interface.\n"); + return; + } + + wl_list_init(&ec->randr->pending.request_list); + wl_list_init(&ec->randr->pending.randr_callback_list); +} + +WL_EXPORT int +module_init(struct weston_compositor *ec, + int *argc, char *argv[]) +{ + weston_randr_init(ec); + if (!ec->randr) { + weston_log("Failed to initialize wrandr module.\n"); + return -1; + } + ec->randr->destroy_listener.notify = handle_randr_destroy; + wl_signal_add(&ec->destroy_signal, &ec->randr->destroy_listener); + + return 0; +} -- 1.8.1.2 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel