Signed-off-by: Quanxian Wang <[email protected]>
---
 module/Makefile.am        |    3 +
 module/wrandr/Makefile.am |   32 ++
 module/wrandr/wrandr.c    | 1262 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1297 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..baa5628
--- /dev/null
+++ b/module/wrandr/wrandr.c
@@ -0,0 +1,1262 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <errno.h>
+
+#include "compositor.h"
+#include "randr-server-protocol.h"
+
+#define RESULT_MASK 0x3
+
+struct randr_mode {
+       int width;
+       int height;
+       int refresh;
+       time_t modi_time;
+};
+
+struct randr_timing {
+       uint32_t clock;
+       int hdisplay;
+       int hsync_start;
+       int hsync_end;
+       int htotal;
+       int vdisplay;
+       int vsync_start;
+       int vsync_end;
+       int vtotal;
+       int vscan;
+       uint32_t flags;
+};
+
+struct randr_request {
+       struct wl_list link;
+       struct wl_client *client;
+       struct wl_list output_request_list;
+       char *config_file;
+       int flags;
+};
+
+struct randr_output_request {
+       struct wl_list link;
+       struct weston_output *output;
+       int flags;
+
+       struct weston_output *loutput;
+       struct weston_output *routput;
+       struct weston_output *aoutput;
+       struct weston_output *boutput;
+
+       struct randr_mode *mode;
+
+       struct {
+               time_t modi_time;
+               int num;
+       } modenum;
+
+       int delmodenum;
+       struct randr_mode *delmode;
+
+       int scale;
+       int32_t transform;
+       struct randr_mode *newmode;
+       struct randr_timing *newtiming;
+};
+
+static void
+del_orequest(struct randr_output_request *orequest)
+{
+       wl_list_remove(&orequest->link);
+
+       if (orequest->mode)
+               free(orequest->mode);
+
+       if (orequest->delmode)
+               free(orequest->delmode);
+
+       if (orequest->newmode)
+               free(orequest->newmode);
+
+       if (orequest->newtiming)
+               free(orequest->newtiming);
+
+       free(orequest);
+}
+
+static void
+del_request(struct randr_request *request)
+{
+       struct randr_output_request *output_request, *next;
+
+       wl_list_remove(&request->link);
+
+       if (request->config_file)
+               free(request->config_file);
+
+       wl_list_for_each_safe(output_request, next,
+                             &request->output_request_list, link)
+               del_orequest(output_request);
+
+       free(request);
+}
+
+static struct randr_output_request *
+create_randr_orequest(struct wl_resource *resource,
+                     struct randr_request *request,
+                     struct weston_output *output)
+{
+       struct randr_output_request *orequest;
+
+       orequest = zalloc(sizeof(*orequest));
+       if (orequest == NULL) {
+               wl_resource_post_no_memory(resource);
+               return NULL;
+       }
+       memset(orequest, 0x0, sizeof(*orequest));
+
+       orequest->output = output;
+       wl_list_insert(&request->output_request_list, &orequest->link);
+       return orequest;
+}
+
+static struct randr_request *
+create_randr_request(struct wl_client *client,
+                    struct wl_resource *resource)
+{
+       struct wrandr *randr =
+               wl_resource_get_user_data(resource);
+       struct randr_request *request;
+
+       request = zalloc(sizeof(*request));
+       if (request == NULL) {
+               wl_resource_post_no_memory(resource);
+               return NULL;
+       }
+       memset(request, 0x0, sizeof(*request));
+
+       request->client = client;
+       wl_list_init(&request->output_request_list);
+       wl_list_insert(&randr->pending.request_list, &request->link);
+       return request;
+}
+
+static struct randr_request *
+get_request(struct wl_client *client,
+           struct wl_resource *resource,
+           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)
+                       return request;
+       }
+
+       if (create)
+               return create_randr_request(client, resource);
+       else
+               return NULL;
+}
+
+static struct randr_output_request *
+get_orequest(struct wl_client *client,
+            struct wl_resource *resource,
+            struct weston_output *output,
+            int create)
+{
+       struct randr_request *request;
+       struct randr_output_request *orequest;
+
+       request = get_request(client, resource, create);
+       if (!request)
+               return NULL;
+
+       wl_list_for_each(orequest, &request->output_request_list, link)
+               if (orequest->output == output)
+                       return orequest;
+
+       if (create)
+               return create_randr_orequest(resource, request, output);
+       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 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_output_request *request;
+
+       request = get_orequest(client, resource, output, 1);
+       if (request) {
+               request->flags |= 1<<WRANDR_TYPE_OOP_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_output_request *request;
+
+       request = get_orequest(client, resource, output, 1);
+       if (request) {
+               request->flags |= 1<<WRANDR_TYPE_OOP_TRANSFORM;
+               request->transform = transform % 8;
+       }
+}
+
+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_output_request *request;
+
+       request = get_orequest(client, resource, output, 1);
+       if (request) {
+               if (move == WESTON_RANDR_MOVE_LEFTOF) {
+                       request->flags |= 1<<WRANDR_TYPE_OOP_MOVEL;
+                       request->loutput =
+                               wl_resource_get_user_data(source_resource);
+               } else if (move == WESTON_RANDR_MOVE_RIGHTOF) {
+                       request->flags |= 1<<WRANDR_TYPE_OOP_MOVER;
+                       request->routput =
+                               wl_resource_get_user_data(source_resource);
+               } else if (move == WESTON_RANDR_MOVE_ABOVE) {
+                       request->flags |= 1<<WRANDR_TYPE_OOP_MOVEA;
+                       /* TODO */
+                       request->aoutput = NULL;
+               } else if (move == WESTON_RANDR_MOVE_BELOW) {
+                       request->flags |= 1<<WRANDR_TYPE_OOP_MOVEB;
+                       /* TODO */
+                       request->boutput = NULL;
+               }
+       }
+}
+
+static struct weston_mode *
+randr_find_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 struct weston_mode *
+randr_find_timing(struct weston_output *output,
+                 uint32_t clock,
+                 int hdisplay,
+                 int hsync_start,
+                 int hsync_end,
+                 int htotal,
+                 int vdisplay,
+                 int vsync_start,
+                 int vsync_end,
+                 int vtotal,
+                 int vscan,
+                 int flags)
+{
+       struct weston_mode *mode;
+       int ret;
+
+       if (!output->compare_timing) {
+               weston_log("Timing compare is not supported\n");
+               return NULL;
+       }
+
+       wl_list_for_each(mode, &output->mode_list, link) {
+               ret = output->compare_timing(mode,
+                                            clock,
+                                            hdisplay,
+                                            hsync_start,
+                                            hsync_end,
+                                            htotal,
+                                            vdisplay,
+                                            vsync_start,
+                                            vsync_end,
+                                            vtotal,
+                                            vscan,
+                                            flags);
+
+               if (ret > 0)
+                       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 void
+randr_switch_mode(struct randr_output_request *request,
+                 uint32_t *results)
+{
+       time_t time = 0;
+       int i = 0, found = 0;
+       int result = WRANDR_TYPE_RET_NOACT;
+       int timing_mode = 0;
+       int move_bits = 0;
+       struct weston_mode *mode;
+       struct weston_output *output = request->output;
+
+       if (request->flags & 1<<WRANDR_TYPE_OOP_MODENUM) {
+               timing_mode = 1;
+               time = request->modenum.modi_time;
+               *results |= result<<
+                          (WRANDR_TYPE_OOP_MODENUM * 2);
+       }
+
+       if (request->flags & 1<<WRANDR_TYPE_OOP_MODE) {
+               *results |= result<<
+                          (WRANDR_TYPE_OOP_MODE * 2);
+               if (request->mode->modi_time > time) {
+                       time = request->mode->modi_time;
+                       timing_mode = 2;
+               }
+       }
+
+       switch (timing_mode) {
+       case 1:
+               i = 0;
+               wl_list_for_each(mode, &output->mode_list, link) {
+                       i++;
+                       if (i == request->modenum.num) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (found != 1)
+                       mode = NULL;
+               break;
+       case 2:
+               mode = randr_find_mode(output,
+                                      request->mode->width,
+                                      request->mode->height,
+                                      request->mode->refresh);
+               break;
+       default:
+               return;
+       }
+
+
+       if (!mode) {
+               result = WRANDR_TYPE_RET_FAIL;
+       } else {
+               if (!(mode->flags & WL_OUTPUT_MODE_CURRENT) &&
+                   output->switch_mode) {
+                       result = output->switch_mode(output, mode);
+                       if (result < 0)
+                               result = WRANDR_TYPE_RET_FAIL;
+                       else
+                               result = WRANDR_TYPE_RET_SUCCESS;
+               } else
+                       result = WRANDR_TYPE_RET_NOACT;
+       }
+
+       move_bits = WRANDR_TYPE_OOP_MODENUM + timing_mode - 1;
+       *results &= ~(RESULT_MASK<<(move_bits * 2));
+       *results |= result<<(move_bits * 2);
+}
+
+static int32_t
+output_modeset(struct wrandr *randr,
+              struct randr_output_request *request)
+{
+       struct weston_compositor *ec = randr->compositor;
+       struct weston_output *output = NULL;
+       int 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;
+
+       /* Switch Mode
+        * Mode and Timing may be set in one commit.
+        * The basic rule is who is the last, who will be
+        * active.
+        */
+       randr_switch_mode(request, &results);
+
+       /* Transform */
+       if (request->transform >= 0) {
+               if ((uint32_t)request->transform != toutput->transform) {
+                       toutput->transform = (uint32_t)request->transform;
+                       result = WRANDR_TYPE_RET_SUCCESS;
+               } else
+                       result = WRANDR_TYPE_RET_NOACT;
+
+               results |= result<<(WRANDR_TYPE_OOP_TRANSFORM * 2);
+       }
+
+       /* Scale */
+       if (request->scale > 0) {
+               if (request->scale && request->scale !=
+                   toutput->current_scale) {
+                       toutput->current_scale = request->scale;
+                       result = WRANDR_TYPE_RET_SUCCESS;
+               } else
+                       result = WRANDR_TYPE_RET_NOACT;
+
+               results |= result<<(WRANDR_TYPE_OOP_SCALE * 2);
+       }
+
+       /* Move */
+       if (request->loutput || request->routput ||
+           request->aoutput || request->boutput) {
+               if (request->loutput &&
+                   request->loutput != toutput &&
+                   &toutput->link !=
+                   request->loutput->link.prev) {
+                       wl_list_remove(&toutput->link);
+                       wl_list_insert(request->loutput->link.prev,
+                                      &toutput->link);
+                       result = WRANDR_TYPE_RET_SUCCESS;
+                       results |= result<<
+                                  (WRANDR_TYPE_OOP_MOVEL * 2);
+               } else if (request->loutput) {
+                       result = WRANDR_TYPE_RET_NOACT;
+                       results |= result<<
+                                  (WRANDR_TYPE_OOP_MOVEL * 2);
+               }
+
+               if (request->routput &&
+                   request->routput != toutput &&
+                   &toutput->link !=
+                   request->routput->link.next) {
+                       wl_list_remove(&toutput->link);
+                       wl_list_insert(&request->routput->link,
+                                      &toutput->link);
+                       result = WRANDR_TYPE_RET_SUCCESS;
+                       results |= result<<
+                                  (WRANDR_TYPE_OOP_MOVER * 2);
+               } else if (request->routput) {
+                       result = WRANDR_TYPE_RET_NOACT;
+                       results |= result<<
+                                  (WRANDR_TYPE_OOP_MOVER * 2);
+               }
+
+               /* TODO for above and below output */
+       }
+
+       /* Check if any action to be done. */
+       mode_change =
+               (results>>(WRANDR_TYPE_OOP_MODE * 2) &
+               RESULT_MASK) == WRANDR_TYPE_RET_SUCCESS ||
+               (results>>(WRANDR_TYPE_OOP_MODENUM * 2) &
+               RESULT_MASK) == WRANDR_TYPE_RET_SUCCESS;
+
+       scale_change =
+               (results>>(WRANDR_TYPE_OOP_SCALE * 2) &
+               RESULT_MASK) == WRANDR_TYPE_RET_SUCCESS;
+
+       trans_change =
+               (results>>(WRANDR_TYPE_OOP_TRANSFORM * 2) &
+               RESULT_MASK) == WRANDR_TYPE_RET_SUCCESS;
+
+       move_change =
+               (results>>(WRANDR_TYPE_OOP_MOVEL * 2) &
+               RESULT_MASK) == WRANDR_TYPE_RET_SUCCESS ||
+               (results>>(WRANDR_TYPE_OOP_MOVER * 2) &
+               RESULT_MASK) == WRANDR_TYPE_RET_SUCCESS ||
+               (results>>(WRANDR_TYPE_OOP_MOVEA * 2) &
+               RESULT_MASK) == WRANDR_TYPE_RET_SUCCESS ||
+               (results>>(WRANDR_TYPE_OOP_MOVEB * 2) &
+               RESULT_MASK) == WRANDR_TYPE_RET_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) {
+               /* TODO: output moves above and below. */
+               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_modenum(struct wl_client *client,
+              struct wl_resource *resource,
+              struct wl_resource *output_resource,
+              int num)
+{
+       struct weston_output *output =
+               wl_resource_get_user_data(output_resource);
+       struct randr_output_request *request;
+
+       request = get_orequest(client, resource, output, 1);
+       if (request) {
+               request->flags |= 1<<WRANDR_TYPE_OOP_MODENUM;
+               request->modenum.num = num;
+               request->modenum.modi_time = time(NULL);
+       }
+}
+
+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_output_request *request;
+
+       request = get_orequest(client, resource, output, 1);
+       if (request) {
+               request->flags |= 1<<WRANDR_TYPE_OOP_MODE;
+
+               if (!request->mode) {
+                       request->mode = zalloc(sizeof(*(request->mode)));
+                       if (request->mode == NULL) {
+                               wl_resource_post_no_memory(resource);
+                               return;
+                       }
+               }
+
+               request->mode->width = width;
+               request->mode->height = height;
+               request->mode->refresh = refresh;
+               request->mode->modi_time = time(NULL);
+       }
+}
+
+static int
+output_newmode(struct wrandr *randr,
+              struct wl_client *client,
+              struct weston_output *output,
+              int width,
+              int height,
+              int refresh)
+{
+       struct weston_mode *mode;
+       struct wl_resource *resource;
+
+       if (!output->new_mode) {
+               weston_log("No new mode callback!\n");
+               return WRANDR_TYPE_RET_FAIL;
+       }
+
+       mode = randr_find_mode(output, width, height, refresh);
+
+       if (mode) {
+               weston_log("New mode exists already!\n");
+               return WRANDR_TYPE_RET_NOACT;
+       }
+
+       mode = output->new_mode(width, height, refresh);
+       mode->flags |= WESTON_RANDR_MODE_CUSTOM;
+
+       /* Notify clients new mode. */
+       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_TYPE_RET_SUCCESS;
+}
+
+static void
+randr_newmode(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_output_request *request;
+
+       request = get_orequest(client, resource, output, 1);
+       if (request) {
+               request->flags |= 1<<WRANDR_TYPE_OOP_NEWMODE;
+
+               if (!request->newmode) {
+                       request->newmode =
+                               zalloc(sizeof(*(request->newmode)));
+                       if (request->newmode == NULL) {
+                               wl_resource_post_no_memory(resource);
+                               return;
+                       }
+                       memset(request->newmode, 0x0,
+                              sizeof(*(request->newmode)));
+               }
+
+               request->newmode->width = width;
+               request->newmode->height = height;
+               request->newmode->refresh = refresh;
+       }
+}
+
+static int
+output_newtiming(struct wrandr *randr,
+                struct wl_client *client,
+                struct weston_output *output,
+                uint32_t clock,
+                int hdisplay,
+                int hsync_start,
+                int hsync_end,
+                int htotal,
+                int vdisplay,
+                int vsync_start,
+                int vsync_end,
+                int vtotal,
+                int vscan,
+                int flags)
+{
+       struct weston_mode *mode;
+       struct wl_resource *resource;
+
+       if (!output->new_timing) {
+               weston_log("New timing is not supported!!\n");
+               return WRANDR_TYPE_RET_FAIL;
+       }
+
+       mode = randr_find_timing(output,
+                                clock,
+                                hdisplay,
+                                hsync_start,
+                                hsync_end,
+                                htotal,
+                                vdisplay,
+                                vsync_start,
+                                vsync_end,
+                                vtotal,
+                                vscan,
+                                flags);
+       if (mode) {
+               weston_log("New mode timing has existed!\n");
+               return WRANDR_TYPE_RET_NOACT;
+       }
+
+       mode = output->new_timing(output,
+                                 clock,
+                                 hdisplay,
+                                 hsync_start,
+                                 hsync_end,
+                                 htotal,
+                                 vdisplay,
+                                 vsync_start,
+                                 vsync_end,
+                                 vtotal,
+                                 vscan,
+                                 flags);
+       if (mode) {
+               mode->flags |= WESTON_RANDR_MODE_CUSTOM;
+
+               /* Notify clients new mode. */
+               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_TYPE_RET_SUCCESS;
+       }
+
+       return WRANDR_TYPE_RET_FAIL;
+}
+
+static void
+randr_newtiming(struct wl_client *client,
+             struct wl_resource *resource,
+             struct wl_resource *target_resource,
+             uint32_t clock,
+             int hdisplay,
+             int hsync_start,
+             int hsync_end,
+             int htotal,
+             int vdisplay,
+             int vsync_start,
+             int vsync_end,
+             int vtotal,
+             int vscan,
+             int flags)
+{
+       struct weston_output *output =
+               wl_resource_get_user_data(target_resource);
+       struct randr_output_request *request;
+
+       request = get_orequest(client, resource, output, 1);
+       if (request) {
+               request->flags |= 1<<WRANDR_TYPE_OOP_NEWTIMING;
+
+               if (!request->newtiming) {
+                       request->newtiming =
+                               zalloc(sizeof(*(request->newtiming)));
+                       if (request->newtiming == NULL) {
+                               wl_resource_post_no_memory(resource);
+                               return;
+                       }
+               }
+
+               request->newtiming->clock = clock;
+               request->newtiming->hdisplay = hdisplay;
+               request->newtiming->hsync_start = hsync_start;
+               request->newtiming->hsync_end = hsync_end;
+               request->newtiming->htotal = htotal;
+               request->newtiming->vdisplay = vdisplay;
+               request->newtiming->vsync_start = vsync_start;
+               request->newtiming->vsync_end = vsync_end;
+               request->newtiming->vtotal = vtotal;
+               request->newtiming->vscan = vscan;
+               request->newtiming->flags = flags;
+       }
+}
+
+static void
+del_mode(struct weston_output *output, struct weston_mode *mode)
+{
+       struct wl_resource *resource;
+
+       mode->flags |= WESTON_RANDR_MODE_DEL;
+
+       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);
+}
+
+static int
+output_delmodenum(struct weston_output *output, int num)
+{
+       struct weston_mode *mode, *next;
+       int i = 0;
+       int found = 0;
+
+       wl_list_for_each_safe(mode, next, &output->mode_list, link) {
+               i++;
+               if (i != num)
+                       continue;
+               found = 1;
+               if (mode->flags & WL_OUTPUT_MODE_CURRENT) {
+                       weston_log("Don't delete active mode\n");
+                       return WRANDR_TYPE_RET_FAIL;
+               }
+               del_mode(output, mode);
+               break;
+       }
+
+       if (found != 1)
+               return WRANDR_TYPE_RET_FAIL;
+
+       return WRANDR_TYPE_RET_SUCCESS;
+}
+
+static void
+randr_delmodenum(struct wl_client *client,
+                    struct wl_resource *resource,
+                    struct wl_resource *output_resource,
+                    int num)
+{
+       struct weston_output *output =
+               wl_resource_get_user_data(output_resource);
+       struct randr_output_request *request;
+
+       request = get_orequest(client, resource, output, 1);
+       if (request) {
+               request->flags |= 1<<WRANDR_TYPE_OOP_DELMODENUM;
+               request->delmodenum = num;
+       }
+}
+
+static int
+output_delmodes(struct weston_output *output,
+              int width,
+              int height,
+              int refresh)
+{
+       struct weston_mode *mode, *next;
+       int found = 0;
+
+       /* Check if the mode is current active mode */
+       if (output->current_mode->width == width &&
+           output->current_mode->height) {
+               if (!refresh ||
+                   refresh == (int) output->current_mode->refresh) {
+                       weston_log("Could't delete active mode.\n");
+                       return WRANDR_TYPE_RET_FAIL;
+               }
+       }
+
+       wl_list_for_each_safe(mode, next, &output->mode_list, link) {
+               if (mode->width == width && mode->height == height) {
+                       if (!refresh ||
+                           refresh == (int)mode->refresh) {
+                               found = 1;
+                               del_mode(output, mode);
+                       }
+               }
+       }
+
+       if (found != 1)
+               return WRANDR_TYPE_RET_FAIL;
+
+       return WRANDR_TYPE_RET_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_output_request *request;
+
+       request = get_orequest(client, resource, output, 1);
+       if (request) {
+               request->flags |= 1<<WRANDR_TYPE_OOP_DELMODE;
+
+               if (!request->delmode) {
+                       request->delmode = zalloc(sizeof(*(request->delmode)));
+                       if (request->delmode == NULL) {
+                               wl_resource_post_no_memory(resource);
+                               return;
+                       }
+               }
+
+               request->delmode->width = width;
+               request->delmode->height = height;
+               request->delmode->refresh = refresh;
+       }
+}
+
+static void
+randr_output_commit(struct wrandr *randr,
+                   struct wl_client *client,
+                   struct randr_output_request *orequest,
+                   struct wl_resource *callback)
+{
+       struct wl_resource *output_resource;
+       struct wl_list *output_reslist = &orequest->output->resource_list;
+       struct weston_output *output = orequest->output;
+       int results = 0;
+       int result = 0;
+
+       /* Process output pending orequests */
+       if (orequest->flags & 1<<WRANDR_TYPE_OOP_NEWTIMING) {
+               result = output_newtiming(randr, client, output,
+                                         orequest->newtiming->clock,
+                                         orequest->newtiming->hdisplay,
+                                         orequest->newtiming->hsync_start,
+                                         orequest->newtiming->hsync_end,
+                                         orequest->newtiming->htotal,
+                                         orequest->newtiming->vdisplay,
+                                         orequest->newtiming->vsync_start,
+                                         orequest->newtiming->vsync_end,
+                                         orequest->newtiming->vtotal,
+                                         orequest->newtiming->vscan,
+                                         orequest->newtiming->flags);
+               results |= result<<(WRANDR_TYPE_OOP_NEWTIMING * 2);
+       }
+
+       if (orequest->flags & 1<<WRANDR_TYPE_OOP_NEWMODE) {
+               result = output_newmode(randr, client, output,
+                                       orequest->newmode->width,
+                                       orequest->newmode->height,
+                                       orequest->newmode->refresh);
+               results |= result<<(WRANDR_TYPE_OOP_NEWMODE * 2);
+       }
+
+       if (orequest->flags & 1<<WRANDR_TYPE_OOP_DELMODE) {
+               result = output_delmodes(output,
+                                        orequest->delmode->width,
+                                        orequest->delmode->height,
+                                        orequest->delmode->refresh);
+               results |= result<<(WRANDR_TYPE_OOP_DELMODE * 2);
+       }
+
+       if (orequest->flags & 1<<WRANDR_TYPE_OOP_DELMODENUM) {
+               result = output_delmodenum(output,
+                                          orequest->delmodenum);
+               results |= result<<(WRANDR_TYPE_OOP_DELMODE * 2);
+       }
+
+       if (orequest->flags & (
+           1<<WRANDR_TYPE_OOP_MODENUM |
+           1<<WRANDR_TYPE_OOP_MODE |
+           1<<WRANDR_TYPE_OOP_MOVEL |
+           1<<WRANDR_TYPE_OOP_MOVER |
+           1<<WRANDR_TYPE_OOP_MOVEA |
+           1<<WRANDR_TYPE_OOP_MOVEB |
+           1<<WRANDR_TYPE_OOP_SCALE |
+           1<<WRANDR_TYPE_OOP_TRANSFORM))
+               results |= output_modeset(randr, orequest);
+
+       output_resource =
+               wl_resource_find_for_client(output_reslist, client);
+
+       wrandr_callback_send_done(callback, output_resource,
+                                 orequest->flags, results);
+}
+
+static void
+randr_configure(struct wl_client *client,
+               struct wl_resource *resource,
+               const char *config_file)
+{
+       struct randr_request *request;
+
+       request = get_request(client, resource, 1);
+       if (request) {
+               if (!request->config_file) {
+                       request->flags |= WRANDR_TYPE_WOP_CONFIGURE;
+                       request->config_file = strdup(config_file);
+               }
+       }
+}
+
+static int
+parse_config_file(char *config_file)
+{
+       int result = 0;
+
+       /* TODO */
+       return result<<(WRANDR_TYPE_WOP_CONFIGURE * 2);
+}
+
+static void
+randr_commit(struct wl_client *client,
+            struct wl_resource *resource,
+            uint32_t callback)
+{
+       struct wrandr *randr =
+               wl_resource_get_user_data(resource);
+       struct randr_request *request;
+       struct randr_output_request *orequest, *next;
+       struct wl_resource *cb;
+       int results = 0; /* Used for compositor results */
+
+       request = get_request(client, resource, 0);
+       if (!request) {
+               weston_log("No memory happens when commit?!\n");
+               return;
+       }
+
+       cb = wl_resource_create(client, &wrandr_callback_interface, 1,
+                               callback);
+
+       if (cb == NULL) {
+               free(request);
+               wl_resource_post_no_memory(resource);
+               return;
+       }
+
+       if (request->config_file)
+               results |= parse_config_file(request->config_file);
+
+       wl_list_for_each_safe(orequest, next,
+                             &request->output_request_list, link)
+               randr_output_commit(randr, client, orequest, cb);
+
+       wrandr_callback_send_done(cb, NULL,
+                                 request->flags, results);
+       del_request(request);
+       wl_resource_destroy(cb);
+}
+
+static const struct weston_randr_interface randr_interface = {
+       randr_destroy,
+       randr_set_modenum,
+       randr_set_mode,
+       randr_set_transform,
+       randr_set_scale,
+       randr_move,
+       randr_delmodenum,
+       randr_delmode,
+       randr_newmode,
+       randr_newtiming,
+       randr_configure,
+       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)
+{
+       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
[email protected]
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to