diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/.gitignore | 9 | ||||
-rw-r--r-- | src/Makefile.am | 113 | ||||
-rw-r--r-- | src/compositor-drm.c | 956 | ||||
-rw-r--r-- | src/compositor-openwfd.c | 699 | ||||
-rw-r--r-- | src/compositor-wayland.c | 587 | ||||
-rw-r--r-- | src/compositor-x11.c | 835 | ||||
-rw-r--r-- | src/compositor.c | 2185 | ||||
-rw-r--r-- | src/compositor.h | 471 | ||||
-rw-r--r-- | src/data-device.c | 478 | ||||
-rw-r--r-- | src/evdev.c | 653 | ||||
-rw-r--r-- | src/evdev.h | 35 | ||||
-rw-r--r-- | src/hash.c | 307 | ||||
-rw-r--r-- | src/hash.h | 49 | ||||
-rw-r--r-- | src/image-loader.c | 176 | ||||
-rw-r--r-- | src/screenshooter.c | 83 | ||||
-rw-r--r-- | src/shell.c | 1296 | ||||
-rw-r--r-- | src/switcher.c | 131 | ||||
-rw-r--r-- | src/tablet-shell.c | 559 | ||||
-rw-r--r-- | src/tty.c | 176 | ||||
-rw-r--r-- | src/util.c | 301 | ||||
-rw-r--r-- | src/xserver-launcher.c | 1701 |
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(®ion, + es->x, es->y, es->width, es->height); + pixman_region32_intersect(®ion, ®ion, &output->region); + + e = pixman_region32_extents(®ion); + 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); +} |