summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKristian Høgsberg <krh@bitplanet.net>2012-01-03 10:29:47 -0500
committerKristian Høgsberg <krh@bitplanet.net>2012-01-03 11:04:04 -0500
commit8334bc1ef946f76c8cd47a818c397010634a1959 (patch)
tree9da587e4359c87c9c91997541d05b62a7ea0d794 /src
parent4c61747a1ffea8a1e199cbd314d6aed7b9f9c5b2 (diff)
Rename wayland-compositor to weston
This rename addresses a few problems around the split between core Wayland and the wayland-demos repository. 1) Initially, we had one big repository with protocol code, sample compositor and sample clients. We split that repository to make it possible to implement the protocol without pulling in the sample/demo code. At this point, the compositor is more than just a "demo" and wayland-demos doesn't send the right message. The sample compositor is a useful, self-contained project in it's own right, and we want to move away from the "demos" label. 2) Another problem is that the wayland-demos compositor is often called "the wayland compsitor", but it's really just one possible compositor. Existing X11 compositors are expected to add Wayland support and then gradually phase out/modularize the X11 support, for example. Conversely, it's hard to talk about the wayland-demos compositor specifically as opposed to, eg, the wayland protocol or a wayland compositor in general. We are also renaming the repo to weston, and the compositor subdirectory to src/, to emphasize that the main "output" is the compositor.
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore9
-rw-r--r--src/Makefile.am113
-rw-r--r--src/compositor-drm.c956
-rw-r--r--src/compositor-openwfd.c699
-rw-r--r--src/compositor-wayland.c587
-rw-r--r--src/compositor-x11.c835
-rw-r--r--src/compositor.c2185
-rw-r--r--src/compositor.h471
-rw-r--r--src/data-device.c478
-rw-r--r--src/evdev.c653
-rw-r--r--src/evdev.h35
-rw-r--r--src/hash.c307
-rw-r--r--src/hash.h49
-rw-r--r--src/image-loader.c176
-rw-r--r--src/screenshooter.c83
-rw-r--r--src/shell.c1296
-rw-r--r--src/switcher.c131
-rw-r--r--src/tablet-shell.c559
-rw-r--r--src/tty.c176
-rw-r--r--src/util.c301
-rw-r--r--src/xserver-launcher.c1701
21 files changed, 11800 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..0a6ccbb
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,9 @@
+wayland-compositor
+screenshooter-protocol.c
+screenshooter-server-protocol.h
+tablet-shell-protocol.c
+tablet-shell-server-protocol.h
+xserver-protocol.c
+xserver-server-protocol.h
+desktop-shell-protocol.c
+desktop-shell-server-protocol.h
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..4c7aeb8
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,113 @@
+bin_PROGRAMS = weston
+
+AM_CPPFLAGS = \
+ -DDATADIR='"$(datadir)"' \
+ -DMODULEDIR='"$(moduledir)"' \
+ -DLIBEXECDIR='"$(libexecdir)"' \
+ -DXSERVER_PATH='"@XSERVER_PATH@"' \
+ $(COMPOSITOR_CFLAGS)
+
+weston_LDFLAGS = -export-dynamic
+weston_CFLAGS = $(GCC_CFLAGS)
+weston_LDADD = \
+ $(COMPOSITOR_LIBS) $(DLOPEN_LIBS) $(XSERVER_LAUNCHER_LIBS)
+
+weston_SOURCES = \
+ compositor.c \
+ compositor.h \
+ image-loader.c \
+ data-device.c \
+ screenshooter.c \
+ screenshooter-protocol.c \
+ screenshooter-server-protocol.h \
+ util.c \
+ $(xserver_launcher_sources)
+
+if ENABLE_XSERVER_LAUNCHER
+xserver_launcher_sources = \
+ xserver-launcher.c \
+ xserver-protocol.c \
+ xserver-server-protocol.h \
+ hash.c \
+ hash.h
+endif
+
+moduledir = @libdir@/weston
+module_LTLIBRARIES = \
+ $(desktop_shell) \
+ $(tablet_shell) \
+ $(x11_backend) \
+ $(drm_backend) \
+ $(wayland_backend) \
+ $(openwfd_backend)
+
+if ENABLE_X11_COMPOSITOR
+x11_backend = x11-backend.la
+x11_backend_la_LDFLAGS = -module -avoid-version
+x11_backend_la_LIBADD = $(COMPOSITOR_LIBS) $(X11_COMPOSITOR_LIBS)
+x11_backend_la_CFLAGS = $(X11_COMPOSITOR_CFLAGS) $(GCC_CFLAGS)
+x11_backend_la_SOURCES = compositor-x11.c
+endif
+
+if ENABLE_DRM_COMPOSITOR
+drm_backend = drm-backend.la
+drm_backend_la_LDFLAGS = -module -avoid-version
+drm_backend_la_LIBADD = $(COMPOSITOR_LIBS) $(DRM_COMPOSITOR_LIBS)
+drm_backend_la_CFLAGS = $(DRM_COMPOSITOR_CFLAGS) $(GCC_CFLAGS)
+drm_backend_la_SOURCES = compositor-drm.c tty.c evdev.c evdev.h
+endif
+
+if ENABLE_WAYLAND_COMPOSITOR
+wayland_backend = wayland-backend.la
+wayland_backend_la_LDFLAGS = -module -avoid-version
+wayland_backend_la_LIBADD = $(COMPOSITOR_LIBS) $(WAYLAND_COMPOSITOR_LIBS)
+wayland_backend_la_CFLAGS = $(WAYLAND_COMPOSITOR_CFLAGS) $(GCC_CFLAGS)
+wayland_backend_la_SOURCES = compositor-wayland.c
+endif
+
+if ENABLE_OPENWFD_COMPOSITOR
+openwfd_backend = openwfd-backend.la
+openwfd_backend_la_LDFLAGS = -module -avoid-version
+openwfd_backend_la_LIBADD = $(COMPOSITOR_LIBS) $(OPENWFD_COMPOSITOR_LIBS)
+openwfd_backend_la_CFLAGS = $(OPENWFD_COMPOSITOR_CFLAGS) $(GCC_CFLAGS)
+openwfd_backend_la_SOURCES = compositor-openwfd.c tty.c evdev.c evdev.h
+endif
+
+if ENABLE_DESKTOP_SHELL
+desktop_shell = desktop-shell.la
+desktop_shell_la_LDFLAGS = -module -avoid-version
+desktop_shell_la_LIBADD = $(COMPOSITOR_LIBS) \
+ ../shared/libconfig-parser.la
+desktop_shell_la_CFLAGS = $(GCC_CFLAGS)
+desktop_shell_la_SOURCES = \
+ shell.c \
+ switcher.c \
+ desktop-shell-protocol.c \
+ desktop-shell-server-protocol.h
+endif
+
+if ENABLE_TABLET_SHELL
+tablet_shell = tablet-shell.la
+tablet_shell_la_LDFLAGS = -module -avoid-version
+tablet_shell_la_LIBADD = $(COMPOSITOR_LIBS)
+tablet_shell_la_CFLAGS = $(GCC_CFLAGS)
+tablet_shell_la_SOURCES = \
+ tablet-shell.c \
+ tablet-shell.h \
+ tablet-shell-protocol.c \
+ tablet-shell-server-protocol.h
+endif
+
+BUILT_SOURCES = \
+ screenshooter-server-protocol.h \
+ screenshooter-protocol.c \
+ tablet-shell-protocol.c \
+ tablet-shell-server-protocol.h \
+ xserver-protocol.c \
+ xserver-server-protocol.h \
+ desktop-shell-protocol.c \
+ desktop-shell-server-protocol.h
+
+CLEANFILES = $(BUILT_SOURCES)
+
+@wayland_scanner_rules@
diff --git a/src/compositor-drm.c b/src/compositor-drm.c
new file mode 100644
index 0000000..a6cedd5
--- /dev/null
+++ b/src/compositor-drm.c
@@ -0,0 +1,956 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2011 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include <gbm.h>
+
+#include "compositor.h"
+#include "evdev.h"
+
+struct drm_compositor {
+ struct weston_compositor base;
+
+ struct udev *udev;
+ struct wl_event_source *drm_source;
+
+ struct udev_monitor *udev_monitor;
+ struct wl_event_source *udev_drm_source;
+
+ struct {
+ int fd;
+ } drm;
+ struct gbm_device *gbm;
+ uint32_t crtc_allocator;
+ uint32_t connector_allocator;
+ struct tty *tty;
+
+ uint32_t prev_state;
+};
+
+struct drm_mode {
+ struct weston_mode base;
+ drmModeModeInfo mode_info;
+};
+
+struct drm_output {
+ struct weston_output base;
+
+ uint32_t crtc_id;
+ uint32_t connector_id;
+ drmModeCrtcPtr original_crtc;
+ GLuint rbo[2];
+ uint32_t fb_id[2];
+ EGLImageKHR image[2];
+ struct gbm_bo *bo[2];
+ uint32_t current;
+
+ uint32_t fs_surf_fb_id;
+ uint32_t pending_fs_surf_fb_id;
+};
+
+static int
+drm_output_prepare_render(struct weston_output *output_base)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER,
+ output->rbo[output->current]);
+
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+ return -1;
+
+ return 0;
+}
+
+static int
+drm_output_present(struct weston_output *output_base)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct drm_compositor *c =
+ (struct drm_compositor *) output->base.compositor;
+ uint32_t fb_id = 0;
+
+ glFlush();
+
+ output->current ^= 1;
+
+ if (output->pending_fs_surf_fb_id != 0) {
+ fb_id = output->pending_fs_surf_fb_id;
+ } else {
+ fb_id = output->fb_id[output->current ^ 1];
+ }
+
+ drmModePageFlip(c->drm.fd, output->crtc_id,
+ fb_id,
+ DRM_MODE_PAGE_FLIP_EVENT, output);
+
+ return 0;
+}
+
+static void
+page_flip_handler(int fd, unsigned int frame,
+ unsigned int sec, unsigned int usec, void *data)
+{
+ struct drm_output *output = (struct drm_output *) data;
+ struct drm_compositor *c =
+ (struct drm_compositor *) output->base.compositor;
+ uint32_t msecs;
+
+ if (output->fs_surf_fb_id) {
+ drmModeRmFB(c->drm.fd, output->fs_surf_fb_id);
+ output->fs_surf_fb_id = 0;
+ }
+
+ if (output->pending_fs_surf_fb_id) {
+ output->fs_surf_fb_id = output->pending_fs_surf_fb_id;
+ output->pending_fs_surf_fb_id = 0;
+ }
+
+ msecs = sec * 1000 + usec / 1000;
+ weston_output_finish_frame(&output->base, msecs);
+}
+
+static int
+drm_output_prepare_scanout_surface(struct weston_output *output_base,
+ struct weston_surface *es)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct drm_compositor *c =
+ (struct drm_compositor *) output->base.compositor;
+ EGLint handle, stride;
+ int ret;
+ uint32_t fb_id = 0;
+ struct gbm_bo *bo;
+
+ if (es->x != output->base.x ||
+ es->y != output->base.y ||
+ es->width != output->base.current->width ||
+ es->height != output->base.current->height ||
+ es->image == EGL_NO_IMAGE_KHR)
+ return -1;
+
+ bo = gbm_bo_create_from_egl_image(c->gbm,
+ c->base.display, es->image,
+ es->width, es->height,
+ GBM_BO_USE_SCANOUT);
+
+ handle = gbm_bo_get_handle(bo).s32;
+ stride = gbm_bo_get_pitch(bo);
+
+ gbm_bo_destroy(bo);
+
+ if (handle == 0)
+ return -1;
+
+ ret = drmModeAddFB(c->drm.fd,
+ output->base.current->width,
+ output->base.current->height,
+ 24, 32, stride, handle, &fb_id);
+
+ if (ret)
+ return -1;
+
+ output->pending_fs_surf_fb_id = fb_id;
+
+ return 0;
+}
+
+static int
+drm_output_set_cursor(struct weston_output *output_base,
+ struct weston_input_device *eid)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct drm_compositor *c =
+ (struct drm_compositor *) output->base.compositor;
+ EGLint handle, stride;
+ int ret = -1;
+ pixman_region32_t cursor_region;
+ struct gbm_bo *bo;
+
+ if (eid == NULL) {
+ drmModeSetCursor(c->drm.fd, output->crtc_id, 0, 0, 0);
+ return 0;
+ }
+
+ pixman_region32_init_rect(&cursor_region,
+ eid->sprite->x, eid->sprite->y,
+ eid->sprite->width, eid->sprite->height);
+
+ pixman_region32_intersect_rect(&cursor_region, &cursor_region,
+ output->base.x, output->base.y,
+ output->base.current->width,
+ output->base.current->height);
+
+ if (!pixman_region32_not_empty(&cursor_region))
+ goto out;
+
+ if (eid->sprite->image == EGL_NO_IMAGE_KHR)
+ goto out;
+
+ if (eid->sprite->width > 64 || eid->sprite->height > 64)
+ goto out;
+
+ bo = gbm_bo_create_from_egl_image(c->gbm,
+ c->base.display,
+ eid->sprite->image, 64, 64,
+ GBM_BO_USE_CURSOR_64X64);
+
+ handle = gbm_bo_get_handle(bo).s32;
+ stride = gbm_bo_get_pitch(bo);
+
+ gbm_bo_destroy(bo);
+
+ if (stride != 64 * 4) {
+ fprintf(stderr, "info: cursor stride is != 64\n");
+ goto out;
+ }
+
+ ret = drmModeSetCursor(c->drm.fd, output->crtc_id, handle, 64, 64);
+ if (ret) {
+ fprintf(stderr, "failed to set cursor: %s\n", strerror(-ret));
+ goto out;
+ }
+
+ ret = drmModeMoveCursor(c->drm.fd, output->crtc_id,
+ eid->sprite->x - output->base.x,
+ eid->sprite->y - output->base.y);
+ if (ret) {
+ fprintf(stderr, "failed to move cursor: %s\n", strerror(-ret));
+ goto out;
+ }
+
+out:
+ pixman_region32_fini(&cursor_region);
+ if (ret)
+ drmModeSetCursor(c->drm.fd, output->crtc_id, 0, 0, 0);
+ return ret;
+}
+
+static void
+drm_output_destroy(struct weston_output *output_base)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct drm_compositor *c =
+ (struct drm_compositor *) output->base.compositor;
+ drmModeCrtcPtr origcrtc = output->original_crtc;
+ int i;
+
+ /* Turn off hardware cursor */
+ drm_output_set_cursor(&output->base, NULL);
+
+ /* Restore original CRTC state */
+ drmModeSetCrtc(c->drm.fd, origcrtc->crtc_id, origcrtc->buffer_id,
+ origcrtc->x, origcrtc->y,
+ &output->connector_id, 1, &origcrtc->mode);
+ drmModeFreeCrtc(origcrtc);
+
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER,
+ 0);
+
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ glDeleteRenderbuffers(2, output->rbo);
+
+ /* Destroy output buffers */
+ for (i = 0; i < 2; i++) {
+ drmModeRmFB(c->drm.fd, output->fb_id[i]);
+ c->base.destroy_image(c->base.display, output->image[i]);
+ gbm_bo_destroy(output->bo[i]);
+ }
+
+ c->crtc_allocator &= ~(1 << output->crtc_id);
+ c->connector_allocator &= ~(1 << output->connector_id);
+
+ weston_output_destroy(&output->base);
+ wl_list_remove(&output->base.link);
+
+ free(output);
+}
+
+static int
+on_drm_input(int fd, uint32_t mask, void *data)
+{
+ drmEventContext evctx;
+
+ memset(&evctx, 0, sizeof evctx);
+ evctx.version = DRM_EVENT_CONTEXT_VERSION;
+ evctx.page_flip_handler = page_flip_handler;
+ drmHandleEvent(fd, &evctx);
+
+ return 1;
+}
+
+static int
+init_egl(struct drm_compositor *ec, struct udev_device *device)
+{
+ EGLint major, minor;
+ const char *extensions, *filename;
+ int fd;
+ static const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ filename = udev_device_get_devnode(device);
+ fd = open(filename, O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ /* Probably permissions error */
+ fprintf(stderr, "couldn't open %s, skipping\n",
+ udev_device_get_devnode(device));
+ return -1;
+ }
+
+ ec->drm.fd = fd;
+ ec->gbm = gbm_create_device(ec->drm.fd);
+ ec->base.display = eglGetDisplay(ec->gbm);
+ if (ec->base.display == NULL) {
+ fprintf(stderr, "failed to create display\n");
+ return -1;
+ }
+
+ if (!eglInitialize(ec->base.display, &major, &minor)) {
+ fprintf(stderr, "failed to initialize display\n");
+ return -1;
+ }
+
+ extensions = eglQueryString(ec->base.display, EGL_EXTENSIONS);
+ if (!strstr(extensions, "EGL_KHR_surfaceless_gles2")) {
+ fprintf(stderr, "EGL_KHR_surfaceless_gles2 not available\n");
+ return -1;
+ }
+
+ if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+ fprintf(stderr, "failed to bind api EGL_OPENGL_ES_API\n");
+ return -1;
+ }
+
+ ec->base.context = eglCreateContext(ec->base.display, NULL,
+ EGL_NO_CONTEXT, context_attribs);
+ if (ec->base.context == NULL) {
+ fprintf(stderr, "failed to create context\n");
+ return -1;
+ }
+
+ if (!eglMakeCurrent(ec->base.display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, ec->base.context)) {
+ fprintf(stderr, "failed to make context current\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static drmModeModeInfo builtin_1024x768 = {
+ 63500, /* clock */
+ 1024, 1072, 1176, 1328, 0,
+ 768, 771, 775, 798, 0,
+ 59920,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC,
+ 0,
+ "1024x768"
+};
+
+
+static int
+drm_output_add_mode(struct drm_output *output, drmModeModeInfo *info)
+{
+ struct drm_mode *mode;
+
+ mode = malloc(sizeof *mode);
+ if (mode == NULL)
+ return -1;
+
+ mode->base.flags = 0;
+ mode->base.width = info->hdisplay;
+ mode->base.height = info->vdisplay;
+ mode->base.refresh = info->vrefresh;
+ mode->mode_info = *info;
+ wl_list_insert(output->base.mode_list.prev, &mode->base.link);
+
+ return 0;
+}
+
+static int
+drm_subpixel_to_wayland(int drm_value)
+{
+ switch (drm_value) {
+ default:
+ case DRM_MODE_SUBPIXEL_UNKNOWN:
+ return WL_OUTPUT_SUBPIXEL_UNKNOWN;
+ case DRM_MODE_SUBPIXEL_NONE:
+ return WL_OUTPUT_SUBPIXEL_NONE;
+ case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
+ return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
+ case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
+ return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
+ case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
+ return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
+ case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
+ return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
+ }
+}
+
+static int
+create_output_for_connector(struct drm_compositor *ec,
+ drmModeRes *resources,
+ drmModeConnector *connector,
+ int x, int y)
+{
+ struct drm_output *output;
+ struct drm_mode *drm_mode, *next;
+ drmModeEncoder *encoder;
+ int i, ret;
+ unsigned handle, stride;
+
+ encoder = drmModeGetEncoder(ec->drm.fd, connector->encoders[0]);
+ if (encoder == NULL) {
+ fprintf(stderr, "No encoder for connector.\n");
+ return -1;
+ }
+
+ for (i = 0; i < resources->count_crtcs; i++) {
+ if (encoder->possible_crtcs & (1 << i) &&
+ !(ec->crtc_allocator & (1 << resources->crtcs[i])))
+ break;
+ }
+ if (i == resources->count_crtcs) {
+ fprintf(stderr, "No usable crtc for encoder.\n");
+ drmModeFreeEncoder(encoder);
+ return -1;
+ }
+
+ output = malloc(sizeof *output);
+ if (output == NULL) {
+ drmModeFreeEncoder(encoder);
+ return -1;
+ }
+ output->fb_id[0] = -1;
+ output->fb_id[1] = -1;
+
+ memset(output, 0, sizeof *output);
+ output->base.subpixel = drm_subpixel_to_wayland(connector->subpixel);
+ output->base.make = "unknown";
+ output->base.model = "unknown";
+ wl_list_init(&output->base.mode_list);
+
+ output->crtc_id = resources->crtcs[i];
+ ec->crtc_allocator |= (1 << output->crtc_id);
+ output->connector_id = connector->connector_id;
+ ec->connector_allocator |= (1 << output->connector_id);
+
+ output->original_crtc = drmModeGetCrtc(ec->drm.fd, output->crtc_id);
+ drmModeFreeEncoder(encoder);
+
+ for (i = 0; i < connector->count_modes; i++) {
+ ret = drm_output_add_mode(output, &connector->modes[i]);
+ if (ret)
+ goto err_free;
+ }
+
+ if (connector->count_modes == 0) {
+ ret = drm_output_add_mode(output, &builtin_1024x768);
+ if (ret)
+ goto err_free;
+ }
+
+ drm_mode = container_of(output->base.mode_list.next,
+ struct drm_mode, base.link);
+ output->base.current = &drm_mode->base;
+ drm_mode->base.flags =
+ WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+
+ glGenRenderbuffers(2, output->rbo);
+ for (i = 0; i < 2; i++) {
+ glBindRenderbuffer(GL_RENDERBUFFER, output->rbo[i]);
+
+ output->bo[i] =
+ gbm_bo_create(ec->gbm,
+ output->base.current->width,
+ output->base.current->height,
+ GBM_BO_FORMAT_XRGB8888,
+ GBM_BO_USE_SCANOUT |
+ GBM_BO_USE_RENDERING);
+ if (!output->bo[i])
+ goto err_bufs;
+
+ output->image[i] = ec->base.create_image(ec->base.display,
+ NULL,
+ EGL_NATIVE_PIXMAP_KHR,
+ output->bo[i], NULL);
+ if (!output->image[i])
+ goto err_bufs;
+
+ ec->base.image_target_renderbuffer_storage(GL_RENDERBUFFER,
+ output->image[i]);
+ stride = gbm_bo_get_pitch(output->bo[i]);
+ handle = gbm_bo_get_handle(output->bo[i]).u32;
+
+ ret = drmModeAddFB(ec->drm.fd,
+ output->base.current->width,
+ output->base.current->height,
+ 24, 32, stride, handle, &output->fb_id[i]);
+ if (ret) {
+ fprintf(stderr, "failed to add fb %d: %m\n", i);
+ goto err_bufs;
+ }
+ }
+
+ output->current = 0;
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER,
+ output->rbo[output->current]);
+ ret = drmModeSetCrtc(ec->drm.fd, output->crtc_id,
+ output->fb_id[output->current ^ 1], 0, 0,
+ &output->connector_id, 1,
+ &drm_mode->mode_info);
+ if (ret) {
+ fprintf(stderr, "failed to set mode: %m\n");
+ goto err_fb;
+ }
+
+ weston_output_init(&output->base, &ec->base, x, y,
+ connector->mmWidth, connector->mmHeight, 0);
+
+ wl_list_insert(ec->base.output_list.prev, &output->base.link);
+
+ output->pending_fs_surf_fb_id = 0;
+ output->base.prepare_render = drm_output_prepare_render;
+ output->base.present = drm_output_present;
+ output->base.prepare_scanout_surface =
+ drm_output_prepare_scanout_surface;
+ output->base.set_hardware_cursor = drm_output_set_cursor;
+ output->base.destroy = drm_output_destroy;
+
+ return 0;
+
+err_fb:
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER,
+ 0);
+err_bufs:
+ for (i = 0; i < 2; i++) {
+ if (output->fb_id[i] != -1)
+ drmModeRmFB(ec->drm.fd, output->fb_id[i]);
+ if (output->image[i])
+ ec->base.destroy_image(ec->base.display,
+ output->image[i]);
+ if (output->bo[i])
+ gbm_bo_destroy(output->bo[i]);
+ }
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ glDeleteRenderbuffers(2, output->rbo);
+err_free:
+ wl_list_for_each_safe(drm_mode, next, &output->base.mode_list,
+ base.link) {
+ wl_list_remove(&drm_mode->base.link);
+ free(drm_mode);
+ }
+
+ drmModeFreeCrtc(output->original_crtc);
+ ec->crtc_allocator &= ~(1 << output->crtc_id);
+ ec->connector_allocator &= ~(1 << output->connector_id);
+
+ free(output);
+ return -1;
+}
+
+static int
+create_outputs(struct drm_compositor *ec, int option_connector)
+{
+ drmModeConnector *connector;
+ drmModeRes *resources;
+ int i;
+ int x = 0, y = 0;
+
+ resources = drmModeGetResources(ec->drm.fd);
+ if (!resources) {
+ fprintf(stderr, "drmModeGetResources failed\n");
+ return -1;
+ }
+
+ for (i = 0; i < resources->count_connectors; i++) {
+ connector = drmModeGetConnector(ec->drm.fd,
+ resources->connectors[i]);
+ if (connector == NULL)
+ continue;
+
+ if (connector->connection == DRM_MODE_CONNECTED &&
+ (option_connector == 0 ||
+ connector->connector_id == option_connector)) {
+ if (create_output_for_connector(ec, resources,
+ connector, x, y) < 0) {
+ drmModeFreeConnector(connector);
+ continue;
+ }
+
+ x += container_of(ec->base.output_list.prev,
+ struct weston_output,
+ link)->current->width;
+ }
+
+ drmModeFreeConnector(connector);
+ }
+
+ if (wl_list_empty(&ec->base.output_list)) {
+ fprintf(stderr, "No currently active connector found.\n");
+ return -1;
+ }
+
+ drmModeFreeResources(resources);
+
+ return 0;
+}
+
+static void
+update_outputs(struct drm_compositor *ec)
+{
+ drmModeConnector *connector;
+ drmModeRes *resources;
+ struct drm_output *output, *next;
+ int x = 0, y = 0;
+ int x_offset = 0, y_offset = 0;
+ uint32_t connected = 0, disconnects = 0;
+ int i;
+
+ resources = drmModeGetResources(ec->drm.fd);
+ if (!resources) {
+ fprintf(stderr, "drmModeGetResources failed\n");
+ return;
+ }
+
+ /* collect new connects */
+ for (i = 0; i < resources->count_connectors; i++) {
+ int connector_id = resources->connectors[i];
+
+ connector = drmModeGetConnector(ec->drm.fd, connector_id);
+ if (connector == NULL)
+ continue;
+
+ if (connector->connection != DRM_MODE_CONNECTED) {
+ drmModeFreeConnector(connector);
+ continue;
+ }
+
+ connected |= (1 << connector_id);
+
+ if (!(ec->connector_allocator & (1 << connector_id))) {
+ struct weston_output *last =
+ container_of(ec->base.output_list.prev,
+ struct weston_output, link);
+
+ /* XXX: not yet needed, we die with 0 outputs */
+ if (!wl_list_empty(&ec->base.output_list))
+ x = last->x + last->current->width;
+ else
+ x = 0;
+ y = 0;
+ create_output_for_connector(ec, resources,
+ connector, x, y);
+ printf("connector %d connected\n", connector_id);
+
+ }
+ drmModeFreeConnector(connector);
+ }
+ drmModeFreeResources(resources);
+
+ disconnects = ec->connector_allocator & ~connected;
+ if (disconnects) {
+ wl_list_for_each_safe(output, next, &ec->base.output_list,
+ base.link) {
+ if (x_offset != 0 || y_offset != 0) {
+ weston_output_move(&output->base,
+ output->base.x - x_offset,
+ output->base.y - y_offset);
+ }
+
+ if (disconnects & (1 << output->connector_id)) {
+ disconnects &= ~(1 << output->connector_id);
+ printf("connector %d disconnected\n",
+ output->connector_id);
+ x_offset += output->base.current->width;
+ drm_output_destroy(&output->base);
+ }
+ }
+ }
+
+ /* FIXME: handle zero outputs, without terminating */
+ if (ec->connector_allocator == 0)
+ wl_display_terminate(ec->base.wl_display);
+}
+
+static int
+udev_event_is_hotplug(struct udev_device *device)
+{
+ struct udev_list_entry *list, *hotplug_entry;
+
+ list = udev_device_get_properties_list_entry(device);
+
+ hotplug_entry = udev_list_entry_get_by_name(list, "HOTPLUG");
+ if (hotplug_entry == NULL)
+ return 0;
+
+ return strcmp(udev_list_entry_get_value(hotplug_entry), "1") == 0;
+}
+
+static int
+udev_drm_event(int fd, uint32_t mask, void *data)
+{
+ struct drm_compositor *ec = data;
+ struct udev_device *event;
+
+ event = udev_monitor_receive_device(ec->udev_monitor);
+
+ if (udev_event_is_hotplug(event))
+ update_outputs(ec);
+
+ udev_device_unref(event);
+
+ return 1;
+}
+
+static EGLImageKHR
+drm_compositor_create_cursor_image(struct weston_compositor *ec,
+ int32_t *width, int32_t *height)
+{
+ struct drm_compositor *c = (struct drm_compositor *) ec;
+ struct gbm_bo *bo;
+ EGLImageKHR image;
+
+ if (*width > 64 || *height > 64)
+ return EGL_NO_IMAGE_KHR;
+
+ bo = gbm_bo_create(c->gbm,
+ /* width, height, */ 64, 64,
+ GBM_BO_FORMAT_ARGB8888,
+ GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_RENDERING);
+
+ image = ec->create_image(c->base.display, NULL,
+ EGL_NATIVE_PIXMAP_KHR, bo, NULL);
+ gbm_bo_destroy(bo);
+
+ *width = 64;
+ *height = 64;
+
+ return image;
+}
+
+static void
+drm_destroy(struct weston_compositor *ec)
+{
+ struct drm_compositor *d = (struct drm_compositor *) ec;
+ struct weston_input_device *input, *next;
+
+ weston_compositor_shutdown(ec);
+ gbm_device_destroy(d->gbm);
+ tty_destroy(d->tty);
+
+ wl_list_for_each_safe(input, next, &ec->input_device_list, link)
+ evdev_input_destroy(input);
+
+ free(d);
+}
+
+static void
+vt_func(struct weston_compositor *compositor, int event)
+{
+ struct drm_compositor *ec = (struct drm_compositor *) compositor;
+ struct weston_output *output;
+ struct weston_input_device *input;
+
+ switch (event) {
+ case TTY_ENTER_VT:
+ compositor->focus = 1;
+ drmSetMaster(ec->drm.fd);
+ compositor->state = ec->prev_state;
+ weston_compositor_damage_all(compositor);
+ wl_list_for_each(input, &compositor->input_device_list, link)
+ evdev_add_devices(ec->udev, input);
+ break;
+ case TTY_LEAVE_VT:
+ compositor->focus = 0;
+ ec->prev_state = compositor->state;
+ compositor->state = WESTON_COMPOSITOR_SLEEPING;
+
+ wl_list_for_each(input, &compositor->input_device_list, link)
+ evdev_remove_devices(input);
+ wl_list_for_each(output, &ec->base.output_list, link)
+ drm_output_set_cursor(output, NULL);
+
+ drmDropMaster(ec->drm.fd);
+ break;
+ };
+}
+
+static const char default_seat[] = "seat0";
+
+static struct weston_compositor *
+drm_compositor_create(struct wl_display *display,
+ int connector, const char *seat, int tty)
+{
+ struct drm_compositor *ec;
+ struct udev_enumerate *e;
+ struct udev_list_entry *entry;
+ struct udev_device *device, *drm_device;
+ const char *path, *device_seat;
+ struct wl_event_loop *loop;
+
+ ec = malloc(sizeof *ec);
+ if (ec == NULL)
+ return NULL;
+
+ memset(ec, 0, sizeof *ec);
+ ec->udev = udev_new();
+ if (ec->udev == NULL) {
+ fprintf(stderr, "failed to initialize udev context\n");
+ return NULL;
+ }
+
+ e = udev_enumerate_new(ec->udev);
+ udev_enumerate_add_match_subsystem(e, "drm");
+ udev_enumerate_add_match_sysname(e, "card[0-9]*");
+
+ udev_enumerate_scan_devices(e);
+ drm_device = NULL;
+ udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
+ path = udev_list_entry_get_name(entry);
+ device = udev_device_new_from_syspath(ec->udev, path);
+ device_seat =
+ udev_device_get_property_value(device, "ID_SEAT");
+ if (!device_seat)
+ device_seat = default_seat;
+ if (strcmp(device_seat, seat) == 0) {
+ drm_device = device;
+ break;
+ }
+ udev_device_unref(device);
+ }
+
+ if (drm_device == NULL) {
+ fprintf(stderr, "no drm device found\n");
+ return NULL;
+ }
+
+ ec->base.wl_display = display;
+ if (init_egl(ec, drm_device) < 0) {
+ fprintf(stderr, "failed to initialize egl\n");
+ return NULL;
+ }
+
+ udev_device_unref(drm_device);
+
+ ec->base.destroy = drm_destroy;
+ ec->base.create_cursor_image = drm_compositor_create_cursor_image;
+
+ ec->base.focus = 1;
+
+ ec->prev_state = WESTON_COMPOSITOR_ACTIVE;
+
+ glGenFramebuffers(1, &ec->base.fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, ec->base.fbo);
+
+ /* Can't init base class until we have a current egl context */
+ if (weston_compositor_init(&ec->base, display) < 0)
+ return NULL;
+
+ if (create_outputs(ec, connector) < 0) {
+ fprintf(stderr, "failed to create output for %s\n", path);
+ return NULL;
+ }
+
+ udev_enumerate_unref(e);
+ path = NULL;
+
+ evdev_input_create(&ec->base, ec->udev, seat);
+
+ loop = wl_display_get_event_loop(ec->base.wl_display);
+ ec->drm_source =
+ wl_event_loop_add_fd(loop, ec->drm.fd,
+ WL_EVENT_READABLE, on_drm_input, ec);
+ ec->tty = tty_create(&ec->base, vt_func, tty);
+
+ ec->udev_monitor = udev_monitor_new_from_netlink(ec->udev, "udev");
+ if (ec->udev_monitor == NULL) {
+ fprintf(stderr, "failed to intialize udev monitor\n");
+ return NULL;
+ }
+ udev_monitor_filter_add_match_subsystem_devtype(ec->udev_monitor,
+ "drm", NULL);
+ ec->udev_drm_source =
+ wl_event_loop_add_fd(loop,
+ udev_monitor_get_fd(ec->udev_monitor),
+ WL_EVENT_READABLE, udev_drm_event, ec);
+
+ if (udev_monitor_enable_receiving(ec->udev_monitor) < 0) {
+ fprintf(stderr, "failed to enable udev-monitor receiving\n");
+ return NULL;
+ }
+
+ return &ec->base;
+}
+
+struct weston_compositor *
+backend_init(struct wl_display *display, char *options);
+
+WL_EXPORT struct weston_compositor *
+backend_init(struct wl_display *display, char *options)
+{
+ int connector = 0, i;
+ const char *seat;
+ char *p, *value;
+ int tty = 1;
+
+ static char * const tokens[] = { "connector", "seat", "tty", NULL };
+
+ p = options;
+ seat = default_seat;
+ while (i = getsubopt(&p, tokens, &value), i != -1) {
+ switch (i) {
+ case 0:
+ connector = strtol(value, NULL, 0);
+ break;
+ case 1:
+ seat = value;
+ break;
+ case 2:
+ tty = strtol(value, NULL, 0);
+ break;
+ }
+ }
+
+ return drm_compositor_create(display, connector, seat, tty);
+}
diff --git a/src/compositor-openwfd.c b/src/compositor-openwfd.c
new file mode 100644
index 0000000..8fb671a
--- /dev/null
+++ b/src/compositor-openwfd.c
@@ -0,0 +1,699 @@
+/*
+ * Copyright © 2011 Benjamin Franzke
+ * Copyright © 2011 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/time.h>
+
+#include <WF/wfd.h>
+#include <WF/wfdext.h>
+
+#include <gbm.h>
+
+#include "compositor.h"
+#include "evdev.h"
+
+struct wfd_compositor {
+ struct wlsc_compositor base;
+
+ struct udev *udev;
+ struct gbm_device *gbm;
+ WFDDevice dev;
+
+ WFDEvent event;
+ int wfd_fd;
+ struct wl_event_source *wfd_source;
+
+ struct tty *tty;
+
+ uint32_t start_time;
+ uint32_t used_pipelines;
+};
+
+struct wfd_mode {
+ struct wlsc_mode base;
+ WFDPortMode mode;
+};
+
+struct wfd_output {
+ struct wlsc_output base;
+
+ WFDPort port;
+
+ WFDPipeline pipeline;
+ WFDint pipeline_id;
+
+ WFDPortMode mode;
+ WFDSource source[2];
+
+ struct gbm_bo *bo[2];
+ EGLImageKHR image[2];
+ GLuint rbo[2];
+ uint32_t current;
+};
+
+union wfd_geometry {
+ struct {
+ WFDint x, y;
+ WFDint width, height;
+ } g;
+
+ WFDint array[4];
+};
+
+static int
+wfd_output_prepare_render(struct wlsc_output *output_base)
+{
+ struct wfd_output *output = (struct wfd_output *) output_base;
+
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER,
+ output->rbo[output->current]);
+
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+ return -1;
+
+ return 0;
+}
+
+static int
+wfd_output_present(struct wlsc_output *output_base)
+{
+ struct wfd_output *output = (struct wfd_output *) output_base;
+ struct wfd_compositor *c =
+ (struct wfd_compositor *) output->base.compositor;
+
+ if (wfd_output_prepare_render(&output->base))
+ return -1;
+ glFlush();
+
+ output->current ^= 1;
+
+ wfdBindSourceToPipeline(c->dev, output->pipeline,
+ output->source[output->current ^ 1],
+ WFD_TRANSITION_AT_VSYNC, NULL);
+
+ wfdDeviceCommit(c->dev, WFD_COMMIT_PIPELINE, output->pipeline);
+
+ return 0;
+}
+
+static int
+init_egl(struct wfd_compositor *ec)
+{
+ EGLint major, minor;
+ const char *extensions;
+ int fd;
+ static const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ fd = wfdGetDeviceAttribi(ec->dev, WFD_DEVICE_ID);
+ if (fd < 0)
+ return -1;
+
+ ec->wfd_fd = fd;
+ ec->gbm = gbm_create_device(ec->wfd_fd);
+ ec->base.display = eglGetDisplay(ec->gbm);
+ if (ec->base.display == NULL) {
+ fprintf(stderr, "failed to create display\n");
+ return -1;
+ }
+
+ if (!eglInitialize(ec->base.display, &major, &minor)) {
+ fprintf(stderr, "failed to initialize display\n");
+ return -1;
+ }
+
+ extensions = eglQueryString(ec->base.display, EGL_EXTENSIONS);
+ if (!strstr(extensions, "EGL_KHR_surfaceless_gles2")) {
+ fprintf(stderr, "EGL_KHR_surfaceless_gles2 not available\n");
+ return -1;
+ }
+
+ if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+ fprintf(stderr, "failed to bind api EGL_OPENGL_ES_API\n");
+ return -1;
+ }
+
+ ec->base.context = eglCreateContext(ec->base.display, NULL,
+ EGL_NO_CONTEXT, context_attribs);
+ if (ec->base.context == NULL) {
+ fprintf(stderr, "failed to create context\n");
+ return -1;
+ }
+
+ if (!eglMakeCurrent(ec->base.display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, ec->base.context)) {
+ fprintf(stderr, "failed to make context current\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+wfd_output_prepare_scanout_surface(struct wlsc_output *output_base,
+ struct wlsc_surface *es)
+{
+ return -1;
+}
+
+static int
+wfd_output_set_cursor(struct wlsc_output *output_base,
+ struct wlsc_input_device *input)
+{
+ return -1;
+}
+
+static void
+wfd_output_destroy(struct wlsc_output *output_base)
+{
+ struct wfd_output *output = (struct wfd_output *) output_base;
+ struct wfd_compositor *ec =
+ (struct wfd_compositor *) output->base.compositor;
+ int i;
+
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER,
+ 0);
+
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ glDeleteRenderbuffers(2, output->rbo);
+
+ for (i = 0; i < 2; i++) {
+ ec->base.destroy_image(ec->base.display, output->image[i]);
+ gbm_bo_destroy(output->bo[i]);
+ wfdDestroySource(ec->dev, output->source[i]);
+ }
+
+ ec->used_pipelines &= ~(1 << output->pipeline_id);
+ wfdDestroyPipeline(ec->dev, output->pipeline);
+ wfdDestroyPort(ec->dev, output->port);
+
+ wlsc_output_destroy(&output->base);
+ wl_list_remove(&output->base.link);
+
+ free(output);
+}
+
+static int
+wfd_output_add_mode(struct wfd_output *output, WFDPortMode mode)
+{
+ struct wfd_compositor *ec =
+ (struct wfd_compositor *) output->base.compositor;
+ struct wfd_mode *wmode;
+
+ wmode = malloc(sizeof *wmode);
+ if (wmode == NULL)
+ return -1;
+
+ wmode->base.flags = 0;
+ wmode->base.width = wfdGetPortModeAttribi(ec->dev, output->port, mode,
+ WFD_PORT_MODE_WIDTH);
+ wmode->base.height = wfdGetPortModeAttribi(ec->dev, output->port, mode,
+ WFD_PORT_MODE_HEIGHT);
+ wmode->base.refresh = wfdGetPortModeAttribi(ec->dev, output->port, mode,
+ WFD_PORT_MODE_REFRESH_RATE);
+ wmode->mode = mode;
+ wl_list_insert(output->base.mode_list.prev, &wmode->base.link);
+
+ return 0;
+}
+
+static int
+create_output_for_port(struct wfd_compositor *ec,
+ WFDHandle port,
+ int x, int y)
+{
+ struct wfd_output *output;
+ int i;
+ WFDint num_pipelines, *pipelines;
+ WFDint num_modes;
+ union wfd_geometry geometry;
+ struct wfd_mode *mode;
+ WFDPortMode *modes;
+ WFDfloat physical_size[2];
+
+ output = malloc(sizeof *output);
+ if (output == NULL)
+ return -1;
+
+ memset(output, 0, sizeof *output);
+ memset(&geometry, 0, sizeof geometry);
+
+ output->port = port;
+ wl_list_init(&output->base.mode_list);
+
+ wfdSetPortAttribi(ec->dev, output->port,
+ WFD_PORT_POWER_MODE, WFD_POWER_MODE_ON);
+
+ num_modes = wfdGetPortModes(ec->dev, output->port, NULL, 0);
+ if (num_modes < 1) {
+ fprintf(stderr, "failed to get port mode\n");
+ goto cleanup_port;
+ }
+
+ modes = malloc(sizeof(WFDPortMode) * num_modes);
+ if (modes == NULL)
+ goto cleanup_port;
+
+ output->base.compositor = &ec->base;
+ num_modes = wfdGetPortModes(ec->dev, output->port, modes, num_modes);
+ for (i = 0; i < num_modes; ++i)
+ wfd_output_add_mode(output, modes[i]);
+
+ free(modes);
+
+ wfdGetPortAttribiv(ec->dev, output->port,
+ WFD_PORT_NATIVE_RESOLUTION,
+ 2, &geometry.array[2]);
+
+ output->base.current = NULL;
+ wl_list_for_each(mode, &output->base.mode_list, base.link) {
+ if (mode->base.width == geometry.g.width &&
+ mode->base.height == geometry.g.height) {
+ output->base.current = &mode->base;
+ break;
+ }
+ }
+ if (output->base.current == NULL) {
+ fprintf(stderr, "failed to find a native mode\n");
+ goto cleanup_port;
+ }
+
+ mode = (struct wfd_mode *) output->base.current;
+ mode->base.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+
+ wfdSetPortMode(ec->dev, output->port, mode->mode);
+
+ wfdEnumeratePipelines(ec->dev, NULL, 0, NULL);
+
+ num_pipelines = wfdGetPortAttribi(ec->dev, output->port,
+ WFD_PORT_PIPELINE_ID_COUNT);
+ if (num_pipelines < 1) {
+ fprintf(stderr, "failed to get a bindable pipeline\n");
+ goto cleanup_port;
+ }
+ pipelines = calloc(num_pipelines, sizeof *pipelines);
+ if (pipelines == NULL)
+ goto cleanup_port;
+
+ wfdGetPortAttribiv(ec->dev, output->port,
+ WFD_PORT_BINDABLE_PIPELINE_IDS,
+ num_pipelines, pipelines);
+
+ output->pipeline_id = WFD_INVALID_PIPELINE_ID;
+ for (i = 0; i < num_pipelines; ++i) {
+ if (!(ec->used_pipelines & (1 << pipelines[i]))) {
+ output->pipeline_id = pipelines[i];
+ break;
+ }
+ }
+ if (output->pipeline_id == WFD_INVALID_PIPELINE_ID) {
+ fprintf(stderr, "no pipeline found for port: %d\n", port);
+ goto cleanup_pipelines;
+ }
+
+ ec->used_pipelines |= (1 << output->pipeline_id);
+
+ wfdGetPortAttribfv(ec->dev, output->port,
+ WFD_PORT_PHYSICAL_SIZE,
+ 2, physical_size);
+
+ wlsc_output_init(&output->base, &ec->base, x, y,
+ physical_size[0], physical_size[1], 0);
+
+ output->pipeline = wfdCreatePipeline(ec->dev, output->pipeline_id, NULL);
+ if (output->pipeline == WFD_INVALID_HANDLE) {
+ fprintf(stderr, "failed to create a pipeline\n");
+ goto cleanup_wlsc_output;
+ }
+
+ glGenRenderbuffers(2, output->rbo);
+ for (i = 0; i < 2; i++) {
+ glBindRenderbuffer(GL_RENDERBUFFER, output->rbo[i]);
+
+ output->bo[i] =
+ gbm_bo_create(ec->gbm,
+ output->base.current->width,
+ output->base.current->height,
+ GBM_BO_FORMAT_XRGB8888,
+ GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+ output->image[i] = ec->base.create_image(ec->base.display,
+ NULL,
+ EGL_NATIVE_PIXMAP_KHR,
+ output->bo[i], NULL);
+
+ printf("output->image[i]: %p\n", output->image[i]);
+ ec->base.image_target_renderbuffer_storage(GL_RENDERBUFFER,
+ output->image[i]);
+ output->source[i] =
+ wfdCreateSourceFromImage(ec->dev, output->pipeline,
+ output->image[i], NULL);
+
+ if (output->source[i] == WFD_INVALID_HANDLE) {
+ fprintf(stderr, "failed to create source\n");
+ goto cleanup_pipeline;
+ }
+ }
+
+ output->current = 0;
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER,
+ output->rbo[output->current]);
+
+ wfdSetPipelineAttribiv(ec->dev, output->pipeline,
+ WFD_PIPELINE_SOURCE_RECTANGLE,
+ 4, geometry.array);
+ wfdSetPipelineAttribiv(ec->dev, output->pipeline,
+ WFD_PIPELINE_DESTINATION_RECTANGLE,
+ 4, geometry.array);
+
+ wfdBindSourceToPipeline(ec->dev, output->pipeline,
+ output->source[output->current ^ 1],
+ WFD_TRANSITION_AT_VSYNC, NULL);
+
+ wfdBindPipelineToPort(ec->dev, output->port, output->pipeline);
+
+ wfdDeviceCommit(ec->dev, WFD_COMMIT_ENTIRE_DEVICE, WFD_INVALID_HANDLE);
+
+ output->base.prepare_render = wfd_output_prepare_render;
+ output->base.present = wfd_output_present;
+ output->base.prepare_scanout_surface =
+ wfd_output_prepare_scanout_surface;
+ output->base.set_hardware_cursor = wfd_output_set_cursor;
+ output->base.destroy = wfd_output_destroy;
+
+ wl_list_insert(ec->base.output_list.prev, &output->base.link);
+
+ return 0;
+
+cleanup_pipeline:
+ wfdDestroyPipeline(ec->dev, output->pipeline);
+cleanup_wlsc_output:
+ wlsc_output_destroy(&output->base);
+cleanup_pipelines:
+ free(pipelines);
+cleanup_port:
+ wfdDestroyPort(ec->dev, output->port);
+ free(output);
+
+ return -1;
+}
+
+static int
+create_outputs(struct wfd_compositor *ec, int option_connector)
+{
+ int x = 0, y = 0;
+ WFDint i, num, *ports;
+ WFDPort port = WFD_INVALID_HANDLE;
+
+ num = wfdEnumeratePorts(ec->dev, NULL, 0, NULL);
+ ports = calloc(num, sizeof *ports);
+ if (ports == NULL)
+ return -1;
+
+ num = wfdEnumeratePorts(ec->dev, ports, num, NULL);
+ if (num < 1)
+ return -1;
+
+ for (i = 0; i < num; ++i) {
+ port = wfdCreatePort(ec->dev, ports[i], NULL);
+ if (port == WFD_INVALID_HANDLE)
+ continue;
+
+ if (wfdGetPortAttribi(ec->dev, port, WFD_PORT_ATTACHED) &&
+ (option_connector == 0 || ports[i] == option_connector)) {
+ create_output_for_port(ec, port, x, y);
+
+ x += container_of(ec->base.output_list.prev,
+ struct wlsc_output,
+ link)->current->width;
+ } else {
+ wfdDestroyPort(ec->dev, port);
+ }
+ }
+
+ free(ports);
+
+ return 0;
+}
+
+static int
+handle_port_state_change(struct wfd_compositor *ec)
+{
+ struct wfd_output *output, *next;
+ WFDint output_port_id;
+ int x = 0, y = 0;
+ int x_offset = 0, y_offset = 0;
+ WFDPort port;
+ WFDint port_id;
+ WFDboolean state;
+
+ port_id = wfdGetEventAttribi(ec->dev, ec->event,
+ WFD_EVENT_PORT_ATTACH_PORT_ID);
+ state = wfdGetEventAttribi(ec->dev, ec->event,
+ WFD_EVENT_PORT_ATTACH_STATE);
+
+ if (state) {
+ struct wlsc_output *last_output =
+ container_of(ec->base.output_list.prev,
+ struct wlsc_output, link);
+
+ /* XXX: not yet needed, we die with 0 outputs */
+ if (!wl_list_empty(&ec->base.output_list))
+ x = last_output->x +
+ last_output->current->width;
+ else
+ x = 0;
+ y = 0;
+
+ port = wfdCreatePort(ec->dev, port_id, NULL);
+ if (port == WFD_INVALID_HANDLE)
+ return -1;
+
+ create_output_for_port(ec, port, x, y);
+
+ return 0;
+ }
+
+ wl_list_for_each_safe(output, next, &ec->base.output_list, base.link) {
+ output_port_id =
+ wfdGetPortAttribi(ec->dev, output->port, WFD_PORT_ID);
+
+ if (!state && output_port_id == port_id) {
+ x_offset += output->base.current->width;
+ wfd_output_destroy(&output->base);
+ continue;
+ }
+
+ if (x_offset != 0 || y_offset != 0) {
+ wlsc_output_move(&output->base,
+ output->base.x - x_offset,
+ output->base.y - y_offset);
+ }
+ }
+
+ if (ec->used_pipelines == 0)
+ wl_display_terminate(ec->base.wl_display);
+
+ return 0;
+}
+
+static int
+on_wfd_event(int fd, uint32_t mask, void *data)
+{
+ struct wfd_compositor *c = data;
+ struct wfd_output *output = NULL, *output_iter;
+ WFDEventType type;
+ const WFDtime timeout = 0;
+ WFDint pipeline_id;
+ WFDint bind_time;
+
+ type = wfdDeviceEventWait(c->dev, c->event, timeout);
+
+ switch (type) {
+ case WFD_EVENT_PIPELINE_BIND_SOURCE_COMPLETE:
+ pipeline_id =
+ wfdGetEventAttribi(c->dev, c->event,
+ WFD_EVENT_PIPELINE_BIND_PIPELINE_ID);
+
+ bind_time =
+ wfdGetEventAttribi(c->dev, c->event,
+ WFD_EVENT_PIPELINE_BIND_TIME_EXT);
+
+ wl_list_for_each(output_iter, &c->base.output_list, base.link) {
+ if (output_iter->pipeline_id == pipeline_id)
+ output = output_iter;
+ }
+
+ if (output == NULL)
+ return 1;
+
+ wlsc_output_finish_frame(&output->base,
+ c->start_time + bind_time);
+ break;
+ case WFD_EVENT_PORT_ATTACH_DETACH:
+ handle_port_state_change(c);
+ break;
+ default:
+ return 1;
+ }
+
+ return 1;
+}
+
+static void
+wfd_destroy(struct wlsc_compositor *ec)
+{
+ struct wfd_compositor *d = (struct wfd_compositor *) ec;
+
+ wlsc_compositor_shutdown(ec);
+
+ udev_unref(d->udev);
+
+ wfdDestroyDevice(d->dev);
+
+ tty_destroy(d->tty);
+
+ free(d);
+}
+
+/* FIXME: Just add a stub here for now
+ * handle drm{Set,Drop}Master in owfdrm somehow */
+static void
+vt_func(struct wlsc_compositor *compositor, int event)
+{
+ return;
+}
+
+static const char default_seat[] = "seat0";
+
+static struct wlsc_compositor *
+wfd_compositor_create(struct wl_display *display,
+ int connector, const char *seat, int tty)
+{
+ struct wfd_compositor *ec;
+ struct wl_event_loop *loop;
+ struct timeval tv;
+
+ ec = malloc(sizeof *ec);
+ if (ec == NULL)
+ return NULL;
+
+ memset(ec, 0, sizeof *ec);
+
+ gettimeofday(&tv, NULL);
+ ec->start_time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+
+ ec->udev = udev_new();
+ if (ec->udev == NULL) {
+ fprintf(stderr, "failed to initialize udev context\n");
+ return NULL;
+ }
+
+ ec->dev = wfdCreateDevice(WFD_DEFAULT_DEVICE_ID, NULL);
+ if (ec->dev == WFD_INVALID_HANDLE) {
+ fprintf(stderr, "failed to create wfd device\n");
+ return NULL;
+ }
+
+ ec->event = wfdCreateEvent(ec->dev, NULL);
+ if (ec->event == WFD_INVALID_HANDLE) {
+ fprintf(stderr, "failed to create wfd event\n");
+ return NULL;
+ }
+
+ ec->base.wl_display = display;
+ if (init_egl(ec) < 0) {
+ fprintf(stderr, "failed to initialize egl\n");
+ return NULL;
+ }
+
+ ec->base.destroy = wfd_destroy;
+ ec->base.focus = 1;
+
+ glGenFramebuffers(1, &ec->base.fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, ec->base.fbo);
+
+ /* Can't init base class until we have a current egl context */
+ if (wlsc_compositor_init(&ec->base, display) < 0)
+ return NULL;
+
+ if (create_outputs(ec, connector) < 0) {
+ fprintf(stderr, "failed to create outputs\n");
+ return NULL;
+ }
+
+ evdev_input_create(&ec->base, ec->udev, seat);
+
+ loop = wl_display_get_event_loop(ec->base.wl_display);
+ ec->wfd_source =
+ wl_event_loop_add_fd(loop,
+ wfdDeviceEventGetFD(ec->dev, ec->event),
+ WL_EVENT_READABLE, on_wfd_event, ec);
+ ec->tty = tty_create(&ec->base, vt_func, tty);
+
+ return &ec->base;
+}
+
+struct wlsc_compositor *
+backend_init(struct wl_display *display, char *options);
+
+WL_EXPORT struct wlsc_compositor *
+backend_init(struct wl_display *display, char *options)
+{
+ int connector = 0, i;
+ const char *seat;
+ char *p, *value;
+ int tty = 1;
+
+ static char * const tokens[] = { "connector", "seat", "tty", NULL };
+
+ p = options;
+ seat = default_seat;
+ while (i = getsubopt(&p, tokens, &value), i != -1) {
+ switch (i) {
+ case 0:
+ connector = strtol(value, NULL, 0);
+ break;
+ case 1:
+ seat = value;
+ break;
+ case 2:
+ tty = strtol(value, NULL, 0);
+ break;
+ }
+ }
+
+ return wfd_compositor_create(display, connector, seat, tty);
+}
diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c
new file mode 100644
index 0000000..4fa9df1
--- /dev/null
+++ b/src/compositor-wayland.c
@@ -0,0 +1,587 @@
+/*
+ * Copyright © 2010-2011 Benjamin Franzke
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stddef.h>
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <wayland-client.h>
+#include <wayland-egl.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "compositor.h"
+
+struct wayland_compositor {
+ struct weston_compositor base;
+
+ struct {
+ struct wl_display *display;
+ struct wl_compositor *compositor;
+ struct wl_shell *shell;
+ struct wl_output *output;
+
+ struct {
+ int32_t x, y, width, height;
+ } screen_allocation;
+
+ struct wl_event_source *wl_source;
+ uint32_t event_mask;
+ } parent;
+
+ struct wl_list input_list;
+};
+
+struct wayland_output {
+ struct weston_output base;
+
+ struct {
+ struct wl_surface *surface;
+ struct wl_shell_surface *shell_surface;
+ struct wl_egl_window *egl_window;
+ } parent;
+ EGLSurface egl_surface;
+ struct weston_mode mode;
+};
+
+struct wayland_input {
+ struct wayland_compositor *compositor;
+ struct wl_input_device *input_device;
+ struct wl_list link;
+};
+
+static int
+wayland_input_create(struct wayland_compositor *c)
+{
+ struct weston_input_device *input;
+
+ input = malloc(sizeof *input);
+ if (input == NULL)
+ return -1;
+
+ memset(input, 0, sizeof *input);
+ weston_input_device_init(input, &c->base);
+
+ c->base.input_device = &input->input_device;
+
+ return 0;
+}
+
+static int
+wayland_compositor_init_egl(struct wayland_compositor *c)
+{
+ EGLint major, minor;
+ EGLint n;
+ const char *extensions;
+ EGLint config_attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_ALPHA_SIZE, 0,
+ EGL_DEPTH_SIZE, 1,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+ static const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ c->base.display = eglGetDisplay(c->parent.display);
+ if (c->base.display == NULL) {
+ fprintf(stderr, "failed to create display\n");
+ return -1;
+ }
+
+ if (!eglInitialize(c->base.display, &major, &minor)) {
+ fprintf(stderr, "failed to initialize display\n");
+ return -1;
+ }
+
+ extensions = eglQueryString(c->base.display, EGL_EXTENSIONS);
+ if (!strstr(extensions, "EGL_KHR_surfaceless_gles2")) {
+ fprintf(stderr, "EGL_KHR_surfaceless_gles2 not available\n");
+ return -1;
+ }
+
+ if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+ fprintf(stderr, "failed to bind EGL_OPENGL_ES_API\n");
+ return -1;
+ }
+ if (!eglChooseConfig(c->base.display, config_attribs,
+ &c->base.config, 1, &n) || n == 0) {
+ fprintf(stderr, "failed to choose config: %d\n", n);
+ return -1;
+ }
+
+ c->base.context = eglCreateContext(c->base.display, c->base.config,
+ EGL_NO_CONTEXT, context_attribs);
+ if (c->base.context == NULL) {
+ fprintf(stderr, "failed to create context\n");
+ return -1;
+ }
+
+ if (!eglMakeCurrent(c->base.display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, c->base.context)) {
+ fprintf(stderr, "failed to make context current\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+wayland_output_prepare_render(struct weston_output *output_base)
+{
+ struct wayland_output *output = (struct wayland_output *) output_base;
+ struct weston_compositor *ec = output->base.compositor;
+
+ if (!eglMakeCurrent(ec->display, output->egl_surface,
+ output->egl_surface, ec->context)) {
+ fprintf(stderr, "failed to make current\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+frame_done(void *data, struct wl_callback *wl_callback, uint32_t time)
+{
+ struct weston_output *output = data;
+
+ weston_output_finish_frame(output, time);
+}
+
+static const struct wl_callback_listener frame_listener = {
+ frame_done
+};
+
+static int
+wayland_output_present(struct weston_output *output_base)
+{
+ struct wayland_output *output = (struct wayland_output *) output_base;
+ struct wayland_compositor *c =
+ (struct wayland_compositor *) output->base.compositor;
+ struct wl_callback *callback;
+
+ if (wayland_output_prepare_render(&output->base))
+ return -1;
+
+ eglSwapBuffers(c->base.display, output->egl_surface);
+ callback = wl_surface_frame(output->parent.surface);
+ wl_callback_add_listener(callback, &frame_listener, output);
+
+ return 0;
+}
+
+static int
+wayland_output_prepare_scanout_surface(struct weston_output *output_base,
+ struct weston_surface *es)
+{
+ return -1;
+}
+
+static int
+wayland_output_set_cursor(struct weston_output *output_base,
+ struct weston_input_device *input)
+{
+ return -1;
+}
+
+static void
+wayland_output_destroy(struct weston_output *output_base)
+{
+ struct wayland_output *output = (struct wayland_output *) output_base;
+ struct weston_compositor *ec = output->base.compositor;
+
+ eglDestroySurface(ec->display, output->egl_surface);
+ wl_egl_window_destroy(output->parent.egl_window);
+ free(output);
+
+ return;
+}
+
+static int
+wayland_compositor_create_output(struct wayland_compositor *c,
+ int width, int height)
+{
+ struct wayland_output *output;
+
+ output = malloc(sizeof *output);
+ if (output == NULL)
+ return -1;
+ memset(output, 0, sizeof *output);
+
+ output->mode.flags =
+ WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+ output->mode.width = width;
+ output->mode.height = height;
+ output->mode.refresh = 60;
+ wl_list_init(&output->base.mode_list);
+ wl_list_insert(&output->base.mode_list, &output->mode.link);
+
+ output->base.current = &output->mode;
+ weston_output_init(&output->base, &c->base, 0, 0, width, height,
+ WL_OUTPUT_FLIPPED);
+
+ output->parent.surface =
+ wl_compositor_create_surface(c->parent.compositor);
+ wl_surface_set_user_data(output->parent.surface, output);
+
+ output->parent.egl_window =
+ wl_egl_window_create(output->parent.surface, width, height);
+ if (!output->parent.egl_window) {
+ fprintf(stderr, "failure to create wl_egl_window\n");
+ goto cleanup_output;
+ }
+
+ output->egl_surface =
+ eglCreateWindowSurface(c->base.display, c->base.config,
+ output->parent.egl_window, NULL);
+ if (!output->egl_surface) {
+ fprintf(stderr, "failed to create window surface\n");
+ goto cleanup_window;
+ }
+
+ if (!eglMakeCurrent(c->base.display, output->egl_surface,
+ output->egl_surface, c->base.context)) {
+ fprintf(stderr, "failed to make surface current\n");
+ goto cleanup_surface;
+ return -1;
+ }
+
+ output->parent.shell_surface =
+ wl_shell_get_shell_surface(c->parent.shell,
+ output->parent.surface);
+ /* FIXME: add shell_surface listener for resizing */
+ wl_shell_surface_set_toplevel(output->parent.shell_surface);
+
+ glClearColor(0, 0, 0, 0.5);
+
+ output->base.prepare_render = wayland_output_prepare_render;
+ output->base.present = wayland_output_present;
+ output->base.prepare_scanout_surface =
+ wayland_output_prepare_scanout_surface;
+ output->base.set_hardware_cursor = wayland_output_set_cursor;
+ output->base.destroy = wayland_output_destroy;
+
+ wl_list_insert(c->base.output_list.prev, &output->base.link);
+
+ return 0;
+
+cleanup_surface:
+ eglDestroySurface(c->base.display, output->egl_surface);
+cleanup_window:
+ wl_egl_window_destroy(output->parent.egl_window);
+cleanup_output:
+ /* FIXME: cleanup weston_output */
+ free(output);
+
+ return -1;
+}
+
+/* Events received from the wayland-server this compositor is client of: */
+
+/* parent output interface */
+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)
+{
+ struct wayland_compositor *c = data;
+
+ c->parent.screen_allocation.x = x;
+ c->parent.screen_allocation.y = y;
+}
+
+static void
+display_handle_mode(void *data,
+ struct wl_output *wl_output,
+ uint32_t flags,
+ int width,
+ int height,
+ int refresh)
+{
+ struct wayland_compositor *c = data;
+
+ c->parent.screen_allocation.width = width;
+ c->parent.screen_allocation.height = height;
+}
+
+static const struct wl_output_listener output_listener = {
+ display_handle_geometry,
+ display_handle_mode
+};
+
+/* parent input interface */
+static void
+input_handle_motion(void *data, struct wl_input_device *input_device,
+ uint32_t time,
+ int32_t x, int32_t y, int32_t sx, int32_t sy)
+{
+ struct wayland_input *input = data;
+ struct wayland_compositor *c = input->compositor;
+
+ notify_motion(c->base.input_device, time, sx, sy);
+}
+
+static void
+input_handle_button(void *data,
+ struct wl_input_device *input_device,
+ uint32_t time, uint32_t button, uint32_t state)
+{
+ struct wayland_input *input = data;
+ struct wayland_compositor *c = input->compositor;
+
+ notify_button(c->base.input_device, time, button, state);
+}
+
+static void
+input_handle_key(void *data, struct wl_input_device *input_device,
+ uint32_t time, uint32_t key, uint32_t state)
+{
+ struct wayland_input *input = data;
+ struct wayland_compositor *c = input->compositor;
+
+ notify_key(c->base.input_device, time, key, state);
+}
+
+static void
+input_handle_pointer_focus(void *data,
+ struct wl_input_device *input_device,
+ uint32_t time, struct wl_surface *surface,
+ int32_t x, int32_t y, int32_t sx, int32_t sy)
+{
+ struct wayland_input *input = data;
+ struct wayland_output *output;
+ struct wayland_compositor *c = input->compositor;
+
+ if (surface) {
+ output = wl_surface_get_user_data(surface);
+ notify_pointer_focus(c->base.input_device,
+ time, &output->base, sx, sy);
+ } else {
+ notify_pointer_focus(c->base.input_device, time, NULL, 0, 0);
+ }
+}
+
+static void
+input_handle_keyboard_focus(void *data,
+ struct wl_input_device *input_device,
+ uint32_t time,
+ struct wl_surface *surface,
+ struct wl_array *keys)
+{
+ struct wayland_input *input = data;
+ struct wayland_compositor *c = input->compositor;
+ struct wayland_output *output;
+
+ if (surface) {
+ output = wl_surface_get_user_data(surface);
+ notify_keyboard_focus(c->base.input_device,
+ time, &output->base, keys);
+ } else {
+ notify_keyboard_focus(c->base.input_device, time, NULL, NULL);
+ }
+}
+
+static const struct wl_input_device_listener input_device_listener = {
+ input_handle_motion,
+ input_handle_button,
+ input_handle_key,
+ input_handle_pointer_focus,
+ input_handle_keyboard_focus,
+};
+
+static void
+display_add_input(struct wayland_compositor *c, uint32_t id)
+{
+ struct wayland_input *input;
+
+ input = malloc(sizeof *input);
+ if (input == NULL)
+ return;
+
+ memset(input, 0, sizeof *input);
+
+ input->compositor = c;
+ input->input_device = wl_display_bind(c->parent.display,
+ id, &wl_input_device_interface);
+ wl_list_insert(c->input_list.prev, &input->link);
+
+ wl_input_device_add_listener(input->input_device,
+ &input_device_listener, input);
+ wl_input_device_set_user_data(input->input_device, input);
+}
+
+static void
+display_handle_global(struct wl_display *display, uint32_t id,
+ const char *interface, uint32_t version, void *data)
+{
+ struct wayland_compositor *c = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ c->parent.compositor =
+ wl_display_bind(display, id, &wl_compositor_interface);
+ } else if (strcmp(interface, "wl_output") == 0) {
+ c->parent.output =
+ wl_display_bind(display, id, &wl_output_interface);
+ wl_output_add_listener(c->parent.output, &output_listener, c);
+ } else if (strcmp(interface, "wl_input_device") == 0) {
+ display_add_input(c, id);
+ } else if (strcmp(interface, "wl_shell") == 0) {
+ c->parent.shell =
+ wl_display_bind(display, id, &wl_shell_interface);
+ }
+}
+
+static int
+update_event_mask(uint32_t mask, void *data)
+{
+ struct wayland_compositor *c = data;
+
+ c->parent.event_mask = mask;
+ if (c->parent.wl_source)
+ wl_event_source_fd_update(c->parent.wl_source, mask);
+
+ return 0;
+}
+
+static int
+wayland_compositor_handle_event(int fd, uint32_t mask, void *data)
+{
+ struct wayland_compositor *c = data;
+
+ if (mask & WL_EVENT_READABLE)
+ wl_display_iterate(c->parent.display, WL_DISPLAY_READABLE);
+ if (mask & WL_EVENT_WRITABLE)
+ wl_display_iterate(c->parent.display, WL_DISPLAY_WRITABLE);
+
+ return 1;
+}
+
+static void
+wayland_destroy(struct weston_compositor *ec)
+{
+ weston_compositor_shutdown(ec);
+
+ free(ec);
+}
+
+static struct weston_compositor *
+wayland_compositor_create(struct wl_display *display, int width, int height)
+{
+ struct wayland_compositor *c;
+ struct wl_event_loop *loop;
+ int fd;
+
+ c = malloc(sizeof *c);
+ if (c == NULL)
+ return NULL;
+
+ memset(c, 0, sizeof *c);
+
+ c->parent.display = wl_display_connect(NULL);
+
+ if (c->parent.display == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return NULL;
+ }
+
+ wl_list_init(&c->input_list);
+ wl_display_add_global_listener(c->parent.display,
+ display_handle_global, c);
+
+ wl_display_iterate(c->parent.display, WL_DISPLAY_READABLE);
+
+ c->base.wl_display = display;
+ if (wayland_compositor_init_egl(c) < 0)
+ return NULL;
+
+ c->base.destroy = wayland_destroy;
+
+ /* Can't init base class until we have a current egl context */
+ if (weston_compositor_init(&c->base, display) < 0)
+ return NULL;
+
+ if (wayland_compositor_create_output(c, width, height) < 0)
+ return NULL;
+
+ if (wayland_input_create(c) < 0)
+ return NULL;
+
+ loop = wl_display_get_event_loop(c->base.wl_display);
+
+ fd = wl_display_get_fd(c->parent.display, update_event_mask, c);
+ c->parent.wl_source =
+ wl_event_loop_add_fd(loop, fd, c->parent.event_mask,
+ wayland_compositor_handle_event, c);
+ if (c->parent.wl_source == NULL)
+ return NULL;
+
+ return &c->base;
+}
+
+struct weston_compositor *
+backend_init(struct wl_display *display, char *options);
+
+WL_EXPORT struct weston_compositor *
+backend_init(struct wl_display *display, char *options)
+{
+ int width = 1024, height = 640, i;
+ char *p, *value;
+
+ static char * const tokens[] = { "width", "height", NULL };
+
+ p = options;
+ while (i = getsubopt(&p, tokens, &value), i != -1) {
+ switch (i) {
+ case 0:
+ width = strtol(value, NULL, 0);
+ break;
+ case 1:
+ height = strtol(value, NULL, 0);
+ break;
+ }
+ }
+
+ return wayland_compositor_create(display, width, height);
+}
diff --git a/src/compositor-x11.c b/src/compositor-x11.c
new file mode 100644
index 0000000..ad5b587
--- /dev/null
+++ b/src/compositor-x11.c
@@ -0,0 +1,835 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2010-2011 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <linux/input.h>
+
+#include <xcb/xcb.h>
+#include <X11/Xlib.h>
+#include <X11/Xlib-xcb.h>
+
+#include <GLES2/gl2.h>
+#include <EGL/egl.h>
+
+#include "compositor.h"
+
+struct x11_compositor {
+ struct weston_compositor base;
+
+ Display *dpy;
+ xcb_connection_t *conn;
+ xcb_screen_t *screen;
+ xcb_cursor_t null_cursor;
+ struct wl_array keys;
+ struct wl_event_source *xcb_source;
+ struct {
+ xcb_atom_t wm_protocols;
+ xcb_atom_t wm_normal_hints;
+ xcb_atom_t wm_size_hints;
+ xcb_atom_t wm_delete_window;
+ xcb_atom_t wm_class;
+ xcb_atom_t net_wm_name;
+ xcb_atom_t net_wm_icon;
+ xcb_atom_t net_wm_state;
+ xcb_atom_t net_wm_state_fullscreen;
+ xcb_atom_t string;
+ xcb_atom_t utf8_string;
+ xcb_atom_t cardinal;
+ } atom;
+};
+
+struct x11_output {
+ struct weston_output base;
+
+ xcb_window_t window;
+ EGLSurface egl_surface;
+ struct weston_mode mode;
+ struct wl_event_source *finish_frame_timer;
+};
+
+struct x11_input {
+ struct weston_input_device base;
+};
+
+
+static int
+x11_input_create(struct x11_compositor *c)
+{
+ struct x11_input *input;
+
+ input = malloc(sizeof *input);
+ if (input == NULL)
+ return -1;
+
+ memset(input, 0, sizeof *input);
+ weston_input_device_init(&input->base, &c->base);
+
+ c->base.input_device = &input->base.input_device;
+
+ return 0;
+}
+
+static int
+x11_compositor_init_egl(struct x11_compositor *c)
+{
+ EGLint major, minor;
+ EGLint n;
+ const char *extensions;
+ EGLint config_attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_DEPTH_SIZE, 1,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+ static const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ c->base.display = eglGetDisplay(c->dpy);
+ if (c->base.display == NULL) {
+ fprintf(stderr, "failed to create display\n");
+ return -1;
+ }
+
+ if (!eglInitialize(c->base.display, &major, &minor)) {
+ fprintf(stderr, "failed to initialize display\n");
+ return -1;
+ }
+
+ extensions = eglQueryString(c->base.display, EGL_EXTENSIONS);
+ if (!strstr(extensions, "EGL_KHR_surfaceless_gles2")) {
+ fprintf(stderr, "EGL_KHR_surfaceless_gles2 not available\n");
+ return -1;
+ }
+
+ if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+ fprintf(stderr, "failed to bind EGL_OPENGL_ES_API\n");
+ return -1;
+ }
+ if (!eglChooseConfig(c->base.display, config_attribs,
+ &c->base.config, 1, &n) || n == 0) {
+ fprintf(stderr, "failed to choose config: %d\n", n);
+ return -1;
+ }
+
+ c->base.context = eglCreateContext(c->base.display, c->base.config,
+ EGL_NO_CONTEXT, context_attribs);
+ if (c->base.context == NULL) {
+ fprintf(stderr, "failed to create context\n");
+ return -1;
+ }
+
+ if (!eglMakeCurrent(c->base.display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, c->base.context)) {
+ fprintf(stderr, "failed to make context current\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+x11_output_prepare_render(struct weston_output *output_base)
+{
+ struct x11_output *output = (struct x11_output *) output_base;
+ struct weston_compositor *ec = output->base.compositor;
+
+ if (!eglMakeCurrent(ec->display, output->egl_surface,
+ output->egl_surface, ec->context)) {
+ fprintf(stderr, "failed to make current\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+finish_frame_handler(void *data)
+{
+ struct x11_output *output = data;
+ uint32_t msec;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+ weston_output_finish_frame(&output->base, msec);
+
+ return 1;
+}
+
+static int
+x11_output_present(struct weston_output *output_base)
+{
+ struct x11_output *output = (struct x11_output *) output_base;
+ struct weston_compositor *ec = output->base.compositor;
+
+ if (x11_output_prepare_render(&output->base))
+ return -1;
+
+ eglSwapBuffers(ec->display, output->egl_surface);
+
+ wl_event_source_timer_update(output->finish_frame_timer, 10);
+
+ return 0;
+}
+
+static int
+x11_output_prepare_scanout_surface(struct weston_output *output_base,
+ struct weston_surface *es)
+{
+ return -1;
+}
+
+static int
+x11_output_set_cursor(struct weston_output *output_base,
+ struct weston_input_device *input)
+{
+ return -1;
+}
+
+static void
+x11_output_destroy(struct weston_output *output_base)
+{
+ return;
+}
+
+static void
+x11_output_set_wm_protocols(struct x11_output *output)
+{
+ xcb_atom_t list[1];
+ struct x11_compositor *c =
+ (struct x11_compositor *) output->base.compositor;
+
+ list[0] = c->atom.wm_delete_window;
+ xcb_change_property (c->conn,
+ XCB_PROP_MODE_REPLACE,
+ output->window,
+ c->atom.wm_protocols,
+ XCB_ATOM_ATOM,
+ 32,
+ ARRAY_LENGTH(list),
+ list);
+}
+
+static void
+x11_output_change_state(struct x11_output *output, int add, xcb_atom_t state)
+{
+ xcb_client_message_event_t event;
+ struct x11_compositor *c =
+ (struct x11_compositor *) output->base.compositor;
+ xcb_screen_iterator_t iter;
+
+#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
+#define _NET_WM_STATE_ADD 1 /* add/set property */
+#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
+
+ memset(&event, 0, sizeof event);
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = output->window;
+ event.type = c->atom.net_wm_state;
+
+ event.data.data32[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
+ event.data.data32[1] = state;
+ event.data.data32[2] = 0;
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ iter = xcb_setup_roots_iterator(xcb_get_setup(c->conn));
+ xcb_send_event(c->conn, 0, iter.data->root,
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ (void *) &event);
+}
+
+
+struct wm_normal_hints {
+ uint32_t flags;
+ uint32_t pad[4];
+ int32_t min_width, min_height;
+ int32_t max_width, max_height;
+ int32_t width_inc, height_inc;
+ int32_t min_aspect_x, min_aspect_y;
+ int32_t max_aspect_x, max_aspect_y;
+ int32_t base_width, base_height;
+ int32_t win_gravity;
+};
+
+#define WM_NORMAL_HINTS_MIN_SIZE 16
+#define WM_NORMAL_HINTS_MAX_SIZE 32
+
+static void
+x11_output_set_icon(struct x11_compositor *c,
+ struct x11_output *output, const char *filename)
+{
+ uint32_t *icon, *pixels, stride;
+ int32_t width, height;
+
+ pixels = weston_load_image(filename, &width, &height, &stride);
+ if (!pixels)
+ return;
+ icon = malloc(width * height * 4 + 8);
+ if (!icon) {
+ free(pixels);
+ return;
+ }
+
+ icon[0] = width;
+ icon[1] = height;
+ memcpy(icon + 2, pixels, width * height * 4);
+ xcb_change_property(c->conn, XCB_PROP_MODE_REPLACE, output->window,
+ c->atom.net_wm_icon, c->atom.cardinal, 32,
+ width * height + 2, icon);
+ free(icon);
+ free(pixels);
+}
+
+static int
+x11_compositor_create_output(struct x11_compositor *c, int x, int y,
+ int width, int height, int fullscreen)
+{
+ static const char name[] = "Wayland Compositor";
+ static const char class[] = "wayland-1\0Wayland Compositor";
+ struct x11_output *output;
+ xcb_screen_iterator_t iter;
+ struct wm_normal_hints normal_hints;
+ struct wl_event_loop *loop;
+ uint32_t mask = XCB_CW_EVENT_MASK | XCB_CW_CURSOR;
+ uint32_t values[2] = {
+ XCB_EVENT_MASK_KEY_PRESS |
+ XCB_EVENT_MASK_KEY_RELEASE |
+ XCB_EVENT_MASK_BUTTON_PRESS |
+ XCB_EVENT_MASK_BUTTON_RELEASE |
+ XCB_EVENT_MASK_POINTER_MOTION |
+ XCB_EVENT_MASK_EXPOSURE |
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY |
+ XCB_EVENT_MASK_ENTER_WINDOW |
+ XCB_EVENT_MASK_LEAVE_WINDOW |
+ XCB_EVENT_MASK_KEYMAP_STATE |
+ XCB_EVENT_MASK_FOCUS_CHANGE,
+ 0
+ };
+
+ output = malloc(sizeof *output);
+ if (output == NULL)
+ return -1;
+
+ memset(output, 0, sizeof *output);
+
+ output->mode.flags =
+ WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+ output->mode.width = width;
+ output->mode.height = height;
+ output->mode.refresh = 60;
+ wl_list_init(&output->base.mode_list);
+ wl_list_insert(&output->base.mode_list, &output->mode.link);
+
+ output->base.current = &output->mode;
+ weston_output_init(&output->base, &c->base, x, y, width, height,
+ WL_OUTPUT_FLIPPED);
+
+ values[1] = c->null_cursor;
+ output->window = xcb_generate_id(c->conn);
+ iter = xcb_setup_roots_iterator(xcb_get_setup(c->conn));
+ xcb_create_window(c->conn,
+ XCB_COPY_FROM_PARENT,
+ output->window,
+ iter.data->root,
+ 0, 0,
+ width, height,
+ 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ iter.data->root_visual,
+ mask, values);
+
+ /* Don't resize me. */
+ memset(&normal_hints, 0, sizeof normal_hints);
+ normal_hints.flags =
+ WM_NORMAL_HINTS_MAX_SIZE | WM_NORMAL_HINTS_MIN_SIZE;
+ normal_hints.min_width = width;
+ normal_hints.min_height = height;
+ normal_hints.max_width = width;
+ normal_hints.max_height = height;
+ xcb_change_property (c->conn, XCB_PROP_MODE_REPLACE, output->window,
+ c->atom.wm_normal_hints,
+ c->atom.wm_size_hints, 32,
+ sizeof normal_hints / 4,
+ (uint8_t *) &normal_hints);
+
+ /* Set window name. Don't bother with non-EWMH WMs. */
+ xcb_change_property(c->conn, XCB_PROP_MODE_REPLACE, output->window,
+ c->atom.net_wm_name, c->atom.utf8_string, 8,
+ strlen(name), name);
+ xcb_change_property(c->conn, XCB_PROP_MODE_REPLACE, output->window,
+ c->atom.wm_class, c->atom.string, 8,
+ sizeof class, class);
+
+ x11_output_set_icon(c, output, DATADIR "/wayland/wayland.png");
+
+ xcb_map_window(c->conn, output->window);
+
+ x11_output_set_wm_protocols(output);
+
+ if (fullscreen)
+ x11_output_change_state(output, 1,
+ c->atom.net_wm_state_fullscreen);
+
+ output->egl_surface =
+ eglCreateWindowSurface(c->base.display, c->base.config,
+ output->window, NULL);
+ if (!output->egl_surface) {
+ fprintf(stderr, "failed to create window surface\n");
+ return -1;
+ }
+ if (!eglMakeCurrent(c->base.display, output->egl_surface,
+ output->egl_surface, c->base.context)) {
+ fprintf(stderr, "failed to make surface current\n");
+ return -1;
+ }
+
+ loop = wl_display_get_event_loop(c->base.wl_display);
+ output->finish_frame_timer =
+ wl_event_loop_add_timer(loop, finish_frame_handler, output);
+
+ output->base.prepare_render = x11_output_prepare_render;
+ output->base.present = x11_output_present;
+ output->base.prepare_scanout_surface =
+ x11_output_prepare_scanout_surface;
+ output->base.set_hardware_cursor = x11_output_set_cursor;
+ output->base.destroy = x11_output_destroy;
+
+ wl_list_insert(c->base.output_list.prev, &output->base.link);
+
+ return 0;
+}
+
+static struct x11_output *
+x11_compositor_find_output(struct x11_compositor *c, xcb_window_t window)
+{
+ struct x11_output *output;
+
+ wl_list_for_each(output, &c->base.output_list, base.link) {
+ if (output->window == window)
+ return output;
+ }
+
+ return NULL;
+}
+
+static void
+x11_compositor_deliver_button_event(struct x11_compositor *c,
+ xcb_generic_event_t *event, int state)
+{
+ xcb_button_press_event_t *button_event =
+ (xcb_button_press_event_t *) event;
+ int button;
+
+ switch (button_event->detail) {
+ default:
+ button = button_event->detail + BTN_LEFT - 1;
+ break;
+ case 2:
+ button = BTN_MIDDLE;
+ break;
+ case 3:
+ button = BTN_RIGHT;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ /* X11 sends wheel events as buttons events. But
+ * linux input treats as REL_WHEEL, therefore not
+ * button type at all. When we update the input
+ * protocol and get the 'axis' event, we'll send
+ * scroll events as axis events. */
+ return;
+ }
+
+ notify_button(c->base.input_device,
+ weston_compositor_get_time(), button, state);
+}
+
+static int
+x11_compositor_next_event(struct x11_compositor *c,
+ xcb_generic_event_t **event, uint32_t mask)
+{
+ if (mask & WL_EVENT_READABLE) {
+ *event = xcb_poll_for_event(c->conn);
+ } else {
+#ifdef HAVE_XCB_POLL_FOR_QUEUED_EVENT
+ *event = xcb_poll_for_queued_event(c->conn);
+#else
+ *event = xcb_poll_for_event(c->conn);
+#endif
+ }
+
+ return *event != NULL;
+}
+
+static int
+x11_compositor_handle_event(int fd, uint32_t mask, void *data)
+{
+ struct x11_compositor *c = data;
+ struct x11_output *output;
+ xcb_generic_event_t *event, *prev;
+ xcb_client_message_event_t *client_message;
+ xcb_motion_notify_event_t *motion_notify;
+ xcb_enter_notify_event_t *enter_notify;
+ xcb_key_press_event_t *key_press, *key_release;
+ xcb_keymap_notify_event_t *keymap_notify;
+ xcb_focus_in_event_t *focus_in;
+ xcb_atom_t atom;
+ uint32_t *k;
+ int i, set;
+
+ prev = NULL;
+ while (x11_compositor_next_event(c, &event, mask)) {
+ switch (prev ? prev->response_type & ~0x80 : 0x80) {
+ case XCB_KEY_RELEASE:
+ key_release = (xcb_key_press_event_t *) prev;
+ key_press = (xcb_key_press_event_t *) event;
+ if ((event->response_type & ~0x80) == XCB_KEY_PRESS &&
+ key_release->time == key_press->time &&
+ key_release->detail == key_press->detail) {
+ /* Don't deliver the held key release
+ * event or the new key press event. */
+ free(event);
+ free(prev);
+ prev = NULL;
+ continue;
+ } else {
+ /* Deliver the held key release now
+ * and fall through and handle the new
+ * event below. */
+ notify_key(c->base.input_device,
+ weston_compositor_get_time(),
+ key_release->detail - 8, 0);
+ free(prev);
+ prev = NULL;
+ break;
+ }
+
+ case XCB_FOCUS_IN:
+ /* assert event is keymap_notify */
+ focus_in = (xcb_focus_in_event_t *) prev;
+ keymap_notify = (xcb_keymap_notify_event_t *) event;
+ c->keys.size = 0;
+ for (i = 0; i < ARRAY_LENGTH(keymap_notify->keys) * 8; i++) {
+ set = keymap_notify->keys[i >> 3] &
+ (1 << (i & 7));
+ if (set) {
+ k = wl_array_add(&c->keys, sizeof *k);
+ *k = i;
+ }
+ }
+
+ output = x11_compositor_find_output(c, focus_in->event);
+ notify_keyboard_focus(c->base.input_device,
+ weston_compositor_get_time(),
+ &output->base, &c->keys);
+
+ free(prev);
+ prev = NULL;
+ break;
+
+ default:
+ /* No previous event held */
+ break;
+ }
+
+ switch (event->response_type & ~0x80) {
+ case XCB_KEY_PRESS:
+ key_press = (xcb_key_press_event_t *) event;
+ notify_key(c->base.input_device,
+ weston_compositor_get_time(),
+ key_press->detail - 8, 1);
+ break;
+ case XCB_KEY_RELEASE:
+ prev = event;
+ break;
+ case XCB_BUTTON_PRESS:
+ x11_compositor_deliver_button_event(c, event, 1);
+ break;
+ case XCB_BUTTON_RELEASE:
+ x11_compositor_deliver_button_event(c, event, 0);
+ break;
+ case XCB_MOTION_NOTIFY:
+ motion_notify = (xcb_motion_notify_event_t *) event;
+ output = x11_compositor_find_output(c, motion_notify->event);
+ notify_motion(c->base.input_device,
+ weston_compositor_get_time(),
+ output->base.x + motion_notify->event_x,
+ output->base.y + motion_notify->event_y);
+ break;
+
+ case XCB_EXPOSE:
+ /* FIXME: schedule output repaint */
+ /* output = x11_compositor_find_output(c, expose->window); */
+
+ weston_compositor_schedule_repaint(&c->base);
+ break;
+
+ case XCB_ENTER_NOTIFY:
+ enter_notify = (xcb_enter_notify_event_t *) event;
+ if (enter_notify->state >= Button1Mask)
+ break;
+ output = x11_compositor_find_output(c, enter_notify->event);
+ notify_pointer_focus(c->base.input_device,
+ weston_compositor_get_time(),
+ &output->base,
+ output->base.x + enter_notify->event_x,
+ output->base.y + enter_notify->event_y);
+ break;
+
+ case XCB_LEAVE_NOTIFY:
+ enter_notify = (xcb_enter_notify_event_t *) event;
+ if (enter_notify->state >= Button1Mask)
+ break;
+ output = x11_compositor_find_output(c, enter_notify->event);
+ notify_pointer_focus(c->base.input_device,
+ weston_compositor_get_time(),
+ NULL,
+ output->base.x + enter_notify->event_x,
+ output->base.y + enter_notify->event_y);
+ break;
+
+ case XCB_CLIENT_MESSAGE:
+ client_message = (xcb_client_message_event_t *) event;
+ atom = client_message->data.data32[0];
+ if (atom == c->atom.wm_delete_window)
+ wl_display_terminate(c->base.wl_display);
+ break;
+
+ case XCB_FOCUS_IN:
+ focus_in = (xcb_focus_in_event_t *) event;
+ if (focus_in->mode == XCB_NOTIFY_MODE_WHILE_GRABBED)
+ break;
+
+ prev = event;
+ break;
+
+ case XCB_FOCUS_OUT:
+ focus_in = (xcb_focus_in_event_t *) event;
+ if (focus_in->mode == XCB_NOTIFY_MODE_WHILE_GRABBED ||
+ focus_in->mode == XCB_NOTIFY_MODE_UNGRAB)
+ break;
+ notify_keyboard_focus(c->base.input_device,
+ weston_compositor_get_time(),
+ NULL, NULL);
+ break;
+
+ default:
+ break;
+ }
+
+ if (prev != event)
+ free (event);
+ }
+
+ switch (prev ? prev->response_type & ~0x80 : 0x80) {
+ case XCB_KEY_RELEASE:
+ key_release = (xcb_key_press_event_t *) prev;
+ notify_key(c->base.input_device,
+ weston_compositor_get_time(),
+ key_release->detail - 8, 0);
+ free(prev);
+ prev = NULL;
+ break;
+ default:
+ break;
+ }
+
+ return event != NULL;
+}
+
+#define F(field) offsetof(struct x11_compositor, field)
+
+static void
+x11_compositor_get_resources(struct x11_compositor *c)
+{
+ static const struct { const char *name; int offset; } atoms[] = {
+ { "WM_PROTOCOLS", F(atom.wm_protocols) },
+ { "WM_NORMAL_HINTS", F(atom.wm_normal_hints) },
+ { "WM_SIZE_HINTS", F(atom.wm_size_hints) },
+ { "WM_DELETE_WINDOW", F(atom.wm_delete_window) },
+ { "WM_CLASS", F(atom.wm_class) },
+ { "_NET_WM_NAME", F(atom.net_wm_name) },
+ { "_NET_WM_ICON", F(atom.net_wm_icon) },
+ { "_NET_WM_STATE", F(atom.net_wm_state) },
+ { "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) },
+ { "STRING", F(atom.string) },
+ { "UTF8_STRING", F(atom.utf8_string) },
+ { "CARDINAL", F(atom.cardinal) },
+ };
+
+ xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
+ xcb_intern_atom_reply_t *reply;
+ xcb_pixmap_t pixmap;
+ xcb_gc_t gc;
+ int i;
+ uint8_t data[] = { 0, 0, 0, 0 };
+
+ for (i = 0; i < ARRAY_LENGTH(atoms); i++)
+ cookies[i] = xcb_intern_atom (c->conn, 0,
+ strlen(atoms[i].name),
+ atoms[i].name);
+
+ for (i = 0; i < ARRAY_LENGTH(atoms); i++) {
+ reply = xcb_intern_atom_reply (c->conn, cookies[i], NULL);
+ *(xcb_atom_t *) ((char *) c + atoms[i].offset) = reply->atom;
+ free(reply);
+ }
+
+ pixmap = xcb_generate_id(c->conn);
+ gc = xcb_generate_id(c->conn);
+ xcb_create_pixmap(c->conn, 1, pixmap, c->screen->root, 1, 1);
+ xcb_create_gc(c->conn, gc, pixmap, 0, NULL);
+ xcb_put_image(c->conn, XCB_IMAGE_FORMAT_XY_PIXMAP,
+ pixmap, gc, 1, 1, 0, 0, 0, 32, sizeof data, data);
+ c->null_cursor = xcb_generate_id(c->conn);
+ xcb_create_cursor (c->conn, c->null_cursor,
+ pixmap, pixmap, 0, 0, 0, 0, 0, 0, 1, 1);
+ xcb_free_gc(c->conn, gc);
+ xcb_free_pixmap(c->conn, pixmap);
+}
+
+static void
+x11_destroy(struct weston_compositor *ec)
+{
+ weston_compositor_shutdown(ec);
+
+ free(ec);
+}
+
+static struct weston_compositor *
+x11_compositor_create(struct wl_display *display,
+ int width, int height, int count, int fullscreen)
+{
+ struct x11_compositor *c;
+ struct wl_event_loop *loop;
+ xcb_screen_iterator_t s;
+ int i, x;
+
+ c = malloc(sizeof *c);
+ if (c == NULL)
+ return NULL;
+
+ memset(c, 0, sizeof *c);
+
+ c->dpy = XOpenDisplay(NULL);
+ if (c->dpy == NULL)
+ return NULL;
+
+ c->conn = XGetXCBConnection(c->dpy);
+ XSetEventQueueOwner(c->dpy, XCBOwnsEventQueue);
+
+ if (xcb_connection_has_error(c->conn))
+ return NULL;
+
+ s = xcb_setup_roots_iterator(xcb_get_setup(c->conn));
+ c->screen = s.data;
+ wl_array_init(&c->keys);
+
+ x11_compositor_get_resources(c);
+
+ c->base.wl_display = display;
+ if (x11_compositor_init_egl(c) < 0)
+ return NULL;
+
+ c->base.destroy = x11_destroy;
+
+ /* Can't init base class until we have a current egl context */
+ if (weston_compositor_init(&c->base, display) < 0)
+ return NULL;
+
+ for (i = 0, x = 0; i < count; i++) {
+ if (x11_compositor_create_output(c, x, 0, width, height,
+ fullscreen) < 0)
+ return NULL;
+ x += width;
+ }
+
+ if (x11_input_create(c) < 0)
+ return NULL;
+
+ loop = wl_display_get_event_loop(c->base.wl_display);
+
+ c->xcb_source =
+ wl_event_loop_add_fd(loop, xcb_get_file_descriptor(c->conn),
+ WL_EVENT_READABLE,
+ x11_compositor_handle_event, c);
+ wl_event_source_check(c->xcb_source);
+
+ return &c->base;
+}
+
+struct weston_compositor *
+backend_init(struct wl_display *display, char *options);
+
+WL_EXPORT struct weston_compositor *
+backend_init(struct wl_display *display, char *options)
+{
+ int width = 1024, height = 640, fullscreen = 0, count = 1, i;
+ char *p, *value;
+
+ static char * const tokens[] = {
+ "width", "height", "fullscreen", "output-count", NULL
+ };
+
+ p = options;
+ while (i = getsubopt(&p, tokens, &value), i != -1) {
+ switch (i) {
+ case 0:
+ width = strtol(value, NULL, 0);
+ break;
+ case 1:
+ height = strtol(value, NULL, 0);
+ break;
+ case 2:
+ fullscreen = 1;
+ break;
+ case 3:
+ count = strtol(value, NULL, 0);
+ break;
+ }
+ }
+
+ return x11_compositor_create(display,
+ width, height, count, fullscreen);
+}
diff --git a/src/compositor.c b/src/compositor.c
new file mode 100644
index 0000000..b1353f4
--- /dev/null
+++ b/src/compositor.c
@@ -0,0 +1,2185 @@
+/*
+ * Copyright © 2010-2011 Intel Corporation
+ * Copyright © 2008-2011 Kristian Høgsberg
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <math.h>
+#include <linux/input.h>
+#include <dlfcn.h>
+#include <getopt.h>
+#include <signal.h>
+
+#include <wayland-server.h>
+#include "compositor.h"
+
+static const char *option_socket_name = NULL;
+
+static struct wl_list child_process_list;
+
+static int
+sigchld_handler(int signal_number, void *data)
+{
+ struct weston_process *p;
+ int status;
+ pid_t pid;
+
+ pid = wait(&status);
+ wl_list_for_each(p, &child_process_list, link) {
+ if (p->pid == pid)
+ break;
+ }
+
+ if (&p->link == &child_process_list) {
+ fprintf(stderr, "unknown child process exited\n");
+ return 1;
+ }
+
+ wl_list_remove(&p->link);
+ p->cleanup(p, status);
+
+ return 1;
+}
+
+WL_EXPORT void
+weston_watch_process(struct weston_process *process)
+{
+ wl_list_insert(&child_process_list, &process->link);
+}
+
+static void
+child_client_exec(int sockfd, const char *path)
+{
+ int clientfd;
+ char s[32];
+ sigset_t allsigs;
+
+ /* do not give our signal mask to the new process */
+ sigfillset(&allsigs);
+ sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
+
+ /* SOCK_CLOEXEC closes both ends, so we dup the fd to get a
+ * non-CLOEXEC fd to pass through exec. */
+ clientfd = dup(sockfd);
+ if (clientfd == -1) {
+ fprintf(stderr, "compositor: dup failed: %m\n");
+ return;
+ }
+
+ snprintf(s, sizeof s, "%d", clientfd);
+ setenv("WAYLAND_SOCKET", s, 1);
+
+ if (execl(path, path, NULL) < 0)
+ fprintf(stderr, "compositor: executing '%s' failed: %m\n",
+ path);
+}
+
+WL_EXPORT struct wl_client *
+weston_client_launch(struct weston_compositor *compositor,
+ struct weston_process *proc,
+ const char *path,
+ weston_process_cleanup_func_t cleanup)
+{
+ int sv[2];
+ pid_t pid;
+ struct wl_client *client;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
+ fprintf(stderr, "weston_client_launch: "
+ "socketpair failed while launching '%s': %m\n",
+ path);
+ return NULL;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ close(sv[0]);
+ close(sv[1]);
+ fprintf(stderr, "weston_client_launch: "
+ "fork failed while launching '%s': %m\n", path);
+ return NULL;
+ }
+
+ if (pid == 0) {
+ child_client_exec(sv[1], path);
+ exit(-1);
+ }
+
+ close(sv[1]);
+
+ client = wl_client_create(compositor->wl_display, sv[0]);
+ if (!client) {
+ close(sv[0]);
+ fprintf(stderr, "weston_client_launch: "
+ "wl_client_create failed while launching '%s'.\n",
+ path);
+ return NULL;
+ }
+
+ proc->pid = pid;
+ proc->cleanup = cleanup;
+ weston_watch_process(proc);
+
+ return client;
+}
+
+static void
+surface_handle_buffer_destroy(struct wl_listener *listener,
+ struct wl_resource *resource, uint32_t time)
+{
+ struct weston_surface *es =
+ container_of(listener, struct weston_surface,
+ buffer_destroy_listener);
+
+ es->buffer = NULL;
+}
+
+static void
+output_handle_scanout_buffer_destroy(struct wl_listener *listener,
+ struct wl_resource *resource,
+ uint32_t time)
+{
+ struct weston_output *output =
+ container_of(listener, struct weston_output,
+ scanout_buffer_destroy_listener);
+
+ output->scanout_buffer = NULL;
+
+ if (!output->pending_scanout_buffer)
+ weston_compositor_schedule_repaint(output->compositor);
+}
+
+static void
+output_handle_pending_scanout_buffer_destroy(struct wl_listener *listener,
+ struct wl_resource *resource,
+ uint32_t time)
+{
+ struct weston_output *output =
+ container_of(listener, struct weston_output,
+ pending_scanout_buffer_destroy_listener);
+
+ output->pending_scanout_buffer = NULL;
+
+ weston_compositor_schedule_repaint(output->compositor);
+}
+
+
+WL_EXPORT struct weston_surface *
+weston_surface_create(struct weston_compositor *compositor,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct weston_surface *surface;
+
+ surface = calloc(1, sizeof *surface);
+ if (surface == NULL)
+ return NULL;
+
+ wl_list_init(&surface->link);
+ wl_list_init(&surface->buffer_link);
+
+ glGenTextures(1, &surface->texture);
+ glBindTexture(GL_TEXTURE_2D, surface->texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ surface->surface.resource.client = NULL;
+
+ surface->compositor = compositor;
+ surface->visual = WESTON_NONE_VISUAL;
+ surface->image = EGL_NO_IMAGE_KHR;
+ surface->saved_texture = 0;
+ surface->x = x;
+ surface->y = y;
+ surface->width = width;
+ surface->height = height;
+ surface->alpha = 255;
+
+ surface->fullscreen_output = NULL;
+ surface->buffer = NULL;
+ surface->output = NULL;
+
+ pixman_region32_init(&surface->damage);
+ pixman_region32_init(&surface->opaque);
+ wl_list_init(&surface->frame_callback_list);
+
+ surface->buffer_destroy_listener.func = surface_handle_buffer_destroy;
+
+ surface->transform = NULL;
+
+ return surface;
+}
+
+WL_EXPORT void
+weston_surface_damage_rectangle(struct weston_surface *surface,
+ int32_t x, int32_t y,
+ int32_t width, int32_t height)
+{
+ struct weston_compositor *compositor = surface->compositor;
+
+ pixman_region32_union_rect(&surface->damage,
+ &surface->damage,
+ surface->x + x, surface->y + y,
+ width, height);
+ weston_compositor_schedule_repaint(compositor);
+}
+
+WL_EXPORT void
+weston_surface_damage(struct weston_surface *surface)
+{
+ weston_surface_damage_rectangle(surface, 0, 0,
+ surface->width, surface->height);
+}
+
+WL_EXPORT void
+weston_surface_damage_below(struct weston_surface *surface)
+{
+ struct weston_surface *below;
+
+ if (surface->output == NULL)
+ return;
+
+ if (surface->link.next == &surface->compositor->surface_list)
+ return;
+
+ below = container_of(surface->link.next, struct weston_surface, link);
+
+ pixman_region32_union_rect(&below->damage,
+ &below->damage,
+ surface->x, surface->y,
+ surface->width, surface->height);
+ weston_compositor_schedule_repaint(surface->compositor);
+}
+
+WL_EXPORT void
+weston_surface_configure(struct weston_surface *surface,
+ int x, int y, int width, int height)
+{
+ weston_surface_damage_below(surface);
+
+ surface->x = x;
+ surface->y = y;
+ surface->width = width;
+ surface->height = height;
+
+ weston_surface_assign_output(surface);
+ weston_surface_damage(surface);
+
+ pixman_region32_fini(&surface->opaque);
+ if (surface->visual == WESTON_RGB_VISUAL)
+ pixman_region32_init_rect(&surface->opaque,
+ surface->x, surface->y,
+ surface->width, surface->height);
+ else
+ pixman_region32_init(&surface->opaque);
+}
+
+WL_EXPORT uint32_t
+weston_compositor_get_time(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+WL_EXPORT void
+weston_compositor_repick(struct weston_compositor *compositor)
+{
+ struct weston_input_device *device;
+ struct weston_surface *surface;
+ int32_t sx, sy;
+ uint32_t time;
+
+ if (!compositor->focus)
+ return;
+
+ time = weston_compositor_get_time();
+ wl_list_for_each(device, &compositor->input_device_list, link) {
+ if (device->input_device.grab)
+ continue;
+ surface = pick_surface(&device->input_device, &sx, &sy);
+ wl_input_device_set_pointer_focus(&device->input_device,
+ &surface->surface,
+ time,
+ device->input_device.x,
+ device->input_device.y,
+ sx, sy);
+ }
+}
+
+static void
+destroy_surface(struct wl_resource *resource)
+{
+ struct weston_surface *surface =
+ container_of(resource,
+ struct weston_surface, surface.resource);
+ struct weston_compositor *compositor = surface->compositor;
+
+ weston_surface_damage_below(surface);
+
+ wl_list_remove(&surface->link);
+ weston_compositor_repick(compositor);
+
+ if (surface->saved_texture == 0)
+ glDeleteTextures(1, &surface->texture);
+ else
+ glDeleteTextures(1, &surface->saved_texture);
+
+ if (surface->buffer)
+ wl_list_remove(&surface->buffer_destroy_listener.link);
+
+ if (surface->image != EGL_NO_IMAGE_KHR)
+ compositor->destroy_image(compositor->display,
+ surface->image);
+
+ wl_list_remove(&surface->buffer_link);
+
+ free(surface);
+}
+
+static void
+weston_buffer_attach(struct wl_buffer *buffer, struct wl_surface *surface)
+{
+ struct weston_surface *es = (struct weston_surface *) surface;
+ struct weston_compositor *ec = es->compositor;
+ struct wl_list *surfaces_attached_to;
+
+ if (es->saved_texture != 0)
+ es->texture = es->saved_texture;
+
+ glBindTexture(GL_TEXTURE_2D, es->texture);
+
+ if (wl_buffer_is_shm(buffer)) {
+ /* Unbind any EGLImage texture that may be bound, so we don't
+ * overwrite it.*/
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT,
+ 0, 0, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, NULL);
+ es->pitch = wl_shm_buffer_get_stride(buffer) / 4;
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT,
+ es->pitch, buffer->height, 0,
+ GL_BGRA_EXT, GL_UNSIGNED_BYTE,
+ wl_shm_buffer_get_data(buffer));
+
+ switch (wl_shm_buffer_get_format(buffer)) {
+ case WL_SHM_FORMAT_ARGB32:
+ es->visual = WESTON_ARGB_VISUAL;
+ break;
+ case WL_SHM_FORMAT_PREMULTIPLIED_ARGB32:
+ es->visual = WESTON_PREMUL_ARGB_VISUAL;
+ break;
+ case WL_SHM_FORMAT_XRGB32:
+ es->visual = WESTON_RGB_VISUAL;
+ break;
+ }
+
+ surfaces_attached_to = buffer->user_data;
+
+ wl_list_remove(&es->buffer_link);
+ wl_list_insert(surfaces_attached_to, &es->buffer_link);
+ } else {
+ if (es->image != EGL_NO_IMAGE_KHR)
+ ec->destroy_image(ec->display, es->image);
+ es->image = ec->create_image(ec->display, NULL,
+ EGL_WAYLAND_BUFFER_WL,
+ buffer, NULL);
+
+ ec->image_target_texture_2d(GL_TEXTURE_2D, es->image);
+
+ /* FIXME: we need to get the visual from the wl_buffer */
+ es->visual = WESTON_PREMUL_ARGB_VISUAL;
+ es->pitch = es->width;
+ }
+}
+
+static int
+texture_region(struct weston_surface *es, pixman_region32_t *region)
+{
+ struct weston_compositor *ec = es->compositor;
+ GLfloat *v, inv_width, inv_height;
+ pixman_box32_t *rectangles;
+ unsigned int *p;
+ int i, n;
+
+ rectangles = pixman_region32_rectangles(region, &n);
+ v = wl_array_add(&ec->vertices, n * 16 * sizeof *v);
+ p = wl_array_add(&ec->indices, n * 6 * sizeof *p);
+ inv_width = 1.0 / es->pitch;
+ inv_height = 1.0 / es->height;
+
+ for (i = 0; i < n; i++, v += 16, p += 6) {
+ v[ 0] = rectangles[i].x1;
+ v[ 1] = rectangles[i].y1;
+ v[ 2] = (GLfloat) (rectangles[i].x1 - es->x) * inv_width;
+ v[ 3] = (GLfloat) (rectangles[i].y1 - es->y) * inv_height;
+
+ v[ 4] = rectangles[i].x1;
+ v[ 5] = rectangles[i].y2;
+ v[ 6] = v[ 2];
+ v[ 7] = (GLfloat) (rectangles[i].y2 - es->y) * inv_height;
+
+ v[ 8] = rectangles[i].x2;
+ v[ 9] = rectangles[i].y1;
+ v[10] = (GLfloat) (rectangles[i].x2 - es->x) * inv_width;
+ v[11] = v[ 3];
+
+ v[12] = rectangles[i].x2;
+ v[13] = rectangles[i].y2;
+ v[14] = v[10];
+ v[15] = v[ 7];
+
+ p[0] = i * 4 + 0;
+ p[1] = i * 4 + 1;
+ p[2] = i * 4 + 2;
+ p[3] = i * 4 + 2;
+ p[4] = i * 4 + 1;
+ p[5] = i * 4 + 3;
+ }
+
+ return n;
+}
+
+static void
+transform_vertex(struct weston_surface *surface,
+ GLfloat x, GLfloat y, GLfloat u, GLfloat v, GLfloat *r)
+{
+ struct weston_vector t;
+
+ t.f[0] = x;
+ t.f[1] = y;
+ t.f[2] = 0.0;
+ t.f[3] = 1.0;
+
+ weston_matrix_transform(&surface->transform->matrix, &t);
+
+ r[ 0] = t.f[0];
+ r[ 1] = t.f[1];
+ r[ 2] = u;
+ r[ 3] = v;
+}
+
+static int
+texture_transformed_surface(struct weston_surface *es)
+{
+ struct weston_compositor *ec = es->compositor;
+ GLfloat *v;
+ unsigned int *p;
+
+ v = wl_array_add(&ec->vertices, 16 * sizeof *v);
+ p = wl_array_add(&ec->indices, 6 * sizeof *p);
+
+ transform_vertex(es, es->x, es->y, 0.0, 0.0, &v[0]);
+ transform_vertex(es, es->x, es->y + es->height, 0.0, 1.0, &v[4]);
+ transform_vertex(es, es->x + es->width, es->y, 1.0, 0.0, &v[8]);
+ transform_vertex(es, es->x + es->width, es->y + es->height,
+ 1.0, 1.0, &v[12]);
+
+ p[0] = 0;
+ p[1] = 1;
+ p[2] = 2;
+ p[3] = 2;
+ p[4] = 1;
+ p[5] = 3;
+
+ return 1;
+}
+
+static void
+weston_surface_draw(struct weston_surface *es,
+ struct weston_output *output, pixman_region32_t *clip)
+{
+ struct weston_compositor *ec = es->compositor;
+ GLfloat *v;
+ pixman_region32_t repaint;
+ GLint filter;
+ int n;
+
+ pixman_region32_init_rect(&repaint,
+ es->x, es->y, es->width, es->height);
+ pixman_region32_intersect(&repaint, &repaint, clip);
+ if (!pixman_region32_not_empty(&repaint))
+ return;
+
+ switch (es->visual) {
+ case WESTON_ARGB_VISUAL:
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ break;
+ case WESTON_PREMUL_ARGB_VISUAL:
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ break;
+ case WESTON_RGB_VISUAL:
+ glDisable(GL_BLEND);
+ break;
+ default:
+ fprintf(stderr, "bogus visual\n");
+ break;
+ }
+
+ if (es->alpha != ec->current_alpha) {
+ glUniform1f(ec->texture_shader.alpha_uniform,
+ es->alpha / 255.0);
+ ec->current_alpha = es->alpha;
+ }
+
+ if (es->transform == NULL) {
+ filter = GL_NEAREST;
+ n = texture_region(es, &repaint);
+ } else {
+ filter = GL_LINEAR;
+ n = texture_transformed_surface(es);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, es->texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+
+ v = ec->vertices.data;
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[0]);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[2]);
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+ glDrawElements(GL_TRIANGLES, n * 6, GL_UNSIGNED_INT, ec->indices.data);
+
+ ec->vertices.size = 0;
+ ec->indices.size = 0;
+ pixman_region32_fini(&repaint);
+}
+
+static void
+weston_surface_raise(struct weston_surface *surface)
+{
+ struct weston_compositor *compositor = surface->compositor;
+
+ wl_list_remove(&surface->link);
+ wl_list_insert(&compositor->surface_list, &surface->link);
+ weston_compositor_repick(compositor);
+ weston_surface_damage(surface);
+}
+
+WL_EXPORT void
+weston_compositor_damage_all(struct weston_compositor *compositor)
+{
+ struct weston_output *output;
+
+ wl_list_for_each(output, &compositor->output_list, link)
+ weston_output_damage(output);
+}
+
+static inline void
+weston_buffer_post_release(struct wl_buffer *buffer)
+{
+ if (--buffer->busy_count > 0)
+ return;
+
+ assert(buffer->resource.client != NULL);
+ wl_resource_queue_event(&buffer->resource, WL_BUFFER_RELEASE);
+}
+
+WL_EXPORT void
+weston_output_damage(struct weston_output *output)
+{
+ struct weston_compositor *compositor = output->compositor;
+ struct weston_surface *es;
+
+ if (wl_list_empty(&compositor->surface_list))
+ return;
+
+ es = container_of(compositor->surface_list.next,
+ struct weston_surface, link);
+ pixman_region32_union(&es->damage, &es->damage, &output->region);
+ weston_compositor_schedule_repaint(compositor);
+}
+
+static void
+fade_frame(struct weston_animation *animation,
+ struct weston_output *output, uint32_t msecs)
+{
+ struct weston_compositor *compositor =
+ container_of(animation,
+ struct weston_compositor, fade.animation);
+
+ weston_spring_update(&compositor->fade.spring, msecs);
+ if (weston_spring_done(&compositor->fade.spring)) {
+ compositor->fade.spring.current =
+ compositor->fade.spring.target;
+ wl_list_remove(&animation->link);
+ wl_list_init(&animation->link);
+
+ if (compositor->fade.spring.current > 0.999) {
+ compositor->state = WESTON_COMPOSITOR_SLEEPING;
+ compositor->shell->lock(compositor->shell);
+ }
+ }
+
+ weston_output_damage(output);
+}
+
+static void
+fade_output(struct weston_output *output,
+ GLfloat tint, pixman_region32_t *region)
+{
+ struct weston_compositor *compositor = output->compositor;
+ struct weston_surface surface;
+ GLfloat color[4] = { 0.0, 0.0, 0.0, tint };
+
+ surface.compositor = compositor;
+ surface.x = output->x;
+ surface.y = output->y;
+ surface.pitch = output->current->width;
+ surface.width = output->current->width;
+ surface.height = output->current->height;
+ surface.texture = GL_NONE;
+ surface.transform = NULL;
+ surface.alpha = compositor->current_alpha;
+
+ if (tint <= 1.0)
+ surface.visual = WESTON_PREMUL_ARGB_VISUAL;
+ else
+ surface.visual = WESTON_RGB_VISUAL;
+
+ glUseProgram(compositor->solid_shader.program);
+ glUniformMatrix4fv(compositor->solid_shader.proj_uniform,
+ 1, GL_FALSE, output->matrix.d);
+ glUniform4fv(compositor->solid_shader.color_uniform, 1, color);
+ weston_surface_draw(&surface, output, region);
+}
+
+static void
+weston_output_set_cursor(struct weston_output *output,
+ struct wl_input_device *dev, int force_sw)
+{
+ struct weston_compositor *ec = output->compositor;
+ struct weston_input_device *device =
+ (struct weston_input_device *) dev;
+ pixman_region32_t cursor_region;
+ int use_hardware_cursor = 1, prior_was_hardware;
+
+ if (device->sprite == NULL)
+ return;
+
+ pixman_region32_init_rect(&cursor_region,
+ device->sprite->x, device->sprite->y,
+ device->sprite->width,
+ device->sprite->height);
+
+ pixman_region32_intersect(&cursor_region, &cursor_region, &output->region);
+
+ if (!pixman_region32_not_empty(&cursor_region)) {
+ output->set_hardware_cursor(output, NULL);
+ goto out;
+ }
+
+ prior_was_hardware = wl_list_empty(&device->sprite->link);
+ if (force_sw || output->set_hardware_cursor(output, device) < 0) {
+ if (prior_was_hardware) {
+ weston_surface_damage(device->sprite);
+ output->set_hardware_cursor(output, NULL);
+ }
+ use_hardware_cursor = 0;
+ } else if (!prior_was_hardware) {
+ weston_surface_damage_below(device->sprite);
+ }
+
+ /* Remove always to be on top. */
+ wl_list_remove(&device->sprite->link);
+ if (!use_hardware_cursor && ec->focus) {
+ wl_list_insert(&ec->surface_list, &device->sprite->link);
+ device->sprite->output = output;
+ } else {
+ wl_list_init(&device->sprite->link);
+ device->sprite->output = NULL;
+ }
+
+out:
+ pixman_region32_fini(&cursor_region);
+}
+
+static int
+setup_scanout_surface(struct weston_output *output, struct weston_surface *es)
+{
+ if (es->visual != WESTON_RGB_VISUAL ||
+ output->prepare_scanout_surface(output, es) != 0)
+ return -1;
+
+ /* assert output->pending_scanout_buffer == NULL */
+ output->pending_scanout_buffer = es->buffer;
+ output->pending_scanout_buffer->busy_count++;
+
+ wl_list_insert(output->pending_scanout_buffer->resource.destroy_listener_list.prev,
+ &output->pending_scanout_buffer_destroy_listener.link);
+
+ return 0;
+}
+
+static void
+weston_output_repaint(struct weston_output *output)
+{
+ struct weston_compositor *ec = output->compositor;
+ struct weston_surface *es;
+ pixman_region32_t opaque, new_damage, total_damage, repaint;
+
+ output->prepare_render(output);
+
+ glViewport(0, 0, output->current->width, output->current->height);
+
+ glUseProgram(ec->texture_shader.program);
+ glUniformMatrix4fv(ec->texture_shader.proj_uniform,
+ 1, GL_FALSE, output->matrix.d);
+ glUniform1i(ec->texture_shader.tex_uniform, 0);
+
+ weston_output_set_cursor(output, ec->input_device,
+ ec->fade.spring.current >= 0.001);
+
+ pixman_region32_init(&new_damage);
+ pixman_region32_init(&opaque);
+
+ wl_list_for_each(es, &ec->surface_list, link) {
+ pixman_region32_subtract(&es->damage, &es->damage, &opaque);
+ pixman_region32_union(&new_damage, &new_damage, &es->damage);
+ pixman_region32_union(&opaque, &opaque, &es->opaque);
+ }
+
+ pixman_region32_init(&total_damage);
+ pixman_region32_union(&total_damage, &new_damage,
+ &output->previous_damage);
+ pixman_region32_intersect(&output->previous_damage,
+ &new_damage, &output->region);
+
+ pixman_region32_fini(&opaque);
+ pixman_region32_fini(&new_damage);
+
+ es = container_of(ec->surface_list.next, struct weston_surface, link);
+
+ if (setup_scanout_surface(output, es) == 0)
+ /* We're drawing nothing, just let the damage accumulate */
+ return;
+
+ if (es->fullscreen_output == output) {
+ if (es->width < output->current->width ||
+ es->height < output->current->height)
+ glClear(GL_COLOR_BUFFER_BIT);
+ weston_surface_draw(es, output, &total_damage);
+ } else {
+ wl_list_for_each(es, &ec->surface_list, link) {
+ pixman_region32_copy(&es->damage, &total_damage);
+ pixman_region32_subtract(&total_damage, &total_damage, &es->opaque);
+ }
+
+ wl_list_for_each_reverse(es, &ec->surface_list, link) {
+ pixman_region32_init(&repaint);
+ pixman_region32_intersect(&repaint, &output->region,
+ &es->damage);
+ weston_surface_draw(es, output, &repaint);
+ pixman_region32_subtract(&es->damage,
+ &es->damage, &output->region);
+ }
+ }
+
+ if (ec->fade.spring.current > 0.001)
+ fade_output(output, ec->fade.spring.current, &total_damage);
+}
+
+struct weston_frame_callback {
+ struct wl_resource resource;
+ struct wl_list link;
+};
+
+static void
+repaint(void *data, int msecs)
+{
+ struct weston_output *output = data;
+ struct weston_compositor *compositor = output->compositor;
+ struct weston_animation *animation, *next;
+ struct weston_frame_callback *cb, *cnext;
+
+ weston_output_repaint(output);
+ output->repaint_needed = 0;
+ output->repaint_scheduled = 1;
+ output->present(output);
+
+ wl_list_for_each_safe(cb, cnext, &output->frame_callback_list, link) {
+ wl_resource_post_event(&cb->resource, WL_CALLBACK_DONE, msecs);
+ wl_resource_destroy(&cb->resource, 0);
+ }
+
+ wl_list_for_each_safe(animation, next,
+ &compositor->animation_list, link)
+ animation->frame(animation, output, msecs);
+}
+
+static void
+idle_repaint(void *data)
+{
+ repaint(data, weston_compositor_get_time());
+}
+
+WL_EXPORT void
+weston_output_finish_frame(struct weston_output *output, int msecs)
+{
+ if (output->scanout_buffer) {
+ weston_buffer_post_release(output->scanout_buffer);
+ wl_list_remove(&output->scanout_buffer_destroy_listener.link);
+ output->scanout_buffer = NULL;
+ }
+
+ if (output->pending_scanout_buffer) {
+ output->scanout_buffer = output->pending_scanout_buffer;
+ wl_list_remove(&output->pending_scanout_buffer_destroy_listener.link);
+ wl_list_insert(output->scanout_buffer->resource.destroy_listener_list.prev,
+ &output->scanout_buffer_destroy_listener.link);
+ output->pending_scanout_buffer = NULL;
+ }
+
+ if (output->repaint_needed)
+ repaint(output, msecs);
+ else
+ output->repaint_scheduled = 0;
+}
+
+WL_EXPORT void
+weston_compositor_schedule_repaint(struct weston_compositor *compositor)
+{
+ struct weston_output *output;
+ struct wl_event_loop *loop;
+
+ if (compositor->state == WESTON_COMPOSITOR_SLEEPING)
+ return;
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+ wl_list_for_each(output, &compositor->output_list, link) {
+ output->repaint_needed = 1;
+ if (output->repaint_scheduled)
+ continue;
+
+ wl_event_loop_add_idle(loop, idle_repaint, output);
+ output->repaint_scheduled = 1;
+ }
+}
+
+WL_EXPORT void
+weston_compositor_fade(struct weston_compositor *compositor, float tint)
+{
+ int done;
+
+ done = weston_spring_done(&compositor->fade.spring);
+ compositor->fade.spring.target = tint;
+ if (weston_spring_done(&compositor->fade.spring))
+ return;
+
+ if (done)
+ compositor->fade.spring.timestamp =
+ weston_compositor_get_time();
+
+ weston_compositor_damage_all(compositor);
+ if (wl_list_empty(&compositor->fade.animation.link))
+ wl_list_insert(compositor->animation_list.prev,
+ &compositor->fade.animation.link);
+}
+
+static void
+surface_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource, weston_compositor_get_time());
+}
+
+WL_EXPORT void
+weston_surface_assign_output(struct weston_surface *es)
+{
+ struct weston_compositor *ec = es->compositor;
+ struct weston_output *output, *new_output;
+ pixman_region32_t region;
+ uint32_t max, area;
+ pixman_box32_t *e;
+
+ new_output = NULL;
+ max = 0;
+ wl_list_for_each(output, &ec->output_list, link) {
+ pixman_region32_init_rect(&region,
+ es->x, es->y, es->width, es->height);
+ pixman_region32_intersect(&region, &region, &output->region);
+
+ e = pixman_region32_extents(&region);
+ area = (e->x2 - e->x1) * (e->y2 - e->y1);
+
+ if (area >= max) {
+ new_output = output;
+ max = area;
+ }
+ }
+
+ es->output = new_output;
+ if (!wl_list_empty(&es->frame_callback_list)) {
+ wl_list_insert_list(new_output->frame_callback_list.prev,
+ &es->frame_callback_list);
+ wl_list_init(&es->frame_callback_list);
+ }
+}
+
+static void
+surface_attach(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *buffer_resource, int32_t x, int32_t y)
+{
+ struct weston_surface *es = resource->data;
+ struct weston_shell *shell = es->compositor->shell;
+ struct wl_buffer *buffer = buffer_resource->data;
+
+ if (es->buffer) {
+ weston_buffer_post_release(es->buffer);
+ wl_list_remove(&es->buffer_destroy_listener.link);
+ }
+
+ buffer->busy_count++;
+ es->buffer = buffer;
+ wl_list_insert(es->buffer->resource.destroy_listener_list.prev,
+ &es->buffer_destroy_listener.link);
+
+ if (es->visual == WESTON_NONE_VISUAL) {
+ shell->map(shell, es, buffer->width, buffer->height);
+ } else if (x != 0 || y != 0 ||
+ es->width != buffer->width ||
+ es->height != buffer->height) {
+ shell->configure(shell, es, es->x + x, es->y + y,
+ buffer->width, buffer->height);
+ }
+
+ weston_buffer_attach(buffer, &es->surface);
+}
+
+static void
+surface_damage(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct weston_surface *es = resource->data;
+
+ weston_surface_damage_rectangle(es, x, y, width, height);
+}
+
+static void
+destroy_frame_callback(struct wl_resource *resource)
+{
+ struct weston_frame_callback *cb = resource->data;
+
+ wl_list_remove(&cb->link);
+ free(cb);
+}
+
+static void
+surface_frame(struct wl_client *client,
+ struct wl_resource *resource, uint32_t callback)
+{
+ struct weston_frame_callback *cb;
+ struct weston_surface *es = resource->data;
+
+ cb = malloc(sizeof *cb);
+ if (cb == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ cb->resource.object.interface = &wl_callback_interface;
+ cb->resource.object.id = callback;
+ cb->resource.destroy = destroy_frame_callback;
+ cb->resource.client = client;
+ cb->resource.data = cb;
+
+ wl_client_add_resource(client, &cb->resource);
+
+ if (es->output) {
+ wl_list_insert(es->output->frame_callback_list.prev,
+ &cb->link);
+ } else {
+ wl_list_insert(es->frame_callback_list.prev, &cb->link);
+ }
+}
+
+const static struct wl_surface_interface surface_interface = {
+ surface_destroy,
+ surface_attach,
+ surface_damage,
+ surface_frame
+};
+
+static void
+compositor_create_surface(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct weston_compositor *ec = resource->data;
+ struct weston_surface *surface;
+
+ surface = weston_surface_create(ec, 0, 0, 0, 0);
+ if (surface == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ surface->surface.resource.destroy = destroy_surface;
+
+ surface->surface.resource.object.id = id;
+ surface->surface.resource.object.interface = &wl_surface_interface;
+ surface->surface.resource.object.implementation =
+ (void (**)(void)) &surface_interface;
+ surface->surface.resource.data = surface;
+
+ wl_client_add_resource(client, &surface->surface.resource);
+}
+
+const static struct wl_compositor_interface compositor_interface = {
+ compositor_create_surface,
+};
+
+static void
+weston_surface_transform(struct weston_surface *surface,
+ int32_t x, int32_t y, int32_t *sx, int32_t *sy)
+{
+ *sx = x - surface->x;
+ *sy = y - surface->y;
+}
+
+static struct weston_surface *
+weston_compositor_pick_surface(struct weston_compositor *compositor,
+ int32_t x, int32_t y, int32_t *sx, int32_t *sy)
+{
+ struct weston_surface *surface;
+
+ wl_list_for_each(surface, &compositor->surface_list, link) {
+ if (surface->surface.resource.client == NULL)
+ continue;
+ weston_surface_transform(surface, x, y, sx, sy);
+ if (0 <= *sx && *sx < surface->width &&
+ 0 <= *sy && *sy < surface->height)
+ return surface;
+ }
+
+ return NULL;
+}
+
+WL_EXPORT struct weston_surface *
+pick_surface(struct wl_input_device *device, int32_t *sx, int32_t *sy)
+{
+ struct weston_input_device *wd = (struct weston_input_device *) device;
+
+ return weston_compositor_pick_surface(wd->compositor,
+ device->x, device->y, sx, sy);
+}
+
+
+static void
+implicit_grab_motion(struct wl_grab *grab,
+ uint32_t time, int32_t x, int32_t y)
+{
+ struct weston_input_device *device =
+ (struct weston_input_device *) grab->input_device;
+ struct weston_surface *es =
+ (struct weston_surface *) device->input_device.pointer_focus;
+ int32_t sx, sy;
+ struct wl_resource *resource;
+
+ resource = grab->input_device->pointer_focus_resource;
+ if (resource) {
+ weston_surface_transform(es, x, y, &sx, &sy);
+ wl_resource_post_event(resource, WL_INPUT_DEVICE_MOTION,
+ time, x, y, sx, sy);
+ }
+}
+
+static void
+implicit_grab_button(struct wl_grab *grab,
+ uint32_t time, int32_t button, int32_t state)
+{
+ struct wl_resource *resource;
+
+ resource = grab->input_device->pointer_focus_resource;
+ if (resource)
+ wl_resource_post_event(resource, WL_INPUT_DEVICE_BUTTON,
+ time, button, state);
+}
+
+static void
+implicit_grab_end(struct wl_grab *grab, uint32_t time)
+{
+}
+
+static const struct wl_grab_interface implicit_grab_interface = {
+ implicit_grab_motion,
+ implicit_grab_button,
+ implicit_grab_end
+};
+
+WL_EXPORT void
+weston_compositor_wake(struct weston_compositor *compositor)
+{
+ compositor->state = WESTON_COMPOSITOR_ACTIVE;
+ weston_compositor_fade(compositor, 0.0);
+
+ wl_event_source_timer_update(compositor->idle_source,
+ compositor->idle_time * 1000);
+}
+
+WL_EXPORT void
+weston_compositor_activity(struct weston_compositor *compositor)
+{
+ if (compositor->state == WESTON_COMPOSITOR_ACTIVE) {
+ weston_compositor_wake(compositor);
+ } else {
+ compositor->shell->unlock(compositor->shell);
+ }
+}
+
+static void
+weston_compositor_idle_inhibit(struct weston_compositor *compositor)
+{
+ weston_compositor_activity(compositor);
+ compositor->idle_inhibit++;
+}
+
+static void
+weston_compositor_idle_release(struct weston_compositor *compositor)
+{
+ compositor->idle_inhibit--;
+ weston_compositor_activity(compositor);
+}
+
+static int
+idle_handler(void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ if (compositor->idle_inhibit)
+ return 1;
+
+ weston_compositor_fade(compositor, 1.0);
+
+ return 1;
+}
+
+WL_EXPORT void
+notify_motion(struct wl_input_device *device, uint32_t time, int x, int y)
+{
+ struct weston_surface *es;
+ struct weston_output *output;
+ const struct wl_grab_interface *interface;
+ struct weston_input_device *wd = (struct weston_input_device *) device;
+ struct weston_compositor *ec = wd->compositor;
+ int32_t sx, sy;
+ int x_valid = 0, y_valid = 0;
+ int min_x = INT_MAX, min_y = INT_MAX, max_x = INT_MIN, max_y = INT_MIN;
+
+ weston_compositor_activity(ec);
+
+ wl_list_for_each(output, &ec->output_list, link) {
+ if (output->x <= x && x <= output->x + output->current->width)
+ x_valid = 1;
+
+ if (output->y <= y && y <= output->y + output->current->height)
+ y_valid = 1;
+
+ /* FIXME: calculate this only on output addition/deletion */
+ if (output->x < min_x)
+ min_x = output->x;
+ if (output->y < min_y)
+ min_y = output->y;
+
+ if (output->x + output->current->width > max_x)
+ max_x = output->x + output->current->width;
+ if (output->y + output->current->height > max_y)
+ max_y = output->y + output->current->height;
+ }
+
+ if (!x_valid) {
+ if (x < min_x)
+ x = min_x;
+ else if (x >= max_x)
+ x = max_x;
+ }
+ if (!y_valid) {
+ if (y < min_y)
+ y = min_y;
+ else if (y >= max_y)
+ y = max_y;
+ }
+
+ device->x = x;
+ device->y = y;
+
+ if (device->grab) {
+ interface = device->grab->interface;
+ interface->motion(device->grab, time, x, y);
+ } else {
+ es = pick_surface(device, &sx, &sy);
+ wl_input_device_set_pointer_focus(device,
+ &es->surface,
+ time, x, y, sx, sy);
+ if (device->pointer_focus_resource)
+ wl_resource_post_event(device->pointer_focus_resource,
+ WL_INPUT_DEVICE_MOTION,
+ time, x, y, sx, sy);
+ }
+
+ if (wd->sprite) {
+ weston_surface_damage_below(wd->sprite);
+
+ wd->sprite->x = device->x - wd->hotspot_x;
+ wd->sprite->y = device->y - wd->hotspot_y;
+
+ weston_surface_damage(wd->sprite);
+ }
+}
+
+WL_EXPORT void
+weston_surface_activate(struct weston_surface *surface,
+ struct weston_input_device *device, uint32_t time)
+{
+ weston_surface_raise(surface);
+ wl_input_device_set_keyboard_focus(&device->input_device,
+ &surface->surface, time);
+ weston_data_device_set_keyboard_focus(device);
+}
+
+WL_EXPORT void
+notify_button(struct wl_input_device *device,
+ uint32_t time, int32_t button, int32_t state)
+{
+ struct weston_input_device *wd = (struct weston_input_device *) device;
+ struct weston_compositor *compositor = wd->compositor;
+ struct weston_surface *surface =
+ (struct weston_surface *) device->pointer_focus;
+ int32_t sx, sy;
+
+ if (state)
+ weston_compositor_idle_inhibit(compositor);
+ else
+ weston_compositor_idle_release(compositor);
+
+ weston_compositor_run_binding(compositor, wd, time, 0, button, state);
+
+ if (state && surface && device->grab == NULL) {
+ wl_input_device_start_grab(device,
+ &device->implicit_grab,
+ button, time);
+ }
+
+ if (device->grab)
+ device->grab->interface->button(device->grab, time,
+ button, state);
+
+ if (!state && device->grab && device->grab_button == button) {
+ wl_input_device_end_grab(device, time);
+ surface = pick_surface(device, &sx, &sy);
+ wl_input_device_set_pointer_focus(device, &surface->surface,
+ time, device->x, device->y,
+ sx, sy);
+ }
+}
+
+static void
+update_modifier_state(struct weston_input_device *device,
+ uint32_t key, uint32_t state)
+{
+ uint32_t modifier;
+
+ switch (key) {
+ case KEY_LEFTCTRL:
+ case KEY_RIGHTCTRL:
+ modifier = MODIFIER_CTRL;
+ break;
+
+ case KEY_LEFTALT:
+ case KEY_RIGHTALT:
+ modifier = MODIFIER_ALT;
+ break;
+
+ case KEY_LEFTMETA:
+ case KEY_RIGHTMETA:
+ modifier = MODIFIER_SUPER;
+ break;
+
+ default:
+ modifier = 0;
+ break;
+ }
+
+ if (state)
+ device->modifier_state |= modifier;
+ else
+ device->modifier_state &= ~modifier;
+}
+
+WL_EXPORT void
+notify_key(struct wl_input_device *device,
+ uint32_t time, uint32_t key, uint32_t state)
+{
+ struct weston_input_device *wd = (struct weston_input_device *) device;
+ struct weston_compositor *compositor = wd->compositor;
+ uint32_t *k, *end;
+
+ if (state)
+ weston_compositor_idle_inhibit(compositor);
+ else
+ weston_compositor_idle_release(compositor);
+
+ weston_compositor_run_binding(compositor, wd, time, key, 0, state);
+
+ update_modifier_state(wd, key, state);
+ end = device->keys.data + device->keys.size;
+ for (k = device->keys.data; k < end; k++) {
+ if (*k == key)
+ *k = *--end;
+ }
+ device->keys.size = (void *) end - device->keys.data;
+ if (state) {
+ k = wl_array_add(&device->keys, sizeof *k);
+ *k = key;
+ }
+
+ if (device->keyboard_focus_resource)
+ wl_resource_post_event(device->keyboard_focus_resource,
+ WL_INPUT_DEVICE_KEY, time, key, state);
+}
+
+WL_EXPORT void
+notify_pointer_focus(struct wl_input_device *device,
+ uint32_t time, struct weston_output *output,
+ int32_t x, int32_t y)
+{
+ struct weston_input_device *wd = (struct weston_input_device *) device;
+ struct weston_compositor *compositor = wd->compositor;
+ struct weston_surface *es;
+ int32_t sx, sy;
+
+ if (output) {
+ device->x = x;
+ device->y = y;
+ es = pick_surface(device, &sx, &sy);
+ wl_input_device_set_pointer_focus(device,
+ &es->surface,
+ time, x, y, sx, sy);
+
+ compositor->focus = 1;
+ } else {
+ wl_input_device_set_pointer_focus(device, NULL,
+ time, 0, 0, 0, 0);
+ compositor->focus = 0;
+ }
+}
+
+WL_EXPORT void
+notify_keyboard_focus(struct wl_input_device *device,
+ uint32_t time, struct weston_output *output,
+ struct wl_array *keys)
+{
+ struct weston_input_device *wd =
+ (struct weston_input_device *) device;
+ struct weston_compositor *compositor = wd->compositor;
+ struct weston_surface *es;
+ uint32_t *k, *end;
+
+ if (!wl_list_empty(&compositor->surface_list))
+ es = container_of(compositor->surface_list.next,
+ struct weston_surface, link);
+ else
+ es = NULL;
+
+ if (output) {
+ wl_array_copy(&wd->input_device.keys, keys);
+ wd->modifier_state = 0;
+ end = device->keys.data + device->keys.size;
+ for (k = device->keys.data; k < end; k++) {
+ weston_compositor_idle_inhibit(compositor);
+ update_modifier_state(wd, *k, 1);
+ }
+
+ if (es && es->surface.resource.client)
+ wl_input_device_set_keyboard_focus(&wd->input_device,
+ &es->surface, time);
+ } else {
+ end = device->keys.data + device->keys.size;
+ for (k = device->keys.data; k < end; k++)
+ weston_compositor_idle_release(compositor);
+
+ wd->modifier_state = 0;
+ wl_input_device_set_keyboard_focus(&wd->input_device,
+ NULL, time);
+ }
+}
+
+/* TODO: share this function with wayland-server.c */
+static struct wl_resource *
+find_resource_for_surface(struct wl_list *list, struct wl_surface *surface)
+{
+ struct wl_resource *r;
+
+ if (!surface)
+ return NULL;
+
+ wl_list_for_each(r, list, link) {
+ if (r->client == surface->resource.client)
+ return r;
+ }
+
+ return NULL;
+}
+
+static void
+lose_touch_focus_resource(struct wl_listener *listener,
+ struct wl_resource *resource, uint32_t time)
+{
+ struct weston_input_device *device =
+ container_of(listener, struct weston_input_device,
+ touch_focus_resource_listener);
+
+ device->touch_focus_resource = NULL;
+}
+
+static void
+lose_touch_focus(struct wl_listener *listener,
+ struct wl_resource *resource, uint32_t time)
+{
+ struct weston_input_device *device =
+ container_of(listener, struct weston_input_device,
+ touch_focus_listener);
+
+ device->touch_focus = NULL;
+}
+
+static void
+touch_set_focus(struct weston_input_device *device,
+ struct wl_surface *surface, uint32_t time)
+{
+ struct wl_input_device *input_device = &device->input_device;
+ struct wl_resource *resource;
+
+ if (device->touch_focus == surface)
+ return;
+
+ if (surface) {
+ resource =
+ find_resource_for_surface(&input_device->resource_list,
+ surface);
+ if (!resource) {
+ fprintf(stderr, "couldn't find resource\n");
+ return;
+ }
+
+ device->touch_focus_resource_listener.func =
+ lose_touch_focus_resource;
+ wl_list_insert(resource->destroy_listener_list.prev,
+ &device->touch_focus_resource_listener.link);
+ device->touch_focus_listener.func = lose_touch_focus;
+ wl_list_insert(surface->resource.destroy_listener_list.prev,
+ &device->touch_focus_listener.link);
+
+ device->touch_focus = surface;
+ device->touch_focus_resource = resource;
+ } else {
+ if (device->touch_focus)
+ wl_list_remove(&device->touch_focus_listener.link);
+ if (device->touch_focus_resource)
+ wl_list_remove(&device->touch_focus_resource_listener.link);
+ device->touch_focus = NULL;
+ device->touch_focus_resource = NULL;
+ }
+}
+
+/**
+ * notify_touch - emulates button touches and notifies surfaces accordingly.
+ *
+ * It assumes always the correct cycle sequence until it gets here: touch_down
+ * → touch_update → ... → touch_update → touch_end. The driver is responsible
+ * for sending along such order.
+ *
+ */
+WL_EXPORT void
+notify_touch(struct wl_input_device *device, uint32_t time, int touch_id,
+ int x, int y, int touch_type)
+{
+ struct weston_input_device *wd = (struct weston_input_device *) device;
+ struct weston_compositor *ec = wd->compositor;
+ struct weston_surface *es;
+ int32_t sx, sy;
+
+ switch (touch_type) {
+ case WL_INPUT_DEVICE_TOUCH_DOWN:
+ weston_compositor_idle_inhibit(ec);
+
+ wd->num_tp++;
+
+ /* the first finger down picks the surface, and all further go
+ * to that surface for the remainder of the touch session i.e.
+ * until all touch points are up again. */
+ if (wd->num_tp == 1) {
+ es = weston_compositor_pick_surface(ec, x, y, &sx, &sy);
+ touch_set_focus(wd, &es->surface, time);
+ } else if (wd->touch_focus) {
+ es = (struct weston_surface *) wd->touch_focus;
+ weston_surface_transform(es, x, y, &sx, &sy);
+ }
+
+ if (wd->touch_focus_resource && wd->touch_focus)
+ wl_resource_post_event(wd->touch_focus_resource,
+ touch_type, time,
+ wd->touch_focus,
+ touch_id, sx, sy);
+ break;
+ case WL_INPUT_DEVICE_TOUCH_MOTION:
+ es = (struct weston_surface *) wd->touch_focus;
+ if (!es)
+ break;
+
+ weston_surface_transform(es, x, y, &sx, &sy);
+ if (wd->touch_focus_resource)
+ wl_resource_post_event(wd->touch_focus_resource,
+ touch_type, time,
+ touch_id, sx, sy);
+ break;
+ case WL_INPUT_DEVICE_TOUCH_UP:
+ weston_compositor_idle_release(ec);
+ wd->num_tp--;
+
+ if (wd->touch_focus_resource)
+ wl_resource_post_event(wd->touch_focus_resource,
+ touch_type, time, touch_id);
+ if (wd->num_tp == 0)
+ touch_set_focus(wd, NULL, time);
+ break;
+ }
+}
+
+static void
+input_device_attach(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t time,
+ struct wl_resource *buffer_resource, int32_t x, int32_t y)
+{
+ struct weston_input_device *device = resource->data;
+ struct weston_compositor *compositor = device->compositor;
+ struct wl_buffer *buffer;
+
+ if (time < device->input_device.pointer_focus_time)
+ return;
+ if (device->input_device.pointer_focus == NULL)
+ return;
+ if (device->input_device.pointer_focus->resource.client != client)
+ return;
+
+ if (device->sprite)
+ weston_surface_damage_below(device->sprite);
+
+ if (!buffer_resource) {
+ destroy_surface(&device->sprite->surface.resource);
+ device->sprite = NULL;
+ return;
+ }
+
+ if (!device->sprite) {
+ device->sprite =
+ weston_surface_create(compositor,
+ device->input_device.x,
+ device->input_device.y, 32, 32);
+ wl_list_init(&device->sprite->link);
+ }
+
+ buffer = buffer_resource->data;
+ weston_buffer_attach(buffer, &device->sprite->surface);
+
+ device->hotspot_x = x;
+ device->hotspot_y = y;
+ device->sprite->width = buffer->width;
+ device->sprite->height = buffer->height;
+ device->sprite->x = device->input_device.x - device->hotspot_x;
+ device->sprite->y = device->input_device.y - device->hotspot_y;
+
+ weston_surface_damage(device->sprite);
+}
+
+const static struct wl_input_device_interface input_device_interface = {
+ input_device_attach,
+};
+
+static void unbind_input_device(struct wl_resource *resource)
+{
+ wl_list_remove(&resource->link);
+ free(resource);
+}
+
+static void
+bind_input_device(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct wl_input_device *device = data;
+ struct wl_resource *resource;
+
+ resource = wl_client_add_object(client, &wl_input_device_interface,
+ &input_device_interface, id, data);
+ wl_list_insert(&device->resource_list, &resource->link);
+ resource->destroy = unbind_input_device;
+}
+
+WL_EXPORT void
+weston_input_device_init(struct weston_input_device *device,
+ struct weston_compositor *ec)
+{
+ wl_input_device_init(&device->input_device);
+ wl_list_init(&device->drag_resource_list);
+
+ wl_display_add_global(ec->wl_display, &wl_input_device_interface,
+ device, bind_input_device);
+
+ device->sprite = NULL;
+
+ device->compositor = ec;
+ device->hotspot_x = 16;
+ device->hotspot_y = 16;
+ device->modifier_state = 0;
+ device->num_tp = 0;
+
+ device->input_device.implicit_grab.interface = &implicit_grab_interface;
+
+ wl_list_insert(ec->input_device_list.prev, &device->link);
+
+ device->selection_data_source = NULL;
+}
+
+static void
+bind_output(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct weston_output *output = data;
+ struct weston_mode *mode;
+ struct wl_resource *resource;
+
+ resource = wl_client_add_object(client,
+ &wl_output_interface, NULL, id, data);
+
+ wl_resource_post_event(resource,
+ WL_OUTPUT_GEOMETRY,
+ output->x,
+ output->y,
+ output->mm_width,
+ output->mm_height,
+ output->subpixel,
+ output->make, output->model);
+
+ wl_list_for_each (mode, &output->mode_list, link) {
+ wl_resource_post_event(resource,
+ WL_OUTPUT_MODE,
+ mode->flags,
+ mode->width,
+ mode->height,
+ mode->refresh);
+ }
+}
+
+static const char vertex_shader[] =
+ "uniform mat4 proj;\n"
+ "attribute vec2 position;\n"
+ "attribute vec2 texcoord;\n"
+ "varying vec2 v_texcoord;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = proj * vec4(position, 0.0, 1.0);\n"
+ " v_texcoord = texcoord;\n"
+ "}\n";
+
+static const char texture_fragment_shader[] =
+ "precision mediump float;\n"
+ "varying vec2 v_texcoord;\n"
+ "uniform sampler2D tex;\n"
+ "uniform float alpha;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = texture2D(tex, v_texcoord)\n;"
+ " gl_FragColor = alpha * gl_FragColor;\n"
+ "}\n";
+
+static const char solid_fragment_shader[] =
+ "precision mediump float;\n"
+ "uniform vec4 color;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = color\n;"
+ "}\n";
+
+static int
+compile_shader(GLenum type, const char *source)
+{
+ GLuint s;
+ char msg[512];
+ GLint status;
+
+ s = glCreateShader(type);
+ glShaderSource(s, 1, &source, NULL);
+ glCompileShader(s);
+ glGetShaderiv(s, GL_COMPILE_STATUS, &status);
+ if (!status) {
+ glGetShaderInfoLog(s, sizeof msg, NULL, msg);
+ fprintf(stderr, "shader info: %s\n", msg);
+ return GL_NONE;
+ }
+
+ return s;
+}
+
+static int
+weston_shader_init(struct weston_shader *shader,
+ const char *vertex_source, const char *fragment_source)
+{
+ char msg[512];
+ GLint status;
+
+ shader->vertex_shader =
+ compile_shader(GL_VERTEX_SHADER, vertex_source);
+ shader->fragment_shader =
+ compile_shader(GL_FRAGMENT_SHADER, fragment_source);
+
+ shader->program = glCreateProgram();
+ glAttachShader(shader->program, shader->vertex_shader);
+ glAttachShader(shader->program, shader->fragment_shader);
+ glBindAttribLocation(shader->program, 0, "position");
+ glBindAttribLocation(shader->program, 1, "texcoord");
+
+ glLinkProgram(shader->program);
+ glGetProgramiv(shader->program, GL_LINK_STATUS, &status);
+ if (!status) {
+ glGetProgramInfoLog(shader->program, sizeof msg, NULL, msg);
+ fprintf(stderr, "link info: %s\n", msg);
+ return -1;
+ }
+
+ shader->proj_uniform = glGetUniformLocation(shader->program, "proj");
+ shader->tex_uniform = glGetUniformLocation(shader->program, "tex");
+ shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha");
+
+ return 0;
+}
+
+static int
+init_solid_shader(struct weston_shader *shader,
+ GLuint vertex_shader, const char *fragment_source)
+{
+ GLint status;
+ char msg[512];
+
+ shader->vertex_shader = vertex_shader;
+ shader->fragment_shader =
+ compile_shader(GL_FRAGMENT_SHADER, fragment_source);
+
+ shader->program = glCreateProgram();
+ glAttachShader(shader->program, shader->vertex_shader);
+ glAttachShader(shader->program, shader->fragment_shader);
+ glBindAttribLocation(shader->program, 0, "position");
+ glBindAttribLocation(shader->program, 1, "texcoord");
+
+ glLinkProgram(shader->program);
+ glGetProgramiv(shader->program, GL_LINK_STATUS, &status);
+ if (!status) {
+ glGetProgramInfoLog(shader->program, sizeof msg, NULL, msg);
+ fprintf(stderr, "link info: %s\n", msg);
+ return -1;
+ }
+
+ shader->proj_uniform = glGetUniformLocation(shader->program, "proj");
+ shader->color_uniform = glGetUniformLocation(shader->program, "color");
+
+ return 0;
+}
+
+WL_EXPORT void
+weston_output_destroy(struct weston_output *output)
+{
+ pixman_region32_fini(&output->region);
+ pixman_region32_fini(&output->previous_damage);
+}
+
+WL_EXPORT void
+weston_output_move(struct weston_output *output, int x, int y)
+{
+ int flip;
+
+ output->x = x;
+ output->y = y;
+
+ pixman_region32_init(&output->previous_damage);
+ pixman_region32_init_rect(&output->region, x, y,
+ output->current->width,
+ output->current->height);
+
+ weston_matrix_init(&output->matrix);
+ weston_matrix_translate(&output->matrix,
+ -output->x - output->current->width / 2.0,
+ -output->y - output->current->height / 2.0, 0);
+
+ flip = (output->flags & WL_OUTPUT_FLIPPED) ? -1 : 1;
+ weston_matrix_scale(&output->matrix,
+ 2.0 / output->current->width,
+ flip * 2.0 / output->current->height, 1);
+
+ weston_output_damage(output);
+}
+
+WL_EXPORT void
+weston_output_init(struct weston_output *output, struct weston_compositor *c,
+ int x, int y, int width, int height, uint32_t flags)
+{
+ output->compositor = c;
+ output->x = x;
+ output->y = y;
+ output->mm_width = width;
+ output->mm_height = height;
+
+ output->flags = flags;
+ weston_output_move(output, x, y);
+
+ output->scanout_buffer_destroy_listener.func =
+ output_handle_scanout_buffer_destroy;
+ output->pending_scanout_buffer_destroy_listener.func =
+ output_handle_pending_scanout_buffer_destroy;
+
+ wl_list_init(&output->frame_callback_list);
+
+ wl_display_add_global(c->wl_display,
+ &wl_output_interface, output, bind_output);
+}
+
+static void
+shm_buffer_created(struct wl_buffer *buffer)
+{
+ struct wl_list *surfaces_attached_to;
+
+ surfaces_attached_to = malloc(sizeof *surfaces_attached_to);
+ if (!surfaces_attached_to) {
+ buffer->user_data = NULL;
+ return;
+ }
+
+ wl_list_init(surfaces_attached_to);
+
+ buffer->user_data = surfaces_attached_to;
+}
+
+static void
+shm_buffer_damaged(struct wl_buffer *buffer,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct wl_list *surfaces_attached_to = buffer->user_data;
+ struct weston_surface *es;
+ GLsizei tex_width = wl_shm_buffer_get_stride(buffer) / 4;
+
+ wl_list_for_each(es, surfaces_attached_to, buffer_link) {
+ glBindTexture(GL_TEXTURE_2D, es->texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT,
+ tex_width, buffer->height, 0,
+ GL_BGRA_EXT, GL_UNSIGNED_BYTE,
+ wl_shm_buffer_get_data(buffer));
+ /* Hmm, should use glTexSubImage2D() here but GLES2 doesn't
+ * support any unpack attributes except GL_UNPACK_ALIGNMENT. */
+ }
+}
+
+static void
+shm_buffer_destroyed(struct wl_buffer *buffer)
+{
+ struct wl_list *surfaces_attached_to = buffer->user_data;
+ struct weston_surface *es, *next;
+
+ wl_list_for_each_safe(es, next, surfaces_attached_to, buffer_link) {
+ wl_list_remove(&es->buffer_link);
+ wl_list_init(&es->buffer_link);
+ }
+
+ free(surfaces_attached_to);
+}
+
+const static struct wl_shm_callbacks shm_callbacks = {
+ shm_buffer_created,
+ shm_buffer_damaged,
+ shm_buffer_destroyed
+};
+
+static void
+compositor_bind(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct weston_compositor *compositor = data;
+
+ wl_client_add_object(client, &wl_compositor_interface,
+ &compositor_interface, id, compositor);
+}
+
+WL_EXPORT int
+weston_compositor_init(struct weston_compositor *ec, struct wl_display *display)
+{
+ struct wl_event_loop *loop;
+ const char *extensions;
+
+ ec->wl_display = display;
+
+ if (!wl_display_add_global(display, &wl_compositor_interface,
+ ec, compositor_bind))
+ return -1;
+
+ ec->shm = wl_shm_init(display, &shm_callbacks);
+
+ ec->image_target_texture_2d =
+ (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
+ ec->image_target_renderbuffer_storage = (void *)
+ eglGetProcAddress("glEGLImageTargetRenderbufferStorageOES");
+ ec->create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
+ ec->destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
+ ec->bind_display =
+ (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
+ ec->unbind_display =
+ (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
+
+ extensions = (const char *) glGetString(GL_EXTENSIONS);
+ if (!strstr(extensions, "GL_EXT_texture_format_BGRA8888")) {
+ fprintf(stderr,
+ "GL_EXT_texture_format_BGRA8888 not available\n");
+ return -1;
+ }
+
+ extensions =
+ (const char *) eglQueryString(ec->display, EGL_EXTENSIONS);
+ if (strstr(extensions, "EGL_WL_bind_wayland_display"))
+ ec->has_bind_display = 1;
+ if (ec->has_bind_display)
+ ec->bind_display(ec->display, ec->wl_display);
+
+ wl_list_init(&ec->surface_list);
+ wl_list_init(&ec->input_device_list);
+ wl_list_init(&ec->output_list);
+ wl_list_init(&ec->binding_list);
+ wl_list_init(&ec->animation_list);
+ weston_spring_init(&ec->fade.spring, 30.0, 1.0, 1.0);
+ ec->fade.animation.frame = fade_frame;
+ wl_list_init(&ec->fade.animation.link);
+
+ screenshooter_create(ec);
+
+ weston_data_device_manager_init(ec);
+
+ glActiveTexture(GL_TEXTURE0);
+
+ if (weston_shader_init(&ec->texture_shader,
+ vertex_shader, texture_fragment_shader) < 0)
+ return -1;
+ if (init_solid_shader(&ec->solid_shader,
+ ec->texture_shader.vertex_shader,
+ solid_fragment_shader) < 0)
+ return -1;
+
+ loop = wl_display_get_event_loop(ec->wl_display);
+ ec->idle_source = wl_event_loop_add_timer(loop, idle_handler, ec);
+ wl_event_source_timer_update(ec->idle_source, ec->idle_time * 1000);
+
+ weston_compositor_schedule_repaint(ec);
+
+ return 0;
+}
+
+WL_EXPORT void
+weston_compositor_shutdown(struct weston_compositor *ec)
+{
+ struct weston_output *output, *next;
+
+ /* Destroy all outputs associated with this compositor */
+ wl_list_for_each_safe(output, next, &ec->output_list, link)
+ output->destroy(output);
+}
+
+static int on_term_signal(int signal_number, void *data)
+{
+ struct wl_display *display = data;
+
+ fprintf(stderr, "caught signal %d\n", signal_number);
+ wl_display_terminate(display);
+
+ return 1;
+}
+
+static void *
+load_module(const char *name, const char *entrypoint, void **handle)
+{
+ char path[PATH_MAX];
+ void *module, *init;
+
+ if (name[0] != '/')
+ snprintf(path, sizeof path, MODULEDIR "/%s", name);
+ else
+ snprintf(path, sizeof path, "%s", name);
+
+ module = dlopen(path, RTLD_LAZY);
+ if (!module) {
+ fprintf(stderr,
+ "failed to load module: %s\n", dlerror());
+ return NULL;
+ }
+
+ init = dlsym(module, entrypoint);
+ if (!init) {
+ fprintf(stderr,
+ "failed to lookup init function: %s\n", dlerror());
+ return NULL;
+ }
+
+ return init;
+}
+
+int main(int argc, char *argv[])
+{
+ struct wl_display *display;
+ struct weston_compositor *ec;
+ struct wl_event_loop *loop;
+ int o, xserver = 0;
+ void *shell_module, *backend_module;
+ int (*shell_init)(struct weston_compositor *ec);
+ struct weston_compositor
+ *(*backend_init)(struct wl_display *display, char *options);
+ char *backend = NULL;
+ char *backend_options = "";
+ char *shell = NULL;
+ char *p;
+ int option_idle_time = 300;
+
+ static const char opts[] = "B:b:o:S:i:s:x";
+ static const struct option longopts[ ] = {
+ { "backend", 1, NULL, 'B' },
+ { "backend-options", 1, NULL, 'o' },
+ { "socket", 1, NULL, 'S' },
+ { "idle-time", 1, NULL, 'i' },
+ { "shell", 1, NULL, 's' },
+ { "xserver", 0, NULL, 'x' },
+ { NULL, }
+ };
+
+ while (o = getopt_long(argc, argv, opts, longopts, &o), o > 0) {
+ switch (o) {
+ case 'B':
+ backend = optarg;
+ break;
+ case 'o':
+ backend_options = optarg;
+ break;
+ case 'S':
+ option_socket_name = optarg;
+ break;
+ case 'i':
+ option_idle_time = strtol(optarg, &p, 0);
+ if (*p != '\0') {
+ fprintf(stderr,
+ "invalid idle time option: %s\n",
+ optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 's':
+ shell = optarg;
+ break;
+ case 'x':
+ xserver = 1;
+ break;
+ }
+ }
+
+ display = wl_display_create();
+
+ loop = wl_display_get_event_loop(display);
+ wl_event_loop_add_signal(loop, SIGTERM, on_term_signal, display);
+ wl_event_loop_add_signal(loop, SIGINT, on_term_signal, display);
+ wl_event_loop_add_signal(loop, SIGQUIT, on_term_signal, display);
+
+ wl_list_init(&child_process_list);
+ wl_event_loop_add_signal(loop, SIGCHLD, sigchld_handler, NULL);
+
+ if (!backend) {
+ if (getenv("WAYLAND_DISPLAY"))
+ backend = "wayland-backend.so";
+ else if (getenv("DISPLAY"))
+ backend = "x11-backend.so";
+ else if (getenv("OPENWFD"))
+ backend = "openwfd-backend.so";
+ else
+ backend = "drm-backend.so";
+ }
+
+ if (!shell)
+ shell = "desktop-shell.so";
+
+ backend_init = load_module(backend, "backend_init", &backend_module);
+ if (!backend_init)
+ exit(EXIT_FAILURE);
+
+ shell_init = load_module(shell, "shell_init", &shell_module);
+ if (!shell_init)
+ exit(EXIT_FAILURE);
+
+ ec = backend_init(display, backend_options);
+ if (ec == NULL) {
+ fprintf(stderr, "failed to create compositor\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ec->option_idle_time = option_idle_time;
+ ec->idle_time = option_idle_time;
+
+ if (shell_init(ec) < 0)
+ exit(EXIT_FAILURE);
+
+ if (xserver)
+ weston_xserver_init(ec);
+
+ if (wl_display_add_socket(display, option_socket_name)) {
+ fprintf(stderr, "failed to add socket: %m\n");
+ exit(EXIT_FAILURE);
+ }
+
+ wl_display_run(display);
+
+ if (xserver)
+ weston_xserver_destroy(ec);
+
+ if (ec->has_bind_display)
+ ec->unbind_display(ec->display, display);
+
+ ec->destroy(ec);
+ wl_display_destroy(display);
+
+ return 0;
+}
diff --git a/src/compositor.h b/src/compositor.h
new file mode 100644
index 0000000..209c21d
--- /dev/null
+++ b/src/compositor.h
@@ -0,0 +1,471 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ *
+ * 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.
+ */
+
+#ifndef _WAYLAND_SYSTEM_COMPOSITOR_H_
+#define _WAYLAND_SYSTEM_COMPOSITOR_H_
+
+#include <libudev.h>
+#include <pixman.h>
+#include <wayland-server.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+struct weston_matrix {
+ GLfloat d[16];
+};
+
+struct weston_vector {
+ GLfloat f[4];
+};
+
+void
+weston_matrix_init(struct weston_matrix *matrix);
+void
+weston_matrix_scale(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z);
+void
+weston_matrix_translate(struct weston_matrix *matrix,
+ GLfloat x, GLfloat y, GLfloat z);
+void
+weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v);
+
+struct weston_transform {
+ struct weston_matrix matrix;
+ struct weston_matrix inverse;
+};
+
+struct weston_surface;
+struct weston_input_device;
+
+struct weston_mode {
+ uint32_t flags;
+ int32_t width, height;
+ uint32_t refresh;
+ struct wl_list link;
+};
+
+struct weston_output {
+ struct wl_list link;
+ struct weston_compositor *compositor;
+ struct weston_matrix matrix;
+ struct wl_list frame_callback_list;
+ int32_t x, y, mm_width, mm_height;
+ pixman_region32_t region;
+ pixman_region32_t previous_damage;
+ uint32_t flags;
+ int repaint_needed;
+ int repaint_scheduled;
+
+ char *make, *model;
+ uint32_t subpixel;
+
+ struct weston_mode *current;
+ struct wl_list mode_list;
+ struct wl_buffer *scanout_buffer;
+ struct wl_listener scanout_buffer_destroy_listener;
+ struct wl_buffer *pending_scanout_buffer;
+ struct wl_listener pending_scanout_buffer_destroy_listener;
+
+ int (*prepare_render)(struct weston_output *output);
+ int (*present)(struct weston_output *output);
+ int (*prepare_scanout_surface)(struct weston_output *output,
+ struct weston_surface *es);
+ int (*set_hardware_cursor)(struct weston_output *output,
+ struct weston_input_device *input);
+ void (*destroy)(struct weston_output *output);
+};
+
+struct weston_input_device {
+ struct wl_input_device input_device;
+ struct weston_compositor *compositor;
+ struct weston_surface *sprite;
+ int32_t hotspot_x, hotspot_y;
+ struct wl_list link;
+ uint32_t modifier_state;
+ struct wl_selection *selection;
+
+ struct wl_list drag_resource_list;
+ struct weston_data_source *drag_data_source;
+ struct wl_surface *drag_focus;
+ struct wl_resource *drag_focus_resource;
+ struct wl_listener drag_focus_listener;
+
+ struct weston_data_source *selection_data_source;
+ struct wl_listener selection_data_source_listener;
+ struct wl_grab grab;
+
+ uint32_t num_tp;
+ struct wl_surface *touch_focus;
+ struct wl_listener touch_focus_listener;
+ struct wl_resource *touch_focus_resource;
+ struct wl_listener touch_focus_resource_listener;
+};
+
+enum weston_visual {
+ WESTON_NONE_VISUAL,
+ WESTON_ARGB_VISUAL,
+ WESTON_PREMUL_ARGB_VISUAL,
+ WESTON_RGB_VISUAL
+};
+
+struct weston_shader {
+ GLuint program;
+ GLuint vertex_shader, fragment_shader;
+ GLuint proj_uniform;
+ GLuint tex_uniform;
+ GLuint alpha_uniform;
+ GLuint color_uniform;
+};
+
+struct weston_animation {
+ void (*frame)(struct weston_animation *animation,
+ struct weston_output *output, uint32_t msecs);
+ struct wl_list link;
+};
+
+struct weston_spring {
+ double k;
+ double friction;
+ double current;
+ double target;
+ double previous;
+ uint32_t timestamp;
+};
+
+struct weston_shell {
+ void (*lock)(struct weston_shell *shell);
+ void (*unlock)(struct weston_shell *shell);
+ void (*map)(struct weston_shell *shell, struct weston_surface *surface,
+ int32_t width, int32_t height);
+ void (*configure)(struct weston_shell *shell,
+ struct weston_surface *surface,
+ int32_t x, int32_t y, int32_t width, int32_t height);
+};
+
+enum {
+ WESTON_COMPOSITOR_ACTIVE,
+ WESTON_COMPOSITOR_IDLE, /* shell->unlock called on activity */
+ WESTON_COMPOSITOR_SLEEPING /* no rendering, no frame events */
+};
+
+struct weston_compositor {
+ struct wl_shm *shm;
+ struct weston_xserver *wxs;
+
+ EGLDisplay display;
+ EGLContext context;
+ EGLConfig config;
+ GLuint fbo;
+ GLuint proj_uniform, tex_uniform, alpha_uniform;
+ uint32_t current_alpha;
+ struct weston_shader texture_shader;
+ struct weston_shader solid_shader;
+ struct wl_display *wl_display;
+
+ struct weston_shell *shell;
+
+ /* There can be more than one, but not right now... */
+ struct wl_input_device *input_device;
+
+ struct wl_list output_list;
+ struct wl_list input_device_list;
+ struct wl_list surface_list;
+ struct wl_list binding_list;
+ struct wl_list animation_list;
+ struct {
+ struct weston_spring spring;
+ struct weston_animation animation;
+ } fade;
+
+ uint32_t state;
+ struct wl_event_source *idle_source;
+ uint32_t idle_inhibit;
+ int option_idle_time; /* default timeout, s */
+ int idle_time; /* effective timeout, s */
+
+ /* Repaint state. */
+ struct timespec previous_swap;
+ struct wl_array vertices, indices;
+
+ struct weston_surface *overlay;
+ struct weston_switcher *switcher;
+ uint32_t focus;
+
+ PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC
+ image_target_renderbuffer_storage;
+ PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
+ PFNEGLCREATEIMAGEKHRPROC create_image;
+ PFNEGLDESTROYIMAGEKHRPROC destroy_image;
+ PFNEGLBINDWAYLANDDISPLAYWL bind_display;
+ PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
+ int has_bind_display;
+
+ void (*destroy)(struct weston_compositor *ec);
+ int (*authenticate)(struct weston_compositor *c, uint32_t id);
+ EGLImageKHR (*create_cursor_image)(struct weston_compositor *c,
+ int32_t *width, int32_t *height);
+};
+
+#define MODIFIER_CTRL (1 << 8)
+#define MODIFIER_ALT (1 << 9)
+#define MODIFIER_SUPER (1 << 10)
+
+enum weston_output_flags {
+ WL_OUTPUT_FLIPPED = 0x01
+};
+
+struct weston_surface {
+ struct wl_surface surface;
+ struct weston_compositor *compositor;
+ GLuint texture, saved_texture;
+ pixman_region32_t damage;
+ pixman_region32_t opaque;
+ int32_t x, y, width, height;
+ int32_t pitch;
+ struct wl_list link;
+ struct wl_list buffer_link;
+ struct weston_transform *transform;
+ uint32_t alpha;
+ uint32_t visual;
+
+ /*
+ * Which output to vsync this surface to.
+ * Used to determine, whether to send or queue frame events.
+ * Must be NULL, if 'link' is not in weston_compositor::surface_list.
+ */
+ struct weston_output *output;
+
+ struct weston_output *fullscreen_output;
+ struct wl_list frame_callback_list;
+
+ EGLImageKHR image;
+
+ struct wl_buffer *buffer;
+ struct wl_listener buffer_destroy_listener;
+};
+
+struct weston_data_source {
+ struct wl_resource resource;
+ struct wl_array mime_types;
+ int refcount;
+ void *data;
+
+ struct wl_resource *(*create_offer)(struct weston_data_source *source,
+ struct wl_resource *target);
+
+ void (*cancel)(struct weston_data_source *source);
+};
+
+void
+weston_data_source_unref(struct weston_data_source *source);
+
+void
+weston_input_device_set_selection(struct weston_input_device *device,
+ struct weston_data_source *source,
+ uint32_t time);
+
+void
+weston_spring_init(struct weston_spring *spring,
+ double k, double current, double target);
+void
+weston_spring_update(struct weston_spring *spring, uint32_t msec);
+int
+weston_spring_done(struct weston_spring *spring);
+
+void
+weston_surface_activate(struct weston_surface *surface,
+ struct weston_input_device *device, uint32_t time);
+
+void
+notify_motion(struct wl_input_device *device,
+ uint32_t time, int x, int y);
+void
+notify_button(struct wl_input_device *device,
+ uint32_t time, int32_t button, int32_t state);
+void
+notify_key(struct wl_input_device *device,
+ uint32_t time, uint32_t key, uint32_t state);
+
+void
+notify_pointer_focus(struct wl_input_device *device,
+ uint32_t time,
+ struct weston_output *output,
+ int32_t x, int32_t y);
+
+void
+notify_keyboard_focus(struct wl_input_device *device,
+ uint32_t time, struct weston_output *output,
+ struct wl_array *keys);
+
+void
+notify_touch(struct wl_input_device *device, uint32_t time, int touch_id,
+ int x, int y, int touch_type);
+
+void
+weston_output_finish_frame(struct weston_output *output, int msecs);
+void
+weston_output_damage(struct weston_output *output);
+void
+weston_compositor_repick(struct weston_compositor *compositor);
+void
+weston_compositor_schedule_repaint(struct weston_compositor *compositor);
+void
+weston_compositor_fade(struct weston_compositor *compositor, float tint);
+void
+weston_compositor_damage_all(struct weston_compositor *compositor);
+void
+weston_compositor_unlock(struct weston_compositor *compositor);
+void
+weston_compositor_wake(struct weston_compositor *compositor);
+void
+weston_compositor_activity(struct weston_compositor *compositor);
+
+struct weston_binding;
+typedef void (*weston_binding_handler_t)(struct wl_input_device *device,
+ uint32_t time, uint32_t key,
+ uint32_t button,
+ uint32_t state, void *data);
+struct weston_binding *
+weston_compositor_add_binding(struct weston_compositor *compositor,
+ uint32_t key, uint32_t button, uint32_t modifier,
+ weston_binding_handler_t binding, void *data);
+void
+weston_binding_destroy(struct weston_binding *binding);
+
+void
+weston_compositor_run_binding(struct weston_compositor *compositor,
+ struct weston_input_device *device,
+ uint32_t time,
+ uint32_t key, uint32_t button, int32_t state);
+
+struct weston_surface *
+weston_surface_create(struct weston_compositor *compositor,
+ int32_t x, int32_t y, int32_t width, int32_t height);
+
+void
+weston_surface_configure(struct weston_surface *surface,
+ int x, int y, int width, int height);
+
+void
+weston_surface_assign_output(struct weston_surface *surface);
+
+void
+weston_surface_damage(struct weston_surface *surface);
+
+void
+weston_surface_damage_below(struct weston_surface *surface);
+
+void
+weston_surface_damage_rectangle(struct weston_surface *surface,
+ int32_t x, int32_t y,
+ int32_t width, int32_t height);
+
+struct weston_surface *
+pick_surface(struct wl_input_device *device, int32_t *sx, int32_t *sy);
+
+uint32_t
+weston_compositor_get_time(void);
+
+int
+weston_compositor_init(struct weston_compositor *ec, struct wl_display *display);
+void
+weston_compositor_shutdown(struct weston_compositor *ec);
+void
+weston_output_move(struct weston_output *output, int x, int y);
+void
+weston_output_init(struct weston_output *output, struct weston_compositor *c,
+ int x, int y, int width, int height, uint32_t flags);
+void
+weston_output_destroy(struct weston_output *output);
+
+void
+weston_input_device_init(struct weston_input_device *device,
+ struct weston_compositor *ec);
+
+void
+weston_switcher_init(struct weston_compositor *compositor);
+
+enum {
+ TTY_ENTER_VT,
+ TTY_LEAVE_VT
+};
+
+typedef void (*tty_vt_func_t)(struct weston_compositor *compositor, int event);
+
+struct tty *
+tty_create(struct weston_compositor *compositor,
+ tty_vt_func_t vt_func, int tty_nr);
+
+void
+tty_destroy(struct tty *tty);
+
+void
+screenshooter_create(struct weston_compositor *ec);
+
+uint32_t *
+weston_load_image(const char *filename,
+ int32_t *width_arg, int32_t *height_arg,
+ uint32_t *stride_arg);
+
+struct weston_process;
+typedef void (*weston_process_cleanup_func_t)(struct weston_process *process,
+ int status);
+
+struct weston_process {
+ pid_t pid;
+ weston_process_cleanup_func_t cleanup;
+ struct wl_list link;
+};
+
+struct wl_client *
+weston_client_launch(struct weston_compositor *compositor,
+ struct weston_process *proc,
+ const char *path,
+ weston_process_cleanup_func_t cleanup);
+
+int
+weston_data_device_manager_init(struct weston_compositor *compositor);
+void
+weston_data_device_set_keyboard_focus(struct weston_input_device *device);
+
+void
+weston_watch_process(struct weston_process *process);
+
+int
+weston_xserver_init(struct weston_compositor *compositor);
+void
+weston_xserver_destroy(struct weston_compositor *compositor);
+void
+weston_xserver_surface_activate(struct weston_surface *surface);
+void
+weston_xserver_set_selection(struct weston_input_device *device);
+
+struct weston_zoom;
+typedef void (*weston_zoom_done_func_t)(struct weston_zoom *zoom, void *data);
+
+struct weston_zoom *
+weston_zoom_run(struct weston_surface *surface, GLfloat start, GLfloat stop,
+ weston_zoom_done_func_t done, void *data);
+
+#endif
diff --git a/src/data-device.c b/src/data-device.c
new file mode 100644
index 0000000..4568a6c
--- /dev/null
+++ b/src/data-device.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright © 2011 Kristian Høgsberg
+ *
+ * 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 <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "compositor.h"
+
+void
+weston_data_source_unref(struct weston_data_source *source)
+{
+ source->refcount--;
+ if (source->refcount == 0)
+ free(source);
+}
+
+static void
+data_offer_accept(struct wl_client *client, struct wl_resource *resource,
+ uint32_t time, const char *mime_type)
+{
+ struct weston_data_source *source = resource->data;
+
+ /* FIXME: Check that client is currently focused by the input
+ * device that is currently dragging this data source. Should
+ * this be a wl_data_device request? */
+
+ wl_resource_post_event(&source->resource,
+ WL_DATA_SOURCE_TARGET, mime_type);
+}
+
+static void
+data_offer_receive(struct wl_client *client, struct wl_resource *resource,
+ const char *mime_type, int32_t fd)
+{
+ struct weston_data_source *source = resource->data;
+
+ wl_resource_post_event(&source->resource,
+ WL_DATA_SOURCE_SEND, mime_type, fd);
+ close(fd);
+}
+
+static void
+data_offer_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource, weston_compositor_get_time());
+}
+
+static void
+destroy_data_offer(struct wl_resource *resource)
+{
+ struct weston_data_source *source = resource->data;
+
+ weston_data_source_unref(source);
+ free(resource);
+}
+
+static const struct wl_data_offer_interface data_offer_interface = {
+ data_offer_accept,
+ data_offer_receive,
+ data_offer_destroy,
+};
+
+static struct wl_resource *
+data_source_create_offer(struct weston_data_source *source,
+ struct wl_resource *target)
+{
+ struct wl_resource *resource;
+
+ resource = wl_client_new_object(target->client,
+ &wl_data_offer_interface,
+ &data_offer_interface, source);
+ resource->destroy = destroy_data_offer;
+
+ return resource;
+}
+
+static void
+data_source_cancel(struct weston_data_source *source)
+{
+ wl_resource_post_event(&source->resource, WL_DATA_SOURCE_CANCELLED);
+}
+
+static struct wl_resource *
+weston_data_source_send_offer(struct weston_data_source *source,
+ struct wl_resource *target)
+{
+ struct wl_resource *resource;
+ char **p, **end;
+
+ resource = source->create_offer(source, target);
+ source->refcount++;
+
+ wl_resource_post_event(target, WL_DATA_DEVICE_DATA_OFFER, resource);
+
+ end = source->mime_types.data + source->mime_types.size;
+ for (p = source->mime_types.data; p < end; p++)
+ wl_resource_post_event(resource, WL_DATA_OFFER_OFFER, *p);
+
+ return resource;
+}
+
+static void
+data_source_offer(struct wl_client *client,
+ struct wl_resource *resource,
+ const char *type)
+{
+ struct weston_data_source *source = resource->data;
+ char **p;
+
+ p = wl_array_add(&source->mime_types, sizeof *p);
+ if (p)
+ *p = strdup(type);
+ if (!p || !*p)
+ wl_resource_post_no_memory(resource);
+}
+
+static void
+data_source_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource, weston_compositor_get_time());
+}
+
+static struct wl_data_source_interface data_source_interface = {
+ data_source_offer,
+ data_source_destroy
+};
+
+static struct wl_resource *
+find_resource(struct wl_list *list, struct wl_client *client)
+{
+ struct wl_resource *r;
+
+ wl_list_for_each(r, list, link) {
+ if (r->client == client)
+ return r;
+ }
+
+ return NULL;
+}
+
+static void
+destroy_drag_focus(struct wl_listener *listener,
+ struct wl_resource *resource, uint32_t time)
+{
+ struct weston_input_device *device =
+ container_of(listener, struct weston_input_device,
+ drag_focus_listener);
+
+ device->drag_focus_resource = NULL;
+}
+
+static void
+drag_set_focus(struct weston_input_device *device,
+ struct wl_surface *surface, uint32_t time,
+ int32_t x, int32_t y)
+{
+ struct wl_resource *resource, *offer;
+
+ if (device->drag_focus == surface)
+ return;
+
+ if (device->drag_focus_resource) {
+ wl_resource_post_event(device->drag_focus_resource,
+ WL_DATA_DEVICE_LEAVE);
+ wl_list_remove(&device->drag_focus_listener.link);
+ device->drag_focus_resource = NULL;
+ device->drag_focus = NULL;
+ }
+
+ if (surface)
+ resource = find_resource(&device->drag_resource_list,
+ surface->resource.client);
+ if (surface && resource) {
+ offer = weston_data_source_send_offer(device->drag_data_source,
+ resource);
+
+ wl_resource_post_event(resource,
+ WL_DATA_DEVICE_ENTER,
+ time, surface, x, y, offer);
+
+ device->drag_focus = surface;
+ device->drag_focus_listener.func = destroy_drag_focus;
+ wl_list_insert(resource->destroy_listener_list.prev,
+ &device->drag_focus_listener.link);
+ device->drag_focus_resource = resource;
+ }
+}
+
+static void
+drag_grab_motion(struct wl_grab *grab,
+ uint32_t time, int32_t x, int32_t y)
+{
+ struct weston_input_device *device =
+ container_of(grab, struct weston_input_device, grab);
+ struct weston_surface *es;
+
+ es = pick_surface(&device->input_device, &x, &y);
+ drag_set_focus(device, &es->surface, time, x, y);
+
+ if (es && device->drag_focus_resource)
+ wl_resource_post_event(device->drag_focus_resource,
+ WL_DATA_DEVICE_MOTION, time, x, y);
+}
+
+static void
+drag_grab_button(struct wl_grab *grab,
+ uint32_t time, int32_t button, int32_t state)
+{
+}
+
+static void
+drag_grab_end(struct wl_grab *grab, uint32_t time)
+{
+ struct weston_input_device *device =
+ container_of(grab, struct weston_input_device, grab);
+
+ if (device->drag_focus_resource)
+ wl_resource_post_event(device->drag_focus_resource,
+ WL_DATA_DEVICE_DROP);
+
+ drag_set_focus(device, NULL, time, 0, 0);
+ weston_data_source_unref(device->drag_data_source);
+ device->drag_data_source = NULL;
+}
+
+static const struct wl_grab_interface drag_grab_interface = {
+ drag_grab_motion,
+ drag_grab_button,
+ drag_grab_end
+};
+
+static void
+data_device_start_drag(struct wl_client *client, struct wl_resource *resource,
+ struct wl_resource *source_resource,
+ struct wl_resource *surface_resource, uint32_t time)
+{
+ struct weston_input_device *device = resource->data;
+ struct weston_surface *surface = surface_resource->data;
+ struct weston_surface *target;
+ int32_t sx, sy;
+
+ /* FIXME: Check that client has implicit grab on the surface
+ * that matches the given time. */
+
+ /* FIXME: Check that the data source type array isn't empty. */
+
+ if (wl_input_device_update_grab(&device->input_device, &device->grab,
+ &surface->surface, time) < 0)
+ return;
+
+ device->grab.interface = &drag_grab_interface;
+ device->drag_data_source = source_resource->data;
+ device->drag_data_source->refcount++;
+
+ target = pick_surface(&device->input_device, &sx, &sy);
+ wl_input_device_set_pointer_focus(&device->input_device,
+ NULL, time, 0, 0, 0, 0);
+ drag_set_focus(device, &target->surface, time, sx, sy);
+}
+
+static void
+data_device_attach(struct wl_client *client, struct wl_resource *resource,
+ uint32_t time,
+ struct wl_resource *buffer, int32_t x, int32_t y)
+{
+}
+
+static void
+destroy_selection_data_source(struct wl_listener *listener,
+ struct wl_resource *resource, uint32_t time)
+{
+ struct weston_input_device *device =
+ container_of(listener, struct weston_input_device,
+ selection_data_source_listener);
+ struct wl_resource *data_device, *focus;
+
+ device->selection_data_source = NULL;
+
+ focus = device->input_device.keyboard_focus_resource;
+ if (focus) {
+ data_device = find_resource(&device->drag_resource_list,
+ focus->client);
+ wl_resource_post_event(data_device,
+ WL_DATA_DEVICE_SELECTION, NULL);
+ }
+}
+
+void
+weston_input_device_set_selection(struct weston_input_device *device,
+ struct weston_data_source *source, uint32_t time)
+{
+ struct wl_resource *data_device, *focus, *offer;
+
+ if (device->selection_data_source) {
+ device->selection_data_source->cancel(device->selection_data_source);
+ /* FIXME: All non-active clients will probably hold a
+ * reference to the selection data source, and thus it
+ * won't get destroyed until every client has been
+ * activated and seen the new selection event. */
+ wl_list_remove(&device->selection_data_source_listener.link);
+ weston_data_source_unref(device->selection_data_source);
+ device->selection_data_source = NULL;
+ }
+
+ device->selection_data_source = source;
+ source->refcount++;
+
+ focus = device->input_device.keyboard_focus_resource;
+ if (focus) {
+ data_device = find_resource(&device->drag_resource_list,
+ focus->client);
+ if (data_device) {
+ offer = weston_data_source_send_offer(device->selection_data_source,
+ data_device);
+ wl_resource_post_event(data_device,
+ WL_DATA_DEVICE_SELECTION,
+ offer);
+ }
+ }
+
+ weston_xserver_set_selection(device);
+
+ device->selection_data_source_listener.func =
+ destroy_selection_data_source;
+ wl_list_insert(source->resource.destroy_listener_list.prev,
+ &device->selection_data_source_listener.link);
+}
+
+static void
+data_device_set_selection(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *source_resource, uint32_t time)
+{
+ if (!source_resource)
+ return;
+
+ weston_input_device_set_selection(resource->data,
+ source_resource->data, time);
+}
+
+static const struct wl_data_device_interface data_device_interface = {
+ data_device_start_drag,
+ data_device_attach,
+ data_device_set_selection,
+};
+
+static void
+destroy_data_source(struct wl_resource *resource)
+{
+ struct weston_data_source *source =
+ container_of(resource, struct weston_data_source, resource);
+ char **p, **end;
+
+ end = source->mime_types.data + source->mime_types.size;
+ for (p = source->mime_types.data; p < end; p++)
+ free(*p);
+
+ wl_array_release(&source->mime_types);
+
+ source->resource.object.id = 0;
+ weston_data_source_unref(source);
+}
+
+static void
+create_data_source(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct weston_data_source *source;
+
+ source = malloc(sizeof *source);
+ if (source == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ source->resource.destroy = destroy_data_source;
+ source->resource.object.id = id;
+ source->resource.object.interface = &wl_data_source_interface;
+ source->resource.object.implementation =
+ (void (**)(void)) &data_source_interface;
+ source->resource.data = source;
+ source->create_offer = data_source_create_offer;
+ source->cancel = data_source_cancel;
+ source->refcount = 1;
+
+ wl_array_init(&source->mime_types);
+ wl_client_add_resource(client, &source->resource);
+}
+
+static void unbind_data_device(struct wl_resource *resource)
+{
+ wl_list_remove(&resource->link);
+ free(resource);
+}
+
+static void
+get_data_device(struct wl_client *client,
+ struct wl_resource *manager_resource,
+ uint32_t id, struct wl_resource *input_device)
+{
+ struct weston_input_device *device = input_device->data;
+ struct wl_resource *resource;
+
+ resource =
+ wl_client_add_object(client, &wl_data_device_interface,
+ &data_device_interface, id, device);
+
+ wl_list_insert(&device->drag_resource_list, &resource->link);
+ resource->destroy = unbind_data_device;
+}
+
+static const struct wl_data_device_manager_interface manager_interface = {
+ create_data_source,
+ get_data_device
+};
+
+static void
+bind_manager(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ wl_client_add_object(client, &wl_data_device_manager_interface,
+ &manager_interface, id, NULL);
+}
+
+WL_EXPORT void
+weston_data_device_set_keyboard_focus(struct weston_input_device *device)
+{
+ struct wl_resource *data_device, *focus, *offer;
+ struct weston_data_source *source;
+
+ focus = device->input_device.keyboard_focus_resource;
+ if (!focus)
+ return;
+
+ data_device = find_resource(&device->drag_resource_list,
+ focus->client);
+ if (!data_device)
+ return;
+
+ source = device->selection_data_source;
+ if (source) {
+ offer = weston_data_source_send_offer(source, data_device);
+ wl_resource_post_event(data_device,
+ WL_DATA_DEVICE_SELECTION, offer);
+ }
+}
+
+WL_EXPORT int
+weston_data_device_manager_init(struct weston_compositor *compositor)
+{
+ if (wl_display_add_global(compositor->wl_display,
+ &wl_data_device_manager_interface,
+ NULL, bind_manager) == NULL)
+ return -1;
+
+ return 0;
+}
diff --git a/src/evdev.c b/src/evdev.c
new file mode 100644
index 0000000..0b2fdfa
--- /dev/null
+++ b/src/evdev.c
@@ -0,0 +1,653 @@
+/*
+ * Copyright © 2010 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 <linux/input.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "compositor.h"
+#include "evdev.h"
+
+struct evdev_input {
+ struct weston_input_device base;
+ struct wl_list devices_list;
+ struct udev_monitor *udev_monitor;
+ char *seat_id;
+};
+
+#define MAX_SLOTS 16
+
+struct evdev_input_device {
+ struct evdev_input *master;
+ struct wl_list link;
+ struct wl_event_source *source;
+ struct weston_output *output;
+ char *devnode;
+ int fd;
+ struct {
+ int min_x, max_x, min_y, max_y;
+ int old_x, old_y, reset_x, reset_y;
+ int32_t x, y;
+ } abs;
+
+ struct {
+ int slot;
+ int32_t x[MAX_SLOTS];
+ int32_t y[MAX_SLOTS];
+ } mt;
+
+ struct {
+ int dx, dy;
+ } rel;
+
+ int type; /* event type flags */
+
+ int is_touchpad, is_mt;
+};
+
+/* event type flags */
+#define EVDEV_ABSOLUTE_MOTION (1 << 0)
+#define EVDEV_ABSOLUTE_MT_DOWN (1 << 1)
+#define EVDEV_ABSOLUTE_MT_MOTION (1 << 2)
+#define EVDEV_ABSOLUTE_MT_UP (1 << 3)
+#define EVDEV_RELATIVE_MOTION (1 << 4)
+
+static inline void
+evdev_process_key(struct evdev_input_device *device,
+ struct input_event *e, int time)
+{
+ if (e->value == 2)
+ return;
+
+ switch (e->code) {
+ case BTN_TOOL_PEN:
+ case BTN_TOOL_RUBBER:
+ case BTN_TOOL_BRUSH:
+ case BTN_TOOL_PENCIL:
+ case BTN_TOOL_AIRBRUSH:
+ case BTN_TOOL_FINGER:
+ case BTN_TOOL_MOUSE:
+ case BTN_TOOL_LENS:
+ if (device->is_touchpad)
+ {
+ device->abs.reset_x = 1;
+ device->abs.reset_y = 1;
+ }
+ break;
+ case BTN_TOUCH:
+ /* Multitouch touchscreen devices might not send individually
+ * button events each time a new finger is down. So we don't
+ * send notification for such devices and we solve the button
+ * case emulating on compositor side. */
+ if (device->is_mt)
+ break;
+
+ /* Treat BTN_TOUCH from devices that only have BTN_TOUCH as
+ * BTN_LEFT */
+ e->code = BTN_LEFT;
+ /* Intentional fallthrough! */
+ case BTN_LEFT:
+ case BTN_RIGHT:
+ case BTN_MIDDLE:
+ case BTN_SIDE:
+ case BTN_EXTRA:
+ case BTN_FORWARD:
+ case BTN_BACK:
+ case BTN_TASK:
+ notify_button(&device->master->base.input_device,
+ time, e->code, e->value);
+ break;
+
+ default:
+ notify_key(&device->master->base.input_device,
+ time, e->code, e->value);
+ break;
+ }
+}
+
+static void
+evdev_process_touch(struct evdev_input_device *device,
+ struct input_event *e)
+{
+ const int screen_width = device->output->current->width;
+ const int screen_height = device->output->current->height;
+
+ switch (e->code) {
+ case ABS_MT_SLOT:
+ device->mt.slot = e->value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ if (e->value >= 0)
+ device->type |= EVDEV_ABSOLUTE_MT_DOWN;
+ else
+ device->type |= EVDEV_ABSOLUTE_MT_UP;
+ break;
+ case ABS_MT_POSITION_X:
+ device->mt.x[device->mt.slot] =
+ (e->value - device->abs.min_x) * screen_width /
+ (device->abs.max_x - device->abs.min_x) +
+ device->output->x;
+ device->type |= EVDEV_ABSOLUTE_MT_MOTION;
+ break;
+ case ABS_MT_POSITION_Y:
+ device->mt.y[device->mt.slot] =
+ (e->value - device->abs.min_y) * screen_height /
+ (device->abs.max_y - device->abs.min_y) +
+ device->output->y;
+ device->type |= EVDEV_ABSOLUTE_MT_MOTION;
+ break;
+ }
+}
+
+static inline void
+evdev_process_absolute_motion(struct evdev_input_device *device,
+ struct input_event *e)
+{
+ const int screen_width = device->output->current->width;
+ const int screen_height = device->output->current->height;
+
+ switch (e->code) {
+ case ABS_X:
+ device->abs.x =
+ (e->value - device->abs.min_x) * screen_width /
+ (device->abs.max_x - device->abs.min_x) +
+ device->output->x;
+ device->type |= EVDEV_ABSOLUTE_MOTION;
+ break;
+ case ABS_Y:
+ device->abs.y =
+ (e->value - device->abs.min_y) * screen_height /
+ (device->abs.max_y - device->abs.min_y) +
+ device->output->y;
+ device->type |= EVDEV_ABSOLUTE_MOTION;
+ break;
+ }
+}
+
+static inline void
+evdev_process_absolute_motion_touchpad(struct evdev_input_device *device,
+ struct input_event *e)
+{
+ /* FIXME: Make this configurable somehow. */
+ const int touchpad_speed = 700;
+
+ switch (e->code) {
+ case ABS_X:
+ e->value -= device->abs.min_x;
+ if (device->abs.reset_x)
+ device->abs.reset_x = 0;
+ else {
+ device->rel.dx =
+ (e->value - device->abs.old_x) *
+ touchpad_speed /
+ (device->abs.max_x - device->abs.min_x);
+ }
+ device->abs.old_x = e->value;
+ device->type |= EVDEV_RELATIVE_MOTION;
+ break;
+ case ABS_Y:
+ e->value -= device->abs.min_y;
+ if (device->abs.reset_y)
+ device->abs.reset_y = 0;
+ else {
+ device->rel.dy =
+ (e->value - device->abs.old_y) *
+ touchpad_speed /
+ /* maybe use x size here to have the same scale? */
+ (device->abs.max_y - device->abs.min_y);
+ }
+ device->abs.old_y = e->value;
+ device->type |= EVDEV_RELATIVE_MOTION;
+ break;
+ }
+}
+
+static inline void
+evdev_process_relative_motion(struct evdev_input_device *device,
+ struct input_event *e)
+{
+ switch (e->code) {
+ case REL_X:
+ device->rel.dx += e->value;
+ device->type |= EVDEV_RELATIVE_MOTION;
+ break;
+ case REL_Y:
+ device->rel.dy += e->value;
+ device->type |= EVDEV_RELATIVE_MOTION;
+ break;
+ }
+}
+
+static inline void
+evdev_process_absolute(struct evdev_input_device *device,
+ struct input_event *e)
+{
+ if (device->is_touchpad) {
+ evdev_process_absolute_motion_touchpad(device, e);
+ } else if (device->is_mt) {
+ evdev_process_touch(device, e);
+ } else {
+ evdev_process_absolute_motion(device, e);
+ }
+}
+
+static int
+is_motion_event(struct input_event *e)
+{
+ switch (e->type) {
+ case EV_REL:
+ switch (e->code) {
+ case REL_X:
+ case REL_Y:
+ return 1;
+ }
+ case EV_ABS:
+ switch (e->code) {
+ case ABS_X:
+ case ABS_Y:
+ case ABS_MT_POSITION_X:
+ case ABS_MT_POSITION_Y:
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+evdev_flush_motion(struct evdev_input_device *device, uint32_t time)
+{
+ struct wl_input_device *master = &device->master->base.input_device;
+
+ if (!device->type)
+ return;
+
+ if (device->type & EVDEV_RELATIVE_MOTION) {
+ notify_motion(master, time,
+ master->x + device->rel.dx,
+ master->y + device->rel.dy);
+ device->type &= ~EVDEV_RELATIVE_MOTION;
+ device->rel.dx = 0;
+ device->rel.dy = 0;
+ }
+ if (device->type & EVDEV_ABSOLUTE_MT_DOWN) {
+ notify_touch(master, time,
+ device->mt.slot,
+ device->mt.x[device->mt.slot],
+ device->mt.y[device->mt.slot],
+ WL_INPUT_DEVICE_TOUCH_DOWN);
+ device->type &= ~EVDEV_ABSOLUTE_MT_DOWN;
+ device->type &= ~EVDEV_ABSOLUTE_MT_MOTION;
+ }
+ if (device->type & EVDEV_ABSOLUTE_MT_MOTION) {
+ notify_touch(master, time,
+ device->mt.slot,
+ device->mt.x[device->mt.slot],
+ device->mt.y[device->mt.slot],
+ WL_INPUT_DEVICE_TOUCH_MOTION);
+ device->type &= ~EVDEV_ABSOLUTE_MT_DOWN;
+ device->type &= ~EVDEV_ABSOLUTE_MT_MOTION;
+ }
+ if (device->type & EVDEV_ABSOLUTE_MT_UP) {
+ notify_touch(master, time, device->mt.slot, 0, 0,
+ WL_INPUT_DEVICE_TOUCH_UP);
+ device->type &= ~EVDEV_ABSOLUTE_MT_UP;
+ }
+ if (device->type & EVDEV_ABSOLUTE_MOTION) {
+ notify_motion(master, time, device->abs.x, device->abs.y);
+ device->type &= ~EVDEV_ABSOLUTE_MOTION;
+ }
+}
+
+static int
+evdev_input_device_data(int fd, uint32_t mask, void *data)
+{
+ struct weston_compositor *ec;
+ struct evdev_input_device *device = data;
+ struct input_event ev[8], *e, *end;
+ int len;
+ uint32_t time = 0;
+
+ ec = device->master->base.compositor;
+ if (!ec->focus)
+ return 1;
+
+ len = read(fd, &ev, sizeof ev);
+ if (len < 0 || len % sizeof e[0] != 0) {
+ /* FIXME: call device_removed when errno is ENODEV. */;
+ return 1;
+ }
+
+ e = ev;
+ end = (void *) ev + len;
+ for (e = ev; e < end; e++) {
+ time = e->time.tv_sec * 1000 + e->time.tv_usec / 1000;
+
+ /* we try to minimize the amount of notifications to be
+ * forwarded to the compositor, so we accumulate motion
+ * events and send as a bunch */
+ if (!is_motion_event(e))
+ evdev_flush_motion(device, time);
+ switch (e->type) {
+ case EV_REL:
+ evdev_process_relative_motion(device, e);
+ break;
+ case EV_ABS:
+ evdev_process_absolute(device, e);
+ break;
+ case EV_KEY:
+ evdev_process_key(device, e, time);
+ break;
+ }
+ }
+
+ evdev_flush_motion(device, time);
+
+ return 1;
+}
+
+
+/* copied from udev/extras/input_id/input_id.c */
+/* we must use this kernel-compatible implementation */
+#define BITS_PER_LONG (sizeof(unsigned long) * 8)
+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+#define OFF(x) ((x)%BITS_PER_LONG)
+#define BIT(x) (1UL<<OFF(x))
+#define LONG(x) ((x)/BITS_PER_LONG)
+#define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
+/* end copied */
+
+static int
+evdev_configure_device(struct evdev_input_device *device)
+{
+ struct input_absinfo absinfo;
+ unsigned long ev_bits[NBITS(EV_MAX)];
+ unsigned long abs_bits[NBITS(ABS_MAX)];
+ unsigned long key_bits[NBITS(KEY_MAX)];
+ int has_key, has_abs;
+
+ has_key = 0;
+ has_abs = 0;
+
+ ioctl(device->fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits);
+ if (TEST_BIT(ev_bits, EV_ABS)) {
+ has_abs = 1;
+
+ ioctl(device->fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)),
+ abs_bits);
+ if (TEST_BIT(abs_bits, ABS_X)) {
+ ioctl(device->fd, EVIOCGABS(ABS_X), &absinfo);
+ device->abs.min_x = absinfo.minimum;
+ device->abs.max_x = absinfo.maximum;
+ }
+ if (TEST_BIT(abs_bits, ABS_Y)) {
+ ioctl(device->fd, EVIOCGABS(ABS_Y), &absinfo);
+ device->abs.min_y = absinfo.minimum;
+ device->abs.max_y = absinfo.maximum;
+ }
+ if (TEST_BIT(abs_bits, ABS_MT_SLOT)) {
+ device->is_mt = 1;
+ device->mt.slot = 0;
+ }
+ }
+ if (TEST_BIT(ev_bits, EV_KEY)) {
+ has_key = 1;
+ ioctl(device->fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)),
+ key_bits);
+ if (TEST_BIT(key_bits, BTN_TOOL_FINGER) &&
+ !TEST_BIT(key_bits, BTN_TOOL_PEN))
+ device->is_touchpad = 1;
+ }
+
+ /* This rule tries to catch accelerometer devices and opt out. We may
+ * want to adjust the protocol later adding a proper event for dealing
+ * with accelerometers and implement here accordingly */
+ if (has_abs && !has_key)
+ return -1;
+
+ return 0;
+}
+
+static struct evdev_input_device *
+evdev_input_device_create(struct evdev_input *master,
+ struct wl_display *display, const char *path)
+{
+ struct evdev_input_device *device;
+ struct wl_event_loop *loop;
+ struct weston_compositor *ec;
+
+ device = malloc(sizeof *device);
+ if (device == NULL)
+ return NULL;
+
+ ec = master->base.compositor;
+ device->output =
+ container_of(ec->output_list.next, struct weston_output, link);
+
+ device->master = master;
+ device->is_touchpad = 0;
+ device->is_mt = 0;
+ device->devnode = strdup(path);
+ device->mt.slot = -1;
+ device->rel.dx = 0;
+ device->rel.dy = 0;
+
+ device->fd = open(path, O_RDONLY);
+ if (device->fd < 0)
+ goto err0;
+
+ if (evdev_configure_device(device) == -1)
+ goto err1;
+
+ loop = wl_display_get_event_loop(display);
+ device->source = wl_event_loop_add_fd(loop, device->fd,
+ WL_EVENT_READABLE,
+ evdev_input_device_data, device);
+ if (device->source == NULL)
+ goto err1;
+
+ wl_list_insert(master->devices_list.prev, &device->link);
+
+ return device;
+
+err1:
+ close(device->fd);
+err0:
+ free(device->devnode);
+ free(device);
+ return NULL;
+}
+
+static const char default_seat[] = "seat0";
+
+static void
+device_added(struct udev_device *udev_device, struct evdev_input *master)
+{
+ struct weston_compositor *c;
+ const char *devnode;
+ const char *device_seat;
+
+ device_seat = udev_device_get_property_value(udev_device, "ID_SEAT");
+ if (!device_seat)
+ device_seat = default_seat;
+
+ if (strcmp(device_seat, master->seat_id))
+ return;
+
+ c = master->base.compositor;
+ devnode = udev_device_get_devnode(udev_device);
+ if (evdev_input_device_create(master, c->wl_display, devnode))
+ fprintf(stderr, "evdev input device: added: %s\n", devnode);
+}
+
+static void
+device_removed(struct udev_device *udev_device, struct evdev_input *master)
+{
+ const char *devnode = udev_device_get_devnode(udev_device);
+ struct evdev_input_device *device, *next;
+
+ wl_list_for_each_safe(device, next, &master->devices_list, link) {
+ if (!strcmp(device->devnode, devnode)) {
+ wl_event_source_remove(device->source);
+ wl_list_remove(&device->link);
+ close(device->fd);
+ free(device->devnode);
+ free(device);
+ break;
+ }
+ }
+ fprintf(stderr, "evdev input device: removed: %s\n", devnode);
+}
+
+void
+evdev_add_devices(struct udev *udev, struct weston_input_device *input_base)
+{
+ struct evdev_input *input = (struct evdev_input *) input_base;
+ struct udev_enumerate *e;
+ struct udev_list_entry *entry;
+ struct udev_device *device;
+ const char *path;
+
+ e = udev_enumerate_new(udev);
+ udev_enumerate_add_match_subsystem(e, "input");
+ udev_enumerate_scan_devices(e);
+ udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
+ path = udev_list_entry_get_name(entry);
+ device = udev_device_new_from_syspath(udev, path);
+
+ if (strncmp("event", udev_device_get_sysname(device), 5) != 0)
+ continue;
+
+ device_added(device, input);
+
+ udev_device_unref(device);
+ }
+ udev_enumerate_unref(e);
+}
+
+static int
+evdev_udev_handler(int fd, uint32_t mask, void *data)
+{
+ struct evdev_input *master = data;
+ struct udev_device *udev_device;
+ const char *action;
+
+ udev_device = udev_monitor_receive_device(master->udev_monitor);
+ if (!udev_device)
+ return 1;
+
+ action = udev_device_get_action(udev_device);
+ if (action) {
+ if (strncmp("event", udev_device_get_sysname(udev_device), 5) != 0)
+ return 0;
+
+ if (!strcmp(action, "add")) {
+ device_added(udev_device, master);
+ }
+ else if (!strcmp(action, "remove"))
+ device_removed(udev_device, master);
+ }
+ udev_device_unref(udev_device);
+
+ return 0;
+}
+
+static int
+evdev_config_udev_monitor(struct udev *udev, struct evdev_input *master)
+{
+ struct wl_event_loop *loop;
+ struct weston_compositor *c = master->base.compositor;
+
+ master->udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
+ if (!master->udev_monitor)
+ return 0;
+
+ udev_monitor_filter_add_match_subsystem_devtype(master->udev_monitor,
+ "input", NULL);
+
+ if (udev_monitor_enable_receiving(master->udev_monitor)) {
+ fprintf(stderr, "udev: failed to bind the udev monitor\n");
+ return 0;
+ }
+
+ loop = wl_display_get_event_loop(c->wl_display);
+ wl_event_loop_add_fd(loop, udev_monitor_get_fd(master->udev_monitor),
+ WL_EVENT_READABLE, evdev_udev_handler, master);
+
+ return 1;
+}
+
+void
+evdev_input_create(struct weston_compositor *c, struct udev *udev,
+ const char *seat)
+{
+ struct evdev_input *input;
+
+ input = malloc(sizeof *input);
+ if (input == NULL)
+ return;
+
+ memset(input, 0, sizeof *input);
+ weston_input_device_init(&input->base, c);
+
+ wl_list_init(&input->devices_list);
+ input->seat_id = strdup(seat);
+ if (!evdev_config_udev_monitor(udev, input)) {
+ free(input->seat_id);
+ free(input);
+ return;
+ }
+
+ evdev_add_devices(udev, &input->base);
+
+ c->input_device = &input->base.input_device;
+}
+
+void
+evdev_remove_devices(struct weston_input_device *input_base)
+{
+ struct evdev_input *input = (struct evdev_input *) input_base;
+ struct evdev_input_device *device, *next;
+
+ wl_list_for_each_safe(device, next, &input->devices_list, link) {
+ fprintf(stderr, "evdev input device: removed: %s\n", device->devnode);
+ wl_event_source_remove(device->source);
+ wl_list_remove(&device->link);
+ close(device->fd);
+ free(device->devnode);
+ free(device);
+ }
+}
+
+void
+evdev_input_destroy(struct weston_input_device *input_base)
+{
+ struct evdev_input *input = (struct evdev_input *) input_base;
+
+ evdev_remove_devices(input_base);
+ wl_list_remove(&input->base.link);
+ free(input->seat_id);
+ free(input);
+}
diff --git a/src/evdev.h b/src/evdev.h
new file mode 100644
index 0000000..af5f6d9
--- /dev/null
+++ b/src/evdev.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2011 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.
+ */
+
+void
+evdev_add_devices(struct udev *udev, struct weston_input_device
+ *input_base);
+
+void
+evdev_remove_devices(struct weston_input_device *input_base);
+
+void
+evdev_input_create(struct weston_compositor *c, struct udev *udev,
+ const char *seat);
+
+void
+evdev_input_destroy(struct weston_input_device *input_base);
diff --git a/src/hash.c b/src/hash.c
new file mode 100644
index 0000000..1dbee82
--- /dev/null
+++ b/src/hash.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright © 2009 Intel Corporation
+ * Copyright © 1988-2004 Keith Packard and Bart Massey.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the authors
+ * or their institutions shall not be used in advertising or
+ * otherwise to promote the sale, use or other dealings in this
+ * Software without prior written authorization from the
+ * authors.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ * Keith Packard <keithp@keithp.com>
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "hash.h"
+
+struct hash_entry {
+ uint32_t hash;
+ void *data;
+};
+
+struct hash_table {
+ struct hash_entry *table;
+ uint32_t size;
+ uint32_t rehash;
+ uint32_t max_entries;
+ uint32_t size_index;
+ uint32_t entries;
+ uint32_t deleted_entries;
+};
+
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+
+/*
+ * From Knuth -- a good choice for hash/rehash values is p, p-2 where
+ * p and p-2 are both prime. These tables are sized to have an extra 10%
+ * free to avoid exponential performance degradation as the hash table fills
+ */
+
+static const uint32_t deleted_data;
+
+static const struct {
+ uint32_t max_entries, size, rehash;
+} hash_sizes[] = {
+ { 2, 5, 3 },
+ { 4, 7, 5 },
+ { 8, 13, 11 },
+ { 16, 19, 17 },
+ { 32, 43, 41 },
+ { 64, 73, 71 },
+ { 128, 151, 149 },
+ { 256, 283, 281 },
+ { 512, 571, 569 },
+ { 1024, 1153, 1151 },
+ { 2048, 2269, 2267 },
+ { 4096, 4519, 4517 },
+ { 8192, 9013, 9011 },
+ { 16384, 18043, 18041 },
+ { 32768, 36109, 36107 },
+ { 65536, 72091, 72089 },
+ { 131072, 144409, 144407 },
+ { 262144, 288361, 288359 },
+ { 524288, 576883, 576881 },
+ { 1048576, 1153459, 1153457 },
+ { 2097152, 2307163, 2307161 },
+ { 4194304, 4613893, 4613891 },
+ { 8388608, 9227641, 9227639 },
+ { 16777216, 18455029, 18455027 },
+ { 33554432, 36911011, 36911009 },
+ { 67108864, 73819861, 73819859 },
+ { 134217728, 147639589, 147639587 },
+ { 268435456, 295279081, 295279079 },
+ { 536870912, 590559793, 590559791 },
+ { 1073741824, 1181116273, 1181116271},
+ { 2147483648ul, 2362232233ul, 2362232231ul}
+};
+
+static int
+entry_is_free(struct hash_entry *entry)
+{
+ return entry->data == NULL;
+}
+
+static int
+entry_is_deleted(struct hash_entry *entry)
+{
+ return entry->data == &deleted_data;
+}
+
+static int
+entry_is_present(struct hash_entry *entry)
+{
+ return entry->data != NULL && entry->data != &deleted_data;
+}
+
+struct hash_table *
+hash_table_create(void)
+{
+ struct hash_table *ht;
+
+ ht = malloc(sizeof(*ht));
+ if (ht == NULL)
+ return NULL;
+
+ ht->size_index = 0;
+ ht->size = hash_sizes[ht->size_index].size;
+ ht->rehash = hash_sizes[ht->size_index].rehash;
+ ht->max_entries = hash_sizes[ht->size_index].max_entries;
+ ht->table = calloc(ht->size, sizeof(*ht->table));
+ ht->entries = 0;
+ ht->deleted_entries = 0;
+
+ if (ht->table == NULL) {
+ free(ht);
+ return NULL;
+ }
+
+ return ht;
+}
+
+/**
+ * Frees the given hash table.
+ */
+void
+hash_table_destroy(struct hash_table *ht)
+{
+ if (!ht)
+ return;
+
+ free(ht->table);
+ free(ht);
+}
+
+/**
+ * Finds a hash table entry with the given key and hash of that key.
+ *
+ * Returns NULL if no entry is found. Note that the data pointer may be
+ * modified by the user.
+ */
+static void *
+hash_table_search(struct hash_table *ht, uint32_t hash)
+{
+ uint32_t hash_address;
+
+ hash_address = hash % ht->size;
+ do {
+ uint32_t double_hash;
+
+ struct hash_entry *entry = ht->table + hash_address;
+
+ if (entry_is_free(entry)) {
+ return NULL;
+ } else if (entry_is_present(entry) && entry->hash == hash) {
+ return entry;
+ }
+
+ double_hash = 1 + hash % ht->rehash;
+
+ hash_address = (hash_address + double_hash) % ht->size;
+ } while (hash_address != hash % ht->size);
+
+ return NULL;
+}
+
+void
+hash_table_for_each(struct hash_table *ht,
+ hash_table_iterator_func_t func, void *data)
+{
+ struct hash_entry *entry;
+ uint32_t i;
+
+ for (i = 0; i < ht->size; i++) {
+ entry = ht->table + i;
+ if (entry_is_present(entry))
+ func(entry->data, data);
+ }
+}
+
+void *
+hash_table_lookup(struct hash_table *ht, uint32_t hash)
+{
+ struct hash_entry *entry;
+
+ entry = hash_table_search(ht, hash);
+ if (entry != NULL)
+ return entry->data;
+
+ return NULL;
+}
+
+static void
+hash_table_rehash(struct hash_table *ht, int new_size_index)
+{
+ struct hash_table old_ht;
+ struct hash_entry *table, *entry;
+
+ if (new_size_index >= ARRAY_SIZE(hash_sizes))
+ return;
+
+ table = calloc(hash_sizes[new_size_index].size, sizeof(*ht->table));
+ if (table == NULL)
+ return;
+
+ old_ht = *ht;
+
+ ht->table = table;
+ ht->size_index = new_size_index;
+ ht->size = hash_sizes[ht->size_index].size;
+ ht->rehash = hash_sizes[ht->size_index].rehash;
+ ht->max_entries = hash_sizes[ht->size_index].max_entries;
+ ht->entries = 0;
+ ht->deleted_entries = 0;
+
+ for (entry = old_ht.table;
+ entry != old_ht.table + old_ht.size;
+ entry++) {
+ if (entry_is_present(entry)) {
+ hash_table_insert(ht, entry->hash, entry->data);
+ }
+ }
+
+ free(old_ht.table);
+}
+
+/**
+ * Inserts the data with the given hash into the table.
+ *
+ * Note that insertion may rearrange the table on a resize or rehash,
+ * so previously found hash_entries are no longer valid after this function.
+ */
+int
+hash_table_insert(struct hash_table *ht, uint32_t hash, void *data)
+{
+ uint32_t hash_address;
+
+ if (ht->entries >= ht->max_entries) {
+ hash_table_rehash(ht, ht->size_index + 1);
+ } else if (ht->deleted_entries + ht->entries >= ht->max_entries) {
+ hash_table_rehash(ht, ht->size_index);
+ }
+
+ hash_address = hash % ht->size;
+ do {
+ struct hash_entry *entry = ht->table + hash_address;
+ uint32_t double_hash;
+
+ if (!entry_is_present(entry)) {
+ if (entry_is_deleted(entry))
+ ht->deleted_entries--;
+ entry->hash = hash;
+ entry->data = data;
+ ht->entries++;
+ return 0;
+ }
+
+ double_hash = 1 + hash % ht->rehash;
+
+ hash_address = (hash_address + double_hash) % ht->size;
+ } while (hash_address != hash % ht->size);
+
+ /* We could hit here if a required resize failed. An unchecked-malloc
+ * application could ignore this result.
+ */
+ return -1;
+}
+
+/**
+ * This function deletes the given hash table entry.
+ *
+ * Note that deletion doesn't otherwise modify the table, so an iteration over
+ * the table deleting entries is safe.
+ */
+void
+hash_table_remove(struct hash_table *ht, uint32_t hash)
+{
+ struct hash_entry *entry;
+
+ entry = hash_table_search(ht, hash);
+ if (entry != NULL) {
+ entry->data = (void *) &deleted_data;
+ ht->entries--;
+ ht->deleted_entries++;
+ }
+}
diff --git a/src/hash.h b/src/hash.h
new file mode 100644
index 0000000..6e1674e
--- /dev/null
+++ b/src/hash.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2009 Intel Corporation
+ * Copyright © 1988-2004 Keith Packard and Bart Massey.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the authors
+ * or their institutions shall not be used in advertising or
+ * otherwise to promote the sale, use or other dealings in this
+ * Software without prior written authorization from the
+ * authors.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ * Keith Packard <keithp@keithp.com>
+ */
+
+#ifndef HASH_H
+#define HASH_H
+
+struct hash_table;
+struct hash_table *hash_table_create(void);
+typedef void (*hash_table_iterator_func_t)(void *element, void *data);
+
+void hash_table_destroy(struct hash_table *ht);
+void *hash_table_lookup(struct hash_table *ht, uint32_t hash);
+int hash_table_insert(struct hash_table *ht, uint32_t hash, void *data);
+void hash_table_remove(struct hash_table *ht, uint32_t hash);
+void hash_table_for_each(struct hash_table *ht,
+ hash_table_iterator_func_t func, void *data);
+
+#endif
diff --git a/src/image-loader.c b/src/image-loader.c
new file mode 100644
index 0000000..f6dec25
--- /dev/null
+++ b/src/image-loader.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright © 2011 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 <stdint.h>
+#include <errno.h>
+#include <png.h>
+
+#include "compositor.h"
+
+static inline int
+multiply_alpha(int alpha, int color)
+{
+ int temp = (alpha * color) + 0x80;
+
+ return ((temp + (temp >> 8)) >> 8);
+}
+
+static void
+premultiply_data(png_structp png,
+ png_row_infop row_info,
+ png_bytep data)
+{
+ unsigned int i;
+ png_bytep p;
+
+ for (i = 0, p = data; i < row_info->rowbytes; i += 4, p += 4) {
+ png_byte alpha = p[3];
+ uint32_t w;
+
+ if (alpha == 0) {
+ w = 0;
+ } else {
+ png_byte red = p[0];
+ png_byte green = p[1];
+ png_byte blue = p[2];
+
+ if (alpha != 0xff) {
+ red = multiply_alpha(alpha, red);
+ green = multiply_alpha(alpha, green);
+ blue = multiply_alpha(alpha, blue);
+ }
+ w = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
+ }
+
+ * (uint32_t *) p = w;
+ }
+}
+
+static void
+read_func(png_structp png, png_bytep data, png_size_t size)
+{
+ FILE *fp = png_get_io_ptr(png);
+
+ if (fread(data, 1, size, fp) < 0)
+ png_error(png, NULL);
+}
+
+WL_EXPORT uint32_t *
+wlsc_load_image(const char *filename,
+ int32_t *width_arg, int32_t *height_arg, uint32_t *stride_arg)
+{
+ png_struct *png;
+ png_info *info;
+ png_byte *data;
+ png_byte **row_pointers = NULL;
+ png_uint_32 width, height;
+ int depth, color_type, interlace, stride;
+ unsigned int i;
+ FILE *fp;
+
+ fp = fopen(filename, "rb");
+ if (fp == NULL)
+ return NULL;
+
+ png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL);
+ if (!png) {
+ fclose(fp);
+ return NULL;
+ }
+
+ info = png_create_info_struct(png);
+ if (!info) {
+ png_destroy_read_struct(&png, &info, NULL);
+ fclose(fp);
+ return NULL;
+ }
+
+ png_set_read_fn(png, fp, read_func);
+ png_read_info(png, info);
+ png_get_IHDR(png, info,
+ &width, &height, &depth,
+ &color_type, &interlace, NULL, NULL);
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ png_set_palette_to_rgb(png);
+
+ if (color_type == PNG_COLOR_TYPE_GRAY)
+ png_set_expand_gray_1_2_4_to_8(png);
+
+ if (png_get_valid(png, info, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha(png);
+
+ if (depth == 16)
+ png_set_strip_16(png);
+
+ if (depth < 8)
+ png_set_packing(png);
+
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png);
+
+ if (interlace != PNG_INTERLACE_NONE)
+ png_set_interlace_handling(png);
+
+ png_set_filler(png, 0xff, PNG_FILLER_AFTER);
+ png_set_read_user_transform_fn(png, premultiply_data);
+ png_read_update_info(png, info);
+ png_get_IHDR(png, info,
+ &width, &height, &depth,
+ &color_type, &interlace, NULL, NULL);
+
+ stride = width * 4;
+ data = malloc(stride * height);
+ if (!data) {
+ png_destroy_read_struct(&png, &info, NULL);
+ fclose(fp);
+ return NULL;
+ }
+
+ row_pointers = malloc(height * sizeof row_pointers[0]);
+ if (row_pointers == NULL) {
+ free(data);
+ png_destroy_read_struct(&png, &info, NULL);
+ fclose(fp);
+ return NULL;
+ }
+
+ for (i = 0; i < height; i++)
+ row_pointers[i] = &data[i * stride];
+
+ png_read_image(png, row_pointers);
+ png_read_end(png, info);
+
+ free(row_pointers);
+ png_destroy_read_struct(&png, &info, NULL);
+ fclose(fp);
+
+ *width_arg = width;
+ *height_arg = height;
+ *stride_arg = stride;
+
+ return (uint32_t *) data;
+}
diff --git a/src/screenshooter.c b/src/screenshooter.c
new file mode 100644
index 0000000..191a77f
--- /dev/null
+++ b/src/screenshooter.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ *
+ * 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 "compositor.h"
+#include "screenshooter-server-protocol.h"
+
+struct screenshooter {
+ struct wl_object base;
+ struct weston_compositor *ec;
+};
+
+static void
+screenshooter_shoot(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *output_resource,
+ struct wl_resource *buffer_resource)
+{
+ struct weston_output *output = output_resource->data;
+ struct wl_buffer *buffer = buffer_resource->data;
+
+ if (!wl_buffer_is_shm(buffer))
+ return;
+
+ if (buffer->width < output->current->width ||
+ buffer->height < output->current->height)
+ return;
+
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+ glReadPixels(0, 0, output->current->width, output->current->height,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ wl_shm_buffer_get_data(buffer));
+}
+
+struct screenshooter_interface screenshooter_implementation = {
+ screenshooter_shoot
+};
+
+static void
+bind_shooter(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ wl_client_add_object(client, &screenshooter_interface,
+ &screenshooter_implementation, id, data);
+}
+
+void
+screenshooter_create(struct weston_compositor *ec)
+{
+ struct screenshooter *shooter;
+
+ shooter = malloc(sizeof *shooter);
+ if (shooter == NULL)
+ return;
+
+ shooter->base.interface = &screenshooter_interface;
+ shooter->base.implementation =
+ (void(**)(void)) &screenshooter_implementation;
+ shooter->ec = ec;
+
+ wl_display_add_global(ec->wl_display,
+ &screenshooter_interface, shooter, bind_shooter);
+};
diff --git a/src/shell.c b/src/shell.c
new file mode 100644
index 0000000..5274d52
--- /dev/null
+++ b/src/shell.c
@@ -0,0 +1,1296 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2011 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 <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/input.h>
+#include <assert.h>
+#include <signal.h>
+
+#include <wayland-server.h>
+#include "compositor.h"
+#include "desktop-shell-server-protocol.h"
+#include "../shared/config-parser.h"
+
+struct shell_surface;
+
+struct wl_shell {
+ struct weston_compositor *compositor;
+ struct weston_shell shell;
+
+ struct {
+ struct weston_process process;
+ struct wl_client *client;
+ struct wl_resource *desktop_shell;
+ } child;
+
+ bool locked;
+ bool prepare_event_sent;
+
+ struct shell_surface *lock_surface;
+ struct wl_listener lock_surface_listener;
+ struct wl_list hidden_surface_list;
+
+ struct wl_list backgrounds;
+ struct wl_list panels;
+
+ struct {
+ const char *path;
+ int duration;
+ struct wl_resource *binding;
+ struct wl_list surfaces;
+ struct weston_process process;
+ } screensaver;
+};
+
+enum shell_surface_type {
+ SHELL_SURFACE_NONE,
+
+ SHELL_SURFACE_PANEL,
+ SHELL_SURFACE_BACKGROUND,
+ SHELL_SURFACE_LOCK,
+ SHELL_SURFACE_SCREENSAVER,
+
+ SHELL_SURFACE_TOPLEVEL,
+ SHELL_SURFACE_TRANSIENT,
+ SHELL_SURFACE_FULLSCREEN
+};
+
+struct shell_surface {
+ struct wl_resource resource;
+
+ struct weston_surface *surface;
+ struct wl_listener surface_destroy_listener;
+
+ enum shell_surface_type type;
+ int32_t saved_x, saved_y;
+
+ struct weston_output *output;
+ struct wl_list link;
+};
+
+struct weston_move_grab {
+ struct wl_grab grab;
+ struct weston_surface *surface;
+ int32_t dx, dy;
+};
+
+static int
+shell_configuration(struct wl_shell *shell)
+{
+ int ret;
+ char *config_file;
+ char *path = NULL;
+ int duration = 60;
+
+ struct config_key saver_keys[] = {
+ { "path", CONFIG_KEY_STRING, &path },
+ { "duration", CONFIG_KEY_INTEGER, &duration },
+ };
+
+ struct config_section cs[] = {
+ { "screensaver", saver_keys, ARRAY_LENGTH(saver_keys), NULL },
+ };
+
+ config_file = config_file_path("wayland-desktop-shell.ini");
+ ret = parse_config_file(config_file, cs, ARRAY_LENGTH(cs), shell);
+ free(config_file);
+
+ shell->screensaver.path = path;
+ shell->screensaver.duration = duration;
+
+ return ret;
+ /* FIXME: free(shell->screensaver.path) on plugin fini */
+}
+
+static void
+move_grab_motion(struct wl_grab *grab,
+ uint32_t time, int32_t x, int32_t y)
+{
+ struct weston_move_grab *move = (struct weston_move_grab *) grab;
+ struct weston_surface *es = move->surface;
+
+ weston_surface_configure(es, x + move->dx, y + move->dy,
+ es->width, es->height);
+}
+
+static void
+move_grab_button(struct wl_grab *grab,
+ uint32_t time, int32_t button, int32_t state)
+{
+}
+
+static void
+move_grab_end(struct wl_grab *grab, uint32_t time)
+{
+ free(grab);
+}
+
+static const struct wl_grab_interface move_grab_interface = {
+ move_grab_motion,
+ move_grab_button,
+ move_grab_end
+};
+
+static int
+weston_surface_move(struct weston_surface *es,
+ struct weston_input_device *wd, uint32_t time)
+{
+ struct weston_move_grab *move;
+
+ move = malloc(sizeof *move);
+ if (!move)
+ return -1;
+
+ move->grab.interface = &move_grab_interface;
+ move->dx = es->x - wd->input_device.grab_x;
+ move->dy = es->y - wd->input_device.grab_y;
+ move->surface = es;
+
+ if (wl_input_device_update_grab(&wd->input_device,
+ &move->grab, &es->surface, time) < 0)
+ return 0;
+
+ wl_input_device_set_pointer_focus(&wd->input_device,
+ NULL, time, 0, 0, 0, 0);
+
+ return 0;
+}
+
+static void
+shell_surface_move(struct wl_client *client, struct wl_resource *resource,
+ struct wl_resource *input_resource, uint32_t time)
+{
+ struct weston_input_device *wd = input_resource->data;
+ struct shell_surface *shsurf = resource->data;
+
+ if (weston_surface_move(shsurf->surface, wd, time) < 0)
+ wl_resource_post_no_memory(resource);
+}
+
+struct weston_resize_grab {
+ struct wl_grab grab;
+ uint32_t edges;
+ int32_t dx, dy, width, height;
+ struct shell_surface *shsurf;
+};
+
+static void
+resize_grab_motion(struct wl_grab *grab,
+ uint32_t time, int32_t x, int32_t y)
+{
+ struct weston_resize_grab *resize = (struct weston_resize_grab *) grab;
+ struct wl_input_device *device = grab->input_device;
+ int32_t width, height;
+
+ if (resize->edges & WL_SHELL_SURFACE_RESIZE_LEFT) {
+ width = device->grab_x - x + resize->width;
+ } else if (resize->edges & WL_SHELL_SURFACE_RESIZE_RIGHT) {
+ width = x - device->grab_x + resize->width;
+ } else {
+ width = resize->width;
+ }
+
+ if (resize->edges & WL_SHELL_SURFACE_RESIZE_TOP) {
+ height = device->grab_y - y + resize->height;
+ } else if (resize->edges & WL_SHELL_SURFACE_RESIZE_BOTTOM) {
+ height = y - device->grab_y + resize->height;
+ } else {
+ height = resize->height;
+ }
+
+ wl_resource_post_event(&resize->shsurf->resource,
+ WL_SHELL_SURFACE_CONFIGURE, time, resize->edges,
+ width, height);
+}
+
+static void
+resize_grab_button(struct wl_grab *grab,
+ uint32_t time, int32_t button, int32_t state)
+{
+}
+
+static void
+resize_grab_end(struct wl_grab *grab, uint32_t time)
+{
+ free(grab);
+}
+
+static const struct wl_grab_interface resize_grab_interface = {
+ resize_grab_motion,
+ resize_grab_button,
+ resize_grab_end
+};
+
+static int
+weston_surface_resize(struct shell_surface *shsurf,
+ struct weston_input_device *wd,
+ uint32_t time, uint32_t edges)
+{
+ struct weston_resize_grab *resize;
+ struct weston_surface *es = shsurf->surface;
+
+ /* FIXME: Reject if fullscreen */
+
+ resize = malloc(sizeof *resize);
+ if (!resize)
+ return -1;
+
+ resize->grab.interface = &resize_grab_interface;
+ resize->edges = edges;
+ resize->dx = es->x - wd->input_device.grab_x;
+ resize->dy = es->y - wd->input_device.grab_y;
+ resize->width = es->width;
+ resize->height = es->height;
+ resize->shsurf = shsurf;
+
+ if (edges == 0 || edges > 15 ||
+ (edges & 3) == 3 || (edges & 12) == 12)
+ return 0;
+
+ if (wl_input_device_update_grab(&wd->input_device,
+ &resize->grab, &es->surface, time) < 0)
+ return 0;
+
+ wl_input_device_set_pointer_focus(&wd->input_device,
+ NULL, time, 0, 0, 0, 0);
+
+ return 0;
+}
+
+static void
+shell_surface_resize(struct wl_client *client, struct wl_resource *resource,
+ struct wl_resource *input_resource, uint32_t time,
+ uint32_t edges)
+{
+ struct weston_input_device *wd = input_resource->data;
+ struct shell_surface *shsurf = resource->data;
+
+ /* FIXME: Reject if fullscreen */
+
+ if (weston_surface_resize(shsurf, wd, time, edges) < 0)
+ wl_resource_post_no_memory(resource);
+}
+
+static int
+reset_shell_surface_type(struct shell_surface *surface)
+{
+ switch (surface->type) {
+ case SHELL_SURFACE_FULLSCREEN:
+ surface->surface->x = surface->saved_x;
+ surface->surface->y = surface->saved_y;
+ surface->surface->fullscreen_output = NULL;
+ break;
+ case SHELL_SURFACE_PANEL:
+ case SHELL_SURFACE_BACKGROUND:
+ wl_list_remove(&surface->link);
+ wl_list_init(&surface->link);
+ break;
+ case SHELL_SURFACE_SCREENSAVER:
+ case SHELL_SURFACE_LOCK:
+ wl_resource_post_error(&surface->resource,
+ WL_DISPLAY_ERROR_INVALID_METHOD,
+ "cannot reassign surface type");
+ return -1;
+ case SHELL_SURFACE_NONE:
+ case SHELL_SURFACE_TOPLEVEL:
+ case SHELL_SURFACE_TRANSIENT:
+ break;
+ }
+
+ surface->type = SHELL_SURFACE_NONE;
+ return 0;
+}
+
+static void
+shell_surface_set_toplevel(struct wl_client *client,
+ struct wl_resource *resource)
+
+{
+ struct shell_surface *surface = resource->data;
+
+ if (reset_shell_surface_type(surface))
+ return;
+
+ weston_surface_damage(surface->surface);
+ surface->type = SHELL_SURFACE_TOPLEVEL;
+}
+
+static void
+shell_surface_set_transient(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *parent_resource,
+ int x, int y, uint32_t flags)
+{
+ struct shell_surface *shsurf = resource->data;
+ struct weston_surface *es = shsurf->surface;
+ struct shell_surface *pshsurf = parent_resource->data;
+ struct weston_surface *pes = pshsurf->surface;
+
+ if (reset_shell_surface_type(shsurf))
+ return;
+
+ /* assign to parents output */
+ es->output = pes->output;
+
+ es->x = pes->x + x;
+ es->y = pes->y + y;
+
+ weston_surface_damage(es);
+ shsurf->type = SHELL_SURFACE_TRANSIENT;
+}
+
+static struct weston_output *
+get_default_output(struct weston_compositor *compositor)
+{
+ return container_of(compositor->output_list.next,
+ struct weston_output, link);
+}
+
+static void
+shell_surface_set_fullscreen(struct wl_client *client,
+ struct wl_resource *resource)
+
+{
+ struct shell_surface *shsurf = resource->data;
+ struct weston_surface *es = shsurf->surface;
+ struct weston_output *output;
+
+ if (reset_shell_surface_type(shsurf))
+ return;
+
+ /* FIXME: Fullscreen on first output */
+ /* FIXME: Handle output going away */
+ output = get_default_output(es->compositor);
+ es->output = output;
+
+ shsurf->saved_x = es->x;
+ shsurf->saved_y = es->y;
+ es->x = (output->current->width - es->width) / 2;
+ es->y = (output->current->height - es->height) / 2;
+ es->fullscreen_output = output;
+ weston_surface_damage(es);
+ shsurf->type = SHELL_SURFACE_FULLSCREEN;
+}
+
+static const struct wl_shell_surface_interface shell_surface_implementation = {
+ shell_surface_move,
+ shell_surface_resize,
+ shell_surface_set_toplevel,
+ shell_surface_set_transient,
+ shell_surface_set_fullscreen
+};
+
+static void
+destroy_shell_surface(struct wl_resource *resource)
+{
+ struct shell_surface *shsurf = resource->data;
+
+ /* in case cleaning up a dead client destroys shell_surface first */
+ if (shsurf->surface)
+ wl_list_remove(&shsurf->surface_destroy_listener.link);
+
+ wl_list_remove(&shsurf->link);
+ free(shsurf);
+}
+
+static void
+shell_handle_surface_destroy(struct wl_listener *listener,
+ struct wl_resource *resource, uint32_t time)
+{
+ struct shell_surface *shsurf = container_of(listener,
+ struct shell_surface,
+ surface_destroy_listener);
+
+ shsurf->surface = NULL;
+ wl_resource_destroy(&shsurf->resource, time);
+}
+
+static struct shell_surface *
+get_shell_surface(struct weston_surface *surface)
+{
+ struct wl_list *lst = &surface->surface.resource.destroy_listener_list;
+ struct wl_listener *listener;
+
+ /* search the destroy listener list for our callback */
+ wl_list_for_each(listener, lst, link) {
+ if (listener->func == shell_handle_surface_destroy) {
+ return container_of(listener, struct shell_surface,
+ surface_destroy_listener);
+ }
+ }
+
+ return NULL;
+}
+
+static void
+shell_get_shell_surface(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id,
+ struct wl_resource *surface_resource)
+{
+ struct weston_surface *surface = surface_resource->data;
+ struct shell_surface *shsurf;
+
+ if (get_shell_surface(surface)) {
+ wl_resource_post_error(surface_resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "wl_shell::get_shell_surface already requested");
+ return;
+ }
+
+ shsurf = calloc(1, sizeof *shsurf);
+ if (!shsurf) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ shsurf->resource.destroy = destroy_shell_surface;
+ shsurf->resource.object.id = id;
+ shsurf->resource.object.interface = &wl_shell_surface_interface;
+ shsurf->resource.object.implementation =
+ (void (**)(void)) &shell_surface_implementation;
+ shsurf->resource.data = shsurf;
+
+ shsurf->surface = surface;
+ shsurf->surface_destroy_listener.func = shell_handle_surface_destroy;
+ wl_list_insert(surface->surface.resource.destroy_listener_list.prev,
+ &shsurf->surface_destroy_listener.link);
+
+ /* init link so its safe to always remove it in destroy_shell_surface */
+ wl_list_init(&shsurf->link);
+
+ shsurf->type = SHELL_SURFACE_NONE;
+
+ wl_client_add_resource(client, &shsurf->resource);
+}
+
+static const struct wl_shell_interface shell_implementation = {
+ shell_get_shell_surface
+};
+
+static void
+handle_screensaver_sigchld(struct weston_process *proc, int status)
+{
+ proc->pid = 0;
+}
+
+static void
+launch_screensaver(struct wl_shell *shell)
+{
+ if (shell->screensaver.binding)
+ return;
+
+ if (!shell->screensaver.path)
+ return;
+
+ weston_client_launch(shell->compositor,
+ &shell->screensaver.process,
+ shell->screensaver.path,
+ handle_screensaver_sigchld);
+}
+
+static void
+terminate_screensaver(struct wl_shell *shell)
+{
+ if (shell->screensaver.process.pid == 0)
+ return;
+
+ kill(shell->screensaver.process.pid, SIGTERM);
+}
+
+static void
+show_screensaver(struct wl_shell *shell, struct shell_surface *surface)
+{
+ struct wl_list *list;
+
+ if (shell->lock_surface)
+ list = &shell->lock_surface->surface->link;
+ else
+ list = &shell->compositor->surface_list;
+
+ wl_list_remove(&surface->surface->link);
+ wl_list_insert(list, &surface->surface->link);
+ weston_surface_configure(surface->surface,
+ surface->surface->x,
+ surface->surface->y,
+ surface->surface->width,
+ surface->surface->height);
+ surface->surface->output = surface->output;
+}
+
+static void
+hide_screensaver(struct wl_shell *shell, struct shell_surface *surface)
+{
+ wl_list_remove(&surface->surface->link);
+ wl_list_init(&surface->surface->link);
+ surface->surface->output = NULL;
+}
+
+static void
+desktop_shell_set_background(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *output_resource,
+ struct wl_resource *surface_resource)
+{
+ struct wl_shell *shell = resource->data;
+ struct shell_surface *shsurf = surface_resource->data;
+ struct weston_surface *surface = shsurf->surface;
+ struct shell_surface *priv;
+
+ if (reset_shell_surface_type(shsurf))
+ return;
+
+ wl_list_for_each(priv, &shell->backgrounds, link) {
+ if (priv->output == output_resource->data) {
+ priv->surface->output = NULL;
+ wl_list_remove(&priv->surface->link);
+ wl_list_remove(&priv->link);
+ break;
+ }
+ }
+
+ shsurf->type = SHELL_SURFACE_BACKGROUND;
+ shsurf->output = output_resource->data;
+
+ wl_list_insert(&shell->backgrounds, &shsurf->link);
+
+ surface->x = shsurf->output->x;
+ surface->y = shsurf->output->y;
+
+ wl_resource_post_event(resource,
+ DESKTOP_SHELL_CONFIGURE,
+ weston_compositor_get_time(), 0, surface_resource,
+ shsurf->output->current->width,
+ shsurf->output->current->height);
+}
+
+static void
+desktop_shell_set_panel(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *output_resource,
+ struct wl_resource *surface_resource)
+{
+ struct wl_shell *shell = resource->data;
+ struct shell_surface *shsurf = surface_resource->data;
+ struct weston_surface *surface = shsurf->surface;
+ struct shell_surface *priv;
+
+ if (reset_shell_surface_type(shsurf))
+ return;
+
+ wl_list_for_each(priv, &shell->panels, link) {
+ if (priv->output == output_resource->data) {
+ priv->surface->output = NULL;
+ wl_list_remove(&priv->surface->link);
+ wl_list_remove(&priv->link);
+ break;
+ }
+ }
+
+ shsurf->type = SHELL_SURFACE_PANEL;
+ shsurf->output = output_resource->data;
+
+ wl_list_insert(&shell->panels, &shsurf->link);
+
+ surface->x = shsurf->output->x;
+ surface->y = shsurf->output->y;
+
+ wl_resource_post_event(resource,
+ DESKTOP_SHELL_CONFIGURE,
+ weston_compositor_get_time(), 0, surface_resource,
+ shsurf->output->current->width,
+ shsurf->output->current->height);
+}
+
+static void
+handle_lock_surface_destroy(struct wl_listener *listener,
+ struct wl_resource *resource, uint32_t time)
+{
+ struct wl_shell *shell =
+ container_of(listener, struct wl_shell, lock_surface_listener);
+
+ fprintf(stderr, "lock surface gone\n");
+ shell->lock_surface = NULL;
+}
+
+static void
+desktop_shell_set_lock_surface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource)
+{
+ struct wl_shell *shell = resource->data;
+ struct shell_surface *surface = surface_resource->data;
+
+ if (reset_shell_surface_type(surface))
+ return;
+
+ shell->prepare_event_sent = false;
+
+ if (!shell->locked)
+ return;
+
+ shell->lock_surface = surface;
+
+ shell->lock_surface_listener.func = handle_lock_surface_destroy;
+ wl_list_insert(&surface_resource->destroy_listener_list,
+ &shell->lock_surface_listener.link);
+
+ shell->lock_surface->type = SHELL_SURFACE_LOCK;
+}
+
+static void
+resume_desktop(struct wl_shell *shell)
+{
+ struct weston_surface *surface;
+ struct wl_list *list;
+ struct shell_surface *tmp;
+
+ wl_list_for_each(tmp, &shell->screensaver.surfaces, link)
+ hide_screensaver(shell, tmp);
+
+ terminate_screensaver(shell);
+
+ wl_list_for_each(surface, &shell->hidden_surface_list, link)
+ weston_surface_configure(surface, surface->x, surface->y,
+ surface->width, surface->height);
+
+ if (wl_list_empty(&shell->backgrounds)) {
+ list = &shell->compositor->surface_list;
+ } else {
+ struct shell_surface *background;
+ background = container_of(shell->backgrounds.prev,
+ struct shell_surface, link);
+ list = background->surface->link.prev;
+ }
+
+ if (!wl_list_empty(&shell->hidden_surface_list))
+ wl_list_insert_list(list, &shell->hidden_surface_list);
+ wl_list_init(&shell->hidden_surface_list);
+
+ shell->locked = false;
+ weston_compositor_repick(shell->compositor);
+ shell->compositor->idle_time = shell->compositor->option_idle_time;
+ weston_compositor_wake(shell->compositor);
+}
+
+static void
+desktop_shell_unlock(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct wl_shell *shell = resource->data;
+
+ shell->prepare_event_sent = false;
+
+ if (shell->locked)
+ resume_desktop(shell);
+}
+
+static const struct desktop_shell_interface desktop_shell_implementation = {
+ desktop_shell_set_background,
+ desktop_shell_set_panel,
+ desktop_shell_set_lock_surface,
+ desktop_shell_unlock
+};
+
+static enum shell_surface_type
+get_shell_surface_type(struct weston_surface *surface)
+{
+ struct shell_surface *shsurf;
+
+ shsurf = get_shell_surface(surface);
+ if (!shsurf)
+ return SHELL_SURFACE_NONE;
+ return shsurf->type;
+}
+
+static void
+move_binding(struct wl_input_device *device, uint32_t time,
+ uint32_t key, uint32_t button, uint32_t state, void *data)
+{
+ struct weston_surface *surface =
+ (struct weston_surface *) device->pointer_focus;
+
+ if (surface == NULL)
+ return;
+
+ switch (get_shell_surface_type(surface)) {
+ case SHELL_SURFACE_PANEL:
+ case SHELL_SURFACE_BACKGROUND:
+ case SHELL_SURFACE_FULLSCREEN:
+ case SHELL_SURFACE_SCREENSAVER:
+ return;
+ default:
+ break;
+ }
+
+ weston_surface_move(surface, (struct weston_input_device *) device, time);
+}
+
+static void
+resize_binding(struct wl_input_device *device, uint32_t time,
+ uint32_t key, uint32_t button, uint32_t state, void *data)
+{
+ struct weston_surface *surface =
+ (struct weston_surface *) device->pointer_focus;
+ uint32_t edges = 0;
+ int32_t x, y;
+ struct shell_surface *shsurf;
+
+ if (surface == NULL)
+ return;
+
+ shsurf = get_shell_surface(surface);
+ if (!shsurf)
+ return;
+
+ switch (shsurf->type) {
+ case SHELL_SURFACE_PANEL:
+ case SHELL_SURFACE_BACKGROUND:
+ case SHELL_SURFACE_FULLSCREEN:
+ case SHELL_SURFACE_SCREENSAVER:
+ return;
+ default:
+ break;
+ }
+
+ x = device->grab_x - surface->x;
+ y = device->grab_y - surface->y;
+
+ if (x < surface->width / 3)
+ edges |= WL_SHELL_SURFACE_RESIZE_LEFT;
+ else if (x < 2 * surface->width / 3)
+ edges |= 0;
+ else
+ edges |= WL_SHELL_SURFACE_RESIZE_RIGHT;
+
+ if (y < surface->height / 3)
+ edges |= WL_SHELL_SURFACE_RESIZE_TOP;
+ else if (y < 2 * surface->height / 3)
+ edges |= 0;
+ else
+ edges |= WL_SHELL_SURFACE_RESIZE_BOTTOM;
+
+ weston_surface_resize(shsurf, (struct weston_input_device *) device,
+ time, edges);
+}
+
+static void
+terminate_binding(struct wl_input_device *device, uint32_t time,
+ uint32_t key, uint32_t button, uint32_t state, void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ if (state)
+ wl_display_terminate(compositor->wl_display);
+}
+
+static void
+activate(struct weston_shell *base, struct weston_surface *es,
+ struct weston_input_device *device, uint32_t time)
+{
+ struct wl_shell *shell = container_of(base, struct wl_shell, shell);
+ struct weston_compositor *compositor = shell->compositor;
+
+ weston_surface_activate(es, device, time);
+
+ if (compositor->wxs)
+ weston_xserver_surface_activate(es);
+
+ switch (get_shell_surface_type(es)) {
+ case SHELL_SURFACE_BACKGROUND:
+ /* put background back to bottom */
+ wl_list_remove(&es->link);
+ wl_list_insert(compositor->surface_list.prev, &es->link);
+ break;
+ case SHELL_SURFACE_PANEL:
+ /* already put on top */
+ break;
+ case SHELL_SURFACE_SCREENSAVER:
+ /* always below lock surface */
+ if (shell->lock_surface) {
+ wl_list_remove(&es->link);
+ wl_list_insert(&shell->lock_surface->surface->link,
+ &es->link);
+ }
+ break;
+ default:
+ if (!shell->locked) {
+ /* bring panel back to top */
+ struct shell_surface *panel;
+ wl_list_for_each(panel, &shell->panels, link) {
+ wl_list_remove(&panel->surface->link);
+ wl_list_insert(&compositor->surface_list,
+ &panel->surface->link);
+ }
+ }
+ }
+}
+
+static void
+click_to_activate_binding(struct wl_input_device *device,
+ uint32_t time, uint32_t key,
+ uint32_t button, uint32_t state, void *data)
+{
+ struct weston_input_device *wd = (struct weston_input_device *) device;
+ struct weston_compositor *compositor = data;
+ struct weston_surface *focus;
+
+ focus = (struct weston_surface *) device->pointer_focus;
+ if (state && focus && device->grab == NULL)
+ activate(compositor->shell, focus, wd, time);
+}
+
+static void
+lock(struct weston_shell *base)
+{
+ struct wl_shell *shell = container_of(base, struct wl_shell, shell);
+ struct wl_list *surface_list = &shell->compositor->surface_list;
+ struct weston_surface *cur;
+ struct weston_surface *tmp;
+ struct weston_input_device *device;
+ struct shell_surface *shsurf;
+ uint32_t time;
+
+ if (shell->locked)
+ return;
+
+ shell->locked = true;
+
+ /* Move all surfaces from compositor's list to our hidden list,
+ * except the background. This way nothing else can show or
+ * receive input events while we are locked. */
+
+ if (!wl_list_empty(&shell->hidden_surface_list)) {
+ fprintf(stderr,
+ "%s: Assertion failed: hidden_surface_list is not empty.\n",
+ __func__);
+ }
+
+ wl_list_for_each_safe(cur, tmp, surface_list, link) {
+ /* skip input device sprites, cur->surface is uninitialised */
+ if (cur->surface.resource.client == NULL)
+ continue;
+
+ if (get_shell_surface_type(cur) == SHELL_SURFACE_BACKGROUND)
+ continue;
+
+ cur->output = NULL;
+ wl_list_remove(&cur->link);
+ wl_list_insert(shell->hidden_surface_list.prev, &cur->link);
+ }
+
+ launch_screensaver(shell);
+
+ wl_list_for_each(shsurf, &shell->screensaver.surfaces, link)
+ show_screensaver(shell, shsurf);
+
+ if (!wl_list_empty(&shell->screensaver.surfaces)) {
+ shell->compositor->idle_time = shell->screensaver.duration;
+ weston_compositor_wake(shell->compositor);
+ shell->compositor->state = WESTON_COMPOSITOR_IDLE;
+ }
+
+ /* reset pointer foci */
+ weston_compositor_repick(shell->compositor);
+
+ /* reset keyboard foci */
+ time = weston_compositor_get_time();
+ wl_list_for_each(device, &shell->compositor->input_device_list, link) {
+ wl_input_device_set_keyboard_focus(&device->input_device,
+ NULL, time);
+ }
+
+ /* TODO: disable bindings that should not work while locked. */
+
+ /* All this must be undone in resume_desktop(). */
+}
+
+static void
+unlock(struct weston_shell *base)
+{
+ struct wl_shell *shell = container_of(base, struct wl_shell, shell);
+
+ if (!shell->locked || shell->lock_surface) {
+ weston_compositor_wake(shell->compositor);
+ return;
+ }
+
+ /* If desktop-shell client has gone away, unlock immediately. */
+ if (!shell->child.desktop_shell) {
+ resume_desktop(shell);
+ return;
+ }
+
+ if (shell->prepare_event_sent)
+ return;
+
+ wl_resource_post_event(shell->child.desktop_shell,
+ DESKTOP_SHELL_PREPARE_LOCK_SURFACE);
+ shell->prepare_event_sent = true;
+}
+
+static void
+center_on_output(struct weston_surface *surface, struct weston_output *output)
+{
+ struct weston_mode *mode = output->current;
+
+ surface->x = output->x + (mode->width - surface->width) / 2;
+ surface->y = output->y + (mode->height - surface->height) / 2;
+}
+
+static void
+map(struct weston_shell *base,
+ struct weston_surface *surface, int32_t width, int32_t height)
+{
+ struct wl_shell *shell = container_of(base, struct wl_shell, shell);
+ struct weston_compositor *compositor = shell->compositor;
+ struct wl_list *list;
+ struct shell_surface *shsurf;
+ enum shell_surface_type surface_type = SHELL_SURFACE_NONE;
+ int do_configure;
+
+ shsurf = get_shell_surface(surface);
+ if (shsurf)
+ surface_type = shsurf->type;
+
+ if (shell->locked) {
+ list = &shell->hidden_surface_list;
+ do_configure = 0;
+ } else {
+ list = &compositor->surface_list;
+ do_configure = 1;
+ }
+
+ surface->width = width;
+ surface->height = height;
+
+ /* initial positioning, see also configure() */
+ switch (surface_type) {
+ case SHELL_SURFACE_TOPLEVEL:
+ surface->x = 10 + random() % 400;
+ surface->y = 10 + random() % 400;
+ break;
+ case SHELL_SURFACE_SCREENSAVER:
+ case SHELL_SURFACE_FULLSCREEN:
+ center_on_output(surface, surface->fullscreen_output);
+ break;
+ case SHELL_SURFACE_LOCK:
+ center_on_output(surface, get_default_output(compositor));
+ break;
+ default:
+ ;
+ }
+
+ /* surface stacking order, see also activate() */
+ switch (surface_type) {
+ case SHELL_SURFACE_BACKGROUND:
+ /* background always visible, at the bottom */
+ wl_list_insert(compositor->surface_list.prev, &surface->link);
+ do_configure = 1;
+ break;
+ case SHELL_SURFACE_PANEL:
+ /* panel always on top, hidden while locked */
+ wl_list_insert(list, &surface->link);
+ break;
+ case SHELL_SURFACE_LOCK:
+ /* lock surface always visible, on top */
+ wl_list_insert(&compositor->surface_list, &surface->link);
+
+ weston_compositor_repick(compositor);
+ weston_compositor_wake(compositor);
+ do_configure = 1;
+ break;
+ case SHELL_SURFACE_SCREENSAVER:
+ /* If locked, show it. */
+ if (shell->locked) {
+ show_screensaver(shell, shsurf);
+ compositor->idle_time = shell->screensaver.duration;
+ weston_compositor_wake(compositor);
+ if (!shell->lock_surface)
+ compositor->state = WESTON_COMPOSITOR_IDLE;
+ }
+ do_configure = 0;
+ break;
+ default:
+ /* everything else just below the panel */
+ if (!wl_list_empty(&shell->panels)) {
+ struct shell_surface *panel =
+ container_of(shell->panels.prev,
+ struct shell_surface, link);
+ wl_list_insert(&panel->surface->link, &surface->link);
+ } else {
+ wl_list_insert(list, &surface->link);
+ }
+ }
+
+ if (do_configure)
+ weston_surface_configure(surface,
+ surface->x, surface->y, width, height);
+
+ switch (surface_type) {
+ case SHELL_SURFACE_TOPLEVEL:
+ case SHELL_SURFACE_TRANSIENT:
+ case SHELL_SURFACE_FULLSCREEN:
+ if (!shell->locked)
+ activate(base, surface,
+ (struct weston_input_device *)
+ compositor->input_device,
+ weston_compositor_get_time());
+ break;
+ default:
+ break;
+ }
+
+ if (surface_type == SHELL_SURFACE_TOPLEVEL)
+ weston_zoom_run(surface, 0.8, 1.0, NULL, NULL);
+}
+
+static void
+configure(struct weston_shell *base, struct weston_surface *surface,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct wl_shell *shell = container_of(base, struct wl_shell, shell);
+ int do_configure = !shell->locked;
+ enum shell_surface_type surface_type = SHELL_SURFACE_NONE;
+ struct shell_surface *shsurf;
+
+ shsurf = get_shell_surface(surface);
+ if (shsurf)
+ surface_type = shsurf->type;
+
+ surface->width = width;
+ surface->height = height;
+
+ switch (surface_type) {
+ case SHELL_SURFACE_SCREENSAVER:
+ do_configure = !do_configure;
+ /* fall through */
+ case SHELL_SURFACE_FULLSCREEN:
+ center_on_output(surface, surface->fullscreen_output);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * weston_surface_configure() will assign an output, which means
+ * the surface is supposed to be in compositor->surface_list.
+ * Be careful with that, and make sure we stay on the right output.
+ * XXX: would a fullscreen surface need the same handling?
+ */
+ if (do_configure) {
+ weston_surface_configure(surface, x, y, width, height);
+
+ if (surface_type == SHELL_SURFACE_SCREENSAVER)
+ surface->output = shsurf->output;
+ }
+}
+
+static void
+desktop_shell_sigchld(struct weston_process *process, int status)
+{
+ struct wl_shell *shell =
+ container_of(process, struct wl_shell, child.process);
+
+ shell->child.process.pid = 0;
+ shell->child.client = NULL; /* already destroyed by wayland */
+}
+
+static int
+launch_desktop_shell_process(struct wl_shell *shell)
+{
+ const char *shell_exe = LIBEXECDIR "/wayland-desktop-shell";
+
+ shell->child.client = weston_client_launch(shell->compositor,
+ &shell->child.process,
+ shell_exe,
+ desktop_shell_sigchld);
+
+ if (!shell->child.client)
+ return -1;
+ return 0;
+}
+
+static void
+bind_shell(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct wl_shell *shell = data;
+
+ wl_client_add_object(client, &wl_shell_interface,
+ &shell_implementation, id, shell);
+}
+
+static void
+unbind_desktop_shell(struct wl_resource *resource)
+{
+ struct wl_shell *shell = resource->data;
+
+ if (shell->locked)
+ resume_desktop(shell);
+
+ shell->child.desktop_shell = NULL;
+ shell->prepare_event_sent = false;
+ free(resource);
+}
+
+static void
+bind_desktop_shell(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct wl_shell *shell = data;
+ struct wl_resource *resource;
+
+ resource = wl_client_add_object(client, &desktop_shell_interface,
+ &desktop_shell_implementation,
+ id, shell);
+
+ if (client == shell->child.client) {
+ resource->destroy = unbind_desktop_shell;
+ shell->child.desktop_shell = resource;
+ return;
+ }
+
+ wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "permission to bind desktop_shell denied");
+ wl_resource_destroy(resource, 0);
+}
+
+static void
+screensaver_set_surface(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *shell_surface_resource,
+ struct wl_resource *output_resource)
+{
+ struct wl_shell *shell = resource->data;
+ struct shell_surface *surface = shell_surface_resource->data;
+ struct weston_output *output = output_resource->data;
+
+ if (reset_shell_surface_type(surface))
+ return;
+
+ surface->type = SHELL_SURFACE_SCREENSAVER;
+
+ surface->surface->fullscreen_output = output;
+ surface->output = output;
+ wl_list_insert(shell->screensaver.surfaces.prev, &surface->link);
+}
+
+static const struct screensaver_interface screensaver_implementation = {
+ screensaver_set_surface
+};
+
+static void
+unbind_screensaver(struct wl_resource *resource)
+{
+ struct wl_shell *shell = resource->data;
+
+ shell->screensaver.binding = NULL;
+ free(resource);
+}
+
+static void
+bind_screensaver(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct wl_shell *shell = data;
+ struct wl_resource *resource;
+
+ resource = wl_client_add_object(client, &screensaver_interface,
+ &screensaver_implementation,
+ id, shell);
+
+ if (shell->screensaver.binding == NULL) {
+ resource->destroy = unbind_screensaver;
+ shell->screensaver.binding = resource;
+ return;
+ }
+
+ wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "interface object already bound");
+ wl_resource_destroy(resource, 0);
+}
+
+int
+shell_init(struct weston_compositor *ec);
+
+WL_EXPORT int
+shell_init(struct weston_compositor *ec)
+{
+ struct wl_shell *shell;
+
+ shell = malloc(sizeof *shell);
+ if (shell == NULL)
+ return -1;
+
+ memset(shell, 0, sizeof *shell);
+ shell->compositor = ec;
+ shell->shell.lock = lock;
+ shell->shell.unlock = unlock;
+ shell->shell.map = map;
+ shell->shell.configure = configure;
+
+ wl_list_init(&shell->hidden_surface_list);
+ wl_list_init(&shell->backgrounds);
+ wl_list_init(&shell->panels);
+ wl_list_init(&shell->screensaver.surfaces);
+
+ if (shell_configuration(shell) < 0)
+ return -1;
+
+ if (wl_display_add_global(ec->wl_display, &wl_shell_interface,
+ shell, bind_shell) == NULL)
+ return -1;
+
+ if (wl_display_add_global(ec->wl_display,
+ &desktop_shell_interface,
+ shell, bind_desktop_shell) == NULL)
+ return -1;
+
+ if (wl_display_add_global(ec->wl_display, &screensaver_interface,
+ shell, bind_screensaver) == NULL)
+ return -1;
+
+ if (launch_desktop_shell_process(shell) != 0)
+ return -1;
+
+ weston_compositor_add_binding(ec, 0, BTN_LEFT, MODIFIER_SUPER,
+ move_binding, shell);
+ weston_compositor_add_binding(ec, 0, BTN_MIDDLE, MODIFIER_SUPER,
+ resize_binding, shell);
+ weston_compositor_add_binding(ec, KEY_BACKSPACE, 0,
+ MODIFIER_CTRL | MODIFIER_ALT,
+ terminate_binding, ec);
+ weston_compositor_add_binding(ec, 0, BTN_LEFT, 0,
+ click_to_activate_binding, ec);
+
+
+
+
+ ec->shell = &shell->shell;
+
+ return 0;
+}
diff --git a/src/switcher.c b/src/switcher.c
new file mode 100644
index 0000000..c4ed79d
--- /dev/null
+++ b/src/switcher.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright © 2011 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 <linux/input.h>
+
+#include "compositor.h"
+
+struct weston_switcher {
+ struct weston_compositor *compositor;
+ struct weston_surface *current;
+ struct wl_listener listener;
+};
+
+static void
+weston_switcher_next(struct weston_switcher *switcher)
+{
+ struct wl_list *l;
+ struct wl_surface *current;
+
+ weston_surface_damage(switcher->current);
+ l = switcher->current->link.next;
+ if (l == &switcher->compositor->surface_list)
+ l = switcher->compositor->surface_list.next;
+ switcher->current = container_of(l, struct weston_surface, link);
+ wl_list_remove(&switcher->listener.link);
+ current = &switcher->current->surface;
+ wl_list_insert(current->resource.destroy_listener_list.prev,
+ &switcher->listener.link);
+ switcher->compositor->overlay = switcher->current;
+ weston_surface_damage(switcher->current);
+}
+
+static void
+switcher_handle_surface_destroy(struct wl_listener *listener,
+ struct wl_resource *resource, uint32_t time)
+{
+ struct weston_switcher *switcher =
+ container_of(listener, struct weston_switcher, listener);
+
+ weston_switcher_next(switcher);
+}
+
+static struct weston_switcher *
+weston_switcher_create(struct weston_compositor *compositor)
+{
+ struct weston_switcher *switcher;
+
+ switcher = malloc(sizeof *switcher);
+ switcher->compositor = compositor;
+ switcher->current = container_of(compositor->surface_list.next,
+ struct weston_surface, link);
+ switcher->listener.func = switcher_handle_surface_destroy;
+ wl_list_init(&switcher->listener.link);
+
+ return switcher;
+}
+
+static void
+weston_switcher_destroy(struct weston_switcher *switcher)
+{
+ wl_list_remove(&switcher->listener.link);
+ free(switcher);
+}
+
+static void
+switcher_next_binding(struct wl_input_device *device, uint32_t time,
+ uint32_t key, uint32_t button,
+ uint32_t state, void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ if (!state)
+ return;
+ if (wl_list_empty(&compositor->surface_list))
+ return;
+ if (compositor->switcher == NULL)
+ compositor->switcher = weston_switcher_create(compositor);
+
+ weston_switcher_next(compositor->switcher);
+}
+
+static void
+switcher_terminate_binding(struct wl_input_device *device,
+ uint32_t time, uint32_t key, uint32_t button,
+ uint32_t state, void *data)
+{
+ struct weston_compositor *compositor = data;
+ struct weston_input_device *wd = (struct weston_input_device *) device;
+
+ if (compositor->switcher && !state) {
+ weston_surface_activate(compositor->switcher->current, wd, time);
+ weston_switcher_destroy(compositor->switcher);
+ compositor->switcher = NULL;
+ compositor->overlay = NULL;
+ }
+}
+
+void
+weston_switcher_init(struct weston_compositor *compositor)
+{
+ weston_compositor_add_binding(compositor,
+ KEY_TAB, 0, MODIFIER_SUPER,
+ switcher_next_binding, compositor);
+ weston_compositor_add_binding(compositor,
+ KEY_LEFTMETA, 0, MODIFIER_SUPER,
+ switcher_terminate_binding, compositor);
+ weston_compositor_add_binding(compositor,
+ KEY_RIGHTMETA, 0, MODIFIER_SUPER,
+ switcher_terminate_binding, compositor);
+}
diff --git a/src/tablet-shell.c b/src/tablet-shell.c
new file mode 100644
index 0000000..46c57a1
--- /dev/null
+++ b/src/tablet-shell.c
@@ -0,0 +1,559 @@
+/*
+ * Copyright © 2011 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 <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <linux/input.h>
+
+#include "compositor.h"
+#include "tablet-shell-server-protocol.h"
+
+/*
+ * TODO: Don't fade back from black until we've received a lockscreen
+ * attachment.
+ */
+
+enum {
+ STATE_STARTING,
+ STATE_LOCKED,
+ STATE_HOME,
+ STATE_SWITCHER,
+ STATE_TASK
+};
+
+struct tablet_shell {
+ struct wl_resource resource;
+
+ struct weston_shell shell;
+
+ struct weston_compositor *compositor;
+ struct weston_process process;
+ struct weston_input_device *device;
+ struct wl_client *client;
+
+ struct weston_surface *surface;
+
+ struct weston_surface *lockscreen_surface;
+ struct wl_listener lockscreen_listener;
+
+ struct weston_surface *home_surface;
+
+ struct weston_surface *switcher_surface;
+ struct wl_listener switcher_listener;
+
+ struct tablet_client *current_client;
+
+ int state, previous_state;
+ int long_press_active;
+ struct wl_event_source *long_press_source;
+};
+
+struct tablet_client {
+ struct wl_resource resource;
+ struct tablet_shell *shell;
+ struct wl_client *client;
+ struct weston_surface *surface;
+ char *name;
+};
+
+static void
+tablet_shell_sigchld(struct weston_process *process, int status)
+{
+ struct tablet_shell *shell =
+ container_of(process, struct tablet_shell, process);
+
+ shell->process.pid = 0;
+
+ fprintf(stderr,
+ "wayland-tablet-daemon crashed, exit code %d\n", status);
+}
+
+static void
+tablet_shell_set_state(struct tablet_shell *shell, int state)
+{
+ static const char *states[] = {
+ "STARTING", "LOCKED", "HOME", "SWITCHER", "TASK"
+ };
+
+ fprintf(stderr, "switching to state %s (from %s)\n",
+ states[state], states[shell->state]);
+ shell->previous_state = shell->state;
+ shell->state = state;
+}
+
+static void
+tablet_shell_map(struct weston_shell *base, struct weston_surface *surface,
+ int32_t width, int32_t height)
+{
+ struct tablet_shell *shell =
+ container_of(base, struct tablet_shell, shell);
+
+ surface->x = 0;
+ surface->y = 0;
+
+ if (surface == shell->lockscreen_surface) {
+ /* */
+ } else if (surface == shell->switcher_surface) {
+ /* */
+ } else if (surface == shell->home_surface) {
+ if (shell->state == STATE_STARTING) {
+ tablet_shell_set_state(shell, STATE_LOCKED);
+ shell->previous_state = STATE_HOME;
+ wl_resource_post_event(&shell->resource,
+ TABLET_SHELL_SHOW_LOCKSCREEN);
+ }
+ } else if (shell->current_client &&
+ shell->current_client->surface != surface &&
+ shell->current_client->client == surface->surface.resource.client) {
+ tablet_shell_set_state(shell, STATE_TASK);
+ shell->current_client->surface = surface;
+ weston_zoom_run(surface, 0.3, 1.0, NULL, NULL);
+ }
+
+ wl_list_insert(&shell->compositor->surface_list, &surface->link);
+ weston_surface_configure(surface, surface->x, surface->y, width, height);
+}
+
+static void
+tablet_shell_configure(struct weston_shell *base,
+ struct weston_surface *surface,
+ int32_t x, int32_t y,
+ int32_t width, int32_t height)
+{
+ weston_surface_configure(surface, x, y, width, height);
+}
+
+static void
+handle_lockscreen_surface_destroy(struct wl_listener *listener,
+ struct wl_resource *resource, uint32_t time)
+{
+ struct tablet_shell *shell =
+ container_of(listener,
+ struct tablet_shell, lockscreen_listener);
+
+ shell->lockscreen_surface = NULL;
+ tablet_shell_set_state(shell, shell->previous_state);
+}
+
+static void
+tablet_shell_set_lockscreen(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource)
+{
+ struct tablet_shell *shell = resource->data;
+ struct weston_surface *es = surface_resource->data;
+
+ es->x = 0;
+ es->y = 0;
+ shell->lockscreen_surface = es;
+ shell->lockscreen_listener.func = handle_lockscreen_surface_destroy;
+ wl_list_insert(es->surface.resource.destroy_listener_list.prev,
+ &shell->lockscreen_listener.link);
+}
+
+static void
+handle_switcher_surface_destroy(struct wl_listener *listener,
+ struct wl_resource *resource, uint32_t time)
+{
+ struct tablet_shell *shell =
+ container_of(listener,
+ struct tablet_shell, switcher_listener);
+
+ shell->switcher_surface = NULL;
+ if (shell->state != STATE_LOCKED)
+ tablet_shell_set_state(shell, shell->previous_state);
+}
+
+static void
+tablet_shell_set_switcher(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource)
+{
+ struct tablet_shell *shell = resource->data;
+ struct weston_surface *es = surface_resource->data;
+
+ /* FIXME: Switcher should be centered and the compositor
+ * should do the tinting of the background. With the cache
+ * layer idea, we should be able to hit the framerate on the
+ * fade/zoom in. */
+ shell->switcher_surface = es;
+ shell->switcher_surface->x = 0;
+ shell->switcher_surface->y = 0;
+
+ shell->switcher_listener.func = handle_switcher_surface_destroy;
+ wl_list_insert(es->surface.resource.destroy_listener_list.prev,
+ &shell->switcher_listener.link);
+}
+
+static void
+tablet_shell_set_homescreen(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource)
+{
+ struct tablet_shell *shell = resource->data;
+
+ shell->home_surface = surface_resource->data;
+ shell->home_surface->x = 0;
+ shell->home_surface->y = 0;
+}
+
+static void
+minimize_zoom_done(struct weston_zoom *zoom, void *data)
+{
+ struct tablet_shell *shell = data;
+ struct weston_compositor *compositor = shell->compositor;
+ struct weston_input_device *device =
+ (struct weston_input_device *) compositor->input_device;
+
+ weston_surface_activate(shell->home_surface,
+ device, weston_compositor_get_time());
+}
+
+static void
+tablet_shell_switch_to(struct tablet_shell *shell,
+ struct weston_surface *surface)
+{
+ struct weston_compositor *compositor = shell->compositor;
+ struct weston_input_device *device =
+ (struct weston_input_device *) compositor->input_device;
+ struct weston_surface *current;
+
+ if (shell->state == STATE_SWITCHER) {
+ wl_list_remove(&shell->switcher_listener.link);
+ shell->switcher_surface = NULL;
+ };
+
+ if (surface == shell->home_surface) {
+ tablet_shell_set_state(shell, STATE_HOME);
+
+ if (shell->current_client && shell->current_client->surface) {
+ current = shell->current_client->surface;
+ weston_zoom_run(current, 1.0, 0.3,
+ minimize_zoom_done, shell);
+ }
+ } else {
+ fprintf(stderr, "switch to %p\n", surface);
+ weston_surface_activate(surface, device,
+ weston_compositor_get_time());
+ tablet_shell_set_state(shell, STATE_TASK);
+ weston_zoom_run(surface, 0.3, 1.0, NULL, NULL);
+ }
+}
+
+static void
+tablet_shell_show_grid(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource)
+{
+ struct tablet_shell *shell = resource->data;
+ struct weston_surface *es = surface_resource->data;
+
+ tablet_shell_switch_to(shell, es);
+}
+
+static void
+tablet_shell_show_panels(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *surface_resource)
+{
+ struct tablet_shell *shell = resource->data;
+ struct weston_surface *es = surface_resource->data;
+
+ tablet_shell_switch_to(shell, es);
+}
+
+static void
+destroy_tablet_client(struct wl_resource *resource)
+{
+ struct tablet_client *tablet_client =
+ container_of(resource, struct tablet_client, resource);
+
+ free(tablet_client->name);
+ free(tablet_client);
+}
+
+static void
+tablet_client_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource, weston_compositor_get_time());
+}
+
+static void
+tablet_client_activate(struct wl_client *client, struct wl_resource *resource)
+{
+ struct tablet_client *tablet_client = resource->data;
+ struct tablet_shell *shell = tablet_client->shell;
+
+ shell->current_client = tablet_client;
+ if (!tablet_client->surface)
+ return;
+
+ tablet_shell_switch_to(shell, tablet_client->surface);
+}
+
+static const struct tablet_client_interface tablet_client_implementation = {
+ tablet_client_destroy,
+ tablet_client_activate
+};
+
+static void
+tablet_shell_create_client(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id, const char *name, int fd)
+{
+ struct tablet_shell *shell = resource->data;
+ struct weston_compositor *compositor = shell->compositor;
+ struct tablet_client *tablet_client;
+
+ tablet_client = malloc(sizeof *tablet_client);
+ if (tablet_client == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ tablet_client->client = wl_client_create(compositor->wl_display, fd);
+ tablet_client->shell = shell;
+ tablet_client->name = strdup(name);
+
+ tablet_client->resource.destroy = destroy_tablet_client;
+ tablet_client->resource.object.id = id;
+ tablet_client->resource.object.interface =
+ &tablet_client_interface;
+ tablet_client->resource.object.implementation =
+ (void (**)(void)) &tablet_client_implementation;
+
+ wl_client_add_resource(client, &tablet_client->resource);
+ tablet_client->surface = NULL;
+ shell->current_client = tablet_client;
+
+ fprintf(stderr, "created client %p, id %d, name %s, fd %d\n",
+ tablet_client->client, id, name, fd);
+}
+
+static const struct tablet_shell_interface tablet_shell_implementation = {
+ tablet_shell_set_lockscreen,
+ tablet_shell_set_switcher,
+ tablet_shell_set_homescreen,
+ tablet_shell_show_grid,
+ tablet_shell_show_panels,
+ tablet_shell_create_client
+};
+
+static void
+launch_ux_daemon(struct tablet_shell *shell)
+{
+ const char *shell_exe = LIBEXECDIR "/wayland-tablet-shell";
+
+ shell->client = weston_client_launch(shell->compositor,
+ &shell->process,
+ shell_exe, tablet_shell_sigchld);
+}
+
+static void
+toggle_switcher(struct tablet_shell *shell)
+{
+ switch (shell->state) {
+ case STATE_SWITCHER:
+ wl_resource_post_event(&shell->resource,
+ TABLET_SHELL_HIDE_SWITCHER);
+ break;
+ default:
+ wl_resource_post_event(&shell->resource,
+ TABLET_SHELL_SHOW_SWITCHER);
+ tablet_shell_set_state(shell, STATE_SWITCHER);
+ break;
+ }
+}
+
+static void
+tablet_shell_lock(struct weston_shell *base)
+{
+ struct tablet_shell *shell =
+ container_of(base, struct tablet_shell, shell);
+
+ if (shell->state == STATE_LOCKED)
+ return;
+ if (shell->state == STATE_SWITCHER)
+ wl_resource_post_event(&shell->resource,
+ TABLET_SHELL_HIDE_SWITCHER);
+
+ wl_resource_post_event(&shell->resource,
+ TABLET_SHELL_SHOW_LOCKSCREEN);
+
+ tablet_shell_set_state(shell, STATE_LOCKED);
+}
+
+static void
+tablet_shell_unlock(struct weston_shell *base)
+{
+ struct tablet_shell *shell =
+ container_of(base, struct tablet_shell, shell);
+
+ weston_compositor_wake(shell->compositor);
+}
+
+static void
+go_home(struct tablet_shell *shell)
+{
+ struct weston_input_device *device =
+ (struct weston_input_device *) shell->compositor->input_device;
+
+ if (shell->state == STATE_SWITCHER)
+ wl_resource_post_event(&shell->resource,
+ TABLET_SHELL_HIDE_SWITCHER);
+
+ weston_surface_activate(shell->home_surface, device,
+ weston_compositor_get_time());
+
+ tablet_shell_set_state(shell, STATE_HOME);
+}
+
+static int
+long_press_handler(void *data)
+{
+ struct tablet_shell *shell = data;
+
+ shell->long_press_active = 0;
+ toggle_switcher(shell);
+
+ return 1;
+}
+
+static void
+menu_key_binding(struct wl_input_device *device, uint32_t time,
+ uint32_t key, uint32_t button, uint32_t state, void *data)
+{
+ struct tablet_shell *shell = data;
+
+ if (shell->state == STATE_LOCKED)
+ return;
+
+ if (state)
+ toggle_switcher(shell);
+}
+
+static void
+home_key_binding(struct wl_input_device *device, uint32_t time,
+ uint32_t key, uint32_t button, uint32_t state, void *data)
+{
+ struct tablet_shell *shell = data;
+
+ if (shell->state == STATE_LOCKED)
+ return;
+
+ shell->device = (struct weston_input_device *) device;
+
+ if (state) {
+ wl_event_source_timer_update(shell->long_press_source, 500);
+ shell->long_press_active = 1;
+ } else if (shell->long_press_active) {
+ wl_event_source_timer_update(shell->long_press_source, 0);
+ shell->long_press_active = 0;
+
+ switch (shell->state) {
+ case STATE_HOME:
+ case STATE_SWITCHER:
+ toggle_switcher(shell);
+ break;
+ default:
+ go_home(shell);
+ break;
+ }
+ }
+}
+
+static void
+destroy_tablet_shell(struct wl_resource *resource)
+{
+}
+
+static void
+bind_shell(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct tablet_shell *shell = data;
+
+ if (shell->client != client)
+ /* Throw an error or just let the client fail when it
+ * tries to access the object?. */
+ return;
+
+ shell->resource.object.id = id;
+ shell->resource.object.interface = &tablet_shell_interface;
+ shell->resource.object.implementation =
+ (void (**)(void)) &tablet_shell_implementation;
+ shell->resource.client = client;
+ shell->resource.data = shell;
+ shell->resource.destroy = destroy_tablet_shell;
+
+ wl_client_add_resource(client, &shell->resource);
+}
+
+void
+shell_init(struct weston_compositor *compositor);
+
+WL_EXPORT void
+shell_init(struct weston_compositor *compositor)
+{
+ struct tablet_shell *shell;
+ struct wl_event_loop *loop;
+
+ shell = malloc(sizeof *shell);
+ if (shell == NULL)
+ return;
+
+ memset(shell, 0, sizeof *shell);
+ shell->compositor = compositor;
+
+ /* FIXME: This will make the object available to all clients. */
+ wl_display_add_global(compositor->wl_display,
+ &tablet_shell_interface, shell, bind_shell);
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+ shell->long_press_source =
+ wl_event_loop_add_timer(loop, long_press_handler, shell);
+
+ weston_compositor_add_binding(compositor, KEY_LEFTMETA, 0, 0,
+ home_key_binding, shell);
+ weston_compositor_add_binding(compositor, KEY_RIGHTMETA, 0, 0,
+ home_key_binding, shell);
+ weston_compositor_add_binding(compositor, KEY_LEFTMETA, 0,
+ MODIFIER_SUPER, home_key_binding, shell);
+ weston_compositor_add_binding(compositor, KEY_RIGHTMETA, 0,
+ MODIFIER_SUPER, home_key_binding, shell);
+ weston_compositor_add_binding(compositor, KEY_COMPOSE, 0, 0,
+ menu_key_binding, shell);
+
+ compositor->shell = &shell->shell;
+
+ shell->shell.lock = tablet_shell_lock;
+ shell->shell.unlock = tablet_shell_unlock;
+ shell->shell.map = tablet_shell_map;
+ shell->shell.configure = tablet_shell_configure;
+
+ launch_ux_daemon(shell);
+
+ tablet_shell_set_state(shell, STATE_STARTING);
+}
diff --git a/src/tty.c b/src/tty.c
new file mode 100644
index 0000000..9e55550
--- /dev/null
+++ b/src/tty.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright © 2010 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 <termios.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <sys/ioctl.h>
+
+#include "compositor.h"
+
+struct tty {
+ struct weston_compositor *compositor;
+ int fd;
+ struct termios terminal_attributes;
+
+ struct wl_event_source *input_source;
+ struct wl_event_source *enter_vt_source;
+ struct wl_event_source *leave_vt_source;
+ tty_vt_func_t vt_func;
+};
+
+static int on_enter_vt(int signal_number, void *data)
+{
+ struct tty *tty = data;
+ int ret;
+
+ tty->vt_func(tty->compositor, TTY_ENTER_VT);
+
+ ioctl(tty->fd, VT_RELDISP, VT_ACKACQ);
+ ret = ioctl(tty->fd, KDSETMODE, KD_GRAPHICS);
+ if (ret)
+ fprintf(stderr, "failed to set KD_GRAPHICS mode on console: %m\n");
+
+ return 1;
+}
+
+static int
+on_leave_vt(int signal_number, void *data)
+{
+ struct tty *tty = data;
+ int ret;
+
+ ioctl (tty->fd, VT_RELDISP, 1);
+ ret = ioctl(tty->fd, KDSETMODE, KD_TEXT);
+ if (ret)
+ fprintf(stderr,
+ "failed to set KD_TEXT mode on console: %m\n");
+
+ tty->vt_func(tty->compositor, TTY_LEAVE_VT);
+
+ return 1;
+}
+
+static int
+on_tty_input(int fd, uint32_t mask, void *data)
+{
+ struct tty *tty = data;
+
+ /* Ignore input to tty. We get keyboard events from evdev
+ */
+ tcflush(tty->fd, TCIFLUSH);
+
+ return 1;
+}
+
+struct tty *
+tty_create(struct weston_compositor *compositor, tty_vt_func_t vt_func,
+ int tty_nr)
+{
+ struct termios raw_attributes;
+ struct vt_mode mode = { 0 };
+ int ret;
+ struct tty *tty;
+ struct wl_event_loop *loop;
+ char filename[16];
+
+ tty = malloc(sizeof *tty);
+ if (tty == NULL)
+ return NULL;
+
+ snprintf(filename, sizeof filename, "/dev/tty%d", tty_nr);
+ fprintf(stderr, "compositor: using %s\n", filename);
+
+ memset(tty, 0, sizeof *tty);
+ tty->compositor = compositor;
+ tty->vt_func = vt_func;
+ tty->fd = open(filename, O_RDWR | O_NOCTTY);
+ if (tty->fd <= 0) {
+ fprintf(stderr, "failed to open active tty: %m\n");
+ return NULL;
+ }
+
+ if (tcgetattr(tty->fd, &tty->terminal_attributes) < 0) {
+ fprintf(stderr, "could not get terminal attributes: %m\n");
+ return NULL;
+ }
+
+ /* Ignore control characters and disable echo */
+ raw_attributes = tty->terminal_attributes;
+ cfmakeraw(&raw_attributes);
+
+ /* Fix up line endings to be normal (cfmakeraw hoses them) */
+ raw_attributes.c_oflag |= OPOST | OCRNL;
+
+ if (tcsetattr(tty->fd, TCSANOW, &raw_attributes) < 0)
+ fprintf(stderr, "could not put terminal into raw mode: %m\n");
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+ tty->input_source =
+ wl_event_loop_add_fd(loop, tty->fd,
+ WL_EVENT_READABLE, on_tty_input, tty);
+
+ ret = ioctl(tty->fd, KDSETMODE, KD_GRAPHICS);
+ if (ret) {
+ fprintf(stderr, "failed to set KD_GRAPHICS mode on tty: %m\n");
+ return NULL;
+ }
+
+ tty->compositor->focus = 1;
+ mode.mode = VT_PROCESS;
+ mode.relsig = SIGUSR1;
+ mode.acqsig = SIGUSR2;
+ if (ioctl(tty->fd, VT_SETMODE, &mode) < 0) {
+ fprintf(stderr, "failed to take control of vt handling\n");
+ return NULL;
+ }
+
+ tty->leave_vt_source =
+ wl_event_loop_add_signal(loop, SIGUSR1, on_leave_vt, tty);
+ tty->enter_vt_source =
+ wl_event_loop_add_signal(loop, SIGUSR2, on_enter_vt, tty);
+
+ return tty;
+}
+
+void
+tty_destroy(struct tty *tty)
+{
+ if(!tty)
+ return;
+
+ if (ioctl(tty->fd, KDSETMODE, KD_TEXT))
+ fprintf(stderr,
+ "failed to set KD_TEXT mode on tty: %m\n");
+
+ if (tcsetattr(tty->fd, TCSANOW, &tty->terminal_attributes) < 0)
+ fprintf(stderr,
+ "could not restore terminal to canonical mode\n");
+
+ free(tty);
+}
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..8914721
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright © 2011 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 <string.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "compositor.h"
+
+WL_EXPORT void
+weston_matrix_init(struct weston_matrix *matrix)
+{
+ static const struct weston_matrix identity = {
+ { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }
+ };
+
+ memcpy(matrix, &identity, sizeof identity);
+}
+
+static void
+weston_matrix_multiply(struct weston_matrix *m, const struct weston_matrix *n)
+{
+ struct weston_matrix tmp;
+ const GLfloat *row, *column;
+ div_t d;
+ int i, j;
+
+ for (i = 0; i < 16; i++) {
+ tmp.d[i] = 0;
+ d = div(i, 4);
+ row = m->d + d.quot * 4;
+ column = n->d + d.rem;
+ for (j = 0; j < 4; j++)
+ tmp.d[i] += row[j] * column[j * 4];
+ }
+ memcpy(m, &tmp, sizeof tmp);
+}
+
+WL_EXPORT void
+weston_matrix_translate(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z)
+{
+ struct weston_matrix translate = {
+ { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 }
+ };
+
+ weston_matrix_multiply(matrix, &translate);
+}
+
+WL_EXPORT void
+weston_matrix_scale(struct weston_matrix *matrix, GLfloat x, GLfloat y, GLfloat z)
+{
+ struct weston_matrix scale = {
+ { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 }
+ };
+
+ weston_matrix_multiply(matrix, &scale);
+}
+
+WL_EXPORT void
+weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v)
+{
+ int i, j;
+ struct weston_vector t;
+
+ for (i = 0; i < 4; i++) {
+ t.f[i] = 0;
+ for (j = 0; j < 4; j++)
+ t.f[i] += v->f[j] * matrix->d[i + j * 4];
+ }
+
+ *v = t;
+}
+
+WL_EXPORT void
+weston_spring_init(struct weston_spring *spring,
+ double k, double current, double target)
+{
+ spring->k = k;
+ spring->friction = 400.0;
+ spring->current = current;
+ spring->previous = current;
+ spring->target = target;
+}
+
+WL_EXPORT void
+weston_spring_update(struct weston_spring *spring, uint32_t msec)
+{
+ double force, v, current, step;
+
+ step = 0.01;
+ while (4 < msec - spring->timestamp) {
+ current = spring->current;
+ v = current - spring->previous;
+ force = spring->k * (spring->target - current) / 10.0 +
+ (spring->previous - current) - v * spring->friction;
+
+ spring->current =
+ current + (current - spring->previous) +
+ force * step * step;
+ spring->previous = current;
+
+#if 0
+ if (spring->current >= 1.0) {
+#ifdef TWEENER_BOUNCE
+ spring->current = 2.0 - spring->current;
+ spring->previous = 2.0 - spring->previous;
+#else
+ spring->current = 1.0;
+ spring->previous = 1.0;
+#endif
+ }
+
+ if (spring->current <= 0.0) {
+ spring->current = 0.0;
+ spring->previous = 0.0;
+ }
+#endif
+ spring->timestamp += 4;
+ }
+}
+
+WL_EXPORT int
+weston_spring_done(struct weston_spring *spring)
+{
+ return fabs(spring->previous - spring->target) < 0.0002 &&
+ fabs(spring->current - spring->target) < 0.0002;
+}
+
+struct weston_zoom {
+ struct weston_surface *surface;
+ struct weston_animation animation;
+ struct weston_spring spring;
+ struct weston_transform transform;
+ struct wl_listener listener;
+ GLfloat start, stop;
+ void (*done)(struct weston_zoom *zoom, void *data);
+ void *data;
+};
+
+static void
+weston_zoom_destroy(struct weston_zoom *zoom)
+{
+ wl_list_remove(&zoom->animation.link);
+ wl_list_remove(&zoom->listener.link);
+ zoom->surface->transform = NULL;
+ if (zoom->done)
+ zoom->done(zoom, zoom->data);
+ free(zoom);
+}
+
+static void
+handle_zoom_surface_destroy(struct wl_listener *listener,
+ struct wl_resource *resource, uint32_t time)
+{
+ struct weston_zoom *zoom =
+ container_of(listener, struct weston_zoom, listener);
+
+ weston_zoom_destroy(zoom);
+}
+
+static void
+weston_zoom_frame(struct weston_animation *animation,
+ struct weston_output *output, uint32_t msecs)
+{
+ struct weston_zoom *zoom =
+ container_of(animation, struct weston_zoom, animation);
+ struct weston_surface *es = zoom->surface;
+ GLfloat scale;
+
+ weston_spring_update(&zoom->spring, msecs);
+
+ if (weston_spring_done(&zoom->spring))
+ weston_zoom_destroy(zoom);
+
+ scale = zoom->start +
+ (zoom->stop - zoom->start) * zoom->spring.current;
+ weston_matrix_init(&zoom->transform.matrix);
+ weston_matrix_translate(&zoom->transform.matrix,
+ -(es->x + es->width / 2.0),
+ -(es->y + es->height / 2.0), 0);
+ weston_matrix_scale(&zoom->transform.matrix, scale, scale, scale);
+ weston_matrix_translate(&zoom->transform.matrix,
+ es->x + es->width / 2.0,
+ es->y + es->height / 2.0, 0);
+
+ es->alpha = zoom->spring.current * 255;
+ if (es->alpha > 255)
+ es->alpha = 255;
+ scale = 1.0 / zoom->spring.current;
+ weston_matrix_init(&zoom->transform.inverse);
+ weston_matrix_scale(&zoom->transform.inverse, scale, scale, scale);
+
+ weston_compositor_damage_all(es->compositor);
+}
+
+WL_EXPORT struct weston_zoom *
+weston_zoom_run(struct weston_surface *surface, GLfloat start, GLfloat stop,
+ weston_zoom_done_func_t done, void *data)
+{
+ struct weston_zoom *zoom;
+
+ zoom = malloc(sizeof *zoom);
+ if (!zoom)
+ return NULL;
+
+ zoom->surface = surface;
+ zoom->done = done;
+ zoom->data = data;
+ zoom->start = start;
+ zoom->stop = stop;
+ surface->transform = &zoom->transform;
+ weston_spring_init(&zoom->spring, 200.0, 0.0, 1.0);
+ zoom->spring.friction = 700;
+ zoom->spring.timestamp = weston_compositor_get_time();
+ zoom->animation.frame = weston_zoom_frame;
+ weston_zoom_frame(&zoom->animation, NULL, zoom->spring.timestamp);
+
+ zoom->listener.func = handle_zoom_surface_destroy;
+ wl_list_insert(surface->surface.resource.destroy_listener_list.prev,
+ &zoom->listener.link);
+
+ wl_list_insert(surface->compositor->animation_list.prev,
+ &zoom->animation.link);
+
+ return zoom;
+}
+
+struct weston_binding {
+ uint32_t key;
+ uint32_t button;
+ uint32_t modifier;
+ weston_binding_handler_t handler;
+ void *data;
+ struct wl_list link;
+};
+
+WL_EXPORT struct weston_binding *
+weston_compositor_add_binding(struct weston_compositor *compositor,
+ uint32_t key, uint32_t button, uint32_t modifier,
+ weston_binding_handler_t handler, void *data)
+{
+ struct weston_binding *binding;
+
+ binding = malloc(sizeof *binding);
+ if (binding == NULL)
+ return NULL;
+
+ binding->key = key;
+ binding->button = button;
+ binding->modifier = modifier;
+ binding->handler = handler;
+ binding->data = data;
+ wl_list_insert(compositor->binding_list.prev, &binding->link);
+
+ return binding;
+}
+
+WL_EXPORT void
+weston_binding_destroy(struct weston_binding *binding)
+{
+ wl_list_remove(&binding->link);
+ free(binding);
+}
+
+WL_EXPORT void
+weston_compositor_run_binding(struct weston_compositor *compositor,
+ struct weston_input_device *device,
+ uint32_t time,
+ uint32_t key, uint32_t button, int32_t state)
+{
+ struct weston_binding *b;
+
+ wl_list_for_each(b, &compositor->binding_list, link) {
+ if (b->key == key && b->button == button &&
+ b->modifier == device->modifier_state && state) {
+ b->handler(&device->input_device,
+ time, key, button, state, b->data);
+ break;
+ }
+ }
+}
diff --git a/src/xserver-launcher.c b/src/xserver-launcher.c
new file mode 100644
index 0000000..4f33ef7
--- /dev/null
+++ b/src/xserver-launcher.c
@@ -0,0 +1,1701 @@
+/*
+ * Copyright © 2011 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <xcb/xcb.h>
+#include <xcb/xfixes.h>
+
+#include <wayland-server.h>
+
+#include "compositor.h"
+#include "xserver-server-protocol.h"
+#include "hash.h"
+
+struct xserver {
+ struct wl_resource resource;
+};
+
+struct weston_xserver {
+ struct wl_display *wl_display;
+ struct wl_event_loop *loop;
+ struct wl_event_source *sigchld_source;
+ int abstract_fd;
+ struct wl_event_source *abstract_source;
+ int unix_fd;
+ struct wl_event_source *unix_source;
+ int display;
+ struct weston_process process;
+ struct wl_resource *resource;
+ struct wl_client *client;
+ struct weston_compositor *compositor;
+ struct weston_wm *wm;
+};
+
+struct weston_wm {
+ xcb_connection_t *conn;
+ const xcb_query_extension_reply_t *xfixes;
+ struct wl_event_source *source;
+ xcb_screen_t *screen;
+ struct hash_table *window_hash;
+ struct weston_xserver *server;
+
+ xcb_window_t selection_window;
+ int incr;
+ int data_source_fd;
+ struct wl_event_source *property_source;
+ xcb_get_property_reply_t *property_reply;
+ int property_start;
+ struct wl_array source_data;
+ xcb_selection_request_event_t selection_request;
+ xcb_atom_t selection_target;
+ xcb_timestamp_t selection_timestamp;
+ int selection_property_set;
+ int flush_property_on_delete;
+
+ struct {
+ xcb_atom_t wm_protocols;
+ xcb_atom_t wm_take_focus;
+ xcb_atom_t wm_delete_window;
+ xcb_atom_t net_wm_name;
+ xcb_atom_t net_wm_icon;
+ xcb_atom_t net_wm_state;
+ xcb_atom_t net_wm_state_fullscreen;
+ xcb_atom_t net_wm_user_time;
+ xcb_atom_t net_wm_icon_name;
+ xcb_atom_t net_wm_window_type;
+ xcb_atom_t clipboard;
+ xcb_atom_t targets;
+ xcb_atom_t utf8_string;
+ xcb_atom_t wl_selection;
+ xcb_atom_t incr;
+ xcb_atom_t timestamp;
+ xcb_atom_t multiple;
+ xcb_atom_t compound_text;
+ xcb_atom_t text;
+ xcb_atom_t string;
+ xcb_atom_t text_plain_utf8;
+ xcb_atom_t text_plain;
+ } atom;
+};
+
+struct weston_wm_window {
+ xcb_window_t id;
+ struct weston_surface *surface;
+ struct wl_listener surface_destroy_listener;
+ char *class;
+ char *name;
+ struct weston_wm_window *transient_for;
+ uint32_t protocols;
+ xcb_atom_t type;
+};
+
+static struct weston_wm_window *
+get_wm_window(struct weston_surface *surface);
+
+static const char *
+get_atom_name(xcb_connection_t *c, xcb_atom_t atom)
+{
+ xcb_get_atom_name_cookie_t cookie;
+ xcb_get_atom_name_reply_t *reply;
+ xcb_generic_error_t *e;
+ static char buffer[64];
+
+ if (atom == XCB_ATOM_NONE)
+ return "None";
+
+ cookie = xcb_get_atom_name (c, atom);
+ reply = xcb_get_atom_name_reply (c, cookie, &e);
+ snprintf(buffer, sizeof buffer, "%.*s",
+ xcb_get_atom_name_name_length (reply),
+ xcb_get_atom_name_name (reply));
+ free(reply);
+
+ return buffer;
+}
+
+static void
+dump_property(struct weston_wm *wm, xcb_atom_t property,
+ xcb_get_property_reply_t *reply)
+{
+ int32_t *incr_value;
+ const char *text_value, *name;
+ xcb_atom_t *atom_value;
+ int i, width, len;
+
+ width = fprintf(stderr, " %s: ", get_atom_name(wm->conn, property));
+ if (reply == NULL) {
+ fprintf(stderr, "(no reply)\n");
+ return;
+ }
+
+ width += fprintf(stderr,
+ "type %s, format %d, length %d (value_len %d): ",
+ get_atom_name(wm->conn, reply->type),
+ reply->format,
+ xcb_get_property_value_length(reply),
+ reply->value_len);
+
+ if (reply->type == wm->atom.incr) {
+ incr_value = xcb_get_property_value(reply);
+ fprintf(stderr, "%d\n", *incr_value);
+ } else if (reply->type == wm->atom.utf8_string ||
+ reply->type == wm->atom.string) {
+ text_value = xcb_get_property_value(reply);
+ if (reply->value_len > 40)
+ len = 40;
+ else
+ len = reply->value_len;
+ fprintf(stderr, "\"%.*s\"\n", len, text_value);
+ } else if (reply->type == XCB_ATOM_ATOM) {
+ atom_value = xcb_get_property_value(reply);
+ for (i = 0; i < reply->value_len; i++) {
+ name = get_atom_name(wm->conn, atom_value[i]);
+ if (width + strlen(name) + 2 > 78) {
+ fprintf(stderr, "\n ");
+ width = 4;
+ } else if (i > 0) {
+ width += fprintf(stderr, ", ");
+ }
+
+ width += fprintf(stderr, "%s", name);
+ }
+ fprintf(stderr, "\n");
+ } else {
+ fprintf(stderr, "huh?\n");
+ }
+}
+
+static void
+dump_window_properties(struct weston_wm *wm, xcb_window_t window)
+{
+ xcb_list_properties_cookie_t list_cookie;
+ xcb_list_properties_reply_t *list_reply;
+ xcb_get_property_cookie_t property_cookie;
+ xcb_get_property_reply_t *property_reply;
+ xcb_atom_t *atoms;
+ int i, length;
+
+ list_cookie = xcb_list_properties(wm->conn, window);
+ list_reply = xcb_list_properties_reply(wm->conn, list_cookie, NULL);
+ if (!list_reply)
+ /* Bad window, typically */
+ return;
+
+ length = xcb_list_properties_atoms_length(list_reply);
+ atoms = xcb_list_properties_atoms(list_reply);
+
+ for (i = 0; i < length; i++) {
+ property_cookie =
+ xcb_get_property(wm->conn,
+ 0, /* delete */
+ window,
+ atoms[i],
+ XCB_ATOM_ANY,
+ 0, 2048);
+
+ property_reply = xcb_get_property_reply(wm->conn,
+ property_cookie, NULL);
+ dump_property(wm, atoms[i], property_reply);
+
+ free(property_reply);
+ }
+
+ free(list_reply);
+}
+
+static void
+data_offer_accept(struct wl_client *client, struct wl_resource *resource,
+ uint32_t time, const char *mime_type)
+{
+ struct weston_data_source *source = resource->data;
+
+ wl_resource_post_event(&source->resource,
+ WL_DATA_SOURCE_TARGET, mime_type);
+}
+
+static void
+data_offer_receive(struct wl_client *client, struct wl_resource *resource,
+ const char *mime_type, int32_t fd)
+{
+ struct weston_data_source *source = resource->data;
+ struct weston_wm *wm = source->data;
+
+ if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
+ /* Get data for the utf8_string target */
+ xcb_convert_selection(wm->conn,
+ wm->selection_window,
+ wm->atom.clipboard,
+ wm->atom.utf8_string,
+ wm->atom.wl_selection,
+ XCB_TIME_CURRENT_TIME);
+
+ xcb_flush(wm->conn);
+
+ fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
+ wm->data_source_fd = fd;
+ } else {
+ close(fd);
+ }
+}
+
+static void
+data_offer_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource, weston_compositor_get_time());
+}
+
+static void
+destroy_data_offer(struct wl_resource *resource)
+{
+ struct weston_data_source *source = resource->data;
+
+ weston_data_source_unref(source);
+ free(resource);
+}
+
+static const struct wl_data_offer_interface data_offer_interface = {
+ data_offer_accept,
+ data_offer_receive,
+ data_offer_destroy,
+};
+
+static struct wl_resource *
+data_source_create_offer(struct weston_data_source *source,
+ struct wl_resource *target)
+{
+ struct wl_resource *resource;
+
+ resource = wl_client_new_object(target->client,
+ &wl_data_offer_interface,
+ &data_offer_interface, source);
+ resource->destroy = destroy_data_offer;
+
+ return resource;
+}
+
+static void
+data_source_cancel(struct weston_data_source *source)
+{
+}
+
+static void
+weston_wm_get_selection_targets(struct weston_wm *wm)
+{
+ struct weston_data_source *source;
+ struct weston_input_device *device;
+ xcb_get_property_cookie_t cookie;
+ xcb_get_property_reply_t *reply;
+ xcb_atom_t *value;
+ char **p;
+ int i;
+
+ cookie = xcb_get_property(wm->conn,
+ 1, /* delete */
+ wm->selection_window,
+ wm->atom.wl_selection,
+ XCB_GET_PROPERTY_TYPE_ANY,
+ 0, /* offset */
+ 4096 /* length */);
+
+ reply = xcb_get_property_reply(wm->conn, cookie, NULL);
+
+ dump_property(wm, wm->atom.wl_selection, reply);
+
+ if (reply->type != XCB_ATOM_ATOM) {
+ free(reply);
+ return;
+ }
+
+ source = malloc(sizeof *source);
+ if (source == NULL)
+ return;
+
+ wl_list_init(&source->resource.destroy_listener_list);
+ source->create_offer = data_source_create_offer;
+ source->cancel = data_source_cancel;
+ source->data = wm;
+ source->refcount = 1;
+
+ wl_array_init(&source->mime_types);
+ value = xcb_get_property_value(reply);
+ for (i = 0; i < reply->value_len; i++) {
+ if (value[i] == wm->atom.utf8_string) {
+ p = wl_array_add(&source->mime_types, sizeof *p);
+ if (p)
+ *p = strdup("text/plain;charset=utf-8");
+ }
+ }
+
+ device = (struct weston_input_device *)
+ wm->server->compositor->input_device;
+ weston_input_device_set_selection(device, source,
+ weston_compositor_get_time());
+
+ weston_data_source_unref(source);
+ free(reply);
+}
+
+static int
+weston_wm_write_property(int fd, uint32_t mask, void *data)
+{
+ struct weston_wm *wm = data;
+ unsigned char *property;
+ int len, remainder;
+
+ property = xcb_get_property_value(wm->property_reply);
+ remainder = xcb_get_property_value_length(wm->property_reply) -
+ wm->property_start;
+
+ len = write(fd, property + wm->property_start, remainder);
+ if (len == -1) {
+ free(wm->property_reply);
+ wl_event_source_remove(wm->property_source);
+ close(fd);
+ fprintf(stderr, "write error to target fd: %m\n");
+ return 1;
+ }
+
+ fprintf(stderr, "wrote %d (chunk size %d) of %d bytes\n",
+ wm->property_start + len,
+ len, xcb_get_property_value_length(wm->property_reply));
+
+ wm->property_start += len;
+ if (len == remainder) {
+ free(wm->property_reply);
+ wl_event_source_remove(wm->property_source);
+
+ if (wm->incr) {
+ xcb_delete_property(wm->conn,
+ wm->selection_window,
+ wm->atom.wl_selection);
+ } else {
+ fprintf(stderr, "transfer complete\n");
+ close(fd);
+ }
+ }
+
+ return 1;
+}
+
+static void
+weston_wm_get_selection_data(struct weston_wm *wm)
+{
+ xcb_get_property_cookie_t cookie;
+ xcb_get_property_reply_t *reply;
+
+ cookie = xcb_get_property(wm->conn,
+ 1, /* delete */
+ wm->selection_window,
+ wm->atom.wl_selection,
+ XCB_GET_PROPERTY_TYPE_ANY,
+ 0, /* offset */
+ 0x1fffffff /* length */);
+
+ reply = xcb_get_property_reply(wm->conn, cookie, NULL);
+
+ if (reply->type == wm->atom.incr) {
+ dump_property(wm, wm->atom.wl_selection, reply);
+ wm->incr = 1;
+ free(reply);
+ } else {
+ dump_property(wm, wm->atom.wl_selection, reply);
+ wm->incr = 0;
+ wm->property_start = 0;
+ wm->property_source =
+ wl_event_loop_add_fd(wm->server->loop,
+ wm->data_source_fd,
+ WL_EVENT_WRITABLE,
+ weston_wm_write_property,
+ wm);
+ wm->property_reply = reply;
+ }
+}
+
+static void
+weston_wm_get_incr_chunk(struct weston_wm *wm)
+{
+ xcb_get_property_cookie_t cookie;
+ xcb_get_property_reply_t *reply;
+
+ cookie = xcb_get_property(wm->conn,
+ 0, /* delete */
+ wm->selection_window,
+ wm->atom.wl_selection,
+ XCB_GET_PROPERTY_TYPE_ANY,
+ 0, /* offset */
+ 0x1fffffff /* length */);
+
+ reply = xcb_get_property_reply(wm->conn, cookie, NULL);
+
+ dump_property(wm, wm->atom.wl_selection, reply);
+
+ if (xcb_get_property_value_length(reply) > 0) {
+ wm->property_start = 0;
+ wm->property_source =
+ wl_event_loop_add_fd(wm->server->loop,
+ wm->data_source_fd,
+ WL_EVENT_WRITABLE,
+ weston_wm_write_property,
+ wm);
+ wm->property_reply = reply;
+ } else {
+ fprintf(stderr, "transfer complete\n");
+ close(wm->data_source_fd);
+ free(reply);
+ }
+}
+
+void
+weston_xserver_set_selection(struct weston_input_device *device)
+{
+ struct weston_xserver *wxs = device->compositor->wxs;
+ struct weston_wm *wm = wxs->wm;
+ struct weston_data_source *source;
+ const char **p, **end;
+ int has_text_plain = 0;
+
+ fprintf(stderr, "set selection\n");
+
+ source = device->selection_data_source;
+ p = source->mime_types.data;
+ end = (const char **)
+ ((char *) source->mime_types.data + source->mime_types.size);
+
+ while (p < end) {
+ fprintf(stderr, " %s\n", *p);
+ if (strcmp(*p, "text/plain") == 0 ||
+ strcmp(*p, "text/plain;charset=utf-8") == 0)
+ has_text_plain = 1;
+ p++;
+ }
+
+ if (wm && has_text_plain &&
+ source->create_offer != data_source_create_offer) {
+ xcb_set_selection_owner(wm->conn,
+ wm->selection_window,
+ wm->atom.clipboard,
+ XCB_TIME_CURRENT_TIME);
+ } else {
+ xcb_set_selection_owner(wm->conn,
+ XCB_ATOM_NONE,
+ wm->atom.clipboard,
+ XCB_TIME_CURRENT_TIME);
+ }
+}
+
+static void
+weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_configure_request_event_t *configure_request =
+ (xcb_configure_request_event_t *) event;
+ uint32_t values[16];
+ int i = 0;
+
+ fprintf(stderr, "XCB_CONFIGURE_REQUEST (window %d) %d,%d @ %dx%d\n",
+ configure_request->window,
+ configure_request->x, configure_request->y,
+ configure_request->width, configure_request->height);
+
+ if (configure_request->value_mask & XCB_CONFIG_WINDOW_X)
+ values[i++] = configure_request->x;
+ if (configure_request->value_mask & XCB_CONFIG_WINDOW_Y)
+ values[i++] = configure_request->y;
+ if (configure_request->value_mask & XCB_CONFIG_WINDOW_WIDTH)
+ values[i++] = configure_request->width;
+ if (configure_request->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
+ values[i++] = configure_request->height;
+ if (configure_request->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
+ values[i++] = configure_request->border_width;
+ if (configure_request->value_mask & XCB_CONFIG_WINDOW_SIBLING)
+ values[i++] = configure_request->sibling;
+ if (configure_request->value_mask & XCB_CONFIG_WINDOW_STACK_MODE)
+ values[i++] = configure_request->stack_mode;
+
+ xcb_configure_window(wm->conn,
+ configure_request->window,
+ configure_request->value_mask, values);
+}
+
+static void
+weston_wm_handle_configure_notify(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_configure_notify_event_t *configure_notify =
+ (xcb_configure_notify_event_t *) event;
+
+ fprintf(stderr, "XCB_CONFIGURE_NOTIFY (window %d) %d,%d @ %dx%d\n",
+ configure_notify->window,
+ configure_notify->x, configure_notify->y,
+ configure_notify->width, configure_notify->height);
+}
+
+static void
+weston_wm_activate(struct weston_wm *wm,
+ struct weston_wm_window *window, xcb_timestamp_t time)
+{
+ xcb_client_message_event_t client_message;
+
+ client_message.response_type = XCB_CLIENT_MESSAGE;
+ client_message.format = 32;
+ client_message.window = window->id;
+ client_message.type = wm->atom.wm_protocols;
+ client_message.data.data32[0] = wm->atom.wm_take_focus;
+ client_message.data.data32[1] = XCB_TIME_CURRENT_TIME;
+
+ xcb_send_event(wm->conn, 0, window->id,
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ (char *) &client_message);
+
+ xcb_set_input_focus (wm->conn,
+ XCB_INPUT_FOCUS_POINTER_ROOT, window->id, time);
+}
+
+WL_EXPORT void
+weston_xserver_surface_activate(struct weston_surface *surface)
+{
+ struct weston_wm_window *window = get_wm_window(surface);
+ struct weston_xserver *wxs = surface->compositor->wxs;
+
+ if (window)
+ weston_wm_activate(wxs->wm, window, XCB_TIME_CURRENT_TIME);
+ else if (wxs && wxs->wm)
+ xcb_set_input_focus (wxs->wm->conn,
+ XCB_INPUT_FOCUS_POINTER_ROOT,
+ XCB_NONE,
+ XCB_TIME_CURRENT_TIME);
+}
+
+static void
+weston_wm_handle_map_request(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_map_request_event_t *map_request =
+ (xcb_map_request_event_t *) event;
+
+ fprintf(stderr, "XCB_MAP_REQUEST (window %d)\n", map_request->window);
+
+ xcb_map_window(wm->conn, map_request->window);
+}
+
+/* We reuse some predefined, but otherwise useles atoms */
+#define TYPE_WM_PROTOCOLS XCB_ATOM_CUT_BUFFER0
+
+static void
+weston_wm_handle_map_notify(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+#define F(field) offsetof(struct weston_wm_window, field)
+
+ const struct {
+ xcb_atom_t atom;
+ xcb_atom_t type;
+ int offset;
+ } props[] = {
+ { XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, F(class) },
+ { XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, F(transient_for) },
+ { wm->atom.wm_protocols, TYPE_WM_PROTOCOLS, F(protocols) },
+ { wm->atom.net_wm_window_type, XCB_ATOM_ATOM, F(type) },
+ { wm->atom.net_wm_name, XCB_ATOM_STRING, F(name) },
+ };
+#undef F
+
+ xcb_map_notify_event_t *map_notify = (xcb_map_notify_event_t *) event;
+ xcb_get_property_cookie_t cookie[ARRAY_LENGTH(props)];
+ xcb_get_property_reply_t *reply;
+ struct weston_wm_window *window;
+ void *p;
+ uint32_t *xid;
+ xcb_atom_t *atom;
+ int i;
+
+ fprintf(stderr, "XCB_MAP_NOTIFY (window %d)\n", map_notify->window);
+
+ dump_window_properties(wm, map_notify->window);
+
+ window = hash_table_lookup(wm->window_hash, map_notify->window);
+
+ for (i = 0; i < ARRAY_LENGTH(props); i++)
+ cookie[i] = xcb_get_property(wm->conn,
+ 0, /* delete */
+ window->id,
+ props[i].atom,
+ XCB_ATOM_ANY, 0, 2048);
+
+ for (i = 0; i < ARRAY_LENGTH(props); i++) {
+ reply = xcb_get_property_reply(wm->conn, cookie[i], NULL);
+ if (!reply)
+ /* Bad window, typically */
+ continue;
+
+ p = ((char *) window + props[i].offset);
+
+ switch (props[i].type) {
+ case XCB_ATOM_STRING:
+ /* FIXME: We're using this for both string and
+ utf8_string */
+ *(char **) p =
+ strndup(xcb_get_property_value(reply),
+ xcb_get_property_value_length(reply));
+ break;
+ case XCB_ATOM_WINDOW:
+ xid = xcb_get_property_value(reply);
+ *(struct weston_wm_window **) p =
+ hash_table_lookup(wm->window_hash, *xid);
+ break;
+ case XCB_ATOM_ATOM:
+ atom = xcb_get_property_value(reply);
+ *(xcb_atom_t *) p = *atom;
+ break;
+ case TYPE_WM_PROTOCOLS:
+ break;
+ default:
+ break;
+ }
+ free(reply);
+ }
+
+ fprintf(stderr, "window %d: name %s, class %s, transient_for %d\n",
+ window->id, window->name, window->class,
+ window->transient_for ? window->transient_for->id : 0);
+ weston_wm_activate(wm, window, XCB_TIME_CURRENT_TIME);
+}
+
+static const int incr_chunk_size = 64 * 1024;
+
+static void
+weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property)
+{
+ xcb_selection_notify_event_t selection_notify;
+
+ memset(&selection_notify, 0, sizeof selection_notify);
+ selection_notify.response_type = XCB_SELECTION_NOTIFY;
+ selection_notify.sequence = 0;
+ selection_notify.time = wm->selection_request.time;
+ selection_notify.requestor = wm->selection_request.requestor;
+ selection_notify.selection = wm->selection_request.selection;
+ selection_notify.target = wm->selection_request.target;
+ selection_notify.property = property;
+
+ xcb_send_event(wm->conn, 0, /* propagate */
+ wm->selection_request.requestor,
+ XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify);
+}
+
+static void
+weston_wm_send_targets(struct weston_wm *wm)
+{
+ xcb_atom_t targets[] = {
+ wm->atom.timestamp,
+ wm->atom.targets,
+ wm->atom.utf8_string,
+ /* wm->atom.compound_text, */
+ wm->atom.text,
+ /* wm->atom.string */
+ };
+
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->selection_request.requestor,
+ wm->selection_request.property,
+ XCB_ATOM_ATOM,
+ 32, /* format */
+ ARRAY_LENGTH(targets), targets);
+
+ weston_wm_send_selection_notify(wm, wm->selection_request.property);
+}
+
+static void
+weston_wm_send_timestamp(struct weston_wm *wm)
+{
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->selection_request.requestor,
+ wm->selection_request.property,
+ XCB_ATOM_INTEGER,
+ 32, /* format */
+ 1, &wm->selection_timestamp);
+
+ weston_wm_send_selection_notify(wm, wm->selection_request.property);
+}
+
+static int
+weston_wm_flush_source_data(struct weston_wm *wm)
+{
+ int length;
+
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->selection_request.requestor,
+ wm->selection_request.property,
+ wm->selection_target,
+ 8, /* format */
+ wm->source_data.size,
+ wm->source_data.data);
+ wm->selection_property_set = 1;
+ length = wm->source_data.size;
+ wm->source_data.size = 0;
+
+ return length;
+}
+
+static int
+weston_wm_read_data_source(int fd, uint32_t mask, void *data)
+{
+ struct weston_wm *wm = data;
+ int len, current, available;
+ void *p;
+
+ current = wm->source_data.size;
+ if (wm->source_data.size < incr_chunk_size)
+ p = wl_array_add(&wm->source_data, incr_chunk_size);
+ else
+ p = (char *) wm->source_data.data + wm->source_data.size;
+ available = wm->source_data.alloc - current;
+
+ len = read(fd, p, available);
+ if (len == -1) {
+ fprintf(stderr, "read error from data source: %m\n");
+ weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
+ wl_event_source_remove(wm->property_source);
+ close(fd);
+ wl_array_release(&wm->source_data);
+ }
+
+ fprintf(stderr, "read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n",
+ len, available, mask, len, (char *) p);
+
+ wm->source_data.size = current + len;
+ if (wm->source_data.size >= incr_chunk_size) {
+ if (!wm->incr) {
+ fprintf(stderr, "got %d bytes, starting incr\n",
+ wm->source_data.size);
+ wm->incr = 1;
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->selection_request.requestor,
+ wm->selection_request.property,
+ wm->atom.incr,
+ 32, /* format */
+ 1, &incr_chunk_size);
+ wm->selection_property_set = 1;
+ wm->flush_property_on_delete = 1;
+ wl_event_source_remove(wm->property_source);
+ weston_wm_send_selection_notify(wm, wm->selection_request.property);
+ } else if (wm->selection_property_set) {
+ fprintf(stderr, "got %d bytes, waiting for "
+ "property delete\n", wm->source_data.size);
+
+ wm->flush_property_on_delete = 1;
+ wl_event_source_remove(wm->property_source);
+ } else {
+ fprintf(stderr, "got %d bytes, "
+ "property deleted, seting new property\n",
+ wm->source_data.size);
+ weston_wm_flush_source_data(wm);
+ }
+ } else if (len == 0 && !wm->incr) {
+ fprintf(stderr, "non-incr transfer complete\n");
+ /* Non-incr transfer all done. */
+ weston_wm_flush_source_data(wm);
+ weston_wm_send_selection_notify(wm, wm->selection_request.property);
+ xcb_flush(wm->conn);
+ wl_event_source_remove(wm->property_source);
+ close(fd);
+ wl_array_release(&wm->source_data);
+ wm->selection_request.requestor = XCB_NONE;
+ } else if (len == 0 && wm->incr) {
+ fprintf(stderr, "incr transfer complete\n");
+
+ wm->flush_property_on_delete = 1;
+ if (wm->selection_property_set) {
+ fprintf(stderr, "got %d bytes, waiting for "
+ "property delete\n", wm->source_data.size);
+ } else {
+ fprintf(stderr, "got %d bytes, "
+ "property deleted, seting new property\n",
+ wm->source_data.size);
+ weston_wm_flush_source_data(wm);
+ }
+ xcb_flush(wm->conn);
+ wl_event_source_remove(wm->property_source);
+ wm->data_source_fd = -1;
+ close(fd);
+ } else {
+ fprintf(stderr, "nothing happened, buffered the bytes\n");
+ }
+
+ return 1;
+}
+
+static void
+weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type)
+{
+ struct weston_input_device *device = (struct weston_input_device *)
+ wm->server->compositor->input_device;
+ int p[2];
+
+ if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
+ fprintf(stderr, "pipe2 failed: %m\n");
+ weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
+ return;
+ }
+
+ wl_array_init(&wm->source_data);
+ wm->selection_target = target;
+ wm->data_source_fd = p[0];
+ wm->property_source = wl_event_loop_add_fd(wm->server->loop,
+ wm->data_source_fd,
+ WL_EVENT_READABLE,
+ weston_wm_read_data_source,
+ wm);
+
+ wl_resource_post_event(&device->selection_data_source->resource,
+ WL_DATA_SOURCE_SEND, mime_type, p[1]);
+ close(p[1]);
+}
+
+static void
+weston_wm_send_incr_chunk(struct weston_wm *wm)
+{
+ fprintf(stderr, "property deleted\n");
+ int length;
+
+ wm->selection_property_set = 0;
+ if (wm->flush_property_on_delete) {
+ fprintf(stderr, "setting new property, %d bytes\n",
+ wm->source_data.size);
+ wm->flush_property_on_delete = 0;
+ length = weston_wm_flush_source_data(wm);
+
+ if (wm->data_source_fd >= 0) {
+ wm->property_source =
+ wl_event_loop_add_fd(wm->server->loop,
+ wm->data_source_fd,
+ WL_EVENT_READABLE,
+ weston_wm_read_data_source,
+ wm);
+ } else if (length > 0) {
+ /* Transfer is all done, but queue a flush for
+ * the delete of the last chunk so we can set
+ * the 0 sized propert to signal the end of
+ * the transfer. */
+ wm->flush_property_on_delete = 1;
+ wl_array_release(&wm->source_data);
+ } else {
+ wm->selection_request.requestor = XCB_NONE;
+ }
+ }
+}
+
+static void
+weston_wm_handle_selection_request(struct weston_wm *wm,
+ xcb_generic_event_t *event)
+{
+ xcb_selection_request_event_t *selection_request =
+ (xcb_selection_request_event_t *) event;
+
+ fprintf(stderr, "selection request, %s, ",
+ get_atom_name(wm->conn, selection_request->selection));
+ fprintf(stderr, "target %s, ",
+ get_atom_name(wm->conn, selection_request->target));
+ fprintf(stderr, "property %s\n",
+ get_atom_name(wm->conn, selection_request->property));
+
+ wm->selection_request = *selection_request;
+ wm->incr = 0;
+ wm->flush_property_on_delete = 0;
+
+ if (selection_request->target == wm->atom.targets) {
+ weston_wm_send_targets(wm);
+ } else if (selection_request->target == wm->atom.timestamp) {
+ weston_wm_send_timestamp(wm);
+ } else if (selection_request->target == wm->atom.utf8_string ||
+ selection_request->target == wm->atom.text) {
+ weston_wm_send_data(wm, wm->atom.utf8_string,
+ "text/plain;charset=utf-8");
+ } else {
+ fprintf(stderr, "can only handle UTF8_STRING targets...\n");
+ weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
+ }
+}
+
+static void
+weston_wm_handle_property_notify(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_property_notify_event_t *property_notify =
+ (xcb_property_notify_event_t *) event;
+
+ if (property_notify->window == wm->selection_window) {
+ if (property_notify->state == XCB_PROPERTY_NEW_VALUE &&
+ property_notify->atom == wm->atom.wl_selection &&
+ wm->incr)
+ weston_wm_get_incr_chunk(wm);
+ } else if (property_notify->window == wm->selection_request.requestor) {
+ if (property_notify->state == XCB_PROPERTY_DELETE &&
+ property_notify->atom == wm->selection_request.property &&
+ wm->incr)
+ weston_wm_send_incr_chunk(wm);
+ } else if (property_notify->atom == XCB_ATOM_WM_CLASS) {
+ fprintf(stderr, "wm_class changed\n");
+ } else if (property_notify->atom == XCB_ATOM_WM_TRANSIENT_FOR) {
+ fprintf(stderr, "wm_transient_for changed\n");
+ } else if (property_notify->atom == wm->atom.wm_protocols) {
+ fprintf(stderr, "wm_protocols changed\n");
+ } else if (property_notify->atom == wm->atom.net_wm_name) {
+ fprintf(stderr, "_net_wm_name changed\n");
+ } else if (property_notify->atom == wm->atom.net_wm_user_time) {
+ fprintf(stderr, "_net_wm_user_time changed\n");
+ } else if (property_notify->atom == wm->atom.net_wm_icon_name) {
+ fprintf(stderr, "_net_wm_icon_name changed\n");
+ } else if (property_notify->atom == XCB_ATOM_WM_NAME) {
+ fprintf(stderr, "wm_name changed\n");
+ } else if (property_notify->atom == XCB_ATOM_WM_ICON_NAME) {
+ fprintf(stderr, "wm_icon_name changed\n");
+ } else {
+ fprintf(stderr, "XCB_PROPERTY_NOTIFY: "
+ "unhandled property change: %s\n",
+ get_atom_name(wm->conn, property_notify->atom));
+ }
+}
+
+static void
+weston_wm_handle_create_notify(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_create_notify_event_t *create_notify =
+ (xcb_create_notify_event_t *) event;
+ struct weston_wm_window *window;
+ uint32_t values[1];
+
+ fprintf(stderr, "XCB_CREATE_NOTIFY (window %d)\n",
+ create_notify->window);
+
+ window = malloc(sizeof *window);
+ if (window == NULL) {
+ fprintf(stderr, "failed to allocate window\n");
+ return;
+ }
+
+ values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
+ xcb_change_window_attributes(wm->conn, create_notify->window,
+ XCB_CW_EVENT_MASK, values);
+
+ memset(window, 0, sizeof *window);
+ window->id = create_notify->window;
+ hash_table_insert(wm->window_hash, window->id, window);
+}
+
+static void
+weston_wm_handle_destroy_notify(struct weston_wm *wm, xcb_generic_event_t *event)
+{
+ xcb_destroy_notify_event_t *destroy_notify =
+ (xcb_destroy_notify_event_t *) event;
+ struct weston_wm_window *window;
+
+ fprintf(stderr, "XCB_DESTROY_NOTIFY, win %d\n",
+ destroy_notify->window);
+
+ window = hash_table_lookup(wm->window_hash, destroy_notify->window);
+ if (window == NULL) {
+ fprintf(stderr, "destroy notify for unknow window %d\n",
+ destroy_notify->window);
+ return;
+ }
+
+ fprintf(stderr, "destroy window %p\n", window);
+ hash_table_remove(wm->window_hash, window->id);
+ if (window->surface)
+ wl_list_remove(&window->surface_destroy_listener.link);
+ free(window);
+}
+
+static void
+weston_wm_handle_selection_notify(struct weston_wm *wm,
+ xcb_generic_event_t *event)
+{
+ xcb_selection_notify_event_t *selection_notify =
+ (xcb_selection_notify_event_t *) event;
+
+ if (selection_notify->property == XCB_ATOM_NONE) {
+ /* convert selection failed */
+ } else if (selection_notify->target == wm->atom.targets) {
+ weston_wm_get_selection_targets(wm);
+ } else {
+ weston_wm_get_selection_data(wm);
+ }
+}
+
+static void
+weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm,
+ xcb_generic_event_t *event)
+{
+ xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
+ (xcb_xfixes_selection_notify_event_t *) event;
+
+ printf("xfixes selection notify event: owner %d\n",
+ xfixes_selection_notify->owner);
+
+ /* We have to use XCB_TIME_CURRENT_TIME when we claim the
+ * selection, so grab the actual timestamp here so we can
+ * answer TIMESTAMP conversion requests correctly. */
+ if (xfixes_selection_notify->owner == wm->selection_window) {
+ wm->selection_timestamp = xfixes_selection_notify->timestamp;
+ fprintf(stderr, "our window, skipping\n");
+ return;
+ }
+
+ xcb_convert_selection(wm->conn, wm->selection_window,
+ wm->atom.clipboard,
+ wm->atom.targets,
+ wm->atom.wl_selection,
+ xfixes_selection_notify->timestamp);
+
+ xcb_flush(wm->conn);
+}
+
+static int
+weston_wm_handle_event(int fd, uint32_t mask, void *data)
+{
+ struct weston_wm *wm = data;
+ xcb_generic_event_t *event;
+ int count = 0;
+
+ while (event = xcb_poll_for_event(wm->conn), event != NULL) {
+ switch (event->response_type & ~0x80) {
+ case XCB_CREATE_NOTIFY:
+ weston_wm_handle_create_notify(wm, event);
+ break;
+ case XCB_MAP_REQUEST:
+ weston_wm_handle_map_request(wm, event);
+ break;
+ case XCB_MAP_NOTIFY:
+ weston_wm_handle_map_notify(wm, event);
+ break;
+ case XCB_UNMAP_NOTIFY:
+ fprintf(stderr, "XCB_UNMAP_NOTIFY\n");
+ break;
+ case XCB_CONFIGURE_REQUEST:
+ weston_wm_handle_configure_request(wm, event);
+ break;
+ case XCB_CONFIGURE_NOTIFY:
+ weston_wm_handle_configure_notify(wm, event);
+ break;
+ case XCB_DESTROY_NOTIFY:
+ weston_wm_handle_destroy_notify(wm, event);
+ break;
+ case XCB_MAPPING_NOTIFY:
+ fprintf(stderr, "XCB_MAPPING_NOTIFY\n");
+ break;
+ case XCB_PROPERTY_NOTIFY:
+ weston_wm_handle_property_notify(wm, event);
+ break;
+ case XCB_SELECTION_NOTIFY:
+ weston_wm_handle_selection_notify(wm, event);
+ break;
+ case XCB_SELECTION_REQUEST:
+ weston_wm_handle_selection_request(wm, event);
+ break;
+ }
+
+ switch (event->response_type - wm->xfixes->first_event) {
+ case XCB_XFIXES_SELECTION_NOTIFY:
+ weston_wm_handle_xfixes_selection_notify(wm, event);
+ break;
+ }
+
+
+ free(event);
+ count++;
+ }
+
+ xcb_flush(wm->conn);
+
+ return count;
+}
+
+static void
+wxs_wm_get_resources(struct weston_wm *wm)
+{
+
+#define F(field) offsetof(struct weston_wm, field)
+
+ static const struct { const char *name; int offset; } atoms[] = {
+ { "WM_PROTOCOLS", F(atom.wm_protocols) },
+ { "WM_TAKE_FOCUS", F(atom.wm_take_focus) },
+ { "WM_DELETE_WINDOW", F(atom.wm_delete_window) },
+ { "_NET_WM_NAME", F(atom.net_wm_name) },
+ { "_NET_WM_ICON", F(atom.net_wm_icon) },
+ { "_NET_WM_STATE", F(atom.net_wm_state) },
+ { "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) },
+ { "_NET_WM_USER_TIME", F(atom.net_wm_user_time) },
+ { "_NET_WM_ICON_NAME", F(atom.net_wm_icon_name) },
+ { "_NET_WM_WINDOW_TYPE", F(atom.net_wm_window_type) },
+ { "CLIPBOARD", F(atom.clipboard) },
+ { "TARGETS", F(atom.targets) },
+ { "UTF8_STRING", F(atom.utf8_string) },
+ { "_WL_SELECTION", F(atom.wl_selection) },
+ { "INCR", F(atom.incr) },
+ { "TIMESTAMP", F(atom.timestamp) },
+ { "MULTIPLE", F(atom.multiple) },
+ { "UTF8_STRING" , F(atom.utf8_string) },
+ { "COMPOUND_TEXT", F(atom.compound_text) },
+ { "TEXT", F(atom.text) },
+ { "STRING", F(atom.string) },
+ { "text/plain;charset=utf-8", F(atom.text_plain_utf8) },
+ { "text/plain", F(atom.text_plain) },
+ };
+
+ xcb_xfixes_query_version_cookie_t xfixes_cookie;
+ xcb_xfixes_query_version_reply_t *xfixes_reply;
+ xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
+ xcb_intern_atom_reply_t *reply;
+ int i;
+
+ xcb_prefetch_extension_data (wm->conn, &xcb_xfixes_id);
+
+ for (i = 0; i < ARRAY_LENGTH(atoms); i++)
+ cookies[i] = xcb_intern_atom (wm->conn, 0,
+ strlen(atoms[i].name),
+ atoms[i].name);
+
+ for (i = 0; i < ARRAY_LENGTH(atoms); i++) {
+ reply = xcb_intern_atom_reply (wm->conn, cookies[i], NULL);
+ *(xcb_atom_t *) ((char *) wm + atoms[i].offset) = reply->atom;
+ free(reply);
+ }
+
+ wm->xfixes = xcb_get_extension_data(wm->conn, &xcb_xfixes_id);
+ if (!wm->xfixes || !wm->xfixes->present)
+ fprintf(stderr, "xfixes not available\n");
+
+ xfixes_cookie = xcb_xfixes_query_version(wm->conn,
+ XCB_XFIXES_MAJOR_VERSION,
+ XCB_XFIXES_MINOR_VERSION);
+ xfixes_reply = xcb_xfixes_query_version_reply(wm->conn,
+ xfixes_cookie, NULL);
+
+ printf("xfixes version: %d.%d\n",
+ xfixes_reply->major_version, xfixes_reply->minor_version);
+
+ free(xfixes_reply);
+}
+
+static struct weston_wm *
+weston_wm_create(struct weston_xserver *wxs)
+{
+ struct weston_wm *wm;
+ struct wl_event_loop *loop;
+ xcb_screen_iterator_t s;
+ uint32_t values[1], mask;
+ int sv[2];
+
+ wm = malloc(sizeof *wm);
+ if (wm == NULL)
+ return NULL;
+
+ wm->server = wxs;
+ wm->window_hash = hash_table_create();
+ if (wm->window_hash == NULL) {
+ free(wm);
+ return NULL;
+ }
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
+ fprintf(stderr, "socketpair failed\n");
+ hash_table_destroy(wm->window_hash);
+ free(wm);
+ return NULL;
+ }
+
+ wl_resource_post_event(wxs->resource, XSERVER_CLIENT, sv[1]);
+ wl_client_flush(wxs->resource->client);
+ close(sv[1]);
+
+ /* xcb_connect_to_fd takes ownership of the fd. */
+ wm->conn = xcb_connect_to_fd(sv[0], NULL);
+ if (xcb_connection_has_error(wm->conn)) {
+ fprintf(stderr, "xcb_connect_to_fd failed\n");
+ close(sv[0]);
+ hash_table_destroy(wm->window_hash);
+ free(wm);
+ return NULL;
+ }
+
+ s = xcb_setup_roots_iterator(xcb_get_setup(wm->conn));
+ wm->screen = s.data;
+
+ loop = wl_display_get_event_loop(wxs->wl_display);
+ wm->source =
+ wl_event_loop_add_fd(loop, sv[0],
+ WL_EVENT_READABLE,
+ weston_wm_handle_event, wm);
+ wl_event_source_check(wm->source);
+
+ wxs_wm_get_resources(wm);
+
+ values[0] =
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY |
+ XCB_EVENT_MASK_RESIZE_REDIRECT |
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
+ XCB_EVENT_MASK_PROPERTY_CHANGE;
+ xcb_change_window_attributes(wm->conn, wm->screen->root,
+ XCB_CW_EVENT_MASK, values);
+
+ wm->selection_request.requestor = XCB_NONE;
+
+ wm->selection_window = xcb_generate_id(wm->conn);
+ xcb_create_window(wm->conn,
+ XCB_COPY_FROM_PARENT,
+ wm->selection_window,
+ wm->screen->root,
+ 0, 0,
+ 10, 10,
+ 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ wm->screen->root_visual,
+ XCB_CW_EVENT_MASK, values);
+
+ mask =
+ XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
+ XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
+ XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
+
+ xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
+ wm->atom.clipboard, mask);
+
+ xcb_flush(wm->conn);
+ fprintf(stderr, "created wm\n");
+
+ return wm;
+}
+
+static void
+weston_wm_destroy(struct weston_wm *wm)
+{
+ /* FIXME: Free windows in hash. */
+ hash_table_destroy(wm->window_hash);
+ xcb_disconnect(wm->conn);
+ wl_event_source_remove(wm->source);
+ free(wm);
+}
+
+static int
+weston_xserver_handle_event(int listen_fd, uint32_t mask, void *data)
+{
+ struct weston_xserver *mxs = data;
+ char display[8], s[8], logfile[32];
+ int sv[2], flags;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
+ fprintf(stderr, "socketpair failed\n");
+ return 1;
+ }
+
+ mxs->process.pid = fork();
+ switch (mxs->process.pid) {
+ case 0:
+ /* SOCK_CLOEXEC closes both ends, so we need to unset
+ * the flag on the client fd. */
+ flags = fcntl(sv[1], F_GETFD);
+ if (flags != -1)
+ fcntl(sv[1], F_SETFD, flags & ~FD_CLOEXEC);
+
+ snprintf(s, sizeof s, "%d", sv[1]);
+ setenv("WAYLAND_SOCKET", s, 1);
+
+ snprintf(display, sizeof display, ":%d", mxs->display);
+ snprintf(logfile, sizeof logfile,
+ "/tmp/x-log-%d", mxs->display);
+
+ if (execl(XSERVER_PATH,
+ XSERVER_PATH,
+ display,
+ "-wayland",
+ "-rootless",
+ "-retro",
+ "-logfile", logfile,
+ "-nolisten", "all",
+ "-terminate",
+ NULL) < 0)
+ fprintf(stderr, "exec failed: %m\n");
+ exit(-1);
+
+ default:
+ fprintf(stderr, "forked X server, pid %d\n", mxs->process.pid);
+
+ close(sv[1]);
+ mxs->client = wl_client_create(mxs->wl_display, sv[0]);
+
+ weston_watch_process(&mxs->process);
+
+ wl_event_source_remove(mxs->abstract_source);
+ wl_event_source_remove(mxs->unix_source);
+ break;
+
+ case -1:
+ fprintf(stderr, "failed to fork\n");
+ break;
+ }
+
+ return 1;
+}
+
+static void
+weston_xserver_shutdown(struct weston_xserver *wxs)
+{
+ char path[256];
+
+ snprintf(path, sizeof path, "/tmp/.X%d-lock", wxs->display);
+ unlink(path);
+ snprintf(path, sizeof path, "/tmp/.X11-unix/X%d", wxs->display);
+ unlink(path);
+ if (wxs->process.pid == 0) {
+ wl_event_source_remove(wxs->abstract_source);
+ wl_event_source_remove(wxs->unix_source);
+ }
+ close(wxs->abstract_fd);
+ close(wxs->unix_fd);
+ if (wxs->wm)
+ weston_wm_destroy(wxs->wm);
+ wxs->loop = NULL;
+}
+
+static void
+weston_xserver_cleanup(struct weston_process *process, int status)
+{
+ struct weston_xserver *mxs =
+ container_of(process, struct weston_xserver, process);
+
+ mxs->process.pid = 0;
+ mxs->client = NULL;
+ mxs->resource = NULL;
+
+ mxs->abstract_source =
+ wl_event_loop_add_fd(mxs->loop, mxs->abstract_fd,
+ WL_EVENT_READABLE,
+ weston_xserver_handle_event, mxs);
+
+ mxs->unix_source =
+ wl_event_loop_add_fd(mxs->loop, mxs->unix_fd,
+ WL_EVENT_READABLE,
+ weston_xserver_handle_event, mxs);
+
+ if (mxs->wm) {
+ fprintf(stderr, "xserver exited, code %d\n", status);
+ weston_wm_destroy(mxs->wm);
+ mxs->wm = NULL;
+ } else {
+ /* If the X server crashes before it binds to the
+ * xserver interface, shut down and don't try
+ * again. */
+ fprintf(stderr, "xserver crashing too fast: %d\n", status);
+ weston_xserver_shutdown(mxs);
+ }
+}
+
+static void
+surface_destroy(struct wl_listener *listener,
+ struct wl_resource *resource, uint32_t time)
+{
+ struct weston_wm_window *window =
+ container_of(listener,
+ struct weston_wm_window, surface_destroy_listener);
+
+ fprintf(stderr, "surface for xid %d destroyed\n", window->id);
+}
+
+static struct weston_wm_window *
+get_wm_window(struct weston_surface *surface)
+{
+ struct wl_resource *resource = &surface->surface.resource;
+ struct wl_listener *listener;
+
+ wl_list_for_each(listener, &resource->destroy_listener_list, link) {
+ if (listener->func == surface_destroy)
+ return container_of(listener,
+ struct weston_wm_window,
+ surface_destroy_listener);
+ }
+
+ return NULL;
+}
+
+static void
+xserver_set_window_id(struct wl_client *client, struct wl_resource *resource,
+ struct wl_resource *surface_resource, uint32_t id)
+{
+ struct weston_xserver *wxs = resource->data;
+ struct weston_wm *wm = wxs->wm;
+ struct wl_surface *surface = surface_resource->data;
+ struct weston_wm_window *window;
+
+ if (client != wxs->client)
+ return;
+
+ window = hash_table_lookup(wm->window_hash, id);
+ if (window == NULL) {
+ fprintf(stderr, "set_window_id for unknown window %d\n", id);
+ return;
+ }
+
+ fprintf(stderr, "set_window_id %d for surface %p\n", id, surface);
+
+ window->surface = (struct weston_surface *) surface;
+ window->surface_destroy_listener.func = surface_destroy;
+ wl_list_insert(surface->resource.destroy_listener_list.prev,
+ &window->surface_destroy_listener.link);
+}
+
+static const struct xserver_interface xserver_implementation = {
+ xserver_set_window_id
+};
+
+static void
+bind_xserver(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct weston_xserver *wxs = data;
+
+ /* If it's a different client than the xserver we launched,
+ * don't start the wm. */
+ if (client != wxs->client)
+ return;
+
+ wxs->resource =
+ wl_client_add_object(client, &xserver_interface,
+ &xserver_implementation, id, wxs);
+
+ wxs->wm = weston_wm_create(wxs);
+ if (wxs->wm == NULL) {
+ fprintf(stderr, "failed to create wm\n");
+ }
+
+ wl_resource_post_event(wxs->resource,
+ XSERVER_LISTEN_SOCKET, wxs->abstract_fd);
+
+ wl_resource_post_event(wxs->resource,
+ XSERVER_LISTEN_SOCKET, wxs->unix_fd);
+}
+
+static int
+bind_to_abstract_socket(int display)
+{
+ struct sockaddr_un addr;
+ socklen_t size, name_size;
+ int fd;
+
+ fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -1;
+
+ addr.sun_family = AF_LOCAL;
+ name_size = snprintf(addr.sun_path, sizeof addr.sun_path,
+ "%c/tmp/.X11-unix/X%d", 0, display);
+ size = offsetof(struct sockaddr_un, sun_path) + name_size;
+ if (bind(fd, (struct sockaddr *) &addr, size) < 0) {
+ fprintf(stderr, "failed to bind to @%s: %s\n",
+ addr.sun_path + 1, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (listen(fd, 1) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int
+bind_to_unix_socket(int display)
+{
+ struct sockaddr_un addr;
+ socklen_t size, name_size;
+ int fd;
+
+ fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -1;
+
+ addr.sun_family = AF_LOCAL;
+ name_size = snprintf(addr.sun_path, sizeof addr.sun_path,
+ "/tmp/.X11-unix/X%d", display) + 1;
+ size = offsetof(struct sockaddr_un, sun_path) + name_size;
+ unlink(addr.sun_path);
+ if (bind(fd, (struct sockaddr *) &addr, size) < 0) {
+ fprintf(stderr, "failed to bind to %s (%s)\n",
+ addr.sun_path, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (listen(fd, 1) < 0) {
+ unlink(addr.sun_path);
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int
+create_lockfile(int display, char *lockfile, size_t lsize)
+{
+ char pid[16], *end;
+ int fd, size;
+ pid_t other;
+
+ snprintf(lockfile, lsize, "/tmp/.X%d-lock", display);
+ fd = open(lockfile, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444);
+ if (fd < 0 && errno == EEXIST) {
+ fd = open(lockfile, O_CLOEXEC, O_RDONLY);
+ if (fd < 0 || read(fd, pid, 11) != 11) {
+ fprintf(stderr, "can't read lock file %s: %s\n",
+ lockfile, strerror(errno));
+ errno = EEXIST;
+ return -1;
+ }
+
+ other = strtol(pid, &end, 0);
+ if (end != pid + 10) {
+ fprintf(stderr, "can't parse lock file %s\n",
+ lockfile);
+ errno = EEXIST;
+ return -1;
+ }
+
+ if (kill(other, 0) < 0 && errno == ESRCH) {
+ /* stale lock file; unlink and try again */
+ fprintf(stderr,
+ "unlinking stale lock file %s\n", lockfile);
+ unlink(lockfile);
+ errno = EAGAIN;
+ return -1;
+ }
+
+ errno = EEXIST;
+ return -1;
+ } else if (fd < 0) {
+ fprintf(stderr, "failed to create lock file %s: %s\n",
+ lockfile, strerror(errno));
+ return -1;
+ }
+
+ /* Subtle detail: we use the pid of the wayland
+ * compositor, not the xserver in the lock file. */
+ size = snprintf(pid, sizeof pid, "%10d\n", getpid());
+ if (write(fd, pid, size) != size) {
+ unlink(lockfile);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+int
+weston_xserver_init(struct weston_compositor *compositor)
+{
+ struct wl_display *display = compositor->wl_display;
+ struct weston_xserver *mxs;
+ char lockfile[256];
+
+ mxs = malloc(sizeof *mxs);
+ memset(mxs, 0, sizeof *mxs);
+
+ mxs->process.cleanup = weston_xserver_cleanup;
+ mxs->wl_display = display;
+ mxs->compositor = compositor;
+
+ mxs->display = 0;
+
+ retry:
+ if (create_lockfile(mxs->display, lockfile, sizeof lockfile) < 0) {
+ if (errno == EAGAIN) {
+ goto retry;
+ } else if (errno == EEXIST) {
+ mxs->display++;
+ goto retry;
+ } else {
+ free(mxs);
+ return -1;
+ }
+ }
+
+ mxs->abstract_fd = bind_to_abstract_socket(mxs->display);
+ if (mxs->abstract_fd < 0 && errno == EADDRINUSE) {
+ mxs->display++;
+ unlink(lockfile);
+ goto retry;
+ }
+
+ mxs->unix_fd = bind_to_unix_socket(mxs->display);
+ if (mxs->unix_fd < 0) {
+ unlink(lockfile);
+ close(mxs->abstract_fd);
+ free(mxs);
+ return -1;
+ }
+
+ fprintf(stderr, "xserver listening on display :%d\n", mxs->display);
+
+ mxs->loop = wl_display_get_event_loop(display);
+ mxs->abstract_source =
+ wl_event_loop_add_fd(mxs->loop, mxs->abstract_fd,
+ WL_EVENT_READABLE,
+ weston_xserver_handle_event, mxs);
+ mxs->unix_source =
+ wl_event_loop_add_fd(mxs->loop, mxs->unix_fd,
+ WL_EVENT_READABLE,
+ weston_xserver_handle_event, mxs);
+
+ wl_display_add_global(display, &xserver_interface, mxs, bind_xserver);
+
+ compositor->wxs = mxs;
+
+ return 0;
+}
+
+void
+weston_xserver_destroy(struct weston_compositor *compositor)
+{
+ struct weston_xserver *wxs = compositor->wxs;
+
+ if (!wxs)
+ return;
+
+ if (wxs->loop)
+ weston_xserver_shutdown(wxs);
+
+ free(wxs);
+}