summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
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);
+}