This clarifies what is supposed to be the libweston code.
v2: screen-share.c is already in compositor/ instead.
Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
Reviewed-by: Yong Bakos <ybakos@humanoriented.com>
Acked-by: Daniel Stone <daniels@collabora.com>
Reviewed-by: Quentin Glidic <sardemff7+git@sardemff7.net>
Tested-by: Quentin Glidic <sardemff7+git@sardemff7.net>
Tested-by: Benoit Gschwind <gschwind@gnu-log.net>
Acked-by: Benoit Gschwind <gschwind@gnu-log.net>
[Pekka: rebased]
AM_CFLAGS = $(GCC_CFLAGS)
AM_CPPFLAGS = \
- -I$(top_srcdir)/src \
- -I$(top_builddir)/src \
+ -I$(top_srcdir)/libweston \
+ -I$(top_builddir)/libweston \
-I$(top_builddir)/clients \
-I$(top_builddir)/tests \
-I$(top_srcdir)/shared \
libweston_la_LDFLAGS = -release ${LIBWESTON_ABI_VERSION}
libweston_la_SOURCES = \
- src/git-version.h \
- src/log.c \
- src/compositor.c \
- src/compositor.h \
- src/compositor-drm.h \
- src/compositor-fbdev.h \
- src/compositor-headless.h \
- src/compositor-rdp.h \
- src/compositor-wayland.h \
- src/compositor-x11.h \
- src/input.c \
- src/data-device.c \
- src/screenshooter.c \
- src/clipboard.c \
- src/zoom.c \
- src/bindings.c \
- src/animation.c \
- src/noop-renderer.c \
- src/pixman-renderer.c \
- src/pixman-renderer.h \
- src/timeline.c \
- src/timeline.h \
- src/timeline-object.h \
- src/linux-dmabuf.c \
- src/linux-dmabuf.h \
+ libweston/git-version.h \
+ libweston/log.c \
+ libweston/compositor.c \
+ libweston/compositor.h \
+ libweston/compositor-drm.h \
+ libweston/compositor-fbdev.h \
+ libweston/compositor-headless.h \
+ libweston/compositor-rdp.h \
+ libweston/compositor-wayland.h \
+ libweston/compositor-x11.h \
+ libweston/input.c \
+ libweston/data-device.c \
+ libweston/screenshooter.c \
+ libweston/clipboard.c \
+ libweston/zoom.c \
+ libweston/bindings.c \
+ libweston/animation.c \
+ libweston/noop-renderer.c \
+ libweston/pixman-renderer.c \
+ libweston/pixman-renderer.h \
+ libweston/timeline.c \
+ libweston/timeline.h \
+ libweston/timeline-object.h \
+ libweston/linux-dmabuf.c \
+ libweston/linux-dmabuf.h \
shared/helpers.h \
shared/matrix.c \
shared/matrix.h \
shared/timespec-util.h \
shared/zalloc.h \
shared/platform.h \
- src/weston-egl-ext.h
+ libweston/weston-egl-ext.h
if SYSTEMD_NOTIFY_SUPPORT
module_LTLIBRARIES += systemd-notify.la
compositor/systemd-notify.c \
shared/helpers.h \
shared/zalloc.h \
- src/compositor.h
+ libweston/compositor.h
endif
nodist_libweston_la_SOURCES = \
# add BUILT_SOURCES to CLEANFILES, but we want to keep git-version.h
# in case we're building from tarballs.
-src/compositor.c : $(top_builddir)/src/git-version.h
+libweston/compositor.c : $(top_builddir)/libweston/git-version.h
noinst_LTLIBRARIES += \
libsession-helper.la
libsession_helper_la_SOURCES = \
- src/launcher-util.c \
- src/launcher-util.h \
- src/launcher-impl.h \
- src/weston-launch.h \
- src/launcher-weston-launch.c \
- src/launcher-direct.c
+ libweston/launcher-util.c \
+ libweston/launcher-util.h \
+ libweston/launcher-impl.h \
+ libweston/weston-launch.h \
+ libweston/launcher-weston-launch.c \
+ libweston/launcher-direct.c
libsession_helper_la_CFLAGS = $(AM_CFLAGS) $(LIBDRM_CFLAGS) $(PIXMAN_CFLAGS) $(COMPOSITOR_CFLAGS)
libsession_helper_la_LIBADD = $(LIBDRM_LIBS)
if ENABLE_DBUS
if HAVE_SYSTEMD_LOGIN
libsession_helper_la_SOURCES += \
- src/dbus.h \
- src/dbus.c \
- src/launcher-logind.c
+ libweston/dbus.h \
+ libweston/dbus.c \
+ libweston/launcher-logind.c
libsession_helper_la_CFLAGS += $(SYSTEMD_LOGIN_CFLAGS) $(DBUS_CFLAGS)
libsession_helper_la_LIBADD += $(SYSTEMD_LOGIN_LIBS) $(DBUS_LIBS)
endif
endif
if HAVE_GIT_REPO
-src/git-version.h : $(top_srcdir)/.git/logs/HEAD
+libweston/git-version.h : $(top_srcdir)/.git/logs/HEAD
$(AM_V_GEN)echo "#define BUILD_ID \"$(shell git --git-dir=$(top_srcdir)/.git describe --always --dirty) $(shell git --git-dir=$(top_srcdir)/.git log -1 --format='%s (%ci)')\"" > $@
else
-src/git-version.h :
+libweston/git-version.h :
$(AM_V_GEN)echo "#define BUILD_ID \"unknown (not built from git or tarball)\"" > $@
endif
if BUILD_WESTON_LAUNCH
bin_PROGRAMS += weston-launch
-weston_launch_SOURCES = src/weston-launch.c src/weston-launch.h
+weston_launch_SOURCES = libweston/weston-launch.c libweston/weston-launch.h
weston_launch_CPPFLAGS = -DBINDIR='"$(bindir)"'
weston_launch_CFLAGS= \
$(AM_CFLAGS) \
endif # BUILD_WESTON_LAUNCH
pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = compositor/weston.pc src/libweston-${LIBWESTON_ABI_VERSION}.pc
+pkgconfig_DATA = compositor/weston.pc libweston/libweston-${LIBWESTON_ABI_VERSION}.pc
wayland_sessiondir = $(datadir)/wayland-sessions
dist_wayland_session_DATA = compositor/weston.desktop
libwestonincludedir = $(includedir)/libweston-${LIBWESTON_ABI_VERSION}
-libwestoninclude_HEADERS = \
- src/version.h \
- src/compositor.h \
- src/compositor-drm.h \
- src/compositor-fbdev.h \
- src/compositor-headless.h \
- src/compositor-rdp.h \
- src/compositor-wayland.h \
- src/compositor-x11.h \
- src/timeline-object.h \
+libwestoninclude_HEADERS = \
+ libweston/version.h \
+ libweston/compositor.h \
+ libweston/compositor-drm.h \
+ libweston/compositor-fbdev.h \
+ libweston/compositor-headless.h \
+ libweston/compositor-rdp.h \
+ libweston/compositor-wayland.h \
+ libweston/compositor-x11.h \
+ libweston/timeline-object.h \
shared/matrix.h \
shared/config-parser.h \
shared/zalloc.h \
$(GL_RENDERER_CFLAGS) \
$(AM_CFLAGS)
gl_renderer_la_SOURCES = \
- src/gl-renderer.h \
- src/gl-renderer.c \
- src/vertex-clipping.c \
- src/vertex-clipping.h \
+ libweston/gl-renderer.h \
+ libweston/gl-renderer.c \
+ libweston/vertex-clipping.c \
+ libweston/vertex-clipping.h \
shared/helpers.h
endif
$(X11_COMPOSITOR_CFLAGS) \
$(AM_CFLAGS)
x11_backend_la_SOURCES = \
- src/compositor-x11.c \
- src/compositor-x11.h \
+ libweston/compositor-x11.c \
+ libweston/compositor-x11.h \
shared/helpers.h
endif
INPUT_BACKEND_LIBS = $(LIBINPUT_BACKEND_LIBS)
INPUT_BACKEND_SOURCES = \
- src/libinput-seat.c \
- src/libinput-seat.h \
- src/libinput-device.c \
- src/libinput-device.h \
+ libweston/libinput-seat.c \
+ libweston/libinput-seat.h \
+ libweston/libinput-device.c \
+ libweston/libinput-device.h \
shared/helpers.h
if ENABLE_DRM_COMPOSITOR
$(DRM_COMPOSITOR_CFLAGS) \
$(AM_CFLAGS)
drm_backend_la_SOURCES = \
- src/compositor-drm.c \
- src/compositor-drm.h \
+ libweston/compositor-drm.c \
+ libweston/compositor-drm.h \
$(INPUT_BACKEND_SOURCES) \
shared/helpers.h \
shared/timespec-util.h \
- src/libbacklight.c \
- src/libbacklight.h
+ libweston/libbacklight.c \
+ libweston/libbacklight.h
if ENABLE_VAAPI_RECORDER
-drm_backend_la_SOURCES += src/vaapi-recorder.c src/vaapi-recorder.h
+drm_backend_la_SOURCES += libweston/vaapi-recorder.c libweston/vaapi-recorder.h
drm_backend_la_LIBADD += $(LIBVA_LIBS)
drm_backend_la_CFLAGS += $(LIBVA_CFLAGS)
endif
$(WAYLAND_COMPOSITOR_CFLAGS) \
$(AM_CFLAGS)
wayland_backend_la_SOURCES = \
- src/compositor-wayland.c \
- src/compositor-wayland.h \
+ libweston/compositor-wayland.c \
+ libweston/compositor-wayland.h \
shared/helpers.h
nodist_wayland_backend_la_SOURCES = \
protocol/fullscreen-shell-unstable-v1-protocol.c \
headless_backend_la_LIBADD = $(COMPOSITOR_LIBS) libshared.la
headless_backend_la_CFLAGS = $(COMPOSITOR_CFLAGS) $(AM_CFLAGS)
headless_backend_la_SOURCES = \
- src/compositor-headless.c \
- src/compositor-headless.h \
+ libweston/compositor-headless.c \
+ libweston/compositor-headless.h \
shared/helpers.h
endif
$(PIXMAN_CFLAGS) \
$(AM_CFLAGS)
fbdev_backend_la_SOURCES = \
- src/compositor-fbdev.c \
- src/compositor-fbdev.h \
+ libweston/compositor-fbdev.c \
+ libweston/compositor-fbdev.h \
shared/helpers.h \
$(INPUT_BACKEND_SOURCES)
endif
$(RDP_COMPOSITOR_CFLAGS) \
$(AM_CFLAGS)
rdp_backend_la_SOURCES = \
- src/compositor-rdp.c \
- src/compositor-rdp.h \
+ libweston/compositor-rdp.c \
+ libweston/compositor-rdp.h \
shared/helpers.h
endif
spring_tool_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS)
spring_tool_LDADD = $(COMPOSITOR_LIBS) -lm
spring_tool_SOURCES = \
- src/spring-tool.c \
- src/animation.c \
+ libweston/spring-tool.c \
+ libweston/animation.c \
shared/matrix.c \
shared/matrix.h \
- src/compositor.h
+ libweston/compositor.h
if BUILD_CLIENTS
weston_cliptest_SOURCES = \
clients/cliptest.c \
- src/vertex-clipping.c \
- src/vertex-clipping.h
+ libweston/vertex-clipping.c \
+ libweston/vertex-clipping.h
weston_cliptest_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS)
weston_cliptest_LDADD = libtoytoolkit.la
desktop_shell_la_CPPFLAGS = \
-I$(top_builddir)/protocol \
-I$(top_srcdir)/shared \
- -I$(top_srcdir)/src \
- -I$(top_builddir)/src \
+ -I$(top_srcdir)/libweston \
+ -I$(top_builddir)/libweston \
-I$(top_builddir)/desktop-shell \
-DDATADIR='"$(datadir)"' \
-DMODULEDIR='"$(moduledir)"' \
fullscreen_shell_la_CPPFLAGS = \
-I$(top_builddir)/protocol \
-I$(top_srcdir)/shared \
- -I$(top_srcdir)/src \
- -I$(top_builddir)/src \
+ -I$(top_srcdir)/libweston \
+ -I$(top_builddir)/libweston \
-DIN_WESTON
fullscreen_shell_la_LDFLAGS = -module -avoid-version
xwayland_la_CPPFLAGS = \
-I$(top_builddir)/protocol \
-I$(top_srcdir)/shared \
- -I$(top_srcdir)/src \
- -I$(top_builddir)/src \
+ -I$(top_srcdir)/libweston \
+ -I$(top_builddir)/libweston \
-I$(top_builddir)/xwayland \
-DDATADIR='"$(datadir)"' \
-DMODULEDIR='"$(moduledir)"' \
vertex_clip_test_SOURCES = \
tests/vertex-clip-test.c \
shared/helpers.h \
- src/vertex-clipping.c \
- src/vertex-clipping.h
+ libweston/vertex-clipping.c \
+ libweston/vertex-clipping.h
vertex_clip_test_LDADD = libtest-runner.la -lm $(CLOCK_GETTIME_LIBS)
libtest_client_la_SOURCES = \
noinst_PROGRAMS += setbacklight
setbacklight_SOURCES = \
tests/setbacklight.c \
- src/libbacklight.c \
- src/libbacklight.h
+ libweston/libbacklight.c \
+ libweston/libbacklight.h
setbacklight_CFLAGS = $(AM_CFLAGS) $(SETBACKLIGHT_CFLAGS)
setbacklight_LDADD = $(SETBACKLIGHT_LIBS)
endif
#include <linux/input.h>
#include <wayland-client.h>
-#include "src/vertex-clipping.h"
+#include "libweston/vertex-clipping.h"
#include "shared/xalloc.h"
#include "window.h"
PKG_CHECK_MODULES(SYSTEMD_DAEMON, [libsystemd])
fi
-AC_CONFIG_FILES([Makefile src/version.h compositor/weston.pc])
+AC_CONFIG_FILES([Makefile libweston/version.h compositor/weston.pc])
# AC_CONFIG_FILES needs the full name when running autoconf, so we need to use
# libweston_abi_version here, and outside [] because of m4 quoting rules
-AC_CONFIG_FILES([src/libweston-]libweston_abi_version[.pc:src/libweston.pc.in])
+AC_CONFIG_FILES([libweston/libweston-]libweston_abi_version[.pc:libweston/libweston.pc.in])
AM_CONDITIONAL([HAVE_GIT_REPO], [test -f $srcdir/.git/logs/HEAD])
--- /dev/null
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "compositor.h"
+#include "shared/helpers.h"
+
+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;
+ spring->clip = WESTON_SPRING_OVERSHOOT;
+ spring->min = 0.0;
+ spring->max = 1.0;
+}
+
+WL_EXPORT void
+weston_spring_update(struct weston_spring *spring, uint32_t msec)
+{
+ double force, v, current, step;
+
+ /* Limit the number of executions of the loop below by ensuring that
+ * the timestamp for last update of the spring is no more than 1s ago.
+ * This handles the case where time moves backwards or forwards in
+ * large jumps.
+ */
+ if (msec - spring->timestamp > 1000) {
+ weston_log("unexpectedly large timestamp jump (from %u to %u)\n",
+ spring->timestamp, msec);
+ spring->timestamp = msec - 1000;
+ }
+
+ 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;
+
+ switch (spring->clip) {
+ case WESTON_SPRING_OVERSHOOT:
+ break;
+
+ case WESTON_SPRING_CLAMP:
+ if (spring->current > spring->max) {
+ spring->current = spring->max;
+ spring->previous = spring->max;
+ } else if (spring->current < 0.0) {
+ spring->current = spring->min;
+ spring->previous = spring->min;
+ }
+ break;
+
+ case WESTON_SPRING_BOUNCE:
+ if (spring->current > spring->max) {
+ spring->current =
+ 2 * spring->max - spring->current;
+ spring->previous =
+ 2 * spring->max - spring->previous;
+ } else if (spring->current < spring->min) {
+ spring->current =
+ 2 * spring->min - spring->current;
+ spring->previous =
+ 2 * spring->min - spring->previous;
+ }
+ break;
+ }
+
+ spring->timestamp += 4;
+ }
+}
+
+WL_EXPORT int
+weston_spring_done(struct weston_spring *spring)
+{
+ return fabs(spring->previous - spring->target) < 0.002 &&
+ fabs(spring->current - spring->target) < 0.002;
+}
+
+typedef void (*weston_view_animation_frame_func_t)(struct weston_view_animation *animation);
+
+struct weston_view_animation {
+ struct weston_view *view;
+ struct weston_animation animation;
+ struct weston_spring spring;
+ struct weston_transform transform;
+ struct wl_listener listener;
+ float start, stop;
+ weston_view_animation_frame_func_t frame;
+ weston_view_animation_frame_func_t reset;
+ weston_view_animation_done_func_t done;
+ void *data;
+ void *private;
+};
+
+WL_EXPORT void
+weston_view_animation_destroy(struct weston_view_animation *animation)
+{
+ wl_list_remove(&animation->animation.link);
+ wl_list_remove(&animation->listener.link);
+ wl_list_remove(&animation->transform.link);
+ if (animation->reset)
+ animation->reset(animation);
+ weston_view_geometry_dirty(animation->view);
+ if (animation->done)
+ animation->done(animation, animation->data);
+ free(animation);
+}
+
+static void
+handle_animation_view_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_view_animation *animation =
+ container_of(listener,
+ struct weston_view_animation, listener);
+
+ weston_view_animation_destroy(animation);
+}
+
+static void
+weston_view_animation_frame(struct weston_animation *base,
+ struct weston_output *output, uint32_t msecs)
+{
+ struct weston_view_animation *animation =
+ container_of(base,
+ struct weston_view_animation, animation);
+ struct weston_compositor *compositor =
+ animation->view->surface->compositor;
+
+ if (base->frame_counter <= 1)
+ animation->spring.timestamp = msecs;
+
+ weston_spring_update(&animation->spring, msecs);
+
+ if (weston_spring_done(&animation->spring)) {
+ weston_view_schedule_repaint(animation->view);
+ weston_view_animation_destroy(animation);
+ return;
+ }
+
+ if (animation->frame)
+ animation->frame(animation);
+
+ weston_view_geometry_dirty(animation->view);
+ weston_view_schedule_repaint(animation->view);
+
+ /* The view's output_mask will be zero if its position is
+ * offscreen. Animations should always run but as they are also
+ * run off the repaint cycle, if there's nothing to repaint
+ * the animation stops running. Therefore if we catch this situation
+ * and schedule a repaint on all outputs it will be avoided.
+ */
+ if (animation->view->output_mask == 0)
+ weston_compositor_schedule_repaint(compositor);
+}
+
+static struct weston_view_animation *
+weston_view_animation_create(struct weston_view *view,
+ float start, float stop,
+ weston_view_animation_frame_func_t frame,
+ weston_view_animation_frame_func_t reset,
+ weston_view_animation_done_func_t done,
+ void *data,
+ void *private)
+{
+ struct weston_view_animation *animation;
+
+ animation = malloc(sizeof *animation);
+ if (!animation)
+ return NULL;
+
+ animation->view = view;
+ animation->frame = frame;
+ animation->reset = reset;
+ animation->done = done;
+ animation->data = data;
+ animation->start = start;
+ animation->stop = stop;
+ animation->private = private;
+
+ weston_matrix_init(&animation->transform.matrix);
+ wl_list_insert(&view->geometry.transformation_list,
+ &animation->transform.link);
+
+ animation->animation.frame = weston_view_animation_frame;
+
+ animation->listener.notify = handle_animation_view_destroy;
+ wl_signal_add(&view->destroy_signal, &animation->listener);
+
+ wl_list_insert(&view->output->animation_list,
+ &animation->animation.link);
+
+ return animation;
+}
+
+static void
+weston_view_animation_run(struct weston_view_animation *animation)
+{
+ animation->animation.frame_counter = 0;
+ weston_view_animation_frame(&animation->animation, NULL, 0);
+}
+
+static void
+reset_alpha(struct weston_view_animation *animation)
+{
+ struct weston_view *view = animation->view;
+
+ view->alpha = animation->stop;
+}
+
+static void
+zoom_frame(struct weston_view_animation *animation)
+{
+ struct weston_view *es = animation->view;
+ float scale;
+
+ scale = animation->start +
+ (animation->stop - animation->start) *
+ animation->spring.current;
+ weston_matrix_init(&animation->transform.matrix);
+ weston_matrix_translate(&animation->transform.matrix,
+ -0.5f * es->surface->width,
+ -0.5f * es->surface->height, 0);
+ weston_matrix_scale(&animation->transform.matrix, scale, scale, scale);
+ weston_matrix_translate(&animation->transform.matrix,
+ 0.5f * es->surface->width,
+ 0.5f * es->surface->height, 0);
+
+ es->alpha = animation->spring.current;
+ if (es->alpha > 1.0)
+ es->alpha = 1.0;
+}
+
+WL_EXPORT struct weston_view_animation *
+weston_zoom_run(struct weston_view *view, float start, float stop,
+ weston_view_animation_done_func_t done, void *data)
+{
+ struct weston_view_animation *zoom;
+
+ zoom = weston_view_animation_create(view, start, stop,
+ zoom_frame, reset_alpha,
+ done, data, NULL);
+
+ if (zoom == NULL)
+ return NULL;
+
+ weston_spring_init(&zoom->spring, 300.0, start, stop);
+ zoom->spring.friction = 1400;
+ zoom->spring.previous = start - (stop - start) * 0.03;
+
+ weston_view_animation_run(zoom);
+
+ return zoom;
+}
+
+static void
+fade_frame(struct weston_view_animation *animation)
+{
+ if (animation->spring.current > 0.999)
+ animation->view->alpha = 1;
+ else if (animation->spring.current < 0.001 )
+ animation->view->alpha = 0;
+ else
+ animation->view->alpha = animation->spring.current;
+}
+
+WL_EXPORT struct weston_view_animation *
+weston_fade_run(struct weston_view *view,
+ float start, float end, float k,
+ weston_view_animation_done_func_t done, void *data)
+{
+ struct weston_view_animation *fade;
+
+ fade = weston_view_animation_create(view, start, end,
+ fade_frame, reset_alpha,
+ done, data, NULL);
+
+ if (fade == NULL)
+ return NULL;
+
+ weston_spring_init(&fade->spring, 1000.0, start, end);
+ fade->spring.friction = 4000;
+ fade->spring.previous = start - (end - start) * 0.1;
+
+ view->alpha = start;
+
+ weston_view_animation_run(fade);
+
+ return fade;
+}
+
+WL_EXPORT void
+weston_fade_update(struct weston_view_animation *fade, float target)
+{
+ fade->spring.target = target;
+ fade->stop = target;
+}
+
+static void
+stable_fade_frame(struct weston_view_animation *animation)
+{
+ struct weston_view *back_view;
+
+ if (animation->spring.current > 0.999)
+ animation->view->alpha = 1;
+ else if (animation->spring.current < 0.001 )
+ animation->view->alpha = 0;
+ else
+ animation->view->alpha = animation->spring.current;
+
+ back_view = (struct weston_view *) animation->private;
+ back_view->alpha =
+ (animation->spring.target - animation->view->alpha) /
+ (1.0 - animation->view->alpha);
+ weston_view_geometry_dirty(back_view);
+}
+
+WL_EXPORT struct weston_view_animation *
+weston_stable_fade_run(struct weston_view *front_view, float start,
+ struct weston_view *back_view, float end,
+ weston_view_animation_done_func_t done, void *data)
+{
+ struct weston_view_animation *fade;
+
+ fade = weston_view_animation_create(front_view, 0, 0,
+ stable_fade_frame, NULL,
+ done, data, back_view);
+
+ if (fade == NULL)
+ return NULL;
+
+ weston_spring_init(&fade->spring, 400, start, end);
+ fade->spring.friction = 1150;
+
+ front_view->alpha = start;
+ back_view->alpha = end;
+
+ weston_view_animation_run(fade);
+
+ return fade;
+}
+
+static void
+slide_frame(struct weston_view_animation *animation)
+{
+ float scale;
+
+ scale = animation->start +
+ (animation->stop - animation->start) *
+ animation->spring.current;
+ weston_matrix_init(&animation->transform.matrix);
+ weston_matrix_translate(&animation->transform.matrix, 0, scale, 0);
+}
+
+WL_EXPORT struct weston_view_animation *
+weston_slide_run(struct weston_view *view, float start, float stop,
+ weston_view_animation_done_func_t done, void *data)
+{
+ struct weston_view_animation *animation;
+
+ animation = weston_view_animation_create(view, start, stop,
+ slide_frame, NULL, done,
+ data, NULL);
+ if (!animation)
+ return NULL;
+
+ weston_spring_init(&animation->spring, 400.0, 0.0, 1.0);
+ animation->spring.friction = 600;
+ animation->spring.clip = WESTON_SPRING_BOUNCE;
+
+ weston_view_animation_run(animation);
+
+ return animation;
+}
+
+struct weston_move_animation {
+ int dx;
+ int dy;
+ int reverse;
+ weston_view_animation_done_func_t done;
+};
+
+static void
+move_frame(struct weston_view_animation *animation)
+{
+ struct weston_move_animation *move = animation->private;
+ float scale;
+ float progress = animation->spring.current;
+
+ if (move->reverse)
+ progress = 1.0 - progress;
+
+ scale = animation->start +
+ (animation->stop - animation->start) *
+ progress;
+ weston_matrix_init(&animation->transform.matrix);
+ weston_matrix_scale(&animation->transform.matrix, scale, scale, 1.0f);
+ weston_matrix_translate(&animation->transform.matrix,
+ move->dx * progress, move->dy * progress,
+ 0);
+}
+
+static void
+move_done(struct weston_view_animation *animation, void *data)
+{
+ struct weston_move_animation *move = animation->private;
+
+ if (move->done)
+ move->done(animation, data);
+
+ free(move);
+}
+
+WL_EXPORT struct weston_view_animation *
+weston_move_scale_run(struct weston_view *view, int dx, int dy,
+ float start, float end, int reverse,
+ weston_view_animation_done_func_t done, void *data)
+{
+ struct weston_move_animation *move;
+ struct weston_view_animation *animation;
+
+ move = malloc(sizeof(*move));
+ if (!move)
+ return NULL;
+ move->dx = dx;
+ move->dy = dy;
+ move->reverse = reverse;
+ move->done = done;
+
+ animation = weston_view_animation_create(view, start, end, move_frame,
+ NULL, move_done, data, move);
+
+ if (animation == NULL){
+ free(move);
+ return NULL;
+ }
+
+ weston_spring_init(&animation->spring, 400.0, 0.0, 1.0);
+ animation->spring.friction = 1150;
+
+ weston_view_animation_run(animation);
+
+ return animation;
+}
--- /dev/null
+/*
+ * Copyright © 2011-2012 Intel Corporation
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <linux/input.h>
+
+#include "compositor.h"
+#include "shared/helpers.h"
+
+struct weston_binding {
+ uint32_t key;
+ uint32_t button;
+ uint32_t axis;
+ uint32_t modifier;
+ void *handler;
+ void *data;
+ struct wl_list link;
+};
+
+static struct weston_binding *
+weston_compositor_add_binding(struct weston_compositor *compositor,
+ uint32_t key, uint32_t button, uint32_t axis,
+ uint32_t modifier, void *handler, void *data)
+{
+ struct weston_binding *binding;
+
+ binding = malloc(sizeof *binding);
+ if (binding == NULL)
+ return NULL;
+
+ binding->key = key;
+ binding->button = button;
+ binding->axis = axis;
+ binding->modifier = modifier;
+ binding->handler = handler;
+ binding->data = data;
+
+ return binding;
+}
+
+WL_EXPORT struct weston_binding *
+weston_compositor_add_key_binding(struct weston_compositor *compositor,
+ uint32_t key, uint32_t modifier,
+ weston_key_binding_handler_t handler,
+ void *data)
+{
+ struct weston_binding *binding;
+
+ binding = weston_compositor_add_binding(compositor, key, 0, 0,
+ modifier, handler, data);
+ if (binding == NULL)
+ return NULL;
+
+ wl_list_insert(compositor->key_binding_list.prev, &binding->link);
+
+ return binding;
+}
+
+WL_EXPORT struct weston_binding *
+weston_compositor_add_modifier_binding(struct weston_compositor *compositor,
+ uint32_t modifier,
+ weston_modifier_binding_handler_t handler,
+ void *data)
+{
+ struct weston_binding *binding;
+
+ binding = weston_compositor_add_binding(compositor, 0, 0, 0,
+ modifier, handler, data);
+ if (binding == NULL)
+ return NULL;
+
+ wl_list_insert(compositor->modifier_binding_list.prev, &binding->link);
+
+ return binding;
+}
+
+WL_EXPORT struct weston_binding *
+weston_compositor_add_button_binding(struct weston_compositor *compositor,
+ uint32_t button, uint32_t modifier,
+ weston_button_binding_handler_t handler,
+ void *data)
+{
+ struct weston_binding *binding;
+
+ binding = weston_compositor_add_binding(compositor, 0, button, 0,
+ modifier, handler, data);
+ if (binding == NULL)
+ return NULL;
+
+ wl_list_insert(compositor->button_binding_list.prev, &binding->link);
+
+ return binding;
+}
+
+WL_EXPORT struct weston_binding *
+weston_compositor_add_touch_binding(struct weston_compositor *compositor,
+ uint32_t modifier,
+ weston_touch_binding_handler_t handler,
+ void *data)
+{
+ struct weston_binding *binding;
+
+ binding = weston_compositor_add_binding(compositor, 0, 0, 0,
+ modifier, handler, data);
+ if (binding == NULL)
+ return NULL;
+
+ wl_list_insert(compositor->touch_binding_list.prev, &binding->link);
+
+ return binding;
+}
+
+WL_EXPORT struct weston_binding *
+weston_compositor_add_axis_binding(struct weston_compositor *compositor,
+ uint32_t axis, uint32_t modifier,
+ weston_axis_binding_handler_t handler,
+ void *data)
+{
+ struct weston_binding *binding;
+
+ binding = weston_compositor_add_binding(compositor, 0, 0, axis,
+ modifier, handler, data);
+ if (binding == NULL)
+ return NULL;
+
+ wl_list_insert(compositor->axis_binding_list.prev, &binding->link);
+
+ return binding;
+}
+
+WL_EXPORT struct weston_binding *
+weston_compositor_add_debug_binding(struct weston_compositor *compositor,
+ uint32_t key,
+ weston_key_binding_handler_t handler,
+ void *data)
+{
+ struct weston_binding *binding;
+
+ binding = weston_compositor_add_binding(compositor, key, 0, 0, 0,
+ handler, data);
+
+ wl_list_insert(compositor->debug_binding_list.prev, &binding->link);
+
+ return binding;
+}
+
+WL_EXPORT void
+weston_binding_destroy(struct weston_binding *binding)
+{
+ wl_list_remove(&binding->link);
+ free(binding);
+}
+
+void
+weston_binding_list_destroy_all(struct wl_list *list)
+{
+ struct weston_binding *binding, *tmp;
+
+ wl_list_for_each_safe(binding, tmp, list, link)
+ weston_binding_destroy(binding);
+}
+
+struct binding_keyboard_grab {
+ uint32_t key;
+ struct weston_keyboard_grab grab;
+};
+
+static void
+binding_key(struct weston_keyboard_grab *grab,
+ uint32_t time, uint32_t key, uint32_t state_w)
+{
+ struct binding_keyboard_grab *b =
+ container_of(grab, struct binding_keyboard_grab, grab);
+ struct wl_resource *resource;
+ enum wl_keyboard_key_state state = state_w;
+ uint32_t serial;
+ struct weston_keyboard *keyboard = grab->keyboard;
+ struct wl_display *display = keyboard->seat->compositor->wl_display;
+
+ if (key == b->key) {
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED) {
+ weston_keyboard_end_grab(grab->keyboard);
+ if (keyboard->input_method_resource)
+ keyboard->grab = &keyboard->input_method_grab;
+ free(b);
+ } else {
+ /* Don't send the key press event for the binding key */
+ return;
+ }
+ }
+ if (!wl_list_empty(&keyboard->focus_resource_list)) {
+ serial = wl_display_next_serial(display);
+ wl_resource_for_each(resource, &keyboard->focus_resource_list) {
+ wl_keyboard_send_key(resource,
+ serial,
+ time,
+ key,
+ state);
+ }
+ }
+}
+
+static void
+binding_modifiers(struct weston_keyboard_grab *grab, uint32_t serial,
+ uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group)
+{
+ struct wl_resource *resource;
+
+ wl_resource_for_each(resource, &grab->keyboard->focus_resource_list) {
+ wl_keyboard_send_modifiers(resource, serial, mods_depressed,
+ mods_latched, mods_locked, group);
+ }
+}
+
+static void
+binding_cancel(struct weston_keyboard_grab *grab)
+{
+ struct binding_keyboard_grab *binding_grab =
+ container_of(grab, struct binding_keyboard_grab, grab);
+
+ weston_keyboard_end_grab(grab->keyboard);
+ free(binding_grab);
+}
+
+static const struct weston_keyboard_grab_interface binding_grab = {
+ binding_key,
+ binding_modifiers,
+ binding_cancel,
+};
+
+static void
+install_binding_grab(struct weston_keyboard *keyboard, uint32_t time,
+ uint32_t key, struct weston_surface *focus)
+{
+ struct binding_keyboard_grab *grab;
+
+ grab = malloc(sizeof *grab);
+ grab->key = key;
+ grab->grab.interface = &binding_grab;
+ weston_keyboard_start_grab(keyboard, &grab->grab);
+
+ /* Notify the surface which had the focus before this binding
+ * triggered that we stole a keypress from under it, by forcing
+ * a wl_keyboard leave/enter pair. The enter event will contain
+ * the pressed key in the keys array, so the client will know
+ * the exact state of the keyboard.
+ * If the old focus surface is different than the new one it
+ * means it was changed in the binding handler, so it received
+ * the enter event already. */
+ if (focus && keyboard->focus == focus) {
+ weston_keyboard_set_focus(keyboard, NULL);
+ weston_keyboard_set_focus(keyboard, focus);
+ }
+}
+
+void
+weston_compositor_run_key_binding(struct weston_compositor *compositor,
+ struct weston_keyboard *keyboard,
+ uint32_t time, uint32_t key,
+ enum wl_keyboard_key_state state)
+{
+ struct weston_binding *b, *tmp;
+ struct weston_surface *focus;
+ struct weston_seat *seat = keyboard->seat;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+ return;
+
+ /* Invalidate all active modifier bindings. */
+ wl_list_for_each(b, &compositor->modifier_binding_list, link)
+ b->key = key;
+
+ wl_list_for_each_safe(b, tmp, &compositor->key_binding_list, link) {
+ if (b->key == key && b->modifier == seat->modifier_state) {
+ weston_key_binding_handler_t handler = b->handler;
+ focus = keyboard->focus;
+ handler(keyboard, time, key, b->data);
+
+ /* If this was a key binding and it didn't
+ * install a keyboard grab, install one now to
+ * swallow the key press. */
+ if (keyboard->grab ==
+ &keyboard->default_grab)
+ install_binding_grab(keyboard,
+ time,
+ key,
+ focus);
+ }
+ }
+}
+
+void
+weston_compositor_run_modifier_binding(struct weston_compositor *compositor,
+ struct weston_keyboard *keyboard,
+ enum weston_keyboard_modifier modifier,
+ enum wl_keyboard_key_state state)
+{
+ struct weston_binding *b, *tmp;
+
+ if (keyboard->grab != &keyboard->default_grab)
+ return;
+
+ wl_list_for_each_safe(b, tmp, &compositor->modifier_binding_list, link) {
+ weston_modifier_binding_handler_t handler = b->handler;
+
+ if (b->modifier != modifier)
+ continue;
+
+ /* Prime the modifier binding. */
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ b->key = 0;
+ continue;
+ }
+ /* Ignore the binding if a key was pressed in between. */
+ else if (b->key != 0) {
+ return;
+ }
+
+ handler(keyboard, modifier, b->data);
+ }
+}
+
+void
+weston_compositor_run_button_binding(struct weston_compositor *compositor,
+ struct weston_pointer *pointer,
+ uint32_t time, uint32_t button,
+ enum wl_pointer_button_state state)
+{
+ struct weston_binding *b, *tmp;
+
+ if (state == WL_POINTER_BUTTON_STATE_RELEASED)
+ return;
+
+ /* Invalidate all active modifier bindings. */
+ wl_list_for_each(b, &compositor->modifier_binding_list, link)
+ b->key = button;
+
+ wl_list_for_each_safe(b, tmp, &compositor->button_binding_list, link) {
+ if (b->button == button &&
+ b->modifier == pointer->seat->modifier_state) {
+ weston_button_binding_handler_t handler = b->handler;
+ handler(pointer, time, button, b->data);
+ }
+ }
+}
+
+void
+weston_compositor_run_touch_binding(struct weston_compositor *compositor,
+ struct weston_touch *touch, uint32_t time,
+ int touch_type)
+{
+ struct weston_binding *b, *tmp;
+
+ if (touch->num_tp != 1 || touch_type != WL_TOUCH_DOWN)
+ return;
+
+ wl_list_for_each_safe(b, tmp, &compositor->touch_binding_list, link) {
+ if (b->modifier == touch->seat->modifier_state) {
+ weston_touch_binding_handler_t handler = b->handler;
+ handler(touch, time, b->data);
+ }
+ }
+}
+
+int
+weston_compositor_run_axis_binding(struct weston_compositor *compositor,
+ struct weston_pointer *pointer,
+ uint32_t time,
+ struct weston_pointer_axis_event *event)
+{
+ struct weston_binding *b, *tmp;
+
+ /* Invalidate all active modifier bindings. */
+ wl_list_for_each(b, &compositor->modifier_binding_list, link)
+ b->key = event->axis;
+
+ wl_list_for_each_safe(b, tmp, &compositor->axis_binding_list, link) {
+ if (b->axis == event->axis &&
+ b->modifier == pointer->seat->modifier_state) {
+ weston_axis_binding_handler_t handler = b->handler;
+ handler(pointer, time, event, b->data);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+weston_compositor_run_debug_binding(struct weston_compositor *compositor,
+ struct weston_keyboard *keyboard,
+ uint32_t time, uint32_t key,
+ enum wl_keyboard_key_state state)
+{
+ weston_key_binding_handler_t handler;
+ struct weston_binding *binding, *tmp;
+ int count = 0;
+
+ wl_list_for_each_safe(binding, tmp, &compositor->debug_binding_list, link) {
+ if (key != binding->key)
+ continue;
+
+ count++;
+ handler = binding->handler;
+ handler(keyboard, time, key, binding->data);
+ }
+
+ return count;
+}
+
+struct debug_binding_grab {
+ struct weston_keyboard_grab grab;
+ struct weston_seat *seat;
+ uint32_t key[2];
+ int key_released[2];
+};
+
+static void
+debug_binding_key(struct weston_keyboard_grab *grab, uint32_t time,
+ uint32_t key, uint32_t state)
+{
+ struct debug_binding_grab *db = (struct debug_binding_grab *) grab;
+ struct weston_compositor *ec = db->seat->compositor;
+ struct wl_display *display = ec->wl_display;
+ struct wl_resource *resource;
+ uint32_t serial;
+ int send = 0, terminate = 0;
+ int check_binding = 1;
+ int i;
+ struct wl_list *resource_list;
+
+ if (state == WL_KEYBOARD_KEY_STATE_RELEASED) {
+ /* Do not run bindings on key releases */
+ check_binding = 0;
+
+ for (i = 0; i < 2; i++)
+ if (key == db->key[i])
+ db->key_released[i] = 1;
+
+ if (db->key_released[0] && db->key_released[1]) {
+ /* All key releases been swalled so end the grab */
+ terminate = 1;
+ } else if (key != db->key[0] && key != db->key[1]) {
+ /* Should not swallow release of other keys */
+ send = 1;
+ }
+ } else if (key == db->key[0] && !db->key_released[0]) {
+ /* Do not check bindings for the first press of the binding
+ * key. This allows it to be used as a debug shortcut.
+ * We still need to swallow this event. */
+ check_binding = 0;
+ } else if (db->key[1]) {
+ /* If we already ran a binding don't process another one since
+ * we can't keep track of all the binding keys that were
+ * pressed in order to swallow the release events. */
+ send = 1;
+ check_binding = 0;
+ }
+
+ if (check_binding) {
+ if (weston_compositor_run_debug_binding(ec, grab->keyboard,
+ time, key, state)) {
+ /* We ran a binding so swallow the press and keep the
+ * grab to swallow the released too. */
+ send = 0;
+ terminate = 0;
+ db->key[1] = key;
+ } else {
+ /* Terminate the grab since the key pressed is not a
+ * debug binding key. */
+ send = 1;
+ terminate = 1;
+ }
+ }
+
+ if (send) {
+ serial = wl_display_next_serial(display);
+ resource_list = &grab->keyboard->focus_resource_list;
+ wl_resource_for_each(resource, resource_list) {
+ wl_keyboard_send_key(resource, serial, time, key, state);
+ }
+ }
+
+ if (terminate) {
+ weston_keyboard_end_grab(grab->keyboard);
+ if (grab->keyboard->input_method_resource)
+ grab->keyboard->grab = &grab->keyboard->input_method_grab;
+ free(db);
+ }
+}
+
+static void
+debug_binding_modifiers(struct weston_keyboard_grab *grab, uint32_t serial,
+ uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group)
+{
+ struct wl_resource *resource;
+ struct wl_list *resource_list;
+
+ resource_list = &grab->keyboard->focus_resource_list;
+
+ wl_resource_for_each(resource, resource_list) {
+ wl_keyboard_send_modifiers(resource, serial, mods_depressed,
+ mods_latched, mods_locked, group);
+ }
+}
+
+static void
+debug_binding_cancel(struct weston_keyboard_grab *grab)
+{
+ struct debug_binding_grab *db = (struct debug_binding_grab *) grab;
+
+ weston_keyboard_end_grab(grab->keyboard);
+ free(db);
+}
+
+struct weston_keyboard_grab_interface debug_binding_keyboard_grab = {
+ debug_binding_key,
+ debug_binding_modifiers,
+ debug_binding_cancel,
+};
+
+static void
+debug_binding(struct weston_keyboard *keyboard, uint32_t time,
+ uint32_t key, void *data)
+{
+ struct debug_binding_grab *grab;
+
+ grab = calloc(1, sizeof *grab);
+ if (!grab)
+ return;
+
+ grab->seat = keyboard->seat;
+ grab->key[0] = key;
+ grab->grab.interface = &debug_binding_keyboard_grab;
+ weston_keyboard_start_grab(keyboard, &grab->grab);
+}
+
+/** Install the trigger binding for debug bindings.
+ *
+ * \param compositor The compositor.
+ * \param mod The modifier.
+ *
+ * This will add a key binding for modifier+SHIFT+SPACE that will trigger
+ * debug key bindings.
+ */
+WL_EXPORT void
+weston_install_debug_key_binding(struct weston_compositor *compositor,
+ uint32_t mod)
+{
+ weston_compositor_add_key_binding(compositor, KEY_SPACE,
+ mod | MODIFIER_SHIFT,
+ debug_binding, NULL);
+}
--- /dev/null
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <linux/input.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/uio.h>
+
+#include "compositor.h"
+#include "shared/helpers.h"
+
+struct clipboard_source {
+ struct weston_data_source base;
+ struct wl_array contents;
+ struct clipboard *clipboard;
+ struct wl_event_source *event_source;
+ uint32_t serial;
+ int refcount;
+ int fd;
+};
+
+struct clipboard {
+ struct weston_seat *seat;
+ struct wl_listener selection_listener;
+ struct wl_listener destroy_listener;
+ struct clipboard_source *source;
+};
+
+static void clipboard_client_create(struct clipboard_source *source, int fd);
+
+static void
+clipboard_source_unref(struct clipboard_source *source)
+{
+ char **s;
+
+ source->refcount--;
+ if (source->refcount > 0)
+ return;
+
+ if (source->event_source) {
+ wl_event_source_remove(source->event_source);
+ close(source->fd);
+ }
+ wl_signal_emit(&source->base.destroy_signal,
+ &source->base);
+ s = source->base.mime_types.data;
+ free(*s);
+ wl_array_release(&source->base.mime_types);
+ wl_array_release(&source->contents);
+ free(source);
+}
+
+static int
+clipboard_source_data(int fd, uint32_t mask, void *data)
+{
+ struct clipboard_source *source = data;
+ struct clipboard *clipboard = source->clipboard;
+ char *p;
+ int len, size;
+
+ if (source->contents.alloc - source->contents.size < 1024) {
+ wl_array_add(&source->contents, 1024);
+ source->contents.size -= 1024;
+ }
+
+ p = source->contents.data + source->contents.size;
+ size = source->contents.alloc - source->contents.size;
+ len = read(fd, p, size);
+ if (len == 0) {
+ wl_event_source_remove(source->event_source);
+ close(fd);
+ source->event_source = NULL;
+ } else if (len < 0) {
+ clipboard_source_unref(source);
+ clipboard->source = NULL;
+ } else {
+ source->contents.size += len;
+ }
+
+ return 1;
+}
+
+static void
+clipboard_source_accept(struct weston_data_source *source,
+ uint32_t time, const char *mime_type)
+{
+}
+
+static void
+clipboard_source_send(struct weston_data_source *base,
+ const char *mime_type, int32_t fd)
+{
+ struct clipboard_source *source =
+ container_of(base, struct clipboard_source, base);
+ char **s;
+
+ s = source->base.mime_types.data;
+ if (strcmp(mime_type, s[0]) == 0)
+ clipboard_client_create(source, fd);
+ else
+ close(fd);
+}
+
+static void
+clipboard_source_cancel(struct weston_data_source *source)
+{
+}
+
+static struct clipboard_source *
+clipboard_source_create(struct clipboard *clipboard,
+ const char *mime_type, uint32_t serial, int fd)
+{
+ struct wl_display *display = clipboard->seat->compositor->wl_display;
+ struct wl_event_loop *loop = wl_display_get_event_loop(display);
+ struct clipboard_source *source;
+ char **s;
+
+ source = zalloc(sizeof *source);
+ if (source == NULL)
+ return NULL;
+
+ wl_array_init(&source->contents);
+ wl_array_init(&source->base.mime_types);
+ source->base.resource = NULL;
+ source->base.accept = clipboard_source_accept;
+ source->base.send = clipboard_source_send;
+ source->base.cancel = clipboard_source_cancel;
+ wl_signal_init(&source->base.destroy_signal);
+ source->refcount = 1;
+ source->clipboard = clipboard;
+ source->serial = serial;
+ source->fd = fd;
+
+ s = wl_array_add(&source->base.mime_types, sizeof *s);
+ if (s == NULL)
+ goto err_add;
+ *s = strdup(mime_type);
+ if (*s == NULL)
+ goto err_strdup;
+ source->event_source =
+ wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
+ clipboard_source_data, source);
+ if (source->event_source == NULL)
+ goto err_source;
+
+ return source;
+
+ err_source:
+ free(*s);
+ err_strdup:
+ wl_array_release(&source->base.mime_types);
+ err_add:
+ free(source);
+
+ return NULL;
+}
+
+struct clipboard_client {
+ struct wl_event_source *event_source;
+ size_t offset;
+ struct clipboard_source *source;
+};
+
+static int
+clipboard_client_data(int fd, uint32_t mask, void *data)
+{
+ struct clipboard_client *client = data;
+ char *p;
+ size_t size;
+ int len;
+
+ size = client->source->contents.size;
+ p = client->source->contents.data;
+ len = write(fd, p + client->offset, size - client->offset);
+ if (len > 0)
+ client->offset += len;
+
+ if (client->offset == size || len <= 0) {
+ close(fd);
+ wl_event_source_remove(client->event_source);
+ clipboard_source_unref(client->source);
+ free(client);
+ }
+
+ return 1;
+}
+
+static void
+clipboard_client_create(struct clipboard_source *source, int fd)
+{
+ struct weston_seat *seat = source->clipboard->seat;
+ struct clipboard_client *client;
+ struct wl_event_loop *loop =
+ wl_display_get_event_loop(seat->compositor->wl_display);
+
+ client = zalloc(sizeof *client);
+ if (client == NULL)
+ return;
+
+ client->source = source;
+ source->refcount++;
+ client->event_source =
+ wl_event_loop_add_fd(loop, fd, WL_EVENT_WRITABLE,
+ clipboard_client_data, client);
+}
+
+static void
+clipboard_set_selection(struct wl_listener *listener, void *data)
+{
+ struct clipboard *clipboard =
+ container_of(listener, struct clipboard, selection_listener);
+ struct weston_seat *seat = data;
+ struct weston_data_source *source = seat->selection_data_source;
+ const char **mime_types;
+ int p[2];
+
+ if (source == NULL) {
+ if (clipboard->source)
+ weston_seat_set_selection(seat,
+ &clipboard->source->base,
+ clipboard->source->serial);
+ return;
+ } else if (source->accept == clipboard_source_accept) {
+ /* Callback for our data source. */
+ return;
+ }
+
+ if (clipboard->source)
+ clipboard_source_unref(clipboard->source);
+
+ clipboard->source = NULL;
+
+ mime_types = source->mime_types.data;
+
+ if (!mime_types || pipe2(p, O_CLOEXEC) == -1)
+ return;
+
+ source->send(source, mime_types[0], p[1]);
+
+ clipboard->source =
+ clipboard_source_create(clipboard, mime_types[0],
+ seat->selection_serial, p[0]);
+ if (clipboard->source == NULL) {
+ close(p[0]);
+ return;
+ }
+}
+
+static void
+clipboard_destroy(struct wl_listener *listener, void *data)
+{
+ struct clipboard *clipboard =
+ container_of(listener, struct clipboard, destroy_listener);
+
+ wl_list_remove(&clipboard->selection_listener.link);
+ wl_list_remove(&clipboard->destroy_listener.link);
+
+ free(clipboard);
+}
+
+struct clipboard *
+clipboard_create(struct weston_seat *seat)
+{
+ struct clipboard *clipboard;
+
+ clipboard = zalloc(sizeof *clipboard);
+ if (clipboard == NULL)
+ return NULL;
+
+ clipboard->seat = seat;
+ clipboard->selection_listener.notify = clipboard_set_selection;
+ clipboard->destroy_listener.notify = clipboard_destroy;
+
+ wl_signal_add(&seat->selection_signal,
+ &clipboard->selection_listener);
+ wl_signal_add(&seat->destroy_signal,
+ &clipboard->destroy_listener);
+
+ return clipboard;
+}
--- /dev/null
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2011 Intel Corporation
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/input.h>
+#include <linux/vt.h>
+#include <assert.h>
+#include <sys/mman.h>
+#include <dlfcn.h>
+#include <time.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <drm_fourcc.h>
+
+#include <gbm.h>
+#include <libudev.h>
+
+#include "compositor.h"
+#include "compositor-drm.h"
+#include "shared/helpers.h"
+#include "shared/timespec-util.h"
+#include "gl-renderer.h"
+#include "pixman-renderer.h"
+#include "libbacklight.h"
+#include "libinput-seat.h"
+#include "launcher-util.h"
+#include "vaapi-recorder.h"
+#include "presentation-time-server-protocol.h"
+#include "linux-dmabuf.h"
+
+#ifndef DRM_CAP_TIMESTAMP_MONOTONIC
+#define DRM_CAP_TIMESTAMP_MONOTONIC 0x6
+#endif
+
+#ifndef DRM_CAP_CURSOR_WIDTH
+#define DRM_CAP_CURSOR_WIDTH 0x8
+#endif
+
+#ifndef DRM_CAP_CURSOR_HEIGHT
+#define DRM_CAP_CURSOR_HEIGHT 0x9
+#endif
+
+#ifndef GBM_BO_USE_CURSOR
+#define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64
+#endif
+
+struct drm_backend {
+ struct weston_backend base;
+ struct weston_compositor *compositor;
+
+ struct udev *udev;
+ struct wl_event_source *drm_source;
+
+ struct udev_monitor *udev_monitor;
+ struct wl_event_source *udev_drm_source;
+
+ struct {
+ int id;
+ int fd;
+ char *filename;
+ } drm;
+ struct gbm_device *gbm;
+ uint32_t *crtcs;
+ int num_crtcs;
+ uint32_t crtc_allocator;
+ uint32_t connector_allocator;
+ struct wl_listener session_listener;
+ uint32_t gbm_format;
+
+ /* we need these parameters in order to not fail drmModeAddFB2()
+ * due to out of bounds dimensions, and then mistakenly set
+ * sprites_are_broken:
+ */
+ uint32_t min_width, max_width;
+ uint32_t min_height, max_height;
+ int no_addfb2;
+
+ struct wl_list sprite_list;
+ int sprites_are_broken;
+ int sprites_hidden;
+
+ int cursors_are_broken;
+
+ int use_pixman;
+
+ uint32_t prev_state;
+
+ struct udev_input input;
+
+ int32_t cursor_width;
+ int32_t cursor_height;
+
+ /** Callback used to configure the outputs.
+ *
+ * This function will be called by the backend when a new DRM
+ * output needs to be configured.
+ */
+ enum weston_drm_backend_output_mode
+ (*configure_output)(struct weston_compositor *compositor,
+ bool use_current_mode,
+ const char *name,
+ struct weston_drm_backend_output_config *output_config);
+ bool use_current_mode;
+};
+
+struct drm_mode {
+ struct weston_mode base;
+ drmModeModeInfo mode_info;
+};
+
+struct drm_fb {
+ uint32_t fb_id, stride, handle, size;
+ int fd;
+ int is_client_buffer;
+ struct weston_buffer_reference buffer_ref;
+
+ /* Used by gbm fbs */
+ struct gbm_bo *bo;
+
+ /* Used by dumb fbs */
+ void *map;
+};
+
+struct drm_edid {
+ char eisa_id[13];
+ char monitor_name[13];
+ char pnp_id[5];
+ char serial_number[13];
+};
+
+struct drm_output {
+ struct weston_output base;
+
+ uint32_t crtc_id;
+ int pipe;
+ uint32_t connector_id;
+ drmModeCrtcPtr original_crtc;
+ struct drm_edid edid;
+ drmModePropertyPtr dpms_prop;
+ uint32_t gbm_format;
+
+ enum dpms_enum dpms;
+
+ int vblank_pending;
+ int page_flip_pending;
+ int destroy_pending;
+
+ struct gbm_surface *gbm_surface;
+ struct gbm_bo *gbm_cursor_bo[2];
+ struct weston_plane cursor_plane;
+ struct weston_plane fb_plane;
+ struct weston_view *cursor_view;
+ int current_cursor;
+ struct drm_fb *current, *next;
+ struct backlight *backlight;
+
+ struct drm_fb *dumb[2];
+ pixman_image_t *image[2];
+ int current_image;
+ pixman_region32_t previous_damage;
+
+ struct vaapi_recorder *recorder;
+ struct wl_listener recorder_frame_listener;
+};
+
+/*
+ * An output has a primary display plane plus zero or more sprites for
+ * blending display contents.
+ */
+struct drm_sprite {
+ struct wl_list link;
+
+ struct weston_plane plane;
+
+ struct drm_fb *current, *next;
+ struct drm_output *output;
+ struct drm_backend *backend;
+
+ uint32_t possible_crtcs;
+ uint32_t plane_id;
+ uint32_t count_formats;
+
+ int32_t src_x, src_y;
+ uint32_t src_w, src_h;
+ uint32_t dest_x, dest_y;
+ uint32_t dest_w, dest_h;
+
+ uint32_t formats[];
+};
+
+static struct gl_renderer_interface *gl_renderer;
+
+static const char default_seat[] = "seat0";
+
+static void
+drm_output_set_cursor(struct drm_output *output);
+
+static void
+drm_output_update_msc(struct drm_output *output, unsigned int seq);
+
+static int
+drm_sprite_crtc_supported(struct drm_output *output, uint32_t supported)
+{
+ struct weston_compositor *ec = output->base.compositor;
+ struct drm_backend *b =(struct drm_backend *)ec->backend;
+ int crtc;
+
+ for (crtc = 0; crtc < b->num_crtcs; crtc++) {
+ if (b->crtcs[crtc] != output->crtc_id)
+ continue;
+
+ if (supported & (1 << crtc))
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
+{
+ struct drm_fb *fb = data;
+ struct gbm_device *gbm = gbm_bo_get_device(bo);
+
+ if (fb->fb_id)
+ drmModeRmFB(gbm_device_get_fd(gbm), fb->fb_id);
+
+ weston_buffer_reference(&fb->buffer_ref, NULL);
+
+ free(data);
+}
+
+static struct drm_fb *
+drm_fb_create_dumb(struct drm_backend *b, unsigned width, unsigned height,
+ uint32_t format)
+{
+ struct drm_fb *fb;
+ int ret;
+ uint32_t bpp, depth;
+
+ struct drm_mode_create_dumb create_arg;
+ struct drm_mode_destroy_dumb destroy_arg;
+ struct drm_mode_map_dumb map_arg;
+
+ fb = zalloc(sizeof *fb);
+ if (!fb)
+ return NULL;
+
+ switch (format) {
+ case GBM_FORMAT_XRGB8888:
+ bpp = 32;
+ depth = 24;
+ break;
+ case GBM_FORMAT_RGB565:
+ bpp = depth = 16;
+ break;
+ default:
+ return NULL;
+ }
+
+ memset(&create_arg, 0, sizeof create_arg);
+ create_arg.bpp = bpp;
+ create_arg.width = width;
+ create_arg.height = height;
+
+ ret = drmIoctl(b->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg);
+ if (ret)
+ goto err_fb;
+
+ fb->handle = create_arg.handle;
+ fb->stride = create_arg.pitch;
+ fb->size = create_arg.size;
+ fb->fd = b->drm.fd;
+
+ ret = -1;
+
+ if (!b->no_addfb2) {
+ uint32_t handles[4], pitches[4], offsets[4];
+
+ handles[0] = fb->handle;
+ pitches[0] = fb->stride;
+ offsets[0] = 0;
+
+ ret = drmModeAddFB2(b->drm.fd, width, height,
+ format, handles, pitches, offsets,
+ &fb->fb_id, 0);
+ if (ret) {
+ weston_log("addfb2 failed: %m\n");
+ b->no_addfb2 = 1;
+ }
+ }
+
+ if (ret) {
+ ret = drmModeAddFB(b->drm.fd, width, height, depth, bpp,
+ fb->stride, fb->handle, &fb->fb_id);
+ }
+
+ if (ret)
+ goto err_bo;
+
+ memset(&map_arg, 0, sizeof map_arg);
+ map_arg.handle = fb->handle;
+ ret = drmIoctl(fb->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_arg);
+ if (ret)
+ goto err_add_fb;
+
+ fb->map = mmap(NULL, fb->size, PROT_WRITE,
+ MAP_SHARED, b->drm.fd, map_arg.offset);
+ if (fb->map == MAP_FAILED)
+ goto err_add_fb;
+
+ return fb;
+
+err_add_fb:
+ drmModeRmFB(b->drm.fd, fb->fb_id);
+err_bo:
+ memset(&destroy_arg, 0, sizeof(destroy_arg));
+ destroy_arg.handle = create_arg.handle;
+ drmIoctl(b->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
+err_fb:
+ free(fb);
+ return NULL;
+}
+
+static void
+drm_fb_destroy_dumb(struct drm_fb *fb)
+{
+ struct drm_mode_destroy_dumb destroy_arg;
+
+ if (!fb->map)
+ return;
+
+ if (fb->fb_id)
+ drmModeRmFB(fb->fd, fb->fb_id);
+
+ weston_buffer_reference(&fb->buffer_ref, NULL);
+
+ munmap(fb->map, fb->size);
+
+ memset(&destroy_arg, 0, sizeof(destroy_arg));
+ destroy_arg.handle = fb->handle;
+ drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
+
+ free(fb);
+}
+
+static struct drm_fb *
+drm_fb_get_from_bo(struct gbm_bo *bo,
+ struct drm_backend *backend, uint32_t format)
+{
+ struct drm_fb *fb = gbm_bo_get_user_data(bo);
+ uint32_t width, height;
+ uint32_t handles[4], pitches[4], offsets[4];
+ int ret;
+
+ if (fb)
+ return fb;
+
+ fb = zalloc(sizeof *fb);
+ if (fb == NULL)
+ return NULL;
+
+ fb->bo = bo;
+
+ width = gbm_bo_get_width(bo);
+ height = gbm_bo_get_height(bo);
+ fb->stride = gbm_bo_get_stride(bo);
+ fb->handle = gbm_bo_get_handle(bo).u32;
+ fb->size = fb->stride * height;
+ fb->fd = backend->drm.fd;
+
+ if (backend->min_width > width || width > backend->max_width ||
+ backend->min_height > height ||
+ height > backend->max_height) {
+ weston_log("bo geometry out of bounds\n");
+ goto err_free;
+ }
+
+ ret = -1;
+
+ if (format && !backend->no_addfb2) {
+ handles[0] = fb->handle;
+ pitches[0] = fb->stride;
+ offsets[0] = 0;
+
+ ret = drmModeAddFB2(backend->drm.fd, width, height,
+ format, handles, pitches, offsets,
+ &fb->fb_id, 0);
+ if (ret) {
+ weston_log("addfb2 failed: %m\n");
+ backend->no_addfb2 = 1;
+ backend->sprites_are_broken = 1;
+ }
+ }
+
+ if (ret)
+ ret = drmModeAddFB(backend->drm.fd, width, height, 24, 32,
+ fb->stride, fb->handle, &fb->fb_id);
+
+ if (ret) {
+ weston_log("failed to create kms fb: %m\n");
+ goto err_free;
+ }
+
+ gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
+
+ return fb;
+
+err_free:
+ free(fb);
+ return NULL;
+}
+
+static void
+drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer)
+{
+ assert(fb->buffer_ref.buffer == NULL);
+
+ fb->is_client_buffer = 1;
+
+ weston_buffer_reference(&fb->buffer_ref, buffer);
+}
+
+static void
+drm_output_release_fb(struct drm_output *output, struct drm_fb *fb)
+{
+ if (!fb)
+ return;
+
+ if (fb->map &&
+ (fb != output->dumb[0] && fb != output->dumb[1])) {
+ drm_fb_destroy_dumb(fb);
+ } else if (fb->bo) {
+ if (fb->is_client_buffer)
+ gbm_bo_destroy(fb->bo);
+ else
+ gbm_surface_release_buffer(output->gbm_surface,
+ fb->bo);
+ }
+}
+
+static uint32_t
+drm_output_check_scanout_format(struct drm_output *output,
+ struct weston_surface *es, struct gbm_bo *bo)
+{
+ uint32_t format;
+ pixman_region32_t r;
+
+ format = gbm_bo_get_format(bo);
+
+ if (format == GBM_FORMAT_ARGB8888) {
+ /* We can scanout an ARGB buffer if the surface's
+ * opaque region covers the whole output, but we have
+ * to use XRGB as the KMS format code. */
+ pixman_region32_init_rect(&r, 0, 0,
+ output->base.width,
+ output->base.height);
+ pixman_region32_subtract(&r, &r, &es->opaque);
+
+ if (!pixman_region32_not_empty(&r))
+ format = GBM_FORMAT_XRGB8888;
+
+ pixman_region32_fini(&r);
+ }
+
+ if (output->gbm_format == format)
+ return format;
+
+ return 0;
+}
+
+static struct weston_plane *
+drm_output_prepare_scanout_view(struct drm_output *output,
+ struct weston_view *ev)
+{
+ struct drm_backend *b =
+ (struct drm_backend *)output->base.compositor->backend;
+ struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
+ struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
+ struct gbm_bo *bo;
+ uint32_t format;
+
+ if (ev->geometry.x != output->base.x ||
+ ev->geometry.y != output->base.y ||
+ buffer == NULL || b->gbm == NULL ||
+ buffer->width != output->base.current_mode->width ||
+ buffer->height != output->base.current_mode->height ||
+ output->base.transform != viewport->buffer.transform ||
+ ev->transform.enabled)
+ return NULL;
+
+ if (ev->geometry.scissor_enabled)
+ return NULL;
+
+ bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
+ buffer->resource, GBM_BO_USE_SCANOUT);
+
+ /* Unable to use the buffer for scanout */
+ if (!bo)
+ return NULL;
+
+ format = drm_output_check_scanout_format(output, ev->surface, bo);
+ if (format == 0) {
+ gbm_bo_destroy(bo);
+ return NULL;
+ }
+
+ output->next = drm_fb_get_from_bo(bo, b, format);
+ if (!output->next) {
+ gbm_bo_destroy(bo);
+ return NULL;
+ }
+
+ drm_fb_set_buffer(output->next, buffer);
+
+ return &output->fb_plane;
+}
+
+static void
+drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage)
+{
+ struct drm_backend *b =
+ (struct drm_backend *)output->base.compositor->backend;
+ struct gbm_bo *bo;
+
+ output->base.compositor->renderer->repaint_output(&output->base,
+ damage);
+
+ bo = gbm_surface_lock_front_buffer(output->gbm_surface);
+ if (!bo) {
+ weston_log("failed to lock front buffer: %m\n");
+ return;
+ }
+
+ output->next = drm_fb_get_from_bo(bo, b, output->gbm_format);
+ if (!output->next) {
+ weston_log("failed to get drm_fb for bo\n");
+ gbm_surface_release_buffer(output->gbm_surface, bo);
+ return;
+ }
+}
+
+static void
+drm_output_render_pixman(struct drm_output *output, pixman_region32_t *damage)
+{
+ struct weston_compositor *ec = output->base.compositor;
+ pixman_region32_t total_damage, previous_damage;
+
+ pixman_region32_init(&total_damage);
+ pixman_region32_init(&previous_damage);
+
+ pixman_region32_copy(&previous_damage, damage);
+
+ pixman_region32_union(&total_damage, damage, &output->previous_damage);
+ pixman_region32_copy(&output->previous_damage, &previous_damage);
+
+ output->current_image ^= 1;
+
+ output->next = output->dumb[output->current_image];
+ pixman_renderer_output_set_buffer(&output->base,
+ output->image[output->current_image]);
+
+ ec->renderer->repaint_output(&output->base, &total_damage);
+
+ pixman_region32_fini(&total_damage);
+ pixman_region32_fini(&previous_damage);
+}
+
+static void
+drm_output_render(struct drm_output *output, pixman_region32_t *damage)
+{
+ struct weston_compositor *c = output->base.compositor;
+ struct drm_backend *b = (struct drm_backend *)c->backend;
+
+ if (b->use_pixman)
+ drm_output_render_pixman(output, damage);
+ else
+ drm_output_render_gl(output, damage);
+
+ pixman_region32_subtract(&c->primary_plane.damage,
+ &c->primary_plane.damage, damage);
+}
+
+static void
+drm_output_set_gamma(struct weston_output *output_base,
+ uint16_t size, uint16_t *r, uint16_t *g, uint16_t *b)
+{
+ int rc;
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct drm_backend *backend =
+ (struct drm_backend *) output->base.compositor->backend;
+
+ /* check */
+ if (output_base->gamma_size != size)
+ return;
+ if (!output->original_crtc)
+ return;
+
+ rc = drmModeCrtcSetGamma(backend->drm.fd,
+ output->crtc_id,
+ size, r, g, b);
+ if (rc)
+ weston_log("set gamma failed: %m\n");
+}
+
+/* Determine the type of vblank synchronization to use for the output.
+ *
+ * The pipe parameter indicates which CRTC is in use. Knowing this, we
+ * can determine which vblank sequence type to use for it. Traditional
+ * cards had only two CRTCs, with CRTC 0 using no special flags, and
+ * CRTC 1 using DRM_VBLANK_SECONDARY. The first bit of the pipe
+ * parameter indicates this.
+ *
+ * Bits 1-5 of the pipe parameter are 5 bit wide pipe number between
+ * 0-31. If this is non-zero it indicates we're dealing with a
+ * multi-gpu situation and we need to calculate the vblank sync
+ * using DRM_BLANK_HIGH_CRTC_MASK.
+ */
+static unsigned int
+drm_waitvblank_pipe(struct drm_output *output)
+{
+ if (output->pipe > 1)
+ return (output->pipe << DRM_VBLANK_HIGH_CRTC_SHIFT) &
+ DRM_VBLANK_HIGH_CRTC_MASK;
+ else if (output->pipe > 0)
+ return DRM_VBLANK_SECONDARY;
+ else
+ return 0;
+}
+
+static int
+drm_output_repaint(struct weston_output *output_base,
+ pixman_region32_t *damage)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct drm_backend *backend =
+ (struct drm_backend *)output->base.compositor->backend;
+ struct drm_sprite *s;
+ struct drm_mode *mode;
+ int ret = 0;
+
+ if (output->destroy_pending)
+ return -1;
+
+ if (!output->next)
+ drm_output_render(output, damage);
+ if (!output->next)
+ return -1;
+
+ mode = container_of(output->base.current_mode, struct drm_mode, base);
+ if (!output->current ||
+ output->current->stride != output->next->stride) {
+ ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
+ output->next->fb_id, 0, 0,
+ &output->connector_id, 1,
+ &mode->mode_info);
+ if (ret) {
+ weston_log("set mode failed: %m\n");
+ goto err_pageflip;
+ }
+ output_base->set_dpms(output_base, WESTON_DPMS_ON);
+ }
+
+ if (drmModePageFlip(backend->drm.fd, output->crtc_id,
+ output->next->fb_id,
+ DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
+ weston_log("queueing pageflip failed: %m\n");
+ goto err_pageflip;
+ }
+
+ output->page_flip_pending = 1;
+
+ drm_output_set_cursor(output);
+
+ /*
+ * Now, update all the sprite surfaces
+ */
+ wl_list_for_each(s, &backend->sprite_list, link) {
+ uint32_t flags = 0, fb_id = 0;
+ drmVBlank vbl = {
+ .request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
+ .request.sequence = 1,
+ };
+
+ if ((!s->current && !s->next) ||
+ !drm_sprite_crtc_supported(output, s->possible_crtcs))
+ continue;
+
+ if (s->next && !backend->sprites_hidden)
+ fb_id = s->next->fb_id;
+
+ ret = drmModeSetPlane(backend->drm.fd, s->plane_id,
+ output->crtc_id, fb_id, flags,
+ s->dest_x, s->dest_y,
+ s->dest_w, s->dest_h,
+ s->src_x, s->src_y,
+ s->src_w, s->src_h);
+ if (ret)
+ weston_log("setplane failed: %d: %s\n",
+ ret, strerror(errno));
+
+ vbl.request.type |= drm_waitvblank_pipe(output);
+
+ /*
+ * Queue a vblank signal so we know when the surface
+ * becomes active on the display or has been replaced.
+ */
+ vbl.request.signal = (unsigned long)s;
+ ret = drmWaitVBlank(backend->drm.fd, &vbl);
+ if (ret) {
+ weston_log("vblank event request failed: %d: %s\n",
+ ret, strerror(errno));
+ }
+
+ s->output = output;
+ output->vblank_pending = 1;
+ }
+
+ return 0;
+
+err_pageflip:
+ output->cursor_view = NULL;
+ if (output->next) {
+ drm_output_release_fb(output, output->next);
+ output->next = NULL;
+ }
+
+ return -1;
+}
+
+static void
+drm_output_start_repaint_loop(struct weston_output *output_base)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct drm_backend *backend = (struct drm_backend *)
+ output_base->compositor->backend;
+ uint32_t fb_id;
+ struct timespec ts, tnow;
+ struct timespec vbl2now;
+ int64_t refresh_nsec;
+ int ret;
+ drmVBlank vbl = {
+ .request.type = DRM_VBLANK_RELATIVE,
+ .request.sequence = 0,
+ .request.signal = 0,
+ };
+
+ if (output->destroy_pending)
+ return;
+
+ if (!output->current) {
+ /* We can't page flip if there's no mode set */
+ goto finish_frame;
+ }
+
+ /* Try to get current msc and timestamp via instant query */
+ vbl.request.type |= drm_waitvblank_pipe(output);
+ ret = drmWaitVBlank(backend->drm.fd, &vbl);
+
+ /* Error ret or zero timestamp means failure to get valid timestamp */
+ if ((ret == 0) && (vbl.reply.tval_sec > 0 || vbl.reply.tval_usec > 0)) {
+ ts.tv_sec = vbl.reply.tval_sec;
+ ts.tv_nsec = vbl.reply.tval_usec * 1000;
+
+ /* Valid timestamp for most recent vblank - not stale?
+ * Stale ts could happen on Linux 3.17+, so make sure it
+ * is not older than 1 refresh duration since now.
+ */
+ weston_compositor_read_presentation_clock(backend->compositor,
+ &tnow);
+ timespec_sub(&vbl2now, &tnow, &ts);
+ refresh_nsec =
+ millihz_to_nsec(output->base.current_mode->refresh);
+ if (timespec_to_nsec(&vbl2now) < refresh_nsec) {
+ drm_output_update_msc(output, vbl.reply.sequence);
+ weston_output_finish_frame(output_base, &ts,
+ WP_PRESENTATION_FEEDBACK_INVALID);
+ return;
+ }
+ }
+
+ /* Immediate query didn't provide valid timestamp.
+ * Use pageflip fallback.
+ */
+ fb_id = output->current->fb_id;
+
+ if (drmModePageFlip(backend->drm.fd, output->crtc_id, fb_id,
+ DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
+ weston_log("queueing pageflip failed: %m\n");
+ goto finish_frame;
+ }
+
+ return;
+
+finish_frame:
+ /* if we cannot page-flip, immediately finish frame */
+ weston_compositor_read_presentation_clock(output_base->compositor, &ts);
+ weston_output_finish_frame(output_base, &ts,
+ WP_PRESENTATION_FEEDBACK_INVALID);
+}
+
+static void
+drm_output_update_msc(struct drm_output *output, unsigned int seq)
+{
+ uint64_t msc_hi = output->base.msc >> 32;
+
+ if (seq < (output->base.msc & 0xffffffff))
+ msc_hi++;
+
+ output->base.msc = (msc_hi << 32) + seq;
+}
+
+static void
+vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
+ void *data)
+{
+ struct drm_sprite *s = (struct drm_sprite *)data;
+ struct drm_output *output = s->output;
+ struct timespec ts;
+ uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
+ WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
+
+ drm_output_update_msc(output, frame);
+ output->vblank_pending = 0;
+
+ drm_output_release_fb(output, s->current);
+ s->current = s->next;
+ s->next = NULL;
+
+ if (!output->page_flip_pending) {
+ ts.tv_sec = sec;
+ ts.tv_nsec = usec * 1000;
+ weston_output_finish_frame(&output->base, &ts, flags);
+ }
+}
+
+static void
+drm_output_destroy(struct weston_output *output_base);
+
+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 timespec ts;
+ uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
+ WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
+ WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
+
+ drm_output_update_msc(output, frame);
+
+ /* We don't set page_flip_pending on start_repaint_loop, in that case
+ * we just want to page flip to the current buffer to get an accurate
+ * timestamp */
+ if (output->page_flip_pending) {
+ drm_output_release_fb(output, output->current);
+ output->current = output->next;
+ output->next = NULL;
+ }
+
+ output->page_flip_pending = 0;
+
+ if (output->destroy_pending)
+ drm_output_destroy(&output->base);
+ else if (!output->vblank_pending) {
+ ts.tv_sec = sec;
+ ts.tv_nsec = usec * 1000;
+ weston_output_finish_frame(&output->base, &ts, flags);
+
+ /* We can't call this from frame_notify, because the output's
+ * repaint needed flag is cleared just after that */
+ if (output->recorder)
+ weston_output_schedule_repaint(&output->base);
+ }
+}
+
+static uint32_t
+drm_output_check_sprite_format(struct drm_sprite *s,
+ struct weston_view *ev, struct gbm_bo *bo)
+{
+ uint32_t i, format;
+
+ format = gbm_bo_get_format(bo);
+
+ if (format == GBM_FORMAT_ARGB8888) {
+ pixman_region32_t r;
+
+ pixman_region32_init_rect(&r, 0, 0,
+ ev->surface->width,
+ ev->surface->height);
+ pixman_region32_subtract(&r, &r, &ev->surface->opaque);
+
+ if (!pixman_region32_not_empty(&r))
+ format = GBM_FORMAT_XRGB8888;
+
+ pixman_region32_fini(&r);
+ }
+
+ for (i = 0; i < s->count_formats; i++)
+ if (s->formats[i] == format)
+ return format;
+
+ return 0;
+}
+
+static int
+drm_view_transform_supported(struct weston_view *ev)
+{
+ return !ev->transform.enabled ||
+ (ev->transform.matrix.type < WESTON_MATRIX_TRANSFORM_ROTATE);
+}
+
+static struct weston_plane *
+drm_output_prepare_overlay_view(struct drm_output *output,
+ struct weston_view *ev)
+{
+ struct weston_compositor *ec = output->base.compositor;
+ struct drm_backend *b = (struct drm_backend *)ec->backend;
+ struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
+ struct wl_resource *buffer_resource;
+ struct drm_sprite *s;
+ struct linux_dmabuf_buffer *dmabuf;
+ int found = 0;
+ struct gbm_bo *bo;
+ pixman_region32_t dest_rect, src_rect;
+ pixman_box32_t *box, tbox;
+ uint32_t format;
+ wl_fixed_t sx1, sy1, sx2, sy2;
+
+ if (b->gbm == NULL)
+ return NULL;
+
+ if (viewport->buffer.transform != output->base.transform)
+ return NULL;
+
+ if (viewport->buffer.scale != output->base.current_scale)
+ return NULL;
+
+ if (b->sprites_are_broken)
+ return NULL;
+
+ if (ev->output_mask != (1u << output->base.id))
+ return NULL;
+
+ if (ev->surface->buffer_ref.buffer == NULL)
+ return NULL;
+ buffer_resource = ev->surface->buffer_ref.buffer->resource;
+
+ if (ev->alpha != 1.0f)
+ return NULL;
+
+ if (wl_shm_buffer_get(buffer_resource))
+ return NULL;
+
+ if (!drm_view_transform_supported(ev))
+ return NULL;
+
+ wl_list_for_each(s, &b->sprite_list, link) {
+ if (!drm_sprite_crtc_supported(output, s->possible_crtcs))
+ continue;
+
+ if (!s->next) {
+ found = 1;
+ break;
+ }
+ }
+
+ /* No sprites available */
+ if (!found)
+ return NULL;
+
+ if ((dmabuf = linux_dmabuf_buffer_get(buffer_resource))) {
+#ifdef HAVE_GBM_FD_IMPORT
+ /* XXX: TODO:
+ *
+ * Use AddFB2 directly, do not go via GBM.
+ * Add support for multiplanar formats.
+ * Both require refactoring in the DRM-backend to
+ * support a mix of gbm_bos and drmfbs.
+ */
+ struct gbm_import_fd_data gbm_dmabuf = {
+ .fd = dmabuf->attributes.fd[0],
+ .width = dmabuf->attributes.width,
+ .height = dmabuf->attributes.height,
+ .stride = dmabuf->attributes.stride[0],
+ .format = dmabuf->attributes.format
+ };
+
+ if (dmabuf->attributes.n_planes != 1 || dmabuf->attributes.offset[0] != 0)
+ return NULL;
+
+ bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
+ GBM_BO_USE_SCANOUT);
+#else
+ return NULL;
+#endif
+ } else {
+ bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
+ buffer_resource, GBM_BO_USE_SCANOUT);
+ }
+ if (!bo)
+ return NULL;
+
+ format = drm_output_check_sprite_format(s, ev, bo);
+ if (format == 0) {
+ gbm_bo_destroy(bo);
+ return NULL;
+ }
+
+ s->next = drm_fb_get_from_bo(bo, b, format);
+ if (!s->next) {
+ gbm_bo_destroy(bo);
+ return NULL;
+ }
+
+ drm_fb_set_buffer(s->next, ev->surface->buffer_ref.buffer);
+
+ box = pixman_region32_extents(&ev->transform.boundingbox);
+ s->plane.x = box->x1;
+ s->plane.y = box->y1;
+
+ /*
+ * Calculate the source & dest rects properly based on actual
+ * position (note the caller has called weston_view_update_transform()
+ * for us already).
+ */
+ pixman_region32_init(&dest_rect);
+ pixman_region32_intersect(&dest_rect, &ev->transform.boundingbox,
+ &output->base.region);
+ pixman_region32_translate(&dest_rect, -output->base.x, -output->base.y);
+ box = pixman_region32_extents(&dest_rect);
+ tbox = weston_transformed_rect(output->base.width,
+ output->base.height,
+ output->base.transform,
+ output->base.current_scale,
+ *box);
+ s->dest_x = tbox.x1;
+ s->dest_y = tbox.y1;
+ s->dest_w = tbox.x2 - tbox.x1;
+ s->dest_h = tbox.y2 - tbox.y1;
+ pixman_region32_fini(&dest_rect);
+
+ pixman_region32_init(&src_rect);
+ pixman_region32_intersect(&src_rect, &ev->transform.boundingbox,
+ &output->base.region);
+ box = pixman_region32_extents(&src_rect);
+
+ weston_view_from_global_fixed(ev,
+ wl_fixed_from_int(box->x1),
+ wl_fixed_from_int(box->y1),
+ &sx1, &sy1);
+ weston_view_from_global_fixed(ev,
+ wl_fixed_from_int(box->x2),
+ wl_fixed_from_int(box->y2),
+ &sx2, &sy2);
+
+ if (sx1 < 0)
+ sx1 = 0;
+ if (sy1 < 0)
+ sy1 = 0;
+ if (sx2 > wl_fixed_from_int(ev->surface->width))
+ sx2 = wl_fixed_from_int(ev->surface->width);
+ if (sy2 > wl_fixed_from_int(ev->surface->height))
+ sy2 = wl_fixed_from_int(ev->surface->height);
+
+ tbox.x1 = sx1;
+ tbox.y1 = sy1;
+ tbox.x2 = sx2;
+ tbox.y2 = sy2;
+
+ tbox = weston_transformed_rect(wl_fixed_from_int(ev->surface->width),
+ wl_fixed_from_int(ev->surface->height),
+ viewport->buffer.transform,
+ viewport->buffer.scale,
+ tbox);
+
+ s->src_x = tbox.x1 << 8;
+ s->src_y = tbox.y1 << 8;
+ s->src_w = (tbox.x2 - tbox.x1) << 8;
+ s->src_h = (tbox.y2 - tbox.y1) << 8;
+ pixman_region32_fini(&src_rect);
+
+ return &s->plane;
+}
+
+static struct weston_plane *
+drm_output_prepare_cursor_view(struct drm_output *output,
+ struct weston_view *ev)
+{
+ struct drm_backend *b =
+ (struct drm_backend *)output->base.compositor->backend;
+ struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
+ struct wl_shm_buffer *shmbuf;
+
+ if (ev->transform.enabled &&
+ (ev->transform.matrix.type > WESTON_MATRIX_TRANSFORM_TRANSLATE))
+ return NULL;
+ if (b->gbm == NULL)
+ return NULL;
+ if (output->base.transform != WL_OUTPUT_TRANSFORM_NORMAL)
+ return NULL;
+ if (viewport->buffer.scale != output->base.current_scale)
+ return NULL;
+ if (output->cursor_view)
+ return NULL;
+ if (ev->output_mask != (1u << output->base.id))
+ return NULL;
+ if (b->cursors_are_broken)
+ return NULL;
+ if (ev->geometry.scissor_enabled)
+ return NULL;
+ if (ev->surface->buffer_ref.buffer == NULL)
+ return NULL;
+ shmbuf = wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource);
+ if (!shmbuf)
+ return NULL;
+ if (wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888)
+ return NULL;
+ if (ev->surface->width > b->cursor_width ||
+ ev->surface->height > b->cursor_height)
+ return NULL;
+
+ output->cursor_view = ev;
+
+ return &output->cursor_plane;
+}
+
+/**
+ * Update the image for the current cursor surface
+ *
+ * @param b DRM backend structure
+ * @param bo GBM buffer object to write into
+ * @param ev View to use for cursor image
+ */
+static void
+cursor_bo_update(struct drm_backend *b, struct gbm_bo *bo,
+ struct weston_view *ev)
+{
+ struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
+ uint32_t buf[b->cursor_width * b->cursor_height];
+ int32_t stride;
+ uint8_t *s;
+ int i;
+
+ assert(buffer && buffer->shm_buffer);
+ assert(buffer->shm_buffer == wl_shm_buffer_get(buffer->resource));
+ assert(ev->surface->width <= b->cursor_width);
+ assert(ev->surface->height <= b->cursor_height);
+
+ memset(buf, 0, sizeof buf);
+ stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
+ s = wl_shm_buffer_get_data(buffer->shm_buffer);
+
+ wl_shm_buffer_begin_access(buffer->shm_buffer);
+ for (i = 0; i < ev->surface->height; i++)
+ memcpy(buf + i * b->cursor_width,
+ s + i * stride,
+ ev->surface->width * 4);
+ wl_shm_buffer_end_access(buffer->shm_buffer);
+
+ if (gbm_bo_write(bo, buf, sizeof buf) < 0)
+ weston_log("failed update cursor: %m\n");
+}
+
+static void
+drm_output_set_cursor(struct drm_output *output)
+{
+ struct weston_view *ev = output->cursor_view;
+ struct weston_buffer *buffer;
+ struct drm_backend *b =
+ (struct drm_backend *) output->base.compositor->backend;
+ EGLint handle;
+ struct gbm_bo *bo;
+ float x, y;
+
+ output->cursor_view = NULL;
+ if (ev == NULL) {
+ drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
+ output->cursor_plane.x = INT32_MIN;
+ output->cursor_plane.y = INT32_MIN;
+ return;
+ }
+
+ buffer = ev->surface->buffer_ref.buffer;
+
+ if (buffer &&
+ pixman_region32_not_empty(&output->cursor_plane.damage)) {
+ pixman_region32_fini(&output->cursor_plane.damage);
+ pixman_region32_init(&output->cursor_plane.damage);
+ output->current_cursor ^= 1;
+ bo = output->gbm_cursor_bo[output->current_cursor];
+
+ cursor_bo_update(b, bo, ev);
+ handle = gbm_bo_get_handle(bo).s32;
+ if (drmModeSetCursor(b->drm.fd, output->crtc_id, handle,
+ b->cursor_width, b->cursor_height)) {
+ weston_log("failed to set cursor: %m\n");
+ b->cursors_are_broken = 1;
+ }
+ }
+
+ weston_view_to_global_float(ev, 0, 0, &x, &y);
+
+ /* From global to output space, output transform is guaranteed to be
+ * NORMAL by drm_output_prepare_cursor_view().
+ */
+ x = (x - output->base.x) * output->base.current_scale;
+ y = (y - output->base.y) * output->base.current_scale;
+
+ if (output->cursor_plane.x != x || output->cursor_plane.y != y) {
+ if (drmModeMoveCursor(b->drm.fd, output->crtc_id, x, y)) {
+ weston_log("failed to move cursor: %m\n");
+ b->cursors_are_broken = 1;
+ }
+
+ output->cursor_plane.x = x;
+ output->cursor_plane.y = y;
+ }
+}
+
+static void
+drm_assign_planes(struct weston_output *output_base)
+{
+ struct drm_backend *b =
+ (struct drm_backend *)output_base->compositor->backend;
+ struct drm_output *output = (struct drm_output *)output_base;
+ struct weston_view *ev, *next;
+ pixman_region32_t overlap, surface_overlap;
+ struct weston_plane *primary, *next_plane;
+
+ /*
+ * Find a surface for each sprite in the output using some heuristics:
+ * 1) size
+ * 2) frequency of update
+ * 3) opacity (though some hw might support alpha blending)
+ * 4) clipping (this can be fixed with color keys)
+ *
+ * The idea is to save on blitting since this should save power.
+ * If we can get a large video surface on the sprite for example,
+ * the main display surface may not need to update at all, and
+ * the client buffer can be used directly for the sprite surface
+ * as we do for flipping full screen surfaces.
+ */
+ pixman_region32_init(&overlap);
+ primary = &output_base->compositor->primary_plane;
+
+ wl_list_for_each_safe(ev, next, &output_base->compositor->view_list, link) {
+ struct weston_surface *es = ev->surface;
+
+ /* Test whether this buffer can ever go into a plane:
+ * non-shm, or small enough to be a cursor.
+ *
+ * Also, keep a reference when using the pixman renderer.
+ * That makes it possible to do a seamless switch to the GL
+ * renderer and since the pixman renderer keeps a reference
+ * to the buffer anyway, there is no side effects.
+ */
+ if (b->use_pixman ||
+ (es->buffer_ref.buffer &&
+ (!wl_shm_buffer_get(es->buffer_ref.buffer->resource) ||
+ (ev->surface->width <= b->cursor_width &&
+ ev->surface->height <= b->cursor_height))))
+ es->keep_buffer = true;
+ else
+ es->keep_buffer = false;
+
+ pixman_region32_init(&surface_overlap);
+ pixman_region32_intersect(&surface_overlap, &overlap,
+ &ev->transform.boundingbox);
+
+ next_plane = NULL;
+ if (pixman_region32_not_empty(&surface_overlap))
+ next_plane = primary;
+ if (next_plane == NULL)
+ next_plane = drm_output_prepare_cursor_view(output, ev);
+ if (next_plane == NULL)
+ next_plane = drm_output_prepare_scanout_view(output, ev);
+ if (next_plane == NULL)
+ next_plane = drm_output_prepare_overlay_view(output, ev);
+ if (next_plane == NULL)
+ next_plane = primary;
+
+ weston_view_move_to_plane(ev, next_plane);
+
+ if (next_plane == primary)
+ pixman_region32_union(&overlap, &overlap,
+ &ev->transform.boundingbox);
+
+ if (next_plane == primary ||
+ next_plane == &output->cursor_plane) {
+ /* cursor plane involves a copy */
+ ev->psf_flags = 0;
+ } else {
+ /* All other planes are a direct scanout of a
+ * single client buffer.
+ */
+ ev->psf_flags = WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY;
+ }
+
+ pixman_region32_fini(&surface_overlap);
+ }
+ pixman_region32_fini(&overlap);
+}
+
+static void
+drm_output_fini_pixman(struct drm_output *output);
+
+static void
+drm_output_destroy(struct weston_output *output_base)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct drm_backend *b =
+ (struct drm_backend *)output->base.compositor->backend;
+ drmModeCrtcPtr origcrtc = output->original_crtc;
+
+ if (output->page_flip_pending) {
+ output->destroy_pending = 1;
+ weston_log("destroy output while page flip pending\n");
+ return;
+ }
+
+ if (output->backlight)
+ backlight_destroy(output->backlight);
+
+ drmModeFreeProperty(output->dpms_prop);
+
+ /* Turn off hardware cursor */
+ drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
+
+ /* Restore original CRTC state */
+ drmModeSetCrtc(b->drm.fd, origcrtc->crtc_id, origcrtc->buffer_id,
+ origcrtc->x, origcrtc->y,
+ &output->connector_id, 1, &origcrtc->mode);
+ drmModeFreeCrtc(origcrtc);
+
+ b->crtc_allocator &= ~(1 << output->crtc_id);
+ b->connector_allocator &= ~(1 << output->connector_id);
+
+ if (b->use_pixman) {
+ drm_output_fini_pixman(output);
+ } else {
+ gl_renderer->output_destroy(output_base);
+ gbm_surface_destroy(output->gbm_surface);
+ }
+
+ weston_plane_release(&output->fb_plane);
+ weston_plane_release(&output->cursor_plane);
+
+ weston_output_destroy(&output->base);
+
+ free(output);
+}
+
+/**
+ * Find the closest-matching mode for a given target
+ *
+ * Given a target mode, find the most suitable mode amongst the output's
+ * current mode list to use, preferring the current mode if possible, to
+ * avoid an expensive mode switch.
+ *
+ * @param output DRM output
+ * @param target_mode Mode to attempt to match
+ * @returns Pointer to a mode from the output's mode list
+ */
+static struct drm_mode *
+choose_mode (struct drm_output *output, struct weston_mode *target_mode)
+{
+ struct drm_mode *tmp_mode = NULL, *mode;
+
+ if (output->base.current_mode->width == target_mode->width &&
+ output->base.current_mode->height == target_mode->height &&
+ (output->base.current_mode->refresh == target_mode->refresh ||
+ target_mode->refresh == 0))
+ return (struct drm_mode *)output->base.current_mode;
+
+ wl_list_for_each(mode, &output->base.mode_list, base.link) {
+ if (mode->mode_info.hdisplay == target_mode->width &&
+ mode->mode_info.vdisplay == target_mode->height) {
+ if (mode->base.refresh == target_mode->refresh ||
+ target_mode->refresh == 0) {
+ return mode;
+ } else if (!tmp_mode)
+ tmp_mode = mode;
+ }
+ }
+
+ return tmp_mode;
+}
+
+static int
+drm_output_init_egl(struct drm_output *output, struct drm_backend *b);
+static int
+drm_output_init_pixman(struct drm_output *output, struct drm_backend *b);
+
+static int
+drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode)
+{
+ struct drm_output *output;
+ struct drm_mode *drm_mode;
+ struct drm_backend *b;
+
+ if (output_base == NULL) {
+ weston_log("output is NULL.\n");
+ return -1;
+ }
+
+ if (mode == NULL) {
+ weston_log("mode is NULL.\n");
+ return -1;
+ }
+
+ b = (struct drm_backend *)output_base->compositor->backend;
+ output = (struct drm_output *)output_base;
+ drm_mode = choose_mode (output, mode);
+
+ if (!drm_mode) {
+ weston_log("%s, invalid resolution:%dx%d\n", __func__, mode->width, mode->height);
+ return -1;
+ }
+
+ if (&drm_mode->base == output->base.current_mode)
+ return 0;
+
+ output->base.current_mode->flags = 0;
+
+ output->base.current_mode = &drm_mode->base;
+ output->base.current_mode->flags =
+ WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+
+ /* reset rendering stuff. */
+ drm_output_release_fb(output, output->current);
+ drm_output_release_fb(output, output->next);
+ output->current = output->next = NULL;
+
+ if (b->use_pixman) {
+ drm_output_fini_pixman(output);
+ if (drm_output_init_pixman(output, b) < 0) {
+ weston_log("failed to init output pixman state with "
+ "new mode\n");
+ return -1;
+ }
+ } else {
+ gl_renderer->output_destroy(&output->base);
+ gbm_surface_destroy(output->gbm_surface);
+
+ if (drm_output_init_egl(output, b) < 0) {
+ weston_log("failed to init output egl state with "
+ "new mode");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+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;
+ evctx.vblank_handler = vblank_handler;
+ drmHandleEvent(fd, &evctx);
+
+ return 1;
+}
+
+static int
+init_drm(struct drm_backend *b, struct udev_device *device)
+{
+ const char *filename, *sysnum;
+ uint64_t cap;
+ int fd, ret;
+ clockid_t clk_id;
+
+ sysnum = udev_device_get_sysnum(device);
+ if (sysnum)
+ b->drm.id = atoi(sysnum);
+ if (!sysnum || b->drm.id < 0) {
+ weston_log("cannot get device sysnum\n");
+ return -1;
+ }
+
+ filename = udev_device_get_devnode(device);
+ fd = weston_launcher_open(b->compositor->launcher, filename, O_RDWR);
+ if (fd < 0) {
+ /* Probably permissions error */
+ weston_log("couldn't open %s, skipping\n",
+ udev_device_get_devnode(device));
+ return -1;
+ }
+
+ weston_log("using %s\n", filename);
+
+ b->drm.fd = fd;
+ b->drm.filename = strdup(filename);
+
+ ret = drmGetCap(fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
+ if (ret == 0 && cap == 1)
+ clk_id = CLOCK_MONOTONIC;
+ else
+ clk_id = CLOCK_REALTIME;
+
+ if (weston_compositor_set_presentation_clock(b->compositor, clk_id) < 0) {
+ weston_log("Error: failed to set presentation clock %d.\n",
+ clk_id);
+ return -1;
+ }
+
+ ret = drmGetCap(fd, DRM_CAP_CURSOR_WIDTH, &cap);
+ if (ret == 0)
+ b->cursor_width = cap;
+ else
+ b->cursor_width = 64;
+
+ ret = drmGetCap(fd, DRM_CAP_CURSOR_HEIGHT, &cap);
+ if (ret == 0)
+ b->cursor_height = cap;
+ else
+ b->cursor_height = 64;
+
+ return 0;
+}
+
+static struct gbm_device *
+create_gbm_device(int fd)
+{
+ struct gbm_device *gbm;
+
+ gl_renderer = weston_load_module("gl-renderer.so",
+ "gl_renderer_interface");
+ if (!gl_renderer)
+ return NULL;
+
+ /* GBM will load a dri driver, but even though they need symbols from
+ * libglapi, in some version of Mesa they are not linked to it. Since
+ * only the gl-renderer module links to it, the call above won't make
+ * these symbols globally available, and loading the DRI driver fails.
+ * Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL. */
+ dlopen("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL);
+
+ gbm = gbm_create_device(fd);
+
+ return gbm;
+}
+
+/* When initializing EGL, if the preferred buffer format isn't available
+ * we may be able to substitute an ARGB format for an XRGB one.
+ *
+ * This returns 0 if substitution isn't possible, but 0 might be a
+ * legitimate format for other EGL platforms, so the caller is
+ * responsible for checking for 0 before calling gl_renderer->create().
+ *
+ * This works around https://bugs.freedesktop.org/show_bug.cgi?id=89689
+ * but it's entirely possible we'll see this again on other implementations.
+ */
+static int
+fallback_format_for(uint32_t format)
+{
+ switch (format) {
+ case GBM_FORMAT_XRGB8888:
+ return GBM_FORMAT_ARGB8888;
+ case GBM_FORMAT_XRGB2101010:
+ return GBM_FORMAT_ARGB2101010;
+ default:
+ return 0;
+ }
+}
+
+static int
+drm_backend_create_gl_renderer(struct drm_backend *b)
+{
+ EGLint format[3] = {
+ b->gbm_format,
+ fallback_format_for(b->gbm_format),
+ 0,
+ };
+ int n_formats = 2;
+
+ if (format[1])
+ n_formats = 3;
+ if (gl_renderer->create(b->compositor,
+ EGL_PLATFORM_GBM_KHR,
+ (void *)b->gbm,
+ gl_renderer->opaque_attribs,
+ format,
+ n_formats) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+init_egl(struct drm_backend *b)
+{
+ b->gbm = create_gbm_device(b->drm.fd);
+
+ if (!b->gbm)
+ return -1;
+
+ if (drm_backend_create_gl_renderer(b) < 0) {
+ gbm_device_destroy(b->gbm);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+init_pixman(struct drm_backend *b)
+{
+ return pixman_renderer_init(b->compositor);
+}
+
+/**
+ * Add a mode to output's mode list
+ *
+ * Copy the supplied DRM mode into a Weston mode structure, and add it to the
+ * output's mode list.
+ *
+ * @param output DRM output to add mode to
+ * @param info DRM mode structure to add
+ * @returns Newly-allocated Weston/DRM mode structure
+ */
+static struct drm_mode *
+drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info)
+{
+ struct drm_mode *mode;
+ uint64_t refresh;
+
+ mode = malloc(sizeof *mode);
+ if (mode == NULL)
+ return NULL;
+
+ mode->base.flags = 0;
+ mode->base.width = info->hdisplay;
+ mode->base.height = info->vdisplay;
+
+ /* Calculate higher precision (mHz) refresh rate */
+ refresh = (info->clock * 1000000LL / info->htotal +
+ info->vtotal / 2) / info->vtotal;
+
+ if (info->flags & DRM_MODE_FLAG_INTERLACE)
+ refresh *= 2;
+ if (info->flags & DRM_MODE_FLAG_DBLSCAN)
+ refresh /= 2;
+ if (info->vscan > 1)
+ refresh /= info->vscan;
+
+ mode->base.refresh = refresh;
+ mode->mode_info = *info;
+
+ if (info->type & DRM_MODE_TYPE_PREFERRED)
+ mode->base.flags |= WL_OUTPUT_MODE_PREFERRED;
+
+ wl_list_insert(output->base.mode_list.prev, &mode->base.link);
+
+ return mode;
+}
+
+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;
+ }
+}
+
+/* returns a value between 0-255 range, where higher is brighter */
+static uint32_t
+drm_get_backlight(struct drm_output *output)
+{
+ long brightness, max_brightness, norm;
+
+ brightness = backlight_get_brightness(output->backlight);
+ max_brightness = backlight_get_max_brightness(output->backlight);
+
+ /* convert it on a scale of 0 to 255 */
+ norm = (brightness * 255)/(max_brightness);
+
+ return (uint32_t) norm;
+}
+
+/* values accepted are between 0-255 range */
+static void
+drm_set_backlight(struct weston_output *output_base, uint32_t value)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ long max_brightness, new_brightness;
+
+ if (!output->backlight)
+ return;
+
+ if (value > 255)
+ return;
+
+ max_brightness = backlight_get_max_brightness(output->backlight);
+
+ /* get denormalized value */
+ new_brightness = (value * max_brightness) / 255;
+
+ backlight_set_brightness(output->backlight, new_brightness);
+}
+
+static drmModePropertyPtr
+drm_get_prop(int fd, drmModeConnectorPtr connector, const char *name)
+{
+ drmModePropertyPtr props;
+ int i;
+
+ for (i = 0; i < connector->count_props; i++) {
+ props = drmModeGetProperty(fd, connector->props[i]);
+ if (!props)
+ continue;
+
+ if (!strcmp(props->name, name))
+ return props;
+
+ drmModeFreeProperty(props);
+ }
+
+ return NULL;
+}
+
+static void
+drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
+{
+ struct drm_output *output = (struct drm_output *) output_base;
+ struct weston_compositor *ec = output_base->compositor;
+ struct drm_backend *b = (struct drm_backend *)ec->backend;
+ int ret;
+
+ if (!output->dpms_prop)
+ return;
+
+ ret = drmModeConnectorSetProperty(b->drm.fd, output->connector_id,
+ output->dpms_prop->prop_id, level);
+ if (ret) {
+ weston_log("DRM: DPMS: failed property set for %s\n",
+ output->base.name);
+ return;
+ }
+
+ output->dpms = level;
+}
+
+static const char * const connector_type_names[] = {
+ [DRM_MODE_CONNECTOR_Unknown] = "Unknown",
+ [DRM_MODE_CONNECTOR_VGA] = "VGA",
+ [DRM_MODE_CONNECTOR_DVII] = "DVI-I",
+ [DRM_MODE_CONNECTOR_DVID] = "DVI-D",
+ [DRM_MODE_CONNECTOR_DVIA] = "DVI-A",
+ [DRM_MODE_CONNECTOR_Composite] = "Composite",
+ [DRM_MODE_CONNECTOR_SVIDEO] = "SVIDEO",
+ [DRM_MODE_CONNECTOR_LVDS] = "LVDS",
+ [DRM_MODE_CONNECTOR_Component] = "Component",
+ [DRM_MODE_CONNECTOR_9PinDIN] = "DIN",
+ [DRM_MODE_CONNECTOR_DisplayPort] = "DP",
+ [DRM_MODE_CONNECTOR_HDMIA] = "HDMI-A",
+ [DRM_MODE_CONNECTOR_HDMIB] = "HDMI-B",
+ [DRM_MODE_CONNECTOR_TV] = "TV",
+ [DRM_MODE_CONNECTOR_eDP] = "eDP",
+#ifdef DRM_MODE_CONNECTOR_DSI
+ [DRM_MODE_CONNECTOR_VIRTUAL] = "Virtual",
+ [DRM_MODE_CONNECTOR_DSI] = "DSI",
+#endif
+};
+
+static char *
+make_connector_name(const drmModeConnector *con)
+{
+ char name[32];
+ const char *type_name = NULL;
+
+ if (con->connector_type < ARRAY_LENGTH(connector_type_names))
+ type_name = connector_type_names[con->connector_type];
+
+ if (!type_name)
+ type_name = "UNNAMED";
+
+ snprintf(name, sizeof name, "%s-%d", type_name, con->connector_type_id);
+
+ return strdup(name);
+}
+
+static int
+find_crtc_for_connector(struct drm_backend *b,
+ drmModeRes *resources, drmModeConnector *connector)
+{
+ drmModeEncoder *encoder;
+ uint32_t possible_crtcs;
+ int i, j;
+
+ for (j = 0; j < connector->count_encoders; j++) {
+ encoder = drmModeGetEncoder(b->drm.fd, connector->encoders[j]);
+ if (encoder == NULL) {
+ weston_log("Failed to get encoder.\n");
+ return -1;
+ }
+ possible_crtcs = encoder->possible_crtcs;
+ drmModeFreeEncoder(encoder);
+
+ for (i = 0; i < resources->count_crtcs; i++) {
+ if (possible_crtcs & (1 << i) &&
+ !(b->crtc_allocator & (1 << resources->crtcs[i])))
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* Init output state that depends on gl or gbm */
+static int
+drm_output_init_egl(struct drm_output *output, struct drm_backend *b)
+{
+ EGLint format[2] = {
+ output->gbm_format,
+ fallback_format_for(output->gbm_format),
+ };
+ int i, flags, n_formats = 1;
+
+ output->gbm_surface = gbm_surface_create(b->gbm,
+ output->base.current_mode->width,
+ output->base.current_mode->height,
+ format[0],
+ GBM_BO_USE_SCANOUT |
+ GBM_BO_USE_RENDERING);
+ if (!output->gbm_surface) {
+ weston_log("failed to create gbm surface\n");
+ return -1;
+ }
+
+ if (format[1])
+ n_formats = 2;
+ if (gl_renderer->output_create(&output->base,
+ (EGLNativeWindowType)output->gbm_surface,
+ output->gbm_surface,
+ gl_renderer->opaque_attribs,
+ format,
+ n_formats) < 0) {
+ weston_log("failed to create gl renderer output state\n");
+ gbm_surface_destroy(output->gbm_surface);
+ return -1;
+ }
+
+ flags = GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE;
+
+ for (i = 0; i < 2; i++) {
+ if (output->gbm_cursor_bo[i])
+ continue;
+
+ output->gbm_cursor_bo[i] =
+ gbm_bo_create(b->gbm, b->cursor_width, b->cursor_height,
+ GBM_FORMAT_ARGB8888, flags);
+ }
+
+ if (output->gbm_cursor_bo[0] == NULL || output->gbm_cursor_bo[1] == NULL) {
+ weston_log("cursor buffers unavailable, using gl cursors\n");
+ b->cursors_are_broken = 1;
+ }
+
+ return 0;
+}
+
+static int
+drm_output_init_pixman(struct drm_output *output, struct drm_backend *b)
+{
+ int w = output->base.current_mode->width;
+ int h = output->base.current_mode->height;
+ uint32_t format = output->gbm_format;
+ uint32_t pixman_format;
+ unsigned int i;
+
+ switch (format) {
+ case GBM_FORMAT_XRGB8888:
+ pixman_format = PIXMAN_x8r8g8b8;
+ break;
+ case GBM_FORMAT_RGB565:
+ pixman_format = PIXMAN_r5g6b5;
+ break;
+ default:
+ weston_log("Unsupported pixman format 0x%x\n", format);
+ return -1;
+ }
+
+ /* FIXME error checking */
+ for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
+ output->dumb[i] = drm_fb_create_dumb(b, w, h, format);
+ if (!output->dumb[i])
+ goto err;
+
+ output->image[i] =
+ pixman_image_create_bits(pixman_format, w, h,
+ output->dumb[i]->map,
+ output->dumb[i]->stride);
+ if (!output->image[i])
+ goto err;
+ }
+
+ if (pixman_renderer_output_create(&output->base) < 0)
+ goto err;
+
+ pixman_region32_init_rect(&output->previous_damage,
+ output->base.x, output->base.y, output->base.width, output->base.height);
+
+ return 0;
+
+err:
+ for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
+ if (output->dumb[i])
+ drm_fb_destroy_dumb(output->dumb[i]);
+ if (output->image[i])
+ pixman_image_unref(output->image[i]);
+
+ output->dumb[i] = NULL;
+ output->image[i] = NULL;
+ }
+
+ return -1;
+}
+
+static void
+drm_output_fini_pixman(struct drm_output *output)
+{
+ unsigned int i;
+
+ pixman_renderer_output_destroy(&output->base);
+ pixman_region32_fini(&output->previous_damage);
+
+ for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
+ drm_fb_destroy_dumb(output->dumb[i]);
+ pixman_image_unref(output->image[i]);
+ output->dumb[i] = NULL;
+ output->image[i] = NULL;
+ }
+}
+
+static void
+edid_parse_string(const uint8_t *data, char text[])
+{
+ int i;
+ int replaced = 0;
+
+ /* this is always 12 bytes, but we can't guarantee it's null
+ * terminated or not junk. */
+ strncpy(text, (const char *) data, 12);
+
+ /* guarantee our new string is null-terminated */
+ text[12] = '\0';
+
+ /* remove insane chars */
+ for (i = 0; text[i] != '\0'; i++) {
+ if (text[i] == '\n' ||
+ text[i] == '\r') {
+ text[i] = '\0';
+ break;
+ }
+ }
+
+ /* ensure string is printable */
+ for (i = 0; text[i] != '\0'; i++) {
+ if (!isprint(text[i])) {
+ text[i] = '-';
+ replaced++;
+ }
+ }
+
+ /* if the string is random junk, ignore the string */
+ if (replaced > 4)
+ text[0] = '\0';
+}
+
+#define EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING 0xfe
+#define EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME 0xfc
+#define EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER 0xff
+#define EDID_OFFSET_DATA_BLOCKS 0x36
+#define EDID_OFFSET_LAST_BLOCK 0x6c
+#define EDID_OFFSET_PNPID 0x08
+#define EDID_OFFSET_SERIAL 0x0c
+
+static int
+edid_parse(struct drm_edid *edid, const uint8_t *data, size_t length)
+{
+ int i;
+ uint32_t serial_number;
+
+ /* check header */
+ if (length < 128)
+ return -1;
+ if (data[0] != 0x00 || data[1] != 0xff)
+ return -1;
+
+ /* decode the PNP ID from three 5 bit words packed into 2 bytes
+ * /--08--\/--09--\
+ * 7654321076543210
+ * |\---/\---/\---/
+ * R C1 C2 C3 */
+ edid->pnp_id[0] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x7c) / 4) - 1;
+ edid->pnp_id[1] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x3) * 8) + ((data[EDID_OFFSET_PNPID + 1] & 0xe0) / 32) - 1;
+ edid->pnp_id[2] = 'A' + (data[EDID_OFFSET_PNPID + 1] & 0x1f) - 1;
+ edid->pnp_id[3] = '\0';
+
+ /* maybe there isn't a ASCII serial number descriptor, so use this instead */
+ serial_number = (uint32_t) data[EDID_OFFSET_SERIAL + 0];
+ serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 1] * 0x100;
+ serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 2] * 0x10000;
+ serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 3] * 0x1000000;
+ if (serial_number > 0)
+ sprintf(edid->serial_number, "%lu", (unsigned long) serial_number);
+
+ /* parse EDID data */
+ for (i = EDID_OFFSET_DATA_BLOCKS;
+ i <= EDID_OFFSET_LAST_BLOCK;
+ i += 18) {
+ /* ignore pixel clock data */
+ if (data[i] != 0)
+ continue;
+ if (data[i+2] != 0)
+ continue;
+
+ /* any useful blocks? */
+ if (data[i+3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME) {
+ edid_parse_string(&data[i+5],
+ edid->monitor_name);
+ } else if (data[i+3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER) {
+ edid_parse_string(&data[i+5],
+ edid->serial_number);
+ } else if (data[i+3] == EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING) {
+ edid_parse_string(&data[i+5],
+ edid->eisa_id);
+ }
+ }
+ return 0;
+}
+
+static void
+find_and_parse_output_edid(struct drm_backend *b,
+ struct drm_output *output,
+ drmModeConnector *connector)
+{
+ drmModePropertyBlobPtr edid_blob = NULL;
+ drmModePropertyPtr property;
+ int i;
+ int rc;
+
+ for (i = 0; i < connector->count_props && !edid_blob; i++) {
+ property = drmModeGetProperty(b->drm.fd, connector->props[i]);
+ if (!property)
+ continue;
+ if ((property->flags & DRM_MODE_PROP_BLOB) &&
+ !strcmp(property->name, "EDID")) {
+ edid_blob = drmModeGetPropertyBlob(b->drm.fd,
+ connector->prop_values[i]);
+ }
+ drmModeFreeProperty(property);
+ }
+ if (!edid_blob)
+ return;
+
+ rc = edid_parse(&output->edid,
+ edid_blob->data,
+ edid_blob->length);
+ if (!rc) {
+ weston_log("EDID data '%s', '%s', '%s'\n",
+ output->edid.pnp_id,
+ output->edid.monitor_name,
+ output->edid.serial_number);
+ if (output->edid.pnp_id[0] != '\0')
+ output->base.make = output->edid.pnp_id;
+ if (output->edid.monitor_name[0] != '\0')
+ output->base.model = output->edid.monitor_name;
+ if (output->edid.serial_number[0] != '\0')
+ output->base.serial_number = output->edid.serial_number;
+ }
+ drmModeFreePropertyBlob(edid_blob);
+}
+
+
+
+static int
+parse_modeline(const char *s, drmModeModeInfo *mode)
+{
+ char hsync[16];
+ char vsync[16];
+ float fclock;
+
+ mode->type = DRM_MODE_TYPE_USERDEF;
+ mode->hskew = 0;
+ mode->vscan = 0;
+ mode->vrefresh = 0;
+ mode->flags = 0;
+
+ if (sscanf(s, "%f %hd %hd %hd %hd %hd %hd %hd %hd %15s %15s",
+ &fclock,
+ &mode->hdisplay,
+ &mode->hsync_start,
+ &mode->hsync_end,
+ &mode->htotal,
+ &mode->vdisplay,
+ &mode->vsync_start,
+ &mode->vsync_end,
+ &mode->vtotal, hsync, vsync) != 11)
+ return -1;
+
+ mode->clock = fclock * 1000;
+ if (strcmp(hsync, "+hsync") == 0)
+ mode->flags |= DRM_MODE_FLAG_PHSYNC;
+ else if (strcmp(hsync, "-hsync") == 0)
+ mode->flags |= DRM_MODE_FLAG_NHSYNC;
+ else
+ return -1;
+
+ if (strcmp(vsync, "+vsync") == 0)
+ mode->flags |= DRM_MODE_FLAG_PVSYNC;
+ else if (strcmp(vsync, "-vsync") == 0)
+ mode->flags |= DRM_MODE_FLAG_NVSYNC;
+ else
+ return -1;
+
+ snprintf(mode->name, sizeof mode->name, "%dx%d@%.3f",
+ mode->hdisplay, mode->vdisplay, fclock);
+
+ return 0;
+}
+
+static void
+setup_output_seat_constraint(struct drm_backend *b,
+ struct weston_output *output,
+ const char *s)
+{
+ if (strcmp(s, "") != 0) {
+ struct weston_pointer *pointer;
+ struct udev_seat *seat;
+
+ seat = udev_seat_get_named(&b->input, s);
+ if (!seat)
+ return;
+
+ seat->base.output = output;
+
+ pointer = weston_seat_get_pointer(&seat->base);
+ if (pointer)
+ weston_pointer_clamp(pointer,
+ &pointer->x,
+ &pointer->y);
+ }
+}
+
+static int
+parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format)
+{
+ int ret = 0;
+
+ if (s == NULL)
+ *gbm_format = default_value;
+ else if (strcmp(s, "xrgb8888") == 0)
+ *gbm_format = GBM_FORMAT_XRGB8888;
+ else if (strcmp(s, "rgb565") == 0)
+ *gbm_format = GBM_FORMAT_RGB565;
+ else if (strcmp(s, "xrgb2101010") == 0)
+ *gbm_format = GBM_FORMAT_XRGB2101010;
+ else {
+ weston_log("fatal: unrecognized pixel format: %s\n", s);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/**
+ * Choose suitable mode for an output
+ *
+ * Find the most suitable mode to use for initial setup (or reconfiguration on
+ * hotplug etc) for a DRM output.
+ *
+ * @param output DRM output to choose mode for
+ * @param kind Strategy and preference to use when choosing mode
+ * @param width Desired width for this output
+ * @param height Desired height for this output
+ * @param current_mode Mode currently being displayed on this output
+ * @param modeline Manually-entered mode (may be NULL)
+ * @returns A mode from the output's mode list, or NULL if none available
+ */
+static struct drm_mode *
+drm_output_choose_initial_mode(struct drm_backend *backend,
+ struct drm_output *output,
+ enum weston_drm_backend_output_mode mode,
+ struct weston_drm_backend_output_config *config,
+ const drmModeModeInfo *current_mode)
+{
+ struct drm_mode *preferred = NULL;
+ struct drm_mode *current = NULL;
+ struct drm_mode *configured = NULL;
+ struct drm_mode *best = NULL;
+ struct drm_mode *drm_mode;
+ drmModeModeInfo modeline;
+ int32_t width = 0;
+ int32_t height = 0;
+
+ if (mode == WESTON_DRM_BACKEND_OUTPUT_PREFERRED && config->modeline) {
+ if (sscanf(config->modeline, "%dx%d", &width, &height) != 2) {
+ width = -1;
+
+ if (parse_modeline(config->modeline, &modeline) == 0) {
+ configured = drm_output_add_mode(output, &modeline);
+ if (!configured)
+ return NULL;
+ } else {
+ weston_log("Invalid modeline \"%s\" for output %s\n",
+ config->modeline, output->base.name);
+ }
+ }
+ }
+
+ wl_list_for_each_reverse(drm_mode, &output->base.mode_list, base.link) {
+ if (width == drm_mode->base.width &&
+ height == drm_mode->base.height)
+ configured = drm_mode;
+
+ if (memcmp(current_mode, &drm_mode->mode_info,
+ sizeof *current_mode) == 0)
+ current = drm_mode;
+
+ if (drm_mode->base.flags & WL_OUTPUT_MODE_PREFERRED)
+ preferred = drm_mode;
+
+ best = drm_mode;
+ }
+
+ if (current == NULL && current_mode->clock != 0) {
+ current = drm_output_add_mode(output, current_mode);
+ if (!current)
+ return NULL;
+ }
+
+ if (mode == WESTON_DRM_BACKEND_OUTPUT_CURRENT)
+ configured = current;
+
+ if (configured)
+ return configured;
+
+ if (preferred)
+ return preferred;
+
+ if (current)
+ return current;
+
+ if (best)
+ return best;
+
+ weston_log("no available modes for %s\n", output->base.name);
+ return NULL;
+}
+
+static int
+connector_get_current_mode(drmModeConnector *connector, int drm_fd,
+ drmModeModeInfo *mode)
+{
+ drmModeEncoder *encoder;
+ drmModeCrtc *crtc;
+
+ /* Get the current mode on the crtc that's currently driving
+ * this connector. */
+ encoder = drmModeGetEncoder(drm_fd, connector->encoder_id);
+ memset(mode, 0, sizeof *mode);
+ if (encoder != NULL) {
+ crtc = drmModeGetCrtc(drm_fd, encoder->crtc_id);
+ drmModeFreeEncoder(encoder);
+ if (crtc == NULL)
+ return -1;
+ if (crtc->mode_valid)
+ *mode = crtc->mode;
+ drmModeFreeCrtc(crtc);
+ }
+
+ return 0;
+}
+
+/**
+ * Create and configure a Weston output structure
+ *
+ * Given a DRM connector, create a matching drm_output structure and add it
+ * to Weston's output list.
+ *
+ * @param b Weston backend structure structure
+ * @param resources DRM resources for this device
+ * @param connector DRM connector to use for this new output
+ * @param x Horizontal offset to use into global co-ordinate space
+ * @param y Vertical offset to use into global co-ordinate space
+ * @param drm_device udev device pointer
+ * @returns 0 on success, or -1 on failure
+ */
+static int
+create_output_for_connector(struct drm_backend *b,
+ drmModeRes *resources,
+ drmModeConnector *connector,
+ int x, int y, struct udev_device *drm_device)
+{
+ struct drm_output *output;
+ struct drm_mode *drm_mode, *next, *current;
+ struct weston_mode *m;
+
+ drmModeModeInfo crtc_mode;
+ int i;
+ enum weston_drm_backend_output_mode mode;
+ struct weston_drm_backend_output_config config = {{ 0 }};
+
+ i = find_crtc_for_connector(b, resources, connector);
+ if (i < 0) {
+ weston_log("No usable crtc/encoder pair for connector.\n");
+ return -1;
+ }
+
+ output = zalloc(sizeof *output);
+ if (output == NULL)
+ return -1;
+
+ output->base.subpixel = drm_subpixel_to_wayland(connector->subpixel);
+ output->base.name = make_connector_name(connector);
+ output->base.make = "unknown";
+ output->base.model = "unknown";
+ output->base.serial_number = "unknown";
+ wl_list_init(&output->base.mode_list);
+
+ mode = b->configure_output(b->compositor, b->use_current_mode,
+ output->base.name, &config);
+ if (parse_gbm_format(config.gbm_format, b->gbm_format, &output->gbm_format) == -1)
+ output->gbm_format = b->gbm_format;
+
+ setup_output_seat_constraint(b, &output->base,
+ config.seat ? config.seat : "");
+ free(config.seat);
+
+ output->crtc_id = resources->crtcs[i];
+ output->pipe = i;
+ b->crtc_allocator |= (1 << output->crtc_id);
+ output->connector_id = connector->connector_id;
+ b->connector_allocator |= (1 << output->connector_id);
+
+ output->original_crtc = drmModeGetCrtc(b->drm.fd, output->crtc_id);
+ output->dpms_prop = drm_get_prop(b->drm.fd, connector, "DPMS");
+
+ if (connector_get_current_mode(connector, b->drm.fd, &crtc_mode) < 0)
+ goto err_free;
+
+ for (i = 0; i < connector->count_modes; i++) {
+ drm_mode = drm_output_add_mode(output, &connector->modes[i]);
+ if (!drm_mode)
+ goto err_free;
+ }
+
+ if (mode == WESTON_DRM_BACKEND_OUTPUT_OFF) {
+ weston_log("Disabling output %s\n", output->base.name);
+ drmModeSetCrtc(b->drm.fd, output->crtc_id,
+ 0, 0, 0, 0, 0, NULL);
+ goto err_free;
+ }
+
+ current = drm_output_choose_initial_mode(b, output, mode, &config,
+ &crtc_mode);
+ if (!current)
+ goto err_free;
+ output->base.current_mode = ¤t->base;
+ output->base.current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
+
+ weston_output_init(&output->base, b->compositor, x, y,
+ connector->mmWidth, connector->mmHeight,
+ config.base.transform, config.base.scale);
+
+ if (b->use_pixman) {
+ if (drm_output_init_pixman(output, b) < 0) {
+ weston_log("Failed to init output pixman state\n");
+ goto err_output;
+ }
+ } else if (drm_output_init_egl(output, b) < 0) {
+ weston_log("Failed to init output gl state\n");
+ goto err_output;
+ }
+
+ output->backlight = backlight_init(drm_device,
+ connector->connector_type);
+ if (output->backlight) {
+ weston_log("Initialized backlight, device %s\n",
+ output->backlight->path);
+ output->base.set_backlight = drm_set_backlight;
+ output->base.backlight_current = drm_get_backlight(output);
+ } else {
+ weston_log("Failed to initialize backlight\n");
+ }
+
+ weston_compositor_add_output(b->compositor, &output->base);
+
+ find_and_parse_output_edid(b, output, connector);
+ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
+ output->base.connection_internal = 1;
+
+ output->base.start_repaint_loop = drm_output_start_repaint_loop;
+ output->base.repaint = drm_output_repaint;
+ output->base.destroy = drm_output_destroy;
+ output->base.assign_planes = drm_assign_planes;
+ output->base.set_dpms = drm_set_dpms;
+ output->base.switch_mode = drm_output_switch_mode;
+
+ output->base.gamma_size = output->original_crtc->gamma_size;
+ output->base.set_gamma = drm_output_set_gamma;
+
+ weston_plane_init(&output->cursor_plane, b->compositor,
+ INT32_MIN, INT32_MIN);
+ weston_plane_init(&output->fb_plane, b->compositor, 0, 0);
+
+ weston_compositor_stack_plane(b->compositor, &output->cursor_plane, NULL);
+ weston_compositor_stack_plane(b->compositor, &output->fb_plane,
+ &b->compositor->primary_plane);
+
+ weston_log("Output %s, (connector %d, crtc %d)\n",
+ output->base.name, output->connector_id, output->crtc_id);
+ wl_list_for_each(m, &output->base.mode_list, link)
+ weston_log_continue(STAMP_SPACE "mode %dx%d@%.1f%s%s%s\n",
+ m->width, m->height, m->refresh / 1000.0,
+ m->flags & WL_OUTPUT_MODE_PREFERRED ?
+ ", preferred" : "",
+ m->flags & WL_OUTPUT_MODE_CURRENT ?
+ ", current" : "",
+ connector->count_modes == 0 ?
+ ", built-in" : "");
+
+ /* Set native_ fields, so weston_output_mode_switch_to_native() works */
+ output->base.native_mode = output->base.current_mode;
+ output->base.native_scale = output->base.current_scale;
+
+ return 0;
+
+err_output:
+ weston_output_destroy(&output->base);
+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);
+ b->crtc_allocator &= ~(1 << output->crtc_id);
+ b->connector_allocator &= ~(1 << output->connector_id);
+ free(output);
+ free(config.modeline);
+
+ return -1;
+}
+
+static void
+create_sprites(struct drm_backend *b)
+{
+ struct drm_sprite *sprite;
+ drmModePlaneRes *plane_res;
+ drmModePlane *plane;
+ uint32_t i;
+
+ plane_res = drmModeGetPlaneResources(b->drm.fd);
+ if (!plane_res) {
+ weston_log("failed to get plane resources: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ for (i = 0; i < plane_res->count_planes; i++) {
+ plane = drmModeGetPlane(b->drm.fd, plane_res->planes[i]);
+ if (!plane)
+ continue;
+
+ sprite = zalloc(sizeof(*sprite) + ((sizeof(uint32_t)) *
+ plane->count_formats));
+ if (!sprite) {
+ weston_log("%s: out of memory\n",
+ __func__);
+ drmModeFreePlane(plane);
+ continue;
+ }
+
+ sprite->possible_crtcs = plane->possible_crtcs;
+ sprite->plane_id = plane->plane_id;
+ sprite->current = NULL;
+ sprite->next = NULL;
+ sprite->backend = b;
+ sprite->count_formats = plane->count_formats;
+ memcpy(sprite->formats, plane->formats,
+ plane->count_formats * sizeof(plane->formats[0]));
+ drmModeFreePlane(plane);
+ weston_plane_init(&sprite->plane, b->compositor, 0, 0);
+ weston_compositor_stack_plane(b->compositor, &sprite->plane,
+ &b->compositor->primary_plane);
+
+ wl_list_insert(&b->sprite_list, &sprite->link);
+ }
+
+ drmModeFreePlaneResources(plane_res);
+}
+
+static void
+destroy_sprites(struct drm_backend *backend)
+{
+ struct drm_sprite *sprite, *next;
+ struct drm_output *output;
+
+ output = container_of(backend->compositor->output_list.next,
+ struct drm_output, base.link);
+
+ wl_list_for_each_safe(sprite, next, &backend->sprite_list, link) {
+ drmModeSetPlane(backend->drm.fd,
+ sprite->plane_id,
+ output->crtc_id, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0);
+ drm_output_release_fb(output, sprite->current);
+ drm_output_release_fb(output, sprite->next);
+ weston_plane_release(&sprite->plane);
+ free(sprite);
+ }
+}
+
+static int
+create_outputs(struct drm_backend *b, uint32_t option_connector,
+ struct udev_device *drm_device)
+{
+ drmModeConnector *connector;
+ drmModeRes *resources;
+ int i;
+ int x = 0, y = 0;
+
+ resources = drmModeGetResources(b->drm.fd);
+ if (!resources) {
+ weston_log("drmModeGetResources failed\n");
+ return -1;
+ }
+
+ b->crtcs = calloc(resources->count_crtcs, sizeof(uint32_t));
+ if (!b->crtcs) {
+ drmModeFreeResources(resources);
+ return -1;
+ }
+
+ b->min_width = resources->min_width;
+ b->max_width = resources->max_width;
+ b->min_height = resources->min_height;
+ b->max_height = resources->max_height;
+
+ b->num_crtcs = resources->count_crtcs;
+ memcpy(b->crtcs, resources->crtcs, sizeof(uint32_t) * b->num_crtcs);
+
+ for (i = 0; i < resources->count_connectors; i++) {
+ connector = drmModeGetConnector(b->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(b, resources,
+ connector, x, y,
+ drm_device) < 0) {
+ drmModeFreeConnector(connector);
+ continue;
+ }
+
+ x += container_of(b->compositor->output_list.prev,
+ struct weston_output,
+ link)->width;
+ }
+
+ drmModeFreeConnector(connector);
+ }
+
+ if (wl_list_empty(&b->compositor->output_list)) {
+ weston_log("No currently active connector found.\n");
+ drmModeFreeResources(resources);
+ return -1;
+ }
+
+ drmModeFreeResources(resources);
+
+ return 0;
+}
+
+static void
+update_outputs(struct drm_backend *b, struct udev_device *drm_device)
+{
+ drmModeConnector *connector;
+ drmModeRes *resources;
+ struct drm_output *output, *next;
+ int x = 0, y = 0;
+ uint32_t connected = 0, disconnects = 0;
+ int i;
+
+ resources = drmModeGetResources(b->drm.fd);
+ if (!resources) {
+ weston_log("drmModeGetResources failed\n");
+ return;
+ }
+
+ /* collect new connects */
+ for (i = 0; i < resources->count_connectors; i++) {
+ int connector_id = resources->connectors[i];
+
+ connector = drmModeGetConnector(b->drm.fd, connector_id);
+ if (connector == NULL)
+ continue;
+
+ if (connector->connection != DRM_MODE_CONNECTED) {
+ drmModeFreeConnector(connector);
+ continue;
+ }
+
+ connected |= (1 << connector_id);
+
+ if (!(b->connector_allocator & (1 << connector_id))) {
+ struct weston_output *last =
+ container_of(b->compositor->output_list.prev,
+ struct weston_output, link);
+
+ /* XXX: not yet needed, we die with 0 outputs */
+ if (!wl_list_empty(&b->compositor->output_list))
+ x = last->x + last->width;
+ else
+ x = 0;
+ y = 0;
+ create_output_for_connector(b, resources,
+ connector, x, y,
+ drm_device);
+ weston_log("connector %d connected\n", connector_id);
+
+ }
+ drmModeFreeConnector(connector);
+ }
+ drmModeFreeResources(resources);
+
+ disconnects = b->connector_allocator & ~connected;
+ if (disconnects) {
+ wl_list_for_each_safe(output, next, &b->compositor->output_list,
+ base.link) {
+ if (disconnects & (1 << output->connector_id)) {
+ disconnects &= ~(1 << output->connector_id);
+ weston_log("connector %d disconnected\n",
+ output->connector_id);
+ drm_output_destroy(&output->base);
+ }
+ }
+ }
+
+ /* FIXME: handle zero outputs, without terminating */
+ if (b->connector_allocator == 0)
+ weston_compositor_exit(b->compositor);
+}
+
+static int
+udev_event_is_hotplug(struct drm_backend *b, struct udev_device *device)
+{
+ const char *sysnum;
+ const char *val;
+
+ sysnum = udev_device_get_sysnum(device);
+ if (!sysnum || atoi(sysnum) != b->drm.id)
+ return 0;
+
+ val = udev_device_get_property_value(device, "HOTPLUG");
+ if (!val)
+ return 0;
+
+ return strcmp(val, "1") == 0;
+}
+
+static int
+udev_drm_event(int fd, uint32_t mask, void *data)
+{
+ struct drm_backend *b = data;
+ struct udev_device *event;
+
+ event = udev_monitor_receive_device(b->udev_monitor);
+
+ if (udev_event_is_hotplug(b, event))
+ update_outputs(b, event);
+
+ udev_device_unref(event);
+
+ return 1;
+}
+
+static void
+drm_restore(struct weston_compositor *ec)
+{
+ weston_launcher_restore(ec->launcher);
+}
+
+static void
+drm_destroy(struct weston_compositor *ec)
+{
+ struct drm_backend *b = (struct drm_backend *) ec->backend;
+
+ udev_input_destroy(&b->input);
+
+ wl_event_source_remove(b->udev_drm_source);
+ wl_event_source_remove(b->drm_source);
+
+ destroy_sprites(b);
+
+ weston_compositor_shutdown(ec);
+
+ if (b->gbm)
+ gbm_device_destroy(b->gbm);
+
+ weston_launcher_destroy(ec->launcher);
+
+ close(b->drm.fd);
+ free(b);
+}
+
+static void
+drm_backend_set_modes(struct drm_backend *backend)
+{
+ struct drm_output *output;
+ struct drm_mode *drm_mode;
+ int ret;
+
+ wl_list_for_each(output, &backend->compositor->output_list, base.link) {
+ if (!output->current) {
+ /* If something that would cause the output to
+ * switch mode happened while in another vt, we
+ * might not have a current drm_fb. In that case,
+ * schedule a repaint and let drm_output_repaint
+ * handle setting the mode. */
+ weston_output_schedule_repaint(&output->base);
+ continue;
+ }
+
+ drm_mode = (struct drm_mode *) output->base.current_mode;
+ ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
+ output->current->fb_id, 0, 0,
+ &output->connector_id, 1,
+ &drm_mode->mode_info);
+ if (ret < 0) {
+ weston_log(
+ "failed to set mode %dx%d for output at %d,%d: %m\n",
+ drm_mode->base.width, drm_mode->base.height,
+ output->base.x, output->base.y);
+ }
+ }
+}
+
+static void
+session_notify(struct wl_listener *listener, void *data)
+{
+ struct weston_compositor *compositor = data;
+ struct drm_backend *b = (struct drm_backend *)compositor->backend;
+ struct drm_sprite *sprite;
+ struct drm_output *output;
+
+ if (compositor->session_active) {
+ weston_log("activating session\n");
+ compositor->state = b->prev_state;
+ drm_backend_set_modes(b);
+ weston_compositor_damage_all(compositor);
+ udev_input_enable(&b->input);
+ } else {
+ weston_log("deactivating session\n");
+ udev_input_disable(&b->input);
+
+ b->prev_state = compositor->state;
+ weston_compositor_offscreen(compositor);
+
+ /* If we have a repaint scheduled (either from a
+ * pending pageflip or the idle handler), make sure we
+ * cancel that so we don't try to pageflip when we're
+ * vt switched away. The OFFSCREEN state will prevent
+ * further attemps at repainting. When we switch
+ * back, we schedule a repaint, which will process
+ * pending frame callbacks. */
+
+ wl_list_for_each(output, &compositor->output_list, base.link) {
+ output->base.repaint_needed = 0;
+ drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
+ }
+
+ output = container_of(compositor->output_list.next,
+ struct drm_output, base.link);
+
+ wl_list_for_each(sprite, &b->sprite_list, link)
+ drmModeSetPlane(b->drm.fd,
+ sprite->plane_id,
+ output->crtc_id, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0);
+ };
+}
+
+/*
+ * Find primary GPU
+ * Some systems may have multiple DRM devices attached to a single seat. This
+ * function loops over all devices and tries to find a PCI device with the
+ * boot_vga sysfs attribute set to 1.
+ * If no such device is found, the first DRM device reported by udev is used.
+ */
+static struct udev_device*
+find_primary_gpu(struct drm_backend *b, const char *seat)
+{
+ struct udev_enumerate *e;
+ struct udev_list_entry *entry;
+ const char *path, *device_seat, *id;
+ struct udev_device *device, *drm_device, *pci;
+
+ e = udev_enumerate_new(b->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(b->udev, path);
+ if (!device)
+ continue;
+ device_seat = udev_device_get_property_value(device, "ID_SEAT");
+ if (!device_seat)
+ device_seat = default_seat;
+ if (strcmp(device_seat, seat)) {
+ udev_device_unref(device);
+ continue;
+ }
+
+ pci = udev_device_get_parent_with_subsystem_devtype(device,
+ "pci", NULL);
+ if (pci) {
+ id = udev_device_get_sysattr_value(pci, "boot_vga");
+ if (id && !strcmp(id, "1")) {
+ if (drm_device)
+ udev_device_unref(drm_device);
+ drm_device = device;
+ break;
+ }
+ }
+
+ if (!drm_device)
+ drm_device = device;
+ else
+ udev_device_unref(device);
+ }
+
+ udev_enumerate_unref(e);
+ return drm_device;
+}
+
+static void
+planes_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key,
+ void *data)
+{
+ struct drm_backend *b = data;
+
+ switch (key) {
+ case KEY_C:
+ b->cursors_are_broken ^= 1;
+ break;
+ case KEY_V:
+ b->sprites_are_broken ^= 1;
+ break;
+ case KEY_O:
+ b->sprites_hidden ^= 1;
+ break;
+ default:
+ break;
+ }
+}
+
+#ifdef BUILD_VAAPI_RECORDER
+static void
+recorder_destroy(struct drm_output *output)
+{
+ vaapi_recorder_destroy(output->recorder);
+ output->recorder = NULL;
+
+ output->base.disable_planes--;
+
+ wl_list_remove(&output->recorder_frame_listener.link);
+ weston_log("[libva recorder] done\n");
+}
+
+static void
+recorder_frame_notify(struct wl_listener *listener, void *data)
+{
+ struct drm_output *output;
+ struct drm_backend *b;
+ int fd, ret;
+
+ output = container_of(listener, struct drm_output,
+ recorder_frame_listener);
+ b = (struct drm_backend *)output->base.compositor->backend;
+
+ if (!output->recorder)
+ return;
+
+ ret = drmPrimeHandleToFD(b->drm.fd, output->current->handle,
+ DRM_CLOEXEC, &fd);
+ if (ret) {
+ weston_log("[libva recorder] "
+ "failed to create prime fd for front buffer\n");
+ return;
+ }
+
+ ret = vaapi_recorder_frame(output->recorder, fd,
+ output->current->stride);
+ if (ret < 0) {
+ weston_log("[libva recorder] aborted: %m\n");
+ recorder_destroy(output);
+ }
+}
+
+static void *
+create_recorder(struct drm_backend *b, int width, int height,
+ const char *filename)
+{
+ int fd;
+ drm_magic_t magic;
+
+ fd = open(b->drm.filename, O_RDWR | O_CLOEXEC);
+ if (fd < 0)
+ return NULL;
+
+ drmGetMagic(fd, &magic);
+ drmAuthMagic(b->drm.fd, magic);
+
+ return vaapi_recorder_create(fd, width, height, filename);
+}
+
+static void
+recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key,
+ void *data)
+{
+ struct drm_backend *b = data;
+ struct drm_output *output;
+ int width, height;
+
+ output = container_of(b->compositor->output_list.next,
+ struct drm_output, base.link);
+
+ if (!output->recorder) {
+ if (output->gbm_format != GBM_FORMAT_XRGB8888) {
+ weston_log("failed to start vaapi recorder: "
+ "output format not supported\n");
+ return;
+ }
+
+ width = output->base.current_mode->width;
+ height = output->base.current_mode->height;
+
+ output->recorder =
+ create_recorder(b, width, height, "capture.h264");
+ if (!output->recorder) {
+ weston_log("failed to create vaapi recorder\n");
+ return;
+ }
+
+ output->base.disable_planes++;
+
+ output->recorder_frame_listener.notify = recorder_frame_notify;
+ wl_signal_add(&output->base.frame_signal,
+ &output->recorder_frame_listener);
+
+ weston_output_schedule_repaint(&output->base);
+
+ weston_log("[libva recorder] initialized\n");
+ } else {
+ recorder_destroy(output);
+ }
+}
+#else
+static void
+recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key,
+ void *data)
+{
+ weston_log("Compiled without libva support\n");
+}
+#endif
+
+static void
+switch_to_gl_renderer(struct drm_backend *b)
+{
+ struct drm_output *output;
+ bool dmabuf_support_inited;
+
+ if (!b->use_pixman)
+ return;
+
+ dmabuf_support_inited = !!b->compositor->renderer->import_dmabuf;
+
+ weston_log("Switching to GL renderer\n");
+
+ b->gbm = create_gbm_device(b->drm.fd);
+ if (!b->gbm) {
+ weston_log("Failed to create gbm device. "
+ "Aborting renderer switch\n");
+ return;
+ }
+
+ wl_list_for_each(output, &b->compositor->output_list, base.link)
+ pixman_renderer_output_destroy(&output->base);
+
+ b->compositor->renderer->destroy(b->compositor);
+
+ if (drm_backend_create_gl_renderer(b) < 0) {
+ gbm_device_destroy(b->gbm);
+ weston_log("Failed to create GL renderer. Quitting.\n");
+ /* FIXME: we need a function to shutdown cleanly */
+ assert(0);
+ }
+
+ wl_list_for_each(output, &b->compositor->output_list, base.link)
+ drm_output_init_egl(output, b);
+
+ b->use_pixman = 0;
+
+ if (!dmabuf_support_inited && b->compositor->renderer->import_dmabuf) {
+ if (linux_dmabuf_setup(b->compositor) < 0)
+ weston_log("Error: initializing dmabuf "
+ "support failed.\n");
+ }
+}
+
+static void
+renderer_switch_binding(struct weston_keyboard *keyboard, uint32_t time,
+ uint32_t key, void *data)
+{
+ struct drm_backend *b =
+ (struct drm_backend *) keyboard->seat->compositor;
+
+ switch_to_gl_renderer(b);
+}
+
+static struct drm_backend *
+drm_backend_create(struct weston_compositor *compositor,
+ struct weston_drm_backend_config *config)
+{
+ struct drm_backend *b;
+ struct udev_device *drm_device;
+ struct wl_event_loop *loop;
+ const char *path;
+ const char *seat_id = default_seat;
+
+ weston_log("initializing drm backend\n");
+
+ b = zalloc(sizeof *b);
+ if (b == NULL)
+ return NULL;
+
+ /*
+ * KMS support for hardware planes cannot properly synchronize
+ * without nuclear page flip. Without nuclear/atomic, hw plane
+ * and cursor plane updates would either tear or cause extra
+ * waits for vblanks which means dropping the compositor framerate
+ * to a fraction. For cursors, it's not so bad, so they are
+ * enabled.
+ *
+ * These can be enabled again when nuclear/atomic support lands.
+ */
+ b->sprites_are_broken = 1;
+ b->compositor = compositor;
+ b->use_pixman = config->use_pixman;
+ b->configure_output = config->configure_output;
+ b->use_current_mode = config->use_current_mode;
+
+ if (parse_gbm_format(config->gbm_format, GBM_FORMAT_XRGB8888, &b->gbm_format) < 0)
+ goto err_compositor;
+
+ if (config->seat_id)
+ seat_id = config->seat_id;
+
+ /* Check if we run drm-backend using weston-launch */
+ compositor->launcher = weston_launcher_connect(compositor, config->tty,
+ seat_id, true);
+ if (compositor->launcher == NULL) {
+ weston_log("fatal: drm backend should be run "
+ "using weston-launch binary or as root\n");
+ goto err_compositor;
+ }
+
+ b->udev = udev_new();
+ if (b->udev == NULL) {
+ weston_log("failed to initialize udev context\n");
+ goto err_launcher;
+ }
+
+ b->session_listener.notify = session_notify;
+ wl_signal_add(&compositor->session_signal, &b->session_listener);
+
+ drm_device = find_primary_gpu(b, seat_id);
+ if (drm_device == NULL) {
+ weston_log("no drm device found\n");
+ goto err_udev;
+ }
+ path = udev_device_get_syspath(drm_device);
+
+ if (init_drm(b, drm_device) < 0) {
+ weston_log("failed to initialize kms\n");
+ goto err_udev_dev;
+ }
+
+ if (b->use_pixman) {
+ if (init_pixman(b) < 0) {
+ weston_log("failed to initialize pixman renderer\n");
+ goto err_udev_dev;
+ }
+ } else {
+ if (init_egl(b) < 0) {
+ weston_log("failed to initialize egl\n");
+ goto err_udev_dev;
+ }
+ }
+
+ b->base.destroy = drm_destroy;
+ b->base.restore = drm_restore;
+
+ b->prev_state = WESTON_COMPOSITOR_ACTIVE;
+
+ weston_setup_vt_switch_bindings(compositor);
+
+ wl_list_init(&b->sprite_list);
+ create_sprites(b);
+
+ if (udev_input_init(&b->input,
+ compositor, b->udev, seat_id,
+ config->configure_device) < 0) {
+ weston_log("failed to create input devices\n");
+ goto err_sprite;
+ }
+
+ if (create_outputs(b, config->connector, drm_device) < 0) {
+ weston_log("failed to create output for %s\n", path);
+ goto err_udev_input;
+ }
+
+ /* A this point we have some idea of whether or not we have a working
+ * cursor plane. */
+ if (!b->cursors_are_broken)
+ compositor->capabilities |= WESTON_CAP_CURSOR_PLANE;
+
+ path = NULL;
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+ b->drm_source =
+ wl_event_loop_add_fd(loop, b->drm.fd,
+ WL_EVENT_READABLE, on_drm_input, b);
+
+ b->udev_monitor = udev_monitor_new_from_netlink(b->udev, "udev");
+ if (b->udev_monitor == NULL) {
+ weston_log("failed to intialize udev monitor\n");
+ goto err_drm_source;
+ }
+ udev_monitor_filter_add_match_subsystem_devtype(b->udev_monitor,
+ "drm", NULL);
+ b->udev_drm_source =
+ wl_event_loop_add_fd(loop,
+ udev_monitor_get_fd(b->udev_monitor),
+ WL_EVENT_READABLE, udev_drm_event, b);
+
+ if (udev_monitor_enable_receiving(b->udev_monitor) < 0) {
+ weston_log("failed to enable udev-monitor receiving\n");
+ goto err_udev_monitor;
+ }
+
+ udev_device_unref(drm_device);
+
+ weston_compositor_add_debug_binding(compositor, KEY_O,
+ planes_binding, b);
+ weston_compositor_add_debug_binding(compositor, KEY_C,
+ planes_binding, b);
+ weston_compositor_add_debug_binding(compositor, KEY_V,
+ planes_binding, b);
+ weston_compositor_add_debug_binding(compositor, KEY_Q,
+ recorder_binding, b);
+ weston_compositor_add_debug_binding(compositor, KEY_W,
+ renderer_switch_binding, b);
+
+ if (compositor->renderer->import_dmabuf) {
+ if (linux_dmabuf_setup(compositor) < 0)
+ weston_log("Error: initializing dmabuf "
+ "support failed.\n");
+ }
+
+ compositor->backend = &b->base;
+
+ return b;
+
+err_udev_monitor:
+ wl_event_source_remove(b->udev_drm_source);
+ udev_monitor_unref(b->udev_monitor);
+err_drm_source:
+ wl_event_source_remove(b->drm_source);
+err_udev_input:
+ udev_input_destroy(&b->input);
+err_sprite:
+ if (b->gbm)
+ gbm_device_destroy(b->gbm);
+ destroy_sprites(b);
+err_udev_dev:
+ udev_device_unref(drm_device);
+err_launcher:
+ weston_launcher_destroy(compositor->launcher);
+err_udev:
+ udev_unref(b->udev);
+err_compositor:
+ weston_compositor_shutdown(compositor);
+ free(b);
+ return NULL;
+}
+
+static void
+config_init_to_defaults(struct weston_drm_backend_config *config)
+{
+}
+
+WL_EXPORT int
+backend_init(struct weston_compositor *compositor,
+ struct weston_backend_config *config_base)
+{
+ struct drm_backend *b;
+ struct weston_drm_backend_config config = {{ 0, }};
+
+ if (config_base == NULL ||
+ config_base->struct_version != WESTON_DRM_BACKEND_CONFIG_VERSION ||
+ config_base->struct_size > sizeof(struct weston_drm_backend_config)) {
+ weston_log("drm backend config structure is invalid\n");
+ return -1;
+ }
+
+ config_init_to_defaults(&config);
+ memcpy(&config, config_base, config_base->struct_size);
+
+ b = drm_backend_create(compositor, &config);
+ if (b == NULL)
+ return -1;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2011 Intel Corporation
+ * Copyright © 2015 Giulio Camuffo
+ *
+ * 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.
+ */
+
+#ifndef WESTON_COMPOSITOR_DRM_H
+#define WESTON_COMPOSITOR_DRM_H
+
+#include "compositor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WESTON_DRM_BACKEND_CONFIG_VERSION 1
+
+struct libinput_device;
+
+enum weston_drm_backend_output_mode {
+ /** The output is disabled */
+ WESTON_DRM_BACKEND_OUTPUT_OFF,
+ /** The output will use the current active mode */
+ WESTON_DRM_BACKEND_OUTPUT_CURRENT,
+ /** The output will use the preferred mode. A modeline can be provided
+ * by setting weston_backend_output_config::modeline in the form of
+ * "WIDTHxHEIGHT" or in the form of an explicit modeline calculated
+ * using e.g. the cvt tool. If a valid modeline is supplied it will be
+ * used, if invalid or NULL the preferred available mode will be used. */
+ WESTON_DRM_BACKEND_OUTPUT_PREFERRED,
+};
+
+struct weston_drm_backend_output_config {
+ struct weston_backend_output_config base;
+
+ /** The pixel format to be used by the output. Valid values are:
+ * - NULL - The format set at backend creation time will be used;
+ * - "xrgb8888";
+ * - "rgb565"
+ * - "xrgb2101010"
+ */
+ char *gbm_format;
+ /** The seat to be used by the output. Set to NULL to use the
+ * default seat. */
+ char *seat;
+ /** The modeline to be used by the output. Refer to the documentation
+ * of WESTON_DRM_BACKEND_OUTPUT_PREFERRED for details. */
+ char *modeline;
+};
+
+/** The backend configuration struct.
+ *
+ * weston_drm_backend_config contains the configuration used by a DRM
+ * backend.
+ */
+struct weston_drm_backend_config {
+ struct weston_backend_config base;
+
+ /** The connector id of the output to be initialized.
+ *
+ * A value of 0 will enable all available outputs.
+ */
+ int connector;
+
+ /** The tty to be used. Set to 0 to use the current tty. */
+ int tty;
+
+ /** Whether to use the pixman renderer instead of the OpenGL ES renderer. */
+ bool use_pixman;
+
+ /** The seat to be used for input and output.
+ *
+ * If NULL the default "seat0" will be used. The backend will
+ * take ownership of the seat_id pointer and will free it on
+ * backend destruction.
+ */
+ char *seat_id;
+
+ /** The pixel format of the framebuffer to be used.
+ *
+ * Valid values are:
+ * - NULL - The default format ("xrgb8888") will be used;
+ * - "xrgb8888";
+ * - "rgb565"
+ * - "xrgb2101010"
+ * The backend will take ownership of the format pointer and will free
+ * it on backend destruction.
+ */
+ char *gbm_format;
+
+ /** Callback used to configure the outputs.
+ *
+ * This function will be called by the backend when a new DRM
+ * output needs to be configured.
+ */
+ enum weston_drm_backend_output_mode
+ (*configure_output)(struct weston_compositor *compositor,
+ bool use_current_mode,
+ const char *name,
+ struct weston_drm_backend_output_config *output_config);
+
+ /** Callback used to configure input devices.
+ *
+ * This function will be called by the backend when a new input device
+ * needs to be configured.
+ * If NULL the device will use the default configuration.
+ */
+ void (*configure_device)(struct weston_compositor *compositor,
+ struct libinput_device *device);
+ bool use_current_mode;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WESTON_COMPOSITOR_DRM_H */
--- /dev/null
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2011 Intel Corporation
+ * Copyright © 2012 Raspberry Pi Foundation
+ * Copyright © 2013 Philip Withnall
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/fb.h>
+#include <linux/input.h>
+
+#include <libudev.h>
+
+#include "shared/helpers.h"
+#include "compositor.h"
+#include "compositor-fbdev.h"
+#include "launcher-util.h"
+#include "pixman-renderer.h"
+#include "libinput-seat.h"
+#include "presentation-time-server-protocol.h"
+
+struct fbdev_backend {
+ struct weston_backend base;
+ struct weston_compositor *compositor;
+ uint32_t prev_state;
+
+ struct udev *udev;
+ struct udev_input input;
+ uint32_t output_transform;
+ struct wl_listener session_listener;
+};
+
+struct fbdev_screeninfo {
+ unsigned int x_resolution; /* pixels, visible area */
+ unsigned int y_resolution; /* pixels, visible area */
+ unsigned int width_mm; /* visible screen width in mm */
+ unsigned int height_mm; /* visible screen height in mm */
+ unsigned int bits_per_pixel;
+
+ size_t buffer_length; /* length of frame buffer memory in bytes */
+ size_t line_length; /* length of a line in bytes */
+ char id[16]; /* screen identifier */
+
+ pixman_format_code_t pixel_format; /* frame buffer pixel format */
+ unsigned int refresh_rate; /* Hertz */
+};
+
+struct fbdev_output {
+ struct fbdev_backend *backend;
+ struct weston_output base;
+
+ struct weston_mode mode;
+ struct wl_event_source *finish_frame_timer;
+
+ /* Frame buffer details. */
+ char *device;
+ struct fbdev_screeninfo fb_info;
+ void *fb; /* length is fb_info.buffer_length */
+
+ /* pixman details. */
+ pixman_image_t *hw_surface;
+ uint8_t depth;
+};
+
+static const char default_seat[] = "seat0";
+
+static inline struct fbdev_output *
+to_fbdev_output(struct weston_output *base)
+{
+ return container_of(base, struct fbdev_output, base);
+}
+
+static inline struct fbdev_backend *
+to_fbdev_backend(struct weston_compositor *base)
+{
+ return container_of(base->backend, struct fbdev_backend, base);
+}
+
+static void
+fbdev_output_start_repaint_loop(struct weston_output *output)
+{
+ struct timespec ts;
+
+ weston_compositor_read_presentation_clock(output->compositor, &ts);
+ weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
+}
+
+static int
+fbdev_output_repaint(struct weston_output *base, pixman_region32_t *damage)
+{
+ struct fbdev_output *output = to_fbdev_output(base);
+ struct weston_compositor *ec = output->base.compositor;
+
+ /* Repaint the damaged region onto the back buffer. */
+ pixman_renderer_output_set_buffer(base, output->hw_surface);
+ ec->renderer->repaint_output(base, damage);
+
+ /* Update the damage region. */
+ pixman_region32_subtract(&ec->primary_plane.damage,
+ &ec->primary_plane.damage, damage);
+
+ /* Schedule the end of the frame. We do not sync this to the frame
+ * buffer clock because users who want that should be using the DRM
+ * compositor. FBIO_WAITFORVSYNC blocks and FB_ACTIVATE_VBL requires
+ * panning, which is broken in most kernel drivers.
+ *
+ * Finish the frame synchronised to the specified refresh rate. The
+ * refresh rate is given in mHz and the interval in ms. */
+ wl_event_source_timer_update(output->finish_frame_timer,
+ 1000000 / output->mode.refresh);
+
+ return 0;
+}
+
+static int
+finish_frame_handler(void *data)
+{
+ struct fbdev_output *output = data;
+ struct timespec ts;
+
+ weston_compositor_read_presentation_clock(output->base.compositor, &ts);
+ weston_output_finish_frame(&output->base, &ts, 0);
+
+ return 1;
+}
+
+static pixman_format_code_t
+calculate_pixman_format(struct fb_var_screeninfo *vinfo,
+ struct fb_fix_screeninfo *finfo)
+{
+ /* Calculate the pixman format supported by the frame buffer from the
+ * buffer's metadata. Return 0 if no known pixman format is supported
+ * (since this has depth 0 it's guaranteed to not conflict with any
+ * actual pixman format).
+ *
+ * Documentation on the vinfo and finfo structures:
+ * http://www.mjmwired.net/kernel/Documentation/fb/api.txt
+ *
+ * TODO: Try a bit harder to support other formats, including setting
+ * the preferred format in the hardware. */
+ int type;
+
+ weston_log("Calculating pixman format from:\n"
+ STAMP_SPACE " - type: %i (aux: %i)\n"
+ STAMP_SPACE " - visual: %i\n"
+ STAMP_SPACE " - bpp: %i (grayscale: %i)\n"
+ STAMP_SPACE " - red: offset: %i, length: %i, MSB: %i\n"
+ STAMP_SPACE " - green: offset: %i, length: %i, MSB: %i\n"
+ STAMP_SPACE " - blue: offset: %i, length: %i, MSB: %i\n"
+ STAMP_SPACE " - transp: offset: %i, length: %i, MSB: %i\n",
+ finfo->type, finfo->type_aux, finfo->visual,
+ vinfo->bits_per_pixel, vinfo->grayscale,
+ vinfo->red.offset, vinfo->red.length, vinfo->red.msb_right,
+ vinfo->green.offset, vinfo->green.length,
+ vinfo->green.msb_right,
+ vinfo->blue.offset, vinfo->blue.length,
+ vinfo->blue.msb_right,
+ vinfo->transp.offset, vinfo->transp.length,
+ vinfo->transp.msb_right);
+
+ /* We only handle packed formats at the moment. */
+ if (finfo->type != FB_TYPE_PACKED_PIXELS)
+ return 0;
+
+ /* We only handle true-colour frame buffers at the moment. */
+ switch(finfo->visual) {
+ case FB_VISUAL_TRUECOLOR:
+ case FB_VISUAL_DIRECTCOLOR:
+ if (vinfo->grayscale != 0)
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+
+ /* We only support formats with MSBs on the left. */
+ if (vinfo->red.msb_right != 0 || vinfo->green.msb_right != 0 ||
+ vinfo->blue.msb_right != 0)
+ return 0;
+
+ /* Work out the format type from the offsets. We only support RGBA and
+ * ARGB at the moment. */
+ type = PIXMAN_TYPE_OTHER;
+
+ if ((vinfo->transp.offset >= vinfo->red.offset ||
+ vinfo->transp.length == 0) &&
+ vinfo->red.offset >= vinfo->green.offset &&
+ vinfo->green.offset >= vinfo->blue.offset)
+ type = PIXMAN_TYPE_ARGB;
+ else if (vinfo->red.offset >= vinfo->green.offset &&
+ vinfo->green.offset >= vinfo->blue.offset &&
+ vinfo->blue.offset >= vinfo->transp.offset)
+ type = PIXMAN_TYPE_RGBA;
+
+ if (type == PIXMAN_TYPE_OTHER)
+ return 0;
+
+ /* Build the format. */
+ return PIXMAN_FORMAT(vinfo->bits_per_pixel, type,
+ vinfo->transp.length,
+ vinfo->red.length,
+ vinfo->green.length,
+ vinfo->blue.length);
+}
+
+static int
+calculate_refresh_rate(struct fb_var_screeninfo *vinfo)
+{
+ uint64_t quot;
+
+ /* Calculate monitor refresh rate. Default is 60 Hz. Units are mHz. */
+ quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres);
+ quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres);
+ quot *= vinfo->pixclock;
+
+ if (quot > 0) {
+ uint64_t refresh_rate;
+
+ refresh_rate = 1000000000000000LLU / quot;
+ if (refresh_rate > 200000)
+ refresh_rate = 200000; /* cap at 200 Hz */
+
+ return refresh_rate;
+ }
+
+ return 60 * 1000; /* default to 60 Hz */
+}
+
+static int
+fbdev_query_screen_info(struct fbdev_output *output, int fd,
+ struct fbdev_screeninfo *info)
+{
+ struct fb_var_screeninfo varinfo;
+ struct fb_fix_screeninfo fixinfo;
+
+ /* Probe the device for screen information. */
+ if (ioctl(fd, FBIOGET_FSCREENINFO, &fixinfo) < 0 ||
+ ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
+ return -1;
+ }
+
+ /* Store the pertinent data. */
+ info->x_resolution = varinfo.xres;
+ info->y_resolution = varinfo.yres;
+ info->width_mm = varinfo.width;
+ info->height_mm = varinfo.height;
+ info->bits_per_pixel = varinfo.bits_per_pixel;
+
+ info->buffer_length = fixinfo.smem_len;
+ info->line_length = fixinfo.line_length;
+ strncpy(info->id, fixinfo.id, sizeof(info->id));
+ info->id[sizeof(info->id)-1] = '\0';
+
+ info->pixel_format = calculate_pixman_format(&varinfo, &fixinfo);
+ info->refresh_rate = calculate_refresh_rate(&varinfo);
+
+ if (info->pixel_format == 0) {
+ weston_log("Frame buffer uses an unsupported format.\n");
+ return -1;
+ }
+
+ return 1;
+}
+
+static int
+fbdev_set_screen_info(struct fbdev_output *output, int fd,
+ struct fbdev_screeninfo *info)
+{
+ struct fb_var_screeninfo varinfo;
+
+ /* Grab the current screen information. */
+ if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
+ return -1;
+ }
+
+ /* Update the information. */
+ varinfo.xres = info->x_resolution;
+ varinfo.yres = info->y_resolution;
+ varinfo.width = info->width_mm;
+ varinfo.height = info->height_mm;
+ varinfo.bits_per_pixel = info->bits_per_pixel;
+
+ /* Try to set up an ARGB (x8r8g8b8) pixel format. */
+ varinfo.grayscale = 0;
+ varinfo.transp.offset = 24;
+ varinfo.transp.length = 0;
+ varinfo.transp.msb_right = 0;
+ varinfo.red.offset = 16;
+ varinfo.red.length = 8;
+ varinfo.red.msb_right = 0;
+ varinfo.green.offset = 8;
+ varinfo.green.length = 8;
+ varinfo.green.msb_right = 0;
+ varinfo.blue.offset = 0;
+ varinfo.blue.length = 8;
+ varinfo.blue.msb_right = 0;
+
+ /* Set the device's screen information. */
+ if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) {
+ return -1;
+ }
+
+ return 1;
+}
+
+static void fbdev_frame_buffer_destroy(struct fbdev_output *output);
+
+/* Returns an FD for the frame buffer device. */
+static int
+fbdev_frame_buffer_open(struct fbdev_output *output, const char *fb_dev,
+ struct fbdev_screeninfo *screen_info)
+{
+ int fd = -1;
+
+ weston_log("Opening fbdev frame buffer.\n");
+
+ /* Open the frame buffer device. */
+ fd = open(fb_dev, O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ weston_log("Failed to open frame buffer device ‘%s’: %s\n",
+ fb_dev, strerror(errno));
+ return -1;
+ }
+
+ /* Grab the screen info. */
+ if (fbdev_query_screen_info(output, fd, screen_info) < 0) {
+ weston_log("Failed to get frame buffer info: %s\n",
+ strerror(errno));
+
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+/* Closes the FD on success or failure. */
+static int
+fbdev_frame_buffer_map(struct fbdev_output *output, int fd)
+{
+ int retval = -1;
+
+ weston_log("Mapping fbdev frame buffer.\n");
+
+ /* Map the frame buffer. Write-only mode, since we don't want to read
+ * anything back (because it's slow). */
+ output->fb = mmap(NULL, output->fb_info.buffer_length,
+ PROT_WRITE, MAP_SHARED, fd, 0);
+ if (output->fb == MAP_FAILED) {
+ weston_log("Failed to mmap frame buffer: %s\n",
+ strerror(errno));
+ goto out_close;
+ }
+
+ /* Create a pixman image to wrap the memory mapped frame buffer. */
+ output->hw_surface =
+ pixman_image_create_bits(output->fb_info.pixel_format,
+ output->fb_info.x_resolution,
+ output->fb_info.y_resolution,
+ output->fb,
+ output->fb_info.line_length);
+ if (output->hw_surface == NULL) {
+ weston_log("Failed to create surface for frame buffer.\n");
+ goto out_unmap;
+ }
+
+ /* Success! */
+ retval = 0;
+
+out_unmap:
+ if (retval != 0 && output->fb != NULL)
+ fbdev_frame_buffer_destroy(output);
+
+out_close:
+ if (fd >= 0)
+ close(fd);
+
+ return retval;
+}
+
+static void
+fbdev_frame_buffer_destroy(struct fbdev_output *output)
+{
+ weston_log("Destroying fbdev frame buffer.\n");
+
+ if (munmap(output->fb, output->fb_info.buffer_length) < 0)
+ weston_log("Failed to munmap frame buffer: %s\n",
+ strerror(errno));
+
+ output->fb = NULL;
+}
+
+static void fbdev_output_destroy(struct weston_output *base);
+static void fbdev_output_disable(struct weston_output *base);
+
+static int
+fbdev_output_create(struct fbdev_backend *backend,
+ const char *device)
+{
+ struct fbdev_output *output;
+ int fb_fd;
+ struct wl_event_loop *loop;
+
+ weston_log("Creating fbdev output.\n");
+
+ output = zalloc(sizeof *output);
+ if (output == NULL)
+ return -1;
+
+ output->backend = backend;
+ output->device = strdup(device);
+
+ /* Create the frame buffer. */
+ fb_fd = fbdev_frame_buffer_open(output, device, &output->fb_info);
+ if (fb_fd < 0) {
+ weston_log("Creating frame buffer failed.\n");
+ goto out_free;
+ }
+
+ if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
+ weston_log("Mapping frame buffer failed.\n");
+ goto out_free;
+ }
+
+ output->base.start_repaint_loop = fbdev_output_start_repaint_loop;
+ output->base.repaint = fbdev_output_repaint;
+ output->base.destroy = fbdev_output_destroy;
+
+ /* only one static mode in list */
+ output->mode.flags =
+ WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+ output->mode.width = output->fb_info.x_resolution;
+ output->mode.height = output->fb_info.y_resolution;
+ output->mode.refresh = output->fb_info.refresh_rate;
+ wl_list_init(&output->base.mode_list);
+ wl_list_insert(&output->base.mode_list, &output->mode.link);
+
+ output->base.current_mode = &output->mode;
+ output->base.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
+ output->base.make = "unknown";
+ output->base.model = output->fb_info.id;
+ output->base.name = strdup("fbdev");
+
+ weston_output_init(&output->base, backend->compositor,
+ 0, 0, output->fb_info.width_mm,
+ output->fb_info.height_mm,
+ backend->output_transform,
+ 1);
+
+ if (pixman_renderer_output_create(&output->base) < 0)
+ goto out_hw_surface;
+
+ loop = wl_display_get_event_loop(backend->compositor->wl_display);
+ output->finish_frame_timer =
+ wl_event_loop_add_timer(loop, finish_frame_handler, output);
+
+ weston_compositor_add_output(backend->compositor, &output->base);
+
+ weston_log("fbdev output %d×%d px\n",
+ output->mode.width, output->mode.height);
+ weston_log_continue(STAMP_SPACE "guessing %d Hz and 96 dpi\n",
+ output->mode.refresh / 1000);
+
+ return 0;
+
+out_hw_surface:
+ pixman_image_unref(output->hw_surface);
+ output->hw_surface = NULL;
+ weston_output_destroy(&output->base);
+ fbdev_frame_buffer_destroy(output);
+out_free:
+ free(output->device);
+ free(output);
+
+ return -1;
+}
+
+static void
+fbdev_output_destroy(struct weston_output *base)
+{
+ struct fbdev_output *output = to_fbdev_output(base);
+
+ weston_log("Destroying fbdev output.\n");
+
+ /* Close the frame buffer. */
+ fbdev_output_disable(base);
+
+ if (base->renderer_state != NULL)
+ pixman_renderer_output_destroy(base);
+
+ /* Remove the output. */
+ weston_output_destroy(&output->base);
+
+ free(output->device);
+ free(output);
+}
+
+/* strcmp()-style return values. */
+static int
+compare_screen_info (const struct fbdev_screeninfo *a,
+ const struct fbdev_screeninfo *b)
+{
+ if (a->x_resolution == b->x_resolution &&
+ a->y_resolution == b->y_resolution &&
+ a->width_mm == b->width_mm &&
+ a->height_mm == b->height_mm &&
+ a->bits_per_pixel == b->bits_per_pixel &&
+ a->pixel_format == b->pixel_format &&
+ a->refresh_rate == b->refresh_rate)
+ return 0;
+
+ return 1;
+}
+
+static int
+fbdev_output_reenable(struct fbdev_backend *backend,
+ struct weston_output *base)
+{
+ struct fbdev_output *output = to_fbdev_output(base);
+ struct fbdev_screeninfo new_screen_info;
+ int fb_fd;
+ char *device;
+
+ weston_log("Re-enabling fbdev output.\n");
+
+ /* Create the frame buffer. */
+ fb_fd = fbdev_frame_buffer_open(output, output->device,
+ &new_screen_info);
+ if (fb_fd < 0) {
+ weston_log("Creating frame buffer failed.\n");
+ goto err;
+ }
+
+ /* Check whether the frame buffer details have changed since we were
+ * disabled. */
+ if (compare_screen_info (&output->fb_info, &new_screen_info) != 0) {
+ /* Perform a mode-set to restore the old mode. */
+ if (fbdev_set_screen_info(output, fb_fd,
+ &output->fb_info) < 0) {
+ weston_log("Failed to restore mode settings. "
+ "Attempting to re-open output anyway.\n");
+ }
+
+ close(fb_fd);
+
+ /* Remove and re-add the output so that resources depending on
+ * the frame buffer X/Y resolution (such as the shadow buffer)
+ * are re-initialised. */
+ device = strdup(output->device);
+ fbdev_output_destroy(&output->base);
+ fbdev_output_create(backend, device);
+ free(device);
+
+ return 0;
+ }
+
+ /* Map the device if it has the same details as before. */
+ if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
+ weston_log("Mapping frame buffer failed.\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ return -1;
+}
+
+/* NOTE: This leaves output->fb_info populated, caching data so that if
+ * fbdev_output_reenable() is called again, it can determine whether a mode-set
+ * is needed. */
+static void
+fbdev_output_disable(struct weston_output *base)
+{
+ struct fbdev_output *output = to_fbdev_output(base);
+
+ weston_log("Disabling fbdev output.\n");
+
+ if (output->hw_surface != NULL) {
+ pixman_image_unref(output->hw_surface);
+ output->hw_surface = NULL;
+ }
+
+ fbdev_frame_buffer_destroy(output);
+}
+
+static void
+fbdev_backend_destroy(struct weston_compositor *base)
+{
+ struct fbdev_backend *backend = to_fbdev_backend(base);
+
+ udev_input_destroy(&backend->input);
+
+ /* Destroy the output. */
+ weston_compositor_shutdown(base);
+
+ /* Chain up. */
+ weston_launcher_destroy(base->launcher);
+
+ free(backend);
+}
+
+static void
+session_notify(struct wl_listener *listener, void *data)
+{
+ struct weston_compositor *compositor = data;
+ struct fbdev_backend *backend = to_fbdev_backend(compositor);
+ struct weston_output *output;
+
+ if (compositor->session_active) {
+ weston_log("entering VT\n");
+ compositor->state = backend->prev_state;
+
+ wl_list_for_each(output, &compositor->output_list, link) {
+ fbdev_output_reenable(backend, output);
+ }
+
+ weston_compositor_damage_all(compositor);
+
+ udev_input_enable(&backend->input);
+ } else {
+ weston_log("leaving VT\n");
+ udev_input_disable(&backend->input);
+
+ wl_list_for_each(output, &compositor->output_list, link) {
+ fbdev_output_disable(output);
+ }
+
+ backend->prev_state = compositor->state;
+ weston_compositor_offscreen(compositor);
+
+ /* If we have a repaint scheduled (from the idle handler), make
+ * sure we cancel that so we don't try to pageflip when we're
+ * vt switched away. The OFFSCREEN state will prevent
+ * further attempts at repainting. When we switch
+ * back, we schedule a repaint, which will process
+ * pending frame callbacks. */
+
+ wl_list_for_each(output,
+ &compositor->output_list, link) {
+ output->repaint_needed = 0;
+ }
+ }
+}
+
+static void
+fbdev_restore(struct weston_compositor *compositor)
+{
+ weston_launcher_restore(compositor->launcher);
+}
+
+static struct fbdev_backend *
+fbdev_backend_create(struct weston_compositor *compositor,
+ struct weston_fbdev_backend_config *param)
+{
+ struct fbdev_backend *backend;
+ const char *seat_id = default_seat;
+
+ weston_log("initializing fbdev backend\n");
+
+ backend = zalloc(sizeof *backend);
+ if (backend == NULL)
+ return NULL;
+
+ backend->compositor = compositor;
+ if (weston_compositor_set_presentation_clock_software(
+ compositor) < 0)
+ goto out_compositor;
+
+ backend->udev = udev_new();
+ if (backend->udev == NULL) {
+ weston_log("Failed to initialize udev context.\n");
+ goto out_compositor;
+ }
+
+ /* Set up the TTY. */
+ backend->session_listener.notify = session_notify;
+ wl_signal_add(&compositor->session_signal,
+ &backend->session_listener);
+ compositor->launcher =
+ weston_launcher_connect(compositor, param->tty, "seat0", false);
+ if (!compositor->launcher) {
+ weston_log("fatal: fbdev backend should be run "
+ "using weston-launch binary or as root\n");
+ goto out_udev;
+ }
+
+ backend->base.destroy = fbdev_backend_destroy;
+ backend->base.restore = fbdev_restore;
+
+ backend->prev_state = WESTON_COMPOSITOR_ACTIVE;
+ backend->output_transform = param->output_transform;
+
+ weston_setup_vt_switch_bindings(compositor);
+
+ if (pixman_renderer_init(compositor) < 0)
+ goto out_launcher;
+
+ if (fbdev_output_create(backend, param->device) < 0)
+ goto out_launcher;
+
+ udev_input_init(&backend->input, compositor, backend->udev,
+ seat_id, param->configure_device);
+
+ compositor->backend = &backend->base;
+ return backend;
+
+out_launcher:
+ weston_launcher_destroy(compositor->launcher);
+
+out_udev:
+ udev_unref(backend->udev);
+
+out_compositor:
+ weston_compositor_shutdown(compositor);
+ free(backend);
+
+ return NULL;
+}
+
+static void
+config_init_to_defaults(struct weston_fbdev_backend_config *config)
+{
+ /* TODO: Ideally, available frame buffers should be enumerated using
+ * udev, rather than passing a device node in as a parameter. */
+ config->tty = 0; /* default to current tty */
+ config->device = "/dev/fb0"; /* default frame buffer */
+ config->output_transform = WL_OUTPUT_TRANSFORM_NORMAL;
+}
+
+WL_EXPORT int
+backend_init(struct weston_compositor *compositor,
+ struct weston_backend_config *config_base)
+{
+ struct fbdev_backend *b;
+ struct weston_fbdev_backend_config config = {{ 0, }};
+
+ if (config_base == NULL ||
+ config_base->struct_version != WESTON_FBDEV_BACKEND_CONFIG_VERSION ||
+ config_base->struct_size > sizeof(struct weston_fbdev_backend_config)) {
+ weston_log("fbdev backend config structure is invalid\n");
+ return -1;
+ }
+
+ config_init_to_defaults(&config);
+ memcpy(&config, config_base, config_base->struct_size);
+
+ b = fbdev_backend_create(compositor, &config);
+ if (b == NULL)
+ return -1;
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 2016 Benoit Gschwind
+ *
+ * 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.
+ */
+
+#ifndef WESTON_COMPOSITOR_FBDEV_H
+#define WESTON_COMPOSITOR_FBDEV_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "compositor.h"
+
+#define WESTON_FBDEV_BACKEND_CONFIG_VERSION 1
+
+struct libinput_device;
+
+struct weston_fbdev_backend_config {
+ struct weston_backend_config base;
+
+ int tty;
+ char *device;
+
+ uint32_t output_transform;
+
+ /** Callback used to configure input devices.
+ *
+ * This function will be called by the backend when a new input device
+ * needs to be configured.
+ * If NULL the device will use the default configuration.
+ */
+ void (*configure_device)(struct weston_compositor *compositor,
+ struct libinput_device *device);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WESTON_COMPOSITOR_FBDEV_H */
--- /dev/null
+/*
+ * Copyright © 2010-2011 Benjamin Franzke
+ * Copyright © 2012 Intel Corporation
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <stdbool.h>
+
+#include "compositor.h"
+#include "compositor-headless.h"
+#include "shared/helpers.h"
+#include "pixman-renderer.h"
+#include "presentation-time-server-protocol.h"
+
+struct headless_backend {
+ struct weston_backend base;
+ struct weston_compositor *compositor;
+
+ struct weston_seat fake_seat;
+ bool use_pixman;
+};
+
+struct headless_output {
+ struct weston_output base;
+
+ struct weston_mode mode;
+ struct wl_event_source *finish_frame_timer;
+ uint32_t *image_buf;
+ pixman_image_t *image;
+};
+
+static void
+headless_output_start_repaint_loop(struct weston_output *output)
+{
+ struct timespec ts;
+
+ weston_compositor_read_presentation_clock(output->compositor, &ts);
+ weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
+}
+
+static int
+finish_frame_handler(void *data)
+{
+ struct headless_output *output = data;
+ struct timespec ts;
+
+ weston_compositor_read_presentation_clock(output->base.compositor, &ts);
+ weston_output_finish_frame(&output->base, &ts, 0);
+
+ return 1;
+}
+
+static int
+headless_output_repaint(struct weston_output *output_base,
+ pixman_region32_t *damage)
+{
+ struct headless_output *output = (struct headless_output *) output_base;
+ struct weston_compositor *ec = output->base.compositor;
+
+ ec->renderer->repaint_output(&output->base, damage);
+
+ pixman_region32_subtract(&ec->primary_plane.damage,
+ &ec->primary_plane.damage, damage);
+
+ wl_event_source_timer_update(output->finish_frame_timer, 16);
+
+ return 0;
+}
+
+static void
+headless_output_destroy(struct weston_output *output_base)
+{
+ struct headless_output *output = (struct headless_output *) output_base;
+ struct headless_backend *b =
+ (struct headless_backend *) output->base.compositor->backend;
+
+ wl_event_source_remove(output->finish_frame_timer);
+
+ if (b->use_pixman) {
+ pixman_renderer_output_destroy(&output->base);
+ pixman_image_unref(output->image);
+ free(output->image_buf);
+ }
+
+ weston_output_destroy(&output->base);
+
+ free(output);
+
+ return;
+}
+
+static int
+headless_backend_create_output(struct headless_backend *b,
+ struct weston_headless_backend_config *config)
+{
+ struct weston_compositor *c = b->compositor;
+ struct headless_output *output;
+ struct wl_event_loop *loop;
+
+ output = zalloc(sizeof *output);
+ if (output == NULL)
+ return -1;
+
+ output->mode.flags =
+ WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+ output->mode.width = config->width;
+ output->mode.height = config->height;
+ output->mode.refresh = 60000;
+ wl_list_init(&output->base.mode_list);
+ wl_list_insert(&output->base.mode_list, &output->mode.link);
+
+ output->base.current_mode = &output->mode;
+ weston_output_init(&output->base, c, 0, 0, config->width,
+ config->height, config->transform, 1);
+
+ output->base.make = "weston";
+ output->base.model = "headless";
+
+ loop = wl_display_get_event_loop(c->wl_display);
+ output->finish_frame_timer =
+ wl_event_loop_add_timer(loop, finish_frame_handler, output);
+
+ output->base.start_repaint_loop = headless_output_start_repaint_loop;
+ output->base.repaint = headless_output_repaint;
+ output->base.destroy = headless_output_destroy;
+ output->base.assign_planes = NULL;
+ output->base.set_backlight = NULL;
+ output->base.set_dpms = NULL;
+ output->base.switch_mode = NULL;
+
+ if (b->use_pixman) {
+ output->image_buf = malloc(config->width * config->height * 4);
+ if (!output->image_buf)
+ return -1;
+
+ output->image = pixman_image_create_bits(PIXMAN_x8r8g8b8,
+ config->width,
+ config->height,
+ output->image_buf,
+ config->width * 4);
+
+ if (pixman_renderer_output_create(&output->base) < 0)
+ return -1;
+
+ pixman_renderer_output_set_buffer(&output->base,
+ output->image);
+ }
+
+ weston_compositor_add_output(c, &output->base);
+
+ return 0;
+}
+
+static void
+headless_restore(struct weston_compositor *ec)
+{
+}
+
+static void
+headless_destroy(struct weston_compositor *ec)
+{
+ struct headless_backend *b = (struct headless_backend *) ec->backend;
+
+ weston_compositor_shutdown(ec);
+
+ free(b);
+}
+
+static struct headless_backend *
+headless_backend_create(struct weston_compositor *compositor,
+ struct weston_headless_backend_config *config)
+{
+ struct headless_backend *b;
+
+ b = zalloc(sizeof *b);
+ if (b == NULL)
+ return NULL;
+
+ b->compositor = compositor;
+ if (weston_compositor_set_presentation_clock_software(compositor) < 0)
+ goto err_free;
+
+ b->base.destroy = headless_destroy;
+ b->base.restore = headless_restore;
+
+ b->use_pixman = config->use_pixman;
+ if (b->use_pixman) {
+ pixman_renderer_init(compositor);
+ }
+ if (headless_backend_create_output(b, config) < 0)
+ goto err_input;
+
+ if (!b->use_pixman && noop_renderer_init(compositor) < 0)
+ goto err_input;
+
+ compositor->backend = &b->base;
+ return b;
+
+err_input:
+ weston_compositor_shutdown(compositor);
+err_free:
+ free(b);
+ return NULL;
+}
+
+static void
+config_init_to_defaults(struct weston_headless_backend_config *config)
+{
+}
+
+WL_EXPORT int
+backend_init(struct weston_compositor *compositor,
+ struct weston_backend_config *config_base)
+{
+ struct headless_backend *b;
+ struct weston_headless_backend_config config = {{ 0, }};
+
+ if (config_base == NULL ||
+ config_base->struct_version != WESTON_HEADLESS_BACKEND_CONFIG_VERSION ||
+ config_base->struct_size > sizeof(struct weston_headless_backend_config)) {
+ weston_log("headless backend config structure is invalid\n");
+ return -1;
+ }
+
+ config_init_to_defaults(&config);
+ memcpy(&config, config_base, config_base->struct_size);
+
+ b = headless_backend_create(compositor, &config);
+ if (b == NULL)
+ return -1;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 2016 Benoit Gschwind
+ *
+ * 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.
+ */
+
+#ifndef WESTON_COMPOSITOR_HEADLESS_H
+#define WESTON_COMPOSITOR_HEADLESS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "compositor.h"
+
+#define WESTON_HEADLESS_BACKEND_CONFIG_VERSION 1
+
+struct weston_headless_backend_config {
+ struct weston_backend_config base;
+
+ int width;
+ int height;
+
+ /** Whether to use the pixman renderer instead of the OpenGL ES renderer. */
+ int use_pixman;
+
+ uint32_t transform;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WESTON_COMPOSITOR_HEADLESS_H */
--- /dev/null
+/*
+ * Copyright © 2013 Hardening <rdp.effort@gmail.com>
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <linux/input.h>
+
+#if HAVE_FREERDP_VERSION_H
+#include <freerdp/version.h>
+#else
+/* assume it's a early 1.1 version */
+#define FREERDP_VERSION_MAJOR 1
+#define FREERDP_VERSION_MINOR 1
+#define FREERDP_VERSION_REVISION 0
+#endif
+
+#define FREERDP_VERSION_NUMBER ((FREERDP_VERSION_MAJOR * 0x10000) + \
+ (FREERDP_VERSION_MINOR * 0x100) + FREERDP_VERSION_REVISION)
+
+
+#if FREERDP_VERSION_NUMBER >= 0x10201
+#define HAVE_SKIP_COMPRESSION
+#endif
+
+#if FREERDP_VERSION_NUMBER < 0x10202
+# define FREERDP_CB_RET_TYPE void
+# define FREERDP_CB_RETURN(V) return
+# define NSC_RESET(C, W, H)
+# define RFX_RESET(C, W, H) do { rfx_context_reset(C); C->width = W; C->height = H; } while(0)
+#else
+#if FREERDP_VERSION_MAJOR >= 2
+# define NSC_RESET(C, W, H) nsc_context_reset(C, W, H)
+# define RFX_RESET(C, W, H) rfx_context_reset(C, W, H)
+#else
+# define NSC_RESET(C, W, H) do { nsc_context_reset(C); C->width = W; C->height = H; } while(0)
+# define RFX_RESET(C, W, H) do { rfx_context_reset(C); C->width = W; C->height = H; } while(0)
+#endif
+#define FREERDP_CB_RET_TYPE BOOL
+#define FREERDP_CB_RETURN(V) return TRUE
+#endif
+
+#include <freerdp/freerdp.h>
+#include <freerdp/listener.h>
+#include <freerdp/update.h>
+#include <freerdp/input.h>
+#include <freerdp/codec/color.h>
+#include <freerdp/codec/rfx.h>
+#include <freerdp/codec/nsc.h>
+#include <freerdp/locale/keyboard.h>
+#include <winpr/input.h>
+
+#include "shared/helpers.h"
+#include "compositor.h"
+#include "compositor-rdp.h"
+#include "pixman-renderer.h"
+
+#define MAX_FREERDP_FDS 32
+#define DEFAULT_AXIS_STEP_DISTANCE 10
+#define RDP_MODE_FREQ 60 * 1000
+
+
+struct rdp_output;
+
+struct rdp_backend {
+ struct weston_backend base;
+ struct weston_compositor *compositor;
+
+ freerdp_listener *listener;
+ struct wl_event_source *listener_events[MAX_FREERDP_FDS];
+ struct rdp_output *output;
+
+ char *server_cert;
+ char *server_key;
+ char *rdp_key;
+ int tls_enabled;
+ int no_clients_resize;
+};
+
+enum peer_item_flags {
+ RDP_PEER_ACTIVATED = (1 << 0),
+ RDP_PEER_OUTPUT_ENABLED = (1 << 1),
+};
+
+struct rdp_peers_item {
+ int flags;
+ freerdp_peer *peer;
+ struct weston_seat *seat;
+
+ struct wl_list link;
+};
+
+struct rdp_output {
+ struct weston_output base;
+ struct wl_event_source *finish_frame_timer;
+ pixman_image_t *shadow_surface;
+
+ struct wl_list peers;
+};
+
+struct rdp_peer_context {
+ rdpContext _p;
+
+ struct rdp_backend *rdpBackend;
+ struct wl_event_source *events[MAX_FREERDP_FDS];
+ RFX_CONTEXT *rfx_context;
+ wStream *encode_stream;
+ RFX_RECT *rfx_rects;
+ NSC_CONTEXT *nsc_context;
+
+ struct rdp_peers_item item;
+};
+typedef struct rdp_peer_context RdpPeerContext;
+
+static void
+rdp_peer_refresh_rfx(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer)
+{
+ int width, height, nrects, i;
+ pixman_box32_t *region, *rects;
+ uint32_t *ptr;
+ RFX_RECT *rfxRect;
+ rdpUpdate *update = peer->update;
+ SURFACE_BITS_COMMAND *cmd = &update->surface_bits_command;
+ RdpPeerContext *context = (RdpPeerContext *)peer->context;
+
+ Stream_Clear(context->encode_stream);
+ Stream_SetPosition(context->encode_stream, 0);
+
+ width = (damage->extents.x2 - damage->extents.x1);
+ height = (damage->extents.y2 - damage->extents.y1);
+
+#ifdef HAVE_SKIP_COMPRESSION
+ cmd->skipCompression = TRUE;
+#else
+ memset(cmd, 0, sizeof(*cmd));
+#endif
+ cmd->destLeft = damage->extents.x1;
+ cmd->destTop = damage->extents.y1;
+ cmd->destRight = damage->extents.x2;
+ cmd->destBottom = damage->extents.y2;
+ cmd->bpp = 32;
+ cmd->codecID = peer->settings->RemoteFxCodecId;
+ cmd->width = width;
+ cmd->height = height;
+
+ ptr = pixman_image_get_data(image) + damage->extents.x1 +
+ damage->extents.y1 * (pixman_image_get_stride(image) / sizeof(uint32_t));
+
+ rects = pixman_region32_rectangles(damage, &nrects);
+ context->rfx_rects = realloc(context->rfx_rects, nrects * sizeof *rfxRect);
+
+ for (i = 0; i < nrects; i++) {
+ region = &rects[i];
+ rfxRect = &context->rfx_rects[i];
+
+ rfxRect->x = (region->x1 - damage->extents.x1);
+ rfxRect->y = (region->y1 - damage->extents.y1);
+ rfxRect->width = (region->x2 - region->x1);
+ rfxRect->height = (region->y2 - region->y1);
+ }
+
+ rfx_compose_message(context->rfx_context, context->encode_stream, context->rfx_rects, nrects,
+ (BYTE *)ptr, width, height,
+ pixman_image_get_stride(image)
+ );
+
+ cmd->bitmapDataLength = Stream_GetPosition(context->encode_stream);
+ cmd->bitmapData = Stream_Buffer(context->encode_stream);
+
+ update->SurfaceBits(update->context, cmd);
+}
+
+
+static void
+rdp_peer_refresh_nsc(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer)
+{
+ int width, height;
+ uint32_t *ptr;
+ rdpUpdate *update = peer->update;
+ SURFACE_BITS_COMMAND *cmd = &update->surface_bits_command;
+ RdpPeerContext *context = (RdpPeerContext *)peer->context;
+
+ Stream_Clear(context->encode_stream);
+ Stream_SetPosition(context->encode_stream, 0);
+
+ width = (damage->extents.x2 - damage->extents.x1);
+ height = (damage->extents.y2 - damage->extents.y1);
+
+#ifdef HAVE_SKIP_COMPRESSION
+ cmd->skipCompression = TRUE;
+#else
+ memset(cmd, 0, sizeof(*cmd));
+#endif
+ cmd->destLeft = damage->extents.x1;
+ cmd->destTop = damage->extents.y1;
+ cmd->destRight = damage->extents.x2;
+ cmd->destBottom = damage->extents.y2;
+ cmd->bpp = 32;
+ cmd->codecID = peer->settings->NSCodecId;
+ cmd->width = width;
+ cmd->height = height;
+
+ ptr = pixman_image_get_data(image) + damage->extents.x1 +
+ damage->extents.y1 * (pixman_image_get_stride(image) / sizeof(uint32_t));
+
+ nsc_compose_message(context->nsc_context, context->encode_stream, (BYTE *)ptr,
+ cmd->width, cmd->height,
+ pixman_image_get_stride(image));
+ cmd->bitmapDataLength = Stream_GetPosition(context->encode_stream);
+ cmd->bitmapData = Stream_Buffer(context->encode_stream);
+ update->SurfaceBits(update->context, cmd);
+}
+
+static void
+pixman_image_flipped_subrect(const pixman_box32_t *rect, pixman_image_t *img, BYTE *dest)
+{
+ int stride = pixman_image_get_stride(img);
+ int h;
+ int toCopy = (rect->x2 - rect->x1) * 4;
+ int height = (rect->y2 - rect->y1);
+ const BYTE *src = (const BYTE *)pixman_image_get_data(img);
+ src += ((rect->y2-1) * stride) + (rect->x1 * 4);
+
+ for (h = 0; h < height; h++, src -= stride, dest += toCopy)
+ memcpy(dest, src, toCopy);
+}
+
+static void
+rdp_peer_refresh_raw(pixman_region32_t *region, pixman_image_t *image, freerdp_peer *peer)
+{
+ rdpUpdate *update = peer->update;
+ SURFACE_BITS_COMMAND *cmd = &update->surface_bits_command;
+ SURFACE_FRAME_MARKER *marker = &update->surface_frame_marker;
+ pixman_box32_t *rect, subrect;
+ int nrects, i;
+ int heightIncrement, remainingHeight, top;
+
+ rect = pixman_region32_rectangles(region, &nrects);
+ if (!nrects)
+ return;
+
+ marker->frameId++;
+ marker->frameAction = SURFACECMD_FRAMEACTION_BEGIN;
+ update->SurfaceFrameMarker(peer->context, marker);
+
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->bpp = 32;
+ cmd->codecID = 0;
+
+ for (i = 0; i < nrects; i++, rect++) {
+ /*weston_log("rect(%d,%d, %d,%d)\n", rect->x1, rect->y1, rect->x2, rect->y2);*/
+ cmd->destLeft = rect->x1;
+ cmd->destRight = rect->x2;
+ cmd->width = rect->x2 - rect->x1;
+
+ heightIncrement = peer->settings->MultifragMaxRequestSize / (16 + cmd->width * 4);
+ remainingHeight = rect->y2 - rect->y1;
+ top = rect->y1;
+
+ subrect.x1 = rect->x1;
+ subrect.x2 = rect->x2;
+
+ while (remainingHeight) {
+ cmd->height = (remainingHeight > heightIncrement) ? heightIncrement : remainingHeight;
+ cmd->destTop = top;
+ cmd->destBottom = top + cmd->height;
+ cmd->bitmapDataLength = cmd->width * cmd->height * 4;
+ cmd->bitmapData = (BYTE *)realloc(cmd->bitmapData, cmd->bitmapDataLength);
+
+ subrect.y1 = top;
+ subrect.y2 = top + cmd->height;
+ pixman_image_flipped_subrect(&subrect, image, cmd->bitmapData);
+
+ /*weston_log("* sending (%d,%d, %d,%d)\n", subrect.x1, subrect.y1, subrect.x2, subrect.y2); */
+ update->SurfaceBits(peer->context, cmd);
+
+ remainingHeight -= cmd->height;
+ top += cmd->height;
+ }
+ }
+
+ marker->frameAction = SURFACECMD_FRAMEACTION_END;
+ update->SurfaceFrameMarker(peer->context, marker);
+}
+
+static void
+rdp_peer_refresh_region(pixman_region32_t *region, freerdp_peer *peer)
+{
+ RdpPeerContext *context = (RdpPeerContext *)peer->context;
+ struct rdp_output *output = context->rdpBackend->output;
+ rdpSettings *settings = peer->settings;
+
+ if (settings->RemoteFxCodec)
+ rdp_peer_refresh_rfx(region, output->shadow_surface, peer);
+ else if (settings->NSCodec)
+ rdp_peer_refresh_nsc(region, output->shadow_surface, peer);
+ else
+ rdp_peer_refresh_raw(region, output->shadow_surface, peer);
+}
+
+static void
+rdp_output_start_repaint_loop(struct weston_output *output)
+{
+ struct timespec ts;
+
+ weston_compositor_read_presentation_clock(output->compositor, &ts);
+ weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
+}
+
+static int
+rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage)
+{
+ struct rdp_output *output = container_of(output_base, struct rdp_output, base);
+ struct weston_compositor *ec = output->base.compositor;
+ struct rdp_peers_item *outputPeer;
+
+ pixman_renderer_output_set_buffer(output_base, output->shadow_surface);
+ ec->renderer->repaint_output(&output->base, damage);
+
+ if (pixman_region32_not_empty(damage)) {
+ wl_list_for_each(outputPeer, &output->peers, link) {
+ if ((outputPeer->flags & RDP_PEER_ACTIVATED) &&
+ (outputPeer->flags & RDP_PEER_OUTPUT_ENABLED))
+ {
+ rdp_peer_refresh_region(damage, outputPeer->peer);
+ }
+ }
+ }
+
+ pixman_region32_subtract(&ec->primary_plane.damage,
+ &ec->primary_plane.damage, damage);
+
+ wl_event_source_timer_update(output->finish_frame_timer, 16);
+ return 0;
+}
+
+static void
+rdp_output_destroy(struct weston_output *output_base)
+{
+ struct rdp_output *output = (struct rdp_output *)output_base;
+
+ wl_event_source_remove(output->finish_frame_timer);
+ free(output);
+}
+
+static int
+finish_frame_handler(void *data)
+{
+ struct rdp_output *output = data;
+ struct timespec ts;
+
+ weston_compositor_read_presentation_clock(output->base.compositor, &ts);
+ weston_output_finish_frame(&output->base, &ts, 0);
+
+ return 1;
+}
+
+static struct weston_mode *
+rdp_insert_new_mode(struct weston_output *output, int width, int height, int rate)
+{
+ struct weston_mode *ret;
+ ret = zalloc(sizeof *ret);
+ if (!ret)
+ return NULL;
+ ret->width = width;
+ ret->height = height;
+ ret->refresh = rate;
+ wl_list_insert(&output->mode_list, &ret->link);
+ return ret;
+}
+
+static struct weston_mode *
+ensure_matching_mode(struct weston_output *output, struct weston_mode *target)
+{
+ struct weston_mode *local;
+
+ wl_list_for_each(local, &output->mode_list, link) {
+ if ((local->width == target->width) && (local->height == target->height))
+ return local;
+ }
+
+ return rdp_insert_new_mode(output, target->width, target->height, RDP_MODE_FREQ);
+}
+
+static int
+rdp_switch_mode(struct weston_output *output, struct weston_mode *target_mode)
+{
+ struct rdp_output *rdpOutput = container_of(output, struct rdp_output, base);
+ struct rdp_peers_item *rdpPeer;
+ rdpSettings *settings;
+ pixman_image_t *new_shadow_buffer;
+ struct weston_mode *local_mode;
+
+ local_mode = ensure_matching_mode(output, target_mode);
+ if (!local_mode) {
+ weston_log("mode %dx%d not available\n", target_mode->width, target_mode->height);
+ return -ENOENT;
+ }
+
+ if (local_mode == output->current_mode)
+ return 0;
+
+ output->current_mode->flags &= ~WL_OUTPUT_MODE_CURRENT;
+
+ output->current_mode = local_mode;
+ output->current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
+
+ pixman_renderer_output_destroy(output);
+ pixman_renderer_output_create(output);
+
+ new_shadow_buffer = pixman_image_create_bits(PIXMAN_x8r8g8b8, target_mode->width,
+ target_mode->height, 0, target_mode->width * 4);
+ pixman_image_composite32(PIXMAN_OP_SRC, rdpOutput->shadow_surface, 0, new_shadow_buffer,
+ 0, 0, 0, 0, 0, 0, target_mode->width, target_mode->height);
+ pixman_image_unref(rdpOutput->shadow_surface);
+ rdpOutput->shadow_surface = new_shadow_buffer;
+
+ wl_list_for_each(rdpPeer, &rdpOutput->peers, link) {
+ settings = rdpPeer->peer->settings;
+ if (settings->DesktopWidth == (UINT32)target_mode->width &&
+ settings->DesktopHeight == (UINT32)target_mode->height)
+ continue;
+
+ if (!settings->DesktopResize) {
+ /* too bad this peer does not support desktop resize */
+ rdpPeer->peer->Close(rdpPeer->peer);
+ } else {
+ settings->DesktopWidth = target_mode->width;
+ settings->DesktopHeight = target_mode->height;
+ rdpPeer->peer->update->DesktopResize(rdpPeer->peer->context);
+ }
+ }
+ return 0;
+}
+
+static int
+rdp_backend_create_output(struct rdp_backend *b, int width, int height)
+{
+ struct rdp_output *output;
+ struct wl_event_loop *loop;
+ struct weston_mode *currentMode;
+ struct weston_mode initMode;
+
+ output = zalloc(sizeof *output);
+ if (output == NULL)
+ return -1;
+
+ wl_list_init(&output->peers);
+ wl_list_init(&output->base.mode_list);
+
+ initMode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+ initMode.width = width;
+ initMode.height = height;
+ initMode.refresh = RDP_MODE_FREQ;
+
+ currentMode = ensure_matching_mode(&output->base, &initMode);
+ if (!currentMode)
+ goto out_free_output;
+
+ output->base.current_mode = output->base.native_mode = currentMode;
+ weston_output_init(&output->base, b->compositor, 0, 0, width, height,
+ WL_OUTPUT_TRANSFORM_NORMAL, 1);
+
+ output->base.make = "weston";
+ output->base.model = "rdp";
+ output->shadow_surface = pixman_image_create_bits(PIXMAN_x8r8g8b8,
+ width, height,
+ NULL,
+ width * 4);
+ if (output->shadow_surface == NULL) {
+ weston_log("Failed to create surface for frame buffer.\n");
+ goto out_output;
+ }
+
+ if (pixman_renderer_output_create(&output->base) < 0)
+ goto out_shadow_surface;
+
+ loop = wl_display_get_event_loop(b->compositor->wl_display);
+ output->finish_frame_timer = wl_event_loop_add_timer(loop, finish_frame_handler, output);
+
+ output->base.start_repaint_loop = rdp_output_start_repaint_loop;
+ output->base.repaint = rdp_output_repaint;
+ output->base.destroy = rdp_output_destroy;
+ output->base.assign_planes = NULL;
+ output->base.set_backlight = NULL;
+ output->base.set_dpms = NULL;
+ output->base.switch_mode = rdp_switch_mode;
+ b->output = output;
+
+ weston_compositor_add_output(b->compositor, &output->base);
+ return 0;
+
+out_shadow_surface:
+ pixman_image_unref(output->shadow_surface);
+out_output:
+ weston_output_destroy(&output->base);
+out_free_output:
+ free(output);
+ return -1;
+}
+
+static void
+rdp_restore(struct weston_compositor *ec)
+{
+}
+
+static void
+rdp_destroy(struct weston_compositor *ec)
+{
+ struct rdp_backend *b = (struct rdp_backend *) ec->backend;
+ int i;
+
+ weston_compositor_shutdown(ec);
+ for (i = 0; i < MAX_FREERDP_FDS; i++)
+ if (b->listener_events[i])
+ wl_event_source_remove(b->listener_events[i]);
+
+ freerdp_listener_free(b->listener);
+
+ free(b->server_cert);
+ free(b->server_key);
+ free(b->rdp_key);
+ free(b);
+}
+
+static
+int rdp_listener_activity(int fd, uint32_t mask, void *data)
+{
+ freerdp_listener* instance = (freerdp_listener *)data;
+
+ if (!(mask & WL_EVENT_READABLE))
+ return 0;
+ if (!instance->CheckFileDescriptor(instance)) {
+ weston_log("failed to check FreeRDP file descriptor\n");
+ return -1;
+ }
+ return 0;
+}
+
+static
+int rdp_implant_listener(struct rdp_backend *b, freerdp_listener* instance)
+{
+ int i, fd;
+ int rcount = 0;
+ void* rfds[MAX_FREERDP_FDS];
+ struct wl_event_loop *loop;
+
+ if (!instance->GetFileDescriptor(instance, rfds, &rcount)) {
+ weston_log("Failed to get FreeRDP file descriptor\n");
+ return -1;
+ }
+
+ loop = wl_display_get_event_loop(b->compositor->wl_display);
+ for (i = 0; i < rcount; i++) {
+ fd = (int)(long)(rfds[i]);
+ b->listener_events[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
+ rdp_listener_activity, instance);
+ }
+
+ for ( ; i < MAX_FREERDP_FDS; i++)
+ b->listener_events[i] = 0;
+ return 0;
+}
+
+
+static FREERDP_CB_RET_TYPE
+rdp_peer_context_new(freerdp_peer* client, RdpPeerContext* context)
+{
+ context->item.peer = client;
+ context->item.flags = RDP_PEER_OUTPUT_ENABLED;
+
+#if FREERDP_VERSION_MAJOR == 1 && FREERDP_VERSION_MINOR == 1
+ context->rfx_context = rfx_context_new();
+#else
+ context->rfx_context = rfx_context_new(TRUE);
+#endif
+ if (!context->rfx_context) {
+ FREERDP_CB_RETURN(FALSE);
+ }
+
+ context->rfx_context->mode = RLGR3;
+ context->rfx_context->width = client->settings->DesktopWidth;
+ context->rfx_context->height = client->settings->DesktopHeight;
+ rfx_context_set_pixel_format(context->rfx_context, RDP_PIXEL_FORMAT_B8G8R8A8);
+
+ context->nsc_context = nsc_context_new();
+ if (!context->nsc_context)
+ goto out_error_nsc;
+
+ nsc_context_set_pixel_format(context->nsc_context, RDP_PIXEL_FORMAT_B8G8R8A8);
+
+ context->encode_stream = Stream_New(NULL, 65536);
+ if (!context->encode_stream)
+ goto out_error_stream;
+
+ FREERDP_CB_RETURN(TRUE);
+
+out_error_nsc:
+ rfx_context_free(context->rfx_context);
+out_error_stream:
+ nsc_context_free(context->nsc_context);
+ FREERDP_CB_RETURN(FALSE);
+}
+
+static void
+rdp_peer_context_free(freerdp_peer* client, RdpPeerContext* context)
+{
+ int i;
+ if (!context)
+ return;
+
+ wl_list_remove(&context->item.link);
+ for (i = 0; i < MAX_FREERDP_FDS; i++) {
+ if (context->events[i])
+ wl_event_source_remove(context->events[i]);
+ }
+
+ if (context->item.flags & RDP_PEER_ACTIVATED) {
+ weston_seat_release_keyboard(context->item.seat);
+ weston_seat_release_pointer(context->item.seat);
+ /* XXX we should weston_seat_release(context->item.seat); here
+ * but it would crash on reconnect */
+ }
+
+ Stream_Free(context->encode_stream, TRUE);
+ nsc_context_free(context->nsc_context);
+ rfx_context_free(context->rfx_context);
+ free(context->rfx_rects);
+}
+
+
+static int
+rdp_client_activity(int fd, uint32_t mask, void *data)
+{
+ freerdp_peer* client = (freerdp_peer *)data;
+
+ if (!client->CheckFileDescriptor(client)) {
+ weston_log("unable to checkDescriptor for %p\n", client);
+ goto out_clean;
+ }
+ return 0;
+
+out_clean:
+ freerdp_peer_context_free(client);
+ freerdp_peer_free(client);
+ return 0;
+}
+
+static BOOL
+xf_peer_capabilities(freerdp_peer* client)
+{
+ return TRUE;
+}
+
+struct rdp_to_xkb_keyboard_layout {
+ UINT32 rdpLayoutCode;
+ const char *xkbLayout;
+ const char *xkbVariant;
+};
+
+/* table reversed from
+ https://github.com/awakecoding/FreeRDP/blob/master/libfreerdp/locale/xkb_layout_ids.c#L811 */
+static
+struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = {
+ {KBD_ARABIC_101, "ara", 0},
+ {KBD_BULGARIAN, 0, 0},
+ {KBD_CHINESE_TRADITIONAL_US, 0},
+ {KBD_CZECH, "cz", 0},
+ {KBD_CZECH_PROGRAMMERS, "cz", "bksl"},
+ {KBD_CZECH_QWERTY, "cz", "qwerty"},
+ {KBD_DANISH, "dk", 0},
+ {KBD_GERMAN, "de", 0},
+ {KBD_GERMAN_NEO, "de", "neo"},
+ {KBD_GERMAN_IBM, "de", "qwerty"},
+ {KBD_GREEK, "gr", 0},
+ {KBD_GREEK_220, "gr", "simple"},
+ {KBD_GREEK_319, "gr", "extended"},
+ {KBD_GREEK_POLYTONIC, "gr", "polytonic"},
+ {KBD_US, "us", 0},
+ {KBD_US_ENGLISH_TABLE_FOR_IBM_ARABIC_238_L, "ara", "buckwalter"},
+ {KBD_SPANISH, "es", 0},
+ {KBD_SPANISH_VARIATION, "es", "nodeadkeys"},
+ {KBD_FINNISH, "fi", 0},
+ {KBD_FRENCH, "fr", 0},
+ {KBD_HEBREW, "il", 0},
+ {KBD_HUNGARIAN, "hu", 0},
+ {KBD_HUNGARIAN_101_KEY, "hu", "standard"},
+ {KBD_ICELANDIC, "is", 0},
+ {KBD_ITALIAN, "it", 0},
+ {KBD_ITALIAN_142, "it", "nodeadkeys"},
+ {KBD_JAPANESE, "jp", 0},
+ {KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002, "jp", "kana"},
+ {KBD_KOREAN, "kr", 0},
+ {KBD_KOREAN_INPUT_SYSTEM_IME_2000, "kr", "kr104"},
+ {KBD_DUTCH, "nl", 0},
+ {KBD_NORWEGIAN, "no", 0},
+ {KBD_POLISH_PROGRAMMERS, "pl", 0},
+ {KBD_POLISH_214, "pl", "qwertz"},
+ {KBD_ROMANIAN, "ro", 0},
+ {KBD_RUSSIAN, "ru", 0},
+ {KBD_RUSSIAN_TYPEWRITER, "ru", "typewriter"},
+ {KBD_CROATIAN, "hr", 0},
+ {KBD_SLOVAK, "sk", 0},
+ {KBD_SLOVAK_QWERTY, "sk", "qwerty"},
+ {KBD_ALBANIAN, 0, 0},
+ {KBD_SWEDISH, "se", 0},
+ {KBD_THAI_KEDMANEE, "th", 0},
+ {KBD_THAI_KEDMANEE_NON_SHIFTLOCK, "th", "tis"},
+ {KBD_TURKISH_Q, "tr", 0},
+ {KBD_TURKISH_F, "tr", "f"},
+ {KBD_URDU, "in", "urd-phonetic3"},
+ {KBD_UKRAINIAN, "ua", 0},
+ {KBD_BELARUSIAN, "by", 0},
+ {KBD_SLOVENIAN, "si", 0},
+ {KBD_ESTONIAN, "ee", 0},
+ {KBD_LATVIAN, "lv", 0},
+ {KBD_LITHUANIAN_IBM, "lt", "ibm"},
+ {KBD_FARSI, "af", 0},
+ {KBD_VIETNAMESE, "vn", 0},
+ {KBD_ARMENIAN_EASTERN, "am", 0},
+ {KBD_AZERI_LATIN, 0, 0},
+ {KBD_FYRO_MACEDONIAN, "mk", 0},
+ {KBD_GEORGIAN, "ge", 0},
+ {KBD_FAEROESE, 0, 0},
+ {KBD_DEVANAGARI_INSCRIPT, 0, 0},
+ {KBD_MALTESE_47_KEY, 0, 0},
+ {KBD_NORWEGIAN_WITH_SAMI, "no", "smi"},
+ {KBD_KAZAKH, "kz", 0},
+ {KBD_KYRGYZ_CYRILLIC, "kg", "phonetic"},
+ {KBD_TATAR, "ru", "tt"},
+ {KBD_BENGALI, "bd", 0},
+ {KBD_BENGALI_INSCRIPT, "bd", "probhat"},
+ {KBD_PUNJABI, 0, 0},
+ {KBD_GUJARATI, "in", "guj"},
+ {KBD_TAMIL, "in", "tam"},
+ {KBD_TELUGU, "in", "tel"},
+ {KBD_KANNADA, "in", "kan"},
+ {KBD_MALAYALAM, "in", "mal"},
+ {KBD_HINDI_TRADITIONAL, "in", 0},
+ {KBD_MARATHI, 0, 0},
+ {KBD_MONGOLIAN_CYRILLIC, "mn", 0},
+ {KBD_UNITED_KINGDOM_EXTENDED, "gb", "intl"},
+ {KBD_SYRIAC, "syc", 0},
+ {KBD_SYRIAC_PHONETIC, "syc", "syc_phonetic"},
+ {KBD_NEPALI, "np", 0},
+ {KBD_PASHTO, "af", "ps"},
+ {KBD_DIVEHI_PHONETIC, 0, 0},
+ {KBD_LUXEMBOURGISH, 0, 0},
+ {KBD_MAORI, "mao", 0},
+ {KBD_CHINESE_SIMPLIFIED_US, 0, 0},
+ {KBD_SWISS_GERMAN, "ch", "de_nodeadkeys"},
+ {KBD_UNITED_KINGDOM, "gb", 0},
+ {KBD_LATIN_AMERICAN, "latam", 0},
+ {KBD_BELGIAN_FRENCH, "be", 0},
+ {KBD_BELGIAN_PERIOD, "be", "oss_sundeadkeys"},
+ {KBD_PORTUGUESE, "pt", 0},
+ {KBD_SERBIAN_LATIN, "rs", 0},
+ {KBD_AZERI_CYRILLIC, "az", "cyrillic"},
+ {KBD_SWEDISH_WITH_SAMI, "se", "smi"},
+ {KBD_UZBEK_CYRILLIC, "af", "uz"},
+ {KBD_INUKTITUT_LATIN, "ca", "ike"},
+ {KBD_CANADIAN_FRENCH_LEGACY, "ca", "fr-legacy"},
+ {KBD_SERBIAN_CYRILLIC, "rs", 0},
+ {KBD_CANADIAN_FRENCH, "ca", "fr-legacy"},
+ {KBD_SWISS_FRENCH, "ch", "fr"},
+ {KBD_BOSNIAN, "ba", 0},
+ {KBD_IRISH, 0, 0},
+ {KBD_BOSNIAN_CYRILLIC, "ba", "us"},
+ {KBD_UNITED_STATES_DVORAK, "us", "dvorak"},
+ {KBD_PORTUGUESE_BRAZILIAN_ABNT2, "br", "nativo"},
+ {KBD_CANADIAN_MULTILINGUAL_STANDARD, "ca", "multix"},
+ {KBD_GAELIC, "ie", "CloGaelach"},
+
+ {0x00000000, 0, 0},
+};
+
+/* taken from 2.2.7.1.6 Input Capability Set (TS_INPUT_CAPABILITYSET) */
+static char *rdp_keyboard_types[] = {
+ "", /* 0: unused */
+ "", /* 1: IBM PC/XT or compatible (83-key) keyboard */
+ "", /* 2: Olivetti "ICO" (102-key) keyboard */
+ "", /* 3: IBM PC/AT (84-key) or similar keyboard */
+ "pc102",/* 4: IBM enhanced (101- or 102-key) keyboard */
+ "", /* 5: Nokia 1050 and similar keyboards */
+ "", /* 6: Nokia 9140 and similar keyboards */
+ "" /* 7: Japanese keyboard */
+};
+
+static BOOL
+xf_peer_activate(freerdp_peer* client)
+{
+ RdpPeerContext *peerCtx;
+ struct rdp_backend *b;
+ struct rdp_output *output;
+ rdpSettings *settings;
+ rdpPointerUpdate *pointer;
+ struct rdp_peers_item *peersItem;
+ struct xkb_context *xkbContext;
+ struct xkb_rule_names xkbRuleNames;
+ struct xkb_keymap *keymap;
+ struct weston_output *weston_output;
+ int i;
+ pixman_box32_t box;
+ pixman_region32_t damage;
+ char seat_name[50];
+
+
+ peerCtx = (RdpPeerContext *)client->context;
+ b = peerCtx->rdpBackend;
+ peersItem = &peerCtx->item;
+ output = b->output;
+ settings = client->settings;
+
+ if (!settings->SurfaceCommandsEnabled) {
+ weston_log("client doesn't support required SurfaceCommands\n");
+ return FALSE;
+ }
+
+ if (output->base.width != (int)settings->DesktopWidth ||
+ output->base.height != (int)settings->DesktopHeight)
+ {
+ if (b->no_clients_resize) {
+ /* RDP peers don't dictate their resolution to weston */
+ if (!settings->DesktopResize) {
+ /* peer does not support desktop resize */
+ weston_log("%s: client doesn't support resizing, closing connection\n", __FUNCTION__);
+ return FALSE;
+ } else {
+ settings->DesktopWidth = output->base.width;
+ settings->DesktopHeight = output->base.height;
+ client->update->DesktopResize(client->context);
+ }
+ } else {
+ /* ask weston to adjust size */
+ struct weston_mode new_mode;
+ struct weston_mode *target_mode;
+ new_mode.width = (int)settings->DesktopWidth;
+ new_mode.height = (int)settings->DesktopHeight;
+ target_mode = ensure_matching_mode(&output->base, &new_mode);
+ if (!target_mode) {
+ weston_log("client mode not found\n");
+ return FALSE;
+ }
+ weston_output_mode_set_native(&output->base, target_mode, 1);
+ output->base.width = new_mode.width;
+ output->base.height = new_mode.height;
+ }
+ }
+
+ weston_output = &output->base;
+ RFX_RESET(peerCtx->rfx_context, weston_output->width, weston_output->height);
+ NSC_RESET(peerCtx->nsc_context, weston_output->width, weston_output->height);
+
+ if (peersItem->flags & RDP_PEER_ACTIVATED)
+ return TRUE;
+
+ /* when here it's the first reactivation, we need to setup a little more */
+ weston_log("kbd_layout:0x%x kbd_type:0x%x kbd_subType:0x%x kbd_functionKeys:0x%x\n",
+ settings->KeyboardLayout, settings->KeyboardType, settings->KeyboardSubType,
+ settings->KeyboardFunctionKey);
+
+ memset(&xkbRuleNames, 0, sizeof(xkbRuleNames));
+ if (settings->KeyboardType <= 7)
+ xkbRuleNames.model = rdp_keyboard_types[settings->KeyboardType];
+ for (i = 0; rdp_keyboards[i].rdpLayoutCode; i++) {
+ if (rdp_keyboards[i].rdpLayoutCode == settings->KeyboardLayout) {
+ xkbRuleNames.layout = rdp_keyboards[i].xkbLayout;
+ xkbRuleNames.variant = rdp_keyboards[i].xkbVariant;
+ weston_log("%s: matching layout=%s variant=%s\n", __FUNCTION__,
+ xkbRuleNames.layout, xkbRuleNames.variant);
+ break;
+ }
+ }
+
+ keymap = NULL;
+ if (xkbRuleNames.layout) {
+ xkbContext = xkb_context_new(0);
+ if (!xkbContext) {
+ weston_log("unable to create a xkb_context\n");
+ return FALSE;
+ }
+
+ keymap = xkb_keymap_new_from_names(xkbContext, &xkbRuleNames, 0);
+ }
+
+ if (settings->ClientHostname)
+ snprintf(seat_name, sizeof(seat_name), "RDP %s", settings->ClientHostname);
+ else
+ snprintf(seat_name, sizeof(seat_name), "RDP peer @%s", settings->ClientAddress);
+
+ peersItem->seat = zalloc(sizeof(*peersItem->seat));
+ if (!peersItem->seat) {
+ xkb_keymap_unref(keymap);
+ weston_log("unable to create a weston_seat\n");
+ return FALSE;
+ }
+
+ weston_seat_init(peersItem->seat, b->compositor, seat_name);
+ weston_seat_init_keyboard(peersItem->seat, keymap);
+ weston_seat_init_pointer(peersItem->seat);
+
+ peersItem->flags |= RDP_PEER_ACTIVATED;
+
+ /* disable pointer on the client side */
+ pointer = client->update->pointer;
+ pointer->pointer_system.type = SYSPTR_NULL;
+ pointer->PointerSystem(client->context, &pointer->pointer_system);
+
+ /* sends a full refresh */
+ box.x1 = 0;
+ box.y1 = 0;
+ box.x2 = output->base.width;
+ box.y2 = output->base.height;
+ pixman_region32_init_with_extents(&damage, &box);
+
+ rdp_peer_refresh_region(&damage, client);
+
+ pixman_region32_fini(&damage);
+
+ return TRUE;
+}
+
+static BOOL xf_peer_post_connect(freerdp_peer *client)
+{
+ return TRUE;
+}
+
+static FREERDP_CB_RET_TYPE
+xf_mouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y)
+{
+ RdpPeerContext *peerContext = (RdpPeerContext *)input->context;
+ struct rdp_output *output;
+ uint32_t button = 0;
+ bool need_frame = false;
+
+ if (flags & PTR_FLAGS_MOVE) {
+ output = peerContext->rdpBackend->output;
+ if (x < output->base.width && y < output->base.height) {
+ notify_motion_absolute(peerContext->item.seat, weston_compositor_get_time(),
+ x, y);
+ need_frame = true;
+ }
+ }
+
+ if (flags & PTR_FLAGS_BUTTON1)
+ button = BTN_LEFT;
+ else if (flags & PTR_FLAGS_BUTTON2)
+ button = BTN_RIGHT;
+ else if (flags & PTR_FLAGS_BUTTON3)
+ button = BTN_MIDDLE;
+
+ if (button) {
+ notify_button(peerContext->item.seat, weston_compositor_get_time(), button,
+ (flags & PTR_FLAGS_DOWN) ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED
+ );
+ need_frame = true;
+ }
+
+ if (flags & PTR_FLAGS_WHEEL) {
+ struct weston_pointer_axis_event weston_event;
+ double value;
+
+ /* DEFAULT_AXIS_STEP_DISTANCE is stolen from compositor-x11.c
+ * The RDP specs says the lower bits of flags contains the "the number of rotation
+ * units the mouse wheel was rotated".
+ *
+ * https://blogs.msdn.microsoft.com/oldnewthing/20130123-00/?p=5473 explains the 120 value
+ */
+ value = (flags & 0xff) / 120.0;
+ if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
+ value = -value;
+
+ weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
+ weston_event.value = DEFAULT_AXIS_STEP_DISTANCE * value;
+ weston_event.discrete = (int)value;
+ weston_event.has_discrete = true;
+
+ notify_axis(peerContext->item.seat, weston_compositor_get_time(),
+ &weston_event);
+ need_frame = true;
+ }
+
+ if (need_frame)
+ notify_pointer_frame(peerContext->item.seat);
+
+ FREERDP_CB_RETURN(TRUE);
+}
+
+static FREERDP_CB_RET_TYPE
+xf_extendedMouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y)
+{
+ RdpPeerContext *peerContext = (RdpPeerContext *)input->context;
+ struct rdp_output *output;
+
+ output = peerContext->rdpBackend->output;
+ if (x < output->base.width && y < output->base.height) {
+ notify_motion_absolute(peerContext->item.seat, weston_compositor_get_time(),
+ x, y);
+ }
+
+ FREERDP_CB_RETURN(TRUE);
+}
+
+
+static FREERDP_CB_RET_TYPE
+xf_input_synchronize_event(rdpInput *input, UINT32 flags)
+{
+ freerdp_peer *client = input->context->peer;
+ RdpPeerContext *peerCtx = (RdpPeerContext *)input->context;
+ struct rdp_output *output = peerCtx->rdpBackend->output;
+ pixman_box32_t box;
+ pixman_region32_t damage;
+
+ /* sends a full refresh */
+ box.x1 = 0;
+ box.y1 = 0;
+ box.x2 = output->base.width;
+ box.y2 = output->base.height;
+ pixman_region32_init_with_extents(&damage, &box);
+
+ rdp_peer_refresh_region(&damage, client);
+
+ pixman_region32_fini(&damage);
+ FREERDP_CB_RETURN(TRUE);
+}
+
+
+static FREERDP_CB_RET_TYPE
+xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code)
+{
+ uint32_t scan_code, vk_code, full_code;
+ enum wl_keyboard_key_state keyState;
+ RdpPeerContext *peerContext = (RdpPeerContext *)input->context;
+ int notify = 0;
+
+ if (!(peerContext->item.flags & RDP_PEER_ACTIVATED))
+ FREERDP_CB_RETURN(TRUE);
+
+ if (flags & KBD_FLAGS_DOWN) {
+ keyState = WL_KEYBOARD_KEY_STATE_PRESSED;
+ notify = 1;
+ } else if (flags & KBD_FLAGS_RELEASE) {
+ keyState = WL_KEYBOARD_KEY_STATE_RELEASED;
+ notify = 1;
+ }
+
+ if (notify) {
+ full_code = code;
+ if (flags & KBD_FLAGS_EXTENDED)
+ full_code |= KBD_FLAGS_EXTENDED;
+
+ vk_code = GetVirtualKeyCodeFromVirtualScanCode(full_code, 4);
+ if (flags & KBD_FLAGS_EXTENDED)
+ vk_code |= KBDEXT;
+
+ scan_code = GetKeycodeFromVirtualKeyCode(vk_code, KEYCODE_TYPE_EVDEV);
+
+ /*weston_log("code=%x ext=%d vk_code=%x scan_code=%x\n", code, (flags & KBD_FLAGS_EXTENDED) ? 1 : 0,
+ vk_code, scan_code);*/
+ notify_key(peerContext->item.seat, weston_compositor_get_time(),
+ scan_code - 8, keyState, STATE_UPDATE_AUTOMATIC);
+ }
+
+ FREERDP_CB_RETURN(TRUE);
+}
+
+static FREERDP_CB_RET_TYPE
+xf_input_unicode_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code)
+{
+ weston_log("Client sent a unicode keyboard event (flags:0x%X code:0x%X)\n", flags, code);
+ FREERDP_CB_RETURN(TRUE);
+}
+
+
+static FREERDP_CB_RET_TYPE
+xf_suppress_output(rdpContext *context, BYTE allow, RECTANGLE_16 *area)
+{
+ RdpPeerContext *peerContext = (RdpPeerContext *)context;
+
+ if (allow)
+ peerContext->item.flags |= RDP_PEER_OUTPUT_ENABLED;
+ else
+ peerContext->item.flags &= (~RDP_PEER_OUTPUT_ENABLED);
+
+ FREERDP_CB_RETURN(TRUE);
+}
+
+static int
+rdp_peer_init(freerdp_peer *client, struct rdp_backend *b)
+{
+ int rcount = 0;
+ void *rfds[MAX_FREERDP_FDS];
+ int i, fd;
+ struct wl_event_loop *loop;
+ rdpSettings *settings;
+ rdpInput *input;
+ RdpPeerContext *peerCtx;
+
+ client->ContextSize = sizeof(RdpPeerContext);
+ client->ContextNew = (psPeerContextNew)rdp_peer_context_new;
+ client->ContextFree = (psPeerContextFree)rdp_peer_context_free;
+ freerdp_peer_context_new(client);
+
+ peerCtx = (RdpPeerContext *) client->context;
+ peerCtx->rdpBackend = b;
+
+ settings = client->settings;
+ /* configure security settings */
+ if (b->rdp_key)
+ settings->RdpKeyFile = strdup(b->rdp_key);
+ if (b->tls_enabled) {
+ settings->CertificateFile = strdup(b->server_cert);
+ settings->PrivateKeyFile = strdup(b->server_key);
+ } else {
+ settings->TlsSecurity = FALSE;
+ }
+ settings->NlaSecurity = FALSE;
+
+ if (!client->Initialize(client)) {
+ weston_log("peer initialization failed\n");
+ goto error_initialize;
+ }
+
+ settings->OsMajorType = OSMAJORTYPE_UNIX;
+ settings->OsMinorType = OSMINORTYPE_PSEUDO_XSERVER;
+ settings->ColorDepth = 32;
+ settings->RefreshRect = TRUE;
+ settings->RemoteFxCodec = TRUE;
+ settings->NSCodec = TRUE;
+ settings->FrameMarkerCommandEnabled = TRUE;
+ settings->SurfaceFrameMarkerEnabled = TRUE;
+
+ client->Capabilities = xf_peer_capabilities;
+ client->PostConnect = xf_peer_post_connect;
+ client->Activate = xf_peer_activate;
+
+ client->update->SuppressOutput = xf_suppress_output;
+
+ input = client->input;
+ input->SynchronizeEvent = xf_input_synchronize_event;
+ input->MouseEvent = xf_mouseEvent;
+ input->ExtendedMouseEvent = xf_extendedMouseEvent;
+ input->KeyboardEvent = xf_input_keyboard_event;
+ input->UnicodeKeyboardEvent = xf_input_unicode_keyboard_event;
+
+ if (!client->GetFileDescriptor(client, rfds, &rcount)) {
+ weston_log("unable to retrieve client fds\n");
+ goto error_initialize;
+ }
+
+ loop = wl_display_get_event_loop(b->compositor->wl_display);
+ for (i = 0; i < rcount; i++) {
+ fd = (int)(long)(rfds[i]);
+
+ peerCtx->events[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
+ rdp_client_activity, client);
+ }
+ for ( ; i < MAX_FREERDP_FDS; i++)
+ peerCtx->events[i] = 0;
+
+ wl_list_insert(&b->output->peers, &peerCtx->item.link);
+ return 0;
+
+error_initialize:
+ client->Close(client);
+ return -1;
+}
+
+
+static FREERDP_CB_RET_TYPE
+rdp_incoming_peer(freerdp_listener *instance, freerdp_peer *client)
+{
+ struct rdp_backend *b = (struct rdp_backend *)instance->param4;
+ if (rdp_peer_init(client, b) < 0) {
+ weston_log("error when treating incoming peer\n");
+ FREERDP_CB_RETURN(FALSE);
+ }
+
+ FREERDP_CB_RETURN(TRUE);
+}
+
+static struct rdp_backend *
+rdp_backend_create(struct weston_compositor *compositor,
+ struct weston_rdp_backend_config *config)
+{
+ struct rdp_backend *b;
+ char *fd_str;
+ int fd;
+
+ b = zalloc(sizeof *b);
+ if (b == NULL)
+ return NULL;
+
+ b->compositor = compositor;
+ b->base.destroy = rdp_destroy;
+ b->base.restore = rdp_restore;
+ b->rdp_key = config->rdp_key ? strdup(config->rdp_key) : NULL;
+ b->no_clients_resize = config->no_clients_resize;
+
+ /* activate TLS only if certificate/key are available */
+ if (config->server_cert && config->server_key) {
+ weston_log("TLS support activated\n");
+ b->server_cert = strdup(config->server_cert);
+ b->server_key = strdup(config->server_key);
+ if (!b->server_cert || !b->server_key)
+ goto err_free_strings;
+ b->tls_enabled = 1;
+ }
+
+ if (weston_compositor_set_presentation_clock_software(compositor) < 0)
+ goto err_compositor;
+
+ if (pixman_renderer_init(compositor) < 0)
+ goto err_compositor;
+
+ if (rdp_backend_create_output(b, config->width, config->height) < 0)
+ goto err_compositor;
+
+ compositor->capabilities |= WESTON_CAP_ARBITRARY_MODES;
+
+ if (!config->env_socket) {
+ b->listener = freerdp_listener_new();
+ b->listener->PeerAccepted = rdp_incoming_peer;
+ b->listener->param4 = b;
+ if (!b->listener->Open(b->listener, config->bind_address, config->port)) {
+ weston_log("unable to bind rdp socket\n");
+ goto err_listener;
+ }
+
+ if (rdp_implant_listener(b, b->listener) < 0)
+ goto err_compositor;
+ } else {
+ /* get the socket from RDP_FD var */
+ fd_str = getenv("RDP_FD");
+ if (!fd_str) {
+ weston_log("RDP_FD env variable not set\n");
+ goto err_output;
+ }
+
+ fd = strtoul(fd_str, NULL, 10);
+ if (rdp_peer_init(freerdp_peer_new(fd), b))
+ goto err_output;
+ }
+
+ compositor->backend = &b->base;
+ return b;
+
+err_listener:
+ freerdp_listener_free(b->listener);
+err_output:
+ weston_output_destroy(&b->output->base);
+err_compositor:
+ weston_compositor_shutdown(compositor);
+err_free_strings:
+ free(b->rdp_key);
+ free(b->server_cert);
+ free(b->server_key);
+ free(b);
+ return NULL;
+}
+
+static void
+config_init_to_defaults(struct weston_rdp_backend_config *config)
+{
+ config->width = 640;
+ config->height = 480;
+ config->bind_address = NULL;
+ config->port = 3389;
+ config->rdp_key = NULL;
+ config->server_cert = NULL;
+ config->server_key = NULL;
+ config->env_socket = 0;
+ config->no_clients_resize = 0;
+}
+
+WL_EXPORT int
+backend_init(struct weston_compositor *compositor,
+ struct weston_backend_config *config_base)
+{
+ struct rdp_backend *b;
+ struct weston_rdp_backend_config config = {{ 0, }};
+ int major, minor, revision;
+
+ freerdp_get_version(&major, &minor, &revision);
+ weston_log("using FreeRDP version %d.%d.%d\n", major, minor, revision);
+
+ if (config_base == NULL ||
+ config_base->struct_version != WESTON_RDP_BACKEND_CONFIG_VERSION ||
+ config_base->struct_size > sizeof(struct weston_rdp_backend_config)) {
+ weston_log("RDP backend config structure is invalid\n");
+ return -1;
+ }
+
+ config_init_to_defaults(&config);
+ memcpy(&config, config_base, config_base->struct_size);
+
+ if (!config.rdp_key && (!config.server_cert || !config.server_key)) {
+ weston_log("the RDP compositor requires keys and an optional certificate for RDP or TLS security ("
+ "--rdp4-key or --rdp-tls-cert/--rdp-tls-key)\n");
+ return -1;
+ }
+
+ b = rdp_backend_create(compositor, &config);
+ if (b == NULL)
+ return -1;
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 2016 Benoit Gschwind
+ *
+ * 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.
+ */
+
+#ifndef WESTON_COMPOSITOR_RDP_H
+#define WESTON_COMPOSITOR_RDP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "compositor.h"
+
+#define WESTON_RDP_BACKEND_CONFIG_VERSION 1
+
+struct weston_rdp_backend_config {
+ struct weston_backend_config base;
+ int width;
+ int height;
+ char *bind_address;
+ int port;
+ char *rdp_key;
+ char *server_cert;
+ char *server_key;
+ int env_socket;
+ int no_clients_resize;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WESTON_COMPOSITOR_RDP_H */
--- /dev/null
+/*
+ * Copyright © 2010-2011 Benjamin Franzke
+ * Copyright © 2013 Jason Ekstrand
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <linux/input.h>
+
+#include <wayland-client.h>
+#include <wayland-egl.h>
+#include <wayland-cursor.h>
+
+#include "compositor.h"
+#include "compositor-wayland.h"
+#include "gl-renderer.h"
+#include "pixman-renderer.h"
+#include "shared/helpers.h"
+#include "shared/image-loader.h"
+#include "shared/os-compatibility.h"
+#include "shared/cairo-util.h"
+#include "fullscreen-shell-unstable-v1-client-protocol.h"
+#include "presentation-time-server-protocol.h"
+#include "linux-dmabuf.h"
+
+#define WINDOW_TITLE "Weston Compositor"
+
+struct wayland_backend {
+ struct weston_backend base;
+ struct weston_compositor *compositor;
+
+ struct {
+ struct wl_display *wl_display;
+ struct wl_registry *registry;
+ struct wl_compositor *compositor;
+ struct wl_shell *shell;
+ struct zwp_fullscreen_shell_v1 *fshell;
+ struct wl_shm *shm;
+
+ struct wl_list output_list;
+
+ struct wl_event_source *wl_source;
+ uint32_t event_mask;
+ } parent;
+
+ int use_pixman;
+ int sprawl_across_outputs;
+
+ struct theme *theme;
+ cairo_device_t *frame_device;
+ struct wl_cursor_theme *cursor_theme;
+ struct wl_cursor *cursor;
+
+ struct wl_list input_list;
+};
+
+struct wayland_output {
+ struct weston_output base;
+
+ struct {
+ int draw_initial_frame;
+ struct wl_surface *surface;
+
+ struct wl_output *output;
+ uint32_t global_id;
+
+ struct wl_shell_surface *shell_surface;
+ int configure_width, configure_height;
+ } parent;
+
+ int keyboard_count;
+
+ char *name;
+ struct frame *frame;
+
+ struct {
+ struct wl_egl_window *egl_window;
+ struct {
+ cairo_surface_t *top;
+ cairo_surface_t *left;
+ cairo_surface_t *right;
+ cairo_surface_t *bottom;
+ } border;
+ } gl;
+
+ struct {
+ struct wl_list buffers;
+ struct wl_list free_buffers;
+ } shm;
+
+ struct weston_mode mode;
+ uint32_t scale;
+};
+
+struct wayland_parent_output {
+ struct wayland_output *output;
+ struct wl_list link;
+
+ struct wl_output *global;
+ uint32_t id;
+
+ struct {
+ char *make;
+ char *model;
+ int32_t width, height;
+ uint32_t subpixel;
+ } physical;
+
+ int32_t x, y;
+ uint32_t transform;
+ uint32_t scale;
+
+ struct wl_list mode_list;
+ struct weston_mode *preferred_mode;
+ struct weston_mode *current_mode;
+};
+
+struct wayland_shm_buffer {
+ struct wayland_output *output;
+ struct wl_list link;
+ struct wl_list free_link;
+
+ struct wl_buffer *buffer;
+ void *data;
+ size_t size;
+ pixman_region32_t damage;
+ int frame_damaged;
+
+ pixman_image_t *pm_image;
+ cairo_surface_t *c_surface;
+};
+
+struct wayland_input {
+ struct weston_seat base;
+ struct wayland_backend *backend;
+ struct wl_list link;
+
+ struct {
+ struct wl_seat *seat;
+ struct wl_pointer *pointer;
+ struct wl_keyboard *keyboard;
+ struct wl_touch *touch;
+
+ struct {
+ struct wl_surface *surface;
+ int32_t hx, hy;
+ } cursor;
+ } parent;
+
+ enum weston_key_state_update keyboard_state_update;
+ uint32_t key_serial;
+ uint32_t enter_serial;
+ uint32_t touch_points;
+ bool touch_active;
+ bool has_focus;
+ int seat_version;
+
+ struct wayland_output *output;
+ struct wayland_output *touch_focus;
+ struct wayland_output *keyboard_focus;
+
+ struct weston_pointer_axis_event vert, horiz;
+};
+
+struct gl_renderer_interface *gl_renderer;
+
+static void
+wayland_shm_buffer_destroy(struct wayland_shm_buffer *buffer)
+{
+ cairo_surface_destroy(buffer->c_surface);
+ pixman_image_unref(buffer->pm_image);
+
+ wl_buffer_destroy(buffer->buffer);
+ munmap(buffer->data, buffer->size);
+
+ pixman_region32_fini(&buffer->damage);
+
+ wl_list_remove(&buffer->link);
+ wl_list_remove(&buffer->free_link);
+ free(buffer);
+}
+
+static void
+buffer_release(void *data, struct wl_buffer *buffer)
+{
+ struct wayland_shm_buffer *sb = data;
+
+ if (sb->output) {
+ wl_list_insert(&sb->output->shm.free_buffers, &sb->free_link);
+ } else {
+ wayland_shm_buffer_destroy(sb);
+ }
+}
+
+static const struct wl_buffer_listener buffer_listener = {
+ buffer_release
+};
+
+static struct wayland_shm_buffer *
+wayland_output_get_shm_buffer(struct wayland_output *output)
+{
+ struct wayland_backend *b =
+ (struct wayland_backend *) output->base.compositor->backend;
+ struct wl_shm *shm = b->parent.shm;
+ struct wayland_shm_buffer *sb;
+
+ struct wl_shm_pool *pool;
+ int width, height, stride;
+ int32_t fx, fy;
+ int fd;
+ unsigned char *data;
+
+ if (!wl_list_empty(&output->shm.free_buffers)) {
+ sb = container_of(output->shm.free_buffers.next,
+ struct wayland_shm_buffer, free_link);
+ wl_list_remove(&sb->free_link);
+ wl_list_init(&sb->free_link);
+
+ return sb;
+ }
+
+ if (output->frame) {
+ width = frame_width(output->frame);
+ height = frame_height(output->frame);
+ } else {
+ width = output->base.current_mode->width;
+ height = output->base.current_mode->height;
+ }
+
+ stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
+
+ fd = os_create_anonymous_file(height * stride);
+ if (fd < 0) {
+ weston_log("could not create an anonymous file buffer: %m\n");
+ return NULL;
+ }
+
+ data = mmap(NULL, height * stride, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (data == MAP_FAILED) {
+ weston_log("could not mmap %d memory for data: %m\n", height * stride);
+ close(fd);
+ return NULL;
+ }
+
+ sb = zalloc(sizeof *sb);
+ if (sb == NULL) {
+ weston_log("could not zalloc %zu memory for sb: %m\n", sizeof *sb);
+ close(fd);
+ free(data);
+ return NULL;
+ }
+
+ sb->output = output;
+ wl_list_init(&sb->free_link);
+ wl_list_insert(&output->shm.buffers, &sb->link);
+
+ pixman_region32_init_rect(&sb->damage, 0, 0,
+ output->base.width, output->base.height);
+ sb->frame_damaged = 1;
+
+ sb->data = data;
+ sb->size = height * stride;
+
+ pool = wl_shm_create_pool(shm, fd, sb->size);
+
+ sb->buffer = wl_shm_pool_create_buffer(pool, 0,
+ width, height,
+ stride,
+ WL_SHM_FORMAT_ARGB8888);
+ wl_buffer_add_listener(sb->buffer, &buffer_listener, sb);
+ wl_shm_pool_destroy(pool);
+ close(fd);
+
+ memset(data, 0, sb->size);
+
+ sb->c_surface =
+ cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32,
+ width, height, stride);
+
+ fx = 0;
+ fy = 0;
+ if (output->frame)
+ frame_interior(output->frame, &fx, &fy, 0, 0);
+ sb->pm_image =
+ pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height,
+ (uint32_t *)(data + fy * stride) + fx,
+ stride);
+
+ return sb;
+}
+
+static void
+frame_done(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct weston_output *output = data;
+ struct timespec ts;
+
+ wl_callback_destroy(callback);
+
+ /* XXX: use the presentation extension for proper timings */
+
+ /*
+ * This is the fallback case, where Presentation extension is not
+ * available from the parent compositor. We do not know the base for
+ * 'time', so we cannot feed it to finish_frame(). Do the only thing
+ * we can, and pretend finish_frame time is when we process this
+ * event.
+ */
+ weston_compositor_read_presentation_clock(output->compositor, &ts);
+ weston_output_finish_frame(output, &ts, 0);
+}
+
+static const struct wl_callback_listener frame_listener = {
+ frame_done
+};
+
+static void
+draw_initial_frame(struct wayland_output *output)
+{
+ struct wayland_shm_buffer *sb;
+
+ sb = wayland_output_get_shm_buffer(output);
+
+ /* If we are rendering with GL, then orphan it so that it gets
+ * destroyed immediately */
+ if (output->gl.egl_window)
+ sb->output = NULL;
+
+ wl_surface_attach(output->parent.surface, sb->buffer, 0, 0);
+ wl_surface_damage(output->parent.surface, 0, 0,
+ output->base.current_mode->width,
+ output->base.current_mode->height);
+}
+
+static void
+wayland_output_update_gl_border(struct wayland_output *output)
+{
+ int32_t ix, iy, iwidth, iheight, fwidth, fheight;
+ cairo_t *cr;
+
+ if (!output->frame)
+ return;
+ if (!(frame_status(output->frame) & FRAME_STATUS_REPAINT))
+ return;
+
+ fwidth = frame_width(output->frame);
+ fheight = frame_height(output->frame);
+ frame_interior(output->frame, &ix, &iy, &iwidth, &iheight);
+
+ if (!output->gl.border.top)
+ output->gl.border.top =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ fwidth, iy);
+ cr = cairo_create(output->gl.border.top);
+ frame_repaint(output->frame, cr);
+ cairo_destroy(cr);
+ gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_TOP,
+ fwidth, iy,
+ cairo_image_surface_get_stride(output->gl.border.top) / 4,
+ cairo_image_surface_get_data(output->gl.border.top));
+
+
+ if (!output->gl.border.left)
+ output->gl.border.left =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ ix, 1);
+ cr = cairo_create(output->gl.border.left);
+ cairo_translate(cr, 0, -iy);
+ frame_repaint(output->frame, cr);
+ cairo_destroy(cr);
+ gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_LEFT,
+ ix, 1,
+ cairo_image_surface_get_stride(output->gl.border.left) / 4,
+ cairo_image_surface_get_data(output->gl.border.left));
+
+
+ if (!output->gl.border.right)
+ output->gl.border.right =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ fwidth - (ix + iwidth), 1);
+ cr = cairo_create(output->gl.border.right);
+ cairo_translate(cr, -(iwidth + ix), -iy);
+ frame_repaint(output->frame, cr);
+ cairo_destroy(cr);
+ gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_RIGHT,
+ fwidth - (ix + iwidth), 1,
+ cairo_image_surface_get_stride(output->gl.border.right) / 4,
+ cairo_image_surface_get_data(output->gl.border.right));
+
+
+ if (!output->gl.border.bottom)
+ output->gl.border.bottom =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ fwidth, fheight - (iy + iheight));
+ cr = cairo_create(output->gl.border.bottom);
+ cairo_translate(cr, 0, -(iy + iheight));
+ frame_repaint(output->frame, cr);
+ cairo_destroy(cr);
+ gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_BOTTOM,
+ fwidth, fheight - (iy + iheight),
+ cairo_image_surface_get_stride(output->gl.border.bottom) / 4,
+ cairo_image_surface_get_data(output->gl.border.bottom));
+}
+
+static void
+wayland_output_start_repaint_loop(struct weston_output *output_base)
+{
+ struct wayland_output *output = (struct wayland_output *) output_base;
+ struct wayland_backend *wb =
+ (struct wayland_backend *)output->base.compositor->backend;
+ struct wl_callback *callback;
+
+ /* If this is the initial frame, we need to attach a buffer so that
+ * the compositor can map the surface and include it in its render
+ * loop. If the surface doesn't end up in the render loop, the frame
+ * callback won't be invoked. The buffer is transparent and of the
+ * same size as the future real output buffer. */
+ if (output->parent.draw_initial_frame) {
+ output->parent.draw_initial_frame = 0;
+
+ draw_initial_frame(output);
+ }
+
+ callback = wl_surface_frame(output->parent.surface);
+ wl_callback_add_listener(callback, &frame_listener, output);
+ wl_surface_commit(output->parent.surface);
+ wl_display_flush(wb->parent.wl_display);
+}
+
+static int
+wayland_output_repaint_gl(struct weston_output *output_base,
+ pixman_region32_t *damage)
+{
+ struct wayland_output *output = (struct wayland_output *) output_base;
+ struct weston_compositor *ec = output->base.compositor;
+ struct wl_callback *callback;
+
+ callback = wl_surface_frame(output->parent.surface);
+ wl_callback_add_listener(callback, &frame_listener, output);
+
+ wayland_output_update_gl_border(output);
+
+ ec->renderer->repaint_output(&output->base, damage);
+
+ pixman_region32_subtract(&ec->primary_plane.damage,
+ &ec->primary_plane.damage, damage);
+ return 0;
+}
+
+static void
+wayland_output_update_shm_border(struct wayland_shm_buffer *buffer)
+{
+ int32_t ix, iy, iwidth, iheight, fwidth, fheight;
+ cairo_t *cr;
+
+ if (!buffer->output->frame || !buffer->frame_damaged)
+ return;
+
+ cr = cairo_create(buffer->c_surface);
+
+ frame_interior(buffer->output->frame, &ix, &iy, &iwidth, &iheight);
+ fwidth = frame_width(buffer->output->frame);
+ fheight = frame_height(buffer->output->frame);
+
+ /* Set the clip so we don't unnecisaraly damage the surface */
+ cairo_move_to(cr, ix, iy);
+ cairo_rel_line_to(cr, iwidth, 0);
+ cairo_rel_line_to(cr, 0, iheight);
+ cairo_rel_line_to(cr, -iwidth, 0);
+ cairo_line_to(cr, ix, iy);
+ cairo_line_to(cr, 0, iy);
+ cairo_line_to(cr, 0, fheight);
+ cairo_line_to(cr, fwidth, fheight);
+ cairo_line_to(cr, fwidth, 0);
+ cairo_line_to(cr, 0, 0);
+ cairo_line_to(cr, 0, iy);
+ cairo_close_path(cr);
+ cairo_clip(cr);
+
+ /* Draw using a pattern so that the final result gets clipped */
+ cairo_push_group(cr);
+ frame_repaint(buffer->output->frame, cr);
+ cairo_pop_group_to_source(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+}
+
+static void
+wayland_shm_buffer_attach(struct wayland_shm_buffer *sb)
+{
+ pixman_region32_t damage;
+ pixman_box32_t *rects;
+ int32_t ix, iy, iwidth, iheight, fwidth, fheight;
+ int i, n;
+
+ pixman_region32_init(&damage);
+ weston_transformed_region(sb->output->base.width,
+ sb->output->base.height,
+ sb->output->base.transform,
+ sb->output->base.current_scale,
+ &sb->damage, &damage);
+
+ if (sb->output->frame) {
+ frame_interior(sb->output->frame, &ix, &iy, &iwidth, &iheight);
+ fwidth = frame_width(sb->output->frame);
+ fheight = frame_height(sb->output->frame);
+
+ pixman_region32_translate(&damage, ix, iy);
+
+ if (sb->frame_damaged) {
+ pixman_region32_union_rect(&damage, &damage,
+ 0, 0, fwidth, iy);
+ pixman_region32_union_rect(&damage, &damage,
+ 0, iy, ix, iheight);
+ pixman_region32_union_rect(&damage, &damage,
+ ix + iwidth, iy,
+ fwidth - (ix + iwidth), iheight);
+ pixman_region32_union_rect(&damage, &damage,
+ 0, iy + iheight,
+ fwidth, fheight - (iy + iheight));
+ }
+ }
+
+ rects = pixman_region32_rectangles(&damage, &n);
+ wl_surface_attach(sb->output->parent.surface, sb->buffer, 0, 0);
+ for (i = 0; i < n; ++i)
+ wl_surface_damage(sb->output->parent.surface, rects[i].x1,
+ rects[i].y1, rects[i].x2 - rects[i].x1,
+ rects[i].y2 - rects[i].y1);
+
+ if (sb->output->frame)
+ pixman_region32_fini(&damage);
+}
+
+static int
+wayland_output_repaint_pixman(struct weston_output *output_base,
+ pixman_region32_t *damage)
+{
+ struct wayland_output *output = (struct wayland_output *) output_base;
+ struct wayland_backend *b =
+ (struct wayland_backend *)output->base.compositor->backend;
+ struct wl_callback *callback;
+ struct wayland_shm_buffer *sb;
+
+ if (output->frame) {
+ if (frame_status(output->frame) & FRAME_STATUS_REPAINT)
+ wl_list_for_each(sb, &output->shm.buffers, link)
+ sb->frame_damaged = 1;
+ }
+
+ wl_list_for_each(sb, &output->shm.buffers, link)
+ pixman_region32_union(&sb->damage, &sb->damage, damage);
+
+ sb = wayland_output_get_shm_buffer(output);
+
+ wayland_output_update_shm_border(sb);
+ pixman_renderer_output_set_buffer(output_base, sb->pm_image);
+ b->compositor->renderer->repaint_output(output_base, &sb->damage);
+
+ wayland_shm_buffer_attach(sb);
+
+ callback = wl_surface_frame(output->parent.surface);
+ wl_callback_add_listener(callback, &frame_listener, output);
+ wl_surface_commit(output->parent.surface);
+ wl_display_flush(b->parent.wl_display);
+
+ pixman_region32_fini(&sb->damage);
+ pixman_region32_init(&sb->damage);
+ sb->frame_damaged = 0;
+
+ pixman_region32_subtract(&b->compositor->primary_plane.damage,
+ &b->compositor->primary_plane.damage, damage);
+ return 0;
+}
+
+static void
+wayland_output_destroy(struct weston_output *output_base)
+{
+ struct wayland_output *output = (struct wayland_output *) output_base;
+ struct wayland_backend *b =
+ (struct wayland_backend *) output->base.compositor->backend;
+
+ if (b->use_pixman) {
+ pixman_renderer_output_destroy(output_base);
+ } else {
+ gl_renderer->output_destroy(output_base);
+ }
+
+ wl_egl_window_destroy(output->gl.egl_window);
+ wl_surface_destroy(output->parent.surface);
+ if (output->parent.shell_surface)
+ wl_shell_surface_destroy(output->parent.shell_surface);
+
+ if (output->frame)
+ frame_destroy(output->frame);
+
+ cairo_surface_destroy(output->gl.border.top);
+ cairo_surface_destroy(output->gl.border.left);
+ cairo_surface_destroy(output->gl.border.right);
+ cairo_surface_destroy(output->gl.border.bottom);
+
+ weston_output_destroy(&output->base);
+ free(output);
+
+ return;
+}
+
+static const struct wl_shell_surface_listener shell_surface_listener;
+
+static int
+wayland_output_init_gl_renderer(struct wayland_output *output)
+{
+ int32_t fwidth = 0, fheight = 0;
+
+ if (output->frame) {
+ fwidth = frame_width(output->frame);
+ fheight = frame_height(output->frame);
+ } else {
+ fwidth = output->base.current_mode->width;
+ fheight = output->base.current_mode->height;
+ }
+
+ output->gl.egl_window =
+ wl_egl_window_create(output->parent.surface,
+ fwidth, fheight);
+ if (!output->gl.egl_window) {
+ weston_log("failure to create wl_egl_window\n");
+ return -1;
+ }
+
+ if (gl_renderer->output_create(&output->base,
+ output->gl.egl_window,
+ output->gl.egl_window,
+ gl_renderer->alpha_attribs,
+ NULL,
+ 0) < 0)
+ goto cleanup_window;
+
+ return 0;
+
+cleanup_window:
+ wl_egl_window_destroy(output->gl.egl_window);
+ return -1;
+}
+
+static int
+wayland_output_init_pixman_renderer(struct wayland_output *output)
+{
+ return pixman_renderer_output_create(&output->base);
+}
+
+static void
+wayland_output_resize_surface(struct wayland_output *output)
+{
+ struct wayland_backend *b =
+ (struct wayland_backend *)output->base.compositor->backend;
+ struct wayland_shm_buffer *buffer, *next;
+ int32_t ix, iy, iwidth, iheight;
+ int32_t width, height;
+ struct wl_region *region;
+
+ width = output->base.current_mode->width;
+ height = output->base.current_mode->height;
+
+ if (output->frame) {
+ frame_resize_inside(output->frame, width, height);
+
+ frame_input_rect(output->frame, &ix, &iy, &iwidth, &iheight);
+ region = wl_compositor_create_region(b->parent.compositor);
+ wl_region_add(region, ix, iy, iwidth, iheight);
+ wl_surface_set_input_region(output->parent.surface, region);
+ wl_region_destroy(region);
+
+ frame_opaque_rect(output->frame, &ix, &iy, &iwidth, &iheight);
+ region = wl_compositor_create_region(b->parent.compositor);
+ wl_region_add(region, ix, iy, iwidth, iheight);
+ wl_surface_set_opaque_region(output->parent.surface, region);
+ wl_region_destroy(region);
+
+ width = frame_width(output->frame);
+ height = frame_height(output->frame);
+ } else {
+ region = wl_compositor_create_region(b->parent.compositor);
+ wl_region_add(region, 0, 0, width, height);
+ wl_surface_set_input_region(output->parent.surface, region);
+ wl_region_destroy(region);
+
+ region = wl_compositor_create_region(b->parent.compositor);
+ wl_region_add(region, 0, 0, width, height);
+ wl_surface_set_opaque_region(output->parent.surface, region);
+ wl_region_destroy(region);
+ }
+
+ if (output->gl.egl_window) {
+ wl_egl_window_resize(output->gl.egl_window,
+ width, height, 0, 0);
+
+ /* These will need to be re-created due to the resize */
+ gl_renderer->output_set_border(&output->base,
+ GL_RENDERER_BORDER_TOP,
+ 0, 0, 0, NULL);
+ cairo_surface_destroy(output->gl.border.top);
+ output->gl.border.top = NULL;
+ gl_renderer->output_set_border(&output->base,
+ GL_RENDERER_BORDER_LEFT,
+ 0, 0, 0, NULL);
+ cairo_surface_destroy(output->gl.border.left);
+ output->gl.border.left = NULL;
+ gl_renderer->output_set_border(&output->base,
+ GL_RENDERER_BORDER_RIGHT,
+ 0, 0, 0, NULL);
+ cairo_surface_destroy(output->gl.border.right);
+ output->gl.border.right = NULL;
+ gl_renderer->output_set_border(&output->base,
+ GL_RENDERER_BORDER_BOTTOM,
+ 0, 0, 0, NULL);
+ cairo_surface_destroy(output->gl.border.bottom);
+ output->gl.border.bottom = NULL;
+ }
+
+ /* Throw away any remaining SHM buffers */
+ wl_list_for_each_safe(buffer, next, &output->shm.free_buffers, free_link)
+ wayland_shm_buffer_destroy(buffer);
+ /* These will get thrown away when they get released */
+ wl_list_for_each(buffer, &output->shm.buffers, link)
+ buffer->output = NULL;
+}
+
+static int
+wayland_output_set_windowed(struct wayland_output *output)
+{
+ struct wayland_backend *b =
+ (struct wayland_backend *)output->base.compositor->backend;
+ int tlen;
+ char *title;
+
+ if (output->frame)
+ return 0;
+
+ if (output->name) {
+ tlen = strlen(output->name) + strlen(WINDOW_TITLE " - ");
+ title = malloc(tlen + 1);
+ if (!title)
+ return -1;
+
+ snprintf(title, tlen + 1, WINDOW_TITLE " - %s", output->name);
+ } else {
+ title = strdup(WINDOW_TITLE);
+ }
+
+ if (!b->theme) {
+ b->theme = theme_create();
+ if (!b->theme) {
+ free(title);
+ return -1;
+ }
+ }
+ output->frame = frame_create(b->theme, 100, 100,
+ FRAME_BUTTON_CLOSE, title);
+ free(title);
+ if (!output->frame)
+ return -1;
+
+ if (output->keyboard_count)
+ frame_set_flag(output->frame, FRAME_FLAG_ACTIVE);
+
+ wayland_output_resize_surface(output);
+
+ wl_shell_surface_set_toplevel(output->parent.shell_surface);
+
+ return 0;
+}
+
+static void
+wayland_output_set_fullscreen(struct wayland_output *output,
+ enum wl_shell_surface_fullscreen_method method,
+ uint32_t framerate, struct wl_output *target)
+{
+ struct wayland_backend *b =
+ (struct wayland_backend *)output->base.compositor->backend;
+
+ if (output->frame) {
+ frame_destroy(output->frame);
+ output->frame = NULL;
+ }
+
+ wayland_output_resize_surface(output);
+
+ if (output->parent.shell_surface) {
+ wl_shell_surface_set_fullscreen(output->parent.shell_surface,
+ method, framerate, target);
+ } else if (b->parent.fshell) {
+ zwp_fullscreen_shell_v1_present_surface(b->parent.fshell,
+ output->parent.surface,
+ method, target);
+ }
+}
+
+static struct weston_mode *
+wayland_output_choose_mode(struct wayland_output *output,
+ struct weston_mode *ref_mode)
+{
+ struct weston_mode *mode;
+
+ /* First look for an exact match */
+ wl_list_for_each(mode, &output->base.mode_list, link)
+ if (mode->width == ref_mode->width &&
+ mode->height == ref_mode->height &&
+ mode->refresh == ref_mode->refresh)
+ return mode;
+
+ /* If we can't find an exact match, ignore refresh and try again */
+ wl_list_for_each(mode, &output->base.mode_list, link)
+ if (mode->width == ref_mode->width &&
+ mode->height == ref_mode->height)
+ return mode;
+
+ /* Yeah, we failed */
+ return NULL;
+}
+
+enum mode_status {
+ MODE_STATUS_UNKNOWN,
+ MODE_STATUS_SUCCESS,
+ MODE_STATUS_FAIL,
+ MODE_STATUS_CANCEL,
+};
+
+static void
+mode_feedback_successful(void *data,
+ struct zwp_fullscreen_shell_mode_feedback_v1 *fb)
+{
+ enum mode_status *value = data;
+
+ printf("Mode switch successful\n");
+
+ *value = MODE_STATUS_SUCCESS;
+}
+
+static void
+mode_feedback_failed(void *data, struct zwp_fullscreen_shell_mode_feedback_v1 *fb)
+{
+ enum mode_status *value = data;
+
+ printf("Mode switch failed\n");
+
+ *value = MODE_STATUS_FAIL;
+}
+
+static void
+mode_feedback_cancelled(void *data, struct zwp_fullscreen_shell_mode_feedback_v1 *fb)
+{
+ enum mode_status *value = data;
+
+ printf("Mode switch cancelled\n");
+
+ *value = MODE_STATUS_CANCEL;
+}
+
+struct zwp_fullscreen_shell_mode_feedback_v1_listener mode_feedback_listener = {
+ mode_feedback_successful,
+ mode_feedback_failed,
+ mode_feedback_cancelled,
+};
+
+static int
+wayland_output_switch_mode(struct weston_output *output_base,
+ struct weston_mode *mode)
+{
+ struct wayland_output *output = (struct wayland_output *) output_base;
+ struct wayland_backend *b;
+ struct wl_surface *old_surface;
+ struct weston_mode *old_mode;
+ struct zwp_fullscreen_shell_mode_feedback_v1 *mode_feedback;
+ enum mode_status mode_status;
+ int ret = 0;
+
+ if (output_base == NULL) {
+ weston_log("output is NULL.\n");
+ return -1;
+ }
+
+ if (mode == NULL) {
+ weston_log("mode is NULL.\n");
+ return -1;
+ }
+
+ b = (struct wayland_backend *)output_base->compositor->backend;
+
+ if (output->parent.shell_surface || !b->parent.fshell)
+ return -1;
+
+ mode = wayland_output_choose_mode(output, mode);
+ if (mode == NULL)
+ return -1;
+
+ if (output->base.current_mode == mode)
+ return 0;
+
+ old_mode = output->base.current_mode;
+ old_surface = output->parent.surface;
+ output->base.current_mode = mode;
+ output->parent.surface =
+ wl_compositor_create_surface(b->parent.compositor);
+ wl_surface_set_user_data(output->parent.surface, output);
+
+ /* Blow the old buffers because we changed size/surfaces */
+ wayland_output_resize_surface(output);
+
+ mode_feedback =
+ zwp_fullscreen_shell_v1_present_surface_for_mode(b->parent.fshell,
+ output->parent.surface,
+ output->parent.output,
+ mode->refresh);
+ zwp_fullscreen_shell_mode_feedback_v1_add_listener(mode_feedback,
+ &mode_feedback_listener,
+ &mode_status);
+
+ /* This should kick-start things again */
+ output->parent.draw_initial_frame = 1;
+ wayland_output_start_repaint_loop(&output->base);
+
+ mode_status = MODE_STATUS_UNKNOWN;
+ while (mode_status == MODE_STATUS_UNKNOWN && ret >= 0)
+ ret = wl_display_dispatch(b->parent.wl_display);
+
+ zwp_fullscreen_shell_mode_feedback_v1_destroy(mode_feedback);
+
+ if (mode_status == MODE_STATUS_FAIL) {
+ output->base.current_mode = old_mode;
+ wl_surface_destroy(output->parent.surface);
+ output->parent.surface = old_surface;
+ wayland_output_resize_surface(output);
+
+ return -1;
+ }
+
+ old_mode->flags &= ~WL_OUTPUT_MODE_CURRENT;
+ output->base.current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
+
+ if (b->use_pixman) {
+ pixman_renderer_output_destroy(output_base);
+ if (wayland_output_init_pixman_renderer(output) < 0)
+ goto err_output;
+ } else {
+ gl_renderer->output_destroy(output_base);
+ wl_egl_window_destroy(output->gl.egl_window);
+ if (wayland_output_init_gl_renderer(output) < 0)
+ goto err_output;
+ }
+ wl_surface_destroy(old_surface);
+
+ weston_output_schedule_repaint(&output->base);
+
+ return 0;
+
+err_output:
+ /* XXX */
+ return -1;
+}
+
+static struct wayland_output *
+wayland_output_create(struct wayland_backend *b, int x, int y,
+ int width, int height, const char *name, int fullscreen,
+ uint32_t transform, int32_t scale)
+{
+ struct wayland_output *output;
+ int output_width, output_height;
+
+ weston_log("Creating %dx%d wayland output at (%d, %d)\n",
+ width, height, x, y);
+
+ output = zalloc(sizeof *output);
+ if (output == NULL)
+ return NULL;
+
+ output->name = name ? strdup(name) : NULL;
+ output->base.make = "wayland";
+ output->base.model = "none";
+
+ output_width = width * scale;
+ output_height = height * scale;
+
+ output->parent.surface =
+ wl_compositor_create_surface(b->parent.compositor);
+ if (!output->parent.surface)
+ goto err_name;
+ wl_surface_set_user_data(output->parent.surface, output);
+
+ output->parent.draw_initial_frame = 1;
+
+ if (b->parent.shell) {
+ output->parent.shell_surface =
+ wl_shell_get_shell_surface(b->parent.shell,
+ output->parent.surface);
+ if (!output->parent.shell_surface)
+ goto err_surface;
+ wl_shell_surface_add_listener(output->parent.shell_surface,
+ &shell_surface_listener, output);
+ }
+
+ if (fullscreen && b->parent.shell) {
+ wl_shell_surface_set_fullscreen(output->parent.shell_surface,
+ 0, 0, NULL);
+ wl_display_roundtrip(b->parent.wl_display);
+ if (!width)
+ output_width = output->parent.configure_width;
+ if (!height)
+ output_height = output->parent.configure_height;
+ }
+
+ output->mode.flags =
+ WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+ output->mode.width = output_width;
+ output->mode.height = output_height;
+ output->mode.refresh = 60000;
+ output->scale = scale;
+ wl_list_init(&output->base.mode_list);
+ wl_list_insert(&output->base.mode_list, &output->mode.link);
+ output->base.current_mode = &output->mode;
+
+ wl_list_init(&output->shm.buffers);
+ wl_list_init(&output->shm.free_buffers);
+
+ weston_output_init(&output->base, b->compositor, x, y, width, height,
+ transform, scale);
+
+ if (b->use_pixman) {
+ if (wayland_output_init_pixman_renderer(output) < 0)
+ goto err_output;
+ output->base.repaint = wayland_output_repaint_pixman;
+ } else {
+ if (wayland_output_init_gl_renderer(output) < 0)
+ goto err_output;
+ output->base.repaint = wayland_output_repaint_gl;
+ }
+
+ output->base.start_repaint_loop = wayland_output_start_repaint_loop;
+ output->base.destroy = wayland_output_destroy;
+ output->base.assign_planes = NULL;
+ output->base.set_backlight = NULL;
+ output->base.set_dpms = NULL;
+ output->base.switch_mode = wayland_output_switch_mode;
+
+ weston_compositor_add_output(b->compositor, &output->base);
+
+ return output;
+
+err_output:
+ weston_output_destroy(&output->base);
+ if (output->parent.shell_surface)
+ wl_shell_surface_destroy(output->parent.shell_surface);
+err_surface:
+ wl_surface_destroy(output->parent.surface);
+err_name:
+ free(output->name);
+
+ /* FIXME: cleanup weston_output */
+ free(output);
+
+ return NULL;
+}
+
+static struct wayland_output *
+wayland_output_create_for_config(struct wayland_backend *b,
+ struct weston_wayland_backend_output_config *oc,
+ int fullscreen, int32_t x, int32_t y)
+{
+ struct wayland_output *output;
+
+ output = wayland_output_create(b, x, y, oc->width, oc->height, oc->name,
+ fullscreen, oc->transform, oc->scale);
+
+ return output;
+}
+
+static struct wayland_output *
+wayland_output_create_for_parent_output(struct wayland_backend *b,
+ struct wayland_parent_output *poutput)
+{
+ struct wayland_output *output;
+ struct weston_mode *mode;
+ int32_t x;
+
+ if (poutput->current_mode) {
+ mode = poutput->current_mode;
+ } else if (poutput->preferred_mode) {
+ mode = poutput->preferred_mode;
+ } else if (!wl_list_empty(&poutput->mode_list)) {
+ mode = container_of(poutput->mode_list.next,
+ struct weston_mode, link);
+ } else {
+ weston_log("No valid modes found. Skipping output\n");
+ return NULL;
+ }
+
+ if (!wl_list_empty(&b->compositor->output_list)) {
+ output = container_of(b->compositor->output_list.prev,
+ struct wayland_output, base.link);
+ x = output->base.x + output->base.current_mode->width;
+ } else {
+ x = 0;
+ }
+
+ output = wayland_output_create(b, x, 0, mode->width, mode->height,
+ NULL, 0,
+ WL_OUTPUT_TRANSFORM_NORMAL, 1);
+ if (!output)
+ return NULL;
+
+ output->parent.output = poutput->global;
+
+ output->base.make = poutput->physical.make;
+ output->base.model = poutput->physical.model;
+ wl_list_init(&output->base.mode_list);
+ wl_list_insert_list(&output->base.mode_list, &poutput->mode_list);
+ wl_list_init(&poutput->mode_list);
+
+ wayland_output_set_fullscreen(output,
+ WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER,
+ mode->refresh, poutput->global);
+
+ if (output->parent.shell_surface) {
+ wl_shell_surface_set_fullscreen(output->parent.shell_surface,
+ WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER,
+ mode->refresh, poutput->global);
+ } else if (b->parent.fshell) {
+ zwp_fullscreen_shell_v1_present_surface(b->parent.fshell,
+ output->parent.surface,
+ ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_CENTER,
+ poutput->global);
+ zwp_fullscreen_shell_mode_feedback_v1_destroy(
+ zwp_fullscreen_shell_v1_present_surface_for_mode(b->parent.fshell,
+ output->parent.surface,
+ poutput->global,
+ mode->refresh));
+ }
+
+ return output;
+}
+
+static void
+shell_surface_ping(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t serial)
+{
+ wl_shell_surface_pong(shell_surface, serial);
+}
+
+static void
+shell_surface_configure(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t edges, int32_t width, int32_t height)
+{
+ struct wayland_output *output = data;
+
+ output->parent.configure_width = width;
+ output->parent.configure_height = height;
+
+ /* FIXME: implement resizing */
+}
+
+static void
+shell_surface_popup_done(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static const struct wl_shell_surface_listener shell_surface_listener = {
+ shell_surface_ping,
+ shell_surface_configure,
+ shell_surface_popup_done
+};
+
+/* Events received from the wayland-server this compositor is client of: */
+
+/* parent input interface */
+static void
+input_set_cursor(struct wayland_input *input)
+{
+
+ struct wl_buffer *buffer;
+ struct wl_cursor_image *image;
+
+ if (!input->backend->cursor)
+ return; /* Couldn't load the cursor. Can't set it */
+
+ image = input->backend->cursor->images[0];
+ buffer = wl_cursor_image_get_buffer(image);
+ if (!buffer)
+ return;
+
+ wl_pointer_set_cursor(input->parent.pointer, input->enter_serial,
+ input->parent.cursor.surface,
+ image->hotspot_x, image->hotspot_y);
+
+ wl_surface_attach(input->parent.cursor.surface, buffer, 0, 0);
+ wl_surface_damage(input->parent.cursor.surface, 0, 0,
+ image->width, image->height);
+ wl_surface_commit(input->parent.cursor.surface);
+}
+
+static void
+input_handle_pointer_enter(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *surface,
+ wl_fixed_t fixed_x, wl_fixed_t fixed_y)
+{
+ struct wayland_input *input = data;
+ int32_t fx, fy;
+ enum theme_location location;
+ double x, y;
+
+ x = wl_fixed_to_double(fixed_x);
+ y = wl_fixed_to_double(fixed_y);
+
+ /* XXX: If we get a modifier event immediately before the focus,
+ * we should try to keep the same serial. */
+ input->enter_serial = serial;
+ input->output = wl_surface_get_user_data(surface);
+
+ if (input->output->frame) {
+ location = frame_pointer_enter(input->output->frame, input,
+ x, y);
+ frame_interior(input->output->frame, &fx, &fy, NULL, NULL);
+ x -= fx;
+ y -= fy;
+
+ if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT)
+ weston_output_schedule_repaint(&input->output->base);
+ } else {
+ location = THEME_LOCATION_CLIENT_AREA;
+ }
+
+ weston_output_transform_coordinate(&input->output->base, x, y, &x, &y);
+
+ if (location == THEME_LOCATION_CLIENT_AREA) {
+ input->has_focus = true;
+ notify_pointer_focus(&input->base, &input->output->base, x, y);
+ wl_pointer_set_cursor(input->parent.pointer,
+ input->enter_serial, NULL, 0, 0);
+ } else {
+ input->has_focus = false;
+ notify_pointer_focus(&input->base, NULL, 0, 0);
+ input_set_cursor(input);
+ }
+}
+
+static void
+input_handle_pointer_leave(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *surface)
+{
+ struct wayland_input *input = data;
+
+ if (!input->output)
+ return;
+
+ if (input->output->frame) {
+ frame_pointer_leave(input->output->frame, input);
+
+ if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT)
+ weston_output_schedule_repaint(&input->output->base);
+ }
+
+ notify_pointer_focus(&input->base, NULL, 0, 0);
+ input->output = NULL;
+ input->has_focus = false;
+}
+
+static void
+input_handle_motion(void *data, struct wl_pointer *pointer,
+ uint32_t time, wl_fixed_t fixed_x, wl_fixed_t fixed_y)
+{
+ struct wayland_input *input = data;
+ int32_t fx, fy;
+ enum theme_location location;
+ bool want_frame = false;
+ double x, y;
+
+ if (!input->output)
+ return;
+
+ x = wl_fixed_to_double(fixed_x);
+ y = wl_fixed_to_double(fixed_y);
+
+ if (input->output->frame) {
+ location = frame_pointer_motion(input->output->frame, input,
+ x, y);
+ frame_interior(input->output->frame, &fx, &fy, NULL, NULL);
+ x -= fx;
+ y -= fy;
+
+ if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT)
+ weston_output_schedule_repaint(&input->output->base);
+ } else {
+ location = THEME_LOCATION_CLIENT_AREA;
+ }
+
+ weston_output_transform_coordinate(&input->output->base, x, y, &x, &y);
+
+ if (input->has_focus && location != THEME_LOCATION_CLIENT_AREA) {
+ input_set_cursor(input);
+ notify_pointer_focus(&input->base, NULL, 0, 0);
+ input->has_focus = false;
+ want_frame = true;
+ } else if (!input->has_focus &&
+ location == THEME_LOCATION_CLIENT_AREA) {
+ wl_pointer_set_cursor(input->parent.pointer,
+ input->enter_serial, NULL, 0, 0);
+ notify_pointer_focus(&input->base, &input->output->base, x, y);
+ input->has_focus = true;
+ want_frame = true;
+ }
+
+ if (location == THEME_LOCATION_CLIENT_AREA) {
+ notify_motion_absolute(&input->base, time, x, y);
+ want_frame = true;
+ }
+
+ if (want_frame && input->seat_version < WL_POINTER_FRAME_SINCE_VERSION)
+ notify_pointer_frame(&input->base);
+}
+
+static void
+input_handle_button(void *data, struct wl_pointer *pointer,
+ uint32_t serial, uint32_t time, uint32_t button,
+ uint32_t state_w)
+{
+ struct wayland_input *input = data;
+ enum wl_pointer_button_state state = state_w;
+ enum frame_button_state fstate;
+ enum theme_location location;
+
+ if (!input->output)
+ return;
+
+ if (input->output->frame) {
+ fstate = state == WL_POINTER_BUTTON_STATE_PRESSED ?
+ FRAME_BUTTON_PRESSED : FRAME_BUTTON_RELEASED;
+
+ location = frame_pointer_button(input->output->frame, input,
+ button, fstate);
+
+ if (frame_status(input->output->frame) & FRAME_STATUS_MOVE) {
+
+ wl_shell_surface_move(input->output->parent.shell_surface,
+ input->parent.seat, serial);
+ frame_status_clear(input->output->frame,
+ FRAME_STATUS_MOVE);
+ return;
+ }
+
+ if (frame_status(input->output->frame) & FRAME_STATUS_CLOSE) {
+ wayland_output_destroy(&input->output->base);
+ input->output = NULL;
+ input->keyboard_focus = NULL;
+
+ if (wl_list_empty(&input->backend->compositor->output_list))
+ weston_compositor_exit(input->backend->compositor);
+
+ return;
+ }
+
+ if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT)
+ weston_output_schedule_repaint(&input->output->base);
+ } else {
+ location = THEME_LOCATION_CLIENT_AREA;
+ }
+
+ if (location == THEME_LOCATION_CLIENT_AREA) {
+ notify_button(&input->base, time, button, state);
+ if (input->seat_version < WL_POINTER_FRAME_SINCE_VERSION)
+ notify_pointer_frame(&input->base);
+ }
+}
+
+static void
+input_handle_axis(void *data, struct wl_pointer *pointer,
+ uint32_t time, uint32_t axis, wl_fixed_t value)
+{
+ struct wayland_input *input = data;
+ struct weston_pointer_axis_event weston_event;
+
+ weston_event.axis = axis;
+ weston_event.value = wl_fixed_to_double(value);
+
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL &&
+ input->vert.has_discrete) {
+ weston_event.has_discrete = true;
+ weston_event.discrete = input->vert.discrete;
+ input->vert.has_discrete = false;
+ } else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL &&
+ input->horiz.has_discrete) {
+ weston_event.has_discrete = true;
+ weston_event.discrete = input->horiz.discrete;
+ input->horiz.has_discrete = false;
+ }
+
+ notify_axis(&input->base, time, &weston_event);
+
+ if (input->seat_version < WL_POINTER_FRAME_SINCE_VERSION)
+ notify_pointer_frame(&input->base);
+}
+
+static void
+input_handle_frame(void *data, struct wl_pointer *pointer)
+{
+ struct wayland_input *input = data;
+
+ notify_pointer_frame(&input->base);
+}
+
+static void
+input_handle_axis_source(void *data, struct wl_pointer *pointer,
+ uint32_t source)
+{
+ struct wayland_input *input = data;
+
+ notify_axis_source(&input->base, source);
+}
+
+static void
+input_handle_axis_stop(void *data, struct wl_pointer *pointer,
+ uint32_t time, uint32_t axis)
+{
+ struct wayland_input *input = data;
+ struct weston_pointer_axis_event weston_event;
+
+ weston_event.axis = axis;
+ weston_event.value = 0;
+
+ notify_axis(&input->base, time, &weston_event);
+}
+
+static void
+input_handle_axis_discrete(void *data, struct wl_pointer *pointer,
+ uint32_t axis, int32_t discrete)
+{
+ struct wayland_input *input = data;
+
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
+ input->vert.has_discrete = true;
+ input->vert.discrete = discrete;
+ } else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
+ input->horiz.has_discrete = true;
+ input->horiz.discrete = discrete;
+ }
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+ input_handle_pointer_enter,
+ input_handle_pointer_leave,
+ input_handle_motion,
+ input_handle_button,
+ input_handle_axis,
+ input_handle_frame,
+ input_handle_axis_source,
+ input_handle_axis_stop,
+ input_handle_axis_discrete,
+};
+
+static void
+input_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format,
+ int fd, uint32_t size)
+{
+ struct wayland_input *input = data;
+ struct xkb_keymap *keymap;
+ char *map_str;
+
+ if (!data) {
+ close(fd);
+ return;
+ }
+
+ if (format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
+ map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ if (map_str == MAP_FAILED) {
+ weston_log("mmap failed: %m\n");
+ goto error;
+ }
+
+ keymap = xkb_keymap_new_from_string(input->backend->compositor->xkb_context,
+ map_str,
+ XKB_KEYMAP_FORMAT_TEXT_V1,
+ 0);
+ munmap(map_str, size);
+
+ if (!keymap) {
+ weston_log("failed to compile keymap\n");
+ goto error;
+ }
+
+ input->keyboard_state_update = STATE_UPDATE_NONE;
+ } else if (format == WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP) {
+ weston_log("No keymap provided; falling back to defalt\n");
+ keymap = NULL;
+ input->keyboard_state_update = STATE_UPDATE_AUTOMATIC;
+ } else {
+ weston_log("Invalid keymap\n");
+ goto error;
+ }
+
+ close(fd);
+
+ if (weston_seat_get_keyboard(&input->base))
+ weston_seat_update_keymap(&input->base, keymap);
+ else
+ weston_seat_init_keyboard(&input->base, keymap);
+
+ xkb_keymap_unref(keymap);
+
+ return;
+
+error:
+ wl_keyboard_release(input->parent.keyboard);
+ close(fd);
+}
+
+static void
+input_handle_keyboard_enter(void *data,
+ struct wl_keyboard *keyboard,
+ uint32_t serial,
+ struct wl_surface *surface,
+ struct wl_array *keys)
+{
+ struct wayland_input *input = data;
+ struct wayland_output *focus;
+
+ focus = input->keyboard_focus;
+ if (focus) {
+ /* This shouldn't happen */
+ focus->keyboard_count--;
+ if (!focus->keyboard_count && focus->frame)
+ frame_unset_flag(focus->frame, FRAME_FLAG_ACTIVE);
+ if (frame_status(focus->frame) & FRAME_STATUS_REPAINT)
+ weston_output_schedule_repaint(&focus->base);
+ }
+
+ input->keyboard_focus = wl_surface_get_user_data(surface);
+ input->keyboard_focus->keyboard_count++;
+
+ focus = input->keyboard_focus;
+ if (focus->frame) {
+ frame_set_flag(focus->frame, FRAME_FLAG_ACTIVE);
+ if (frame_status(focus->frame) & FRAME_STATUS_REPAINT)
+ weston_output_schedule_repaint(&focus->base);
+ }
+
+
+ /* XXX: If we get a modifier event immediately before the focus,
+ * we should try to keep the same serial. */
+ notify_keyboard_focus_in(&input->base, keys,
+ STATE_UPDATE_AUTOMATIC);
+}
+
+static void
+input_handle_keyboard_leave(void *data,
+ struct wl_keyboard *keyboard,
+ uint32_t serial,
+ struct wl_surface *surface)
+{
+ struct wayland_input *input = data;
+ struct wayland_output *focus;
+
+ notify_keyboard_focus_out(&input->base);
+
+ focus = input->keyboard_focus;
+ if (!focus)
+ return;
+
+ focus->keyboard_count--;
+ if (!focus->keyboard_count && focus->frame) {
+ frame_unset_flag(focus->frame, FRAME_FLAG_ACTIVE);
+ if (frame_status(focus->frame) & FRAME_STATUS_REPAINT)
+ weston_output_schedule_repaint(&focus->base);
+ }
+
+ input->keyboard_focus = NULL;
+}
+
+static void
+input_handle_key(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
+{
+ struct wayland_input *input = data;
+
+ input->key_serial = serial;
+ notify_key(&input->base, time, key,
+ state ? WL_KEYBOARD_KEY_STATE_PRESSED :
+ WL_KEYBOARD_KEY_STATE_RELEASED,
+ input->keyboard_state_update);
+}
+
+static void
+input_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial_in, uint32_t mods_depressed,
+ uint32_t mods_latched, uint32_t mods_locked,
+ uint32_t group)
+{
+ struct weston_keyboard *keyboard;
+ struct wayland_input *input = data;
+ struct wayland_backend *b = input->backend;
+ uint32_t serial_out;
+
+ /* If we get a key event followed by a modifier event with the
+ * same serial number, then we try to preserve those semantics by
+ * reusing the same serial number on the way out too. */
+ if (serial_in == input->key_serial)
+ serial_out = wl_display_get_serial(b->compositor->wl_display);
+ else
+ serial_out = wl_display_next_serial(b->compositor->wl_display);
+
+ keyboard = weston_seat_get_keyboard(&input->base);
+ xkb_state_update_mask(keyboard->xkb_state.state,
+ mods_depressed, mods_latched,
+ mods_locked, 0, 0, group);
+ notify_modifiers(&input->base, serial_out);
+}
+
+static void
+input_handle_repeat_info(void *data, struct wl_keyboard *keyboard,
+ int32_t rate, int32_t delay)
+{
+ struct wayland_input *input = data;
+ struct wayland_backend *b = input->backend;
+
+ b->compositor->kb_repeat_rate = rate;
+ b->compositor->kb_repeat_delay = delay;
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+ input_handle_keymap,
+ input_handle_keyboard_enter,
+ input_handle_keyboard_leave,
+ input_handle_key,
+ input_handle_modifiers,
+ input_handle_repeat_info,
+};
+
+static void
+input_handle_touch_down(void *data, struct wl_touch *wl_touch,
+ uint32_t serial, uint32_t time,
+ struct wl_surface *surface, int32_t id,
+ wl_fixed_t fixed_x, wl_fixed_t fixed_y)
+{
+ struct wayland_input *input = data;
+ struct wayland_output *output;
+ enum theme_location location;
+ bool first_touch;
+ int32_t fx, fy;
+ double x, y;
+
+ x = wl_fixed_to_double(fixed_x);
+ y = wl_fixed_to_double(fixed_y);
+
+ first_touch = (input->touch_points == 0);
+ input->touch_points++;
+
+ input->touch_focus = wl_surface_get_user_data(surface);
+ output = input->touch_focus;
+ if (!first_touch && !input->touch_active)
+ return;
+
+ if (output->frame) {
+ location = frame_touch_down(output->frame, input, id, x, y);
+
+ frame_interior(output->frame, &fx, &fy, NULL, NULL);
+ x -= fx;
+ y -= fy;
+
+ if (frame_status(output->frame) & FRAME_STATUS_REPAINT)
+ weston_output_schedule_repaint(&output->base);
+
+ if (first_touch && (frame_status(output->frame) & FRAME_STATUS_MOVE)) {
+ input->touch_points--;
+ wl_shell_surface_move(output->parent.shell_surface,
+ input->parent.seat, serial);
+ frame_status_clear(output->frame,
+ FRAME_STATUS_MOVE);
+ return;
+ }
+
+ if (first_touch && location != THEME_LOCATION_CLIENT_AREA)
+ return;
+ }
+
+ weston_output_transform_coordinate(&output->base, x, y, &x, &y);
+
+ notify_touch(&input->base, time, id, x, y, WL_TOUCH_DOWN);
+ input->touch_active = true;
+}
+
+static void
+input_handle_touch_up(void *data, struct wl_touch *wl_touch,
+ uint32_t serial, uint32_t time, int32_t id)
+{
+ struct wayland_input *input = data;
+ struct wayland_output *output = input->touch_focus;
+ bool active = input->touch_active;
+
+ input->touch_points--;
+ if (input->touch_points == 0) {
+ input->touch_focus = NULL;
+ input->touch_active = false;
+ }
+
+ if (!output)
+ return;
+
+ if (output->frame) {
+ frame_touch_up(output->frame, input, id);
+
+ if (frame_status(output->frame) & FRAME_STATUS_CLOSE) {
+ wayland_output_destroy(&output->base);
+ input->touch_focus = NULL;
+ input->keyboard_focus = NULL;
+ if (wl_list_empty(&input->backend->compositor->output_list))
+ weston_compositor_exit(input->backend->compositor);
+
+ return;
+ }
+ if (frame_status(output->frame) & FRAME_STATUS_REPAINT)
+ weston_output_schedule_repaint(&output->base);
+ }
+
+ if (active)
+ notify_touch(&input->base, time, id, 0, 0, WL_TOUCH_UP);
+}
+
+static void
+input_handle_touch_motion(void *data, struct wl_touch *wl_touch,
+ uint32_t time, int32_t id,
+ wl_fixed_t fixed_x, wl_fixed_t fixed_y)
+{
+ struct wayland_input *input = data;
+ struct wayland_output *output = input->touch_focus;
+ int32_t fx, fy;
+ double x, y;
+
+ x = wl_fixed_to_double(fixed_x);
+ y = wl_fixed_to_double(fixed_y);
+
+ if (!output || !input->touch_active)
+ return;
+
+ if (output->frame) {
+ frame_interior(output->frame, &fx, &fy, NULL, NULL);
+ x -= fx;
+ y -= fy;
+ }
+
+ weston_output_transform_coordinate(&output->base, x, y, &x, &y);
+
+ notify_touch(&input->base, time, id, x, y, WL_TOUCH_MOTION);
+}
+
+static void
+input_handle_touch_frame(void *data, struct wl_touch *wl_touch)
+{
+ struct wayland_input *input = data;
+
+ if (!input->touch_focus || !input->touch_active)
+ return;
+
+ notify_touch_frame(&input->base);
+}
+
+static void
+input_handle_touch_cancel(void *data, struct wl_touch *wl_touch)
+{
+ struct wayland_input *input = data;
+
+ if (!input->touch_focus || !input->touch_active)
+ return;
+
+ notify_touch_cancel(&input->base);
+}
+
+static const struct wl_touch_listener touch_listener = {
+ input_handle_touch_down,
+ input_handle_touch_up,
+ input_handle_touch_motion,
+ input_handle_touch_frame,
+ input_handle_touch_cancel,
+};
+
+
+static void
+input_handle_capabilities(void *data, struct wl_seat *seat,
+ enum wl_seat_capability caps)
+{
+ struct wayland_input *input = data;
+
+ if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->parent.pointer) {
+ input->parent.pointer = wl_seat_get_pointer(seat);
+ wl_pointer_set_user_data(input->parent.pointer, input);
+ wl_pointer_add_listener(input->parent.pointer,
+ &pointer_listener, input);
+ weston_seat_init_pointer(&input->base);
+ } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->parent.pointer) {
+ if (input->seat_version >= WL_POINTER_RELEASE_SINCE_VERSION)
+ wl_pointer_release(input->parent.pointer);
+ else
+ wl_pointer_destroy(input->parent.pointer);
+ input->parent.pointer = NULL;
+ weston_seat_release_pointer(&input->base);
+ }
+
+ if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->parent.keyboard) {
+ input->parent.keyboard = wl_seat_get_keyboard(seat);
+ wl_keyboard_set_user_data(input->parent.keyboard, input);
+ wl_keyboard_add_listener(input->parent.keyboard,
+ &keyboard_listener, input);
+ } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->parent.keyboard) {
+ if (input->seat_version >= WL_KEYBOARD_RELEASE_SINCE_VERSION)
+ wl_keyboard_release(input->parent.keyboard);
+ else
+ wl_keyboard_destroy(input->parent.keyboard);
+ input->parent.keyboard = NULL;
+ weston_seat_release_keyboard(&input->base);
+ }
+
+ if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->parent.touch) {
+ input->parent.touch = wl_seat_get_touch(seat);
+ wl_touch_set_user_data(input->parent.touch, input);
+ wl_touch_add_listener(input->parent.touch,
+ &touch_listener, input);
+ weston_seat_init_touch(&input->base);
+ } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->parent.touch) {
+ if (input->seat_version >= WL_TOUCH_RELEASE_SINCE_VERSION)
+ wl_touch_release(input->parent.touch);
+ else
+ wl_touch_destroy(input->parent.touch);
+ input->parent.touch = NULL;
+ weston_seat_release_touch(&input->base);
+ }
+}
+
+static void
+input_handle_name(void *data, struct wl_seat *seat,
+ const char *name)
+{
+}
+
+static const struct wl_seat_listener seat_listener = {
+ input_handle_capabilities,
+ input_handle_name,
+};
+
+static void
+display_add_seat(struct wayland_backend *b, uint32_t id, uint32_t available_version)
+{
+ struct wayland_input *input;
+ uint32_t version = MIN(available_version, 4);
+
+ input = zalloc(sizeof *input);
+ if (input == NULL)
+ return;
+
+ weston_seat_init(&input->base, b->compositor, "default");
+ input->backend = b;
+ input->parent.seat = wl_registry_bind(b->parent.registry, id,
+ &wl_seat_interface, version);
+ input->seat_version = version;
+ wl_list_insert(b->input_list.prev, &input->link);
+
+ wl_seat_add_listener(input->parent.seat, &seat_listener, input);
+ wl_seat_set_user_data(input->parent.seat, input);
+
+ input->parent.cursor.surface =
+ wl_compositor_create_surface(b->parent.compositor);
+
+ input->vert.axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
+ input->horiz.axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL;
+}
+
+static void
+wayland_parent_output_geometry(void *data, struct wl_output *output_proxy,
+ int32_t x, int32_t y,
+ int32_t physical_width, int32_t physical_height,
+ int32_t subpixel, const char *make,
+ const char *model, int32_t transform)
+{
+ struct wayland_parent_output *output = data;
+
+ output->x = x;
+ output->y = y;
+ output->physical.width = physical_width;
+ output->physical.height = physical_height;
+ output->physical.subpixel = subpixel;
+
+ free(output->physical.make);
+ output->physical.make = strdup(make);
+ free(output->physical.model);
+ output->physical.model = strdup(model);
+
+ output->transform = transform;
+}
+
+static struct weston_mode *
+find_mode(struct wl_list *list, int32_t width, int32_t height, uint32_t refresh)
+{
+ struct weston_mode *mode;
+
+ wl_list_for_each(mode, list, link) {
+ if (mode->width == width && mode->height == height &&
+ mode->refresh == refresh)
+ return mode;
+ }
+
+ mode = zalloc(sizeof *mode);
+ if (!mode)
+ return NULL;
+
+ mode->width = width;
+ mode->height = height;
+ mode->refresh = refresh;
+ wl_list_insert(list, &mode->link);
+
+ return mode;
+}
+
+static void
+wayland_parent_output_mode(void *data, struct wl_output *wl_output_proxy,
+ uint32_t flags, int32_t width, int32_t height,
+ int32_t refresh)
+{
+ struct wayland_parent_output *output = data;
+ struct weston_mode *mode;
+
+ if (output->output) {
+ mode = find_mode(&output->output->base.mode_list,
+ width, height, refresh);
+ if (!mode)
+ return;
+ mode->flags = flags;
+ /* Do a mode-switch on current mode change? */
+ } else {
+ mode = find_mode(&output->mode_list, width, height, refresh);
+ if (!mode)
+ return;
+ mode->flags = flags;
+ if (flags & WL_OUTPUT_MODE_CURRENT)
+ output->current_mode = mode;
+ if (flags & WL_OUTPUT_MODE_PREFERRED)
+ output->preferred_mode = mode;
+ }
+}
+
+static const struct wl_output_listener output_listener = {
+ wayland_parent_output_geometry,
+ wayland_parent_output_mode
+};
+
+static void
+wayland_backend_register_output(struct wayland_backend *b, uint32_t id)
+{
+ struct wayland_parent_output *output;
+
+ output = zalloc(sizeof *output);
+ if (!output)
+ return;
+
+ output->id = id;
+ output->global = wl_registry_bind(b->parent.registry, id,
+ &wl_output_interface, 1);
+ if (!output->global) {
+ free(output);
+ return;
+ }
+
+ wl_output_add_listener(output->global, &output_listener, output);
+
+ output->scale = 0;
+ output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ output->physical.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
+ wl_list_init(&output->mode_list);
+ wl_list_insert(&b->parent.output_list, &output->link);
+
+ if (b->sprawl_across_outputs) {
+ wl_display_roundtrip(b->parent.wl_display);
+ wayland_output_create_for_parent_output(b, output);
+ }
+}
+
+static void
+wayland_parent_output_destroy(struct wayland_parent_output *output)
+{
+ struct weston_mode *mode, *next;
+
+ if (output->output)
+ wayland_output_destroy(&output->output->base);
+
+ wl_output_destroy(output->global);
+ free(output->physical.make);
+ free(output->physical.model);
+
+ wl_list_for_each_safe(mode, next, &output->mode_list, link) {
+ wl_list_remove(&mode->link);
+ free(mode);
+ }
+}
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry, uint32_t name,
+ const char *interface, uint32_t version)
+{
+ struct wayland_backend *b = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ b->parent.compositor =
+ wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ } else if (strcmp(interface, "wl_shell") == 0) {
+ b->parent.shell =
+ wl_registry_bind(registry, name,
+ &wl_shell_interface, 1);
+ } else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
+ b->parent.fshell =
+ wl_registry_bind(registry, name,
+ &zwp_fullscreen_shell_v1_interface, 1);
+ } else if (strcmp(interface, "wl_seat") == 0) {
+ display_add_seat(b, name, version);
+ } else if (strcmp(interface, "wl_output") == 0) {
+ wayland_backend_register_output(b, name);
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ b->parent.shm =
+ wl_registry_bind(registry, name, &wl_shm_interface, 1);
+ }
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name)
+{
+ struct wayland_backend *b = data;
+ struct wayland_parent_output *output;
+
+ wl_list_for_each(output, &b->parent.output_list, link)
+ if (output->id == name)
+ wayland_parent_output_destroy(output);
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global,
+ registry_handle_global_remove
+};
+
+static int
+wayland_backend_handle_event(int fd, uint32_t mask, void *data)
+{
+ struct wayland_backend *b = data;
+ int count = 0;
+
+ if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
+ weston_compositor_exit(b->compositor);
+ return 0;
+ }
+
+ if (mask & WL_EVENT_READABLE)
+ count = wl_display_dispatch(b->parent.wl_display);
+ if (mask & WL_EVENT_WRITABLE)
+ wl_display_flush(b->parent.wl_display);
+
+ if (mask == 0) {
+ count = wl_display_dispatch_pending(b->parent.wl_display);
+ wl_display_flush(b->parent.wl_display);
+ }
+
+ return count;
+}
+
+static void
+wayland_restore(struct weston_compositor *ec)
+{
+}
+
+static void
+wayland_destroy(struct weston_compositor *ec)
+{
+ struct wayland_backend *b = (struct wayland_backend *) ec->backend;
+
+ weston_compositor_shutdown(ec);
+
+ if (b->parent.shm)
+ wl_shm_destroy(b->parent.shm);
+
+ free(b);
+}
+
+static const char *left_ptrs[] = {
+ "left_ptr",
+ "default",
+ "top_left_arrow",
+ "left-arrow"
+};
+
+static void
+create_cursor(struct wayland_backend *b,
+ struct weston_wayland_backend_config *config)
+{
+ unsigned int i;
+
+ b->cursor_theme = wl_cursor_theme_load(config->cursor_theme,
+ config->cursor_size,
+ b->parent.shm);
+ if (!b->cursor_theme) {
+ fprintf(stderr, "could not load cursor theme\n");
+ return;
+ }
+
+ b->cursor = NULL;
+ for (i = 0; !b->cursor && i < ARRAY_LENGTH(left_ptrs); ++i)
+ b->cursor = wl_cursor_theme_get_cursor(b->cursor_theme,
+ left_ptrs[i]);
+ if (!b->cursor) {
+ fprintf(stderr, "could not load left cursor\n");
+ return;
+ }
+}
+
+static void
+fullscreen_binding(struct weston_keyboard *keyboard, uint32_t time,
+ uint32_t key, void *data)
+{
+ struct wayland_backend *b = data;
+ struct wayland_input *input = NULL;
+
+ wl_list_for_each(input, &b->input_list, link)
+ if (&input->base == keyboard->seat)
+ break;
+
+ if (!input || !input->output)
+ return;
+
+ if (input->output->frame)
+ wayland_output_set_fullscreen(input->output, 0, 0, NULL);
+ else
+ wayland_output_set_windowed(input->output);
+
+ weston_output_schedule_repaint(&input->output->base);
+}
+
+static struct wayland_backend *
+wayland_backend_create(struct weston_compositor *compositor,
+ struct weston_wayland_backend_config *new_config)
+{
+ struct wayland_backend *b;
+ struct wl_event_loop *loop;
+ int fd;
+
+ b = zalloc(sizeof *b);
+ if (b == NULL)
+ return NULL;
+
+ b->compositor = compositor;
+ if (weston_compositor_set_presentation_clock_software(compositor) < 0)
+ goto err_compositor;
+
+ b->parent.wl_display = wl_display_connect(new_config->display_name);
+ if (b->parent.wl_display == NULL) {
+ weston_log("failed to create display: %m\n");
+ goto err_compositor;
+ }
+
+ wl_list_init(&b->parent.output_list);
+ wl_list_init(&b->input_list);
+ b->parent.registry = wl_display_get_registry(b->parent.wl_display);
+ wl_registry_add_listener(b->parent.registry, ®istry_listener, b);
+ wl_display_roundtrip(b->parent.wl_display);
+
+ create_cursor(b, new_config);
+
+ b->use_pixman = new_config->use_pixman;
+
+ if (!b->use_pixman) {
+ gl_renderer = weston_load_module("gl-renderer.so",
+ "gl_renderer_interface");
+ if (!gl_renderer)
+ b->use_pixman = 1;
+ }
+
+ if (!b->use_pixman) {
+ if (gl_renderer->create(compositor,
+ EGL_PLATFORM_WAYLAND_KHR,
+ b->parent.wl_display,
+ gl_renderer->alpha_attribs,
+ NULL,
+ 0) < 0) {
+ weston_log("Failed to initialize the GL renderer; "
+ "falling back to pixman.\n");
+ b->use_pixman = 1;
+ }
+ }
+
+ if (b->use_pixman) {
+ if (pixman_renderer_init(compositor) < 0) {
+ weston_log("Failed to initialize pixman renderer\n");
+ goto err_display;
+ }
+ }
+
+ b->base.destroy = wayland_destroy;
+ b->base.restore = wayland_restore;
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+
+ fd = wl_display_get_fd(b->parent.wl_display);
+ b->parent.wl_source =
+ wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
+ wayland_backend_handle_event, b);
+ if (b->parent.wl_source == NULL)
+ goto err_display;
+
+ wl_event_source_check(b->parent.wl_source);
+
+ if (compositor->renderer->import_dmabuf) {
+ if (linux_dmabuf_setup(compositor) < 0)
+ weston_log("Error: initializing dmabuf "
+ "support failed.\n");
+ }
+
+ compositor->backend = &b->base;
+ return b;
+err_display:
+ wl_display_disconnect(b->parent.wl_display);
+err_compositor:
+ weston_compositor_shutdown(compositor);
+ free(b);
+ return NULL;
+}
+
+static void
+wayland_backend_destroy(struct wayland_backend *b)
+{
+ wl_display_disconnect(b->parent.wl_display);
+
+ if (b->theme)
+ theme_destroy(b->theme);
+ if (b->frame_device)
+ cairo_device_destroy(b->frame_device);
+ wl_cursor_theme_destroy(b->cursor_theme);
+
+ weston_compositor_shutdown(b->compositor);
+ free(b);
+}
+
+static void
+config_init_to_defaults(struct weston_wayland_backend_config *config)
+{
+}
+
+WL_EXPORT int
+backend_init(struct weston_compositor *compositor,
+ struct weston_backend_config *config_base)
+{
+ struct wayland_backend *b;
+ struct wayland_output *output;
+ struct wayland_parent_output *poutput;
+ struct weston_wayland_backend_config new_config;
+ int x, count;
+
+ if (config_base == NULL ||
+ config_base->struct_version != WESTON_WAYLAND_BACKEND_CONFIG_VERSION ||
+ config_base->struct_size > sizeof(struct weston_wayland_backend_config)) {
+ weston_log("wayland backend config structure is invalid\n");
+ return -1;
+ }
+
+ config_init_to_defaults(&new_config);
+ memcpy(&new_config, config_base, config_base->struct_size);
+
+ b = wayland_backend_create(compositor, &new_config);
+
+ if (!b)
+ return -1;
+
+ if (new_config.sprawl || b->parent.fshell) {
+ b->sprawl_across_outputs = 1;
+ wl_display_roundtrip(b->parent.wl_display);
+
+ wl_list_for_each(poutput, &b->parent.output_list, link)
+ wayland_output_create_for_parent_output(b, poutput);
+
+ return 0;
+ }
+
+ if (new_config.fullscreen) {
+ if (new_config.num_outputs != 1 || !new_config.outputs)
+ goto err_outputs;
+
+ output = wayland_output_create_for_config(b,
+ &new_config.outputs[0],
+ 1, 0, 0);
+ if (!output)
+ goto err_outputs;
+
+ wayland_output_set_fullscreen(output, 0, 0, NULL);
+ return 0;
+ }
+
+ x = 0;
+ for (count = 0; count < new_config.num_outputs; ++count) {
+ output = wayland_output_create_for_config(b, &new_config.outputs[count],
+ 0, x, 0);
+ if (!output)
+ goto err_outputs;
+ if (wayland_output_set_windowed(output))
+ goto err_outputs;
+
+ x += output->base.width;
+ }
+
+ weston_compositor_add_key_binding(compositor, KEY_F,
+ MODIFIER_CTRL | MODIFIER_ALT,
+ fullscreen_binding, b);
+ return 0;
+
+err_outputs:
+ wayland_backend_destroy(b);
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright © 2016 Benoit Gschwind
+ *
+ * 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.
+ */
+
+#ifndef WESTON_COMPOSITOR_WAYLAND_H
+#define WESTON_COMPOSITOR_WAYLAND_H
+
+#include "compositor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WESTON_WAYLAND_BACKEND_CONFIG_VERSION 1
+
+struct weston_wayland_backend_output_config {
+ int width;
+ int height;
+ char *name;
+ uint32_t transform;
+ int32_t scale;
+};
+
+struct weston_wayland_backend_config {
+ struct weston_backend_config base;
+ int use_pixman;
+ int sprawl;
+ char *display_name;
+ int fullscreen;
+ char *cursor_theme;
+ int cursor_size;
+ int num_outputs;
+ struct weston_wayland_backend_output_config *outputs;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WESTON_COMPOSITOR_WAYLAND_H */
--- /dev/null
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2010-2011 Intel Corporation
+ * Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/shm.h>
+#include <linux/input.h>
+
+#include <xcb/xcb.h>
+#include <xcb/shm.h>
+#ifdef HAVE_XCB_XKB
+#include <xcb/xkb.h>
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xlib-xcb.h>
+
+#include <xkbcommon/xkbcommon.h>
+
+#include "compositor.h"
+#include "compositor-x11.h"
+#include "shared/config-parser.h"
+#include "shared/helpers.h"
+#include "shared/image-loader.h"
+#include "gl-renderer.h"
+#include "pixman-renderer.h"
+#include "presentation-time-server-protocol.h"
+#include "linux-dmabuf.h"
+
+#define DEFAULT_AXIS_STEP_DISTANCE 10
+
+struct x11_backend {
+ struct weston_backend base;
+ struct weston_compositor *compositor;
+
+ 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 xkb_keymap *xkb_keymap;
+ unsigned int has_xkb;
+ uint8_t xkb_event_base;
+ int use_pixman;
+
+ int has_net_wm_state_fullscreen;
+
+ /* We could map multi-pointer X to multiple wayland seats, but
+ * for now we only support core X input. */
+ struct weston_seat core_seat;
+ double prev_x;
+ double prev_y;
+
+ 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_supporting_wm_check;
+ xcb_atom_t net_supported;
+ 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;
+ xcb_atom_t xkb_names;
+ } atom;
+};
+
+struct x11_output {
+ struct weston_output base;
+
+ xcb_window_t window;
+ struct weston_mode mode;
+ struct wl_event_source *finish_frame_timer;
+
+ xcb_gc_t gc;
+ xcb_shm_seg_t segment;
+ pixman_image_t *hw_surface;
+ int shm_id;
+ void *buf;
+ uint8_t depth;
+ int32_t scale;
+};
+
+struct window_delete_data {
+ struct x11_backend *backend;
+ xcb_window_t window;
+};
+
+struct gl_renderer_interface *gl_renderer;
+
+static xcb_screen_t *
+x11_compositor_get_default_screen(struct x11_backend *b)
+{
+ xcb_screen_iterator_t iter;
+ int i, screen_nbr = XDefaultScreen(b->dpy);
+
+ iter = xcb_setup_roots_iterator(xcb_get_setup(b->conn));
+ for (i = 0; iter.rem; xcb_screen_next(&iter), i++)
+ if (i == screen_nbr)
+ return iter.data;
+
+ return xcb_setup_roots_iterator(xcb_get_setup(b->conn)).data;
+}
+
+static struct xkb_keymap *
+x11_backend_get_keymap(struct x11_backend *b)
+{
+ xcb_get_property_cookie_t cookie;
+ xcb_get_property_reply_t *reply;
+ struct xkb_rule_names names;
+ struct xkb_keymap *ret;
+ const char *value_all, *value_part;
+ int length_all, length_part;
+
+ memset(&names, 0, sizeof(names));
+
+ cookie = xcb_get_property(b->conn, 0, b->screen->root,
+ b->atom.xkb_names, b->atom.string, 0, 1024);
+ reply = xcb_get_property_reply(b->conn, cookie, NULL);
+ if (reply == NULL)
+ return NULL;
+
+ value_all = xcb_get_property_value(reply);
+ length_all = xcb_get_property_value_length(reply);
+ value_part = value_all;
+
+#define copy_prop_value(to) \
+ length_part = strlen(value_part); \
+ if (value_part + length_part < (value_all + length_all) && \
+ length_part > 0) \
+ names.to = value_part; \
+ value_part += length_part + 1;
+
+ copy_prop_value(rules);
+ copy_prop_value(model);
+ copy_prop_value(layout);
+ copy_prop_value(variant);
+ copy_prop_value(options);
+#undef copy_prop_value
+
+ ret = xkb_keymap_new_from_names(b->compositor->xkb_context, &names, 0);
+
+ free(reply);
+ return ret;
+}
+
+static uint32_t
+get_xkb_mod_mask(struct x11_backend *b, uint32_t in)
+{
+ struct weston_keyboard *keyboard =
+ weston_seat_get_keyboard(&b->core_seat);
+ struct weston_xkb_info *info = keyboard->xkb_info;
+ uint32_t ret = 0;
+
+ if ((in & ShiftMask) && info->shift_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->shift_mod);
+ if ((in & LockMask) && info->caps_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->caps_mod);
+ if ((in & ControlMask) && info->ctrl_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->ctrl_mod);
+ if ((in & Mod1Mask) && info->alt_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->alt_mod);
+ if ((in & Mod2Mask) && info->mod2_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->mod2_mod);
+ if ((in & Mod3Mask) && info->mod3_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->mod3_mod);
+ if ((in & Mod4Mask) && info->super_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->super_mod);
+ if ((in & Mod5Mask) && info->mod5_mod != XKB_MOD_INVALID)
+ ret |= (1 << info->mod5_mod);
+
+ return ret;
+}
+
+static void
+x11_backend_setup_xkb(struct x11_backend *b)
+{
+#ifndef HAVE_XCB_XKB
+ weston_log("XCB-XKB not available during build\n");
+ b->has_xkb = 0;
+ b->xkb_event_base = 0;
+ return;
+#else
+ struct weston_keyboard *keyboard;
+ const xcb_query_extension_reply_t *ext;
+ xcb_generic_error_t *error;
+ xcb_void_cookie_t select;
+ xcb_xkb_use_extension_cookie_t use_ext;
+ xcb_xkb_use_extension_reply_t *use_ext_reply;
+ xcb_xkb_per_client_flags_cookie_t pcf;
+ xcb_xkb_per_client_flags_reply_t *pcf_reply;
+ xcb_xkb_get_state_cookie_t state;
+ xcb_xkb_get_state_reply_t *state_reply;
+ uint32_t values[1] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
+
+ b->has_xkb = 0;
+ b->xkb_event_base = 0;
+
+ ext = xcb_get_extension_data(b->conn, &xcb_xkb_id);
+ if (!ext) {
+ weston_log("XKB extension not available on host X11 server\n");
+ return;
+ }
+ b->xkb_event_base = ext->first_event;
+
+ select = xcb_xkb_select_events_checked(b->conn,
+ XCB_XKB_ID_USE_CORE_KBD,
+ XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
+ 0,
+ XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
+ 0,
+ 0,
+ NULL);
+ error = xcb_request_check(b->conn, select);
+ if (error) {
+ weston_log("error: failed to select for XKB state events\n");
+ free(error);
+ return;
+ }
+
+ use_ext = xcb_xkb_use_extension(b->conn,
+ XCB_XKB_MAJOR_VERSION,
+ XCB_XKB_MINOR_VERSION);
+ use_ext_reply = xcb_xkb_use_extension_reply(b->conn, use_ext, NULL);
+ if (!use_ext_reply) {
+ weston_log("couldn't start using XKB extension\n");
+ return;
+ }
+
+ if (!use_ext_reply->supported) {
+ weston_log("XKB extension version on the server is too old "
+ "(want %d.%d, has %d.%d)\n",
+ XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION,
+ use_ext_reply->serverMajor, use_ext_reply->serverMinor);
+ free(use_ext_reply);
+ return;
+ }
+ free(use_ext_reply);
+
+ pcf = xcb_xkb_per_client_flags(b->conn,
+ XCB_XKB_ID_USE_CORE_KBD,
+ XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT,
+ XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT,
+ 0,
+ 0,
+ 0);
+ pcf_reply = xcb_xkb_per_client_flags_reply(b->conn, pcf, NULL);
+ if (!pcf_reply ||
+ !(pcf_reply->value & XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT)) {
+ weston_log("failed to set XKB per-client flags, not using "
+ "detectable repeat\n");
+ free(pcf_reply);
+ return;
+ }
+ free(pcf_reply);
+
+ state = xcb_xkb_get_state(b->conn, XCB_XKB_ID_USE_CORE_KBD);
+ state_reply = xcb_xkb_get_state_reply(b->conn, state, NULL);
+ if (!state_reply) {
+ weston_log("failed to get initial XKB state\n");
+ return;
+ }
+
+ keyboard = weston_seat_get_keyboard(&b->core_seat);
+ xkb_state_update_mask(keyboard->xkb_state.state,
+ get_xkb_mod_mask(b, state_reply->baseMods),
+ get_xkb_mod_mask(b, state_reply->latchedMods),
+ get_xkb_mod_mask(b, state_reply->lockedMods),
+ 0,
+ 0,
+ state_reply->group);
+
+ free(state_reply);
+
+ xcb_change_window_attributes(b->conn, b->screen->root,
+ XCB_CW_EVENT_MASK, values);
+
+ b->has_xkb = 1;
+#endif
+}
+
+#ifdef HAVE_XCB_XKB
+static void
+update_xkb_keymap(struct x11_backend *b)
+{
+ struct xkb_keymap *keymap;
+
+ keymap = x11_backend_get_keymap(b);
+ if (!keymap) {
+ weston_log("failed to get XKB keymap\n");
+ return;
+ }
+ weston_seat_update_keymap(&b->core_seat, keymap);
+ xkb_keymap_unref(keymap);
+}
+#endif
+
+static int
+x11_input_create(struct x11_backend *b, int no_input)
+{
+ struct xkb_keymap *keymap;
+
+ weston_seat_init(&b->core_seat, b->compositor, "default");
+
+ if (no_input)
+ return 0;
+
+ weston_seat_init_pointer(&b->core_seat);
+
+ keymap = x11_backend_get_keymap(b);
+ if (weston_seat_init_keyboard(&b->core_seat, keymap) < 0)
+ return -1;
+ xkb_keymap_unref(keymap);
+
+ x11_backend_setup_xkb(b);
+
+ return 0;
+}
+
+static void
+x11_input_destroy(struct x11_backend *b)
+{
+ weston_seat_release(&b->core_seat);
+}
+
+static void
+x11_output_start_repaint_loop(struct weston_output *output)
+{
+ struct timespec ts;
+
+ weston_compositor_read_presentation_clock(output->compositor, &ts);
+ weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
+}
+
+static int
+x11_output_repaint_gl(struct weston_output *output_base,
+ pixman_region32_t *damage)
+{
+ struct x11_output *output = (struct x11_output *)output_base;
+ struct weston_compositor *ec = output->base.compositor;
+
+ ec->renderer->repaint_output(output_base, damage);
+
+ pixman_region32_subtract(&ec->primary_plane.damage,
+ &ec->primary_plane.damage, damage);
+
+ wl_event_source_timer_update(output->finish_frame_timer, 10);
+ return 0;
+}
+
+static void
+set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region)
+{
+ struct x11_output *output = (struct x11_output *)output_base;
+ struct weston_compositor *ec = output->base.compositor;
+ struct x11_backend *b = (struct x11_backend *)ec->backend;
+ pixman_region32_t transformed_region;
+ pixman_box32_t *rects;
+ xcb_rectangle_t *output_rects;
+ xcb_void_cookie_t cookie;
+ int nrects, i;
+ xcb_generic_error_t *err;
+
+ pixman_region32_init(&transformed_region);
+ pixman_region32_copy(&transformed_region, region);
+ pixman_region32_translate(&transformed_region,
+ -output_base->x, -output_base->y);
+ weston_transformed_region(output_base->width, output_base->height,
+ output_base->transform,
+ output_base->current_scale,
+ &transformed_region, &transformed_region);
+
+ rects = pixman_region32_rectangles(&transformed_region, &nrects);
+ output_rects = calloc(nrects, sizeof(xcb_rectangle_t));
+
+ if (output_rects == NULL) {
+ pixman_region32_fini(&transformed_region);
+ return;
+ }
+
+ for (i = 0; i < nrects; i++) {
+ output_rects[i].x = rects[i].x1;
+ output_rects[i].y = rects[i].y1;
+ output_rects[i].width = rects[i].x2 - rects[i].x1;
+ output_rects[i].height = rects[i].y2 - rects[i].y1;
+ }
+
+ pixman_region32_fini(&transformed_region);
+
+ cookie = xcb_set_clip_rectangles_checked(b->conn, XCB_CLIP_ORDERING_UNSORTED,
+ output->gc,
+ 0, 0, nrects,
+ output_rects);
+ err = xcb_request_check(b->conn, cookie);
+ if (err != NULL) {
+ weston_log("Failed to set clip rects, err: %d\n", err->error_code);
+ free(err);
+ }
+ free(output_rects);
+}
+
+
+static int
+x11_output_repaint_shm(struct weston_output *output_base,
+ pixman_region32_t *damage)
+{
+ struct x11_output *output = (struct x11_output *)output_base;
+ struct weston_compositor *ec = output->base.compositor;
+ struct x11_backend *b = (struct x11_backend *)ec->backend;
+ xcb_void_cookie_t cookie;
+ xcb_generic_error_t *err;
+
+ pixman_renderer_output_set_buffer(output_base, output->hw_surface);
+ ec->renderer->repaint_output(output_base, damage);
+
+ pixman_region32_subtract(&ec->primary_plane.damage,
+ &ec->primary_plane.damage, damage);
+ set_clip_for_output(output_base, damage);
+ cookie = xcb_shm_put_image_checked(b->conn, output->window, output->gc,
+ pixman_image_get_width(output->hw_surface),
+ pixman_image_get_height(output->hw_surface),
+ 0, 0,
+ pixman_image_get_width(output->hw_surface),
+ pixman_image_get_height(output->hw_surface),
+ 0, 0, output->depth, XCB_IMAGE_FORMAT_Z_PIXMAP,
+ 0, output->segment, 0);
+ err = xcb_request_check(b->conn, cookie);
+ if (err != NULL) {
+ weston_log("Failed to put shm image, err: %d\n", err->error_code);
+ free(err);
+ }
+
+ wl_event_source_timer_update(output->finish_frame_timer, 10);
+ return 0;
+}
+
+static int
+finish_frame_handler(void *data)
+{
+ struct x11_output *output = data;
+ struct timespec ts;
+
+ weston_compositor_read_presentation_clock(output->base.compositor, &ts);
+ weston_output_finish_frame(&output->base, &ts, 0);
+
+ return 1;
+}
+
+static void
+x11_output_deinit_shm(struct x11_backend *b, struct x11_output *output)
+{
+ xcb_void_cookie_t cookie;
+ xcb_generic_error_t *err;
+ xcb_free_gc(b->conn, output->gc);
+
+ pixman_image_unref(output->hw_surface);
+ output->hw_surface = NULL;
+ cookie = xcb_shm_detach_checked(b->conn, output->segment);
+ err = xcb_request_check(b->conn, cookie);
+ if (err) {
+ weston_log("xcb_shm_detach failed, error %d\n", err->error_code);
+ free(err);
+ }
+ shmdt(output->buf);
+}
+
+static void
+x11_output_destroy(struct weston_output *output_base)
+{
+ struct x11_output *output = (struct x11_output *)output_base;
+ struct x11_backend *backend =
+ (struct x11_backend *)output->base.compositor->backend;
+
+ wl_event_source_remove(output->finish_frame_timer);
+
+ if (backend->use_pixman) {
+ pixman_renderer_output_destroy(output_base);
+ x11_output_deinit_shm(backend, output);
+ } else
+ gl_renderer->output_destroy(output_base);
+
+ xcb_destroy_window(backend->conn, output->window);
+
+ weston_output_destroy(&output->base);
+
+ free(output);
+}
+
+static void
+x11_output_set_wm_protocols(struct x11_backend *b,
+ struct x11_output *output)
+{
+ xcb_atom_t list[1];
+
+ list[0] = b->atom.wm_delete_window;
+ xcb_change_property (b->conn,
+ XCB_PROP_MODE_REPLACE,
+ output->window,
+ b->atom.wm_protocols,
+ XCB_ATOM_ATOM,
+ 32,
+ ARRAY_LENGTH(list),
+ list);
+}
+
+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_backend *b,
+ struct x11_output *output, const char *filename)
+{
+ uint32_t *icon;
+ int32_t width, height;
+ pixman_image_t *image;
+
+ image = load_image(filename);
+ if (!image)
+ return;
+ width = pixman_image_get_width(image);
+ height = pixman_image_get_height(image);
+ icon = malloc(width * height * 4 + 8);
+ if (!icon) {
+ pixman_image_unref(image);
+ return;
+ }
+
+ icon[0] = width;
+ icon[1] = height;
+ memcpy(icon + 2, pixman_image_get_data(image), width * height * 4);
+ xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window,
+ b->atom.net_wm_icon, b->atom.cardinal, 32,
+ width * height + 2, icon);
+ free(icon);
+ pixman_image_unref(image);
+}
+
+static void
+x11_output_wait_for_map(struct x11_backend *b, struct x11_output *output)
+{
+ xcb_map_notify_event_t *map_notify;
+ xcb_configure_notify_event_t *configure_notify;
+ xcb_generic_event_t *event;
+ int mapped = 0, configured = 0;
+ uint8_t response_type;
+
+ /* This isn't the nicest way to do this. Ideally, we could
+ * just go back to the main loop and once we get the configure
+ * notify, we add the output to the compositor. While we do
+ * support output hotplug, we can't start up with no outputs.
+ * We could add the output and then resize once we get the
+ * configure notify, but we don't want to start up and
+ * immediately resize the output.
+ *
+ * Also, some window managers don't give us our final
+ * fullscreen size before map_notify, so if we don't get a
+ * configure_notify before map_notify, we just wait for the
+ * first one and hope that's our size. */
+
+ xcb_flush(b->conn);
+
+ while (!mapped || !configured) {
+ event = xcb_wait_for_event(b->conn);
+ response_type = event->response_type & ~0x80;
+
+ switch (response_type) {
+ case XCB_MAP_NOTIFY:
+ map_notify = (xcb_map_notify_event_t *) event;
+ if (map_notify->window == output->window)
+ mapped = 1;
+ break;
+
+ case XCB_CONFIGURE_NOTIFY:
+ configure_notify =
+ (xcb_configure_notify_event_t *) event;
+
+
+ if (configure_notify->width % output->scale != 0 ||
+ configure_notify->height % output->scale != 0)
+ weston_log("Resolution is not a multiple of screen size, rounding\n");
+ output->mode.width = configure_notify->width;
+ output->mode.height = configure_notify->height;
+ configured = 1;
+ break;
+ }
+ }
+}
+
+static xcb_visualtype_t *
+find_visual_by_id(xcb_screen_t *screen,
+ xcb_visualid_t id)
+{
+ xcb_depth_iterator_t i;
+ xcb_visualtype_iterator_t j;
+ for (i = xcb_screen_allowed_depths_iterator(screen);
+ i.rem;
+ xcb_depth_next(&i)) {
+ for (j = xcb_depth_visuals_iterator(i.data);
+ j.rem;
+ xcb_visualtype_next(&j)) {
+ if (j.data->visual_id == id)
+ return j.data;
+ }
+ }
+ return 0;
+}
+
+static uint8_t
+get_depth_of_visual(xcb_screen_t *screen,
+ xcb_visualid_t id)
+{
+ xcb_depth_iterator_t i;
+ xcb_visualtype_iterator_t j;
+ for (i = xcb_screen_allowed_depths_iterator(screen);
+ i.rem;
+ xcb_depth_next(&i)) {
+ for (j = xcb_depth_visuals_iterator(i.data);
+ j.rem;
+ xcb_visualtype_next(&j)) {
+ if (j.data->visual_id == id)
+ return i.data->depth;
+ }
+ }
+ return 0;
+}
+
+static int
+x11_output_init_shm(struct x11_backend *b, struct x11_output *output,
+ int width, int height)
+{
+ xcb_visualtype_t *visual_type;
+ xcb_screen_t *screen;
+ xcb_format_iterator_t fmt;
+ xcb_void_cookie_t cookie;
+ xcb_generic_error_t *err;
+ const xcb_query_extension_reply_t *ext;
+ int bitsperpixel = 0;
+ pixman_format_code_t pixman_format;
+
+ /* Check if SHM is available */
+ ext = xcb_get_extension_data(b->conn, &xcb_shm_id);
+ if (ext == NULL || !ext->present) {
+ /* SHM is missing */
+ weston_log("SHM extension is not available\n");
+ errno = ENOENT;
+ return -1;
+ }
+
+ screen = x11_compositor_get_default_screen(b);
+ visual_type = find_visual_by_id(screen, screen->root_visual);
+ if (!visual_type) {
+ weston_log("Failed to lookup visual for root window\n");
+ errno = ENOENT;
+ return -1;
+ }
+ weston_log("Found visual, bits per value: %d, red_mask: %.8x, green_mask: %.8x, blue_mask: %.8x\n",
+ visual_type->bits_per_rgb_value,
+ visual_type->red_mask,
+ visual_type->green_mask,
+ visual_type->blue_mask);
+ output->depth = get_depth_of_visual(screen, screen->root_visual);
+ weston_log("Visual depth is %d\n", output->depth);
+
+ for (fmt = xcb_setup_pixmap_formats_iterator(xcb_get_setup(b->conn));
+ fmt.rem;
+ xcb_format_next(&fmt)) {
+ if (fmt.data->depth == output->depth) {
+ bitsperpixel = fmt.data->bits_per_pixel;
+ break;
+ }
+ }
+ weston_log("Found format for depth %d, bpp: %d\n",
+ output->depth, bitsperpixel);
+
+ if (bitsperpixel == 32 &&
+ visual_type->red_mask == 0xff0000 &&
+ visual_type->green_mask == 0x00ff00 &&
+ visual_type->blue_mask == 0x0000ff) {
+ weston_log("Will use x8r8g8b8 format for SHM surfaces\n");
+ pixman_format = PIXMAN_x8r8g8b8;
+ } else if (bitsperpixel == 16 &&
+ visual_type->red_mask == 0x00f800 &&
+ visual_type->green_mask == 0x0007e0 &&
+ visual_type->blue_mask == 0x00001f) {
+ weston_log("Will use r5g6b5 format for SHM surfaces\n");
+ pixman_format = PIXMAN_r5g6b5;
+ } else {
+ weston_log("Can't find appropriate format for SHM pixmap\n");
+ errno = ENOTSUP;
+ return -1;
+ }
+
+
+ /* Create SHM segment and attach it */
+ output->shm_id = shmget(IPC_PRIVATE, width * height * (bitsperpixel / 8), IPC_CREAT | S_IRWXU);
+ if (output->shm_id == -1) {
+ weston_log("x11shm: failed to allocate SHM segment\n");
+ return -1;
+ }
+ output->buf = shmat(output->shm_id, NULL, 0 /* read/write */);
+ if (-1 == (long)output->buf) {
+ weston_log("x11shm: failed to attach SHM segment\n");
+ return -1;
+ }
+ output->segment = xcb_generate_id(b->conn);
+ cookie = xcb_shm_attach_checked(b->conn, output->segment, output->shm_id, 1);
+ err = xcb_request_check(b->conn, cookie);
+ if (err) {
+ weston_log("x11shm: xcb_shm_attach error %d, op code %d, resource id %d\n",
+ err->error_code, err->major_code, err->minor_code);
+ free(err);
+ return -1;
+ }
+
+ shmctl(output->shm_id, IPC_RMID, NULL);
+
+ /* Now create pixman image */
+ output->hw_surface = pixman_image_create_bits(pixman_format, width, height, output->buf,
+ width * (bitsperpixel / 8));
+
+ output->gc = xcb_generate_id(b->conn);
+ xcb_create_gc(b->conn, output->gc, output->window, 0, NULL);
+
+ return 0;
+}
+
+static struct x11_output *
+x11_backend_create_output(struct x11_backend *b, int x, int y,
+ int width, int height, int fullscreen,
+ int no_input, char *configured_name,
+ uint32_t transform, int32_t scale)
+{
+ static const char name[] = "Weston Compositor";
+ static const char class[] = "weston-1\0Weston Compositor";
+ char title[32];
+ struct x11_output *output;
+ xcb_screen_t *screen;
+ struct wm_normal_hints normal_hints;
+ struct wl_event_loop *loop;
+ int output_width, output_height, width_mm, height_mm;
+ int ret;
+ uint32_t mask = XCB_CW_EVENT_MASK | XCB_CW_CURSOR;
+ xcb_atom_t atom_list[1];
+ uint32_t values[2] = {
+ XCB_EVENT_MASK_EXPOSURE |
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY,
+ 0
+ };
+
+ output_width = width * scale;
+ output_height = height * scale;
+
+ if (configured_name)
+ sprintf(title, "%s - %s", name, configured_name);
+ else
+ strcpy(title, name);
+
+ if (!no_input)
+ values[0] |=
+ 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_ENTER_WINDOW |
+ XCB_EVENT_MASK_LEAVE_WINDOW |
+ XCB_EVENT_MASK_KEYMAP_STATE |
+ XCB_EVENT_MASK_FOCUS_CHANGE;
+
+ output = zalloc(sizeof *output);
+ if (output == NULL) {
+ perror("zalloc");
+ return NULL;
+ }
+
+ output->mode.flags =
+ WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+
+ output->mode.width = output_width;
+ output->mode.height = output_height;
+ output->mode.refresh = 60000;
+ output->scale = scale;
+ wl_list_init(&output->base.mode_list);
+ wl_list_insert(&output->base.mode_list, &output->mode.link);
+
+ values[1] = b->null_cursor;
+ output->window = xcb_generate_id(b->conn);
+ screen = x11_compositor_get_default_screen(b);
+ xcb_create_window(b->conn,
+ XCB_COPY_FROM_PARENT,
+ output->window,
+ screen->root,
+ 0, 0,
+ output_width, output_height,
+ 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ screen->root_visual,
+ mask, values);
+
+ if (fullscreen) {
+ atom_list[0] = b->atom.net_wm_state_fullscreen;
+ xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE,
+ output->window,
+ b->atom.net_wm_state,
+ XCB_ATOM_ATOM, 32,
+ ARRAY_LENGTH(atom_list), atom_list);
+ } else {
+ /* 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 = output_width;
+ normal_hints.min_height = output_height;
+ normal_hints.max_width = output_width;
+ normal_hints.max_height = output_height;
+ xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window,
+ b->atom.wm_normal_hints,
+ b->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(b->conn, XCB_PROP_MODE_REPLACE, output->window,
+ b->atom.net_wm_name, b->atom.utf8_string, 8,
+ strlen(title), title);
+ xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window,
+ b->atom.wm_class, b->atom.string, 8,
+ sizeof class, class);
+
+ x11_output_set_icon(b, output, DATADIR "/weston/wayland.png");
+
+ x11_output_set_wm_protocols(b, output);
+
+ xcb_map_window(b->conn, output->window);
+
+ if (fullscreen)
+ x11_output_wait_for_map(b, output);
+
+ output->base.start_repaint_loop = x11_output_start_repaint_loop;
+ if (b->use_pixman)
+ output->base.repaint = x11_output_repaint_shm;
+ else
+ output->base.repaint = x11_output_repaint_gl;
+ output->base.destroy = x11_output_destroy;
+ output->base.assign_planes = NULL;
+ output->base.set_backlight = NULL;
+ output->base.set_dpms = NULL;
+ output->base.switch_mode = NULL;
+ output->base.current_mode = &output->mode;
+ output->base.make = "weston-X11";
+ output->base.model = "none";
+
+ if (configured_name)
+ output->base.name = strdup(configured_name);
+
+ width_mm = width * b->screen->width_in_millimeters /
+ b->screen->width_in_pixels;
+ height_mm = height * b->screen->height_in_millimeters /
+ b->screen->height_in_pixels;
+ weston_output_init(&output->base, b->compositor,
+ x, y, width_mm, height_mm, transform, scale);
+
+ if (b->use_pixman) {
+ if (x11_output_init_shm(b, output,
+ output->mode.width,
+ output->mode.height) < 0) {
+ weston_log("Failed to initialize SHM for the X11 output\n");
+ return NULL;
+ }
+ if (pixman_renderer_output_create(&output->base) < 0) {
+ weston_log("Failed to create pixman renderer for output\n");
+ x11_output_deinit_shm(b, output);
+ return NULL;
+ }
+ } else {
+ /* eglCreatePlatformWindowSurfaceEXT takes a Window*
+ * but eglCreateWindowSurface takes a Window. */
+ Window xid = (Window) output->window;
+
+ ret = gl_renderer->output_create(&output->base,
+ (EGLNativeWindowType) output->window,
+ &xid,
+ gl_renderer->opaque_attribs,
+ NULL,
+ 0);
+ if (ret < 0)
+ return NULL;
+ }
+
+ loop = wl_display_get_event_loop(b->compositor->wl_display);
+ output->finish_frame_timer =
+ wl_event_loop_add_timer(loop, finish_frame_handler, output);
+
+ weston_compositor_add_output(b->compositor, &output->base);
+
+ weston_log("x11 output %dx%d, window id %d\n",
+ width, height, output->window);
+
+ return output;
+}
+
+static struct x11_output *
+x11_backend_find_output(struct x11_backend *b, xcb_window_t window)
+{
+ struct x11_output *output;
+
+ wl_list_for_each(output, &b->compositor->output_list, base.link) {
+ if (output->window == window)
+ return output;
+ }
+
+ return NULL;
+}
+
+static void
+x11_backend_delete_window(struct x11_backend *b, xcb_window_t window)
+{
+ struct x11_output *output;
+
+ output = x11_backend_find_output(b, window);
+ if (output)
+ x11_output_destroy(&output->base);
+
+ xcb_flush(b->conn);
+
+ if (wl_list_empty(&b->compositor->output_list))
+ weston_compositor_exit(b->compositor);
+}
+
+static void delete_cb(void *data)
+{
+ struct window_delete_data *wd = data;
+
+ x11_backend_delete_window(wd->backend, wd->window);
+ free(wd);
+}
+
+#ifdef HAVE_XCB_XKB
+static void
+update_xkb_state(struct x11_backend *b, xcb_xkb_state_notify_event_t *state)
+{
+ struct weston_keyboard *keyboard =
+ weston_seat_get_keyboard(&b->core_seat);
+
+ xkb_state_update_mask(keyboard->xkb_state.state,
+ get_xkb_mod_mask(b, state->baseMods),
+ get_xkb_mod_mask(b, state->latchedMods),
+ get_xkb_mod_mask(b, state->lockedMods),
+ 0,
+ 0,
+ state->group);
+
+ notify_modifiers(&b->core_seat,
+ wl_display_next_serial(b->compositor->wl_display));
+}
+#endif
+
+/**
+ * This is monumentally unpleasant. If we don't have XCB-XKB bindings,
+ * the best we can do (given that XCB also lacks XI2 support), is to take
+ * the state from the core key events. Unfortunately that only gives us
+ * the effective (i.e. union of depressed/latched/locked) state, and we
+ * need the granularity.
+ *
+ * So we still update the state with every key event we see, but also use
+ * the state field from X11 events as a mask so we don't get any stuck
+ * modifiers.
+ */
+static void
+update_xkb_state_from_core(struct x11_backend *b, uint16_t x11_mask)
+{
+ uint32_t mask = get_xkb_mod_mask(b, x11_mask);
+ struct weston_keyboard *keyboard
+ = weston_seat_get_keyboard(&b->core_seat);
+
+ xkb_state_update_mask(keyboard->xkb_state.state,
+ keyboard->modifiers.mods_depressed & mask,
+ keyboard->modifiers.mods_latched & mask,
+ keyboard->modifiers.mods_locked & mask,
+ 0,
+ 0,
+ (x11_mask >> 13) & 3);
+ notify_modifiers(&b->core_seat,
+ wl_display_next_serial(b->compositor->wl_display));
+}
+
+static void
+x11_backend_deliver_button_event(struct x11_backend *b,
+ xcb_generic_event_t *event, int state)
+{
+ xcb_button_press_event_t *button_event =
+ (xcb_button_press_event_t *) event;
+ uint32_t button;
+ struct x11_output *output;
+ struct weston_pointer_axis_event weston_event;
+
+ output = x11_backend_find_output(b, button_event->event);
+ if (!output)
+ return;
+
+ if (state)
+ xcb_grab_pointer(b->conn, 0, output->window,
+ XCB_EVENT_MASK_BUTTON_PRESS |
+ XCB_EVENT_MASK_BUTTON_RELEASE |
+ XCB_EVENT_MASK_POINTER_MOTION |
+ XCB_EVENT_MASK_ENTER_WINDOW |
+ XCB_EVENT_MASK_LEAVE_WINDOW,
+ XCB_GRAB_MODE_ASYNC,
+ XCB_GRAB_MODE_ASYNC,
+ output->window, XCB_CURSOR_NONE,
+ button_event->time);
+ else
+ xcb_ungrab_pointer(b->conn, button_event->time);
+
+ if (!b->has_xkb)
+ update_xkb_state_from_core(b, button_event->state);
+
+ switch (button_event->detail) {
+ case 1:
+ button = BTN_LEFT;
+ break;
+ case 2:
+ button = BTN_MIDDLE;
+ break;
+ case 3:
+ button = BTN_RIGHT;
+ break;
+ case 4:
+ /* Axis are measured in pixels, but the xcb events are discrete
+ * steps. Therefore move the axis by some pixels every step. */
+ if (state) {
+ weston_event.value = -DEFAULT_AXIS_STEP_DISTANCE;
+ weston_event.discrete = -1;
+ weston_event.has_discrete = true;
+ weston_event.axis =
+ WL_POINTER_AXIS_VERTICAL_SCROLL;
+ notify_axis(&b->core_seat,
+ weston_compositor_get_time(),
+ &weston_event);
+ notify_pointer_frame(&b->core_seat);
+ }
+ return;
+ case 5:
+ if (state) {
+ weston_event.value = DEFAULT_AXIS_STEP_DISTANCE;
+ weston_event.discrete = 1;
+ weston_event.has_discrete = true;
+ weston_event.axis =
+ WL_POINTER_AXIS_VERTICAL_SCROLL;
+ notify_axis(&b->core_seat,
+ weston_compositor_get_time(),
+ &weston_event);
+ notify_pointer_frame(&b->core_seat);
+ }
+ return;
+ case 6:
+ if (state) {
+ weston_event.value = -DEFAULT_AXIS_STEP_DISTANCE;
+ weston_event.discrete = -1;
+ weston_event.has_discrete = true;
+ weston_event.axis =
+ WL_POINTER_AXIS_HORIZONTAL_SCROLL;
+ notify_axis(&b->core_seat,
+ weston_compositor_get_time(),
+ &weston_event);
+ notify_pointer_frame(&b->core_seat);
+ }
+ return;
+ case 7:
+ if (state) {
+ weston_event.value = DEFAULT_AXIS_STEP_DISTANCE;
+ weston_event.discrete = 1;
+ weston_event.has_discrete = true;
+ weston_event.axis =
+ WL_POINTER_AXIS_HORIZONTAL_SCROLL;
+ notify_axis(&b->core_seat,
+ weston_compositor_get_time(),
+ &weston_event);
+ notify_pointer_frame(&b->core_seat);
+ }
+ return;
+ default:
+ button = button_event->detail + BTN_SIDE - 8;
+ break;
+ }
+
+ notify_button(&b->core_seat,
+ weston_compositor_get_time(), button,
+ state ? WL_POINTER_BUTTON_STATE_PRESSED :
+ WL_POINTER_BUTTON_STATE_RELEASED);
+ notify_pointer_frame(&b->core_seat);
+}
+
+static void
+x11_backend_deliver_motion_event(struct x11_backend *b,
+ xcb_generic_event_t *event)
+{
+ struct x11_output *output;
+ double x, y;
+ struct weston_pointer_motion_event motion_event = { 0 };
+ xcb_motion_notify_event_t *motion_notify =
+ (xcb_motion_notify_event_t *) event;
+
+ if (!b->has_xkb)
+ update_xkb_state_from_core(b, motion_notify->state);
+ output = x11_backend_find_output(b, motion_notify->event);
+ if (!output)
+ return;
+
+ weston_output_transform_coordinate(&output->base,
+ motion_notify->event_x,
+ motion_notify->event_y,
+ &x, &y);
+
+ motion_event = (struct weston_pointer_motion_event) {
+ .mask = WESTON_POINTER_MOTION_REL,
+ .dx = x - b->prev_x,
+ .dy = y - b->prev_y
+ };
+
+ notify_motion(&b->core_seat, weston_compositor_get_time(),
+ &motion_event);
+ notify_pointer_frame(&b->core_seat);
+
+ b->prev_x = x;
+ b->prev_y = y;
+}
+
+static void
+x11_backend_deliver_enter_event(struct x11_backend *b,
+ xcb_generic_event_t *event)
+{
+ struct x11_output *output;
+ double x, y;
+
+ xcb_enter_notify_event_t *enter_notify =
+ (xcb_enter_notify_event_t *) event;
+ if (enter_notify->state >= Button1Mask)
+ return;
+ if (!b->has_xkb)
+ update_xkb_state_from_core(b, enter_notify->state);
+ output = x11_backend_find_output(b, enter_notify->event);
+ if (!output)
+ return;
+
+ weston_output_transform_coordinate(&output->base,
+ enter_notify->event_x,
+ enter_notify->event_y, &x, &y);
+
+ notify_pointer_focus(&b->core_seat, &output->base, x, y);
+
+ b->prev_x = x;
+ b->prev_y = y;
+}
+
+static int
+x11_backend_next_event(struct x11_backend *b,
+ xcb_generic_event_t **event, uint32_t mask)
+{
+ if (mask & WL_EVENT_READABLE) {
+ *event = xcb_poll_for_event(b->conn);
+ } else {
+#ifdef HAVE_XCB_POLL_FOR_QUEUED_EVENT
+ *event = xcb_poll_for_queued_event(b->conn);
+#else
+ *event = xcb_poll_for_event(b->conn);
+#endif
+ }
+
+ return *event != NULL;
+}
+
+static int
+x11_backend_handle_event(int fd, uint32_t mask, void *data)
+{
+ struct x11_backend *b = data;
+ struct x11_output *output;
+ xcb_generic_event_t *event, *prev;
+ xcb_client_message_event_t *client_message;
+ 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_expose_event_t *expose;
+ xcb_atom_t atom;
+ xcb_window_t window;
+ uint32_t *k;
+ uint32_t i, set;
+ uint8_t response_type;
+ int count;
+
+ prev = NULL;
+ count = 0;
+ while (x11_backend_next_event(b, &event, mask)) {
+ response_type = event->response_type & ~0x80;
+
+ switch (prev ? prev->response_type & ~0x80 : 0x80) {
+ case XCB_KEY_RELEASE:
+ /* Suppress key repeat events; this is only used if we
+ * don't have XCB XKB support. */
+ key_release = (xcb_key_press_event_t *) prev;
+ key_press = (xcb_key_press_event_t *) event;
+ if (response_type == 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. */
+ update_xkb_state_from_core(b, key_release->state);
+ notify_key(&b->core_seat,
+ weston_compositor_get_time(),
+ key_release->detail - 8,
+ WL_KEYBOARD_KEY_STATE_RELEASED,
+ STATE_UPDATE_AUTOMATIC);
+ free(prev);
+ prev = NULL;
+ break;
+ }
+
+ case XCB_FOCUS_IN:
+ assert(response_type == XCB_KEYMAP_NOTIFY);
+ keymap_notify = (xcb_keymap_notify_event_t *) event;
+ b->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(&b->keys, sizeof *k);
+ *k = i;
+ }
+ }
+
+ /* Unfortunately the state only comes with the enter
+ * event, rather than with the focus event. I'm not
+ * sure of the exact semantics around it and whether
+ * we can ensure that we get both? */
+ notify_keyboard_focus_in(&b->core_seat, &b->keys,
+ STATE_UPDATE_AUTOMATIC);
+
+ free(prev);
+ prev = NULL;
+ break;
+
+ default:
+ /* No previous event held */
+ break;
+ }
+
+ switch (response_type) {
+ case XCB_KEY_PRESS:
+ key_press = (xcb_key_press_event_t *) event;
+ if (!b->has_xkb)
+ update_xkb_state_from_core(b, key_press->state);
+ notify_key(&b->core_seat,
+ weston_compositor_get_time(),
+ key_press->detail - 8,
+ WL_KEYBOARD_KEY_STATE_PRESSED,
+ b->has_xkb ? STATE_UPDATE_NONE :
+ STATE_UPDATE_AUTOMATIC);
+ break;
+ case XCB_KEY_RELEASE:
+ /* If we don't have XKB, we need to use the lame
+ * autorepeat detection above. */
+ if (!b->has_xkb) {
+ prev = event;
+ break;
+ }
+ key_release = (xcb_key_press_event_t *) event;
+ notify_key(&b->core_seat,
+ weston_compositor_get_time(),
+ key_release->detail - 8,
+ WL_KEYBOARD_KEY_STATE_RELEASED,
+ STATE_UPDATE_NONE);
+ break;
+ case XCB_BUTTON_PRESS:
+ x11_backend_deliver_button_event(b, event, 1);
+ break;
+ case XCB_BUTTON_RELEASE:
+ x11_backend_deliver_button_event(b, event, 0);
+ break;
+ case XCB_MOTION_NOTIFY:
+ x11_backend_deliver_motion_event(b, event);
+ break;
+
+ case XCB_EXPOSE:
+ expose = (xcb_expose_event_t *) event;
+ output = x11_backend_find_output(b, expose->window);
+ if (!output)
+ break;
+
+ weston_output_damage(&output->base);
+ weston_output_schedule_repaint(&output->base);
+ break;
+
+ case XCB_ENTER_NOTIFY:
+ x11_backend_deliver_enter_event(b, event);
+ break;
+
+ case XCB_LEAVE_NOTIFY:
+ enter_notify = (xcb_enter_notify_event_t *) event;
+ if (enter_notify->state >= Button1Mask)
+ break;
+ if (!b->has_xkb)
+ update_xkb_state_from_core(b, enter_notify->state);
+ notify_pointer_focus(&b->core_seat, NULL, 0, 0);
+ break;
+
+ case XCB_CLIENT_MESSAGE:
+ client_message = (xcb_client_message_event_t *) event;
+ atom = client_message->data.data32[0];
+ window = client_message->window;
+ if (atom == b->atom.wm_delete_window) {
+ struct wl_event_loop *loop;
+ struct window_delete_data *data = malloc(sizeof *data);
+
+ /* if malloc failed we should at least try to
+ * delete the window, even if it may result in
+ * a crash.
+ */
+ if (!data) {
+ x11_backend_delete_window(b, window);
+ break;
+ }
+ data->backend = b;
+ data->window = window;
+ loop = wl_display_get_event_loop(b->compositor->wl_display);
+ wl_event_loop_add_idle(loop, delete_cb, data);
+ }
+ 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_out(&b->core_seat);
+ break;
+
+ default:
+ break;
+ }
+
+#ifdef HAVE_XCB_XKB
+ if (b->has_xkb) {
+ if (response_type == b->xkb_event_base) {
+ xcb_xkb_state_notify_event_t *state =
+ (xcb_xkb_state_notify_event_t *) event;
+ if (state->xkbType == XCB_XKB_STATE_NOTIFY)
+ update_xkb_state(b, state);
+ } else if (response_type == XCB_PROPERTY_NOTIFY) {
+ xcb_property_notify_event_t *prop_notify =
+ (xcb_property_notify_event_t *) event;
+ if (prop_notify->window == b->screen->root &&
+ prop_notify->atom == b->atom.xkb_names &&
+ prop_notify->state == XCB_PROPERTY_NEW_VALUE)
+ update_xkb_keymap(b);
+ }
+ }
+#endif
+
+ count++;
+ if (prev != event)
+ free (event);
+ }
+
+ switch (prev ? prev->response_type & ~0x80 : 0x80) {
+ case XCB_KEY_RELEASE:
+ key_release = (xcb_key_press_event_t *) prev;
+ update_xkb_state_from_core(b, key_release->state);
+ notify_key(&b->core_seat,
+ weston_compositor_get_time(),
+ key_release->detail - 8,
+ WL_KEYBOARD_KEY_STATE_RELEASED,
+ STATE_UPDATE_AUTOMATIC);
+ free(prev);
+ prev = NULL;
+ break;
+ default:
+ break;
+ }
+
+ return count;
+}
+
+#define F(field) offsetof(struct x11_backend, field)
+
+static void
+x11_backend_get_resources(struct x11_backend *b)
+{
+ 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) },
+ { "_NET_SUPPORTING_WM_CHECK",
+ F(atom.net_supporting_wm_check) },
+ { "_NET_SUPPORTED", F(atom.net_supported) },
+ { "STRING", F(atom.string) },
+ { "UTF8_STRING", F(atom.utf8_string) },
+ { "CARDINAL", F(atom.cardinal) },
+ { "_XKB_RULES_NAMES", F(atom.xkb_names) },
+ };
+
+ xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
+ xcb_intern_atom_reply_t *reply;
+ xcb_pixmap_t pixmap;
+ xcb_gc_t gc;
+ unsigned int i;
+ uint8_t data[] = { 0, 0, 0, 0 };
+
+ for (i = 0; i < ARRAY_LENGTH(atoms); i++)
+ cookies[i] = xcb_intern_atom (b->conn, 0,
+ strlen(atoms[i].name),
+ atoms[i].name);
+
+ for (i = 0; i < ARRAY_LENGTH(atoms); i++) {
+ reply = xcb_intern_atom_reply (b->conn, cookies[i], NULL);
+ *(xcb_atom_t *) ((char *) b + atoms[i].offset) = reply->atom;
+ free(reply);
+ }
+
+ pixmap = xcb_generate_id(b->conn);
+ gc = xcb_generate_id(b->conn);
+ xcb_create_pixmap(b->conn, 1, pixmap, b->screen->root, 1, 1);
+ xcb_create_gc(b->conn, gc, pixmap, 0, NULL);
+ xcb_put_image(b->conn, XCB_IMAGE_FORMAT_XY_PIXMAP,
+ pixmap, gc, 1, 1, 0, 0, 0, 32, sizeof data, data);
+ b->null_cursor = xcb_generate_id(b->conn);
+ xcb_create_cursor (b->conn, b->null_cursor,
+ pixmap, pixmap, 0, 0, 0, 0, 0, 0, 1, 1);
+ xcb_free_gc(b->conn, gc);
+ xcb_free_pixmap(b->conn, pixmap);
+}
+
+static void
+x11_backend_get_wm_info(struct x11_backend *c)
+{
+ xcb_get_property_cookie_t cookie;
+ xcb_get_property_reply_t *reply;
+ xcb_atom_t *atom;
+ unsigned int i;
+
+ cookie = xcb_get_property(c->conn, 0, c->screen->root,
+ c->atom.net_supported,
+ XCB_ATOM_ATOM, 0, 1024);
+ reply = xcb_get_property_reply(c->conn, cookie, NULL);
+ if (reply == NULL)
+ return;
+
+ atom = (xcb_atom_t *) xcb_get_property_value(reply);
+
+ for (i = 0; i < reply->value_len; i++) {
+ if (atom[i] == c->atom.net_wm_state_fullscreen)
+ c->has_net_wm_state_fullscreen = 1;
+ }
+
+ free(reply);
+}
+
+static void
+x11_restore(struct weston_compositor *ec)
+{
+}
+
+static void
+x11_destroy(struct weston_compositor *ec)
+{
+ struct x11_backend *backend = (struct x11_backend *)ec->backend;
+
+ wl_event_source_remove(backend->xcb_source);
+ x11_input_destroy(backend);
+
+ weston_compositor_shutdown(ec); /* destroys outputs, too */
+
+ XCloseDisplay(backend->dpy);
+ free(backend);
+}
+
+static int
+init_gl_renderer(struct x11_backend *b)
+{
+ int ret;
+
+ gl_renderer = weston_load_module("gl-renderer.so",
+ "gl_renderer_interface");
+ if (!gl_renderer)
+ return -1;
+
+ ret = gl_renderer->create(b->compositor, EGL_PLATFORM_X11_KHR, (void *) b->dpy,
+ gl_renderer->opaque_attribs, NULL, 0);
+
+ return ret;
+}
+
+static struct x11_backend *
+x11_backend_create(struct weston_compositor *compositor,
+ struct weston_x11_backend_config *config)
+{
+ struct x11_backend *b;
+ struct x11_output *output;
+ struct wl_event_loop *loop;
+ int x = 0;
+ unsigned i;
+
+ b = zalloc(sizeof *b);
+ if (b == NULL)
+ return NULL;
+
+ b->compositor = compositor;
+ if (weston_compositor_set_presentation_clock_software(compositor) < 0)
+ goto err_free;
+
+ b->dpy = XOpenDisplay(NULL);
+ if (b->dpy == NULL)
+ goto err_free;
+
+ b->conn = XGetXCBConnection(b->dpy);
+ XSetEventQueueOwner(b->dpy, XCBOwnsEventQueue);
+
+ if (xcb_connection_has_error(b->conn))
+ goto err_xdisplay;
+
+ b->screen = x11_compositor_get_default_screen(b);
+ wl_array_init(&b->keys);
+
+ x11_backend_get_resources(b);
+ x11_backend_get_wm_info(b);
+
+ if (!b->has_net_wm_state_fullscreen && config->fullscreen) {
+ weston_log("Can not fullscreen without window manager support"
+ "(need _NET_WM_STATE_FULLSCREEN)\n");
+ config->fullscreen = 0;
+ }
+
+ b->use_pixman = config->use_pixman;
+ if (b->use_pixman) {
+ if (pixman_renderer_init(compositor) < 0) {
+ weston_log("Failed to initialize pixman renderer for X11 backend\n");
+ goto err_xdisplay;
+ }
+ }
+ else if (init_gl_renderer(b) < 0) {
+ goto err_xdisplay;
+ }
+ weston_log("Using %s renderer\n", config->use_pixman ? "pixman" : "gl");
+
+ b->base.destroy = x11_destroy;
+ b->base.restore = x11_restore;
+
+ if (x11_input_create(b, config->no_input) < 0) {
+ weston_log("Failed to create X11 input\n");
+ goto err_renderer;
+ }
+
+ for (i = 0; i < config->num_outputs; ++i) {
+ struct weston_x11_backend_output_config *output_iterator =
+ &config->outputs[i];
+
+ if (output_iterator->name == NULL) {
+ continue;
+ }
+
+ if (output_iterator->width < 1) {
+ weston_log("Invalid width \"%d\" for output %s\n",
+ output_iterator->width, output_iterator->name);
+ goto err_x11_input;
+ }
+
+ if (output_iterator->height < 1) {
+ weston_log("Invalid height \"%d\" for output %s\n",
+ output_iterator->height, output_iterator->name);
+ goto err_x11_input;
+ }
+
+ output = x11_backend_create_output(b,
+ x,
+ 0,
+ output_iterator->width,
+ output_iterator->height,
+ config->fullscreen,
+ config->no_input,
+ output_iterator->name,
+ output_iterator->transform,
+ output_iterator->scale);
+ if (output == NULL) {
+ weston_log("Failed to create configured x11 output\n");
+ goto err_x11_input;
+ }
+
+ x = pixman_region32_extents(&output->base.region)->x2;
+ }
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+ b->xcb_source =
+ wl_event_loop_add_fd(loop,
+ xcb_get_file_descriptor(b->conn),
+ WL_EVENT_READABLE,
+ x11_backend_handle_event, b);
+ wl_event_source_check(b->xcb_source);
+
+ if (compositor->renderer->import_dmabuf) {
+ if (linux_dmabuf_setup(compositor) < 0)
+ weston_log("Error: initializing dmabuf "
+ "support failed.\n");
+ }
+
+ compositor->backend = &b->base;
+
+ return b;
+
+err_x11_input:
+ x11_input_destroy(b);
+err_renderer:
+ compositor->renderer->destroy(compositor);
+err_xdisplay:
+ XCloseDisplay(b->dpy);
+err_free:
+ free(b);
+ return NULL;
+}
+
+static void
+config_init_to_defaults(struct weston_x11_backend_config *config)
+{
+}
+
+WL_EXPORT int
+backend_init(struct weston_compositor *compositor,
+ struct weston_backend_config *config_base)
+{
+ struct x11_backend *b;
+ struct weston_x11_backend_config config = {{ 0, }};
+
+ if (config_base == NULL ||
+ config_base->struct_version != WESTON_X11_BACKEND_CONFIG_VERSION ||
+ config_base->struct_size > sizeof(struct weston_x11_backend_config)) {
+ weston_log("X11 backend config structure is invalid\n");
+ return -1;
+ }
+
+ config_init_to_defaults(&config);
+ memcpy(&config, config_base, config_base->struct_size);
+
+ b = x11_backend_create(compositor, &config);
+ if (b == NULL)
+ return -1;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 2016 Benoit Gschwind
+ *
+ * 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.
+ */
+
+#ifndef WESTON_COMPOSITOR_X11_H
+#define WESTON_COMPOSITOR_X11_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "compositor.h"
+
+#define WESTON_X11_BACKEND_CONFIG_VERSION 1
+
+struct weston_x11_backend_output_config {
+ int width;
+ int height;
+ char *name;
+ uint32_t transform;
+ int32_t scale;
+};
+
+struct weston_x11_backend_config {
+ struct weston_backend_config base;
+
+ bool fullscreen;
+ bool no_input;
+
+ /** Whether to use the pixman renderer instead of the OpenGL ES renderer. */
+ bool use_pixman;
+
+ uint32_t num_outputs;
+ struct weston_x11_backend_output_config *outputs;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WESTON_COMPOSITOR_X11_H_ */
--- /dev/null
+/*
+ * Copyright © 2010-2011 Intel Corporation
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2012-2015 Collabora, Ltd.
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <fcntl.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/mman.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <math.h>
+#include <linux/input.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <sys/time.h>
+#include <time.h>
+#include <errno.h>
+
+#include "timeline.h"
+
+#include "compositor.h"
+#include "viewporter-server-protocol.h"
+#include "presentation-time-server-protocol.h"
+#include "shared/helpers.h"
+#include "shared/os-compatibility.h"
+#include "shared/timespec-util.h"
+#include "git-version.h"
+#include "version.h"
+
+#define DEFAULT_REPAINT_WINDOW 7 /* milliseconds */
+
+static void
+weston_output_transform_scale_init(struct weston_output *output,
+ uint32_t transform, uint32_t scale);
+
+static void
+weston_compositor_build_view_list(struct weston_compositor *compositor);
+
+static void weston_mode_switch_finish(struct weston_output *output,
+ int mode_changed,
+ int scale_changed)
+{
+ struct weston_seat *seat;
+ struct wl_resource *resource;
+ pixman_region32_t old_output_region;
+ int version;
+
+ pixman_region32_init(&old_output_region);
+ pixman_region32_copy(&old_output_region, &output->region);
+
+ /* Update output region and transformation matrix */
+ weston_output_transform_scale_init(output, output->transform, output->current_scale);
+
+ pixman_region32_init(&output->previous_damage);
+ pixman_region32_init_rect(&output->region, output->x, output->y,
+ output->width, output->height);
+
+ weston_output_update_matrix(output);
+
+ /* If a pointer falls outside the outputs new geometry, move it to its
+ * lower-right corner */
+ wl_list_for_each(seat, &output->compositor->seat_list, link) {
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+ int32_t x, y;
+
+ if (!pointer)
+ continue;
+
+ x = wl_fixed_to_int(pointer->x);
+ y = wl_fixed_to_int(pointer->y);
+
+ if (!pixman_region32_contains_point(&old_output_region,
+ x, y, NULL) ||
+ pixman_region32_contains_point(&output->region,
+ x, y, NULL))
+ continue;
+
+ if (x >= output->x + output->width)
+ x = output->x + output->width - 1;
+ if (y >= output->y + output->height)
+ y = output->y + output->height - 1;
+
+ pointer->x = wl_fixed_from_int(x);
+ pointer->y = wl_fixed_from_int(y);
+ }
+
+ pixman_region32_fini(&old_output_region);
+
+ if (!mode_changed && !scale_changed)
+ return;
+
+ /* notify clients of the changes */
+ wl_resource_for_each(resource, &output->resource_list) {
+ if (mode_changed) {
+ wl_output_send_mode(resource,
+ output->current_mode->flags,
+ output->current_mode->width,
+ output->current_mode->height,
+ output->current_mode->refresh);
+ }
+
+ version = wl_resource_get_version(resource);
+ if (version >= WL_OUTPUT_SCALE_SINCE_VERSION && scale_changed)
+ wl_output_send_scale(resource, output->current_scale);
+
+ if (version >= WL_OUTPUT_DONE_SINCE_VERSION)
+ wl_output_send_done(resource);
+ }
+}
+
+
+static void
+weston_compositor_reflow_outputs(struct weston_compositor *compositor,
+ struct weston_output *resized_output, int delta_width);
+
+WL_EXPORT int
+weston_output_mode_set_native(struct weston_output *output,
+ struct weston_mode *mode,
+ int32_t scale)
+{
+ int ret;
+ int mode_changed = 0, scale_changed = 0;
+ int32_t old_width;
+
+ if (!output->switch_mode)
+ return -1;
+
+ if (!output->original_mode) {
+ mode_changed = 1;
+ ret = output->switch_mode(output, mode);
+ if (ret < 0)
+ return ret;
+ if (output->current_scale != scale) {
+ scale_changed = 1;
+ output->current_scale = scale;
+ }
+ }
+
+ old_width = output->width;
+ output->native_mode = mode;
+ output->native_scale = scale;
+
+ weston_mode_switch_finish(output, mode_changed, scale_changed);
+
+ if (mode_changed || scale_changed) {
+ weston_compositor_reflow_outputs(output->compositor, output, output->width - old_width);
+
+ wl_signal_emit(&output->compositor->output_resized_signal, output);
+ }
+ return 0;
+}
+
+WL_EXPORT int
+weston_output_mode_switch_to_native(struct weston_output *output)
+{
+ int ret;
+ int mode_changed = 0, scale_changed = 0;
+
+ if (!output->switch_mode)
+ return -1;
+
+ if (!output->original_mode) {
+ weston_log("already in the native mode\n");
+ return -1;
+ }
+ /* the non fullscreen clients haven't seen a mode set since we
+ * switched into a temporary, so we need to notify them if the
+ * mode at that time is different from the native mode now.
+ */
+ mode_changed = (output->original_mode != output->native_mode);
+ scale_changed = (output->original_scale != output->native_scale);
+
+ ret = output->switch_mode(output, output->native_mode);
+ if (ret < 0)
+ return ret;
+
+ output->current_scale = output->native_scale;
+
+ output->original_mode = NULL;
+ output->original_scale = 0;
+
+ weston_mode_switch_finish(output, mode_changed, scale_changed);
+
+ return 0;
+}
+
+WL_EXPORT int
+weston_output_mode_switch_to_temporary(struct weston_output *output,
+ struct weston_mode *mode,
+ int32_t scale)
+{
+ int ret;
+
+ if (!output->switch_mode)
+ return -1;
+
+ /* original_mode is the last mode non full screen clients have seen,
+ * so we shouldn't change it if we already have one set.
+ */
+ if (!output->original_mode) {
+ output->original_mode = output->native_mode;
+ output->original_scale = output->native_scale;
+ }
+ ret = output->switch_mode(output, mode);
+ if (ret < 0)
+ return ret;
+
+ output->current_scale = scale;
+
+ weston_mode_switch_finish(output, 0, 0);
+
+ return 0;
+}
+
+static void
+region_init_infinite(pixman_region32_t *region)
+{
+ pixman_region32_init_rect(region, INT32_MIN, INT32_MIN,
+ UINT32_MAX, UINT32_MAX);
+}
+
+static struct weston_subsurface *
+weston_surface_to_subsurface(struct weston_surface *surface);
+
+WL_EXPORT struct weston_view *
+weston_view_create(struct weston_surface *surface)
+{
+ struct weston_view *view;
+
+ view = zalloc(sizeof *view);
+ if (view == NULL)
+ return NULL;
+
+ view->surface = surface;
+
+ /* Assign to surface */
+ wl_list_insert(&surface->views, &view->surface_link);
+
+ wl_signal_init(&view->destroy_signal);
+ wl_list_init(&view->link);
+ wl_list_init(&view->layer_link.link);
+
+ pixman_region32_init(&view->clip);
+
+ view->alpha = 1.0;
+ pixman_region32_init(&view->transform.opaque);
+
+ wl_list_init(&view->geometry.transformation_list);
+ wl_list_insert(&view->geometry.transformation_list,
+ &view->transform.position.link);
+ weston_matrix_init(&view->transform.position.matrix);
+ wl_list_init(&view->geometry.child_list);
+ pixman_region32_init(&view->geometry.scissor);
+ pixman_region32_init(&view->transform.boundingbox);
+ view->transform.dirty = 1;
+
+ return view;
+}
+
+struct weston_frame_callback {
+ struct wl_resource *resource;
+ struct wl_list link;
+};
+
+struct weston_presentation_feedback {
+ struct wl_resource *resource;
+
+ /* XXX: could use just wl_resource_get_link() instead */
+ struct wl_list link;
+
+ /* The per-surface feedback flags */
+ uint32_t psf_flags;
+};
+
+static void
+weston_presentation_feedback_discard(
+ struct weston_presentation_feedback *feedback)
+{
+ wp_presentation_feedback_send_discarded(feedback->resource);
+ wl_resource_destroy(feedback->resource);
+}
+
+static void
+weston_presentation_feedback_discard_list(struct wl_list *list)
+{
+ struct weston_presentation_feedback *feedback, *tmp;
+
+ wl_list_for_each_safe(feedback, tmp, list, link)
+ weston_presentation_feedback_discard(feedback);
+}
+
+static void
+weston_presentation_feedback_present(
+ struct weston_presentation_feedback *feedback,
+ struct weston_output *output,
+ uint32_t refresh_nsec,
+ const struct timespec *ts,
+ uint64_t seq,
+ uint32_t flags)
+{
+ struct wl_client *client = wl_resource_get_client(feedback->resource);
+ struct wl_resource *o;
+ uint64_t secs;
+
+ wl_resource_for_each(o, &output->resource_list) {
+ if (wl_resource_get_client(o) != client)
+ continue;
+
+ wp_presentation_feedback_send_sync_output(feedback->resource, o);
+ }
+
+ secs = ts->tv_sec;
+ wp_presentation_feedback_send_presented(feedback->resource,
+ secs >> 32, secs & 0xffffffff,
+ ts->tv_nsec,
+ refresh_nsec,
+ seq >> 32, seq & 0xffffffff,
+ flags | feedback->psf_flags);
+ wl_resource_destroy(feedback->resource);
+}
+
+static void
+weston_presentation_feedback_present_list(struct wl_list *list,
+ struct weston_output *output,
+ uint32_t refresh_nsec,
+ const struct timespec *ts,
+ uint64_t seq,
+ uint32_t flags)
+{
+ struct weston_presentation_feedback *feedback, *tmp;
+
+ assert(!(flags & WP_PRESENTATION_FEEDBACK_INVALID) ||
+ wl_list_empty(list));
+
+ wl_list_for_each_safe(feedback, tmp, list, link)
+ weston_presentation_feedback_present(feedback, output,
+ refresh_nsec, ts, seq,
+ flags);
+}
+
+static void
+surface_state_handle_buffer_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_surface_state *state =
+ container_of(listener, struct weston_surface_state,
+ buffer_destroy_listener);
+
+ state->buffer = NULL;
+}
+
+static void
+weston_surface_state_init(struct weston_surface_state *state)
+{
+ state->newly_attached = 0;
+ state->buffer = NULL;
+ state->buffer_destroy_listener.notify =
+ surface_state_handle_buffer_destroy;
+ state->sx = 0;
+ state->sy = 0;
+
+ pixman_region32_init(&state->damage_surface);
+ pixman_region32_init(&state->damage_buffer);
+ pixman_region32_init(&state->opaque);
+ region_init_infinite(&state->input);
+
+ wl_list_init(&state->frame_callback_list);
+ wl_list_init(&state->feedback_list);
+
+ state->buffer_viewport.buffer.transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ state->buffer_viewport.buffer.scale = 1;
+ state->buffer_viewport.buffer.src_width = wl_fixed_from_int(-1);
+ state->buffer_viewport.surface.width = -1;
+ state->buffer_viewport.changed = 0;
+}
+
+static void
+weston_surface_state_fini(struct weston_surface_state *state)
+{
+ struct weston_frame_callback *cb, *next;
+
+ wl_list_for_each_safe(cb, next,
+ &state->frame_callback_list, link)
+ wl_resource_destroy(cb->resource);
+
+ weston_presentation_feedback_discard_list(&state->feedback_list);
+
+ pixman_region32_fini(&state->input);
+ pixman_region32_fini(&state->opaque);
+ pixman_region32_fini(&state->damage_surface);
+ pixman_region32_fini(&state->damage_buffer);
+
+ if (state->buffer)
+ wl_list_remove(&state->buffer_destroy_listener.link);
+ state->buffer = NULL;
+}
+
+static void
+weston_surface_state_set_buffer(struct weston_surface_state *state,
+ struct weston_buffer *buffer)
+{
+ if (state->buffer == buffer)
+ return;
+
+ if (state->buffer)
+ wl_list_remove(&state->buffer_destroy_listener.link);
+ state->buffer = buffer;
+ if (state->buffer)
+ wl_signal_add(&state->buffer->destroy_signal,
+ &state->buffer_destroy_listener);
+}
+
+WL_EXPORT struct weston_surface *
+weston_surface_create(struct weston_compositor *compositor)
+{
+ struct weston_surface *surface;
+
+ surface = zalloc(sizeof *surface);
+ if (surface == NULL)
+ return NULL;
+
+ wl_signal_init(&surface->destroy_signal);
+
+ surface->compositor = compositor;
+ surface->ref_count = 1;
+
+ surface->buffer_viewport.buffer.transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ surface->buffer_viewport.buffer.scale = 1;
+ surface->buffer_viewport.buffer.src_width = wl_fixed_from_int(-1);
+ surface->buffer_viewport.surface.width = -1;
+
+ weston_surface_state_init(&surface->pending);
+
+ pixman_region32_init(&surface->damage);
+ pixman_region32_init(&surface->opaque);
+ region_init_infinite(&surface->input);
+
+ wl_list_init(&surface->views);
+
+ wl_list_init(&surface->frame_callback_list);
+ wl_list_init(&surface->feedback_list);
+
+ wl_list_init(&surface->subsurface_list);
+ wl_list_init(&surface->subsurface_list_pending);
+
+ weston_matrix_init(&surface->buffer_to_surface_matrix);
+ weston_matrix_init(&surface->surface_to_buffer_matrix);
+
+ return surface;
+}
+
+WL_EXPORT void
+weston_surface_set_color(struct weston_surface *surface,
+ float red, float green, float blue, float alpha)
+{
+ surface->compositor->renderer->surface_set_color(surface, red, green, blue, alpha);
+}
+
+WL_EXPORT void
+weston_view_to_global_float(struct weston_view *view,
+ float sx, float sy, float *x, float *y)
+{
+ if (view->transform.enabled) {
+ struct weston_vector v = { { sx, sy, 0.0f, 1.0f } };
+
+ weston_matrix_transform(&view->transform.matrix, &v);
+
+ if (fabsf(v.f[3]) < 1e-6) {
+ weston_log("warning: numerical instability in "
+ "%s(), divisor = %g\n", __func__,
+ v.f[3]);
+ *x = 0;
+ *y = 0;
+ return;
+ }
+
+ *x = v.f[0] / v.f[3];
+ *y = v.f[1] / v.f[3];
+ } else {
+ *x = sx + view->geometry.x;
+ *y = sy + view->geometry.y;
+ }
+}
+
+WL_EXPORT void
+weston_transformed_coord(int width, int height,
+ enum wl_output_transform transform,
+ int32_t scale,
+ float sx, float sy, float *bx, float *by)
+{
+ switch (transform) {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ default:
+ *bx = sx;
+ *by = sy;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ *bx = width - sx;
+ *by = sy;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ *bx = height - sy;
+ *by = sx;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ *bx = height - sy;
+ *by = width - sx;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ *bx = width - sx;
+ *by = height - sy;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ *bx = sx;
+ *by = height - sy;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ *bx = sy;
+ *by = width - sx;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ *bx = sy;
+ *by = sx;
+ break;
+ }
+
+ *bx *= scale;
+ *by *= scale;
+}
+
+WL_EXPORT pixman_box32_t
+weston_transformed_rect(int width, int height,
+ enum wl_output_transform transform,
+ int32_t scale,
+ pixman_box32_t rect)
+{
+ float x1, x2, y1, y2;
+
+ pixman_box32_t ret;
+
+ weston_transformed_coord(width, height, transform, scale,
+ rect.x1, rect.y1, &x1, &y1);
+ weston_transformed_coord(width, height, transform, scale,
+ rect.x2, rect.y2, &x2, &y2);
+
+ if (x1 <= x2) {
+ ret.x1 = x1;
+ ret.x2 = x2;
+ } else {
+ ret.x1 = x2;
+ ret.x2 = x1;
+ }
+
+ if (y1 <= y2) {
+ ret.y1 = y1;
+ ret.y2 = y2;
+ } else {
+ ret.y1 = y2;
+ ret.y2 = y1;
+ }
+
+ return ret;
+}
+
+/** Transform a region by a matrix, restricted to axis-aligned transformations
+ *
+ * Warning: This function does not work for projective, affine, or matrices
+ * that encode arbitrary rotations. Only 90-degree step rotations are
+ * supported.
+ */
+WL_EXPORT void
+weston_matrix_transform_region(pixman_region32_t *dest,
+ struct weston_matrix *matrix,
+ pixman_region32_t *src)
+{
+ pixman_box32_t *src_rects, *dest_rects;
+ int nrects, i;
+
+ src_rects = pixman_region32_rectangles(src, &nrects);
+ dest_rects = malloc(nrects * sizeof(*dest_rects));
+ if (!dest_rects)
+ return;
+
+ for (i = 0; i < nrects; i++) {
+ struct weston_vector vec1 = {{
+ src_rects[i].x1, src_rects[i].y1, 0, 1
+ }};
+ weston_matrix_transform(matrix, &vec1);
+ vec1.f[0] /= vec1.f[3];
+ vec1.f[1] /= vec1.f[3];
+
+ struct weston_vector vec2 = {{
+ src_rects[i].x2, src_rects[i].y2, 0, 1
+ }};
+ weston_matrix_transform(matrix, &vec2);
+ vec2.f[0] /= vec2.f[3];
+ vec2.f[1] /= vec2.f[3];
+
+ if (vec1.f[0] < vec2.f[0]) {
+ dest_rects[i].x1 = floor(vec1.f[0]);
+ dest_rects[i].x2 = ceil(vec2.f[0]);
+ } else {
+ dest_rects[i].x1 = floor(vec2.f[0]);
+ dest_rects[i].x2 = ceil(vec1.f[0]);
+ }
+
+ if (vec1.f[1] < vec2.f[1]) {
+ dest_rects[i].y1 = floor(vec1.f[1]);
+ dest_rects[i].y2 = ceil(vec2.f[1]);
+ } else {
+ dest_rects[i].y1 = floor(vec2.f[1]);
+ dest_rects[i].y2 = ceil(vec1.f[1]);
+ }
+ }
+
+ pixman_region32_clear(dest);
+ pixman_region32_init_rects(dest, dest_rects, nrects);
+ free(dest_rects);
+}
+
+WL_EXPORT void
+weston_transformed_region(int width, int height,
+ enum wl_output_transform transform,
+ int32_t scale,
+ pixman_region32_t *src, pixman_region32_t *dest)
+{
+ pixman_box32_t *src_rects, *dest_rects;
+ int nrects, i;
+
+ if (transform == WL_OUTPUT_TRANSFORM_NORMAL && scale == 1) {
+ if (src != dest)
+ pixman_region32_copy(dest, src);
+ return;
+ }
+
+ src_rects = pixman_region32_rectangles(src, &nrects);
+ dest_rects = malloc(nrects * sizeof(*dest_rects));
+ if (!dest_rects)
+ return;
+
+ if (transform == WL_OUTPUT_TRANSFORM_NORMAL) {
+ memcpy(dest_rects, src_rects, nrects * sizeof(*dest_rects));
+ } else {
+ for (i = 0; i < nrects; i++) {
+ switch (transform) {
+ default:
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ dest_rects[i].x1 = src_rects[i].x1;
+ dest_rects[i].y1 = src_rects[i].y1;
+ dest_rects[i].x2 = src_rects[i].x2;
+ dest_rects[i].y2 = src_rects[i].y2;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ dest_rects[i].x1 = height - src_rects[i].y2;
+ dest_rects[i].y1 = src_rects[i].x1;
+ dest_rects[i].x2 = height - src_rects[i].y1;
+ dest_rects[i].y2 = src_rects[i].x2;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ dest_rects[i].x1 = width - src_rects[i].x2;
+ dest_rects[i].y1 = height - src_rects[i].y2;
+ dest_rects[i].x2 = width - src_rects[i].x1;
+ dest_rects[i].y2 = height - src_rects[i].y1;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ dest_rects[i].x1 = src_rects[i].y1;
+ dest_rects[i].y1 = width - src_rects[i].x2;
+ dest_rects[i].x2 = src_rects[i].y2;
+ dest_rects[i].y2 = width - src_rects[i].x1;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ dest_rects[i].x1 = width - src_rects[i].x2;
+ dest_rects[i].y1 = src_rects[i].y1;
+ dest_rects[i].x2 = width - src_rects[i].x1;
+ dest_rects[i].y2 = src_rects[i].y2;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ dest_rects[i].x1 = height - src_rects[i].y2;
+ dest_rects[i].y1 = width - src_rects[i].x2;
+ dest_rects[i].x2 = height - src_rects[i].y1;
+ dest_rects[i].y2 = width - src_rects[i].x1;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ dest_rects[i].x1 = src_rects[i].x1;
+ dest_rects[i].y1 = height - src_rects[i].y2;
+ dest_rects[i].x2 = src_rects[i].x2;
+ dest_rects[i].y2 = height - src_rects[i].y1;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ dest_rects[i].x1 = src_rects[i].y1;
+ dest_rects[i].y1 = src_rects[i].x1;
+ dest_rects[i].x2 = src_rects[i].y2;
+ dest_rects[i].y2 = src_rects[i].x2;
+ break;
+ }
+ }
+ }
+
+ if (scale != 1) {
+ for (i = 0; i < nrects; i++) {
+ dest_rects[i].x1 *= scale;
+ dest_rects[i].x2 *= scale;
+ dest_rects[i].y1 *= scale;
+ dest_rects[i].y2 *= scale;
+ }
+ }
+
+ pixman_region32_clear(dest);
+ pixman_region32_init_rects(dest, dest_rects, nrects);
+ free(dest_rects);
+}
+
+static void
+viewport_surface_to_buffer(struct weston_surface *surface,
+ float sx, float sy, float *bx, float *by)
+{
+ struct weston_buffer_viewport *vp = &surface->buffer_viewport;
+ double src_width, src_height;
+ double src_x, src_y;
+
+ if (vp->buffer.src_width == wl_fixed_from_int(-1)) {
+ if (vp->surface.width == -1) {
+ *bx = sx;
+ *by = sy;
+ return;
+ }
+
+ src_x = 0.0;
+ src_y = 0.0;
+ src_width = surface->width_from_buffer;
+ src_height = surface->height_from_buffer;
+ } else {
+ src_x = wl_fixed_to_double(vp->buffer.src_x);
+ src_y = wl_fixed_to_double(vp->buffer.src_y);
+ src_width = wl_fixed_to_double(vp->buffer.src_width);
+ src_height = wl_fixed_to_double(vp->buffer.src_height);
+ }
+
+ *bx = sx * src_width / surface->width + src_x;
+ *by = sy * src_height / surface->height + src_y;
+}
+
+WL_EXPORT void
+weston_surface_to_buffer_float(struct weston_surface *surface,
+ float sx, float sy, float *bx, float *by)
+{
+ struct weston_buffer_viewport *vp = &surface->buffer_viewport;
+
+ /* first transform coordinates if the viewport is set */
+ viewport_surface_to_buffer(surface, sx, sy, bx, by);
+
+ weston_transformed_coord(surface->width_from_buffer,
+ surface->height_from_buffer,
+ vp->buffer.transform, vp->buffer.scale,
+ *bx, *by, bx, by);
+}
+
+/** Transform a rectangle from surface coordinates to buffer coordinates
+ *
+ * \param surface The surface to fetch wp_viewport and buffer transformation
+ * from.
+ * \param rect The rectangle to transform.
+ * \return The transformed rectangle.
+ *
+ * Viewport and buffer transformations can only do translation, scaling,
+ * and rotations in 90-degree steps. Therefore the only loss in the
+ * conversion is coordinate rounding.
+ *
+ * However, some coordinate rounding takes place as an intermediate
+ * step before the buffer scale factor is applied, so the rectangle
+ * boundary may not be exactly as expected.
+ *
+ * This is OK for damage tracking since a little extra coverage is
+ * not a problem.
+ */
+WL_EXPORT pixman_box32_t
+weston_surface_to_buffer_rect(struct weston_surface *surface,
+ pixman_box32_t rect)
+{
+ struct weston_buffer_viewport *vp = &surface->buffer_viewport;
+ float xf, yf;
+
+ /* first transform box coordinates if the viewport is set */
+ viewport_surface_to_buffer(surface, rect.x1, rect.y1, &xf, &yf);
+ rect.x1 = floorf(xf);
+ rect.y1 = floorf(yf);
+
+ viewport_surface_to_buffer(surface, rect.x2, rect.y2, &xf, &yf);
+ rect.x2 = ceilf(xf);
+ rect.y2 = ceilf(yf);
+
+ return weston_transformed_rect(surface->width_from_buffer,
+ surface->height_from_buffer,
+ vp->buffer.transform, vp->buffer.scale,
+ rect);
+}
+
+/** Transform a region from surface coordinates to buffer coordinates
+ *
+ * \param surface The surface to fetch wp_viewport and buffer transformation
+ * from.
+ * \param surface_region[in] The region in surface coordinates.
+ * \param buffer_region[out] The region converted to buffer coordinates.
+ *
+ * Buffer_region must be init'd, but will be completely overwritten.
+ *
+ * Viewport and buffer transformations can only do translation, scaling,
+ * and rotations in 90-degree steps. Therefore the only loss in the
+ * conversion is from the coordinate rounding that takes place in
+ * \ref weston_surface_to_buffer_rect.
+ */
+WL_EXPORT void
+weston_surface_to_buffer_region(struct weston_surface *surface,
+ pixman_region32_t *surface_region,
+ pixman_region32_t *buffer_region)
+{
+ pixman_box32_t *src_rects, *dest_rects;
+ int nrects, i;
+
+ src_rects = pixman_region32_rectangles(surface_region, &nrects);
+ dest_rects = malloc(nrects * sizeof(*dest_rects));
+ if (!dest_rects)
+ return;
+
+ for (i = 0; i < nrects; i++) {
+ dest_rects[i] = weston_surface_to_buffer_rect(surface,
+ src_rects[i]);
+ }
+
+ pixman_region32_fini(buffer_region);
+ pixman_region32_init_rects(buffer_region, dest_rects, nrects);
+ free(dest_rects);
+}
+
+WL_EXPORT void
+weston_view_move_to_plane(struct weston_view *view,
+ struct weston_plane *plane)
+{
+ if (view->plane == plane)
+ return;
+
+ weston_view_damage_below(view);
+ view->plane = plane;
+ weston_surface_damage(view->surface);
+}
+
+/** Inflict damage on the plane where the view is visible.
+ *
+ * \param view The view that causes the damage.
+ *
+ * If the view is currently on a plane (including the primary plane),
+ * take the view's boundingbox, subtract all the opaque views that cover it,
+ * and add the remaining region as damage to the plane. This corresponds
+ * to the damage inflicted to the plane if this view disappeared.
+ *
+ * A repaint is scheduled for this view.
+ *
+ * The region of all opaque views covering this view is stored in
+ * weston_view::clip and updated by view_accumulate_damage() during
+ * weston_output_repaint(). Specifically, that region matches the
+ * scenegraph as it was last painted.
+ */
+WL_EXPORT void
+weston_view_damage_below(struct weston_view *view)
+{
+ pixman_region32_t damage;
+
+ pixman_region32_init(&damage);
+ pixman_region32_subtract(&damage, &view->transform.boundingbox,
+ &view->clip);
+ if (view->plane)
+ pixman_region32_union(&view->plane->damage,
+ &view->plane->damage, &damage);
+ pixman_region32_fini(&damage);
+ weston_view_schedule_repaint(view);
+}
+
+/**
+ * \param es The surface
+ * \param mask The new set of outputs for the surface
+ *
+ * Sets the surface's set of outputs to the ones specified by
+ * the new output mask provided. Identifies the outputs that
+ * have changed, the posts enter and leave events for these
+ * outputs as appropriate.
+ */
+static void
+weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask)
+{
+ uint32_t different = es->output_mask ^ mask;
+ uint32_t entered = mask & different;
+ uint32_t left = es->output_mask & different;
+ struct weston_output *output;
+ struct wl_resource *resource = NULL;
+ struct wl_client *client;
+
+ es->output_mask = mask;
+ if (es->resource == NULL)
+ return;
+ if (different == 0)
+ return;
+
+ client = wl_resource_get_client(es->resource);
+
+ wl_list_for_each(output, &es->compositor->output_list, link) {
+ if (1u << output->id & different)
+ resource =
+ wl_resource_find_for_client(&output->resource_list,
+ client);
+ if (resource == NULL)
+ continue;
+ if (1u << output->id & entered)
+ wl_surface_send_enter(es->resource, resource);
+ if (1u << output->id & left)
+ wl_surface_send_leave(es->resource, resource);
+ }
+}
+
+/** Recalculate which output(s) the surface has views displayed on
+ *
+ * \param es The surface to remap to outputs
+ *
+ * Finds the output that is showing the largest amount of one
+ * of the surface's various views. This output becomes the
+ * surface's primary output for vsync and frame callback purposes.
+ *
+ * Also notes all outputs of all of the surface's views
+ * in the output_mask for the surface.
+ */
+static void
+weston_surface_assign_output(struct weston_surface *es)
+{
+ struct weston_output *new_output;
+ struct weston_view *view;
+ pixman_region32_t region;
+ uint32_t max, area, mask;
+ pixman_box32_t *e;
+
+ new_output = NULL;
+ max = 0;
+ mask = 0;
+ pixman_region32_init(®ion);
+ wl_list_for_each(view, &es->views, surface_link) {
+ if (!view->output)
+ continue;
+
+ pixman_region32_intersect(®ion, &view->transform.boundingbox,
+ &view->output->region);
+
+ e = pixman_region32_extents(®ion);
+ area = (e->x2 - e->x1) * (e->y2 - e->y1);
+
+ mask |= view->output_mask;
+
+ if (area >= max) {
+ new_output = view->output;
+ max = area;
+ }
+ }
+ pixman_region32_fini(®ion);
+
+ es->output = new_output;
+ weston_surface_update_output_mask(es, mask);
+}
+
+/** Recalculate which output(s) the view is displayed on
+ *
+ * \param ev The view to remap to outputs
+ *
+ * Identifies the set of outputs that the view is visible on,
+ * noting them into the output_mask. The output that the view
+ * is most visible on is set as the view's primary output.
+ *
+ * Also does the same for the view's surface. See
+ * weston_surface_assign_output().
+ */
+static void
+weston_view_assign_output(struct weston_view *ev)
+{
+ struct weston_compositor *ec = ev->surface->compositor;
+ struct weston_output *output, *new_output;
+ pixman_region32_t region;
+ uint32_t max, area, mask;
+ pixman_box32_t *e;
+
+ new_output = NULL;
+ max = 0;
+ mask = 0;
+ pixman_region32_init(®ion);
+ wl_list_for_each(output, &ec->output_list, link) {
+ if (output->destroying)
+ continue;
+
+ pixman_region32_intersect(®ion, &ev->transform.boundingbox,
+ &output->region);
+
+ e = pixman_region32_extents(®ion);
+ area = (e->x2 - e->x1) * (e->y2 - e->y1);
+
+ if (area > 0)
+ mask |= 1u << output->id;
+
+ if (area >= max) {
+ new_output = output;
+ max = area;
+ }
+ }
+ pixman_region32_fini(®ion);
+
+ ev->output = new_output;
+ ev->output_mask = mask;
+
+ weston_surface_assign_output(ev->surface);
+}
+
+static void
+weston_view_to_view_map(struct weston_view *from, struct weston_view *to,
+ int from_x, int from_y, int *to_x, int *to_y)
+{
+ float x, y;
+
+ weston_view_to_global_float(from, from_x, from_y, &x, &y);
+ weston_view_from_global_float(to, x, y, &x, &y);
+
+ *to_x = round(x);
+ *to_y = round(y);
+}
+
+static void
+weston_view_transfer_scissor(struct weston_view *from, struct weston_view *to)
+{
+ pixman_box32_t *a;
+ pixman_box32_t b;
+
+ a = pixman_region32_extents(&from->geometry.scissor);
+
+ weston_view_to_view_map(from, to, a->x1, a->y1, &b.x1, &b.y1);
+ weston_view_to_view_map(from, to, a->x2, a->y2, &b.x2, &b.y2);
+
+ pixman_region32_fini(&to->geometry.scissor);
+ pixman_region32_init_with_extents(&to->geometry.scissor, &b);
+}
+
+static void
+view_compute_bbox(struct weston_view *view, const pixman_box32_t *inbox,
+ pixman_region32_t *bbox)
+{
+ float min_x = HUGE_VALF, min_y = HUGE_VALF;
+ float max_x = -HUGE_VALF, max_y = -HUGE_VALF;
+ int32_t s[4][2] = {
+ { inbox->x1, inbox->y1 },
+ { inbox->x1, inbox->y2 },
+ { inbox->x2, inbox->y1 },
+ { inbox->x2, inbox->y2 },
+ };
+ float int_x, int_y;
+ int i;
+
+ if (inbox->x1 == inbox->x2 || inbox->y1 == inbox->y2) {
+ /* avoid rounding empty bbox to 1x1 */
+ pixman_region32_init(bbox);
+ return;
+ }
+
+ for (i = 0; i < 4; ++i) {
+ float x, y;
+ weston_view_to_global_float(view, s[i][0], s[i][1], &x, &y);
+ if (x < min_x)
+ min_x = x;
+ if (x > max_x)
+ max_x = x;
+ if (y < min_y)
+ min_y = y;
+ if (y > max_y)
+ max_y = y;
+ }
+
+ int_x = floorf(min_x);
+ int_y = floorf(min_y);
+ pixman_region32_init_rect(bbox, int_x, int_y,
+ ceilf(max_x) - int_x, ceilf(max_y) - int_y);
+}
+
+static void
+weston_view_update_transform_disable(struct weston_view *view)
+{
+ view->transform.enabled = 0;
+
+ /* round off fractions when not transformed */
+ view->geometry.x = roundf(view->geometry.x);
+ view->geometry.y = roundf(view->geometry.y);
+
+ /* Otherwise identity matrix, but with x and y translation. */
+ view->transform.position.matrix.type = WESTON_MATRIX_TRANSFORM_TRANSLATE;
+ view->transform.position.matrix.d[12] = view->geometry.x;
+ view->transform.position.matrix.d[13] = view->geometry.y;
+
+ view->transform.matrix = view->transform.position.matrix;
+
+ view->transform.inverse = view->transform.position.matrix;
+ view->transform.inverse.d[12] = -view->geometry.x;
+ view->transform.inverse.d[13] = -view->geometry.y;
+
+ pixman_region32_init_rect(&view->transform.boundingbox,
+ 0, 0,
+ view->surface->width,
+ view->surface->height);
+ if (view->geometry.scissor_enabled)
+ pixman_region32_intersect(&view->transform.boundingbox,
+ &view->transform.boundingbox,
+ &view->geometry.scissor);
+
+ pixman_region32_translate(&view->transform.boundingbox,
+ view->geometry.x, view->geometry.y);
+
+ if (view->alpha == 1.0) {
+ pixman_region32_copy(&view->transform.opaque,
+ &view->surface->opaque);
+ pixman_region32_translate(&view->transform.opaque,
+ view->geometry.x,
+ view->geometry.y);
+ }
+}
+
+static int
+weston_view_update_transform_enable(struct weston_view *view)
+{
+ struct weston_view *parent = view->geometry.parent;
+ struct weston_matrix *matrix = &view->transform.matrix;
+ struct weston_matrix *inverse = &view->transform.inverse;
+ struct weston_transform *tform;
+ pixman_region32_t surfregion;
+ const pixman_box32_t *surfbox;
+
+ view->transform.enabled = 1;
+
+ /* Otherwise identity matrix, but with x and y translation. */
+ view->transform.position.matrix.type = WESTON_MATRIX_TRANSFORM_TRANSLATE;
+ view->transform.position.matrix.d[12] = view->geometry.x;
+ view->transform.position.matrix.d[13] = view->geometry.y;
+
+ weston_matrix_init(matrix);
+ wl_list_for_each(tform, &view->geometry.transformation_list, link)
+ weston_matrix_multiply(matrix, &tform->matrix);
+
+ if (parent)
+ weston_matrix_multiply(matrix, &parent->transform.matrix);
+
+ if (weston_matrix_invert(inverse, matrix) < 0) {
+ /* Oops, bad total transformation, not invertible */
+ weston_log("error: weston_view %p"
+ " transformation not invertible.\n", view);
+ return -1;
+ }
+
+ pixman_region32_init_rect(&surfregion, 0, 0,
+ view->surface->width, view->surface->height);
+ if (view->geometry.scissor_enabled)
+ pixman_region32_intersect(&surfregion, &surfregion,
+ &view->geometry.scissor);
+ surfbox = pixman_region32_extents(&surfregion);
+
+ view_compute_bbox(view, surfbox, &view->transform.boundingbox);
+ pixman_region32_fini(&surfregion);
+
+ return 0;
+}
+
+static struct weston_layer *
+get_view_layer(struct weston_view *view)
+{
+ if (view->parent_view)
+ return get_view_layer(view->parent_view);
+ return view->layer_link.layer;
+}
+
+WL_EXPORT void
+weston_view_update_transform(struct weston_view *view)
+{
+ struct weston_view *parent = view->geometry.parent;
+ struct weston_layer *layer;
+ pixman_region32_t mask;
+
+ if (!view->transform.dirty)
+ return;
+
+ if (parent)
+ weston_view_update_transform(parent);
+
+ view->transform.dirty = 0;
+
+ weston_view_damage_below(view);
+
+ pixman_region32_fini(&view->transform.boundingbox);
+ pixman_region32_fini(&view->transform.opaque);
+ pixman_region32_init(&view->transform.opaque);
+
+ /* transform.position is always in transformation_list */
+ if (view->geometry.transformation_list.next ==
+ &view->transform.position.link &&
+ view->geometry.transformation_list.prev ==
+ &view->transform.position.link &&
+ !parent) {
+ weston_view_update_transform_disable(view);
+ } else {
+ if (weston_view_update_transform_enable(view) < 0)
+ weston_view_update_transform_disable(view);
+ }
+
+ layer = get_view_layer(view);
+ if (layer) {
+ pixman_region32_init_with_extents(&mask, &layer->mask);
+ pixman_region32_intersect(&view->transform.boundingbox,
+ &view->transform.boundingbox, &mask);
+ pixman_region32_intersect(&view->transform.opaque,
+ &view->transform.opaque, &mask);
+ pixman_region32_fini(&mask);
+ }
+
+ if (parent) {
+ if (parent->geometry.scissor_enabled) {
+ view->geometry.scissor_enabled = true;
+ weston_view_transfer_scissor(parent, view);
+ } else {
+ view->geometry.scissor_enabled = false;
+ }
+ }
+
+ weston_view_damage_below(view);
+
+ weston_view_assign_output(view);
+
+ wl_signal_emit(&view->surface->compositor->transform_signal,
+ view->surface);
+}
+
+WL_EXPORT void
+weston_view_geometry_dirty(struct weston_view *view)
+{
+ struct weston_view *child;
+
+ /*
+ * The invariant: if view->geometry.dirty, then all views
+ * in view->geometry.child_list have geometry.dirty too.
+ * Corollary: if not parent->geometry.dirty, then all ancestors
+ * are not dirty.
+ */
+
+ if (view->transform.dirty)
+ return;
+
+ view->transform.dirty = 1;
+
+ wl_list_for_each(child, &view->geometry.child_list,
+ geometry.parent_link)
+ weston_view_geometry_dirty(child);
+}
+
+WL_EXPORT void
+weston_view_to_global_fixed(struct weston_view *view,
+ wl_fixed_t vx, wl_fixed_t vy,
+ wl_fixed_t *x, wl_fixed_t *y)
+{
+ float xf, yf;
+
+ weston_view_to_global_float(view,
+ wl_fixed_to_double(vx),
+ wl_fixed_to_double(vy),
+ &xf, &yf);
+ *x = wl_fixed_from_double(xf);
+ *y = wl_fixed_from_double(yf);
+}
+
+WL_EXPORT void
+weston_view_from_global_float(struct weston_view *view,
+ float x, float y, float *vx, float *vy)
+{
+ if (view->transform.enabled) {
+ struct weston_vector v = { { x, y, 0.0f, 1.0f } };
+
+ weston_matrix_transform(&view->transform.inverse, &v);
+
+ if (fabsf(v.f[3]) < 1e-6) {
+ weston_log("warning: numerical instability in "
+ "weston_view_from_global(), divisor = %g\n",
+ v.f[3]);
+ *vx = 0;
+ *vy = 0;
+ return;
+ }
+
+ *vx = v.f[0] / v.f[3];
+ *vy = v.f[1] / v.f[3];
+ } else {
+ *vx = x - view->geometry.x;
+ *vy = y - view->geometry.y;
+ }
+}
+
+WL_EXPORT void
+weston_view_from_global_fixed(struct weston_view *view,
+ wl_fixed_t x, wl_fixed_t y,
+ wl_fixed_t *vx, wl_fixed_t *vy)
+{
+ float vxf, vyf;
+
+ weston_view_from_global_float(view,
+ wl_fixed_to_double(x),
+ wl_fixed_to_double(y),
+ &vxf, &vyf);
+ *vx = wl_fixed_from_double(vxf);
+ *vy = wl_fixed_from_double(vyf);
+}
+
+WL_EXPORT void
+weston_view_from_global(struct weston_view *view,
+ int32_t x, int32_t y, int32_t *vx, int32_t *vy)
+{
+ float vxf, vyf;
+
+ weston_view_from_global_float(view, x, y, &vxf, &vyf);
+ *vx = floorf(vxf);
+ *vy = floorf(vyf);
+}
+
+/**
+ * \param surface The surface to be repainted
+ *
+ * Marks the output(s) that the surface is shown on as needing to be
+ * repainted. See weston_output_schedule_repaint().
+ */
+WL_EXPORT void
+weston_surface_schedule_repaint(struct weston_surface *surface)
+{
+ struct weston_output *output;
+
+ wl_list_for_each(output, &surface->compositor->output_list, link)
+ if (surface->output_mask & (1u << output->id))
+ weston_output_schedule_repaint(output);
+}
+
+/**
+ * \param view The view to be repainted
+ *
+ * Marks the output(s) that the view is shown on as needing to be
+ * repainted. See weston_output_schedule_repaint().
+ */
+WL_EXPORT void
+weston_view_schedule_repaint(struct weston_view *view)
+{
+ struct weston_output *output;
+
+ wl_list_for_each(output, &view->surface->compositor->output_list, link)
+ if (view->output_mask & (1u << output->id))
+ weston_output_schedule_repaint(output);
+}
+
+/**
+ * XXX: This function does it the wrong way.
+ * surface->damage is the damage from the client, and causes
+ * surface_flush_damage() to copy pixels. No window management action can
+ * cause damage to the client-provided content, warranting re-upload!
+ *
+ * Instead of surface->damage, this function should record the damage
+ * with all the views for this surface to avoid extraneous texture
+ * uploads.
+ */
+WL_EXPORT void
+weston_surface_damage(struct weston_surface *surface)
+{
+ pixman_region32_union_rect(&surface->damage, &surface->damage,
+ 0, 0, surface->width,
+ surface->height);
+
+ weston_surface_schedule_repaint(surface);
+}
+
+WL_EXPORT void
+weston_view_set_position(struct weston_view *view, float x, float y)
+{
+ if (view->geometry.x == x && view->geometry.y == y)
+ return;
+
+ view->geometry.x = x;
+ view->geometry.y = y;
+ weston_view_geometry_dirty(view);
+}
+
+static void
+transform_parent_handle_parent_destroy(struct wl_listener *listener,
+ void *data)
+{
+ struct weston_view *view =
+ container_of(listener, struct weston_view,
+ geometry.parent_destroy_listener);
+
+ weston_view_set_transform_parent(view, NULL);
+}
+
+WL_EXPORT void
+weston_view_set_transform_parent(struct weston_view *view,
+ struct weston_view *parent)
+{
+ if (view->geometry.parent) {
+ wl_list_remove(&view->geometry.parent_destroy_listener.link);
+ wl_list_remove(&view->geometry.parent_link);
+
+ if (!parent)
+ view->geometry.scissor_enabled = false;
+ }
+
+ view->geometry.parent = parent;
+
+ view->geometry.parent_destroy_listener.notify =
+ transform_parent_handle_parent_destroy;
+ if (parent) {
+ wl_signal_add(&parent->destroy_signal,
+ &view->geometry.parent_destroy_listener);
+ wl_list_insert(&parent->geometry.child_list,
+ &view->geometry.parent_link);
+ }
+
+ weston_view_geometry_dirty(view);
+}
+
+/** Set a clip mask rectangle on a view
+ *
+ * \param view The view to set the clip mask on.
+ * \param x Top-left corner X coordinate of the clip rectangle.
+ * \param y Top-left corner Y coordinate of the clip rectangle.
+ * \param width Width of the clip rectangle, non-negative.
+ * \param height Height of the clip rectangle, non-negative.
+ *
+ * A shell may set a clip mask rectangle on a view. Everything outside
+ * the rectangle is cut away for input and output purposes: it is
+ * not drawn and cannot be hit by hit-test based input like pointer
+ * motion or touch-downs. Everything inside the rectangle will behave
+ * normally. Clients are unaware of clipping.
+ *
+ * The rectangle is set in surface-local coordinates. Setting a clip
+ * mask rectangle does not affect the view position, the view is positioned
+ * as it would be without a clip. The clip also does not change
+ * weston_surface::width,height.
+ *
+ * The clip mask rectangle is part of transformation inheritance
+ * (weston_view_set_transform_parent()). A clip set in the root of the
+ * transformation inheritance tree will affect all views in the tree.
+ * A clip can be set only on the root view. Attempting to set a clip
+ * on view that has a transformation parent will fail. Assigning a parent
+ * to a view that has a clip set will cause the clip to be forgotten.
+ *
+ * Because the clip mask is an axis-aligned rectangle, it poses restrictions
+ * on the additional transformations in the child views. These transformations
+ * may not rotate the coordinate axes, i.e., only translation and scaling
+ * are allowed. Violating this restriction causes the clipping to malfunction.
+ * Furthermore, using scaling may cause rounding errors in child clipping.
+ *
+ * The clip mask rectangle is not automatically adjusted based on
+ * wl_surface.attach dx and dy arguments.
+ *
+ * A clip mask rectangle can be set only if the compositor capability
+ * WESTON_CAP_VIEW_CLIP_MASK is present.
+ *
+ * This function sets the clip mask rectangle and schedules a repaint for
+ * the view.
+ */
+WL_EXPORT void
+weston_view_set_mask(struct weston_view *view,
+ int x, int y, int width, int height)
+{
+ struct weston_compositor *compositor = view->surface->compositor;
+
+ if (!(compositor->capabilities & WESTON_CAP_VIEW_CLIP_MASK)) {
+ weston_log("%s not allowed without capability!\n", __func__);
+ return;
+ }
+
+ if (view->geometry.parent) {
+ weston_log("view %p has a parent, clip forbidden!\n", view);
+ return;
+ }
+
+ if (width < 0 || height < 0) {
+ weston_log("%s: illegal args %d, %d, %d, %d\n", __func__,
+ x, y, width, height);
+ return;
+ }
+
+ pixman_region32_fini(&view->geometry.scissor);
+ pixman_region32_init_rect(&view->geometry.scissor, x, y, width, height);
+ view->geometry.scissor_enabled = true;
+ weston_view_geometry_dirty(view);
+ weston_view_schedule_repaint(view);
+}
+
+/** Remove the clip mask from a view
+ *
+ * \param view The view to remove the clip mask from.
+ *
+ * Removed the clip mask rectangle and schedules a repaint.
+ *
+ * \sa weston_view_set_mask
+ */
+WL_EXPORT void
+weston_view_set_mask_infinite(struct weston_view *view)
+{
+ view->geometry.scissor_enabled = false;
+ weston_view_geometry_dirty(view);
+ weston_view_schedule_repaint(view);
+}
+
+WL_EXPORT bool
+weston_view_is_mapped(struct weston_view *view)
+{
+ if (view->output)
+ return true;
+ else
+ return false;
+}
+
+WL_EXPORT bool
+weston_surface_is_mapped(struct weston_surface *surface)
+{
+ if (surface->output)
+ return true;
+ else
+ return false;
+}
+
+static void
+surface_set_size(struct weston_surface *surface, int32_t width, int32_t height)
+{
+ struct weston_view *view;
+
+ if (surface->width == width && surface->height == height)
+ return;
+
+ surface->width = width;
+ surface->height = height;
+
+ wl_list_for_each(view, &surface->views, surface_link)
+ weston_view_geometry_dirty(view);
+}
+
+WL_EXPORT void
+weston_surface_set_size(struct weston_surface *surface,
+ int32_t width, int32_t height)
+{
+ assert(!surface->resource);
+ surface_set_size(surface, width, height);
+}
+
+static int
+fixed_round_up_to_int(wl_fixed_t f)
+{
+ return wl_fixed_to_int(wl_fixed_from_int(1) - 1 + f);
+}
+
+static void
+convert_size_by_transform_scale(int32_t *width_out, int32_t *height_out,
+ int32_t width, int32_t height,
+ uint32_t transform,
+ int32_t scale)
+{
+ assert(scale > 0);
+
+ switch (transform) {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ case WL_OUTPUT_TRANSFORM_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ *width_out = width / scale;
+ *height_out = height / scale;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ *width_out = height / scale;
+ *height_out = width / scale;
+ break;
+ default:
+ assert(0 && "invalid transform");
+ }
+}
+
+static void
+weston_surface_calculate_size_from_buffer(struct weston_surface *surface)
+{
+ struct weston_buffer_viewport *vp = &surface->buffer_viewport;
+
+ if (!surface->buffer_ref.buffer) {
+ surface->width_from_buffer = 0;
+ surface->height_from_buffer = 0;
+ return;
+ }
+
+ convert_size_by_transform_scale(&surface->width_from_buffer,
+ &surface->height_from_buffer,
+ surface->buffer_ref.buffer->width,
+ surface->buffer_ref.buffer->height,
+ vp->buffer.transform,
+ vp->buffer.scale);
+}
+
+static void
+weston_surface_update_size(struct weston_surface *surface)
+{
+ struct weston_buffer_viewport *vp = &surface->buffer_viewport;
+ int32_t width, height;
+
+ width = surface->width_from_buffer;
+ height = surface->height_from_buffer;
+
+ if (width != 0 && vp->surface.width != -1) {
+ surface_set_size(surface,
+ vp->surface.width, vp->surface.height);
+ return;
+ }
+
+ if (width != 0 && vp->buffer.src_width != wl_fixed_from_int(-1)) {
+ int32_t w = fixed_round_up_to_int(vp->buffer.src_width);
+ int32_t h = fixed_round_up_to_int(vp->buffer.src_height);
+
+ surface_set_size(surface, w ?: 1, h ?: 1);
+ return;
+ }
+
+ surface_set_size(surface, width, height);
+}
+
+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 struct weston_view *
+weston_compositor_pick_view(struct weston_compositor *compositor,
+ wl_fixed_t x, wl_fixed_t y,
+ wl_fixed_t *vx, wl_fixed_t *vy)
+{
+ struct weston_view *view;
+ wl_fixed_t view_x, view_y;
+ int view_ix, view_iy;
+ int ix = wl_fixed_to_int(x);
+ int iy = wl_fixed_to_int(y);
+
+ wl_list_for_each(view, &compositor->view_list, link) {
+ if (!pixman_region32_contains_point(
+ &view->transform.boundingbox, ix, iy, NULL))
+ continue;
+
+ weston_view_from_global_fixed(view, x, y, &view_x, &view_y);
+ view_ix = wl_fixed_to_int(view_x);
+ view_iy = wl_fixed_to_int(view_y);
+
+ if (!pixman_region32_contains_point(&view->surface->input,
+ view_ix, view_iy, NULL))
+ continue;
+
+ if (view->geometry.scissor_enabled &&
+ !pixman_region32_contains_point(&view->geometry.scissor,
+ view_ix, view_iy, NULL))
+ continue;
+
+ *vx = view_x;
+ *vy = view_y;
+ return view;
+ }
+
+ *vx = wl_fixed_from_int(-1000000);
+ *vy = wl_fixed_from_int(-1000000);
+ return NULL;
+}
+
+static void
+weston_compositor_repick(struct weston_compositor *compositor)
+{
+ struct weston_seat *seat;
+
+ if (!compositor->session_active)
+ return;
+
+ wl_list_for_each(seat, &compositor->seat_list, link)
+ weston_seat_repick(seat);
+}
+
+WL_EXPORT void
+weston_view_unmap(struct weston_view *view)
+{
+ struct weston_seat *seat;
+
+ if (!weston_view_is_mapped(view))
+ return;
+
+ weston_view_damage_below(view);
+ view->output = NULL;
+ view->plane = NULL;
+ weston_layer_entry_remove(&view->layer_link);
+ wl_list_remove(&view->link);
+ wl_list_init(&view->link);
+ view->output_mask = 0;
+ weston_surface_assign_output(view->surface);
+
+ if (weston_surface_is_mapped(view->surface))
+ return;
+
+ wl_list_for_each(seat, &view->surface->compositor->seat_list, link) {
+ struct weston_touch *touch = weston_seat_get_touch(seat);
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+ struct weston_keyboard *keyboard =
+ weston_seat_get_keyboard(seat);
+
+ if (keyboard && keyboard->focus == view->surface)
+ weston_keyboard_set_focus(keyboard, NULL);
+ if (pointer && pointer->focus == view)
+ weston_pointer_clear_focus(pointer);
+ if (touch && touch->focus == view)
+ weston_touch_set_focus(touch, NULL);
+ }
+}
+
+WL_EXPORT void
+weston_surface_unmap(struct weston_surface *surface)
+{
+ struct weston_view *view;
+
+ wl_list_for_each(view, &surface->views, surface_link)
+ weston_view_unmap(view);
+ surface->output = NULL;
+}
+
+static void
+weston_surface_reset_pending_buffer(struct weston_surface *surface)
+{
+ weston_surface_state_set_buffer(&surface->pending, NULL);
+ surface->pending.sx = 0;
+ surface->pending.sy = 0;
+ surface->pending.newly_attached = 0;
+ surface->pending.buffer_viewport.changed = 0;
+}
+
+WL_EXPORT void
+weston_view_destroy(struct weston_view *view)
+{
+ wl_signal_emit(&view->destroy_signal, view);
+
+ assert(wl_list_empty(&view->geometry.child_list));
+
+ if (weston_view_is_mapped(view)) {
+ weston_view_unmap(view);
+ weston_compositor_build_view_list(view->surface->compositor);
+ }
+
+ wl_list_remove(&view->link);
+ weston_layer_entry_remove(&view->layer_link);
+
+ pixman_region32_fini(&view->clip);
+ pixman_region32_fini(&view->geometry.scissor);
+ pixman_region32_fini(&view->transform.boundingbox);
+ pixman_region32_fini(&view->transform.opaque);
+
+ weston_view_set_transform_parent(view, NULL);
+
+ wl_list_remove(&view->surface_link);
+
+ free(view);
+}
+
+WL_EXPORT void
+weston_surface_destroy(struct weston_surface *surface)
+{
+ struct weston_frame_callback *cb, *next;
+ struct weston_view *ev, *nv;
+
+ if (--surface->ref_count > 0)
+ return;
+
+ assert(surface->resource == NULL);
+
+ wl_signal_emit(&surface->destroy_signal, surface);
+
+ assert(wl_list_empty(&surface->subsurface_list_pending));
+ assert(wl_list_empty(&surface->subsurface_list));
+
+ wl_list_for_each_safe(ev, nv, &surface->views, surface_link)
+ weston_view_destroy(ev);
+
+ weston_surface_state_fini(&surface->pending);
+
+ weston_buffer_reference(&surface->buffer_ref, NULL);
+
+ pixman_region32_fini(&surface->damage);
+ pixman_region32_fini(&surface->opaque);
+ pixman_region32_fini(&surface->input);
+
+ wl_list_for_each_safe(cb, next, &surface->frame_callback_list, link)
+ wl_resource_destroy(cb->resource);
+
+ weston_presentation_feedback_discard_list(&surface->feedback_list);
+
+ free(surface);
+}
+
+static void
+destroy_surface(struct wl_resource *resource)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+
+ assert(surface);
+
+ /* Set the resource to NULL, since we don't want to leave a
+ * dangling pointer if the surface was refcounted and survives
+ * the weston_surface_destroy() call. */
+ surface->resource = NULL;
+
+ if (surface->viewport_resource)
+ wl_resource_set_user_data(surface->viewport_resource, NULL);
+
+ weston_surface_destroy(surface);
+}
+
+static void
+weston_buffer_destroy_handler(struct wl_listener *listener, void *data)
+{
+ struct weston_buffer *buffer =
+ container_of(listener, struct weston_buffer, destroy_listener);
+
+ wl_signal_emit(&buffer->destroy_signal, buffer);
+ free(buffer);
+}
+
+WL_EXPORT struct weston_buffer *
+weston_buffer_from_resource(struct wl_resource *resource)
+{
+ struct weston_buffer *buffer;
+ struct wl_listener *listener;
+
+ listener = wl_resource_get_destroy_listener(resource,
+ weston_buffer_destroy_handler);
+
+ if (listener)
+ return container_of(listener, struct weston_buffer,
+ destroy_listener);
+
+ buffer = zalloc(sizeof *buffer);
+ if (buffer == NULL)
+ return NULL;
+
+ buffer->resource = resource;
+ wl_signal_init(&buffer->destroy_signal);
+ buffer->destroy_listener.notify = weston_buffer_destroy_handler;
+ buffer->y_inverted = 1;
+ wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);
+
+ return buffer;
+}
+
+static void
+weston_buffer_reference_handle_destroy(struct wl_listener *listener,
+ void *data)
+{
+ struct weston_buffer_reference *ref =
+ container_of(listener, struct weston_buffer_reference,
+ destroy_listener);
+
+ assert((struct weston_buffer *)data == ref->buffer);
+ ref->buffer = NULL;
+}
+
+WL_EXPORT void
+weston_buffer_reference(struct weston_buffer_reference *ref,
+ struct weston_buffer *buffer)
+{
+ if (ref->buffer && buffer != ref->buffer) {
+ ref->buffer->busy_count--;
+ if (ref->buffer->busy_count == 0) {
+ assert(wl_resource_get_client(ref->buffer->resource));
+ wl_resource_queue_event(ref->buffer->resource,
+ WL_BUFFER_RELEASE);
+ }
+ wl_list_remove(&ref->destroy_listener.link);
+ }
+
+ if (buffer && buffer != ref->buffer) {
+ buffer->busy_count++;
+ wl_signal_add(&buffer->destroy_signal,
+ &ref->destroy_listener);
+ }
+
+ ref->buffer = buffer;
+ ref->destroy_listener.notify = weston_buffer_reference_handle_destroy;
+}
+
+static void
+weston_surface_attach(struct weston_surface *surface,
+ struct weston_buffer *buffer)
+{
+ weston_buffer_reference(&surface->buffer_ref, buffer);
+
+ if (!buffer) {
+ if (weston_surface_is_mapped(surface))
+ weston_surface_unmap(surface);
+ }
+
+ surface->compositor->renderer->attach(surface, buffer);
+
+ weston_surface_calculate_size_from_buffer(surface);
+ weston_presentation_feedback_discard_list(&surface->feedback_list);
+}
+
+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);
+}
+
+WL_EXPORT void
+weston_output_damage(struct weston_output *output)
+{
+ struct weston_compositor *compositor = output->compositor;
+
+ pixman_region32_union(&compositor->primary_plane.damage,
+ &compositor->primary_plane.damage,
+ &output->region);
+ weston_output_schedule_repaint(output);
+}
+
+static void
+surface_flush_damage(struct weston_surface *surface)
+{
+ if (surface->buffer_ref.buffer &&
+ wl_shm_buffer_get(surface->buffer_ref.buffer->resource))
+ surface->compositor->renderer->flush_damage(surface);
+
+ if (weston_timeline_enabled_ &&
+ pixman_region32_not_empty(&surface->damage))
+ TL_POINT("core_flush_damage", TLP_SURFACE(surface),
+ TLP_OUTPUT(surface->output), TLP_END);
+
+ pixman_region32_clear(&surface->damage);
+}
+
+static void
+view_accumulate_damage(struct weston_view *view,
+ pixman_region32_t *opaque)
+{
+ pixman_region32_t damage;
+
+ pixman_region32_init(&damage);
+ if (view->transform.enabled) {
+ pixman_box32_t *extents;
+
+ extents = pixman_region32_extents(&view->surface->damage);
+ view_compute_bbox(view, extents, &damage);
+ } else {
+ pixman_region32_copy(&damage, &view->surface->damage);
+ pixman_region32_translate(&damage,
+ view->geometry.x, view->geometry.y);
+ }
+
+ pixman_region32_intersect(&damage, &damage,
+ &view->transform.boundingbox);
+ pixman_region32_subtract(&damage, &damage, opaque);
+ pixman_region32_union(&view->plane->damage,
+ &view->plane->damage, &damage);
+ pixman_region32_fini(&damage);
+ pixman_region32_copy(&view->clip, opaque);
+ pixman_region32_union(opaque, opaque, &view->transform.opaque);
+}
+
+static void
+compositor_accumulate_damage(struct weston_compositor *ec)
+{
+ struct weston_plane *plane;
+ struct weston_view *ev;
+ pixman_region32_t opaque, clip;
+
+ pixman_region32_init(&clip);
+
+ wl_list_for_each(plane, &ec->plane_list, link) {
+ pixman_region32_copy(&plane->clip, &clip);
+
+ pixman_region32_init(&opaque);
+
+ wl_list_for_each(ev, &ec->view_list, link) {
+ if (ev->plane != plane)
+ continue;
+
+ view_accumulate_damage(ev, &opaque);
+ }
+
+ pixman_region32_union(&clip, &clip, &opaque);
+ pixman_region32_fini(&opaque);
+ }
+
+ pixman_region32_fini(&clip);
+
+ wl_list_for_each(ev, &ec->view_list, link)
+ ev->surface->touched = false;
+
+ wl_list_for_each(ev, &ec->view_list, link) {
+ if (ev->surface->touched)
+ continue;
+ ev->surface->touched = true;
+
+ surface_flush_damage(ev->surface);
+
+ /* Both the renderer and the backend have seen the buffer
+ * by now. If renderer needs the buffer, it has its own
+ * reference set. If the backend wants to keep the buffer
+ * around for migrating the surface into a non-primary plane
+ * later, keep_buffer is true. Otherwise, drop the core
+ * reference now, and allow early buffer release. This enables
+ * clients to use single-buffering.
+ */
+ if (!ev->surface->keep_buffer)
+ weston_buffer_reference(&ev->surface->buffer_ref, NULL);
+ }
+}
+
+static void
+surface_stash_subsurface_views(struct weston_surface *surface)
+{
+ struct weston_subsurface *sub;
+
+ wl_list_for_each(sub, &surface->subsurface_list, parent_link) {
+ if (sub->surface == surface)
+ continue;
+
+ wl_list_insert_list(&sub->unused_views, &sub->surface->views);
+ wl_list_init(&sub->surface->views);
+
+ surface_stash_subsurface_views(sub->surface);
+ }
+}
+
+static void
+surface_free_unused_subsurface_views(struct weston_surface *surface)
+{
+ struct weston_subsurface *sub;
+ struct weston_view *view, *nv;
+
+ wl_list_for_each(sub, &surface->subsurface_list, parent_link) {
+ if (sub->surface == surface)
+ continue;
+
+ wl_list_for_each_safe(view, nv, &sub->unused_views, surface_link) {
+ weston_view_unmap (view);
+ weston_view_destroy(view);
+ }
+
+ surface_free_unused_subsurface_views(sub->surface);
+ }
+}
+
+static void
+view_list_add_subsurface_view(struct weston_compositor *compositor,
+ struct weston_subsurface *sub,
+ struct weston_view *parent)
+{
+ struct weston_subsurface *child;
+ struct weston_view *view = NULL, *iv;
+
+ if (!weston_surface_is_mapped(sub->surface))
+ return;
+
+ wl_list_for_each(iv, &sub->unused_views, surface_link) {
+ if (iv->geometry.parent == parent) {
+ view = iv;
+ break;
+ }
+ }
+
+ if (view) {
+ /* Put it back in the surface's list of views */
+ wl_list_remove(&view->surface_link);
+ wl_list_insert(&sub->surface->views, &view->surface_link);
+ } else {
+ view = weston_view_create(sub->surface);
+ weston_view_set_position(view,
+ sub->position.x,
+ sub->position.y);
+ weston_view_set_transform_parent(view, parent);
+ }
+
+ view->parent_view = parent;
+ weston_view_update_transform(view);
+
+ if (wl_list_empty(&sub->surface->subsurface_list)) {
+ wl_list_insert(compositor->view_list.prev, &view->link);
+ return;
+ }
+
+ wl_list_for_each(child, &sub->surface->subsurface_list, parent_link) {
+ if (child->surface == sub->surface)
+ wl_list_insert(compositor->view_list.prev, &view->link);
+ else
+ view_list_add_subsurface_view(compositor, child, view);
+ }
+}
+
+static void
+view_list_add(struct weston_compositor *compositor,
+ struct weston_view *view)
+{
+ struct weston_subsurface *sub;
+
+ weston_view_update_transform(view);
+
+ if (wl_list_empty(&view->surface->subsurface_list)) {
+ wl_list_insert(compositor->view_list.prev, &view->link);
+ return;
+ }
+
+ wl_list_for_each(sub, &view->surface->subsurface_list, parent_link) {
+ if (sub->surface == view->surface)
+ wl_list_insert(compositor->view_list.prev, &view->link);
+ else
+ view_list_add_subsurface_view(compositor, sub, view);
+ }
+}
+
+static void
+weston_compositor_build_view_list(struct weston_compositor *compositor)
+{
+ struct weston_view *view;
+ struct weston_layer *layer;
+
+ wl_list_for_each(layer, &compositor->layer_list, link)
+ wl_list_for_each(view, &layer->view_list.link, layer_link.link)
+ surface_stash_subsurface_views(view->surface);
+
+ wl_list_init(&compositor->view_list);
+ wl_list_for_each(layer, &compositor->layer_list, link) {
+ wl_list_for_each(view, &layer->view_list.link, layer_link.link) {
+ view_list_add(compositor, view);
+ }
+ }
+
+ wl_list_for_each(layer, &compositor->layer_list, link)
+ wl_list_for_each(view, &layer->view_list.link, layer_link.link)
+ surface_free_unused_subsurface_views(view->surface);
+}
+
+static void
+weston_output_take_feedback_list(struct weston_output *output,
+ struct weston_surface *surface)
+{
+ struct weston_view *view;
+ struct weston_presentation_feedback *feedback;
+ uint32_t flags = 0xffffffff;
+
+ if (wl_list_empty(&surface->feedback_list))
+ return;
+
+ /* All views must have the flag for the flag to survive. */
+ wl_list_for_each(view, &surface->views, surface_link) {
+ /* ignore views that are not on this output at all */
+ if (view->output_mask & (1u << output->id))
+ flags &= view->psf_flags;
+ }
+
+ wl_list_for_each(feedback, &surface->feedback_list, link)
+ feedback->psf_flags = flags;
+
+ wl_list_insert_list(&output->feedback_list, &surface->feedback_list);
+ wl_list_init(&surface->feedback_list);
+}
+
+static int
+weston_output_repaint(struct weston_output *output)
+{
+ struct weston_compositor *ec = output->compositor;
+ struct weston_view *ev;
+ struct weston_animation *animation, *next;
+ struct weston_frame_callback *cb, *cnext;
+ struct wl_list frame_callback_list;
+ pixman_region32_t output_damage;
+ int r;
+
+ if (output->destroying)
+ return 0;
+
+ TL_POINT("core_repaint_begin", TLP_OUTPUT(output), TLP_END);
+
+ /* Rebuild the surface list and update surface transforms up front. */
+ weston_compositor_build_view_list(ec);
+
+ if (output->assign_planes && !output->disable_planes) {
+ output->assign_planes(output);
+ } else {
+ wl_list_for_each(ev, &ec->view_list, link) {
+ weston_view_move_to_plane(ev, &ec->primary_plane);
+ ev->psf_flags = 0;
+ }
+ }
+
+ wl_list_init(&frame_callback_list);
+ wl_list_for_each(ev, &ec->view_list, link) {
+ /* Note: This operation is safe to do multiple times on the
+ * same surface.
+ */
+ if (ev->surface->output == output) {
+ wl_list_insert_list(&frame_callback_list,
+ &ev->surface->frame_callback_list);
+ wl_list_init(&ev->surface->frame_callback_list);
+
+ weston_output_take_feedback_list(output, ev->surface);
+ }
+ }
+
+ compositor_accumulate_damage(ec);
+
+ pixman_region32_init(&output_damage);
+ pixman_region32_intersect(&output_damage,
+ &ec->primary_plane.damage, &output->region);
+ pixman_region32_subtract(&output_damage,
+ &output_damage, &ec->primary_plane.clip);
+
+ if (output->dirty)
+ weston_output_update_matrix(output);
+
+ r = output->repaint(output, &output_damage);
+
+ pixman_region32_fini(&output_damage);
+
+ output->repaint_needed = 0;
+
+ weston_compositor_repick(ec);
+
+ wl_list_for_each_safe(cb, cnext, &frame_callback_list, link) {
+ wl_callback_send_done(cb->resource, output->frame_time);
+ wl_resource_destroy(cb->resource);
+ }
+
+ wl_list_for_each_safe(animation, next, &output->animation_list, link) {
+ animation->frame_counter++;
+ animation->frame(animation, output, output->frame_time);
+ }
+
+ TL_POINT("core_repaint_posted", TLP_OUTPUT(output), TLP_END);
+
+ return r;
+}
+
+static void
+weston_output_schedule_repaint_reset(struct weston_output *output)
+{
+ output->repaint_scheduled = 0;
+ TL_POINT("core_repaint_exit_loop", TLP_OUTPUT(output), TLP_END);
+}
+
+static int
+output_repaint_timer_handler(void *data)
+{
+ struct weston_output *output = data;
+ struct weston_compositor *compositor = output->compositor;
+
+ if (output->repaint_needed &&
+ compositor->state != WESTON_COMPOSITOR_SLEEPING &&
+ compositor->state != WESTON_COMPOSITOR_OFFSCREEN &&
+ weston_output_repaint(output) == 0)
+ return 0;
+
+ weston_output_schedule_repaint_reset(output);
+
+ return 0;
+}
+
+WL_EXPORT void
+weston_output_finish_frame(struct weston_output *output,
+ const struct timespec *stamp,
+ uint32_t presented_flags)
+{
+ struct weston_compositor *compositor = output->compositor;
+ int32_t refresh_nsec;
+ struct timespec now;
+ struct timespec gone;
+ int msec;
+
+ TL_POINT("core_repaint_finished", TLP_OUTPUT(output),
+ TLP_VBLANK(stamp), TLP_END);
+
+ refresh_nsec = millihz_to_nsec(output->current_mode->refresh);
+ weston_presentation_feedback_present_list(&output->feedback_list,
+ output, refresh_nsec, stamp,
+ output->msc,
+ presented_flags);
+
+ output->frame_time = stamp->tv_sec * 1000 + stamp->tv_nsec / 1000000;
+
+ weston_compositor_read_presentation_clock(compositor, &now);
+ timespec_sub(&gone, &now, stamp);
+ msec = (refresh_nsec - timespec_to_nsec(&gone)) / 1000000; /* floor */
+ msec -= compositor->repaint_msec;
+
+ if (msec < -1000 || msec > 1000) {
+ static bool warned;
+
+ if (!warned)
+ weston_log("Warning: computed repaint delay is "
+ "insane: %d msec\n", msec);
+ warned = true;
+
+ msec = 0;
+ }
+
+ /* Called from restart_repaint_loop and restart happens already after
+ * the deadline given by repaint_msec? In that case we delay until
+ * the deadline of the next frame, to give clients a more predictable
+ * timing of the repaint cycle to lock on. */
+ if (presented_flags == WP_PRESENTATION_FEEDBACK_INVALID && msec < 0)
+ msec += refresh_nsec / 1000000;
+
+ if (msec < 1)
+ output_repaint_timer_handler(output);
+ else
+ wl_event_source_timer_update(output->repaint_timer, msec);
+}
+
+static void
+idle_repaint(void *data)
+{
+ struct weston_output *output = data;
+
+ output->start_repaint_loop(output);
+}
+
+WL_EXPORT void
+weston_layer_entry_insert(struct weston_layer_entry *list,
+ struct weston_layer_entry *entry)
+{
+ wl_list_insert(&list->link, &entry->link);
+ entry->layer = list->layer;
+}
+
+WL_EXPORT void
+weston_layer_entry_remove(struct weston_layer_entry *entry)
+{
+ wl_list_remove(&entry->link);
+ wl_list_init(&entry->link);
+ entry->layer = NULL;
+}
+
+WL_EXPORT void
+weston_layer_init(struct weston_layer *layer, struct wl_list *below)
+{
+ wl_list_init(&layer->view_list.link);
+ layer->view_list.layer = layer;
+ weston_layer_set_mask_infinite(layer);
+ if (below != NULL)
+ wl_list_insert(below, &layer->link);
+}
+
+WL_EXPORT void
+weston_layer_set_mask(struct weston_layer *layer,
+ int x, int y, int width, int height)
+{
+ struct weston_view *view;
+
+ layer->mask.x1 = x;
+ layer->mask.x2 = x + width;
+ layer->mask.y1 = y;
+ layer->mask.y2 = y + height;
+
+ wl_list_for_each(view, &layer->view_list.link, layer_link.link) {
+ weston_view_geometry_dirty(view);
+ }
+}
+
+WL_EXPORT void
+weston_layer_set_mask_infinite(struct weston_layer *layer)
+{
+ weston_layer_set_mask(layer, INT32_MIN, INT32_MIN,
+ UINT32_MAX, UINT32_MAX);
+}
+
+WL_EXPORT void
+weston_output_schedule_repaint(struct weston_output *output)
+{
+ struct weston_compositor *compositor = output->compositor;
+ struct wl_event_loop *loop;
+
+ if (compositor->state == WESTON_COMPOSITOR_SLEEPING ||
+ compositor->state == WESTON_COMPOSITOR_OFFSCREEN)
+ return;
+
+ if (!output->repaint_needed)
+ TL_POINT("core_repaint_req", TLP_OUTPUT(output), TLP_END);
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+ output->repaint_needed = 1;
+ if (output->repaint_scheduled)
+ return;
+
+ wl_event_loop_add_idle(loop, idle_repaint, output);
+ output->repaint_scheduled = 1;
+ TL_POINT("core_repaint_enter_loop", TLP_OUTPUT(output), TLP_END);
+}
+
+WL_EXPORT void
+weston_compositor_schedule_repaint(struct weston_compositor *compositor)
+{
+ struct weston_output *output;
+
+ wl_list_for_each(output, &compositor->output_list, link)
+ weston_output_schedule_repaint(output);
+}
+
+static void
+surface_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+surface_attach(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+ struct weston_buffer *buffer = NULL;
+
+ if (buffer_resource) {
+ buffer = weston_buffer_from_resource(buffer_resource);
+ if (buffer == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ }
+
+ /* Attach, attach, without commit in between does not send
+ * wl_buffer.release. */
+ weston_surface_state_set_buffer(&surface->pending, buffer);
+
+ surface->pending.sx = sx;
+ surface->pending.sy = sy;
+ surface->pending.newly_attached = 1;
+}
+
+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 *surface = wl_resource_get_user_data(resource);
+
+ if (width <= 0 || height <= 0)
+ return;
+
+ pixman_region32_union_rect(&surface->pending.damage_surface,
+ &surface->pending.damage_surface,
+ x, y, width, height);
+}
+
+static void
+surface_damage_buffer(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+
+ if (width <= 0 || height <= 0)
+ return;
+
+ pixman_region32_union_rect(&surface->pending.damage_buffer,
+ &surface->pending.damage_buffer,
+ x, y, width, height);
+}
+
+static void
+destroy_frame_callback(struct wl_resource *resource)
+{
+ struct weston_frame_callback *cb = wl_resource_get_user_data(resource);
+
+ 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 *surface = wl_resource_get_user_data(resource);
+
+ cb = malloc(sizeof *cb);
+ if (cb == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ cb->resource = wl_resource_create(client, &wl_callback_interface, 1,
+ callback);
+ if (cb->resource == NULL) {
+ free(cb);
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ wl_resource_set_implementation(cb->resource, NULL, cb,
+ destroy_frame_callback);
+
+ wl_list_insert(surface->pending.frame_callback_list.prev, &cb->link);
+}
+
+static void
+surface_set_opaque_region(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *region_resource)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+ struct weston_region *region;
+
+ if (region_resource) {
+ region = wl_resource_get_user_data(region_resource);
+ pixman_region32_copy(&surface->pending.opaque,
+ ®ion->region);
+ } else {
+ pixman_region32_clear(&surface->pending.opaque);
+ }
+}
+
+static void
+surface_set_input_region(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *region_resource)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+ struct weston_region *region;
+
+ if (region_resource) {
+ region = wl_resource_get_user_data(region_resource);
+ pixman_region32_copy(&surface->pending.input,
+ ®ion->region);
+ } else {
+ pixman_region32_fini(&surface->pending.input);
+ region_init_infinite(&surface->pending.input);
+ }
+}
+
+static void
+weston_surface_commit_subsurface_order(struct weston_surface *surface)
+{
+ struct weston_subsurface *sub;
+
+ wl_list_for_each_reverse(sub, &surface->subsurface_list_pending,
+ parent_link_pending) {
+ wl_list_remove(&sub->parent_link);
+ wl_list_insert(&surface->subsurface_list, &sub->parent_link);
+ }
+}
+
+static void
+weston_surface_build_buffer_matrix(const struct weston_surface *surface,
+ struct weston_matrix *matrix)
+{
+ const struct weston_buffer_viewport *vp = &surface->buffer_viewport;
+ double src_width, src_height, dest_width, dest_height;
+
+ weston_matrix_init(matrix);
+
+ if (vp->buffer.src_width == wl_fixed_from_int(-1)) {
+ src_width = surface->width_from_buffer;
+ src_height = surface->height_from_buffer;
+ } else {
+ src_width = wl_fixed_to_double(vp->buffer.src_width);
+ src_height = wl_fixed_to_double(vp->buffer.src_height);
+ }
+
+ if (vp->surface.width == -1) {
+ dest_width = src_width;
+ dest_height = src_height;
+ } else {
+ dest_width = vp->surface.width;
+ dest_height = vp->surface.height;
+ }
+
+ if (src_width != dest_width || src_height != dest_height)
+ weston_matrix_scale(matrix,
+ src_width / dest_width,
+ src_height / dest_height, 1);
+
+ if (vp->buffer.src_width != wl_fixed_from_int(-1))
+ weston_matrix_translate(matrix,
+ wl_fixed_to_double(vp->buffer.src_x),
+ wl_fixed_to_double(vp->buffer.src_y),
+ 0);
+
+ switch (vp->buffer.transform) {
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ weston_matrix_scale(matrix, -1, 1, 1);
+ weston_matrix_translate(matrix,
+ surface->width_from_buffer, 0, 0);
+ break;
+ }
+
+ switch (vp->buffer.transform) {
+ default:
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ weston_matrix_rotate_xy(matrix, 0, 1);
+ weston_matrix_translate(matrix,
+ surface->height_from_buffer, 0, 0);
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ weston_matrix_rotate_xy(matrix, -1, 0);
+ weston_matrix_translate(matrix,
+ surface->width_from_buffer,
+ surface->height_from_buffer, 0);
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ weston_matrix_rotate_xy(matrix, 0, -1);
+ weston_matrix_translate(matrix,
+ 0, surface->width_from_buffer, 0);
+ break;
+ }
+
+ weston_matrix_scale(matrix, vp->buffer.scale, vp->buffer.scale, 1);
+}
+
+/**
+ * Compute a + b > c while being safe to overflows.
+ */
+static bool
+fixed_sum_gt(wl_fixed_t a, wl_fixed_t b, wl_fixed_t c)
+{
+ return (int64_t)a + (int64_t)b > (int64_t)c;
+}
+
+static bool
+weston_surface_is_pending_viewport_source_valid(
+ const struct weston_surface *surface)
+{
+ const struct weston_surface_state *pend = &surface->pending;
+ const struct weston_buffer_viewport *vp = &pend->buffer_viewport;
+ int width_from_buffer = 0;
+ int height_from_buffer = 0;
+ wl_fixed_t w;
+ wl_fixed_t h;
+
+ /* If viewport source rect is not set, it is always ok. */
+ if (vp->buffer.src_width == wl_fixed_from_int(-1))
+ return true;
+
+ if (pend->newly_attached) {
+ if (pend->buffer) {
+ convert_size_by_transform_scale(&width_from_buffer,
+ &height_from_buffer,
+ pend->buffer->width,
+ pend->buffer->height,
+ vp->buffer.transform,
+ vp->buffer.scale);
+ } else {
+ /* No buffer: viewport is irrelevant. */
+ return true;
+ }
+ } else {
+ width_from_buffer = surface->width_from_buffer;
+ height_from_buffer = surface->height_from_buffer;
+ }
+
+ assert((width_from_buffer == 0) == (height_from_buffer == 0));
+ assert(width_from_buffer >= 0 && height_from_buffer >= 0);
+
+ /* No buffer: viewport is irrelevant. */
+ if (width_from_buffer == 0 || height_from_buffer == 0)
+ return true;
+
+ /* overflow checks for wl_fixed_from_int() */
+ if (width_from_buffer > wl_fixed_to_int(INT32_MAX))
+ return false;
+ if (height_from_buffer > wl_fixed_to_int(INT32_MAX))
+ return false;
+
+ w = wl_fixed_from_int(width_from_buffer);
+ h = wl_fixed_from_int(height_from_buffer);
+
+ if (fixed_sum_gt(vp->buffer.src_x, vp->buffer.src_width, w))
+ return false;
+ if (fixed_sum_gt(vp->buffer.src_y, vp->buffer.src_height, h))
+ return false;
+
+ return true;
+}
+
+static bool
+fixed_is_integer(wl_fixed_t v)
+{
+ return (v & 0xff) == 0;
+}
+
+static bool
+weston_surface_is_pending_viewport_dst_size_int(
+ const struct weston_surface *surface)
+{
+ const struct weston_buffer_viewport *vp =
+ &surface->pending.buffer_viewport;
+
+ if (vp->surface.width != -1) {
+ assert(vp->surface.width > 0 && vp->surface.height > 0);
+ return true;
+ }
+
+ return fixed_is_integer(vp->buffer.src_width) &&
+ fixed_is_integer(vp->buffer.src_height);
+}
+
+/* Translate pending damage in buffer co-ordinates to surface
+ * co-ordinates and union it with a pixman_region32_t.
+ * This should only be called after the buffer is attached.
+ */
+static void
+apply_damage_buffer(pixman_region32_t *dest,
+ struct weston_surface *surface,
+ struct weston_surface_state *state)
+{
+ struct weston_buffer *buffer = surface->buffer_ref.buffer;
+
+ /* wl_surface.damage_buffer needs to be clipped to the buffer,
+ * translated into surface co-ordinates and unioned with
+ * any other surface damage.
+ * None of this makes sense if there is no buffer though.
+ */
+ if (buffer && pixman_region32_not_empty(&state->damage_buffer)) {
+ pixman_region32_t buffer_damage;
+
+ pixman_region32_intersect_rect(&state->damage_buffer,
+ &state->damage_buffer,
+ 0, 0, buffer->width,
+ buffer->height);
+ pixman_region32_init(&buffer_damage);
+ weston_matrix_transform_region(&buffer_damage,
+ &surface->buffer_to_surface_matrix,
+ &state->damage_buffer);
+ pixman_region32_union(dest, dest, &buffer_damage);
+ pixman_region32_fini(&buffer_damage);
+ }
+ /* We should clear this on commit even if there was no buffer */
+ pixman_region32_clear(&state->damage_buffer);
+}
+
+static void
+weston_surface_commit_state(struct weston_surface *surface,
+ struct weston_surface_state *state)
+{
+ struct weston_view *view;
+ pixman_region32_t opaque;
+
+ /* wl_surface.set_buffer_transform */
+ /* wl_surface.set_buffer_scale */
+ /* wp_viewport.set_source */
+ /* wp_viewport.set_destination */
+ surface->buffer_viewport = state->buffer_viewport;
+
+ /* wl_surface.attach */
+ if (state->newly_attached)
+ weston_surface_attach(surface, state->buffer);
+ weston_surface_state_set_buffer(state, NULL);
+
+ weston_surface_build_buffer_matrix(surface,
+ &surface->surface_to_buffer_matrix);
+ weston_matrix_invert(&surface->buffer_to_surface_matrix,
+ &surface->surface_to_buffer_matrix);
+
+ if (state->newly_attached || state->buffer_viewport.changed) {
+ weston_surface_update_size(surface);
+ if (surface->configure)
+ surface->configure(surface, state->sx, state->sy);
+ }
+
+ state->sx = 0;
+ state->sy = 0;
+ state->newly_attached = 0;
+ state->buffer_viewport.changed = 0;
+
+ /* wl_surface.damage and wl_surface.damage_buffer */
+ if (weston_timeline_enabled_ &&
+ (pixman_region32_not_empty(&state->damage_surface) ||
+ pixman_region32_not_empty(&state->damage_buffer)))
+ TL_POINT("core_commit_damage", TLP_SURFACE(surface), TLP_END);
+
+ pixman_region32_union(&surface->damage, &surface->damage,
+ &state->damage_surface);
+
+ apply_damage_buffer(&surface->damage, surface, state);
+
+ pixman_region32_intersect_rect(&surface->damage, &surface->damage,
+ 0, 0, surface->width, surface->height);
+ pixman_region32_clear(&state->damage_surface);
+
+ /* wl_surface.set_opaque_region */
+ pixman_region32_init(&opaque);
+ pixman_region32_intersect_rect(&opaque, &state->opaque,
+ 0, 0, surface->width, surface->height);
+
+ if (!pixman_region32_equal(&opaque, &surface->opaque)) {
+ pixman_region32_copy(&surface->opaque, &opaque);
+ wl_list_for_each(view, &surface->views, surface_link)
+ weston_view_geometry_dirty(view);
+ }
+
+ pixman_region32_fini(&opaque);
+
+ /* wl_surface.set_input_region */
+ pixman_region32_intersect_rect(&surface->input, &state->input,
+ 0, 0, surface->width, surface->height);
+
+ /* wl_surface.frame */
+ wl_list_insert_list(&surface->frame_callback_list,
+ &state->frame_callback_list);
+ wl_list_init(&state->frame_callback_list);
+
+ /* XXX:
+ * What should happen with a feedback request, if there
+ * is no wl_buffer attached for this commit?
+ */
+
+ /* presentation.feedback */
+ wl_list_insert_list(&surface->feedback_list,
+ &state->feedback_list);
+ wl_list_init(&state->feedback_list);
+}
+
+static void
+weston_surface_commit(struct weston_surface *surface)
+{
+ weston_surface_commit_state(surface, &surface->pending);
+
+ weston_surface_commit_subsurface_order(surface);
+
+ weston_surface_schedule_repaint(surface);
+}
+
+static void
+weston_subsurface_commit(struct weston_subsurface *sub);
+
+static void
+weston_subsurface_parent_commit(struct weston_subsurface *sub,
+ int parent_is_synchronized);
+
+static void
+surface_commit(struct wl_client *client, struct wl_resource *resource)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+ struct weston_subsurface *sub = weston_surface_to_subsurface(surface);
+
+ if (!weston_surface_is_pending_viewport_source_valid(surface)) {
+ assert(surface->viewport_resource);
+
+ wl_resource_post_error(surface->viewport_resource,
+ WP_VIEWPORT_ERROR_OUT_OF_BUFFER,
+ "wl_surface@%d has viewport source outside buffer",
+ wl_resource_get_id(resource));
+ return;
+ }
+
+ if (!weston_surface_is_pending_viewport_dst_size_int(surface)) {
+ assert(surface->viewport_resource);
+
+ wl_resource_post_error(surface->viewport_resource,
+ WP_VIEWPORT_ERROR_BAD_SIZE,
+ "wl_surface@%d viewport dst size not integer",
+ wl_resource_get_id(resource));
+ return;
+ }
+
+ if (sub) {
+ weston_subsurface_commit(sub);
+ return;
+ }
+
+ weston_surface_commit(surface);
+
+ wl_list_for_each(sub, &surface->subsurface_list, parent_link) {
+ if (sub->surface != surface)
+ weston_subsurface_parent_commit(sub, 0);
+ }
+}
+
+static void
+surface_set_buffer_transform(struct wl_client *client,
+ struct wl_resource *resource, int transform)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+
+ /* if wl_output.transform grows more members this will need to be updated. */
+ if (transform < 0 ||
+ transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) {
+ wl_resource_post_error(resource,
+ WL_SURFACE_ERROR_INVALID_TRANSFORM,
+ "buffer transform must be a valid transform "
+ "('%d' specified)", transform);
+ return;
+ }
+
+ surface->pending.buffer_viewport.buffer.transform = transform;
+ surface->pending.buffer_viewport.changed = 1;
+}
+
+static void
+surface_set_buffer_scale(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t scale)
+{
+ struct weston_surface *surface = wl_resource_get_user_data(resource);
+
+ if (scale < 1) {
+ wl_resource_post_error(resource,
+ WL_SURFACE_ERROR_INVALID_SCALE,
+ "buffer scale must be at least one "
+ "('%d' specified)", scale);
+ return;
+ }
+
+ surface->pending.buffer_viewport.buffer.scale = scale;
+ surface->pending.buffer_viewport.changed = 1;
+}
+
+static const struct wl_surface_interface surface_interface = {
+ surface_destroy,
+ surface_attach,
+ surface_damage,
+ surface_frame,
+ surface_set_opaque_region,
+ surface_set_input_region,
+ surface_commit,
+ surface_set_buffer_transform,
+ surface_set_buffer_scale,
+ surface_damage_buffer
+};
+
+static void
+compositor_create_surface(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct weston_compositor *ec = wl_resource_get_user_data(resource);
+ struct weston_surface *surface;
+
+ surface = weston_surface_create(ec);
+ if (surface == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ surface->resource =
+ wl_resource_create(client, &wl_surface_interface,
+ wl_resource_get_version(resource), id);
+ if (surface->resource == NULL) {
+ weston_surface_destroy(surface);
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+ wl_resource_set_implementation(surface->resource, &surface_interface,
+ surface, destroy_surface);
+
+ wl_signal_emit(&ec->create_surface_signal, surface);
+}
+
+static void
+destroy_region(struct wl_resource *resource)
+{
+ struct weston_region *region = wl_resource_get_user_data(resource);
+
+ pixman_region32_fini(®ion->region);
+ free(region);
+}
+
+static void
+region_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+region_add(struct wl_client *client, struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct weston_region *region = wl_resource_get_user_data(resource);
+
+ pixman_region32_union_rect(®ion->region, ®ion->region,
+ x, y, width, height);
+}
+
+static void
+region_subtract(struct wl_client *client, struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct weston_region *region = wl_resource_get_user_data(resource);
+ pixman_region32_t rect;
+
+ pixman_region32_init_rect(&rect, x, y, width, height);
+ pixman_region32_subtract(®ion->region, ®ion->region, &rect);
+ pixman_region32_fini(&rect);
+}
+
+static const struct wl_region_interface region_interface = {
+ region_destroy,
+ region_add,
+ region_subtract
+};
+
+static void
+compositor_create_region(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct weston_region *region;
+
+ region = malloc(sizeof *region);
+ if (region == NULL) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ pixman_region32_init(®ion->region);
+
+ region->resource =
+ wl_resource_create(client, &wl_region_interface, 1, id);
+ if (region->resource == NULL) {
+ free(region);
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+ wl_resource_set_implementation(region->resource, ®ion_interface,
+ region, destroy_region);
+}
+
+static const struct wl_compositor_interface compositor_interface = {
+ compositor_create_surface,
+ compositor_create_region
+};
+
+static void
+weston_subsurface_commit_from_cache(struct weston_subsurface *sub)
+{
+ struct weston_surface *surface = sub->surface;
+
+ weston_surface_commit_state(surface, &sub->cached);
+ weston_buffer_reference(&sub->cached_buffer_ref, NULL);
+
+ weston_surface_commit_subsurface_order(surface);
+
+ weston_surface_schedule_repaint(surface);
+
+ sub->has_cached_data = 0;
+}
+
+static void
+weston_subsurface_commit_to_cache(struct weston_subsurface *sub)
+{
+ struct weston_surface *surface = sub->surface;
+
+ /*
+ * If this commit would cause the surface to move by the
+ * attach(dx, dy) parameters, the old damage region must be
+ * translated to correspond to the new surface coordinate system
+ * origin.
+ */
+ pixman_region32_translate(&sub->cached.damage_surface,
+ -surface->pending.sx, -surface->pending.sy);
+ pixman_region32_union(&sub->cached.damage_surface,
+ &sub->cached.damage_surface,
+ &surface->pending.damage_surface);
+ pixman_region32_clear(&surface->pending.damage_surface);
+
+ if (surface->pending.newly_attached) {
+ sub->cached.newly_attached = 1;
+ weston_surface_state_set_buffer(&sub->cached,
+ surface->pending.buffer);
+ weston_buffer_reference(&sub->cached_buffer_ref,
+ surface->pending.buffer);
+ weston_presentation_feedback_discard_list(
+ &sub->cached.feedback_list);
+ }
+ sub->cached.sx += surface->pending.sx;
+ sub->cached.sy += surface->pending.sy;
+
+ apply_damage_buffer(&sub->cached.damage_surface, surface, &surface->pending);
+
+ sub->cached.buffer_viewport.changed |=
+ surface->pending.buffer_viewport.changed;
+ sub->cached.buffer_viewport.buffer =
+ surface->pending.buffer_viewport.buffer;
+ sub->cached.buffer_viewport.surface =
+ surface->pending.buffer_viewport.surface;
+
+ weston_surface_reset_pending_buffer(surface);
+
+ pixman_region32_copy(&sub->cached.opaque, &surface->pending.opaque);
+
+ pixman_region32_copy(&sub->cached.input, &surface->pending.input);
+
+ wl_list_insert_list(&sub->cached.frame_callback_list,
+ &surface->pending.frame_callback_list);
+ wl_list_init(&surface->pending.frame_callback_list);
+
+ wl_list_insert_list(&sub->cached.feedback_list,
+ &surface->pending.feedback_list);
+ wl_list_init(&surface->pending.feedback_list);
+
+ sub->has_cached_data = 1;
+}
+
+static bool
+weston_subsurface_is_synchronized(struct weston_subsurface *sub)
+{
+ while (sub) {
+ if (sub->synchronized)
+ return true;
+
+ if (!sub->parent)
+ return false;
+
+ sub = weston_surface_to_subsurface(sub->parent);
+ }
+
+ return false;
+}
+
+static void
+weston_subsurface_commit(struct weston_subsurface *sub)
+{
+ struct weston_surface *surface = sub->surface;
+ struct weston_subsurface *tmp;
+
+ /* Recursive check for effectively synchronized. */
+ if (weston_subsurface_is_synchronized(sub)) {
+ weston_subsurface_commit_to_cache(sub);
+ } else {
+ if (sub->has_cached_data) {
+ /* flush accumulated state from cache */
+ weston_subsurface_commit_to_cache(sub);
+ weston_subsurface_commit_from_cache(sub);
+ } else {
+ weston_surface_commit(surface);
+ }
+
+ wl_list_for_each(tmp, &surface->subsurface_list, parent_link) {
+ if (tmp->surface != surface)
+ weston_subsurface_parent_commit(tmp, 0);
+ }
+ }
+}
+
+static void
+weston_subsurface_synchronized_commit(struct weston_subsurface *sub)
+{
+ struct weston_surface *surface = sub->surface;
+ struct weston_subsurface *tmp;
+
+ /* From now on, commit_from_cache the whole sub-tree, regardless of
+ * the synchronized mode of each child. This sub-surface or some
+ * of its ancestors were synchronized, so we are synchronized
+ * all the way down.
+ */
+
+ if (sub->has_cached_data)
+ weston_subsurface_commit_from_cache(sub);
+
+ wl_list_for_each(tmp, &surface->subsurface_list, parent_link) {
+ if (tmp->surface != surface)
+ weston_subsurface_parent_commit(tmp, 1);
+ }
+}
+
+static void
+weston_subsurface_parent_commit(struct weston_subsurface *sub,
+ int parent_is_synchronized)
+{
+ struct weston_view *view;
+ if (sub->position.set) {
+ wl_list_for_each(view, &sub->surface->views, surface_link)
+ weston_view_set_position(view,
+ sub->position.x,
+ sub->position.y);
+
+ sub->position.set = 0;
+ }
+
+ if (parent_is_synchronized || sub->synchronized)
+ weston_subsurface_synchronized_commit(sub);
+}
+
+static int
+subsurface_get_label(struct weston_surface *surface, char *buf, size_t len)
+{
+ return snprintf(buf, len, "sub-surface");
+}
+
+static void
+subsurface_configure(struct weston_surface *surface, int32_t dx, int32_t dy)
+{
+ struct weston_compositor *compositor = surface->compositor;
+ struct weston_view *view;
+
+ wl_list_for_each(view, &surface->views, surface_link)
+ weston_view_set_position(view,
+ view->geometry.x + dx,
+ view->geometry.y + dy);
+
+ /* No need to check parent mappedness, because if parent is not
+ * mapped, parent is not in a visible layer, so this sub-surface
+ * will not be drawn either.
+ */
+ if (!weston_surface_is_mapped(surface)) {
+ struct weston_output *output;
+
+ /* Cannot call weston_view_update_transform(),
+ * because that would call it also for the parent surface,
+ * which might not be mapped yet. That would lead to
+ * inconsistent state, where the window could never be
+ * mapped.
+ *
+ * Instead just assign any output, to make
+ * weston_surface_is_mapped() return true, so that when the
+ * parent surface does get mapped, this one will get
+ * included, too. See view_list_add().
+ */
+ assert(!wl_list_empty(&compositor->output_list));
+ output = container_of(compositor->output_list.next,
+ struct weston_output, link);
+
+ surface->output = output;
+ weston_surface_update_output_mask(surface, 1u << output->id);
+ }
+}
+
+static struct weston_subsurface *
+weston_surface_to_subsurface(struct weston_surface *surface)
+{
+ if (surface->configure == subsurface_configure)
+ return surface->configure_private;
+
+ return NULL;
+}
+
+WL_EXPORT struct weston_surface *
+weston_surface_get_main_surface(struct weston_surface *surface)
+{
+ struct weston_subsurface *sub;
+
+ while (surface && (sub = weston_surface_to_subsurface(surface)))
+ surface = sub->parent;
+
+ return surface;
+}
+
+WL_EXPORT int
+weston_surface_set_role(struct weston_surface *surface,
+ const char *role_name,
+ struct wl_resource *error_resource,
+ uint32_t error_code)
+{
+ assert(role_name);
+
+ if (surface->role_name == NULL ||
+ surface->role_name == role_name ||
+ strcmp(surface->role_name, role_name) == 0) {
+ surface->role_name = role_name;
+
+ return 0;
+ }
+
+ wl_resource_post_error(error_resource, error_code,
+ "Cannot assign role %s to wl_surface@%d,"
+ " already has role %s\n",
+ role_name,
+ wl_resource_get_id(surface->resource),
+ surface->role_name);
+ return -1;
+}
+
+WL_EXPORT void
+weston_surface_set_label_func(struct weston_surface *surface,
+ int (*desc)(struct weston_surface *,
+ char *, size_t))
+{
+ surface->get_label = desc;
+ surface->timeline.force_refresh = 1;
+}
+
+/** Get the size of surface contents
+ *
+ * \param surface The surface to query.
+ * \param width Returns the width of raw contents.
+ * \param height Returns the height of raw contents.
+ *
+ * Retrieves the raw surface content size in pixels for the given surface.
+ * This is the whole content size in buffer pixels. If the surface
+ * has no content or the renderer does not implement this feature,
+ * zeroes are returned.
+ *
+ * This function is used to determine the buffer size needed for
+ * a weston_surface_copy_content() call.
+ */
+WL_EXPORT void
+weston_surface_get_content_size(struct weston_surface *surface,
+ int *width, int *height)
+{
+ struct weston_renderer *rer = surface->compositor->renderer;
+
+ if (!rer->surface_get_content_size) {
+ *width = 0;
+ *height = 0;
+ return;
+ }
+
+ rer->surface_get_content_size(surface, width, height);
+}
+
+/** Copy surface contents to system memory.
+ *
+ * \param surface The surface to copy from.
+ * \param target Pointer to the target memory buffer.
+ * \param size Size of the target buffer in bytes.
+ * \param src_x X location on contents to copy from.
+ * \param src_y Y location on contents to copy from.
+ * \param width Width in pixels of the area to copy.
+ * \param height Height in pixels of the area to copy.
+ * \return 0 for success, -1 for failure.
+ *
+ * Surface contents are maintained by the renderer. They can be in a
+ * reserved weston_buffer or as a copy, e.g. a GL texture, or something
+ * else.
+ *
+ * Surface contents are copied into memory pointed to by target,
+ * which has size bytes of space available. The target memory
+ * may be larger than needed, but being smaller returns an error.
+ * The extra bytes in target may or may not be written; their content is
+ * unspecified. Size must be large enough to hold the image.
+ *
+ * The image in the target memory will be arranged in rows from
+ * top to bottom, and pixels on a row from left to right. The pixel
+ * format is PIXMAN_a8b8g8r8, 4 bytes per pixel, and stride is exactly
+ * width * 4.
+ *
+ * Parameters src_x and src_y define the upper-left corner in buffer
+ * coordinates (pixels) to copy from. Parameters width and height
+ * define the size of the area to copy in pixels.
+ *
+ * The rectangle defined by src_x, src_y, width, height must fit in
+ * the surface contents. Otherwise an error is returned.
+ *
+ * Use surface_get_data_size to determine the content size; the
+ * needed target buffer size and rectangle limits.
+ *
+ * CURRENT IMPLEMENTATION RESTRICTIONS:
+ * - the machine must be little-endian due to Pixman formats.
+ *
+ * NOTE: Pixman formats are premultiplied.
+ */
+WL_EXPORT int
+weston_surface_copy_content(struct weston_surface *surface,
+ void *target, size_t size,
+ int src_x, int src_y,
+ int width, int height)
+{
+ struct weston_renderer *rer = surface->compositor->renderer;
+ int cw, ch;
+ const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */
+
+ if (!rer->surface_copy_content)
+ return -1;
+
+ weston_surface_get_content_size(surface, &cw, &ch);
+
+ if (src_x < 0 || src_y < 0)
+ return -1;
+
+ if (width <= 0 || height <= 0)
+ return -1;
+
+ if (src_x + width > cw || src_y + height > ch)
+ return -1;
+
+ if (width * bytespp * height > size)
+ return -1;
+
+ return rer->surface_copy_content(surface, target, size,
+ src_x, src_y, width, height);
+}
+
+static void
+subsurface_set_position(struct wl_client *client,
+ struct wl_resource *resource, int32_t x, int32_t y)
+{
+ struct weston_subsurface *sub = wl_resource_get_user_data(resource);
+
+ if (!sub)
+ return;
+
+ sub->position.x = x;
+ sub->position.y = y;
+ sub->position.set = 1;
+}
+
+static struct weston_subsurface *
+subsurface_from_surface(struct weston_surface *surface)
+{
+ struct weston_subsurface *sub;
+
+ sub = weston_surface_to_subsurface(surface);
+ if (sub)
+ return sub;
+
+ wl_list_for_each(sub, &surface->subsurface_list, parent_link)
+ if (sub->surface == surface)
+ return sub;
+
+ return NULL;
+}
+
+static struct weston_subsurface *
+subsurface_sibling_check(struct weston_subsurface *sub,
+ struct weston_surface *surface,
+ const char *request)
+{
+ struct weston_subsurface *sibling;
+
+ sibling = subsurface_from_surface(surface);
+
+ if (!sibling) {
+ wl_resource_post_error(sub->resource,
+ WL_SUBSURFACE_ERROR_BAD_SURFACE,
+ "%s: wl_surface@%d is not a parent or sibling",
+ request, wl_resource_get_id(surface->resource));
+ return NULL;
+ }
+
+ if (sibling->parent != sub->parent) {
+ wl_resource_post_error(sub->resource,
+ WL_SUBSURFACE_ERROR_BAD_SURFACE,
+ "%s: wl_surface@%d has a different parent",
+ request, wl_resource_get_id(surface->resource));
+ return NULL;
+ }
+
+ return sibling;
+}
+
+static void
+subsurface_place_above(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *sibling_resource)
+{
+ struct weston_subsurface *sub = wl_resource_get_user_data(resource);
+ struct weston_surface *surface =
+ wl_resource_get_user_data(sibling_resource);
+ struct weston_subsurface *sibling;
+
+ if (!sub)
+ return;
+
+ sibling = subsurface_sibling_check(sub, surface, "place_above");
+ if (!sibling)
+ return;
+
+ wl_list_remove(&sub->parent_link_pending);
+ wl_list_insert(sibling->parent_link_pending.prev,
+ &sub->parent_link_pending);
+}
+
+static void
+subsurface_place_below(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *sibling_resource)
+{
+ struct weston_subsurface *sub = wl_resource_get_user_data(resource);
+ struct weston_surface *surface =
+ wl_resource_get_user_data(sibling_resource);
+ struct weston_subsurface *sibling;
+
+ if (!sub)
+ return;
+
+ sibling = subsurface_sibling_check(sub, surface, "place_below");
+ if (!sibling)
+ return;
+
+ wl_list_remove(&sub->parent_link_pending);
+ wl_list_insert(&sibling->parent_link_pending,
+ &sub->parent_link_pending);
+}
+
+static void
+subsurface_set_sync(struct wl_client *client, struct wl_resource *resource)
+{
+ struct weston_subsurface *sub = wl_resource_get_user_data(resource);
+
+ if (sub)
+ sub->synchronized = 1;
+}
+
+static void
+subsurface_set_desync(struct wl_client *client, struct wl_resource *resource)
+{
+ struct weston_subsurface *sub = wl_resource_get_user_data(resource);
+
+ if (sub && sub->synchronized) {
+ sub->synchronized = 0;
+
+ /* If sub became effectively desynchronized, flush. */
+ if (!weston_subsurface_is_synchronized(sub))
+ weston_subsurface_synchronized_commit(sub);
+ }
+}
+
+static void
+weston_subsurface_unlink_parent(struct weston_subsurface *sub)
+{
+ wl_list_remove(&sub->parent_link);
+ wl_list_remove(&sub->parent_link_pending);
+ wl_list_remove(&sub->parent_destroy_listener.link);
+ sub->parent = NULL;
+}
+
+static void
+weston_subsurface_destroy(struct weston_subsurface *sub);
+
+static void
+subsurface_handle_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_subsurface *sub =
+ container_of(listener, struct weston_subsurface,
+ surface_destroy_listener);
+ assert(data == sub->surface);
+
+ /* The protocol object (wl_resource) is left inert. */
+ if (sub->resource)
+ wl_resource_set_user_data(sub->resource, NULL);
+
+ weston_subsurface_destroy(sub);
+}
+
+static void
+subsurface_handle_parent_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_subsurface *sub =
+ container_of(listener, struct weston_subsurface,
+ parent_destroy_listener);
+ assert(data == sub->parent);
+ assert(sub->surface != sub->parent);
+
+ if (weston_surface_is_mapped(sub->surface))
+ weston_surface_unmap(sub->surface);
+
+ weston_subsurface_unlink_parent(sub);
+}
+
+static void
+subsurface_resource_destroy(struct wl_resource *resource)
+{
+ struct weston_subsurface *sub = wl_resource_get_user_data(resource);
+
+ if (sub)
+ weston_subsurface_destroy(sub);
+}
+
+static void
+subsurface_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+weston_subsurface_link_parent(struct weston_subsurface *sub,
+ struct weston_surface *parent)
+{
+ sub->parent = parent;
+ sub->parent_destroy_listener.notify = subsurface_handle_parent_destroy;
+ wl_signal_add(&parent->destroy_signal,
+ &sub->parent_destroy_listener);
+
+ wl_list_insert(&parent->subsurface_list, &sub->parent_link);
+ wl_list_insert(&parent->subsurface_list_pending,
+ &sub->parent_link_pending);
+}
+
+static void
+weston_subsurface_link_surface(struct weston_subsurface *sub,
+ struct weston_surface *surface)
+{
+ sub->surface = surface;
+ sub->surface_destroy_listener.notify =
+ subsurface_handle_surface_destroy;
+ wl_signal_add(&surface->destroy_signal,
+ &sub->surface_destroy_listener);
+}
+
+static void
+weston_subsurface_destroy(struct weston_subsurface *sub)
+{
+ struct weston_view *view, *next;
+
+ assert(sub->surface);
+
+ if (sub->resource) {
+ assert(weston_surface_to_subsurface(sub->surface) == sub);
+ assert(sub->parent_destroy_listener.notify ==
+ subsurface_handle_parent_destroy);
+
+ wl_list_for_each_safe(view, next, &sub->surface->views, surface_link) {
+ weston_view_unmap(view);
+ weston_view_destroy(view);
+ }
+
+ if (sub->parent)
+ weston_subsurface_unlink_parent(sub);
+
+ weston_surface_state_fini(&sub->cached);
+ weston_buffer_reference(&sub->cached_buffer_ref, NULL);
+
+ sub->surface->configure = NULL;
+ sub->surface->configure_private = NULL;
+ weston_surface_set_label_func(sub->surface, NULL);
+ } else {
+ /* the dummy weston_subsurface for the parent itself */
+ assert(sub->parent_destroy_listener.notify == NULL);
+ wl_list_remove(&sub->parent_link);
+ wl_list_remove(&sub->parent_link_pending);
+ }
+
+ wl_list_remove(&sub->surface_destroy_listener.link);
+ free(sub);
+}
+
+static const struct wl_subsurface_interface subsurface_implementation = {
+ subsurface_destroy,
+ subsurface_set_position,
+ subsurface_place_above,
+ subsurface_place_below,
+ subsurface_set_sync,
+ subsurface_set_desync
+};
+
+static struct weston_subsurface *
+weston_subsurface_create(uint32_t id, struct weston_surface *surface,
+ struct weston_surface *parent)
+{
+ struct weston_subsurface *sub;
+ struct wl_client *client = wl_resource_get_client(surface->resource);
+
+ sub = zalloc(sizeof *sub);
+ if (sub == NULL)
+ return NULL;
+
+ wl_list_init(&sub->unused_views);
+
+ sub->resource =
+ wl_resource_create(client, &wl_subsurface_interface, 1, id);
+ if (!sub->resource) {
+ free(sub);
+ return NULL;
+ }
+
+ wl_resource_set_implementation(sub->resource,
+ &subsurface_implementation,
+ sub, subsurface_resource_destroy);
+ weston_subsurface_link_surface(sub, surface);
+ weston_subsurface_link_parent(sub, parent);
+ weston_surface_state_init(&sub->cached);
+ sub->cached_buffer_ref.buffer = NULL;
+ sub->synchronized = 1;
+
+ return sub;
+}
+
+/* Create a dummy subsurface for having the parent itself in its
+ * sub-surface lists. Makes stacking order manipulation easy.
+ */
+static struct weston_subsurface *
+weston_subsurface_create_for_parent(struct weston_surface *parent)
+{
+ struct weston_subsurface *sub;
+
+ sub = zalloc(sizeof *sub);
+ if (sub == NULL)
+ return NULL;
+
+ weston_subsurface_link_surface(sub, parent);
+ sub->parent = parent;
+ wl_list_insert(&parent->subsurface_list, &sub->parent_link);
+ wl_list_insert(&parent->subsurface_list_pending,
+ &sub->parent_link_pending);
+
+ return sub;
+}
+
+static void
+subcompositor_get_subsurface(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id,
+ struct wl_resource *surface_resource,
+ struct wl_resource *parent_resource)
+{
+ struct weston_surface *surface =
+ wl_resource_get_user_data(surface_resource);
+ struct weston_surface *parent =
+ wl_resource_get_user_data(parent_resource);
+ struct weston_subsurface *sub;
+ static const char where[] = "get_subsurface: wl_subsurface@";
+
+ if (surface == parent) {
+ wl_resource_post_error(resource,
+ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
+ "%s%d: wl_surface@%d cannot be its own parent",
+ where, id, wl_resource_get_id(surface_resource));
+ return;
+ }
+
+ if (weston_surface_to_subsurface(surface)) {
+ wl_resource_post_error(resource,
+ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
+ "%s%d: wl_surface@%d is already a sub-surface",
+ where, id, wl_resource_get_id(surface_resource));
+ return;
+ }
+
+ if (weston_surface_set_role(surface, "wl_subsurface", resource,
+ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE) < 0)
+ return;
+
+ if (weston_surface_get_main_surface(parent) == surface) {
+ wl_resource_post_error(resource,
+ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
+ "%s%d: wl_surface@%d is an ancestor of parent",
+ where, id, wl_resource_get_id(surface_resource));
+ return;
+ }
+
+ /* make sure the parent is in its own list */
+ if (wl_list_empty(&parent->subsurface_list)) {
+ if (!weston_subsurface_create_for_parent(parent)) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+ }
+
+ sub = weston_subsurface_create(id, surface, parent);
+ if (!sub) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ surface->configure = subsurface_configure;
+ surface->configure_private = sub;
+ weston_surface_set_label_func(surface, subsurface_get_label);
+}
+
+static void
+subcompositor_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_subcompositor_interface subcompositor_interface = {
+ subcompositor_destroy,
+ subcompositor_get_subsurface
+};
+
+static void
+bind_subcompositor(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct weston_compositor *compositor = data;
+ struct wl_resource *resource;
+
+ resource =
+ wl_resource_create(client, &wl_subcompositor_interface, 1, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(resource, &subcompositor_interface,
+ compositor, NULL);
+}
+
+/** Set a DPMS mode on all of the compositor's outputs
+ *
+ * \param compositor The compositor instance
+ * \param state The DPMS state the outputs will be set to
+ */
+static void
+weston_compositor_dpms(struct weston_compositor *compositor,
+ enum dpms_enum state)
+{
+ struct weston_output *output;
+
+ wl_list_for_each(output, &compositor->output_list, link)
+ if (output->set_dpms)
+ output->set_dpms(output, state);
+}
+
+/** Restores the compositor to active status
+ *
+ * \param compositor The compositor instance
+ *
+ * If the compositor was in a sleeping mode, all outputs are powered
+ * back on via DPMS. Otherwise if the compositor was inactive
+ * (idle/locked, offscreen, or sleeping) then the compositor's wake
+ * signal will fire.
+ *
+ * Restarts the idle timer.
+ */
+WL_EXPORT void
+weston_compositor_wake(struct weston_compositor *compositor)
+{
+ uint32_t old_state = compositor->state;
+
+ /* The state needs to be changed before emitting the wake
+ * signal because that may try to schedule a repaint which
+ * will not work if the compositor is still sleeping */
+ compositor->state = WESTON_COMPOSITOR_ACTIVE;
+
+ switch (old_state) {
+ case WESTON_COMPOSITOR_SLEEPING:
+ weston_compositor_dpms(compositor, WESTON_DPMS_ON);
+ /* fall through */
+ case WESTON_COMPOSITOR_IDLE:
+ case WESTON_COMPOSITOR_OFFSCREEN:
+ wl_signal_emit(&compositor->wake_signal, compositor);
+ /* fall through */
+ default:
+ wl_event_source_timer_update(compositor->idle_source,
+ compositor->idle_time * 1000);
+ }
+}
+
+/** Turns off rendering and frame events for the compositor.
+ *
+ * \param compositor The compositor instance
+ *
+ * This is used for example to prevent further rendering while the
+ * compositor is shutting down.
+ *
+ * \note When offscreen state is entered, outputs will be powered
+ * back on if they were sleeping (in DPMS off mode), even though
+ * no rendering will be performed.
+ *
+ * Stops the idle timer.
+ */
+WL_EXPORT void
+weston_compositor_offscreen(struct weston_compositor *compositor)
+{
+ switch (compositor->state) {
+ case WESTON_COMPOSITOR_OFFSCREEN:
+ return;
+ case WESTON_COMPOSITOR_SLEEPING:
+ weston_compositor_dpms(compositor, WESTON_DPMS_ON);
+ /* fall through */
+ default:
+ compositor->state = WESTON_COMPOSITOR_OFFSCREEN;
+ wl_event_source_timer_update(compositor->idle_source, 0);
+ }
+}
+
+/** Powers down all attached output devices
+ *
+ * \param compositor The compositor instance
+ *
+ * Causes rendering to the outputs to cease, and no frame events to be
+ * sent. Only powers down the outputs if the compositor is not already
+ * in sleep mode.
+ *
+ * Stops the idle timer.
+ */
+WL_EXPORT void
+weston_compositor_sleep(struct weston_compositor *compositor)
+{
+ if (compositor->state == WESTON_COMPOSITOR_SLEEPING)
+ return;
+
+ wl_event_source_timer_update(compositor->idle_source, 0);
+ compositor->state = WESTON_COMPOSITOR_SLEEPING;
+ weston_compositor_dpms(compositor, WESTON_DPMS_OFF);
+}
+
+/** Sets compositor to idle mode
+ *
+ * \param data The compositor instance
+ *
+ * This is called when the idle timer fires. Once the compositor is in
+ * idle mode it requires a wake action (e.g. via
+ * weston_compositor_wake()) to restore it. The compositor's
+ * idle_signal will be triggered when the idle event occurs.
+ *
+ * Idleness can be inhibited by setting the compositor's idle_inhibit
+ * property.
+ */
+static int
+idle_handler(void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ if (compositor->idle_inhibit)
+ return 1;
+
+ compositor->state = WESTON_COMPOSITOR_IDLE;
+ wl_signal_emit(&compositor->idle_signal, compositor);
+
+ return 1;
+}
+
+WL_EXPORT void
+weston_plane_init(struct weston_plane *plane,
+ struct weston_compositor *ec,
+ int32_t x, int32_t y)
+{
+ pixman_region32_init(&plane->damage);
+ pixman_region32_init(&plane->clip);
+ plane->x = x;
+ plane->y = y;
+ plane->compositor = ec;
+
+ /* Init the link so that the call to wl_list_remove() when releasing
+ * the plane without ever stacking doesn't lead to a crash */
+ wl_list_init(&plane->link);
+}
+
+WL_EXPORT void
+weston_plane_release(struct weston_plane *plane)
+{
+ struct weston_view *view;
+
+ pixman_region32_fini(&plane->damage);
+ pixman_region32_fini(&plane->clip);
+
+ wl_list_for_each(view, &plane->compositor->view_list, link) {
+ if (view->plane == plane)
+ view->plane = NULL;
+ }
+
+ wl_list_remove(&plane->link);
+}
+
+WL_EXPORT void
+weston_compositor_stack_plane(struct weston_compositor *ec,
+ struct weston_plane *plane,
+ struct weston_plane *above)
+{
+ if (above)
+ wl_list_insert(above->link.prev, &plane->link);
+ else
+ wl_list_insert(&ec->plane_list, &plane->link);
+}
+
+static void unbind_resource(struct wl_resource *resource)
+{
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+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_resource_create(client, &wl_output_interface,
+ version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_list_insert(&output->resource_list, wl_resource_get_link(resource));
+ wl_resource_set_implementation(resource, NULL, data, unbind_resource);
+
+ wl_output_send_geometry(resource,
+ output->x,
+ output->y,
+ output->mm_width,
+ output->mm_height,
+ output->subpixel,
+ output->make, output->model,
+ output->transform);
+ if (version >= WL_OUTPUT_SCALE_SINCE_VERSION)
+ wl_output_send_scale(resource,
+ output->current_scale);
+
+ wl_list_for_each (mode, &output->mode_list, link) {
+ wl_output_send_mode(resource,
+ mode->flags,
+ mode->width,
+ mode->height,
+ mode->refresh);
+ }
+
+ if (version >= WL_OUTPUT_DONE_SINCE_VERSION)
+ wl_output_send_done(resource);
+}
+
+/* Move other outputs when one is resized so the space remains contiguous. */
+static void
+weston_compositor_reflow_outputs(struct weston_compositor *compositor,
+ struct weston_output *resized_output, int delta_width)
+{
+ struct weston_output *output;
+ bool start_resizing = false;
+
+ if (!delta_width)
+ return;
+
+ wl_list_for_each(output, &compositor->output_list, link) {
+ if (output == resized_output) {
+ start_resizing = true;
+ continue;
+ }
+
+ if (start_resizing) {
+ weston_output_move(output, output->x + delta_width, output->y);
+ output->dirty = 1;
+ }
+ }
+}
+
+WL_EXPORT void
+weston_output_destroy(struct weston_output *output)
+{
+ struct wl_resource *resource;
+ struct weston_view *view;
+
+ output->destroying = 1;
+
+ wl_list_for_each(view, &output->compositor->view_list, link) {
+ if (view->output_mask & (1u << output->id))
+ weston_view_assign_output(view);
+ }
+
+ wl_event_source_remove(output->repaint_timer);
+
+ weston_presentation_feedback_discard_list(&output->feedback_list);
+
+ weston_compositor_reflow_outputs(output->compositor, output, output->width);
+ wl_list_remove(&output->link);
+
+ wl_signal_emit(&output->compositor->output_destroyed_signal, output);
+ wl_signal_emit(&output->destroy_signal, output);
+
+ free(output->name);
+ pixman_region32_fini(&output->region);
+ pixman_region32_fini(&output->previous_damage);
+ output->compositor->output_id_pool &= ~(1u << output->id);
+
+ wl_resource_for_each(resource, &output->resource_list) {
+ wl_resource_set_destructor(resource, NULL);
+ }
+
+ wl_global_destroy(output->global);
+}
+
+WL_EXPORT void
+weston_output_update_matrix(struct weston_output *output)
+{
+ float magnification;
+
+ weston_matrix_init(&output->matrix);
+ weston_matrix_translate(&output->matrix, -output->x, -output->y, 0);
+
+ if (output->zoom.active) {
+ magnification = 1 / (1 - output->zoom.spring_z.current);
+ weston_output_update_zoom(output);
+ weston_matrix_translate(&output->matrix, -output->zoom.trans_x,
+ -output->zoom.trans_y, 0);
+ weston_matrix_scale(&output->matrix, magnification,
+ magnification, 1.0);
+ }
+
+ switch (output->transform) {
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ weston_matrix_translate(&output->matrix, -output->width, 0, 0);
+ weston_matrix_scale(&output->matrix, -1, 1, 1);
+ break;
+ }
+
+ switch (output->transform) {
+ default:
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ weston_matrix_translate(&output->matrix, 0, -output->height, 0);
+ weston_matrix_rotate_xy(&output->matrix, 0, 1);
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ weston_matrix_translate(&output->matrix,
+ -output->width, -output->height, 0);
+ weston_matrix_rotate_xy(&output->matrix, -1, 0);
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ weston_matrix_translate(&output->matrix, -output->width, 0, 0);
+ weston_matrix_rotate_xy(&output->matrix, 0, -1);
+ break;
+ }
+
+ if (output->current_scale != 1)
+ weston_matrix_scale(&output->matrix,
+ output->current_scale,
+ output->current_scale, 1);
+
+ output->dirty = 0;
+
+ weston_matrix_invert(&output->inverse_matrix, &output->matrix);
+}
+
+static void
+weston_output_transform_scale_init(struct weston_output *output, uint32_t transform, uint32_t scale)
+{
+ output->transform = transform;
+ output->native_scale = scale;
+ output->current_scale = scale;
+
+ convert_size_by_transform_scale(&output->width, &output->height,
+ output->current_mode->width,
+ output->current_mode->height,
+ transform, scale);
+}
+
+static void
+weston_output_init_geometry(struct weston_output *output, int x, int y)
+{
+ output->x = x;
+ output->y = y;
+
+ pixman_region32_init(&output->previous_damage);
+ pixman_region32_init_rect(&output->region, x, y,
+ output->width,
+ output->height);
+}
+
+WL_EXPORT void
+weston_output_move(struct weston_output *output, int x, int y)
+{
+ struct wl_resource *resource;
+
+ output->move_x = x - output->x;
+ output->move_y = y - output->y;
+
+ if (output->move_x == 0 && output->move_y == 0)
+ return;
+
+ weston_output_init_geometry(output, x, y);
+
+ output->dirty = 1;
+
+ /* Move views on this output. */
+ wl_signal_emit(&output->compositor->output_moved_signal, output);
+
+ /* Notify clients of the change for output position. */
+ wl_resource_for_each(resource, &output->resource_list) {
+ wl_output_send_geometry(resource,
+ output->x,
+ output->y,
+ output->mm_width,
+ output->mm_height,
+ output->subpixel,
+ output->make,
+ output->model,
+ output->transform);
+
+ if (wl_resource_get_version(resource) >= WL_OUTPUT_DONE_SINCE_VERSION)
+ wl_output_send_done(resource);
+ }
+}
+
+/** Initialize a weston_output object's parameters
+ *
+ * \param output The weston_output object to initialize
+ * \param c The output's compositor
+ * \param x x coordinate for the output in global coordinate space
+ * \param y y coordinate for the output in global coordinate space
+ * \param mm_width Physical width of the output as reported by the backend
+ * \param mm_height Physical height of the output as reported by the backend
+ * \param transform Rotation of the output
+ * \param scale Native scaling factor for the output
+ *
+ * Sets up the transformation, zoom, and geometry of the output using
+ * the input properties.
+ *
+ * Establishes a repaint timer for the output with the relevant display
+ * object's event loop. See output_repaint_timer_handler().
+ *
+ * The output is assigned an ID. Weston can support up to 32 distinct
+ * outputs, with IDs numbered from 0-31; the compositor's output_id_pool
+ * is referred to and used to find the first available ID number, and
+ * then this ID is marked as used in output_id_pool.
+ *
+ * The output is also assigned a Wayland global with the wl_output
+ * external interface.
+ */
+WL_EXPORT void
+weston_output_init(struct weston_output *output, struct weston_compositor *c,
+ int x, int y, int mm_width, int mm_height, uint32_t transform,
+ int32_t scale)
+{
+ struct wl_event_loop *loop;
+
+ /* Verify we haven't reached the limit of 32 available output IDs */
+ assert(ffs(~c->output_id_pool) > 0);
+
+ output->compositor = c;
+ output->x = x;
+ output->y = y;
+ output->mm_width = mm_width;
+ output->mm_height = mm_height;
+ output->dirty = 1;
+ output->original_scale = scale;
+
+ weston_output_transform_scale_init(output, transform, scale);
+ weston_output_init_zoom(output);
+
+ weston_output_init_geometry(output, x, y);
+ weston_output_damage(output);
+
+ wl_signal_init(&output->frame_signal);
+ wl_signal_init(&output->destroy_signal);
+ wl_list_init(&output->animation_list);
+ wl_list_init(&output->resource_list);
+ wl_list_init(&output->feedback_list);
+ wl_list_init(&output->link);
+
+ loop = wl_display_get_event_loop(c->wl_display);
+ output->repaint_timer = wl_event_loop_add_timer(loop,
+ output_repaint_timer_handler, output);
+
+ /* Invert the output id pool and look for the lowest numbered
+ * switch (the least significant bit). Take that bit's position
+ * as our ID, and mark it used in the compositor's output_id_pool.
+ */
+ output->id = ffs(~output->compositor->output_id_pool) - 1;
+ output->compositor->output_id_pool |= 1u << output->id;
+
+ output->global =
+ wl_global_create(c->wl_display, &wl_output_interface, 2,
+ output, bind_output);
+}
+
+/** Adds an output to the compositor's output list and
+ * send the compositor's output_created signal.
+ *
+ * \param compositor The compositor instance.
+ * \param output The output to be added.
+ */
+WL_EXPORT void
+weston_compositor_add_output(struct weston_compositor *compositor,
+ struct weston_output *output)
+{
+ wl_list_insert(compositor->output_list.prev, &output->link);
+ wl_signal_emit(&compositor->output_created_signal, output);
+}
+
+WL_EXPORT void
+weston_output_transform_coordinate(struct weston_output *output,
+ double device_x, double device_y,
+ double *x, double *y)
+{
+ struct weston_vector p = { {
+ device_x,
+ device_y,
+ 0.0,
+ 1.0 } };
+
+ weston_matrix_transform(&output->inverse_matrix, &p);
+
+ *x = p.f[0] / p.f[3];
+ *y = p.f[1] / p.f[3];
+}
+
+static void
+destroy_viewport(struct wl_resource *resource)
+{
+ struct weston_surface *surface =
+ wl_resource_get_user_data(resource);
+
+ if (!surface)
+ return;
+
+ surface->viewport_resource = NULL;
+ surface->pending.buffer_viewport.buffer.src_width =
+ wl_fixed_from_int(-1);
+ surface->pending.buffer_viewport.surface.width = -1;
+ surface->pending.buffer_viewport.changed = 1;
+}
+
+static void
+viewport_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+viewport_set_source(struct wl_client *client,
+ struct wl_resource *resource,
+ wl_fixed_t src_x,
+ wl_fixed_t src_y,
+ wl_fixed_t src_width,
+ wl_fixed_t src_height)
+{
+ struct weston_surface *surface =
+ wl_resource_get_user_data(resource);
+
+ if (!surface) {
+ wl_resource_post_error(resource,
+ WP_VIEWPORT_ERROR_NO_SURFACE,
+ "wl_surface for this viewport is no longer exists");
+ return;
+ }
+
+ assert(surface->viewport_resource == resource);
+ assert(surface->resource);
+
+ if (src_width == wl_fixed_from_int(-1) &&
+ src_height == wl_fixed_from_int(-1) &&
+ src_x == wl_fixed_from_int(-1) &&
+ src_y == wl_fixed_from_int(-1)) {
+ /* unset source rect */
+ surface->pending.buffer_viewport.buffer.src_width =
+ wl_fixed_from_int(-1);
+ surface->pending.buffer_viewport.changed = 1;
+ return;
+ }
+
+ if (src_width <= 0 || src_height <= 0 || src_x < 0 || src_y < 0) {
+ wl_resource_post_error(resource,
+ WP_VIEWPORT_ERROR_BAD_VALUE,
+ "wl_surface@%d viewport source "
+ "w=%f <= 0, h=%f <= 0, x=%f < 0, or y=%f < 0",
+ wl_resource_get_id(surface->resource),
+ wl_fixed_to_double(src_width),
+ wl_fixed_to_double(src_height),
+ wl_fixed_to_double(src_x),
+ wl_fixed_to_double(src_y));
+ return;
+ }
+
+ surface->pending.buffer_viewport.buffer.src_x = src_x;
+ surface->pending.buffer_viewport.buffer.src_y = src_y;
+ surface->pending.buffer_viewport.buffer.src_width = src_width;
+ surface->pending.buffer_viewport.buffer.src_height = src_height;
+ surface->pending.buffer_viewport.changed = 1;
+}
+
+static void
+viewport_set_destination(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t dst_width,
+ int32_t dst_height)
+{
+ struct weston_surface *surface =
+ wl_resource_get_user_data(resource);
+
+ if (!surface) {
+ wl_resource_post_error(resource,
+ WP_VIEWPORT_ERROR_NO_SURFACE,
+ "wl_surface for this viewport no longer exists");
+ return;
+ }
+
+ assert(surface->viewport_resource == resource);
+
+ if (dst_width == -1 && dst_height == -1) {
+ /* unset destination size */
+ surface->pending.buffer_viewport.surface.width = -1;
+ surface->pending.buffer_viewport.changed = 1;
+ return;
+ }
+
+ if (dst_width <= 0 || dst_height <= 0) {
+ wl_resource_post_error(resource,
+ WP_VIEWPORT_ERROR_BAD_VALUE,
+ "destination size must be positive (%dx%d)",
+ dst_width, dst_height);
+ return;
+ }
+
+ surface->pending.buffer_viewport.surface.width = dst_width;
+ surface->pending.buffer_viewport.surface.height = dst_height;
+ surface->pending.buffer_viewport.changed = 1;
+}
+
+static const struct wp_viewport_interface viewport_interface = {
+ viewport_destroy,
+ viewport_set_source,
+ viewport_set_destination
+};
+
+static void
+viewporter_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+viewporter_get_viewport(struct wl_client *client,
+ struct wl_resource *viewporter,
+ uint32_t id,
+ struct wl_resource *surface_resource)
+{
+ int version = wl_resource_get_version(viewporter);
+ struct weston_surface *surface =
+ wl_resource_get_user_data(surface_resource);
+ struct wl_resource *resource;
+
+ if (surface->viewport_resource) {
+ wl_resource_post_error(viewporter,
+ WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS,
+ "a viewport for that surface already exists");
+ return;
+ }
+
+ resource = wl_resource_create(client, &wp_viewport_interface,
+ version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &viewport_interface,
+ surface, destroy_viewport);
+
+ surface->viewport_resource = resource;
+}
+
+static const struct wp_viewporter_interface viewporter_interface = {
+ viewporter_destroy,
+ viewporter_get_viewport
+};
+
+static void
+bind_viewporter(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &wp_viewporter_interface,
+ version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &viewporter_interface,
+ NULL, NULL);
+}
+
+static void
+destroy_presentation_feedback(struct wl_resource *feedback_resource)
+{
+ struct weston_presentation_feedback *feedback;
+
+ feedback = wl_resource_get_user_data(feedback_resource);
+
+ wl_list_remove(&feedback->link);
+ free(feedback);
+}
+
+static void
+presentation_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+presentation_feedback(struct wl_client *client,
+ struct wl_resource *presentation_resource,
+ struct wl_resource *surface_resource,
+ uint32_t callback)
+{
+ struct weston_surface *surface;
+ struct weston_presentation_feedback *feedback;
+
+ surface = wl_resource_get_user_data(surface_resource);
+
+ feedback = zalloc(sizeof *feedback);
+ if (feedback == NULL)
+ goto err_calloc;
+
+ feedback->resource = wl_resource_create(client,
+ &wp_presentation_feedback_interface,
+ 1, callback);
+ if (!feedback->resource)
+ goto err_create;
+
+ wl_resource_set_implementation(feedback->resource, NULL, feedback,
+ destroy_presentation_feedback);
+
+ wl_list_insert(&surface->pending.feedback_list, &feedback->link);
+
+ return;
+
+err_create:
+ free(feedback);
+
+err_calloc:
+ wl_client_post_no_memory(client);
+}
+
+static const struct wp_presentation_interface presentation_implementation = {
+ presentation_destroy,
+ presentation_feedback
+};
+
+static void
+bind_presentation(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct weston_compositor *compositor = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &wp_presentation_interface,
+ version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &presentation_implementation,
+ compositor, NULL);
+ wp_presentation_send_clock_id(resource, compositor->presentation_clock);
+}
+
+static void
+compositor_bind(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct weston_compositor *compositor = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &wl_compositor_interface,
+ version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &compositor_interface,
+ compositor, NULL);
+}
+
+WL_EXPORT int
+weston_environment_get_fd(const char *env)
+{
+ char *e, *end;
+ int fd, flags;
+
+ e = getenv(env);
+ if (!e)
+ return -1;
+ fd = strtol(e, &end, 0);
+ if (*end != '\0')
+ return -1;
+
+ flags = fcntl(fd, F_GETFD);
+ if (flags == -1)
+ return -1;
+
+ fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+ unsetenv(env);
+
+ return fd;
+}
+
+static void
+timeline_key_binding_handler(struct weston_keyboard *keyboard, uint32_t time,
+ uint32_t key, void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ if (weston_timeline_enabled_)
+ weston_timeline_close();
+ else
+ weston_timeline_open(compositor);
+}
+
+/** Create the compositor.
+ *
+ * This functions creates and initializes a compositor instance.
+ *
+ * \param display The Wayland display to be used.
+ * \param user_data A pointer to an object that can later be retrieved
+ * using the \ref weston_compositor_get_user_data function.
+ * \return The compositor instance on success or NULL on failure.
+ */
+WL_EXPORT struct weston_compositor *
+weston_compositor_create(struct wl_display *display, void *user_data)
+{
+ struct weston_compositor *ec;
+ struct wl_event_loop *loop;
+
+ ec = zalloc(sizeof *ec);
+ if (!ec)
+ return NULL;
+
+ ec->wl_display = display;
+ ec->user_data = user_data;
+ wl_signal_init(&ec->destroy_signal);
+ wl_signal_init(&ec->create_surface_signal);
+ wl_signal_init(&ec->activate_signal);
+ wl_signal_init(&ec->transform_signal);
+ wl_signal_init(&ec->kill_signal);
+ wl_signal_init(&ec->idle_signal);
+ wl_signal_init(&ec->wake_signal);
+ wl_signal_init(&ec->show_input_panel_signal);
+ wl_signal_init(&ec->hide_input_panel_signal);
+ wl_signal_init(&ec->update_input_panel_signal);
+ wl_signal_init(&ec->seat_created_signal);
+ wl_signal_init(&ec->output_created_signal);
+ wl_signal_init(&ec->output_destroyed_signal);
+ wl_signal_init(&ec->output_moved_signal);
+ wl_signal_init(&ec->output_resized_signal);
+ wl_signal_init(&ec->session_signal);
+ ec->session_active = 1;
+
+ ec->output_id_pool = 0;
+ ec->repaint_msec = DEFAULT_REPAINT_WINDOW;
+
+ if (!wl_global_create(ec->wl_display, &wl_compositor_interface, 4,
+ ec, compositor_bind))
+ goto fail;
+
+ if (!wl_global_create(ec->wl_display, &wl_subcompositor_interface, 1,
+ ec, bind_subcompositor))
+ goto fail;
+
+ if (!wl_global_create(ec->wl_display, &wp_viewporter_interface, 1,
+ ec, bind_viewporter))
+ goto fail;
+
+ if (!wl_global_create(ec->wl_display, &wp_presentation_interface, 1,
+ ec, bind_presentation))
+ goto fail;
+
+ wl_list_init(&ec->view_list);
+ wl_list_init(&ec->plane_list);
+ wl_list_init(&ec->layer_list);
+ wl_list_init(&ec->seat_list);
+ wl_list_init(&ec->output_list);
+ wl_list_init(&ec->key_binding_list);
+ wl_list_init(&ec->modifier_binding_list);
+ wl_list_init(&ec->button_binding_list);
+ wl_list_init(&ec->touch_binding_list);
+ wl_list_init(&ec->axis_binding_list);
+ wl_list_init(&ec->debug_binding_list);
+
+ weston_plane_init(&ec->primary_plane, ec, 0, 0);
+ weston_compositor_stack_plane(ec, &ec->primary_plane, NULL);
+
+ wl_data_device_manager_init(ec->wl_display);
+
+ wl_display_init_shm(ec->wl_display);
+
+ loop = wl_display_get_event_loop(ec->wl_display);
+ ec->idle_source = wl_event_loop_add_timer(loop, idle_handler, ec);
+
+ weston_layer_init(&ec->fade_layer, &ec->layer_list);
+ weston_layer_init(&ec->cursor_layer, &ec->fade_layer.link);
+
+ weston_compositor_add_debug_binding(ec, KEY_T,
+ timeline_key_binding_handler, ec);
+
+ return ec;
+
+fail:
+ free(ec);
+ return NULL;
+}
+
+WL_EXPORT void
+weston_compositor_shutdown(struct weston_compositor *ec)
+{
+ struct weston_output *output, *next;
+
+ wl_event_source_remove(ec->idle_source);
+
+ /* Destroy all outputs associated with this compositor */
+ wl_list_for_each_safe(output, next, &ec->output_list, link)
+ output->destroy(output);
+
+ if (ec->renderer)
+ ec->renderer->destroy(ec);
+
+ weston_binding_list_destroy_all(&ec->key_binding_list);
+ weston_binding_list_destroy_all(&ec->modifier_binding_list);
+ weston_binding_list_destroy_all(&ec->button_binding_list);
+ weston_binding_list_destroy_all(&ec->touch_binding_list);
+ weston_binding_list_destroy_all(&ec->axis_binding_list);
+ weston_binding_list_destroy_all(&ec->debug_binding_list);
+
+ weston_plane_release(&ec->primary_plane);
+}
+
+WL_EXPORT void
+weston_compositor_exit_with_code(struct weston_compositor *compositor,
+ int exit_code)
+{
+ if (compositor->exit_code == EXIT_SUCCESS)
+ compositor->exit_code = exit_code;
+
+ weston_compositor_exit(compositor);
+}
+
+WL_EXPORT void
+weston_compositor_set_default_pointer_grab(struct weston_compositor *ec,
+ const struct weston_pointer_grab_interface *interface)
+{
+ struct weston_seat *seat;
+
+ ec->default_pointer_grab = interface;
+ wl_list_for_each(seat, &ec->seat_list, link) {
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+
+ if (pointer)
+ weston_pointer_set_default_grab(pointer, interface);
+ }
+}
+
+WL_EXPORT int
+weston_compositor_set_presentation_clock(struct weston_compositor *compositor,
+ clockid_t clk_id)
+{
+ struct timespec ts;
+
+ if (clock_gettime(clk_id, &ts) < 0)
+ return -1;
+
+ compositor->presentation_clock = clk_id;
+
+ return 0;
+}
+
+/*
+ * For choosing the software clock, when the display hardware or API
+ * does not expose a compatible presentation timestamp.
+ */
+WL_EXPORT int
+weston_compositor_set_presentation_clock_software(
+ struct weston_compositor *compositor)
+{
+ /* In order of preference */
+ static const clockid_t clocks[] = {
+ CLOCK_MONOTONIC_RAW, /* no jumps, no crawling */
+ CLOCK_MONOTONIC_COARSE, /* no jumps, may crawl, fast & coarse */
+ CLOCK_MONOTONIC, /* no jumps, may crawl */
+ CLOCK_REALTIME_COARSE, /* may jump and crawl, fast & coarse */
+ CLOCK_REALTIME /* may jump and crawl */
+ };
+ unsigned i;
+
+ for (i = 0; i < ARRAY_LENGTH(clocks); i++)
+ if (weston_compositor_set_presentation_clock(compositor,
+ clocks[i]) == 0)
+ return 0;
+
+ weston_log("Error: no suitable presentation clock available.\n");
+
+ return -1;
+}
+
+/** Read the current time from the Presentation clock
+ *
+ * \param compositor
+ * \param ts[out] The current time.
+ *
+ * \note Reading the current time in user space is always imprecise to some
+ * degree.
+ *
+ * This function is never meant to fail. If reading the clock does fail,
+ * an error message is logged and a zero time is returned. Callers are not
+ * supposed to detect or react to failures.
+ */
+WL_EXPORT void
+weston_compositor_read_presentation_clock(
+ const struct weston_compositor *compositor,
+ struct timespec *ts)
+{
+ static bool warned;
+ int ret;
+
+ ret = clock_gettime(compositor->presentation_clock, ts);
+ if (ret < 0) {
+ ts->tv_sec = 0;
+ ts->tv_nsec = 0;
+
+ if (!warned)
+ weston_log("Error: failure to read "
+ "the presentation clock %#x: '%m' (%d)\n",
+ compositor->presentation_clock, errno);
+ warned = true;
+ }
+}
+
+/** Import dmabuf buffer into current renderer
+ *
+ * \param compositor
+ * \param buffer the dmabuf buffer to import
+ * \return true on usable buffers, false otherwise
+ *
+ * This function tests that the linux_dmabuf_buffer is usable
+ * for the current renderer. Returns false on unusable buffers. Usually
+ * usability is tested by importing the dmabufs for composition.
+ *
+ * This hook is also used for detecting if the renderer supports
+ * dmabufs at all. If the renderer hook is NULL, dmabufs are not
+ * supported.
+ * */
+WL_EXPORT bool
+weston_compositor_import_dmabuf(struct weston_compositor *compositor,
+ struct linux_dmabuf_buffer *buffer)
+{
+ struct weston_renderer *renderer;
+
+ renderer = compositor->renderer;
+
+ if (renderer->import_dmabuf == NULL)
+ return false;
+
+ return renderer->import_dmabuf(compositor, buffer);
+}
+
+WL_EXPORT void
+weston_version(int *major, int *minor, int *micro)
+{
+ *major = WESTON_VERSION_MAJOR;
+ *minor = WESTON_VERSION_MINOR;
+ *micro = WESTON_VERSION_MICRO;
+}
+
+WL_EXPORT void *
+weston_load_module(const char *name, const char *entrypoint)
+{
+ const char *builddir = getenv("WESTON_BUILD_DIR");
+ char path[PATH_MAX];
+ void *module, *init;
+
+ if (name == NULL)
+ return NULL;
+
+ if (name[0] != '/') {
+ if (builddir)
+ snprintf(path, sizeof path, "%s/.libs/%s", builddir, name);
+ else
+ snprintf(path, sizeof path, "%s/%s", LIBWESTON_MODULEDIR, name);
+ } else {
+ snprintf(path, sizeof path, "%s", name);
+ }
+
+ module = dlopen(path, RTLD_NOW | RTLD_NOLOAD);
+ if (module) {
+ weston_log("Module '%s' already loaded\n", path);
+ dlclose(module);
+ return NULL;
+ }
+
+ weston_log("Loading module '%s'\n", path);
+ module = dlopen(path, RTLD_NOW);
+ if (!module) {
+ weston_log("Failed to load module: %s\n", dlerror());
+ return NULL;
+ }
+
+ init = dlsym(module, entrypoint);
+ if (!init) {
+ weston_log("Failed to lookup init function: %s\n", dlerror());
+ dlclose(module);
+ return NULL;
+ }
+
+ return init;
+}
+
+
+/** Destroys the compositor.
+ *
+ * This function cleans up the compositor state and destroys it.
+ *
+ * \param compositor The compositor to be destroyed.
+ */
+WL_EXPORT void
+weston_compositor_destroy(struct weston_compositor *compositor)
+{
+ /* prevent further rendering while shutting down */
+ compositor->state = WESTON_COMPOSITOR_OFFSCREEN;
+
+ wl_signal_emit(&compositor->destroy_signal, compositor);
+
+ weston_compositor_xkb_destroy(compositor);
+
+ if (compositor->backend)
+ compositor->backend->destroy(compositor);
+ free(compositor);
+}
+
+/** Instruct the compositor to exit.
+ *
+ * This functions does not directly destroy the compositor object, it merely
+ * command it to start the tear down process. It is not guaranteed that the
+ * tear down will happen immediately.
+ *
+ * \param compositor The compositor to tear down.
+ */
+WL_EXPORT void
+weston_compositor_exit(struct weston_compositor *compositor)
+{
+ compositor->exit(compositor);
+}
+
+/** Return the user data stored in the compositor.
+ *
+ * This function returns the user data pointer set with user_data parameter
+ * to the \ref weston_compositor_create function.
+ */
+WL_EXPORT void *
+weston_compositor_get_user_data(struct weston_compositor *compositor)
+{
+ return compositor->user_data;
+}
+
+static const char * const backend_map[] = {
+ [WESTON_BACKEND_DRM] = "drm-backend.so",
+ [WESTON_BACKEND_FBDEV] = "fbdev-backend.so",
+ [WESTON_BACKEND_HEADLESS] = "headless-backend.so",
+ [WESTON_BACKEND_RDP] = "rdp-backend.so",
+ [WESTON_BACKEND_WAYLAND] = "wayland-backend.so",
+ [WESTON_BACKEND_X11] = "x11-backend.so",
+};
+
+/** Load a backend into a weston_compositor
+ *
+ * A backend must be loaded to make a weston_compositor work. A backend
+ * provides input and output capabilities, and determines the renderer to use.
+ *
+ * \param compositor A compositor that has not had a backend loaded yet.
+ * \param backend Name of the backend file.
+ * \param config_base A pointer to a backend-specific configuration
+ * structure's 'base' member.
+ *
+ * \return 0 on success, or -1 on error.
+ */
+WL_EXPORT int
+weston_compositor_load_backend(struct weston_compositor *compositor,
+ enum weston_compositor_backend backend,
+ struct weston_backend_config *config_base)
+{
+ int (*backend_init)(struct weston_compositor *c,
+ struct weston_backend_config *config_base);
+
+ if (backend < 0 || backend >= ARRAY_LENGTH(backend_map))
+ return -1;
+
+ backend_init = weston_load_module(backend_map[backend], "backend_init");
+ if (!backend_init)
+ return -1;
+
+ return backend_init(compositor, config_base);
+}
--- /dev/null
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * 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.
+ */
+
+#ifndef _WAYLAND_SYSTEM_COMPOSITOR_H_
+#define _WAYLAND_SYSTEM_COMPOSITOR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <time.h>
+#include <pixman.h>
+#include <xkbcommon/xkbcommon.h>
+
+#define WL_HIDE_DEPRECATED
+#include <wayland-server.h>
+
+#include "version.h"
+#include "matrix.h"
+#include "config-parser.h"
+#include "zalloc.h"
+#include "timeline-object.h"
+
+struct weston_transform {
+ struct weston_matrix matrix;
+ struct wl_list link;
+};
+
+struct weston_surface;
+struct weston_buffer;
+struct shell_surface;
+struct weston_seat;
+struct weston_output;
+struct input_method;
+struct weston_pointer;
+struct linux_dmabuf_buffer;
+struct weston_recorder;
+
+enum weston_keyboard_modifier {
+ MODIFIER_CTRL = (1 << 0),
+ MODIFIER_ALT = (1 << 1),
+ MODIFIER_SUPER = (1 << 2),
+ MODIFIER_SHIFT = (1 << 3),
+};
+
+enum weston_keyboard_locks {
+ WESTON_NUM_LOCK = (1 << 0),
+ WESTON_CAPS_LOCK = (1 << 1),
+};
+
+enum weston_led {
+ LED_NUM_LOCK = (1 << 0),
+ LED_CAPS_LOCK = (1 << 1),
+ LED_SCROLL_LOCK = (1 << 2),
+};
+
+struct weston_mode {
+ uint32_t flags;
+ int32_t width, height;
+ uint32_t refresh;
+ struct wl_list link;
+};
+
+struct weston_shell_client {
+ void (*send_configure)(struct weston_surface *surface, int32_t width, int32_t height);
+ void (*send_position)(struct weston_surface *surface, int32_t x, int32_t y);
+};
+
+struct weston_shell_interface {
+ void *shell; /* either desktop or tablet */
+
+ struct shell_surface *(*create_shell_surface)(void *shell,
+ struct weston_surface *surface,
+ const struct weston_shell_client *client);
+ void (*set_toplevel)(struct shell_surface *shsurf);
+
+ void (*set_transient)(struct shell_surface *shsurf,
+ struct weston_surface *parent,
+ int x, int y, uint32_t flags);
+ void (*set_fullscreen)(struct shell_surface *shsurf,
+ uint32_t method,
+ uint32_t framerate,
+ struct weston_output *output);
+ void (*set_xwayland)(struct shell_surface *shsurf,
+ int x, int y, uint32_t flags);
+ int (*move)(struct shell_surface *shsurf, struct weston_pointer *pointer);
+ int (*resize)(struct shell_surface *shsurf,
+ struct weston_pointer *pointer, uint32_t edges);
+ void (*set_title)(struct shell_surface *shsurf,
+ const char *title);
+ void (*set_window_geometry)(struct shell_surface *shsurf,
+ int32_t x, int32_t y,
+ int32_t width, int32_t height);
+ void (*set_maximized)(struct shell_surface *shsurf);
+ void (*set_pid)(struct shell_surface *shsurf, pid_t pid);
+ void (*get_output_work_area)(void *shell, struct weston_output *output, pixman_rectangle32_t *area);
+};
+
+struct weston_animation {
+ void (*frame)(struct weston_animation *animation,
+ struct weston_output *output, uint32_t msecs);
+ int frame_counter;
+ struct wl_list link;
+};
+
+enum {
+ WESTON_SPRING_OVERSHOOT,
+ WESTON_SPRING_CLAMP,
+ WESTON_SPRING_BOUNCE
+};
+
+struct weston_spring {
+ double k;
+ double friction;
+ double current;
+ double target;
+ double previous;
+ double min, max;
+ uint32_t timestamp;
+ uint32_t clip;
+};
+
+struct weston_output_zoom {
+ bool active;
+ float increment;
+ float level;
+ float max_level;
+ float trans_x, trans_y;
+ struct {
+ double x, y;
+ } current;
+ struct weston_seat *seat;
+ struct weston_animation animation_z;
+ struct weston_spring spring_z;
+ struct wl_listener motion_listener;
+};
+
+/* bit compatible with drm definitions. */
+enum dpms_enum {
+ WESTON_DPMS_ON,
+ WESTON_DPMS_STANDBY,
+ WESTON_DPMS_SUSPEND,
+ WESTON_DPMS_OFF
+};
+
+struct weston_output {
+ uint32_t id;
+ char *name;
+
+ void *renderer_state;
+
+ struct wl_list link;
+ struct wl_list resource_list;
+ struct wl_global *global;
+ struct weston_compositor *compositor;
+
+ /** From global to output buffer coordinates. */
+ struct weston_matrix matrix;
+ /** From output buffer to global coordinates. */
+ struct weston_matrix inverse_matrix;
+
+ struct wl_list animation_list;
+ int32_t x, y, width, height;
+ int32_t mm_width, mm_height;
+
+ /** Output area in global coordinates, simple rect */
+ pixman_region32_t region;
+
+ pixman_region32_t previous_damage;
+ int repaint_needed;
+ int repaint_scheduled;
+ struct wl_event_source *repaint_timer;
+ struct weston_output_zoom zoom;
+ int dirty;
+ struct wl_signal frame_signal;
+ struct wl_signal destroy_signal;
+ int move_x, move_y;
+ uint32_t frame_time; /* presentation timestamp in milliseconds */
+ uint64_t msc; /* media stream counter */
+ int disable_planes;
+ int destroying;
+ struct wl_list feedback_list;
+
+ char *make, *model, *serial_number;
+ uint32_t subpixel;
+ uint32_t transform;
+ int32_t native_scale;
+ int32_t current_scale;
+ int32_t original_scale;
+
+ struct weston_mode *native_mode;
+ struct weston_mode *current_mode;
+ struct weston_mode *original_mode;
+ struct wl_list mode_list;
+
+ void (*start_repaint_loop)(struct weston_output *output);
+ int (*repaint)(struct weston_output *output,
+ pixman_region32_t *damage);
+ void (*destroy)(struct weston_output *output);
+ void (*assign_planes)(struct weston_output *output);
+ int (*switch_mode)(struct weston_output *output, struct weston_mode *mode);
+
+ /* backlight values are on 0-255 range, where higher is brighter */
+ int32_t backlight_current;
+ void (*set_backlight)(struct weston_output *output, uint32_t value);
+ void (*set_dpms)(struct weston_output *output, enum dpms_enum level);
+
+ int connection_internal;
+ uint16_t gamma_size;
+ void (*set_gamma)(struct weston_output *output,
+ uint16_t size,
+ uint16_t *r,
+ uint16_t *g,
+ uint16_t *b);
+
+ struct weston_timeline_object timeline;
+};
+
+enum weston_pointer_motion_mask {
+ WESTON_POINTER_MOTION_ABS = 1 << 0,
+ WESTON_POINTER_MOTION_REL = 1 << 1,
+};
+
+struct weston_pointer_motion_event {
+ uint32_t mask;
+ double x;
+ double y;
+ double dx;
+ double dy;
+};
+
+struct weston_pointer_axis_event {
+ uint32_t axis;
+ double value;
+ bool has_discrete;
+ int32_t discrete;
+};
+
+struct weston_pointer_grab;
+struct weston_pointer_grab_interface {
+ void (*focus)(struct weston_pointer_grab *grab);
+ void (*motion)(struct weston_pointer_grab *grab, uint32_t time,
+ struct weston_pointer_motion_event *event);
+ void (*button)(struct weston_pointer_grab *grab,
+ uint32_t time, uint32_t button, uint32_t state);
+ void (*axis)(struct weston_pointer_grab *grab,
+ uint32_t time,
+ struct weston_pointer_axis_event *event);
+ void (*axis_source)(struct weston_pointer_grab *grab, uint32_t source);
+ void (*frame)(struct weston_pointer_grab *grab);
+ void (*cancel)(struct weston_pointer_grab *grab);
+};
+
+struct weston_pointer_grab {
+ const struct weston_pointer_grab_interface *interface;
+ struct weston_pointer *pointer;
+};
+
+struct weston_keyboard_grab;
+struct weston_keyboard_grab_interface {
+ void (*key)(struct weston_keyboard_grab *grab, uint32_t time,
+ uint32_t key, uint32_t state);
+ void (*modifiers)(struct weston_keyboard_grab *grab, uint32_t serial,
+ uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group);
+ void (*cancel)(struct weston_keyboard_grab *grab);
+};
+
+struct weston_keyboard_grab {
+ const struct weston_keyboard_grab_interface *interface;
+ struct weston_keyboard *keyboard;
+};
+
+struct weston_touch_grab;
+struct weston_touch_grab_interface {
+ void (*down)(struct weston_touch_grab *grab,
+ uint32_t time,
+ int touch_id,
+ wl_fixed_t sx,
+ wl_fixed_t sy);
+ void (*up)(struct weston_touch_grab *grab,
+ uint32_t time,
+ int touch_id);
+ void (*motion)(struct weston_touch_grab *grab,
+ uint32_t time,
+ int touch_id,
+ wl_fixed_t sx,
+ wl_fixed_t sy);
+ void (*frame)(struct weston_touch_grab *grab);
+ void (*cancel)(struct weston_touch_grab *grab);
+};
+
+struct weston_touch_grab {
+ const struct weston_touch_grab_interface *interface;
+ struct weston_touch *touch;
+};
+
+struct weston_data_offer {
+ struct wl_resource *resource;
+ struct weston_data_source *source;
+ struct wl_listener source_destroy_listener;
+ uint32_t dnd_actions;
+ enum wl_data_device_manager_dnd_action preferred_dnd_action;
+ bool in_ask;
+};
+
+struct weston_data_source {
+ struct wl_resource *resource;
+ struct wl_signal destroy_signal;
+ struct wl_array mime_types;
+ struct weston_data_offer *offer;
+ struct weston_seat *seat;
+ bool accepted;
+ bool actions_set;
+ uint32_t dnd_actions;
+ enum wl_data_device_manager_dnd_action current_dnd_action;
+ enum wl_data_device_manager_dnd_action compositor_action;
+
+ void (*accept)(struct weston_data_source *source,
+ uint32_t serial, const char *mime_type);
+ void (*send)(struct weston_data_source *source,
+ const char *mime_type, int32_t fd);
+ void (*cancel)(struct weston_data_source *source);
+};
+
+struct weston_pointer_client {
+ struct wl_list link;
+ struct wl_client *client;
+ struct wl_list pointer_resources;
+};
+
+struct weston_pointer {
+ struct weston_seat *seat;
+
+ struct wl_list pointer_clients;
+
+ struct weston_view *focus;
+ struct weston_pointer_client *focus_client;
+ uint32_t focus_serial;
+ struct wl_listener focus_view_listener;
+ struct wl_listener focus_resource_listener;
+ struct wl_signal focus_signal;
+ struct wl_signal motion_signal;
+
+ struct weston_view *sprite;
+ struct wl_listener sprite_destroy_listener;
+ int32_t hotspot_x, hotspot_y;
+
+ struct weston_pointer_grab *grab;
+ struct weston_pointer_grab default_grab;
+ wl_fixed_t grab_x, grab_y;
+ uint32_t grab_button;
+ uint32_t grab_serial;
+ uint32_t grab_time;
+
+ wl_fixed_t x, y;
+ wl_fixed_t sx, sy;
+ uint32_t button_count;
+
+ struct wl_listener output_destroy_listener;
+};
+
+
+struct weston_touch {
+ struct weston_seat *seat;
+
+ struct wl_list resource_list;
+ struct wl_list focus_resource_list;
+ struct weston_view *focus;
+ struct wl_listener focus_view_listener;
+ struct wl_listener focus_resource_listener;
+ uint32_t focus_serial;
+ struct wl_signal focus_signal;
+
+ uint32_t num_tp;
+
+ struct weston_touch_grab *grab;
+ struct weston_touch_grab default_grab;
+ int grab_touch_id;
+ wl_fixed_t grab_x, grab_y;
+ uint32_t grab_serial;
+ uint32_t grab_time;
+};
+
+void
+weston_pointer_motion_to_abs(struct weston_pointer *pointer,
+ struct weston_pointer_motion_event *event,
+ wl_fixed_t *x, wl_fixed_t *y);
+
+struct weston_pointer *
+weston_pointer_create(struct weston_seat *seat);
+void
+weston_pointer_destroy(struct weston_pointer *pointer);
+void
+weston_pointer_send_axis(struct weston_pointer *pointer,
+ uint32_t time,
+ struct weston_pointer_axis_event *event);
+void
+weston_pointer_send_axis_source(struct weston_pointer *pointer,
+ uint32_t source);
+void
+weston_pointer_send_frame(struct weston_pointer *pointer);
+
+void
+weston_pointer_set_focus(struct weston_pointer *pointer,
+ struct weston_view *view,
+ wl_fixed_t sx, wl_fixed_t sy);
+void
+weston_pointer_clear_focus(struct weston_pointer *pointer);
+void
+weston_pointer_start_grab(struct weston_pointer *pointer,
+ struct weston_pointer_grab *grab);
+void
+weston_pointer_end_grab(struct weston_pointer *pointer);
+void
+weston_pointer_clamp(struct weston_pointer *pointer,
+ wl_fixed_t *fx, wl_fixed_t *fy);
+void
+weston_pointer_move(struct weston_pointer *pointer,
+ struct weston_pointer_motion_event *event);
+void
+weston_pointer_set_default_grab(struct weston_pointer *pointer,
+ const struct weston_pointer_grab_interface *interface);
+
+struct weston_keyboard *
+weston_keyboard_create(void);
+void
+weston_keyboard_destroy(struct weston_keyboard *keyboard);
+void
+weston_keyboard_set_focus(struct weston_keyboard *keyboard,
+ struct weston_surface *surface);
+void
+weston_keyboard_start_grab(struct weston_keyboard *device,
+ struct weston_keyboard_grab *grab);
+void
+weston_keyboard_end_grab(struct weston_keyboard *keyboard);
+int
+/*
+ * 'mask' and 'value' should be a bitwise mask of one or more
+ * valued of the weston_keyboard_locks enum.
+ */
+weston_keyboard_set_locks(struct weston_keyboard *keyboard,
+ uint32_t mask, uint32_t value);
+
+struct weston_touch *
+weston_touch_create(void);
+void
+weston_touch_destroy(struct weston_touch *touch);
+void
+weston_touch_set_focus(struct weston_touch *touch,
+ struct weston_view *view);
+void
+weston_touch_start_grab(struct weston_touch *device,
+ struct weston_touch_grab *grab);
+void
+weston_touch_end_grab(struct weston_touch *touch);
+
+void
+wl_data_device_set_keyboard_focus(struct weston_seat *seat);
+
+int
+wl_data_device_manager_init(struct wl_display *display);
+
+
+void
+weston_seat_set_selection(struct weston_seat *seat,
+ struct weston_data_source *source, uint32_t serial);
+void
+weston_seat_send_selection(struct weston_seat *seat, struct wl_client *client);
+
+int
+weston_pointer_start_drag(struct weston_pointer *pointer,
+ struct weston_data_source *source,
+ struct weston_surface *icon,
+ struct wl_client *client);
+int
+weston_touch_start_drag(struct weston_touch *touch,
+ struct weston_data_source *source,
+ struct weston_surface *icon,
+ struct wl_client *client);
+
+struct weston_xkb_info {
+ struct xkb_keymap *keymap;
+ int keymap_fd;
+ size_t keymap_size;
+ char *keymap_area;
+ int32_t ref_count;
+ xkb_mod_index_t shift_mod;
+ xkb_mod_index_t caps_mod;
+ xkb_mod_index_t ctrl_mod;
+ xkb_mod_index_t alt_mod;
+ xkb_mod_index_t mod2_mod;
+ xkb_mod_index_t mod3_mod;
+ xkb_mod_index_t super_mod;
+ xkb_mod_index_t mod5_mod;
+ xkb_led_index_t num_led;
+ xkb_led_index_t caps_led;
+ xkb_led_index_t scroll_led;
+};
+
+struct weston_keyboard {
+ struct weston_seat *seat;
+
+ struct wl_list resource_list;
+ struct wl_list focus_resource_list;
+ struct weston_surface *focus;
+ struct wl_listener focus_resource_listener;
+ uint32_t focus_serial;
+ struct wl_signal focus_signal;
+
+ struct weston_keyboard_grab *grab;
+ struct weston_keyboard_grab default_grab;
+ uint32_t grab_key;
+ uint32_t grab_serial;
+ uint32_t grab_time;
+
+ struct wl_array keys;
+
+ struct {
+ uint32_t mods_depressed;
+ uint32_t mods_latched;
+ uint32_t mods_locked;
+ uint32_t group;
+ } modifiers;
+
+ struct weston_keyboard_grab input_method_grab;
+ struct wl_resource *input_method_resource;
+
+ struct weston_xkb_info *xkb_info;
+ struct {
+ struct xkb_state *state;
+ enum weston_led leds;
+ } xkb_state;
+ struct xkb_keymap *pending_keymap;
+};
+
+struct weston_seat {
+ struct wl_list base_resource_list;
+
+ struct wl_global *global;
+ struct weston_pointer *pointer_state;
+ struct weston_keyboard *keyboard_state;
+ struct weston_touch *touch_state;
+ int pointer_device_count;
+ int keyboard_device_count;
+ int touch_device_count;
+
+ struct weston_output *output; /* constraint */
+
+ struct wl_signal destroy_signal;
+ struct wl_signal updated_caps_signal;
+
+ struct weston_compositor *compositor;
+ struct wl_list link;
+ enum weston_keyboard_modifier modifier_state;
+ struct weston_surface *saved_kbd_focus;
+ struct wl_listener saved_kbd_focus_listener;
+ struct wl_list drag_resource_list;
+
+ uint32_t selection_serial;
+ struct weston_data_source *selection_data_source;
+ struct wl_listener selection_data_source_listener;
+ struct wl_signal selection_signal;
+
+ void (*led_update)(struct weston_seat *ws, enum weston_led leds);
+
+ struct input_method *input_method;
+ char *seat_name;
+};
+
+enum {
+ WESTON_COMPOSITOR_ACTIVE, /* normal rendering and events */
+ WESTON_COMPOSITOR_IDLE, /* shell->unlock called on activity */
+ WESTON_COMPOSITOR_OFFSCREEN, /* no rendering, no frame events */
+ WESTON_COMPOSITOR_SLEEPING /* same as offscreen, but also set dpms
+ * to off */
+};
+
+struct weston_layer_entry {
+ struct wl_list link;
+ struct weston_layer *layer;
+};
+
+struct weston_layer {
+ struct weston_layer_entry view_list;
+ struct wl_list link;
+ pixman_box32_t mask;
+};
+
+struct weston_plane {
+ struct weston_compositor *compositor;
+ pixman_region32_t damage; /**< in global coords */
+ pixman_region32_t clip;
+ int32_t x, y;
+ struct wl_list link;
+};
+
+struct weston_renderer {
+ int (*read_pixels)(struct weston_output *output,
+ pixman_format_code_t format, void *pixels,
+ uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height);
+ void (*repaint_output)(struct weston_output *output,
+ pixman_region32_t *output_damage);
+ void (*flush_damage)(struct weston_surface *surface);
+ void (*attach)(struct weston_surface *es, struct weston_buffer *buffer);
+ void (*surface_set_color)(struct weston_surface *surface,
+ float red, float green,
+ float blue, float alpha);
+ void (*destroy)(struct weston_compositor *ec);
+
+
+ /** See weston_surface_get_content_size() */
+ void (*surface_get_content_size)(struct weston_surface *surface,
+ int *width, int *height);
+
+ /** See weston_surface_copy_content() */
+ int (*surface_copy_content)(struct weston_surface *surface,
+ void *target, size_t size,
+ int src_x, int src_y,
+ int width, int height);
+
+ /** See weston_compositor_import_dmabuf() */
+ bool (*import_dmabuf)(struct weston_compositor *ec,
+ struct linux_dmabuf_buffer *buffer);
+};
+
+enum weston_capability {
+ /* backend/renderer supports arbitrary rotation */
+ WESTON_CAP_ROTATION_ANY = 0x0001,
+
+ /* screencaptures need to be y-flipped */
+ WESTON_CAP_CAPTURE_YFLIP = 0x0002,
+
+ /* backend/renderer has a separate cursor plane */
+ WESTON_CAP_CURSOR_PLANE = 0x0004,
+
+ /* backend supports setting arbitrary resolutions */
+ WESTON_CAP_ARBITRARY_MODES = 0x0008,
+
+ /* renderer supports weston_view_set_mask() clipping */
+ WESTON_CAP_VIEW_CLIP_MASK = 0x0010,
+};
+
+/* Configuration struct for an output.
+ *
+ * This struct is used to pass the configuration for an output
+ * to the compositor backend when creating a new output.
+ * The backend can subclass this struct to handle backend
+ * specific data.
+ */
+struct weston_backend_output_config {
+ uint32_t transform;
+ uint32_t width;
+ uint32_t height;
+ uint32_t scale;
+};
+
+/* Configuration struct for a backend.
+ *
+ * This struct carries the configuration for a backend, and it's
+ * passed to the backend's init entry point. The backend will
+ * likely want to subclass this in order to handle backend specific
+ * data.
+ *
+ * NOTE: Alternate designs were proposed (Feb 2016) for using opaque
+ * structures[1] and for section+key/value getter/setters[2]. The rationale
+ * for selecting the transparent structure design is based on several
+ * assumptions[3] which may require re-evaluating the design choice if they
+ * fail to hold.
+ *
+ * 1: https://lists.freedesktop.org/archives/wayland-devel/2016-February/026989.html
+ * 2: https://lists.freedesktop.org/archives/wayland-devel/2016-February/026929.html
+ * 3: https://lists.freedesktop.org/archives/wayland-devel/2016-February/027228.html
+ */
+struct weston_backend_config {
+ /** Major version for the backend-specific config struct
+ *
+ * This version must match exactly what the backend expects, otherwise
+ * the struct is incompatible.
+ */
+ uint32_t struct_version;
+
+ /** Minor version of the backend-specific config struct
+ *
+ * This must be set to sizeof(struct backend-specific config).
+ * If the value here is smaller than what the backend expects, the
+ * extra config members will assume their default values.
+ *
+ * A value greater than what the backend expects is incompatible.
+ */
+ size_t struct_size;
+};
+
+struct weston_backend {
+ void (*destroy)(struct weston_compositor *compositor);
+ void (*restore)(struct weston_compositor *compositor);
+};
+
+struct weston_compositor {
+ struct wl_signal destroy_signal;
+
+ struct wl_display *wl_display;
+ struct weston_shell_interface shell_interface;
+
+ /* surface signals */
+ struct wl_signal create_surface_signal;
+ struct wl_signal activate_signal;
+ struct wl_signal transform_signal;
+
+ struct wl_signal kill_signal;
+ struct wl_signal idle_signal;
+ struct wl_signal wake_signal;
+
+ struct wl_signal show_input_panel_signal;
+ struct wl_signal hide_input_panel_signal;
+ struct wl_signal update_input_panel_signal;
+
+ struct wl_signal seat_created_signal;
+ struct wl_signal output_created_signal;
+ struct wl_signal output_destroyed_signal;
+ struct wl_signal output_moved_signal;
+ struct wl_signal output_resized_signal; /* callback argument: resized output */
+
+ struct wl_signal session_signal;
+ int session_active;
+
+ struct weston_layer fade_layer;
+ struct weston_layer cursor_layer;
+
+ struct wl_list output_list;
+ struct wl_list seat_list;
+ struct wl_list layer_list;
+ struct wl_list view_list; /* struct weston_view::link */
+ struct wl_list plane_list;
+ struct wl_list key_binding_list;
+ struct wl_list modifier_binding_list;
+ struct wl_list button_binding_list;
+ struct wl_list touch_binding_list;
+ struct wl_list axis_binding_list;
+ struct wl_list debug_binding_list;
+
+ uint32_t state;
+ struct wl_event_source *idle_source;
+ uint32_t idle_inhibit;
+ int idle_time; /* timeout, s */
+
+ const struct weston_pointer_grab_interface *default_pointer_grab;
+
+ /* Repaint state. */
+ struct weston_plane primary_plane;
+ uint32_t capabilities; /* combination of enum weston_capability */
+
+ struct weston_renderer *renderer;
+
+ pixman_format_code_t read_format;
+
+ struct weston_backend *backend;
+
+ struct weston_launcher *launcher;
+
+ uint32_t output_id_pool;
+
+ struct xkb_rule_names xkb_names;
+ struct xkb_context *xkb_context;
+ struct weston_xkb_info *xkb_info;
+
+ /* Raw keyboard processing (no libxkbcommon initialization or handling) */
+ int use_xkbcommon;
+
+ int32_t kb_repeat_rate;
+ int32_t kb_repeat_delay;
+
+ bool vt_switching;
+
+ clockid_t presentation_clock;
+ int32_t repaint_msec;
+
+ int exit_code;
+
+ void *user_data;
+ void (*exit)(struct weston_compositor *c);
+};
+
+struct weston_buffer {
+ struct wl_resource *resource;
+ struct wl_signal destroy_signal;
+ struct wl_listener destroy_listener;
+
+ union {
+ struct wl_shm_buffer *shm_buffer;
+ void *legacy_buffer;
+ };
+ int32_t width, height;
+ uint32_t busy_count;
+ int y_inverted;
+};
+
+struct weston_buffer_reference {
+ struct weston_buffer *buffer;
+ struct wl_listener destroy_listener;
+};
+
+struct weston_buffer_viewport {
+ struct {
+ /* wl_surface.set_buffer_transform */
+ uint32_t transform;
+
+ /* wl_surface.set_scaling_factor */
+ int32_t scale;
+
+ /*
+ * If src_width != wl_fixed_from_int(-1),
+ * then and only then src_* are used.
+ */
+ wl_fixed_t src_x, src_y;
+ wl_fixed_t src_width, src_height;
+ } buffer;
+
+ struct {
+ /*
+ * If width == -1, the size is inferred from the buffer.
+ */
+ int32_t width, height;
+ } surface;
+
+ int changed;
+};
+
+struct weston_region {
+ struct wl_resource *resource;
+ pixman_region32_t region;
+};
+
+/* Using weston_view transformations
+ *
+ * To add a transformation to a view, create a struct weston_transform, and
+ * add it to the list view->geometry.transformation_list. Whenever you
+ * change the list, anything under view->geometry, or anything in the
+ * weston_transforms linked into the list, you must call
+ * weston_view_geometry_dirty().
+ *
+ * The order in the list defines the order of transformations. Let the list
+ * contain the transformation matrices M1, ..., Mn as head to tail. The
+ * transformation is applied to view-local coordinate vector p as
+ * P = Mn * ... * M2 * M1 * p
+ * to produce the global coordinate vector P. The total transform
+ * Mn * ... * M2 * M1
+ * is cached in view->transform.matrix, and the inverse of it in
+ * view->transform.inverse.
+ *
+ * The list always contains view->transform.position transformation, which
+ * is the translation by view->geometry.x and y.
+ *
+ * If you want to apply a transformation in local coordinates, add your
+ * weston_transform to the head of the list. If you want to apply a
+ * transformation in global coordinates, add it to the tail of the list.
+ *
+ * If view->geometry.parent is set, the total transformation of this
+ * view will be the parent's total transformation and this transformation
+ * combined:
+ * Mparent * Mn * ... * M2 * M1
+ */
+
+struct weston_view {
+ struct weston_surface *surface;
+ struct wl_list surface_link;
+ struct wl_signal destroy_signal;
+
+ struct wl_list link; /* weston_compositor::view_list */
+ struct weston_layer_entry layer_link; /* part of geometry */
+ struct weston_plane *plane;
+
+ /* For weston_layer inheritance from another view */
+ struct weston_view *parent_view;
+
+ pixman_region32_t clip; /* See weston_view_damage_below() */
+ float alpha; /* part of geometry, see below */
+
+ void *renderer_state;
+
+ /* Surface geometry state, mutable.
+ * If you change anything, call weston_surface_geometry_dirty().
+ * That includes the transformations referenced from the list.
+ */
+ struct {
+ float x, y; /* surface translation on display */
+
+ /* struct weston_transform */
+ struct wl_list transformation_list;
+
+ /* managed by weston_surface_set_transform_parent() */
+ struct weston_view *parent;
+ struct wl_listener parent_destroy_listener;
+ struct wl_list child_list; /* geometry.parent_link */
+ struct wl_list parent_link;
+
+ /* managed by weston_view_set_mask() */
+ bool scissor_enabled;
+ pixman_region32_t scissor; /* always a simple rect */
+ } geometry;
+
+ /* State derived from geometry state, read-only.
+ * This is updated by weston_view_update_transform().
+ */
+ struct {
+ int dirty;
+
+ /* Approximations in global coordinates:
+ * - boundingbox is guaranteed to include the whole view in
+ * the smallest possible single rectangle.
+ * - opaque is guaranteed to be fully opaque, though not
+ * necessarily include all opaque areas.
+ */
+ pixman_region32_t boundingbox;
+ pixman_region32_t opaque;
+
+ /* matrix and inverse are used only if enabled = 1.
+ * If enabled = 0, use x, y, width, height directly.
+ */
+ int enabled;
+ struct weston_matrix matrix;
+ struct weston_matrix inverse;
+
+ struct weston_transform position; /* matrix from x, y */
+ } transform;
+
+ /*
+ * The primary output for this view.
+ * Used for picking the output for driving internal animations on the
+ * view, inheriting the primary output for related views in shells, etc.
+ */
+ struct weston_output *output;
+
+ /*
+ * A more complete representation of all outputs this surface is
+ * displayed on.
+ */
+ uint32_t output_mask;
+
+ /* Per-surface Presentation feedback flags, controlled by backend. */
+ uint32_t psf_flags;
+};
+
+struct weston_surface_state {
+ /* wl_surface.attach */
+ int newly_attached;
+ struct weston_buffer *buffer;
+ struct wl_listener buffer_destroy_listener;
+ int32_t sx;
+ int32_t sy;
+
+ /* wl_surface.damage */
+ pixman_region32_t damage_surface;
+ /* wl_surface.damage_buffer */
+ pixman_region32_t damage_buffer;
+
+ /* wl_surface.set_opaque_region */
+ pixman_region32_t opaque;
+
+ /* wl_surface.set_input_region */
+ pixman_region32_t input;
+
+ /* wl_surface.frame */
+ struct wl_list frame_callback_list;
+
+ /* presentation.feedback */
+ struct wl_list feedback_list;
+
+ /* wl_surface.set_buffer_transform */
+ /* wl_surface.set_scaling_factor */
+ /* wp_viewport.set_source */
+ /* wp_viewport.set_destination */
+ struct weston_buffer_viewport buffer_viewport;
+};
+
+struct weston_surface {
+ struct wl_resource *resource;
+ struct wl_signal destroy_signal; /* callback argument: this surface */
+ struct weston_compositor *compositor;
+
+ /** Damage in local coordinates from the client, for tex upload. */
+ pixman_region32_t damage;
+
+ pixman_region32_t opaque; /* part of geometry, see below */
+ pixman_region32_t input;
+ int32_t width, height;
+ int32_t ref_count;
+
+ /* Not for long-term storage. This exists for book-keeping while
+ * iterating over surfaces and views
+ */
+ bool touched;
+
+ void *renderer_state;
+
+ struct wl_list views;
+
+ /*
+ * Which output to vsync this surface to.
+ * Used to determine whether to send or queue frame events, and for
+ * other client-visible syncing/throttling tied to the output
+ * repaint cycle.
+ */
+ struct weston_output *output;
+
+ /*
+ * A more complete representation of all outputs this surface is
+ * displayed on.
+ */
+ uint32_t output_mask;
+
+ struct wl_list frame_callback_list;
+ struct wl_list feedback_list;
+
+ struct weston_buffer_reference buffer_ref;
+ struct weston_buffer_viewport buffer_viewport;
+ int32_t width_from_buffer; /* before applying viewport */
+ int32_t height_from_buffer;
+ bool keep_buffer; /* for backends to prevent early release */
+
+ /* wp_viewport resource for this surface */
+ struct wl_resource *viewport_resource;
+
+ /* All the pending state, that wl_surface.commit will apply. */
+ struct weston_surface_state pending;
+
+ /* Matrices representating of the full transformation between
+ * buffer and surface coordinates. These matrices are updated
+ * using the weston_surface_build_buffer_matrix function. */
+ struct weston_matrix buffer_to_surface_matrix;
+ struct weston_matrix surface_to_buffer_matrix;
+
+ /*
+ * If non-NULL, this function will be called on
+ * wl_surface::commit after a new buffer has been set up for
+ * this surface. The integer params are the sx and sy
+ * parameters supplied to wl_surface::attach.
+ */
+ void (*configure)(struct weston_surface *es, int32_t sx, int32_t sy);
+ void *configure_private;
+ int (*get_label)(struct weston_surface *surface, char *buf, size_t len);
+
+ /* Parent's list of its sub-surfaces, weston_subsurface:parent_link.
+ * Contains also the parent itself as a dummy weston_subsurface,
+ * if the list is not empty.
+ */
+ struct wl_list subsurface_list; /* weston_subsurface::parent_link */
+ struct wl_list subsurface_list_pending; /* ...::parent_link_pending */
+
+ /*
+ * For tracking protocol role assignments. Different roles may
+ * have the same configure hook, e.g. in shell.c. Configure hook
+ * may get reset, this will not.
+ * XXX: map configure functions 1:1 to roles, and never reset it,
+ * and replace role_name with configure.
+ */
+ const char *role_name;
+
+ struct weston_timeline_object timeline;
+};
+
+struct weston_subsurface {
+ struct wl_resource *resource;
+
+ /* guaranteed to be valid and non-NULL */
+ struct weston_surface *surface;
+ struct wl_listener surface_destroy_listener;
+
+ /* can be NULL */
+ struct weston_surface *parent;
+ struct wl_listener parent_destroy_listener;
+ struct wl_list parent_link;
+ struct wl_list parent_link_pending;
+
+ struct {
+ int32_t x;
+ int32_t y;
+ int set;
+ } position;
+
+ int has_cached_data;
+ struct weston_surface_state cached;
+ struct weston_buffer_reference cached_buffer_ref;
+
+ int synchronized;
+
+ /* Used for constructing the view tree */
+ struct wl_list unused_views;
+};
+
+enum weston_key_state_update {
+ STATE_UPDATE_AUTOMATIC,
+ STATE_UPDATE_NONE,
+};
+
+void
+weston_version(int *major, int *minor, int *micro);
+
+void
+weston_view_update_transform(struct weston_view *view);
+
+void
+weston_view_geometry_dirty(struct weston_view *view);
+
+void
+weston_view_to_global_fixed(struct weston_view *view,
+ wl_fixed_t sx, wl_fixed_t sy,
+ wl_fixed_t *x, wl_fixed_t *y);
+void
+weston_view_to_global_float(struct weston_view *view,
+ float sx, float sy, float *x, float *y);
+
+void
+weston_view_from_global_float(struct weston_view *view,
+ float x, float y, float *vx, float *vy);
+void
+weston_view_from_global(struct weston_view *view,
+ int32_t x, int32_t y, int32_t *vx, int32_t *vy);
+void
+weston_view_from_global_fixed(struct weston_view *view,
+ wl_fixed_t x, wl_fixed_t y,
+ wl_fixed_t *vx, wl_fixed_t *vy);
+
+void
+weston_surface_to_buffer_float(struct weston_surface *surface,
+ float x, float y, float *bx, float *by);
+pixman_box32_t
+weston_surface_to_buffer_rect(struct weston_surface *surface,
+ pixman_box32_t rect);
+
+void
+weston_surface_to_buffer_region(struct weston_surface *surface,
+ pixman_region32_t *surface_region,
+ pixman_region32_t *buffer_region);
+
+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_seat *seat);
+void
+notify_motion(struct weston_seat *seat, uint32_t time,
+ struct weston_pointer_motion_event *event);
+void
+notify_motion_absolute(struct weston_seat *seat, uint32_t time,
+ double x, double y);
+void
+notify_button(struct weston_seat *seat, uint32_t time, int32_t button,
+ enum wl_pointer_button_state state);
+void
+notify_axis(struct weston_seat *seat, uint32_t time,
+ struct weston_pointer_axis_event *event);
+void
+notify_axis_source(struct weston_seat *seat, uint32_t source);
+
+void
+notify_pointer_frame(struct weston_seat *seat);
+
+void
+notify_key(struct weston_seat *seat, uint32_t time, uint32_t key,
+ enum wl_keyboard_key_state state,
+ enum weston_key_state_update update_state);
+void
+notify_modifiers(struct weston_seat *seat, uint32_t serial);
+
+void
+notify_pointer_focus(struct weston_seat *seat, struct weston_output *output,
+ double x, double y);
+
+void
+notify_keyboard_focus_in(struct weston_seat *seat, struct wl_array *keys,
+ enum weston_key_state_update update_state);
+void
+notify_keyboard_focus_out(struct weston_seat *seat);
+
+void
+notify_touch(struct weston_seat *seat, uint32_t time, int touch_id,
+ double x, double y, int touch_type);
+void
+notify_touch_frame(struct weston_seat *seat);
+
+void
+notify_touch_cancel(struct weston_seat *seat);
+
+void
+weston_layer_entry_insert(struct weston_layer_entry *list,
+ struct weston_layer_entry *entry);
+void
+weston_layer_entry_remove(struct weston_layer_entry *entry);
+void
+weston_layer_init(struct weston_layer *layer, struct wl_list *below);
+
+void
+weston_layer_set_mask(struct weston_layer *layer, int x, int y, int width, int height);
+
+void
+weston_layer_set_mask_infinite(struct weston_layer *layer);
+
+void
+weston_plane_init(struct weston_plane *plane,
+ struct weston_compositor *ec,
+ int32_t x, int32_t y);
+void
+weston_plane_release(struct weston_plane *plane);
+
+void
+weston_compositor_stack_plane(struct weston_compositor *ec,
+ struct weston_plane *plane,
+ struct weston_plane *above);
+
+/* An invalid flag in presented_flags to catch logic errors. */
+#define WP_PRESENTATION_FEEDBACK_INVALID (1U << 31)
+
+void
+weston_output_finish_frame(struct weston_output *output,
+ const struct timespec *stamp,
+ uint32_t presented_flags);
+void
+weston_output_schedule_repaint(struct weston_output *output);
+void
+weston_output_damage(struct weston_output *output);
+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_offscreen(struct weston_compositor *compositor);
+void
+weston_compositor_sleep(struct weston_compositor *compositor);
+struct weston_view *
+weston_compositor_pick_view(struct weston_compositor *compositor,
+ wl_fixed_t x, wl_fixed_t y,
+ wl_fixed_t *sx, wl_fixed_t *sy);
+
+
+struct weston_binding;
+typedef void (*weston_key_binding_handler_t)(struct weston_keyboard *keyboard,
+ uint32_t time, uint32_t key,
+ void *data);
+struct weston_binding *
+weston_compositor_add_key_binding(struct weston_compositor *compositor,
+ uint32_t key,
+ enum weston_keyboard_modifier modifier,
+ weston_key_binding_handler_t binding,
+ void *data);
+
+typedef void (*weston_modifier_binding_handler_t)(struct weston_keyboard *keyboard,
+ enum weston_keyboard_modifier modifier,
+ void *data);
+struct weston_binding *
+weston_compositor_add_modifier_binding(struct weston_compositor *compositor,
+ enum weston_keyboard_modifier modifier,
+ weston_modifier_binding_handler_t binding,
+ void *data);
+
+typedef void (*weston_button_binding_handler_t)(struct weston_pointer *pointer,
+ uint32_t time, uint32_t button,
+ void *data);
+struct weston_binding *
+weston_compositor_add_button_binding(struct weston_compositor *compositor,
+ uint32_t button,
+ enum weston_keyboard_modifier modifier,
+ weston_button_binding_handler_t binding,
+ void *data);
+
+typedef void (*weston_touch_binding_handler_t)(struct weston_touch *touch,
+ uint32_t time,
+ void *data);
+struct weston_binding *
+weston_compositor_add_touch_binding(struct weston_compositor *compositor,
+ enum weston_keyboard_modifier modifier,
+ weston_touch_binding_handler_t binding,
+ void *data);
+
+typedef void (*weston_axis_binding_handler_t)(struct weston_pointer *pointer,
+ uint32_t time,
+ struct weston_pointer_axis_event *event,
+ void *data);
+struct weston_binding *
+weston_compositor_add_axis_binding(struct weston_compositor *compositor,
+ uint32_t axis,
+ enum weston_keyboard_modifier modifier,
+ weston_axis_binding_handler_t binding,
+ void *data);
+struct weston_binding *
+weston_compositor_add_debug_binding(struct weston_compositor *compositor,
+ uint32_t key,
+ weston_key_binding_handler_t binding,
+ void *data);
+void
+weston_binding_destroy(struct weston_binding *binding);
+
+void
+weston_install_debug_key_binding(struct weston_compositor *compositor,
+ uint32_t mod);
+
+void
+weston_binding_list_destroy_all(struct wl_list *list);
+
+void
+weston_compositor_run_key_binding(struct weston_compositor *compositor,
+ struct weston_keyboard *keyboard,
+ uint32_t time,
+ uint32_t key,
+ enum wl_keyboard_key_state state);
+
+void
+weston_compositor_run_modifier_binding(struct weston_compositor *compositor,
+ struct weston_keyboard *keyboard,
+ enum weston_keyboard_modifier modifier,
+ enum wl_keyboard_key_state state);
+void
+weston_compositor_run_button_binding(struct weston_compositor *compositor,
+ struct weston_pointer *pointer, uint32_t time,
+ uint32_t button,
+ enum wl_pointer_button_state value);
+void
+weston_compositor_run_touch_binding(struct weston_compositor *compositor,
+ struct weston_touch *touch, uint32_t time,
+ int touch_type);
+int
+weston_compositor_run_axis_binding(struct weston_compositor *compositor,
+ struct weston_pointer *pointer, uint32_t time,
+ struct weston_pointer_axis_event *event);
+int
+weston_compositor_run_debug_binding(struct weston_compositor *compositor,
+ struct weston_keyboard *keyboard, uint32_t time,
+ uint32_t key,
+ enum wl_keyboard_key_state state);
+
+void
+weston_compositor_set_default_pointer_grab(struct weston_compositor *compositor,
+ const struct weston_pointer_grab_interface *interface);
+
+int
+weston_environment_get_fd(const char *env);
+
+struct wl_list *
+weston_compositor_top(struct weston_compositor *compositor);
+
+struct weston_surface *
+weston_surface_create(struct weston_compositor *compositor);
+
+struct weston_view *
+weston_view_create(struct weston_surface *surface);
+
+void
+weston_view_destroy(struct weston_view *view);
+
+void
+weston_view_set_position(struct weston_view *view,
+ float x, float y);
+
+void
+weston_view_set_transform_parent(struct weston_view *view,
+ struct weston_view *parent);
+
+void
+weston_view_set_mask(struct weston_view *view,
+ int x, int y, int width, int height);
+
+void
+weston_view_set_mask_infinite(struct weston_view *view);
+
+bool
+weston_view_is_mapped(struct weston_view *view);
+
+void
+weston_view_schedule_repaint(struct weston_view *view);
+
+bool
+weston_surface_is_mapped(struct weston_surface *surface);
+
+void
+weston_surface_set_size(struct weston_surface *surface,
+ int32_t width, int32_t height);
+
+void
+weston_surface_schedule_repaint(struct weston_surface *surface);
+
+void
+weston_surface_damage(struct weston_surface *surface);
+
+void
+weston_view_damage_below(struct weston_view *view);
+
+void
+weston_view_move_to_plane(struct weston_view *view,
+ struct weston_plane *plane);
+void
+weston_view_unmap(struct weston_view *view);
+
+void
+weston_surface_unmap(struct weston_surface *surface);
+
+struct weston_surface *
+weston_surface_get_main_surface(struct weston_surface *surface);
+
+int
+weston_surface_set_role(struct weston_surface *surface,
+ const char *role_name,
+ struct wl_resource *error_resource,
+ uint32_t error_code);
+
+void
+weston_surface_set_label_func(struct weston_surface *surface,
+ int (*desc)(struct weston_surface *,
+ char *, size_t));
+
+void
+weston_surface_get_content_size(struct weston_surface *surface,
+ int *width, int *height);
+
+int
+weston_surface_copy_content(struct weston_surface *surface,
+ void *target, size_t size,
+ int src_x, int src_y,
+ int width, int height);
+
+struct weston_buffer *
+weston_buffer_from_resource(struct wl_resource *resource);
+
+void
+weston_buffer_reference(struct weston_buffer_reference *ref,
+ struct weston_buffer *buffer);
+
+uint32_t
+weston_compositor_get_time(void);
+
+void
+weston_compositor_destroy(struct weston_compositor *ec);
+struct weston_compositor *
+weston_compositor_create(struct wl_display *display, void *user_data);
+
+enum weston_compositor_backend {
+ WESTON_BACKEND_DRM,
+ WESTON_BACKEND_FBDEV,
+ WESTON_BACKEND_HEADLESS,
+ WESTON_BACKEND_RDP,
+ WESTON_BACKEND_WAYLAND,
+ WESTON_BACKEND_X11,
+};
+
+int
+weston_compositor_load_backend(struct weston_compositor *compositor,
+ enum weston_compositor_backend backend,
+ struct weston_backend_config *config_base);
+void
+weston_compositor_exit(struct weston_compositor *ec);
+void *
+weston_compositor_get_user_data(struct weston_compositor *compositor);
+int
+weston_compositor_set_presentation_clock(struct weston_compositor *compositor,
+ clockid_t clk_id);
+int
+weston_compositor_set_presentation_clock_software(
+ struct weston_compositor *compositor);
+void
+weston_compositor_read_presentation_clock(
+ const struct weston_compositor *compositor,
+ struct timespec *ts);
+
+bool
+weston_compositor_import_dmabuf(struct weston_compositor *compositor,
+ struct linux_dmabuf_buffer *buffer);
+
+void
+weston_compositor_shutdown(struct weston_compositor *ec);
+void
+weston_compositor_exit_with_code(struct weston_compositor *compositor,
+ int exit_code);
+void
+weston_output_init_zoom(struct weston_output *output);
+void
+weston_output_update_zoom(struct weston_output *output);
+void
+weston_output_activate_zoom(struct weston_output *output,
+ struct weston_seat *seat);
+void
+weston_output_update_matrix(struct weston_output *output);
+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 transform, int32_t scale);
+void
+weston_compositor_add_output(struct weston_compositor *compositor,
+ struct weston_output *output);
+void
+weston_output_destroy(struct weston_output *output);
+void
+weston_output_transform_coordinate(struct weston_output *output,
+ double device_x, double device_y,
+ double *x, double *y);
+
+void
+weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec,
+ const char *seat_name);
+void
+weston_seat_init_pointer(struct weston_seat *seat);
+void
+weston_seat_release_pointer(struct weston_seat *seat);
+int
+weston_seat_init_keyboard(struct weston_seat *seat, struct xkb_keymap *keymap);
+void
+weston_seat_release_keyboard(struct weston_seat *seat);
+void
+weston_seat_init_touch(struct weston_seat *seat);
+void
+weston_seat_release_touch(struct weston_seat *seat);
+void
+weston_seat_repick(struct weston_seat *seat);
+void
+weston_seat_update_keymap(struct weston_seat *seat, struct xkb_keymap *keymap);
+
+void
+weston_seat_release(struct weston_seat *seat);
+int
+weston_compositor_set_xkb_rule_names(struct weston_compositor *ec,
+ struct xkb_rule_names *names);
+void
+weston_compositor_xkb_destroy(struct weston_compositor *ec);
+
+/* String literal of spaces, the same width as the timestamp. */
+#define STAMP_SPACE " "
+
+typedef int (*log_func_t)(const char *fmt, va_list ap);
+void
+weston_log_set_handler(log_func_t log, log_func_t cont);
+int
+weston_vlog(const char *fmt, va_list ap);
+int
+weston_vlog_continue(const char *fmt, va_list ap);
+int
+weston_log(const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+int
+weston_log_continue(const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+
+enum {
+ TTY_ENTER_VT,
+ TTY_LEAVE_VT
+};
+
+struct tty *
+tty_create(struct weston_compositor *compositor, int tty_nr);
+
+void
+tty_destroy(struct tty *tty);
+
+void
+tty_reset(struct tty *tty);
+
+int
+tty_activate_vt(struct tty *tty, int vt);
+
+enum weston_screenshooter_outcome {
+ WESTON_SCREENSHOOTER_SUCCESS,
+ WESTON_SCREENSHOOTER_NO_MEMORY,
+ WESTON_SCREENSHOOTER_BAD_BUFFER
+};
+
+typedef void (*weston_screenshooter_done_func_t)(void *data,
+ enum weston_screenshooter_outcome outcome);
+int
+weston_screenshooter_shoot(struct weston_output *output, struct weston_buffer *buffer,
+ weston_screenshooter_done_func_t done, void *data);
+struct weston_recorder *
+weston_recorder_start(struct weston_output *output, const char *filename);
+void
+weston_recorder_stop(struct weston_recorder *recorder);
+
+struct clipboard *
+clipboard_create(struct weston_seat *seat);
+
+struct text_backend;
+
+struct text_backend *
+text_backend_init(struct weston_compositor *ec);
+
+void
+text_backend_destroy(struct text_backend *text_backend);
+
+struct weston_view_animation;
+typedef void (*weston_view_animation_done_func_t)(struct weston_view_animation *animation, void *data);
+
+void
+weston_view_animation_destroy(struct weston_view_animation *animation);
+
+struct weston_view_animation *
+weston_zoom_run(struct weston_view *view, float start, float stop,
+ weston_view_animation_done_func_t done, void *data);
+
+struct weston_view_animation *
+weston_fade_run(struct weston_view *view,
+ float start, float end, float k,
+ weston_view_animation_done_func_t done, void *data);
+
+struct weston_view_animation *
+weston_move_scale_run(struct weston_view *view, int dx, int dy,
+ float start, float end, int reverse,
+ weston_view_animation_done_func_t done, void *data);
+
+void
+weston_fade_update(struct weston_view_animation *fade, float target);
+
+struct weston_view_animation *
+weston_stable_fade_run(struct weston_view *front_view, float start,
+ struct weston_view *back_view, float end,
+ weston_view_animation_done_func_t done, void *data);
+
+struct weston_view_animation *
+weston_slide_run(struct weston_view *view, float start, float stop,
+ weston_view_animation_done_func_t done, void *data);
+
+void
+weston_surface_set_color(struct weston_surface *surface,
+ float red, float green, float blue, float alpha);
+
+void
+weston_surface_destroy(struct weston_surface *surface);
+
+int
+weston_output_mode_set_native(struct weston_output *output,
+ struct weston_mode *mode,
+ int32_t scale);
+int
+weston_output_mode_switch_to_temporary(struct weston_output *output,
+ struct weston_mode *mode,
+ int32_t scale);
+int
+weston_output_mode_switch_to_native(struct weston_output *output);
+
+int
+noop_renderer_init(struct weston_compositor *ec);
+
+int
+backend_init(struct weston_compositor *c,
+ struct weston_backend_config *config_base);
+int
+module_init(struct weston_compositor *compositor,
+ int *argc, char *argv[]);
+
+void
+weston_transformed_coord(int width, int height,
+ enum wl_output_transform transform,
+ int32_t scale,
+ float sx, float sy, float *bx, float *by);
+pixman_box32_t
+weston_transformed_rect(int width, int height,
+ enum wl_output_transform transform,
+ int32_t scale,
+ pixman_box32_t rect);
+void
+weston_matrix_transform_region(pixman_region32_t *dest,
+ struct weston_matrix *matrix,
+ pixman_region32_t *src);
+void
+weston_transformed_region(int width, int height,
+ enum wl_output_transform transform,
+ int32_t scale,
+ pixman_region32_t *src, pixman_region32_t *dest);
+
+void *
+weston_load_module(const char *name, const char *entrypoint);
+
+int
+weston_parse_transform(const char *transform, uint32_t *out);
+
+const char *
+weston_transform_to_string(uint32_t output_transform);
+
+struct weston_keyboard *
+weston_seat_get_keyboard(struct weston_seat *seat);
+
+struct weston_pointer *
+weston_seat_get_pointer(struct weston_seat *seat);
+
+struct weston_touch *
+weston_seat_get_touch(struct weston_seat *seat);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright © 2011 Kristian Høgsberg
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "compositor.h"
+#include "shared/helpers.h"
+
+struct weston_drag {
+ struct wl_client *client;
+ struct weston_data_source *data_source;
+ struct wl_listener data_source_listener;
+ struct weston_view *focus;
+ struct wl_resource *focus_resource;
+ struct wl_listener focus_listener;
+ struct weston_view *icon;
+ struct wl_listener icon_destroy_listener;
+ int32_t dx, dy;
+ struct weston_keyboard_grab keyboard_grab;
+};
+
+struct weston_pointer_drag {
+ struct weston_drag base;
+ struct weston_pointer_grab grab;
+};
+
+struct weston_touch_drag {
+ struct weston_drag base;
+ struct weston_touch_grab grab;
+};
+
+#define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
+
+static void
+data_offer_accept(struct wl_client *client, struct wl_resource *resource,
+ uint32_t serial, const char *mime_type)
+{
+ struct weston_data_offer *offer = wl_resource_get_user_data(resource);
+
+ /* Protect against untimely calls from older data offers */
+ if (!offer->source || offer != offer->source->offer)
+ return;
+
+ /* 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? */
+
+ offer->source->accept(offer->source, serial, mime_type);
+ offer->source->accepted = mime_type != NULL;
+}
+
+static void
+data_offer_receive(struct wl_client *client, struct wl_resource *resource,
+ const char *mime_type, int32_t fd)
+{
+ struct weston_data_offer *offer = wl_resource_get_user_data(resource);
+
+ if (offer->source && offer == offer->source->offer)
+ offer->source->send(offer->source, mime_type, fd);
+ else
+ close(fd);
+}
+
+static void
+data_offer_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+data_source_notify_finish(struct weston_data_source *source)
+{
+ if (!source->actions_set)
+ return;
+
+ if (source->offer->in_ask &&
+ wl_resource_get_version(source->resource) >=
+ WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
+ wl_data_source_send_action(source->resource,
+ source->current_dnd_action);
+ }
+
+ if (wl_resource_get_version(source->resource) >=
+ WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) {
+ wl_data_source_send_dnd_finished(source->resource);
+ }
+
+ source->offer = NULL;
+}
+
+static uint32_t
+data_offer_choose_action(struct weston_data_offer *offer)
+{
+ uint32_t available_actions, preferred_action = 0;
+ uint32_t source_actions, offer_actions;
+
+ if (wl_resource_get_version(offer->resource) >=
+ WL_DATA_OFFER_ACTION_SINCE_VERSION) {
+ offer_actions = offer->dnd_actions;
+ preferred_action = offer->preferred_dnd_action;
+ } else {
+ offer_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+ }
+
+ if (wl_resource_get_version(offer->source->resource) >=
+ WL_DATA_SOURCE_ACTION_SINCE_VERSION)
+ source_actions = offer->source->dnd_actions;
+ else
+ source_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+
+ available_actions = offer_actions & source_actions;
+
+ if (!available_actions)
+ return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
+
+ if (offer->source->seat &&
+ offer->source->compositor_action & available_actions)
+ return offer->source->compositor_action;
+
+ /* If the dest side has a preferred DnD action, use it */
+ if ((preferred_action & available_actions) != 0)
+ return preferred_action;
+
+ /* Use the first found action, in bit order */
+ return 1 << (ffs(available_actions) - 1);
+}
+
+static void
+data_offer_update_action(struct weston_data_offer *offer)
+{
+ uint32_t action;
+
+ if (!offer->source)
+ return;
+
+ action = data_offer_choose_action(offer);
+
+ if (offer->source->current_dnd_action == action)
+ return;
+
+ offer->source->current_dnd_action = action;
+
+ if (offer->in_ask)
+ return;
+
+ if (wl_resource_get_version(offer->source->resource) >=
+ WL_DATA_SOURCE_ACTION_SINCE_VERSION)
+ wl_data_source_send_action(offer->source->resource, action);
+
+ if (wl_resource_get_version(offer->resource) >=
+ WL_DATA_OFFER_ACTION_SINCE_VERSION)
+ wl_data_offer_send_action(offer->resource, action);
+}
+
+static void
+data_offer_set_actions(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t dnd_actions, uint32_t preferred_action)
+{
+ struct weston_data_offer *offer = wl_resource_get_user_data(resource);
+
+ if (dnd_actions & ~ALL_ACTIONS) {
+ wl_resource_post_error(offer->resource,
+ WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK,
+ "invalid action mask %x", dnd_actions);
+ return;
+ }
+
+ if (preferred_action &&
+ (!(preferred_action & dnd_actions) ||
+ __builtin_popcount(preferred_action) > 1)) {
+ wl_resource_post_error(offer->resource,
+ WL_DATA_OFFER_ERROR_INVALID_ACTION,
+ "invalid action %x", preferred_action);
+ return;
+ }
+
+ offer->dnd_actions = dnd_actions;
+ offer->preferred_dnd_action = preferred_action;
+ data_offer_update_action(offer);
+}
+
+static void
+data_offer_finish(struct wl_client *client, struct wl_resource *resource)
+{
+ struct weston_data_offer *offer = wl_resource_get_user_data(resource);
+
+ if (!offer->source || offer->source->offer != offer)
+ return;
+
+ /* Disallow finish while we have a grab driving drag-and-drop, or
+ * if the negotiation is not at the right stage
+ */
+ if (offer->source->seat ||
+ !offer->source->accepted) {
+ wl_resource_post_error(offer->resource,
+ WL_DATA_OFFER_ERROR_INVALID_FINISH,
+ "premature finish request");
+ return;
+ }
+
+ switch (offer->source->current_dnd_action) {
+ case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE:
+ case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK:
+ wl_resource_post_error(offer->resource,
+ WL_DATA_OFFER_ERROR_INVALID_OFFER,
+ "offer finished with an invalid action");
+ return;
+ default:
+ break;
+ }
+
+ data_source_notify_finish(offer->source);
+}
+
+static const struct wl_data_offer_interface data_offer_interface = {
+ data_offer_accept,
+ data_offer_receive,
+ data_offer_destroy,
+ data_offer_finish,
+ data_offer_set_actions,
+};
+
+static void
+destroy_data_offer(struct wl_resource *resource)
+{
+ struct weston_data_offer *offer = wl_resource_get_user_data(resource);
+
+ if (!offer->source)
+ goto out;
+
+ wl_list_remove(&offer->source_destroy_listener.link);
+
+ if (offer->source->offer != offer)
+ goto out;
+
+ /* If the drag destination has version < 3, wl_data_offer.finish
+ * won't be called, so do this here as a safety net, because
+ * we still want the version >=3 drag source to be happy.
+ */
+ if (wl_resource_get_version(offer->resource) <
+ WL_DATA_OFFER_ACTION_SINCE_VERSION) {
+ data_source_notify_finish(offer->source);
+ } else if (offer->source->resource &&
+ wl_resource_get_version(offer->source->resource) >=
+ WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) {
+ wl_data_source_send_cancelled(offer->source->resource);
+ }
+
+ offer->source->offer = NULL;
+out:
+ free(offer);
+}
+
+static void
+destroy_offer_data_source(struct wl_listener *listener, void *data)
+{
+ struct weston_data_offer *offer;
+
+ offer = container_of(listener, struct weston_data_offer,
+ source_destroy_listener);
+
+ offer->source = NULL;
+}
+
+static struct weston_data_offer *
+weston_data_source_send_offer(struct weston_data_source *source,
+ struct wl_resource *target)
+{
+ struct weston_data_offer *offer;
+ char **p;
+
+ offer = malloc(sizeof *offer);
+ if (offer == NULL)
+ return NULL;
+
+ offer->resource =
+ wl_resource_create(wl_resource_get_client(target),
+ &wl_data_offer_interface,
+ wl_resource_get_version(target), 0);
+ if (offer->resource == NULL) {
+ free(offer);
+ return NULL;
+ }
+
+ wl_resource_set_implementation(offer->resource, &data_offer_interface,
+ offer, destroy_data_offer);
+
+ offer->in_ask = false;
+ offer->dnd_actions = 0;
+ offer->preferred_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
+ offer->source = source;
+ offer->source_destroy_listener.notify = destroy_offer_data_source;
+ wl_signal_add(&source->destroy_signal,
+ &offer->source_destroy_listener);
+
+ wl_data_device_send_data_offer(target, offer->resource);
+
+ wl_array_for_each(p, &source->mime_types)
+ wl_data_offer_send_offer(offer->resource, *p);
+
+ source->offer = offer;
+ source->accepted = false;
+
+ return offer;
+}
+
+static void
+data_source_offer(struct wl_client *client,
+ struct wl_resource *resource,
+ const char *type)
+{
+ struct weston_data_source *source =
+ wl_resource_get_user_data(resource);
+ 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);
+}
+
+static void
+data_source_set_actions(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t dnd_actions)
+{
+ struct weston_data_source *source =
+ wl_resource_get_user_data(resource);
+
+ if (source->actions_set) {
+ wl_resource_post_error(source->resource,
+ WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
+ "cannot set actions more than once");
+ return;
+ }
+
+ if (dnd_actions & ~ALL_ACTIONS) {
+ wl_resource_post_error(source->resource,
+ WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
+ "invalid action mask %x", dnd_actions);
+ return;
+ }
+
+ if (source->seat) {
+ wl_resource_post_error(source->resource,
+ WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
+ "invalid action change after "
+ "wl_data_device.start_drag");
+ return;
+ }
+
+ source->dnd_actions = dnd_actions;
+ source->actions_set = true;
+}
+
+static struct wl_data_source_interface data_source_interface = {
+ data_source_offer,
+ data_source_destroy,
+ data_source_set_actions
+};
+
+static void
+drag_surface_configure(struct weston_drag *drag,
+ struct weston_pointer *pointer,
+ struct weston_touch *touch,
+ struct weston_surface *es,
+ int32_t sx, int32_t sy)
+{
+ struct weston_layer_entry *list;
+ float fx, fy;
+
+ assert((pointer != NULL && touch == NULL) ||
+ (pointer == NULL && touch != NULL));
+
+ if (!weston_surface_is_mapped(es) && es->buffer_ref.buffer) {
+ if (pointer && pointer->sprite &&
+ weston_view_is_mapped(pointer->sprite))
+ list = &pointer->sprite->layer_link;
+ else
+ list = &es->compositor->cursor_layer.view_list;
+
+ weston_layer_entry_remove(&drag->icon->layer_link);
+ weston_layer_entry_insert(list, &drag->icon->layer_link);
+ weston_view_update_transform(drag->icon);
+ pixman_region32_clear(&es->pending.input);
+ }
+
+ drag->dx += sx;
+ drag->dy += sy;
+
+ /* init to 0 for avoiding a compile warning */
+ fx = fy = 0;
+ if (pointer) {
+ fx = wl_fixed_to_double(pointer->x) + drag->dx;
+ fy = wl_fixed_to_double(pointer->y) + drag->dy;
+ } else if (touch) {
+ fx = wl_fixed_to_double(touch->grab_x) + drag->dx;
+ fy = wl_fixed_to_double(touch->grab_y) + drag->dy;
+ }
+ weston_view_set_position(drag->icon, fx, fy);
+}
+
+static int
+pointer_drag_surface_get_label(struct weston_surface *surface,
+ char *buf, size_t len)
+{
+ return snprintf(buf, len, "pointer drag icon");
+}
+
+static void
+pointer_drag_surface_configure(struct weston_surface *es,
+ int32_t sx, int32_t sy)
+{
+ struct weston_pointer_drag *drag = es->configure_private;
+ struct weston_pointer *pointer = drag->grab.pointer;
+
+ assert(es->configure == pointer_drag_surface_configure);
+
+ drag_surface_configure(&drag->base, pointer, NULL, es, sx, sy);
+}
+
+static int
+touch_drag_surface_get_label(struct weston_surface *surface,
+ char *buf, size_t len)
+{
+ return snprintf(buf, len, "touch drag icon");
+}
+
+static void
+touch_drag_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy)
+{
+ struct weston_touch_drag *drag = es->configure_private;
+ struct weston_touch *touch = drag->grab.touch;
+
+ assert(es->configure == touch_drag_surface_configure);
+
+ drag_surface_configure(&drag->base, NULL, touch, es, sx, sy);
+}
+
+static void
+destroy_drag_focus(struct wl_listener *listener, void *data)
+{
+ struct weston_drag *drag =
+ container_of(listener, struct weston_drag, focus_listener);
+
+ drag->focus_resource = NULL;
+}
+
+static void
+weston_drag_set_focus(struct weston_drag *drag,
+ struct weston_seat *seat,
+ struct weston_view *view,
+ wl_fixed_t sx, wl_fixed_t sy)
+{
+ struct wl_resource *resource, *offer_resource = NULL;
+ struct wl_display *display = seat->compositor->wl_display;
+ struct weston_data_offer *offer;
+ uint32_t serial;
+
+ if (drag->focus && view && drag->focus->surface == view->surface) {
+ drag->focus = view;
+ return;
+ }
+
+ if (drag->focus_resource) {
+ wl_data_device_send_leave(drag->focus_resource);
+ wl_list_remove(&drag->focus_listener.link);
+ drag->focus_resource = NULL;
+ drag->focus = NULL;
+ }
+
+ if (!view || !view->surface->resource)
+ return;
+
+ if (!drag->data_source &&
+ wl_resource_get_client(view->surface->resource) != drag->client)
+ return;
+
+ if (drag->data_source &&
+ drag->data_source->offer) {
+ /* Unlink the offer from the source */
+ offer = drag->data_source->offer;
+ offer->source = NULL;
+ drag->data_source->offer = NULL;
+ wl_list_remove(&offer->source_destroy_listener.link);
+ }
+
+ resource = wl_resource_find_for_client(&seat->drag_resource_list,
+ wl_resource_get_client(view->surface->resource));
+ if (!resource)
+ return;
+
+ serial = wl_display_next_serial(display);
+
+ if (drag->data_source) {
+ drag->data_source->accepted = false;
+ offer = weston_data_source_send_offer(drag->data_source, resource);
+ if (offer == NULL)
+ return;
+
+ data_offer_update_action(offer);
+
+ offer_resource = offer->resource;
+ if (wl_resource_get_version (offer_resource) >=
+ WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) {
+ wl_data_offer_send_source_actions (offer_resource,
+ drag->data_source->dnd_actions);
+ }
+ }
+
+ wl_data_device_send_enter(resource, serial, view->surface->resource,
+ sx, sy, offer_resource);
+
+ drag->focus = view;
+ drag->focus_listener.notify = destroy_drag_focus;
+ wl_resource_add_destroy_listener(resource, &drag->focus_listener);
+ drag->focus_resource = resource;
+}
+
+static void
+drag_grab_focus(struct weston_pointer_grab *grab)
+{
+ struct weston_pointer_drag *drag =
+ container_of(grab, struct weston_pointer_drag, grab);
+ struct weston_pointer *pointer = grab->pointer;
+ struct weston_view *view;
+ wl_fixed_t sx, sy;
+
+ view = weston_compositor_pick_view(pointer->seat->compositor,
+ pointer->x, pointer->y,
+ &sx, &sy);
+ if (drag->base.focus != view)
+ weston_drag_set_focus(&drag->base, pointer->seat, view, sx, sy);
+}
+
+static void
+drag_grab_motion(struct weston_pointer_grab *grab, uint32_t time,
+ struct weston_pointer_motion_event *event)
+{
+ struct weston_pointer_drag *drag =
+ container_of(grab, struct weston_pointer_drag, grab);
+ struct weston_pointer *pointer = drag->grab.pointer;
+ float fx, fy;
+ wl_fixed_t sx, sy;
+
+ weston_pointer_move(pointer, event);
+
+ if (drag->base.icon) {
+ fx = wl_fixed_to_double(pointer->x) + drag->base.dx;
+ fy = wl_fixed_to_double(pointer->y) + drag->base.dy;
+ weston_view_set_position(drag->base.icon, fx, fy);
+ weston_view_schedule_repaint(drag->base.icon);
+ }
+
+ if (drag->base.focus_resource) {
+ weston_view_from_global_fixed(drag->base.focus,
+ pointer->x, pointer->y,
+ &sx, &sy);
+
+ wl_data_device_send_motion(drag->base.focus_resource, time, sx, sy);
+ }
+}
+
+static void
+data_device_end_drag_grab(struct weston_drag *drag,
+ struct weston_seat *seat)
+{
+ if (drag->icon) {
+ if (weston_view_is_mapped(drag->icon))
+ weston_view_unmap(drag->icon);
+
+ drag->icon->surface->configure = NULL;
+ weston_surface_set_label_func(drag->icon->surface, NULL);
+ pixman_region32_clear(&drag->icon->surface->pending.input);
+ wl_list_remove(&drag->icon_destroy_listener.link);
+ weston_view_destroy(drag->icon);
+ }
+
+ weston_drag_set_focus(drag, seat, NULL, 0, 0);
+}
+
+static void
+data_device_end_pointer_drag_grab(struct weston_pointer_drag *drag)
+{
+ struct weston_pointer *pointer = drag->grab.pointer;
+ struct weston_keyboard *keyboard = drag->base.keyboard_grab.keyboard;
+
+ data_device_end_drag_grab(&drag->base, pointer->seat);
+ weston_pointer_end_grab(pointer);
+ weston_keyboard_end_grab(keyboard);
+ free(drag);
+}
+
+static void
+drag_grab_button(struct weston_pointer_grab *grab,
+ uint32_t time, uint32_t button, uint32_t state_w)
+{
+ struct weston_pointer_drag *drag =
+ container_of(grab, struct weston_pointer_drag, grab);
+ struct weston_pointer *pointer = drag->grab.pointer;
+ enum wl_pointer_button_state state = state_w;
+ struct weston_data_source *data_source = drag->base.data_source;
+
+ if (data_source &&
+ pointer->grab_button == button &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED) {
+ if (drag->base.focus_resource &&
+ data_source->accepted &&
+ data_source->current_dnd_action) {
+ wl_data_device_send_drop(drag->base.focus_resource);
+
+ if (wl_resource_get_version(data_source->resource) >=
+ WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION)
+ wl_data_source_send_dnd_drop_performed(data_source->resource);
+
+ data_source->offer->in_ask =
+ data_source->current_dnd_action ==
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
+
+ data_source->seat = NULL;
+ } else if (wl_resource_get_version(data_source->resource) >=
+ WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) {
+ wl_data_source_send_cancelled(data_source->resource);
+ }
+ }
+
+ if (pointer->button_count == 0 &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED) {
+ if (drag->base.data_source)
+ wl_list_remove(&drag->base.data_source_listener.link);
+ data_device_end_pointer_drag_grab(drag);
+ }
+}
+
+static void
+drag_grab_axis(struct weston_pointer_grab *grab,
+ uint32_t time, struct weston_pointer_axis_event *event)
+{
+}
+
+static void
+drag_grab_axis_source(struct weston_pointer_grab *grab, uint32_t source)
+{
+}
+
+static void
+drag_grab_frame(struct weston_pointer_grab *grab)
+{
+}
+
+static void
+drag_grab_cancel(struct weston_pointer_grab *grab)
+{
+ struct weston_pointer_drag *drag =
+ container_of(grab, struct weston_pointer_drag, grab);
+
+ if (drag->base.data_source)
+ wl_list_remove(&drag->base.data_source_listener.link);
+
+ data_device_end_pointer_drag_grab(drag);
+}
+
+static const struct weston_pointer_grab_interface pointer_drag_grab_interface = {
+ drag_grab_focus,
+ drag_grab_motion,
+ drag_grab_button,
+ drag_grab_axis,
+ drag_grab_axis_source,
+ drag_grab_frame,
+ drag_grab_cancel,
+};
+
+static void
+drag_grab_touch_down(struct weston_touch_grab *grab, uint32_t time,
+ int touch_id, wl_fixed_t sx, wl_fixed_t sy)
+{
+}
+
+static void
+data_device_end_touch_drag_grab(struct weston_touch_drag *drag)
+{
+ struct weston_touch *touch = drag->grab.touch;
+ struct weston_keyboard *keyboard = drag->base.keyboard_grab.keyboard;
+
+ data_device_end_drag_grab(&drag->base, touch->seat);
+ weston_touch_end_grab(touch);
+ weston_keyboard_end_grab(keyboard);
+ free(drag);
+}
+
+static void
+drag_grab_touch_up(struct weston_touch_grab *grab,
+ uint32_t time, int touch_id)
+{
+ struct weston_touch_drag *touch_drag =
+ container_of(grab, struct weston_touch_drag, grab);
+ struct weston_touch *touch = grab->touch;
+
+ if (touch_id != touch->grab_touch_id)
+ return;
+
+ if (touch_drag->base.focus_resource)
+ wl_data_device_send_drop(touch_drag->base.focus_resource);
+ if (touch_drag->base.data_source)
+ wl_list_remove(&touch_drag->base.data_source_listener.link);
+ data_device_end_touch_drag_grab(touch_drag);
+}
+
+static void
+drag_grab_touch_focus(struct weston_touch_drag *drag)
+{
+ struct weston_touch *touch = drag->grab.touch;
+ struct weston_view *view;
+ wl_fixed_t view_x, view_y;
+
+ view = weston_compositor_pick_view(touch->seat->compositor,
+ touch->grab_x, touch->grab_y,
+ &view_x, &view_y);
+ if (drag->base.focus != view)
+ weston_drag_set_focus(&drag->base, touch->seat,
+ view, view_x, view_y);
+}
+
+static void
+drag_grab_touch_motion(struct weston_touch_grab *grab, uint32_t time,
+ int touch_id, wl_fixed_t x, wl_fixed_t y)
+{
+ struct weston_touch_drag *touch_drag =
+ container_of(grab, struct weston_touch_drag, grab);
+ struct weston_touch *touch = grab->touch;
+ wl_fixed_t view_x, view_y;
+ float fx, fy;
+
+ if (touch_id != touch->grab_touch_id)
+ return;
+
+ drag_grab_touch_focus(touch_drag);
+ if (touch_drag->base.icon) {
+ fx = wl_fixed_to_double(touch->grab_x) + touch_drag->base.dx;
+ fy = wl_fixed_to_double(touch->grab_y) + touch_drag->base.dy;
+ weston_view_set_position(touch_drag->base.icon, fx, fy);
+ weston_view_schedule_repaint(touch_drag->base.icon);
+ }
+
+ if (touch_drag->base.focus_resource) {
+ weston_view_from_global_fixed(touch_drag->base.focus,
+ touch->grab_x, touch->grab_y,
+ &view_x, &view_y);
+ wl_data_device_send_motion(touch_drag->base.focus_resource, time,
+ view_x, view_y);
+ }
+}
+
+static void
+drag_grab_touch_frame(struct weston_touch_grab *grab)
+{
+}
+
+static void
+drag_grab_touch_cancel(struct weston_touch_grab *grab)
+{
+ struct weston_touch_drag *touch_drag =
+ container_of(grab, struct weston_touch_drag, grab);
+
+ if (touch_drag->base.data_source)
+ wl_list_remove(&touch_drag->base.data_source_listener.link);
+ data_device_end_touch_drag_grab(touch_drag);
+}
+
+static const struct weston_touch_grab_interface touch_drag_grab_interface = {
+ drag_grab_touch_down,
+ drag_grab_touch_up,
+ drag_grab_touch_motion,
+ drag_grab_touch_frame,
+ drag_grab_touch_cancel
+};
+
+static void
+drag_grab_keyboard_key(struct weston_keyboard_grab *grab,
+ uint32_t time, uint32_t key, uint32_t state)
+{
+}
+
+static void
+drag_grab_keyboard_modifiers(struct weston_keyboard_grab *grab,
+ uint32_t serial, uint32_t mods_depressed,
+ uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group)
+{
+ struct weston_keyboard *keyboard = grab->keyboard;
+ struct weston_drag *drag =
+ container_of(grab, struct weston_drag, keyboard_grab);
+ uint32_t compositor_action;
+
+ if (mods_depressed & (1 << keyboard->xkb_info->shift_mod))
+ compositor_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+ else if (mods_depressed & (1 << keyboard->xkb_info->ctrl_mod))
+ compositor_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+ else
+ compositor_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
+
+ drag->data_source->compositor_action = compositor_action;
+
+ if (drag->data_source->offer)
+ data_offer_update_action(drag->data_source->offer);
+}
+
+static void
+drag_grab_keyboard_cancel(struct weston_keyboard_grab *grab)
+{
+ struct weston_drag *drag =
+ container_of(grab, struct weston_drag, keyboard_grab);
+ struct weston_pointer *pointer = grab->keyboard->seat->pointer_state;
+ struct weston_touch *touch = grab->keyboard->seat->touch_state;
+
+ if (pointer && pointer->grab->interface == &pointer_drag_grab_interface) {
+ struct weston_touch_drag *touch_drag =
+ (struct weston_touch_drag *) drag;
+ drag_grab_touch_cancel(&touch_drag->grab);
+ } else if (touch && touch->grab->interface == &touch_drag_grab_interface) {
+ struct weston_pointer_drag *pointer_drag =
+ (struct weston_pointer_drag *) drag;
+ drag_grab_cancel(&pointer_drag->grab);
+ }
+}
+
+static const struct weston_keyboard_grab_interface keyboard_drag_grab_interface = {
+ drag_grab_keyboard_key,
+ drag_grab_keyboard_modifiers,
+ drag_grab_keyboard_cancel
+};
+
+static void
+destroy_pointer_data_device_source(struct wl_listener *listener, void *data)
+{
+ struct weston_pointer_drag *drag = container_of(listener,
+ struct weston_pointer_drag, base.data_source_listener);
+
+ data_device_end_pointer_drag_grab(drag);
+}
+
+static void
+handle_drag_icon_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_drag *drag = container_of(listener, struct weston_drag,
+ icon_destroy_listener);
+
+ drag->icon = NULL;
+}
+
+WL_EXPORT int
+weston_pointer_start_drag(struct weston_pointer *pointer,
+ struct weston_data_source *source,
+ struct weston_surface *icon,
+ struct wl_client *client)
+{
+ struct weston_pointer_drag *drag;
+ struct weston_keyboard *keyboard =
+ weston_seat_get_keyboard(pointer->seat);
+
+ drag = zalloc(sizeof *drag);
+ if (drag == NULL)
+ return -1;
+
+ drag->grab.interface = &pointer_drag_grab_interface;
+ drag->base.keyboard_grab.interface = &keyboard_drag_grab_interface;
+ drag->base.client = client;
+ drag->base.data_source = source;
+
+ if (icon) {
+ drag->base.icon = weston_view_create(icon);
+ if (drag->base.icon == NULL) {
+ free(drag);
+ return -1;
+ }
+
+ drag->base.icon_destroy_listener.notify = handle_drag_icon_destroy;
+ wl_signal_add(&icon->destroy_signal,
+ &drag->base.icon_destroy_listener);
+
+ icon->configure = pointer_drag_surface_configure;
+ icon->configure_private = drag;
+ weston_surface_set_label_func(icon,
+ pointer_drag_surface_get_label);
+ } else {
+ drag->base.icon = NULL;
+ }
+
+ if (source) {
+ drag->base.data_source_listener.notify = destroy_pointer_data_device_source;
+ wl_signal_add(&source->destroy_signal,
+ &drag->base.data_source_listener);
+ }
+
+ weston_pointer_clear_focus(pointer);
+ weston_keyboard_set_focus(keyboard, NULL);
+
+ weston_pointer_start_grab(pointer, &drag->grab);
+ weston_keyboard_start_grab(keyboard, &drag->base.keyboard_grab);
+
+ return 0;
+}
+
+static void
+destroy_touch_data_device_source(struct wl_listener *listener, void *data)
+{
+ struct weston_touch_drag *drag = container_of(listener,
+ struct weston_touch_drag, base.data_source_listener);
+
+ data_device_end_touch_drag_grab(drag);
+}
+
+WL_EXPORT int
+weston_touch_start_drag(struct weston_touch *touch,
+ struct weston_data_source *source,
+ struct weston_surface *icon,
+ struct wl_client *client)
+{
+ struct weston_touch_drag *drag;
+ struct weston_keyboard *keyboard =
+ weston_seat_get_keyboard(touch->seat);
+
+ drag = zalloc(sizeof *drag);
+ if (drag == NULL)
+ return -1;
+
+ drag->grab.interface = &touch_drag_grab_interface;
+ drag->base.client = client;
+ drag->base.data_source = source;
+
+ if (icon) {
+ drag->base.icon = weston_view_create(icon);
+ if (drag->base.icon == NULL) {
+ free(drag);
+ return -1;
+ }
+
+ drag->base.icon_destroy_listener.notify = handle_drag_icon_destroy;
+ wl_signal_add(&icon->destroy_signal,
+ &drag->base.icon_destroy_listener);
+
+ icon->configure = touch_drag_surface_configure;
+ icon->configure_private = drag;
+ weston_surface_set_label_func(icon,
+ touch_drag_surface_get_label);
+ } else {
+ drag->base.icon = NULL;
+ }
+
+ if (source) {
+ drag->base.data_source_listener.notify = destroy_touch_data_device_source;
+ wl_signal_add(&source->destroy_signal,
+ &drag->base.data_source_listener);
+ }
+
+ weston_keyboard_set_focus(keyboard, NULL);
+
+ weston_touch_start_grab(touch, &drag->grab);
+ weston_keyboard_start_grab(keyboard, &drag->base.keyboard_grab);
+
+ drag_grab_touch_focus(drag);
+
+ return 0;
+}
+
+static void
+data_device_start_drag(struct wl_client *client, struct wl_resource *resource,
+ struct wl_resource *source_resource,
+ struct wl_resource *origin_resource,
+ struct wl_resource *icon_resource, uint32_t serial)
+{
+ struct weston_seat *seat = wl_resource_get_user_data(resource);
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+ struct weston_touch *touch = weston_seat_get_touch(seat);
+ struct weston_surface *origin = wl_resource_get_user_data(origin_resource);
+ struct weston_data_source *source = NULL;
+ struct weston_surface *icon = NULL;
+ int is_pointer_grab, is_touch_grab;
+ int32_t ret = 0;
+
+ is_pointer_grab = pointer &&
+ pointer->button_count == 1 &&
+ pointer->grab_serial == serial &&
+ pointer->focus &&
+ pointer->focus->surface == origin;
+
+ is_touch_grab = touch &&
+ touch->num_tp == 1 &&
+ touch->grab_serial == serial &&
+ touch->focus &&
+ touch->focus->surface == origin;
+
+ if (!is_pointer_grab && !is_touch_grab)
+ return;
+
+ /* FIXME: Check that the data source type array isn't empty. */
+
+ if (source_resource)
+ source = wl_resource_get_user_data(source_resource);
+ if (icon_resource)
+ icon = wl_resource_get_user_data(icon_resource);
+
+ if (icon) {
+ if (weston_surface_set_role(icon, "wl_data_device-icon",
+ resource,
+ WL_DATA_DEVICE_ERROR_ROLE) < 0)
+ return;
+ }
+
+ if (is_pointer_grab)
+ ret = weston_pointer_start_drag(pointer, source, icon, client);
+ else if (is_touch_grab)
+ ret = weston_touch_start_drag(touch, source, icon, client);
+
+ if (ret < 0)
+ wl_resource_post_no_memory(resource);
+ else
+ source->seat = seat;
+}
+
+static void
+destroy_selection_data_source(struct wl_listener *listener, void *data)
+{
+ struct weston_seat *seat = container_of(listener, struct weston_seat,
+ selection_data_source_listener);
+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
+ struct wl_resource *data_device;
+ struct weston_surface *focus = NULL;
+
+ seat->selection_data_source = NULL;
+
+ if (keyboard)
+ focus = keyboard->focus;
+ if (focus && focus->resource) {
+ data_device = wl_resource_find_for_client(&seat->drag_resource_list,
+ wl_resource_get_client(focus->resource));
+ if (data_device)
+ wl_data_device_send_selection(data_device, NULL);
+ }
+
+ wl_signal_emit(&seat->selection_signal, seat);
+}
+
+/** \brief Send the selection to the specified client
+ *
+ * This function creates a new wl_data_offer if there is a wl_data_source
+ * currently set as the selection and sends it to the specified client,
+ * followed by the wl_data_device.selection() event.
+ * If there is no current selection the wl_data_device.selection() event
+ * will carry a NULL wl_data_offer.
+ *
+ * If the client does not have a wl_data_device for the specified seat
+ * nothing will be done.
+ *
+ * \param seat The seat owning the wl_data_device used to send the events.
+ * \param client The client to which to send the selection.
+ */
+WL_EXPORT void
+weston_seat_send_selection(struct weston_seat *seat, struct wl_client *client)
+{
+ struct weston_data_offer *offer;
+ struct wl_resource *data_device;
+
+ wl_resource_for_each(data_device, &seat->drag_resource_list) {
+ if (wl_resource_get_client(data_device) != client)
+ continue;
+
+ if (seat->selection_data_source) {
+ offer = weston_data_source_send_offer(seat->selection_data_source,
+ data_device);
+ wl_data_device_send_selection(data_device, offer->resource);
+ } else {
+ wl_data_device_send_selection(data_device, NULL);
+ }
+ }
+}
+
+WL_EXPORT void
+weston_seat_set_selection(struct weston_seat *seat,
+ struct weston_data_source *source, uint32_t serial)
+{
+ struct weston_surface *focus = NULL;
+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
+
+ if (seat->selection_data_source &&
+ seat->selection_serial - serial < UINT32_MAX / 2)
+ return;
+
+ if (seat->selection_data_source) {
+ seat->selection_data_source->cancel(seat->selection_data_source);
+ wl_list_remove(&seat->selection_data_source_listener.link);
+ seat->selection_data_source = NULL;
+ }
+
+ seat->selection_data_source = source;
+ seat->selection_serial = serial;
+
+ if (keyboard)
+ focus = keyboard->focus;
+ if (focus && focus->resource) {
+ weston_seat_send_selection(seat, wl_resource_get_client(focus->resource));
+ }
+
+ wl_signal_emit(&seat->selection_signal, seat);
+
+ if (source) {
+ seat->selection_data_source_listener.notify =
+ destroy_selection_data_source;
+ wl_signal_add(&source->destroy_signal,
+ &seat->selection_data_source_listener);
+ }
+}
+
+static void
+data_device_set_selection(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *source_resource, uint32_t serial)
+{
+ struct weston_data_source *source;
+
+ if (!source_resource)
+ return;
+
+ source = wl_resource_get_user_data(source_resource);
+
+ if (source->actions_set) {
+ wl_resource_post_error(source_resource,
+ WL_DATA_SOURCE_ERROR_INVALID_SOURCE,
+ "cannot set drag-and-drop source as selection");
+ return;
+ }
+
+ /* FIXME: Store serial and check against incoming serial here. */
+ weston_seat_set_selection(wl_resource_get_user_data(resource),
+ source, serial);
+}
+static void
+data_device_release(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_data_device_interface data_device_interface = {
+ data_device_start_drag,
+ data_device_set_selection,
+ data_device_release
+};
+
+static void
+destroy_data_source(struct wl_resource *resource)
+{
+ struct weston_data_source *source =
+ wl_resource_get_user_data(resource);
+ char **p;
+
+ wl_signal_emit(&source->destroy_signal, source);
+
+ wl_array_for_each(p, &source->mime_types)
+ free(*p);
+
+ wl_array_release(&source->mime_types);
+
+ free(source);
+}
+
+static void
+client_source_accept(struct weston_data_source *source,
+ uint32_t time, const char *mime_type)
+{
+ wl_data_source_send_target(source->resource, mime_type);
+}
+
+static void
+client_source_send(struct weston_data_source *source,
+ const char *mime_type, int32_t fd)
+{
+ wl_data_source_send_send(source->resource, mime_type, fd);
+ close(fd);
+}
+
+static void
+client_source_cancel(struct weston_data_source *source)
+{
+ wl_data_source_send_cancelled(source->resource);
+}
+
+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 =
+ wl_resource_create(client, &wl_data_source_interface,
+ wl_resource_get_version(resource), id);
+ if (source->resource == NULL) {
+ free(source);
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ wl_signal_init(&source->destroy_signal);
+ source->accept = client_source_accept;
+ source->send = client_source_send;
+ source->cancel = client_source_cancel;
+ source->offer = NULL;
+ source->accepted = false;
+ source->seat = NULL;
+ source->actions_set = false;
+ source->dnd_actions = 0;
+ source->current_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
+ source->compositor_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
+
+ wl_array_init(&source->mime_types);
+
+ wl_resource_set_implementation(source->resource, &data_source_interface,
+ source, destroy_data_source);
+}
+
+static void unbind_data_device(struct wl_resource *resource)
+{
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void
+get_data_device(struct wl_client *client,
+ struct wl_resource *manager_resource,
+ uint32_t id, struct wl_resource *seat_resource)
+{
+ struct weston_seat *seat = wl_resource_get_user_data(seat_resource);
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client,
+ &wl_data_device_interface,
+ wl_resource_get_version(manager_resource),
+ id);
+ if (resource == NULL) {
+ wl_resource_post_no_memory(manager_resource);
+ return;
+ }
+
+ wl_list_insert(&seat->drag_resource_list,
+ wl_resource_get_link(resource));
+ wl_resource_set_implementation(resource, &data_device_interface,
+ seat, 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)
+{
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client,
+ &wl_data_device_manager_interface,
+ version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &manager_interface,
+ NULL, NULL);
+}
+
+WL_EXPORT void
+wl_data_device_set_keyboard_focus(struct weston_seat *seat)
+{
+ struct weston_surface *focus;
+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
+
+ if (!keyboard)
+ return;
+
+ focus = keyboard->focus;
+ if (!focus || !focus->resource)
+ return;
+
+ weston_seat_send_selection(seat, wl_resource_get_client(focus->resource));
+}
+
+WL_EXPORT int
+wl_data_device_manager_init(struct wl_display *display)
+{
+ if (wl_global_create(display,
+ &wl_data_device_manager_interface, 3,
+ NULL, bind_manager) == NULL)
+ return -1;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 2013 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * 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.
+ */
+
+/*
+ * DBus Helpers
+ * This file contains the dbus mainloop integration and several helpers to
+ * make lowlevel libdbus access easier.
+ */
+
+#include "config.h"
+
+#include <dbus/dbus.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/timerfd.h>
+#include <unistd.h>
+#include <wayland-server.h>
+
+#include "compositor.h"
+#include "dbus.h"
+
+/*
+ * DBus Mainloop Integration
+ * weston_dbus_bind() and weston_dbus_unbind() allow to bind an existing
+ * DBusConnection to an existing wl_event_loop object. All dbus dispatching
+ * is then nicely integrated into the wayland event loop.
+ * Note that this only provides basic watch and timeout dispatching. No
+ * remote thread wakeup, signal handling or other dbus insanity is supported.
+ * This is fine as long as you don't use any of the deprecated libdbus
+ * interfaces (like waking up remote threads..). There is really no rational
+ * reason to support these.
+ */
+
+static int weston_dbus_dispatch_watch(int fd, uint32_t mask, void *data)
+{
+ DBusWatch *watch = data;
+ uint32_t flags = 0;
+
+ if (dbus_watch_get_enabled(watch)) {
+ if (mask & WL_EVENT_READABLE)
+ flags |= DBUS_WATCH_READABLE;
+ if (mask & WL_EVENT_WRITABLE)
+ flags |= DBUS_WATCH_WRITABLE;
+ if (mask & WL_EVENT_HANGUP)
+ flags |= DBUS_WATCH_HANGUP;
+ if (mask & WL_EVENT_ERROR)
+ flags |= DBUS_WATCH_ERROR;
+
+ dbus_watch_handle(watch, flags);
+ }
+
+ return 0;
+}
+
+static dbus_bool_t weston_dbus_add_watch(DBusWatch *watch, void *data)
+{
+ struct wl_event_loop *loop = data;
+ struct wl_event_source *s;
+ int fd;
+ uint32_t mask = 0, flags;
+
+ if (dbus_watch_get_enabled(watch)) {
+ flags = dbus_watch_get_flags(watch);
+ if (flags & DBUS_WATCH_READABLE)
+ mask |= WL_EVENT_READABLE;
+ if (flags & DBUS_WATCH_WRITABLE)
+ mask |= WL_EVENT_WRITABLE;
+ }
+
+ fd = dbus_watch_get_unix_fd(watch);
+ s = wl_event_loop_add_fd(loop, fd, mask, weston_dbus_dispatch_watch,
+ watch);
+ if (!s)
+ return FALSE;
+
+ dbus_watch_set_data(watch, s, NULL);
+ return TRUE;
+}
+
+static void weston_dbus_remove_watch(DBusWatch *watch, void *data)
+{
+ struct wl_event_source *s;
+
+ s = dbus_watch_get_data(watch);
+ if (!s)
+ return;
+
+ wl_event_source_remove(s);
+}
+
+static void weston_dbus_toggle_watch(DBusWatch *watch, void *data)
+{
+ struct wl_event_source *s;
+ uint32_t mask = 0, flags;
+
+ s = dbus_watch_get_data(watch);
+ if (!s)
+ return;
+
+ if (dbus_watch_get_enabled(watch)) {
+ flags = dbus_watch_get_flags(watch);
+ if (flags & DBUS_WATCH_READABLE)
+ mask |= WL_EVENT_READABLE;
+ if (flags & DBUS_WATCH_WRITABLE)
+ mask |= WL_EVENT_WRITABLE;
+ }
+
+ wl_event_source_fd_update(s, mask);
+}
+
+static int weston_dbus_dispatch_timeout(void *data)
+{
+ DBusTimeout *timeout = data;
+
+ if (dbus_timeout_get_enabled(timeout))
+ dbus_timeout_handle(timeout);
+
+ return 0;
+}
+
+static int weston_dbus_adjust_timeout(DBusTimeout *timeout,
+ struct wl_event_source *s)
+{
+ int64_t t = 0;
+
+ if (dbus_timeout_get_enabled(timeout))
+ t = dbus_timeout_get_interval(timeout);
+
+ return wl_event_source_timer_update(s, t);
+}
+
+static dbus_bool_t weston_dbus_add_timeout(DBusTimeout *timeout, void *data)
+{
+ struct wl_event_loop *loop = data;
+ struct wl_event_source *s;
+ int r;
+
+ s = wl_event_loop_add_timer(loop, weston_dbus_dispatch_timeout,
+ timeout);
+ if (!s)
+ return FALSE;
+
+ r = weston_dbus_adjust_timeout(timeout, s);
+ if (r < 0) {
+ wl_event_source_remove(s);
+ return FALSE;
+ }
+
+ dbus_timeout_set_data(timeout, s, NULL);
+ return TRUE;
+}
+
+static void weston_dbus_remove_timeout(DBusTimeout *timeout, void *data)
+{
+ struct wl_event_source *s;
+
+ s = dbus_timeout_get_data(timeout);
+ if (!s)
+ return;
+
+ wl_event_source_remove(s);
+}
+
+static void weston_dbus_toggle_timeout(DBusTimeout *timeout, void *data)
+{
+ struct wl_event_source *s;
+
+ s = dbus_timeout_get_data(timeout);
+ if (!s)
+ return;
+
+ weston_dbus_adjust_timeout(timeout, s);
+}
+
+static int weston_dbus_dispatch(int fd, uint32_t mask, void *data)
+{
+ DBusConnection *c = data;
+ int r;
+
+ do {
+ r = dbus_connection_dispatch(c);
+ if (r == DBUS_DISPATCH_COMPLETE)
+ r = 0;
+ else if (r == DBUS_DISPATCH_DATA_REMAINS)
+ r = -EAGAIN;
+ else if (r == DBUS_DISPATCH_NEED_MEMORY)
+ r = -ENOMEM;
+ else
+ r = -EIO;
+ } while (r == -EAGAIN);
+
+ if (r)
+ weston_log("cannot dispatch dbus events: %d\n", r);
+
+ return 0;
+}
+
+static int weston_dbus_bind(struct wl_event_loop *loop, DBusConnection *c,
+ struct wl_event_source **ctx_out)
+{
+ bool b;
+ int r, fd;
+
+ /* Idle events cannot reschedule themselves, therefore we use a dummy
+ * event-fd and mark it for post-dispatch. Hence, the dbus
+ * dispatcher is called after every dispatch-round.
+ * This is required as dbus doesn't allow dispatching events from
+ * within its own event sources. */
+ fd = eventfd(0, EFD_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ *ctx_out = wl_event_loop_add_fd(loop, fd, 0, weston_dbus_dispatch, c);
+ close(fd);
+
+ if (!*ctx_out)
+ return -ENOMEM;
+
+ wl_event_source_check(*ctx_out);
+
+ b = dbus_connection_set_watch_functions(c,
+ weston_dbus_add_watch,
+ weston_dbus_remove_watch,
+ weston_dbus_toggle_watch,
+ loop,
+ NULL);
+ if (!b) {
+ r = -ENOMEM;
+ goto error;
+ }
+
+ b = dbus_connection_set_timeout_functions(c,
+ weston_dbus_add_timeout,
+ weston_dbus_remove_timeout,
+ weston_dbus_toggle_timeout,
+ loop,
+ NULL);
+ if (!b) {
+ r = -ENOMEM;
+ goto error;
+ }
+
+ dbus_connection_ref(c);
+ return 0;
+
+error:
+ dbus_connection_set_timeout_functions(c, NULL, NULL, NULL,
+ NULL, NULL);
+ dbus_connection_set_watch_functions(c, NULL, NULL, NULL,
+ NULL, NULL);
+ wl_event_source_remove(*ctx_out);
+ *ctx_out = NULL;
+ return r;
+}
+
+static void weston_dbus_unbind(DBusConnection *c, struct wl_event_source *ctx)
+{
+ dbus_connection_set_timeout_functions(c, NULL, NULL, NULL,
+ NULL, NULL);
+ dbus_connection_set_watch_functions(c, NULL, NULL, NULL,
+ NULL, NULL);
+ dbus_connection_unref(c);
+ wl_event_source_remove(ctx);
+}
+
+/*
+ * Convenience Helpers
+ * Several convenience helpers are provided to make using dbus in weston
+ * easier. We don't use any of the gdbus or qdbus helpers as they pull in
+ * huge dependencies and actually are quite awful to use. Instead, we only
+ * use the basic low-level libdbus library.
+ */
+
+int weston_dbus_open(struct wl_event_loop *loop, DBusBusType bus,
+ DBusConnection **out, struct wl_event_source **ctx_out)
+{
+ DBusConnection *c;
+ int r;
+
+ /* Ihhh, global state.. stupid dbus. */
+ dbus_connection_set_change_sigpipe(FALSE);
+
+ /* This is actually synchronous. It blocks for some authentication and
+ * setup. We just trust the dbus-server here and accept this blocking
+ * call. There is no real reason to complicate things further and make
+ * this asynchronous/non-blocking. A context should be created during
+ * thead/process/app setup, so blocking calls should be fine. */
+ c = dbus_bus_get_private(bus, NULL);
+ if (!c)
+ return -EIO;
+
+ dbus_connection_set_exit_on_disconnect(c, FALSE);
+
+ r = weston_dbus_bind(loop, c, ctx_out);
+ if (r < 0)
+ goto error;
+
+ *out = c;
+ return r;
+
+error:
+ dbus_connection_close(c);
+ dbus_connection_unref(c);
+ return r;
+}
+
+void weston_dbus_close(DBusConnection *c, struct wl_event_source *ctx)
+{
+ weston_dbus_unbind(c, ctx);
+ dbus_connection_close(c);
+ dbus_connection_unref(c);
+}
+
+int weston_dbus_add_match(DBusConnection *c, const char *format, ...)
+{
+ DBusError err;
+ int r;
+ va_list list;
+ char *str;
+
+ va_start(list, format);
+ r = vasprintf(&str, format, list);
+ va_end(list);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ dbus_error_init(&err);
+ dbus_bus_add_match(c, str, &err);
+ free(str);
+ if (dbus_error_is_set(&err)) {
+ dbus_error_free(&err);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int weston_dbus_add_match_signal(DBusConnection *c, const char *sender,
+ const char *iface, const char *member,
+ const char *path)
+{
+ return weston_dbus_add_match(c,
+ "type='signal',"
+ "sender='%s',"
+ "interface='%s',"
+ "member='%s',"
+ "path='%s'",
+ sender, iface, member, path);
+}
+
+void weston_dbus_remove_match(DBusConnection *c, const char *format, ...)
+{
+ int r;
+ va_list list;
+ char *str;
+
+ va_start(list, format);
+ r = vasprintf(&str, format, list);
+ va_end(list);
+
+ if (r < 0)
+ return;
+
+ dbus_bus_remove_match(c, str, NULL);
+ free(str);
+}
+
+void weston_dbus_remove_match_signal(DBusConnection *c, const char *sender,
+ const char *iface, const char *member,
+ const char *path)
+{
+ return weston_dbus_remove_match(c,
+ "type='signal',"
+ "sender='%s',"
+ "interface='%s',"
+ "member='%s',"
+ "path='%s'",
+ sender, iface, member, path);
+}
--- /dev/null
+/*
+ * Copyright © 2013 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef _WESTON_DBUS_H_
+#define _WESTON_DBUS_H_
+
+#include "config.h"
+
+#include <errno.h>
+#include <wayland-server.h>
+
+#include "compositor.h"
+
+#ifdef HAVE_DBUS
+
+#include <dbus/dbus.h>
+
+/*
+ * weston_dbus_open() - Open new dbus connection
+ *
+ * Opens a new dbus connection to the bus given as @bus. It automatically
+ * integrates the new connection into the main-loop @loop. The connection
+ * itself is returned in @out.
+ * This also returns a context source used for dbus dispatching. It is
+ * returned on success in @ctx_out and must be passed to weston_dbus_close()
+ * unchanged. You must not access it from outside of a dbus helper!
+ *
+ * Returns 0 on success, negative error code on failure.
+ */
+int weston_dbus_open(struct wl_event_loop *loop, DBusBusType bus,
+ DBusConnection **out, struct wl_event_source **ctx_out);
+
+/*
+ * weston_dbus_close() - Close dbus connection
+ *
+ * Closes a dbus connection that was previously opened via weston_dbus_open().
+ * It unbinds the connection from the main-loop it was previously bound to,
+ * closes the dbus connection and frees all resources. If you want to access
+ * @c after this call returns, you must hold a dbus-reference to it. But
+ * notice that the connection is closed after this returns so it cannot be
+ * used to spawn new dbus requests.
+ * You must pass the context source returns by weston_dbus_open() as @ctx.
+ */
+void weston_dbus_close(DBusConnection *c, struct wl_event_source *ctx);
+
+/*
+ * weston_dbus_add_match() - Add dbus match
+ *
+ * Configure a dbus-match on the given dbus-connection. This match is saved
+ * on the dbus-server as long as the connection is open. See dbus-manual
+ * for information. Compared to the dbus_bus_add_match() this allows a
+ * var-arg formatted match-string.
+ */
+int weston_dbus_add_match(DBusConnection *c, const char *format, ...);
+
+/*
+ * weston_dbus_add_match_signal() - Add dbus signal match
+ *
+ * Same as weston_dbus_add_match() but does the dbus-match formatting for
+ * signals internally.
+ */
+int weston_dbus_add_match_signal(DBusConnection *c, const char *sender,
+ const char *iface, const char *member,
+ const char *path);
+
+/*
+ * weston_dbus_remove_match() - Remove dbus match
+ *
+ * Remove a previously configured dbus-match from the dbus server. There is
+ * no need to remove dbus-matches if you close the connection, anyway.
+ * Compared to dbus_bus_remove_match() this allows a var-arg formatted
+ * match string.
+ */
+void weston_dbus_remove_match(DBusConnection *c, const char *format, ...);
+
+/*
+ * weston_dbus_remove_match_signal() - Remove dbus signal match
+ *
+ * Same as weston_dbus_remove_match() but does the dbus-match formatting for
+ * signals internally.
+ */
+void weston_dbus_remove_match_signal(DBusConnection *c, const char *sender,
+ const char *iface, const char *member,
+ const char *path);
+
+#endif /* HAVE_DBUS */
+
+#endif // _WESTON_DBUS_H_
--- /dev/null
+/*
+ * Copyright © 2012 Intel Corporation
+ * Copyright © 2015 Collabora, Ltd.
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <float.h>
+#include <assert.h>
+#include <linux/input.h>
+#include <drm_fourcc.h>
+
+#include "gl-renderer.h"
+#include "vertex-clipping.h"
+#include "linux-dmabuf.h"
+#include "linux-dmabuf-unstable-v1-server-protocol.h"
+
+#include "shared/helpers.h"
+#include "weston-egl-ext.h"
+
+struct gl_shader {
+ GLuint program;
+ GLuint vertex_shader, fragment_shader;
+ GLint proj_uniform;
+ GLint tex_uniforms[3];
+ GLint alpha_uniform;
+ GLint color_uniform;
+ const char *vertex_source, *fragment_source;
+};
+
+#define BUFFER_DAMAGE_COUNT 2
+
+enum gl_border_status {
+ BORDER_STATUS_CLEAN = 0,
+ BORDER_TOP_DIRTY = 1 << GL_RENDERER_BORDER_TOP,
+ BORDER_LEFT_DIRTY = 1 << GL_RENDERER_BORDER_LEFT,
+ BORDER_RIGHT_DIRTY = 1 << GL_RENDERER_BORDER_RIGHT,
+ BORDER_BOTTOM_DIRTY = 1 << GL_RENDERER_BORDER_BOTTOM,
+ BORDER_ALL_DIRTY = 0xf,
+ BORDER_SIZE_CHANGED = 0x10
+};
+
+struct gl_border_image {
+ GLuint tex;
+ int32_t width, height;
+ int32_t tex_width;
+ void *data;
+};
+
+struct gl_output_state {
+ EGLSurface egl_surface;
+ pixman_region32_t buffer_damage[BUFFER_DAMAGE_COUNT];
+ int buffer_damage_index;
+ enum gl_border_status border_damage[BUFFER_DAMAGE_COUNT];
+ struct gl_border_image borders[4];
+ enum gl_border_status border_status;
+
+ struct weston_matrix output_matrix;
+};
+
+enum buffer_type {
+ BUFFER_TYPE_NULL,
+ BUFFER_TYPE_SOLID, /* internal solid color surfaces without a buffer */
+ BUFFER_TYPE_SHM,
+ BUFFER_TYPE_EGL
+};
+
+struct gl_renderer;
+
+struct egl_image {
+ struct gl_renderer *renderer;
+ EGLImageKHR image;
+ int refcount;
+};
+
+enum import_type {
+ IMPORT_TYPE_INVALID,
+ IMPORT_TYPE_DIRECT,
+ IMPORT_TYPE_GL_CONVERSION
+};
+
+struct dmabuf_image {
+ struct linux_dmabuf_buffer *dmabuf;
+ int num_images;
+ struct egl_image *images[3];
+ struct wl_list link;
+
+ enum import_type import_type;
+ GLenum target;
+ struct gl_shader *shader;
+};
+
+struct yuv_plane_descriptor {
+ int width_divisor;
+ int height_divisor;
+ uint32_t format;
+ int plane_index;
+};
+
+struct yuv_format_descriptor {
+ uint32_t format;
+ int input_planes;
+ int output_planes;
+ int texture_type;
+ struct yuv_plane_descriptor plane[4];
+};
+
+struct gl_surface_state {
+ GLfloat color[4];
+ struct gl_shader *shader;
+
+ GLuint textures[3];
+ int num_textures;
+ bool needs_full_upload;
+ pixman_region32_t texture_damage;
+
+ /* These are only used by SHM surfaces to detect when we need
+ * to do a full upload to specify a new internal texture
+ * format */
+ GLenum gl_format;
+ GLenum gl_pixel_type;
+
+ struct egl_image* images[3];
+ GLenum target;
+ int num_images;
+
+ struct weston_buffer_reference buffer_ref;
+ enum buffer_type buffer_type;
+ int pitch; /* in pixels */
+ int height; /* in pixels */
+ int y_inverted;
+
+ struct weston_surface *surface;
+
+ struct wl_listener surface_destroy_listener;
+ struct wl_listener renderer_destroy_listener;
+};
+
+struct gl_renderer {
+ struct weston_renderer base;
+ int fragment_shader_debug;
+ int fan_debug;
+ struct weston_binding *fragment_binding;
+ struct weston_binding *fan_binding;
+
+ EGLDisplay egl_display;
+ EGLContext egl_context;
+ EGLConfig egl_config;
+
+ struct wl_array vertices;
+ struct wl_array vtxcnt;
+
+ PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
+ PFNEGLCREATEIMAGEKHRPROC create_image;
+ PFNEGLDESTROYIMAGEKHRPROC destroy_image;
+
+#ifdef EGL_EXT_swap_buffers_with_damage
+ PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage;
+#endif
+
+ PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC create_platform_window;
+
+ int has_unpack_subimage;
+
+ PFNEGLBINDWAYLANDDISPLAYWL bind_display;
+ PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
+ PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
+ int has_bind_display;
+
+ int has_egl_image_external;
+
+ int has_egl_buffer_age;
+
+ int has_configless_context;
+
+ int has_dmabuf_import;
+ struct wl_list dmabuf_images;
+
+ struct gl_shader texture_shader_rgba;
+ struct gl_shader texture_shader_rgbx;
+ struct gl_shader texture_shader_egl_external;
+ struct gl_shader texture_shader_y_uv;
+ struct gl_shader texture_shader_y_u_v;
+ struct gl_shader texture_shader_y_xuxv;
+ struct gl_shader invert_color_shader;
+ struct gl_shader solid_shader;
+ struct gl_shader *current_shader;
+
+ struct wl_signal destroy_signal;
+};
+
+static PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = NULL;
+
+static inline const char *
+dump_format(uint32_t format, char out[4])
+{
+#if BYTE_ORDER == BIG_ENDIAN
+ format = __builtin_bswap32(format);
+#endif
+ memcpy(out, &format, 4);
+ return out;
+}
+
+static inline struct gl_output_state *
+get_output_state(struct weston_output *output)
+{
+ return (struct gl_output_state *)output->renderer_state;
+}
+
+static int
+gl_renderer_create_surface(struct weston_surface *surface);
+
+static inline struct gl_surface_state *
+get_surface_state(struct weston_surface *surface)
+{
+ if (!surface->renderer_state)
+ gl_renderer_create_surface(surface);
+
+ return (struct gl_surface_state *)surface->renderer_state;
+}
+
+static inline struct gl_renderer *
+get_renderer(struct weston_compositor *ec)
+{
+ return (struct gl_renderer *)ec->renderer;
+}
+
+static struct egl_image*
+egl_image_create(struct gl_renderer *gr, EGLenum target,
+ EGLClientBuffer buffer, const EGLint *attribs)
+{
+ struct egl_image *img;
+
+ img = zalloc(sizeof *img);
+ img->renderer = gr;
+ img->refcount = 1;
+ img->image = gr->create_image(gr->egl_display, EGL_NO_CONTEXT,
+ target, buffer, attribs);
+
+ if (img->image == EGL_NO_IMAGE_KHR) {
+ free(img);
+ return NULL;
+ }
+
+ return img;
+}
+
+static struct egl_image*
+egl_image_ref(struct egl_image *image)
+{
+ image->refcount++;
+
+ return image;
+}
+
+static int
+egl_image_unref(struct egl_image *image)
+{
+ struct gl_renderer *gr = image->renderer;
+
+ assert(image->refcount > 0);
+
+ image->refcount--;
+ if (image->refcount > 0)
+ return image->refcount;
+
+ gr->destroy_image(gr->egl_display, image->image);
+ free(image);
+
+ return 0;
+}
+
+static struct dmabuf_image*
+dmabuf_image_create(void)
+{
+ struct dmabuf_image *img;
+
+ img = zalloc(sizeof *img);
+ wl_list_init(&img->link);
+
+ return img;
+}
+
+static void
+dmabuf_image_destroy(struct dmabuf_image *image)
+{
+ int i;
+
+ for (i = 0; i < image->num_images; ++i)
+ egl_image_unref(image->images[i]);
+
+ if (image->dmabuf)
+ linux_dmabuf_buffer_set_user_data(image->dmabuf, NULL, NULL);
+
+ wl_list_remove(&image->link);
+}
+
+static const char *
+egl_error_string(EGLint code)
+{
+#define MYERRCODE(x) case x: return #x;
+ switch (code) {
+ MYERRCODE(EGL_SUCCESS)
+ MYERRCODE(EGL_NOT_INITIALIZED)
+ MYERRCODE(EGL_BAD_ACCESS)
+ MYERRCODE(EGL_BAD_ALLOC)
+ MYERRCODE(EGL_BAD_ATTRIBUTE)
+ MYERRCODE(EGL_BAD_CONTEXT)
+ MYERRCODE(EGL_BAD_CONFIG)
+ MYERRCODE(EGL_BAD_CURRENT_SURFACE)
+ MYERRCODE(EGL_BAD_DISPLAY)
+ MYERRCODE(EGL_BAD_SURFACE)
+ MYERRCODE(EGL_BAD_MATCH)
+ MYERRCODE(EGL_BAD_PARAMETER)
+ MYERRCODE(EGL_BAD_NATIVE_PIXMAP)
+ MYERRCODE(EGL_BAD_NATIVE_WINDOW)
+ MYERRCODE(EGL_CONTEXT_LOST)
+ default:
+ return "unknown";
+ }
+#undef MYERRCODE
+}
+
+static void
+gl_renderer_print_egl_error_state(void)
+{
+ EGLint code;
+
+ code = eglGetError();
+ weston_log("EGL error state: %s (0x%04lx)\n",
+ egl_error_string(code), (long)code);
+}
+
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+#define min(a, b) (((a) > (b)) ? (b) : (a))
+
+/*
+ * Compute the boundary vertices of the intersection of the global coordinate
+ * aligned rectangle 'rect', and an arbitrary quadrilateral produced from
+ * 'surf_rect' when transformed from surface coordinates into global coordinates.
+ * The vertices are written to 'ex' and 'ey', and the return value is the
+ * number of vertices. Vertices are produced in clockwise winding order.
+ * Guarantees to produce either zero vertices, or 3-8 vertices with non-zero
+ * polygon area.
+ */
+static int
+calculate_edges(struct weston_view *ev, pixman_box32_t *rect,
+ pixman_box32_t *surf_rect, GLfloat *ex, GLfloat *ey)
+{
+
+ struct clip_context ctx;
+ int i, n;
+ GLfloat min_x, max_x, min_y, max_y;
+ struct polygon8 surf = {
+ { surf_rect->x1, surf_rect->x2, surf_rect->x2, surf_rect->x1 },
+ { surf_rect->y1, surf_rect->y1, surf_rect->y2, surf_rect->y2 },
+ 4
+ };
+
+ ctx.clip.x1 = rect->x1;
+ ctx.clip.y1 = rect->y1;
+ ctx.clip.x2 = rect->x2;
+ ctx.clip.y2 = rect->y2;
+
+ /* transform surface to screen space: */
+ for (i = 0; i < surf.n; i++)
+ weston_view_to_global_float(ev, surf.x[i], surf.y[i],
+ &surf.x[i], &surf.y[i]);
+
+ /* find bounding box: */
+ min_x = max_x = surf.x[0];
+ min_y = max_y = surf.y[0];
+
+ for (i = 1; i < surf.n; i++) {
+ min_x = min(min_x, surf.x[i]);
+ max_x = max(max_x, surf.x[i]);
+ min_y = min(min_y, surf.y[i]);
+ max_y = max(max_y, surf.y[i]);
+ }
+
+ /* First, simple bounding box check to discard early transformed
+ * surface rects that do not intersect with the clip region:
+ */
+ if ((min_x >= ctx.clip.x2) || (max_x <= ctx.clip.x1) ||
+ (min_y >= ctx.clip.y2) || (max_y <= ctx.clip.y1))
+ return 0;
+
+ /* Simple case, bounding box edges are parallel to surface edges,
+ * there will be only four edges. We just need to clip the surface
+ * vertices to the clip rect bounds:
+ */
+ if (!ev->transform.enabled)
+ return clip_simple(&ctx, &surf, ex, ey);
+
+ /* Transformed case: use a general polygon clipping algorithm to
+ * clip the surface rectangle with each side of 'rect'.
+ * The algorithm is Sutherland-Hodgman, as explained in
+ * http://www.codeguru.com/cpp/misc/misc/graphics/article.php/c8965/Polygon-Clipping.htm
+ * but without looking at any of that code.
+ */
+ n = clip_transformed(&ctx, &surf, ex, ey);
+
+ if (n < 3)
+ return 0;
+
+ return n;
+}
+
+static bool
+merge_down(pixman_box32_t *a, pixman_box32_t *b, pixman_box32_t *merge)
+{
+ if (a->x1 == b->x1 && a->x2 == b->x2 && a->y1 == b->y2) {
+ merge->x1 = a->x1;
+ merge->x2 = a->x2;
+ merge->y1 = b->y1;
+ merge->y2 = a->y2;
+ return true;
+ }
+ return false;
+}
+
+static int
+compress_bands(pixman_box32_t *inrects, int nrects,
+ pixman_box32_t **outrects)
+{
+ bool merged;
+ pixman_box32_t *out, merge_rect;
+ int i, j, nout;
+
+ if (!nrects) {
+ *outrects = NULL;
+ return 0;
+ }
+
+ /* nrects is an upper bound - we're not too worried about
+ * allocating a little extra
+ */
+ out = malloc(sizeof(pixman_box32_t) * nrects);
+ out[0] = inrects[0];
+ nout = 1;
+ for (i = 1; i < nrects; i++) {
+ for (j = 0; j < nout; j++) {
+ merged = merge_down(&inrects[i], &out[j], &merge_rect);
+ if (merged) {
+ out[j] = merge_rect;
+ break;
+ }
+ }
+ if (!merged) {
+ out[nout] = inrects[i];
+ nout++;
+ }
+ }
+ *outrects = out;
+ return nout;
+}
+
+static int
+texture_region(struct weston_view *ev, pixman_region32_t *region,
+ pixman_region32_t *surf_region)
+{
+ struct gl_surface_state *gs = get_surface_state(ev->surface);
+ struct weston_compositor *ec = ev->surface->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ GLfloat *v, inv_width, inv_height;
+ unsigned int *vtxcnt, nvtx = 0;
+ pixman_box32_t *rects, *surf_rects;
+ pixman_box32_t *raw_rects;
+ int i, j, k, nrects, nsurf, raw_nrects;
+ bool used_band_compression;
+ raw_rects = pixman_region32_rectangles(region, &raw_nrects);
+ surf_rects = pixman_region32_rectangles(surf_region, &nsurf);
+
+ if (raw_nrects < 4) {
+ used_band_compression = false;
+ nrects = raw_nrects;
+ rects = raw_rects;
+ } else {
+ nrects = compress_bands(raw_rects, raw_nrects, &rects);
+ used_band_compression = true;
+ }
+ /* worst case we can have 8 vertices per rect (ie. clipped into
+ * an octagon):
+ */
+ v = wl_array_add(&gr->vertices, nrects * nsurf * 8 * 4 * sizeof *v);
+ vtxcnt = wl_array_add(&gr->vtxcnt, nrects * nsurf * sizeof *vtxcnt);
+
+ inv_width = 1.0 / gs->pitch;
+ inv_height = 1.0 / gs->height;
+
+ for (i = 0; i < nrects; i++) {
+ pixman_box32_t *rect = &rects[i];
+ for (j = 0; j < nsurf; j++) {
+ pixman_box32_t *surf_rect = &surf_rects[j];
+ GLfloat sx, sy, bx, by;
+ GLfloat ex[8], ey[8]; /* edge points in screen space */
+ int n;
+
+ /* The transformed surface, after clipping to the clip region,
+ * can have as many as eight sides, emitted as a triangle-fan.
+ * The first vertex in the triangle fan can be chosen arbitrarily,
+ * since the area is guaranteed to be convex.
+ *
+ * If a corner of the transformed surface falls outside of the
+ * clip region, instead of emitting one vertex for the corner
+ * of the surface, up to two are emitted for two corresponding
+ * intersection point(s) between the surface and the clip region.
+ *
+ * To do this, we first calculate the (up to eight) points that
+ * form the intersection of the clip rect and the transformed
+ * surface.
+ */
+ n = calculate_edges(ev, rect, surf_rect, ex, ey);
+ if (n < 3)
+ continue;
+
+ /* emit edge points: */
+ for (k = 0; k < n; k++) {
+ weston_view_from_global_float(ev, ex[k], ey[k],
+ &sx, &sy);
+ /* position: */
+ *(v++) = ex[k];
+ *(v++) = ey[k];
+ /* texcoord: */
+ weston_surface_to_buffer_float(ev->surface,
+ sx, sy,
+ &bx, &by);
+ *(v++) = bx * inv_width;
+ if (gs->y_inverted) {
+ *(v++) = by * inv_height;
+ } else {
+ *(v++) = (gs->height - by) * inv_height;
+ }
+ }
+
+ vtxcnt[nvtx++] = n;
+ }
+ }
+
+ if (used_band_compression)
+ free(rects);
+ return nvtx;
+}
+
+static void
+triangle_fan_debug(struct weston_view *view, int first, int count)
+{
+ struct weston_compositor *compositor = view->surface->compositor;
+ struct gl_renderer *gr = get_renderer(compositor);
+ int i;
+ GLushort *buffer;
+ GLushort *index;
+ int nelems;
+ static int color_idx = 0;
+ static const GLfloat color[][4] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 1.0, 1.0 },
+ };
+
+ nelems = (count - 1 + count - 2) * 2;
+
+ buffer = malloc(sizeof(GLushort) * nelems);
+ index = buffer;
+
+ for (i = 1; i < count; i++) {
+ *index++ = first;
+ *index++ = first + i;
+ }
+
+ for (i = 2; i < count; i++) {
+ *index++ = first + i - 1;
+ *index++ = first + i;
+ }
+
+ glUseProgram(gr->solid_shader.program);
+ glUniform4fv(gr->solid_shader.color_uniform, 1,
+ color[color_idx++ % ARRAY_LENGTH(color)]);
+ glDrawElements(GL_LINES, nelems, GL_UNSIGNED_SHORT, buffer);
+ glUseProgram(gr->current_shader->program);
+ free(buffer);
+}
+
+static void
+repaint_region(struct weston_view *ev, pixman_region32_t *region,
+ pixman_region32_t *surf_region)
+{
+ struct weston_compositor *ec = ev->surface->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ GLfloat *v;
+ unsigned int *vtxcnt;
+ int i, first, nfans;
+
+ /* The final region to be painted is the intersection of
+ * 'region' and 'surf_region'. However, 'region' is in the global
+ * coordinates, and 'surf_region' is in the surface-local
+ * coordinates. texture_region() will iterate over all pairs of
+ * rectangles from both regions, compute the intersection
+ * polygon for each pair, and store it as a triangle fan if
+ * it has a non-zero area (at least 3 vertices, actually).
+ */
+ nfans = texture_region(ev, region, surf_region);
+
+ v = gr->vertices.data;
+ vtxcnt = gr->vtxcnt.data;
+
+ /* position: */
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[0]);
+ glEnableVertexAttribArray(0);
+
+ /* texcoord: */
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[2]);
+ glEnableVertexAttribArray(1);
+
+ for (i = 0, first = 0; i < nfans; i++) {
+ glDrawArrays(GL_TRIANGLE_FAN, first, vtxcnt[i]);
+ if (gr->fan_debug)
+ triangle_fan_debug(ev, first, vtxcnt[i]);
+ first += vtxcnt[i];
+ }
+
+ glDisableVertexAttribArray(1);
+ glDisableVertexAttribArray(0);
+
+ gr->vertices.size = 0;
+ gr->vtxcnt.size = 0;
+}
+
+static int
+use_output(struct weston_output *output)
+{
+ static int errored;
+ struct gl_output_state *go = get_output_state(output);
+ struct gl_renderer *gr = get_renderer(output->compositor);
+ EGLBoolean ret;
+
+ ret = eglMakeCurrent(gr->egl_display, go->egl_surface,
+ go->egl_surface, gr->egl_context);
+
+ if (ret == EGL_FALSE) {
+ if (errored)
+ return -1;
+ errored = 1;
+ weston_log("Failed to make EGL context current.\n");
+ gl_renderer_print_egl_error_state();
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+shader_init(struct gl_shader *shader, struct gl_renderer *gr,
+ const char *vertex_source, const char *fragment_source);
+
+static void
+use_shader(struct gl_renderer *gr, struct gl_shader *shader)
+{
+ if (!shader->program) {
+ int ret;
+
+ ret = shader_init(shader, gr,
+ shader->vertex_source,
+ shader->fragment_source);
+
+ if (ret < 0)
+ weston_log("warning: failed to compile shader\n");
+ }
+
+ if (gr->current_shader == shader)
+ return;
+ glUseProgram(shader->program);
+ gr->current_shader = shader;
+}
+
+static void
+shader_uniforms(struct gl_shader *shader,
+ struct weston_view *view,
+ struct weston_output *output)
+{
+ int i;
+ struct gl_surface_state *gs = get_surface_state(view->surface);
+ struct gl_output_state *go = get_output_state(output);
+
+ glUniformMatrix4fv(shader->proj_uniform,
+ 1, GL_FALSE, go->output_matrix.d);
+ glUniform4fv(shader->color_uniform, 1, gs->color);
+ glUniform1f(shader->alpha_uniform, view->alpha);
+
+ for (i = 0; i < gs->num_textures; i++)
+ glUniform1i(shader->tex_uniforms[i], i);
+}
+
+static void
+draw_view(struct weston_view *ev, struct weston_output *output,
+ pixman_region32_t *damage) /* in global coordinates */
+{
+ struct weston_compositor *ec = ev->surface->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ struct gl_surface_state *gs = get_surface_state(ev->surface);
+ /* repaint bounding region in global coordinates: */
+ pixman_region32_t repaint;
+ /* opaque region in surface coordinates: */
+ pixman_region32_t surface_opaque;
+ /* non-opaque region in surface coordinates: */
+ pixman_region32_t surface_blend;
+ GLint filter;
+ int i;
+
+ /* In case of a runtime switch of renderers, we may not have received
+ * an attach for this surface since the switch. In that case we don't
+ * have a valid buffer or a proper shader set up so skip rendering. */
+ if (!gs->shader)
+ return;
+
+ pixman_region32_init(&repaint);
+ pixman_region32_intersect(&repaint,
+ &ev->transform.boundingbox, damage);
+ pixman_region32_subtract(&repaint, &repaint, &ev->clip);
+
+ if (!pixman_region32_not_empty(&repaint))
+ goto out;
+
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ if (gr->fan_debug) {
+ use_shader(gr, &gr->solid_shader);
+ shader_uniforms(&gr->solid_shader, ev, output);
+ }
+
+ use_shader(gr, gs->shader);
+ shader_uniforms(gs->shader, ev, output);
+
+ if (ev->transform.enabled || output->zoom.active ||
+ output->current_scale != ev->surface->buffer_viewport.buffer.scale)
+ filter = GL_LINEAR;
+ else
+ filter = GL_NEAREST;
+
+ for (i = 0; i < gs->num_textures; i++) {
+ glActiveTexture(GL_TEXTURE0 + i);
+ glBindTexture(gs->target, gs->textures[i]);
+ glTexParameteri(gs->target, GL_TEXTURE_MIN_FILTER, filter);
+ glTexParameteri(gs->target, GL_TEXTURE_MAG_FILTER, filter);
+ }
+
+ /* blended region is whole surface minus opaque region: */
+ pixman_region32_init_rect(&surface_blend, 0, 0,
+ ev->surface->width, ev->surface->height);
+ if (ev->geometry.scissor_enabled)
+ pixman_region32_intersect(&surface_blend, &surface_blend,
+ &ev->geometry.scissor);
+ pixman_region32_subtract(&surface_blend, &surface_blend,
+ &ev->surface->opaque);
+
+ /* XXX: Should we be using ev->transform.opaque here? */
+ pixman_region32_init(&surface_opaque);
+ if (ev->geometry.scissor_enabled)
+ pixman_region32_intersect(&surface_opaque,
+ &ev->surface->opaque,
+ &ev->geometry.scissor);
+ else
+ pixman_region32_copy(&surface_opaque, &ev->surface->opaque);
+
+ if (pixman_region32_not_empty(&surface_opaque)) {
+ if (gs->shader == &gr->texture_shader_rgba) {
+ /* Special case for RGBA textures with possibly
+ * bad data in alpha channel: use the shader
+ * that forces texture alpha = 1.0.
+ * Xwayland surfaces need this.
+ */
+ use_shader(gr, &gr->texture_shader_rgbx);
+ shader_uniforms(&gr->texture_shader_rgbx, ev, output);
+ }
+
+ if (ev->alpha < 1.0)
+ glEnable(GL_BLEND);
+ else
+ glDisable(GL_BLEND);
+
+ repaint_region(ev, &repaint, &surface_opaque);
+ }
+
+ if (pixman_region32_not_empty(&surface_blend)) {
+ use_shader(gr, gs->shader);
+ glEnable(GL_BLEND);
+ repaint_region(ev, &repaint, &surface_blend);
+ }
+
+ pixman_region32_fini(&surface_blend);
+ pixman_region32_fini(&surface_opaque);
+
+out:
+ pixman_region32_fini(&repaint);
+}
+
+static void
+repaint_views(struct weston_output *output, pixman_region32_t *damage)
+{
+ struct weston_compositor *compositor = output->compositor;
+ struct weston_view *view;
+
+ wl_list_for_each_reverse(view, &compositor->view_list, link)
+ if (view->plane == &compositor->primary_plane)
+ draw_view(view, output, damage);
+}
+
+static void
+draw_output_border_texture(struct gl_output_state *go,
+ enum gl_renderer_border_side side,
+ int32_t x, int32_t y,
+ int32_t width, int32_t height)
+{
+ struct gl_border_image *img = &go->borders[side];
+ static GLushort indices [] = { 0, 1, 3, 3, 1, 2 };
+
+ if (!img->data) {
+ if (img->tex) {
+ glDeleteTextures(1, &img->tex);
+ img->tex = 0;
+ }
+
+ return;
+ }
+
+ if (!img->tex) {
+ glGenTextures(1, &img->tex);
+ glBindTexture(GL_TEXTURE_2D, img->tex);
+
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ } else {
+ glBindTexture(GL_TEXTURE_2D, img->tex);
+ }
+
+ if (go->border_status & (1 << side)) {
+#ifdef GL_EXT_unpack_subimage
+ glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0);
+#endif
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT,
+ img->tex_width, img->height, 0,
+ GL_BGRA_EXT, GL_UNSIGNED_BYTE, img->data);
+ }
+
+ GLfloat texcoord[] = {
+ 0.0f, 0.0f,
+ (GLfloat)img->width / (GLfloat)img->tex_width, 0.0f,
+ (GLfloat)img->width / (GLfloat)img->tex_width, 1.0f,
+ 0.0f, 1.0f,
+ };
+
+ GLfloat verts[] = {
+ x, y,
+ x + width, y,
+ x + width, y + height,
+ x, y + height
+ };
+
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texcoord);
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+
+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
+
+ glDisableVertexAttribArray(1);
+ glDisableVertexAttribArray(0);
+}
+
+static int
+output_has_borders(struct weston_output *output)
+{
+ struct gl_output_state *go = get_output_state(output);
+
+ return go->borders[GL_RENDERER_BORDER_TOP].data ||
+ go->borders[GL_RENDERER_BORDER_RIGHT].data ||
+ go->borders[GL_RENDERER_BORDER_BOTTOM].data ||
+ go->borders[GL_RENDERER_BORDER_LEFT].data;
+}
+
+static void
+draw_output_borders(struct weston_output *output,
+ enum gl_border_status border_status)
+{
+ struct gl_output_state *go = get_output_state(output);
+ struct gl_renderer *gr = get_renderer(output->compositor);
+ struct gl_shader *shader = &gr->texture_shader_rgba;
+ struct gl_border_image *top, *bottom, *left, *right;
+ struct weston_matrix matrix;
+ int full_width, full_height;
+
+ if (border_status == BORDER_STATUS_CLEAN)
+ return; /* Clean. Nothing to do. */
+
+ top = &go->borders[GL_RENDERER_BORDER_TOP];
+ bottom = &go->borders[GL_RENDERER_BORDER_BOTTOM];
+ left = &go->borders[GL_RENDERER_BORDER_LEFT];
+ right = &go->borders[GL_RENDERER_BORDER_RIGHT];
+
+ full_width = output->current_mode->width + left->width + right->width;
+ full_height = output->current_mode->height + top->height + bottom->height;
+
+ glDisable(GL_BLEND);
+ use_shader(gr, shader);
+
+ glViewport(0, 0, full_width, full_height);
+
+ weston_matrix_init(&matrix);
+ weston_matrix_translate(&matrix, -full_width/2.0, -full_height/2.0, 0);
+ weston_matrix_scale(&matrix, 2.0/full_width, -2.0/full_height, 1);
+ glUniformMatrix4fv(shader->proj_uniform, 1, GL_FALSE, matrix.d);
+
+ glUniform1i(shader->tex_uniforms[0], 0);
+ glUniform1f(shader->alpha_uniform, 1);
+ glActiveTexture(GL_TEXTURE0);
+
+ if (border_status & BORDER_TOP_DIRTY)
+ draw_output_border_texture(go, GL_RENDERER_BORDER_TOP,
+ 0, 0,
+ full_width, top->height);
+ if (border_status & BORDER_LEFT_DIRTY)
+ draw_output_border_texture(go, GL_RENDERER_BORDER_LEFT,
+ 0, top->height,
+ left->width, output->current_mode->height);
+ if (border_status & BORDER_RIGHT_DIRTY)
+ draw_output_border_texture(go, GL_RENDERER_BORDER_RIGHT,
+ full_width - right->width, top->height,
+ right->width, output->current_mode->height);
+ if (border_status & BORDER_BOTTOM_DIRTY)
+ draw_output_border_texture(go, GL_RENDERER_BORDER_BOTTOM,
+ 0, full_height - bottom->height,
+ full_width, bottom->height);
+}
+
+static void
+output_get_border_damage(struct weston_output *output,
+ enum gl_border_status border_status,
+ pixman_region32_t *damage)
+{
+ struct gl_output_state *go = get_output_state(output);
+ struct gl_border_image *top, *bottom, *left, *right;
+ int full_width, full_height;
+
+ if (border_status == BORDER_STATUS_CLEAN)
+ return; /* Clean. Nothing to do. */
+
+ top = &go->borders[GL_RENDERER_BORDER_TOP];
+ bottom = &go->borders[GL_RENDERER_BORDER_BOTTOM];
+ left = &go->borders[GL_RENDERER_BORDER_LEFT];
+ right = &go->borders[GL_RENDERER_BORDER_RIGHT];
+
+ full_width = output->current_mode->width + left->width + right->width;
+ full_height = output->current_mode->height + top->height + bottom->height;
+ if (border_status & BORDER_TOP_DIRTY)
+ pixman_region32_union_rect(damage, damage,
+ 0, 0,
+ full_width, top->height);
+ if (border_status & BORDER_LEFT_DIRTY)
+ pixman_region32_union_rect(damage, damage,
+ 0, top->height,
+ left->width, output->current_mode->height);
+ if (border_status & BORDER_RIGHT_DIRTY)
+ pixman_region32_union_rect(damage, damage,
+ full_width - right->width, top->height,
+ right->width, output->current_mode->height);
+ if (border_status & BORDER_BOTTOM_DIRTY)
+ pixman_region32_union_rect(damage, damage,
+ 0, full_height - bottom->height,
+ full_width, bottom->height);
+}
+
+static void
+output_get_damage(struct weston_output *output,
+ pixman_region32_t *buffer_damage, uint32_t *border_damage)
+{
+ struct gl_output_state *go = get_output_state(output);
+ struct gl_renderer *gr = get_renderer(output->compositor);
+ EGLint buffer_age = 0;
+ EGLBoolean ret;
+ int i;
+
+ if (gr->has_egl_buffer_age) {
+ ret = eglQuerySurface(gr->egl_display, go->egl_surface,
+ EGL_BUFFER_AGE_EXT, &buffer_age);
+ if (ret == EGL_FALSE) {
+ weston_log("buffer age query failed.\n");
+ gl_renderer_print_egl_error_state();
+ }
+ }
+
+ if (buffer_age == 0 || buffer_age - 1 > BUFFER_DAMAGE_COUNT) {
+ pixman_region32_copy(buffer_damage, &output->region);
+ *border_damage = BORDER_ALL_DIRTY;
+ } else {
+ for (i = 0; i < buffer_age - 1; i++)
+ *border_damage |= go->border_damage[(go->buffer_damage_index + i) % BUFFER_DAMAGE_COUNT];
+
+ if (*border_damage & BORDER_SIZE_CHANGED) {
+ /* If we've had a resize, we have to do a full
+ * repaint. */
+ *border_damage |= BORDER_ALL_DIRTY;
+ pixman_region32_copy(buffer_damage, &output->region);
+ } else {
+ for (i = 0; i < buffer_age - 1; i++)
+ pixman_region32_union(buffer_damage,
+ buffer_damage,
+ &go->buffer_damage[(go->buffer_damage_index + i) % BUFFER_DAMAGE_COUNT]);
+ }
+ }
+}
+
+static void
+output_rotate_damage(struct weston_output *output,
+ pixman_region32_t *output_damage,
+ enum gl_border_status border_status)
+{
+ struct gl_output_state *go = get_output_state(output);
+ struct gl_renderer *gr = get_renderer(output->compositor);
+
+ if (!gr->has_egl_buffer_age)
+ return;
+
+ go->buffer_damage_index += BUFFER_DAMAGE_COUNT - 1;
+ go->buffer_damage_index %= BUFFER_DAMAGE_COUNT;
+
+ pixman_region32_copy(&go->buffer_damage[go->buffer_damage_index], output_damage);
+ go->border_damage[go->buffer_damage_index] = border_status;
+}
+
+/* NOTE: We now allow falling back to ARGB gl visuals when XRGB is
+ * unavailable, so we're assuming the background has no transparency
+ * and that everything with a blend, like drop shadows, will have something
+ * opaque (like the background) drawn underneath it.
+ *
+ * Depending on the underlying hardware, violating that assumption could
+ * result in seeing through to another display plane.
+ */
+static void
+gl_renderer_repaint_output(struct weston_output *output,
+ pixman_region32_t *output_damage)
+{
+ struct gl_output_state *go = get_output_state(output);
+ struct weston_compositor *compositor = output->compositor;
+ struct gl_renderer *gr = get_renderer(compositor);
+ EGLBoolean ret;
+ static int errored;
+#ifdef EGL_EXT_swap_buffers_with_damage
+ int i, nrects, buffer_height;
+ EGLint *egl_damage, *d;
+ pixman_box32_t *rects;
+#endif
+ pixman_region32_t buffer_damage, total_damage;
+ enum gl_border_status border_damage = BORDER_STATUS_CLEAN;
+
+ if (use_output(output) < 0)
+ return;
+
+ /* Calculate the viewport */
+ glViewport(go->borders[GL_RENDERER_BORDER_LEFT].width,
+ go->borders[GL_RENDERER_BORDER_BOTTOM].height,
+ output->current_mode->width,
+ output->current_mode->height);
+
+ /* Calculate the global GL matrix */
+ go->output_matrix = output->matrix;
+ weston_matrix_translate(&go->output_matrix,
+ -(output->current_mode->width / 2.0),
+ -(output->current_mode->height / 2.0), 0);
+ weston_matrix_scale(&go->output_matrix,
+ 2.0 / output->current_mode->width,
+ -2.0 / output->current_mode->height, 1);
+
+ /* if debugging, redraw everything outside the damage to clean up
+ * debug lines from the previous draw on this buffer:
+ */
+ if (gr->fan_debug) {
+ pixman_region32_t undamaged;
+ pixman_region32_init(&undamaged);
+ pixman_region32_subtract(&undamaged, &output->region,
+ output_damage);
+ gr->fan_debug = 0;
+ repaint_views(output, &undamaged);
+ gr->fan_debug = 1;
+ pixman_region32_fini(&undamaged);
+ }
+
+ pixman_region32_init(&total_damage);
+ pixman_region32_init(&buffer_damage);
+
+ output_get_damage(output, &buffer_damage, &border_damage);
+ output_rotate_damage(output, output_damage, go->border_status);
+
+ pixman_region32_union(&total_damage, &buffer_damage, output_damage);
+ border_damage |= go->border_status;
+
+ repaint_views(output, &total_damage);
+
+ pixman_region32_fini(&total_damage);
+ pixman_region32_fini(&buffer_damage);
+
+ draw_output_borders(output, border_damage);
+
+ pixman_region32_copy(&output->previous_damage, output_damage);
+ wl_signal_emit(&output->frame_signal, output);
+
+#ifdef EGL_EXT_swap_buffers_with_damage
+ if (gr->swap_buffers_with_damage) {
+ pixman_region32_init(&buffer_damage);
+ weston_transformed_region(output->width, output->height,
+ output->transform,
+ output->current_scale,
+ output_damage, &buffer_damage);
+
+ if (output_has_borders(output)) {
+ pixman_region32_translate(&buffer_damage,
+ go->borders[GL_RENDERER_BORDER_LEFT].width,
+ go->borders[GL_RENDERER_BORDER_TOP].height);
+ output_get_border_damage(output, go->border_status,
+ &buffer_damage);
+ }
+
+ rects = pixman_region32_rectangles(&buffer_damage, &nrects);
+ egl_damage = malloc(nrects * 4 * sizeof(EGLint));
+
+ buffer_height = go->borders[GL_RENDERER_BORDER_TOP].height +
+ output->current_mode->height +
+ go->borders[GL_RENDERER_BORDER_BOTTOM].height;
+
+ d = egl_damage;
+ for (i = 0; i < nrects; ++i) {
+ *d++ = rects[i].x1;
+ *d++ = buffer_height - rects[i].y2;
+ *d++ = rects[i].x2 - rects[i].x1;
+ *d++ = rects[i].y2 - rects[i].y1;
+ }
+ ret = gr->swap_buffers_with_damage(gr->egl_display,
+ go->egl_surface,
+ egl_damage, nrects);
+ free(egl_damage);
+ pixman_region32_fini(&buffer_damage);
+ } else {
+ ret = eglSwapBuffers(gr->egl_display, go->egl_surface);
+ }
+#else /* ! defined EGL_EXT_swap_buffers_with_damage */
+ ret = eglSwapBuffers(gr->egl_display, go->egl_surface);
+#endif
+
+ if (ret == EGL_FALSE && !errored) {
+ errored = 1;
+ weston_log("Failed in eglSwapBuffers.\n");
+ gl_renderer_print_egl_error_state();
+ }
+
+ go->border_status = BORDER_STATUS_CLEAN;
+}
+
+static int
+gl_renderer_read_pixels(struct weston_output *output,
+ pixman_format_code_t format, void *pixels,
+ uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height)
+{
+ GLenum gl_format;
+ struct gl_output_state *go = get_output_state(output);
+
+ x += go->borders[GL_RENDERER_BORDER_LEFT].width;
+ y += go->borders[GL_RENDERER_BORDER_BOTTOM].height;
+
+ switch (format) {
+ case PIXMAN_a8r8g8b8:
+ gl_format = GL_BGRA_EXT;
+ break;
+ case PIXMAN_a8b8g8r8:
+ gl_format = GL_RGBA;
+ break;
+ default:
+ return -1;
+ }
+
+ if (use_output(output) < 0)
+ return -1;
+
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+ glReadPixels(x, y, width, height, gl_format,
+ GL_UNSIGNED_BYTE, pixels);
+
+ return 0;
+}
+
+static void
+gl_renderer_flush_damage(struct weston_surface *surface)
+{
+ struct gl_renderer *gr = get_renderer(surface->compositor);
+ struct gl_surface_state *gs = get_surface_state(surface);
+ struct weston_buffer *buffer = gs->buffer_ref.buffer;
+ struct weston_view *view;
+ bool texture_used;
+
+#ifdef GL_EXT_unpack_subimage
+ pixman_box32_t *rectangles;
+ void *data;
+ int i, n;
+#endif
+
+ pixman_region32_union(&gs->texture_damage,
+ &gs->texture_damage, &surface->damage);
+
+ if (!buffer)
+ return;
+
+ /* Avoid upload, if the texture won't be used this time.
+ * We still accumulate the damage in texture_damage, and
+ * hold the reference to the buffer, in case the surface
+ * migrates back to the primary plane.
+ */
+ texture_used = false;
+ wl_list_for_each(view, &surface->views, surface_link) {
+ if (view->plane == &surface->compositor->primary_plane) {
+ texture_used = true;
+ break;
+ }
+ }
+ if (!texture_used)
+ return;
+
+ if (!pixman_region32_not_empty(&gs->texture_damage) &&
+ !gs->needs_full_upload)
+ goto done;
+
+ glBindTexture(GL_TEXTURE_2D, gs->textures[0]);
+
+ if (!gr->has_unpack_subimage) {
+ wl_shm_buffer_begin_access(buffer->shm_buffer);
+ glTexImage2D(GL_TEXTURE_2D, 0, gs->gl_format,
+ gs->pitch, buffer->height, 0,
+ gs->gl_format, gs->gl_pixel_type,
+ wl_shm_buffer_get_data(buffer->shm_buffer));
+ wl_shm_buffer_end_access(buffer->shm_buffer);
+
+ goto done;
+ }
+
+#ifdef GL_EXT_unpack_subimage
+ glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, gs->pitch);
+ data = wl_shm_buffer_get_data(buffer->shm_buffer);
+
+ if (gs->needs_full_upload) {
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0);
+ wl_shm_buffer_begin_access(buffer->shm_buffer);
+ glTexImage2D(GL_TEXTURE_2D, 0, gs->gl_format,
+ gs->pitch, buffer->height, 0,
+ gs->gl_format, gs->gl_pixel_type, data);
+ wl_shm_buffer_end_access(buffer->shm_buffer);
+ goto done;
+ }
+
+ rectangles = pixman_region32_rectangles(&gs->texture_damage, &n);
+ wl_shm_buffer_begin_access(buffer->shm_buffer);
+ for (i = 0; i < n; i++) {
+ pixman_box32_t r;
+
+ r = weston_surface_to_buffer_rect(surface, rectangles[i]);
+
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, r.x1);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, r.y1);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, r.x1, r.y1,
+ r.x2 - r.x1, r.y2 - r.y1,
+ gs->gl_format, gs->gl_pixel_type, data);
+ }
+ wl_shm_buffer_end_access(buffer->shm_buffer);
+#endif
+
+done:
+ pixman_region32_fini(&gs->texture_damage);
+ pixman_region32_init(&gs->texture_damage);
+ gs->needs_full_upload = false;
+
+ weston_buffer_reference(&gs->buffer_ref, NULL);
+}
+
+static void
+ensure_textures(struct gl_surface_state *gs, int num_textures)
+{
+ int i;
+
+ if (num_textures <= gs->num_textures)
+ return;
+
+ for (i = gs->num_textures; i < num_textures; i++) {
+ glGenTextures(1, &gs->textures[i]);
+ glBindTexture(gs->target, gs->textures[i]);
+ glTexParameteri(gs->target,
+ GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(gs->target,
+ GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+ gs->num_textures = num_textures;
+ glBindTexture(gs->target, 0);
+}
+
+static void
+gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer,
+ struct wl_shm_buffer *shm_buffer)
+{
+ struct weston_compositor *ec = es->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ struct gl_surface_state *gs = get_surface_state(es);
+ GLenum gl_format, gl_pixel_type;
+ int pitch;
+
+ buffer->shm_buffer = shm_buffer;
+ buffer->width = wl_shm_buffer_get_width(shm_buffer);
+ buffer->height = wl_shm_buffer_get_height(shm_buffer);
+
+ switch (wl_shm_buffer_get_format(shm_buffer)) {
+ case WL_SHM_FORMAT_XRGB8888:
+ gs->shader = &gr->texture_shader_rgbx;
+ pitch = wl_shm_buffer_get_stride(shm_buffer) / 4;
+ gl_format = GL_BGRA_EXT;
+ gl_pixel_type = GL_UNSIGNED_BYTE;
+ break;
+ case WL_SHM_FORMAT_ARGB8888:
+ gs->shader = &gr->texture_shader_rgba;
+ pitch = wl_shm_buffer_get_stride(shm_buffer) / 4;
+ gl_format = GL_BGRA_EXT;
+ gl_pixel_type = GL_UNSIGNED_BYTE;
+ break;
+ case WL_SHM_FORMAT_RGB565:
+ gs->shader = &gr->texture_shader_rgbx;
+ pitch = wl_shm_buffer_get_stride(shm_buffer) / 2;
+ gl_format = GL_RGB;
+ gl_pixel_type = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ default:
+ weston_log("warning: unknown shm buffer format: %08x\n",
+ wl_shm_buffer_get_format(shm_buffer));
+ return;
+ }
+
+ /* Only allocate a texture if it doesn't match existing one.
+ * If a switch from DRM allocated buffer to a SHM buffer is
+ * happening, we need to allocate a new texture buffer. */
+ if (pitch != gs->pitch ||
+ buffer->height != gs->height ||
+ gl_format != gs->gl_format ||
+ gl_pixel_type != gs->gl_pixel_type ||
+ gs->buffer_type != BUFFER_TYPE_SHM) {
+ gs->pitch = pitch;
+ gs->height = buffer->height;
+ gs->target = GL_TEXTURE_2D;
+ gs->gl_format = gl_format;
+ gs->gl_pixel_type = gl_pixel_type;
+ gs->buffer_type = BUFFER_TYPE_SHM;
+ gs->needs_full_upload = true;
+ gs->y_inverted = 1;
+
+ gs->surface = es;
+
+ ensure_textures(gs, 1);
+ }
+}
+
+static void
+gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer,
+ uint32_t format)
+{
+ struct weston_compositor *ec = es->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ struct gl_surface_state *gs = get_surface_state(es);
+ EGLint attribs[3];
+ int i, num_planes;
+
+ buffer->legacy_buffer = (struct wl_buffer *)buffer->resource;
+ gr->query_buffer(gr->egl_display, buffer->legacy_buffer,
+ EGL_WIDTH, &buffer->width);
+ gr->query_buffer(gr->egl_display, buffer->legacy_buffer,
+ EGL_HEIGHT, &buffer->height);
+ gr->query_buffer(gr->egl_display, buffer->legacy_buffer,
+ EGL_WAYLAND_Y_INVERTED_WL, &buffer->y_inverted);
+
+ for (i = 0; i < gs->num_images; i++) {
+ egl_image_unref(gs->images[i]);
+ gs->images[i] = NULL;
+ }
+ gs->num_images = 0;
+ gs->target = GL_TEXTURE_2D;
+ switch (format) {
+ case EGL_TEXTURE_RGB:
+ case EGL_TEXTURE_RGBA:
+ default:
+ num_planes = 1;
+ gs->shader = &gr->texture_shader_rgba;
+ break;
+ case EGL_TEXTURE_EXTERNAL_WL:
+ num_planes = 1;
+ gs->target = GL_TEXTURE_EXTERNAL_OES;
+ gs->shader = &gr->texture_shader_egl_external;
+ break;
+ case EGL_TEXTURE_Y_UV_WL:
+ num_planes = 2;
+ gs->shader = &gr->texture_shader_y_uv;
+ break;
+ case EGL_TEXTURE_Y_U_V_WL:
+ num_planes = 3;
+ gs->shader = &gr->texture_shader_y_u_v;
+ break;
+ case EGL_TEXTURE_Y_XUXV_WL:
+ num_planes = 2;
+ gs->shader = &gr->texture_shader_y_xuxv;
+ break;
+ }
+
+ ensure_textures(gs, num_planes);
+ for (i = 0; i < num_planes; i++) {
+ attribs[0] = EGL_WAYLAND_PLANE_WL;
+ attribs[1] = i;
+ attribs[2] = EGL_NONE;
+ gs->images[i] = egl_image_create(gr,
+ EGL_WAYLAND_BUFFER_WL,
+ buffer->legacy_buffer,
+ attribs);
+ if (!gs->images[i]) {
+ weston_log("failed to create img for plane %d\n", i);
+ continue;
+ }
+ gs->num_images++;
+
+ glActiveTexture(GL_TEXTURE0 + i);
+ glBindTexture(gs->target, gs->textures[i]);
+ gr->image_target_texture_2d(gs->target,
+ gs->images[i]->image);
+ }
+
+ gs->pitch = buffer->width;
+ gs->height = buffer->height;
+ gs->buffer_type = BUFFER_TYPE_EGL;
+ gs->y_inverted = buffer->y_inverted;
+}
+
+static void
+gl_renderer_destroy_dmabuf(struct linux_dmabuf_buffer *dmabuf)
+{
+ struct dmabuf_image *image = dmabuf->user_data;
+
+ dmabuf_image_destroy(image);
+}
+
+static struct egl_image *
+import_simple_dmabuf(struct gl_renderer *gr,
+ struct dmabuf_attributes *attributes)
+{
+ struct egl_image *image;
+ EGLint attribs[30];
+ int atti = 0;
+
+ /* This requires the Mesa commit in
+ * Mesa 10.3 (08264e5dad4df448e7718e782ad9077902089a07) or
+ * Mesa 10.2.7 (55d28925e6109a4afd61f109e845a8a51bd17652).
+ * Otherwise Mesa closes the fd behind our back and re-importing
+ * will fail.
+ * https://bugs.freedesktop.org/show_bug.cgi?id=76188
+ */
+
+ attribs[atti++] = EGL_WIDTH;
+ attribs[atti++] = attributes->width;
+ attribs[atti++] = EGL_HEIGHT;
+ attribs[atti++] = attributes->height;
+ attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
+ attribs[atti++] = attributes->format;
+ /* XXX: Add modifier here when supported */
+
+ if (attributes->n_planes > 0) {
+ attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT;
+ attribs[atti++] = attributes->fd[0];
+ attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
+ attribs[atti++] = attributes->offset[0];
+ attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
+ attribs[atti++] = attributes->stride[0];
+ }
+
+ if (attributes->n_planes > 1) {
+ attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT;
+ attribs[atti++] = attributes->fd[1];
+ attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
+ attribs[atti++] = attributes->offset[1];
+ attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
+ attribs[atti++] = attributes->stride[1];
+ }
+
+ if (attributes->n_planes > 2) {
+ attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT;
+ attribs[atti++] = attributes->fd[2];
+ attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
+ attribs[atti++] = attributes->offset[2];
+ attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
+ attribs[atti++] = attributes->stride[2];
+ }
+
+ attribs[atti++] = EGL_NONE;
+
+ image = egl_image_create(gr, EGL_LINUX_DMA_BUF_EXT, NULL,
+ attribs);
+
+ return image;
+}
+
+/* The kernel header drm_fourcc.h defines the DRM formats below. We duplicate
+ * some of the definitions here so that building Weston won't require
+ * bleeding-edge kernel headers.
+ */
+#ifndef DRM_FORMAT_R8
+#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */
+#endif
+
+#ifndef DRM_FORMAT_GR88
+#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
+#endif
+
+struct yuv_format_descriptor yuv_formats[] = {
+ {
+ .format = DRM_FORMAT_YUYV,
+ .input_planes = 1,
+ .output_planes = 2,
+ .texture_type = EGL_TEXTURE_Y_XUXV_WL,
+ {{
+ .width_divisor = 1,
+ .height_divisor = 1,
+ .format = DRM_FORMAT_GR88,
+ .plane_index = 0
+ }, {
+ .width_divisor = 2,
+ .height_divisor = 1,
+ .format = DRM_FORMAT_ARGB8888,
+ .plane_index = 0
+ }}
+ }, {
+ .format = DRM_FORMAT_NV12,
+ .input_planes = 2,
+ .output_planes = 2,
+ .texture_type = EGL_TEXTURE_Y_UV_WL,
+ {{
+ .width_divisor = 1,
+ .height_divisor = 1,
+ .format = DRM_FORMAT_R8,
+ .plane_index = 0
+ }, {
+ .width_divisor = 2,
+ .height_divisor = 2,
+ .format = DRM_FORMAT_GR88,
+ .plane_index = 1
+ }}
+ }, {
+ .format = DRM_FORMAT_YUV420,
+ .input_planes = 3,
+ .output_planes = 3,
+ .texture_type = EGL_TEXTURE_Y_U_V_WL,
+ {{
+ .width_divisor = 1,
+ .height_divisor = 1,
+ .format = DRM_FORMAT_R8,
+ .plane_index = 0
+ }, {
+ .width_divisor = 2,
+ .height_divisor = 2,
+ .format = DRM_FORMAT_R8,
+ .plane_index = 1
+ }, {
+ .width_divisor = 2,
+ .height_divisor = 2,
+ .format = DRM_FORMAT_R8,
+ .plane_index = 2
+ }}
+ }
+};
+
+static struct egl_image *
+import_dmabuf_single_plane(struct gl_renderer *gr,
+ const struct dmabuf_attributes *attributes,
+ struct yuv_plane_descriptor *descriptor)
+{
+ struct dmabuf_attributes plane;
+ struct egl_image *image;
+ char fmt[4];
+
+ plane.width = attributes->width / descriptor->width_divisor;
+ plane.height = attributes->height / descriptor->height_divisor;
+ plane.format = descriptor->format;
+ plane.n_planes = 1;
+ plane.fd[0] = attributes->fd[descriptor->plane_index];
+ plane.offset[0] = attributes->offset[descriptor->plane_index];
+ plane.stride[0] = attributes->stride[descriptor->plane_index];
+ plane.modifier[0] = attributes->modifier[descriptor->plane_index];
+
+ image = import_simple_dmabuf(gr, &plane);
+ if (!image) {
+ weston_log("Failed to import plane %d as %.4s\n",
+ descriptor->plane_index,
+ dump_format(descriptor->format, fmt));
+ return NULL;
+ }
+
+ return image;
+}
+
+static bool
+import_yuv_dmabuf(struct gl_renderer *gr,
+ struct dmabuf_image *image)
+{
+ unsigned i;
+ int j;
+ int ret;
+ struct yuv_format_descriptor *format = NULL;
+ struct dmabuf_attributes *attributes = &image->dmabuf->attributes;
+ char fmt[4];
+
+ for (i = 0; i < ARRAY_LENGTH(yuv_formats); ++i) {
+ if (yuv_formats[i].format == attributes->format) {
+ format = &yuv_formats[i];
+ break;
+ }
+ }
+
+ if (!format) {
+ weston_log("Error during import, and no known conversion for format "
+ "%.4s in the renderer",
+ dump_format(attributes->format, fmt));
+ return false;
+ }
+
+ if (attributes->n_planes != format->input_planes) {
+ weston_log("%.4s dmabuf must contain %d plane%s (%d provided)",
+ dump_format(format->format, fmt),
+ format->input_planes,
+ (format->input_planes > 1) ? "s" : "",
+ attributes->n_planes);
+ return false;
+ }
+
+ for (j = 0; j < format->output_planes; ++j) {
+ image->images[j] = import_dmabuf_single_plane(gr, attributes,
+ &format->plane[j]);
+ if (!image->images[j]) {
+ while (j) {
+ ret = egl_image_unref(image->images[--j]);
+ assert(ret == 0);
+ }
+ return false;
+ }
+ }
+
+ image->num_images = format->output_planes;
+
+ switch (format->texture_type) {
+ case EGL_TEXTURE_Y_XUXV_WL:
+ image->shader = &gr->texture_shader_y_xuxv;
+ break;
+ case EGL_TEXTURE_Y_UV_WL:
+ image->shader = &gr->texture_shader_y_uv;
+ break;
+ case EGL_TEXTURE_Y_U_V_WL:
+ image->shader = &gr->texture_shader_y_u_v;
+ break;
+ default:
+ assert(false);
+ }
+
+ return true;
+}
+
+static GLenum
+choose_texture_target(struct dmabuf_attributes *attributes)
+{
+ if (attributes->n_planes > 1)
+ return GL_TEXTURE_EXTERNAL_OES;
+
+ switch (attributes->format & ~DRM_FORMAT_BIG_ENDIAN) {
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_VYUY:
+ case DRM_FORMAT_AYUV:
+ return GL_TEXTURE_EXTERNAL_OES;
+ default:
+ return GL_TEXTURE_2D;
+ }
+}
+
+static struct dmabuf_image *
+import_dmabuf(struct gl_renderer *gr,
+ struct linux_dmabuf_buffer *dmabuf)
+{
+ struct egl_image *egl_image;
+ struct dmabuf_image *image;
+
+ image = dmabuf_image_create();
+ image->dmabuf = dmabuf;
+
+ egl_image = import_simple_dmabuf(gr, &dmabuf->attributes);
+ if (egl_image) {
+ image->num_images = 1;
+ image->images[0] = egl_image;
+ image->import_type = IMPORT_TYPE_DIRECT;
+ image->target = choose_texture_target(&dmabuf->attributes);
+
+ switch (image->target) {
+ case GL_TEXTURE_2D:
+ image->shader = &gr->texture_shader_rgba;
+ break;
+ default:
+ image->shader = &gr->texture_shader_egl_external;
+ }
+ } else {
+ if (!import_yuv_dmabuf(gr, image)) {
+ dmabuf_image_destroy(image);
+ return NULL;
+ }
+ image->import_type = IMPORT_TYPE_GL_CONVERSION;
+ image->target = GL_TEXTURE_2D;
+ }
+
+ return image;
+}
+
+static bool
+gl_renderer_import_dmabuf(struct weston_compositor *ec,
+ struct linux_dmabuf_buffer *dmabuf)
+{
+ struct gl_renderer *gr = get_renderer(ec);
+ struct dmabuf_image *image;
+ int i;
+
+ assert(gr->has_dmabuf_import);
+
+ for (i = 0; i < dmabuf->attributes.n_planes; i++) {
+ /* EGL import does not have modifiers */
+ if (dmabuf->attributes.modifier[i] != 0)
+ return false;
+ }
+
+ /* reject all flags we do not recognize or handle */
+ if (dmabuf->attributes.flags & ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT)
+ return false;
+
+ image = import_dmabuf(gr, dmabuf);
+ if (!image)
+ return false;
+
+ wl_list_insert(&gr->dmabuf_images, &image->link);
+ linux_dmabuf_buffer_set_user_data(dmabuf, image,
+ gl_renderer_destroy_dmabuf);
+
+ return true;
+}
+
+static bool
+import_known_dmabuf(struct gl_renderer *gr,
+ struct dmabuf_image *image)
+{
+ switch (image->import_type) {
+ case IMPORT_TYPE_DIRECT:
+ image->images[0] = import_simple_dmabuf(gr, &image->dmabuf->attributes);
+ if (!image->images[0])
+ return false;
+ break;
+
+ case IMPORT_TYPE_GL_CONVERSION:
+ if (!import_yuv_dmabuf(gr, image))
+ return false;
+ break;
+
+ default:
+ weston_log("Invalid import type for dmabuf\n");
+ return false;
+ }
+
+ return true;
+}
+
+static void
+gl_renderer_attach_dmabuf(struct weston_surface *surface,
+ struct weston_buffer *buffer,
+ struct linux_dmabuf_buffer *dmabuf)
+{
+ struct gl_renderer *gr = get_renderer(surface->compositor);
+ struct gl_surface_state *gs = get_surface_state(surface);
+ struct dmabuf_image *image;
+ int i;
+ int ret;
+
+ if (!gr->has_dmabuf_import) {
+ linux_dmabuf_buffer_send_server_error(dmabuf,
+ "EGL dmabuf import not supported");
+ return;
+ }
+
+ buffer->width = dmabuf->attributes.width;
+ buffer->height = dmabuf->attributes.height;
+ buffer->y_inverted =
+ !!(dmabuf->attributes.flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT);
+
+ for (i = 0; i < gs->num_images; i++)
+ egl_image_unref(gs->images[i]);
+ gs->num_images = 0;
+
+ /*
+ * We try to always hold an imported EGLImage from the dmabuf
+ * to prevent the client from preventing re-imports. But, we also
+ * need to re-import every time the contents may change because
+ * GL driver's caching may need flushing.
+ *
+ * Here we release the cache reference which has to be final.
+ */
+ image = linux_dmabuf_buffer_get_user_data(dmabuf);
+
+ /* The dmabuf_image should have been created during the import */
+ assert(image != NULL);
+
+ for (i = 0; i < image->num_images; ++i) {
+ ret = egl_image_unref(image->images[i]);
+ assert(ret == 0);
+ }
+
+ if (!import_known_dmabuf(gr, image)) {
+ linux_dmabuf_buffer_send_server_error(dmabuf, "EGL dmabuf import failed");
+ return;
+ }
+
+ gs->num_images = image->num_images;
+ for (i = 0; i < gs->num_images; ++i)
+ gs->images[i] = egl_image_ref(image->images[i]);
+
+ gs->target = image->target;
+ ensure_textures(gs, gs->num_images);
+ for (i = 0; i < gs->num_images; ++i) {
+ glActiveTexture(GL_TEXTURE0 + i);
+ glBindTexture(gs->target, gs->textures[i]);
+ gr->image_target_texture_2d(gs->target, gs->images[i]->image);
+ }
+
+ gs->shader = image->shader;
+ gs->pitch = buffer->width;
+ gs->height = buffer->height;
+ gs->buffer_type = BUFFER_TYPE_EGL;
+ gs->y_inverted = buffer->y_inverted;
+}
+
+static void
+gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
+{
+ struct weston_compositor *ec = es->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ struct gl_surface_state *gs = get_surface_state(es);
+ struct wl_shm_buffer *shm_buffer;
+ struct linux_dmabuf_buffer *dmabuf;
+ EGLint format;
+ int i;
+
+ weston_buffer_reference(&gs->buffer_ref, buffer);
+
+ if (!buffer) {
+ for (i = 0; i < gs->num_images; i++) {
+ egl_image_unref(gs->images[i]);
+ gs->images[i] = NULL;
+ }
+ gs->num_images = 0;
+ glDeleteTextures(gs->num_textures, gs->textures);
+ gs->num_textures = 0;
+ gs->buffer_type = BUFFER_TYPE_NULL;
+ gs->y_inverted = 1;
+ return;
+ }
+
+ shm_buffer = wl_shm_buffer_get(buffer->resource);
+
+ if (shm_buffer)
+ gl_renderer_attach_shm(es, buffer, shm_buffer);
+ else if (gr->query_buffer(gr->egl_display, (void *) buffer->resource,
+ EGL_TEXTURE_FORMAT, &format))
+ gl_renderer_attach_egl(es, buffer, format);
+ else if ((dmabuf = linux_dmabuf_buffer_get(buffer->resource)))
+ gl_renderer_attach_dmabuf(es, buffer, dmabuf);
+ else {
+ weston_log("unhandled buffer type!\n");
+ weston_buffer_reference(&gs->buffer_ref, NULL);
+ gs->buffer_type = BUFFER_TYPE_NULL;
+ gs->y_inverted = 1;
+ }
+}
+
+static void
+gl_renderer_surface_set_color(struct weston_surface *surface,
+ float red, float green, float blue, float alpha)
+{
+ struct gl_surface_state *gs = get_surface_state(surface);
+ struct gl_renderer *gr = get_renderer(surface->compositor);
+
+ gs->color[0] = red;
+ gs->color[1] = green;
+ gs->color[2] = blue;
+ gs->color[3] = alpha;
+ gs->buffer_type = BUFFER_TYPE_SOLID;
+ gs->pitch = 1;
+ gs->height = 1;
+
+ gs->shader = &gr->solid_shader;
+}
+
+static void
+gl_renderer_surface_get_content_size(struct weston_surface *surface,
+ int *width, int *height)
+{
+ struct gl_surface_state *gs = get_surface_state(surface);
+
+ if (gs->buffer_type == BUFFER_TYPE_NULL) {
+ *width = 0;
+ *height = 0;
+ } else {
+ *width = gs->pitch;
+ *height = gs->height;
+ }
+}
+
+static uint32_t
+pack_color(pixman_format_code_t format, float *c)
+{
+ uint8_t r = round(c[0] * 255.0f);
+ uint8_t g = round(c[1] * 255.0f);
+ uint8_t b = round(c[2] * 255.0f);
+ uint8_t a = round(c[3] * 255.0f);
+
+ switch (format) {
+ case PIXMAN_a8b8g8r8:
+ return (a << 24) | (b << 16) | (g << 8) | r;
+ default:
+ assert(0);
+ return 0;
+ }
+}
+
+static int
+gl_renderer_surface_copy_content(struct weston_surface *surface,
+ void *target, size_t size,
+ int src_x, int src_y,
+ int width, int height)
+{
+ static const GLfloat verts[4 * 2] = {
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ 1.0f, 1.0f,
+ 0.0f, 1.0f
+ };
+ static const GLfloat projmat_normal[16] = { /* transpose */
+ 2.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 2.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ -1.0f, -1.0f, 0.0f, 1.0f
+ };
+ static const GLfloat projmat_yinvert[16] = { /* transpose */
+ 2.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, -2.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ -1.0f, 1.0f, 0.0f, 1.0f
+ };
+ const pixman_format_code_t format = PIXMAN_a8b8g8r8;
+ const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */
+ const GLenum gl_format = GL_RGBA; /* PIXMAN_a8b8g8r8 little-endian */
+ struct gl_renderer *gr = get_renderer(surface->compositor);
+ struct gl_surface_state *gs = get_surface_state(surface);
+ int cw, ch;
+ GLuint fbo;
+ GLuint tex;
+ GLenum status;
+ const GLfloat *proj;
+ int i;
+
+ gl_renderer_surface_get_content_size(surface, &cw, &ch);
+
+ switch (gs->buffer_type) {
+ case BUFFER_TYPE_NULL:
+ return -1;
+ case BUFFER_TYPE_SOLID:
+ *(uint32_t *)target = pack_color(format, gs->color);
+ return 0;
+ case BUFFER_TYPE_SHM:
+ gl_renderer_flush_damage(surface);
+ /* fall through */
+ case BUFFER_TYPE_EGL:
+ break;
+ }
+
+ glGenTextures(1, &tex);
+ glBindTexture(GL_TEXTURE_2D, tex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cw, ch,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, tex, 0);
+
+ status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ weston_log("%s: fbo error: %#x\n", __func__, status);
+ glDeleteFramebuffers(1, &fbo);
+ glDeleteTextures(1, &tex);
+ return -1;
+ }
+
+ glViewport(0, 0, cw, ch);
+ glDisable(GL_BLEND);
+ use_shader(gr, gs->shader);
+ if (gs->y_inverted)
+ proj = projmat_normal;
+ else
+ proj = projmat_yinvert;
+
+ glUniformMatrix4fv(gs->shader->proj_uniform, 1, GL_FALSE, proj);
+ glUniform1f(gs->shader->alpha_uniform, 1.0f);
+
+ for (i = 0; i < gs->num_textures; i++) {
+ glUniform1i(gs->shader->tex_uniforms[i], i);
+
+ glActiveTexture(GL_TEXTURE0 + i);
+ glBindTexture(gs->target, gs->textures[i]);
+ glTexParameteri(gs->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(gs->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ }
+
+ /* position: */
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts);
+ glEnableVertexAttribArray(0);
+
+ /* texcoord: */
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, verts);
+ glEnableVertexAttribArray(1);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glDisableVertexAttribArray(1);
+ glDisableVertexAttribArray(0);
+
+ glPixelStorei(GL_PACK_ALIGNMENT, bytespp);
+ glReadPixels(src_x, src_y, width, height, gl_format,
+ GL_UNSIGNED_BYTE, target);
+
+ glDeleteFramebuffers(1, &fbo);
+ glDeleteTextures(1, &tex);
+
+ return 0;
+}
+
+static void
+surface_state_destroy(struct gl_surface_state *gs, struct gl_renderer *gr)
+{
+ int i;
+
+ wl_list_remove(&gs->surface_destroy_listener.link);
+ wl_list_remove(&gs->renderer_destroy_listener.link);
+
+ gs->surface->renderer_state = NULL;
+
+ glDeleteTextures(gs->num_textures, gs->textures);
+
+ for (i = 0; i < gs->num_images; i++)
+ egl_image_unref(gs->images[i]);
+
+ weston_buffer_reference(&gs->buffer_ref, NULL);
+ pixman_region32_fini(&gs->texture_damage);
+ free(gs);
+}
+
+static void
+surface_state_handle_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct gl_surface_state *gs;
+ struct gl_renderer *gr;
+
+ gs = container_of(listener, struct gl_surface_state,
+ surface_destroy_listener);
+
+ gr = get_renderer(gs->surface->compositor);
+
+ surface_state_destroy(gs, gr);
+}
+
+static void
+surface_state_handle_renderer_destroy(struct wl_listener *listener, void *data)
+{
+ struct gl_surface_state *gs;
+ struct gl_renderer *gr;
+
+ gr = data;
+
+ gs = container_of(listener, struct gl_surface_state,
+ renderer_destroy_listener);
+
+ surface_state_destroy(gs, gr);
+}
+
+static int
+gl_renderer_create_surface(struct weston_surface *surface)
+{
+ struct gl_surface_state *gs;
+ struct gl_renderer *gr = get_renderer(surface->compositor);
+
+ gs = zalloc(sizeof *gs);
+ if (gs == NULL)
+ return -1;
+
+ /* A buffer is never attached to solid color surfaces, yet
+ * they still go through texcoord computations. Do not divide
+ * by zero there.
+ */
+ gs->pitch = 1;
+ gs->y_inverted = 1;
+
+ gs->surface = surface;
+
+ pixman_region32_init(&gs->texture_damage);
+ surface->renderer_state = gs;
+
+ gs->surface_destroy_listener.notify =
+ surface_state_handle_surface_destroy;
+ wl_signal_add(&surface->destroy_signal,
+ &gs->surface_destroy_listener);
+
+ gs->renderer_destroy_listener.notify =
+ surface_state_handle_renderer_destroy;
+ wl_signal_add(&gr->destroy_signal,
+ &gs->renderer_destroy_listener);
+
+ if (surface->buffer_ref.buffer) {
+ gl_renderer_attach(surface, surface->buffer_ref.buffer);
+ gl_renderer_flush_damage(surface);
+ }
+
+ return 0;
+}
+
+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";
+
+/* Declare common fragment shader uniforms */
+#define FRAGMENT_CONVERT_YUV \
+ " y *= alpha;\n" \
+ " u *= alpha;\n" \
+ " v *= alpha;\n" \
+ " gl_FragColor.r = y + 1.59602678 * v;\n" \
+ " gl_FragColor.g = y - 0.39176229 * u - 0.81296764 * v;\n" \
+ " gl_FragColor.b = y + 2.01723214 * u;\n" \
+ " gl_FragColor.a = alpha;\n"
+
+static const char fragment_debug[] =
+ " gl_FragColor = vec4(0.0, 0.3, 0.0, 0.2) + gl_FragColor * 0.8;\n";
+
+static const char fragment_brace[] =
+ "}\n";
+
+static const char texture_fragment_shader_rgba[] =
+ "precision mediump float;\n"
+ "varying vec2 v_texcoord;\n"
+ "uniform sampler2D tex;\n"
+ "uniform float alpha;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = alpha * texture2D(tex, v_texcoord)\n;"
+ ;
+
+static const char texture_fragment_shader_rgbx[] =
+ "precision mediump float;\n"
+ "varying vec2 v_texcoord;\n"
+ "uniform sampler2D tex;\n"
+ "uniform float alpha;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor.rgb = alpha * texture2D(tex, v_texcoord).rgb\n;"
+ " gl_FragColor.a = alpha;\n"
+ ;
+
+static const char texture_fragment_shader_egl_external[] =
+ "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "varying vec2 v_texcoord;\n"
+ "uniform samplerExternalOES tex;\n"
+ "uniform float alpha;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = alpha * texture2D(tex, v_texcoord)\n;"
+ ;
+
+static const char texture_fragment_shader_y_uv[] =
+ "precision mediump float;\n"
+ "uniform sampler2D tex;\n"
+ "uniform sampler2D tex1;\n"
+ "varying vec2 v_texcoord;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n"
+ " float u = texture2D(tex1, v_texcoord).r - 0.5;\n"
+ " float v = texture2D(tex1, v_texcoord).g - 0.5;\n"
+ FRAGMENT_CONVERT_YUV
+ ;
+
+static const char texture_fragment_shader_y_u_v[] =
+ "precision mediump float;\n"
+ "uniform sampler2D tex;\n"
+ "uniform sampler2D tex1;\n"
+ "uniform sampler2D tex2;\n"
+ "varying vec2 v_texcoord;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n"
+ " float u = texture2D(tex1, v_texcoord).x - 0.5;\n"
+ " float v = texture2D(tex2, v_texcoord).x - 0.5;\n"
+ FRAGMENT_CONVERT_YUV
+ ;
+
+static const char texture_fragment_shader_y_xuxv[] =
+ "precision mediump float;\n"
+ "uniform sampler2D tex;\n"
+ "uniform sampler2D tex1;\n"
+ "varying vec2 v_texcoord;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n"
+ " float u = texture2D(tex1, v_texcoord).g - 0.5;\n"
+ " float v = texture2D(tex1, v_texcoord).a - 0.5;\n"
+ FRAGMENT_CONVERT_YUV
+ ;
+
+static const char solid_fragment_shader[] =
+ "precision mediump float;\n"
+ "uniform vec4 color;\n"
+ "uniform float alpha;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = alpha * color\n;"
+ ;
+
+static int
+compile_shader(GLenum type, int count, const char **sources)
+{
+ GLuint s;
+ char msg[512];
+ GLint status;
+
+ s = glCreateShader(type);
+ glShaderSource(s, count, sources, NULL);
+ glCompileShader(s);
+ glGetShaderiv(s, GL_COMPILE_STATUS, &status);
+ if (!status) {
+ glGetShaderInfoLog(s, sizeof msg, NULL, msg);
+ weston_log("shader info: %s\n", msg);
+ return GL_NONE;
+ }
+
+ return s;
+}
+
+static int
+shader_init(struct gl_shader *shader, struct gl_renderer *renderer,
+ const char *vertex_source, const char *fragment_source)
+{
+ char msg[512];
+ GLint status;
+ int count;
+ const char *sources[3];
+
+ shader->vertex_shader =
+ compile_shader(GL_VERTEX_SHADER, 1, &vertex_source);
+
+ if (renderer->fragment_shader_debug) {
+ sources[0] = fragment_source;
+ sources[1] = fragment_debug;
+ sources[2] = fragment_brace;
+ count = 3;
+ } else {
+ sources[0] = fragment_source;
+ sources[1] = fragment_brace;
+ count = 2;
+ }
+
+ shader->fragment_shader =
+ compile_shader(GL_FRAGMENT_SHADER, count, sources);
+
+ 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);
+ weston_log("link info: %s\n", msg);
+ return -1;
+ }
+
+ shader->proj_uniform = glGetUniformLocation(shader->program, "proj");
+ shader->tex_uniforms[0] = glGetUniformLocation(shader->program, "tex");
+ shader->tex_uniforms[1] = glGetUniformLocation(shader->program, "tex1");
+ shader->tex_uniforms[2] = glGetUniformLocation(shader->program, "tex2");
+ shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha");
+ shader->color_uniform = glGetUniformLocation(shader->program, "color");
+
+ return 0;
+}
+
+static void
+shader_release(struct gl_shader *shader)
+{
+ glDeleteShader(shader->vertex_shader);
+ glDeleteShader(shader->fragment_shader);
+ glDeleteProgram(shader->program);
+
+ shader->vertex_shader = 0;
+ shader->fragment_shader = 0;
+ shader->program = 0;
+}
+
+static void
+log_extensions(const char *name, const char *extensions)
+{
+ const char *p, *end;
+ int l;
+ int len;
+
+ l = weston_log("%s:", name);
+ p = extensions;
+ while (*p) {
+ end = strchrnul(p, ' ');
+ len = end - p;
+ if (l + len > 78)
+ l = weston_log_continue("\n" STAMP_SPACE "%.*s",
+ len, p);
+ else
+ l += weston_log_continue(" %.*s", len, p);
+ for (p = end; isspace(*p); p++)
+ ;
+ }
+ weston_log_continue("\n");
+}
+
+static void
+log_egl_gl_info(EGLDisplay egldpy)
+{
+ const char *str;
+
+ str = eglQueryString(egldpy, EGL_VERSION);
+ weston_log("EGL version: %s\n", str ? str : "(null)");
+
+ str = eglQueryString(egldpy, EGL_VENDOR);
+ weston_log("EGL vendor: %s\n", str ? str : "(null)");
+
+ str = eglQueryString(egldpy, EGL_CLIENT_APIS);
+ weston_log("EGL client APIs: %s\n", str ? str : "(null)");
+
+ str = eglQueryString(egldpy, EGL_EXTENSIONS);
+ log_extensions("EGL extensions", str ? str : "(null)");
+
+ str = (char *)glGetString(GL_VERSION);
+ weston_log("GL version: %s\n", str ? str : "(null)");
+
+ str = (char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
+ weston_log("GLSL version: %s\n", str ? str : "(null)");
+
+ str = (char *)glGetString(GL_VENDOR);
+ weston_log("GL vendor: %s\n", str ? str : "(null)");
+
+ str = (char *)glGetString(GL_RENDERER);
+ weston_log("GL renderer: %s\n", str ? str : "(null)");
+
+ str = (char *)glGetString(GL_EXTENSIONS);
+ log_extensions("GL extensions", str ? str : "(null)");
+}
+
+static void
+log_egl_config_info(EGLDisplay egldpy, EGLConfig eglconfig)
+{
+ EGLint r, g, b, a;
+
+ weston_log("Chosen EGL config details:\n");
+
+ weston_log_continue(STAMP_SPACE "RGBA bits");
+ if (eglGetConfigAttrib(egldpy, eglconfig, EGL_RED_SIZE, &r) &&
+ eglGetConfigAttrib(egldpy, eglconfig, EGL_GREEN_SIZE, &g) &&
+ eglGetConfigAttrib(egldpy, eglconfig, EGL_BLUE_SIZE, &b) &&
+ eglGetConfigAttrib(egldpy, eglconfig, EGL_ALPHA_SIZE, &a))
+ weston_log_continue(": %d %d %d %d\n", r, g, b, a);
+ else
+ weston_log_continue(" unknown\n");
+
+ weston_log_continue(STAMP_SPACE "swap interval range");
+ if (eglGetConfigAttrib(egldpy, eglconfig, EGL_MIN_SWAP_INTERVAL, &a) &&
+ eglGetConfigAttrib(egldpy, eglconfig, EGL_MAX_SWAP_INTERVAL, &b))
+ weston_log_continue(": %d - %d\n", a, b);
+ else
+ weston_log_continue(" unknown\n");
+}
+
+static int
+match_config_to_visual(EGLDisplay egl_display,
+ EGLint visual_id,
+ EGLConfig *configs,
+ int count)
+{
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ EGLint id;
+
+ if (!eglGetConfigAttrib(egl_display,
+ configs[i], EGL_NATIVE_VISUAL_ID,
+ &id))
+ continue;
+
+ if (id == visual_id)
+ return i;
+ }
+
+ return -1;
+}
+
+static int
+egl_choose_config(struct gl_renderer *gr, const EGLint *attribs,
+ const EGLint *visual_id, const int n_ids,
+ EGLConfig *config_out)
+{
+ EGLint count = 0;
+ EGLint matched = 0;
+ EGLConfig *configs;
+ int i, config_index = -1;
+
+ if (!eglGetConfigs(gr->egl_display, NULL, 0, &count) || count < 1) {
+ weston_log("No EGL configs to choose from.\n");
+ return -1;
+ }
+ configs = calloc(count, sizeof *configs);
+ if (!configs)
+ return -1;
+
+ if (!eglChooseConfig(gr->egl_display, attribs, configs,
+ count, &matched) || !matched) {
+ weston_log("No EGL configs with appropriate attributes.\n");
+ goto out;
+ }
+
+ if (!visual_id)
+ config_index = 0;
+
+ for (i = 0; config_index == -1 && i < n_ids; i++)
+ config_index = match_config_to_visual(gr->egl_display,
+ visual_id[i],
+ configs,
+ matched);
+
+ if (config_index != -1)
+ *config_out = configs[config_index];
+
+out:
+ free(configs);
+ if (config_index == -1)
+ return -1;
+
+ if (i > 1)
+ weston_log("Unable to use first choice EGL config with id"
+ " 0x%x, succeeded with alternate id 0x%x.\n",
+ visual_id[0], visual_id[i - 1]);
+ return 0;
+}
+
+static void
+gl_renderer_output_set_border(struct weston_output *output,
+ enum gl_renderer_border_side side,
+ int32_t width, int32_t height,
+ int32_t tex_width, unsigned char *data)
+{
+ struct gl_output_state *go = get_output_state(output);
+
+ if (go->borders[side].width != width ||
+ go->borders[side].height != height)
+ /* In this case, we have to blow everything and do a full
+ * repaint. */
+ go->border_status |= BORDER_SIZE_CHANGED | BORDER_ALL_DIRTY;
+
+ if (data == NULL) {
+ width = 0;
+ height = 0;
+ }
+
+ go->borders[side].width = width;
+ go->borders[side].height = height;
+ go->borders[side].tex_width = tex_width;
+ go->borders[side].data = data;
+ go->border_status |= 1 << side;
+}
+
+static int
+gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface);
+
+static int
+gl_renderer_output_create(struct weston_output *output,
+ EGLNativeWindowType window_for_legacy,
+ void *window_for_platform,
+ const EGLint *attribs,
+ const EGLint *visual_id,
+ int n_ids)
+{
+ struct weston_compositor *ec = output->compositor;
+ struct gl_renderer *gr = get_renderer(ec);
+ struct gl_output_state *go;
+ EGLConfig egl_config;
+ int i;
+
+ if (egl_choose_config(gr, attribs, visual_id,
+ n_ids, &egl_config) == -1) {
+ weston_log("failed to choose EGL config for output\n");
+ return -1;
+ }
+
+ if (egl_config != gr->egl_config &&
+ !gr->has_configless_context) {
+ weston_log("attempted to use a different EGL config for an "
+ "output but EGL_MESA_configless_context is not "
+ "supported\n");
+ return -1;
+ }
+
+ go = zalloc(sizeof *go);
+ if (go == NULL)
+ return -1;
+
+ if (gr->create_platform_window) {
+ go->egl_surface =
+ gr->create_platform_window(gr->egl_display,
+ egl_config,
+ window_for_platform,
+ NULL);
+ } else {
+ go->egl_surface =
+ eglCreateWindowSurface(gr->egl_display,
+ egl_config,
+ window_for_legacy, NULL);
+ }
+
+ if (go->egl_surface == EGL_NO_SURFACE) {
+ weston_log("failed to create egl surface\n");
+ free(go);
+ return -1;
+ }
+
+ if (gr->egl_context == NULL)
+ if (gl_renderer_setup(ec, go->egl_surface) < 0) {
+ free(go);
+ return -1;
+ }
+
+ for (i = 0; i < BUFFER_DAMAGE_COUNT; i++)
+ pixman_region32_init(&go->buffer_damage[i]);
+
+ output->renderer_state = go;
+
+ log_egl_config_info(gr->egl_display, egl_config);
+
+ return 0;
+}
+
+static void
+gl_renderer_output_destroy(struct weston_output *output)
+{
+ struct gl_renderer *gr = get_renderer(output->compositor);
+ struct gl_output_state *go = get_output_state(output);
+ int i;
+
+ for (i = 0; i < 2; i++)
+ pixman_region32_fini(&go->buffer_damage[i]);
+
+ eglDestroySurface(gr->egl_display, go->egl_surface);
+
+ free(go);
+}
+
+static EGLSurface
+gl_renderer_output_surface(struct weston_output *output)
+{
+ return get_output_state(output)->egl_surface;
+}
+
+static void
+gl_renderer_destroy(struct weston_compositor *ec)
+{
+ struct gl_renderer *gr = get_renderer(ec);
+ struct dmabuf_image *image, *next;
+
+ wl_signal_emit(&gr->destroy_signal, gr);
+
+ if (gr->has_bind_display)
+ gr->unbind_display(gr->egl_display, ec->wl_display);
+
+ /* Work around crash in egl_dri2.c's dri2_make_current() - when does this apply? */
+ eglMakeCurrent(gr->egl_display,
+ EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+
+
+ wl_list_for_each_safe(image, next, &gr->dmabuf_images, link)
+ dmabuf_image_destroy(image);
+
+ eglTerminate(gr->egl_display);
+ eglReleaseThread();
+
+ wl_array_release(&gr->vertices);
+ wl_array_release(&gr->vtxcnt);
+
+ if (gr->fragment_binding)
+ weston_binding_destroy(gr->fragment_binding);
+ if (gr->fan_binding)
+ weston_binding_destroy(gr->fan_binding);
+
+ free(gr);
+}
+
+static bool
+check_extension(const char *extensions, const char *extension)
+{
+ size_t extlen = strlen(extension);
+ const char *end = extensions + strlen(extensions);
+
+ while (extensions < end) {
+ size_t n = 0;
+
+ /* Skip whitespaces, if any */
+ if (*extensions == ' ') {
+ extensions++;
+ continue;
+ }
+
+ n = strcspn(extensions, " ");
+
+ /* Compare strings */
+ if (n == extlen && strncmp(extension, extensions, n) == 0)
+ return true; /* Found */
+
+ extensions += n;
+ }
+
+ /* Not found */
+ return false;
+}
+
+static void
+renderer_setup_egl_client_extensions(struct gl_renderer *gr)
+{
+ const char *extensions;
+
+ extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if (!extensions) {
+ weston_log("Retrieving EGL client extension string failed.\n");
+ return;
+ }
+
+ if (check_extension(extensions, "EGL_EXT_platform_base"))
+ gr->create_platform_window =
+ (void *) eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
+ else
+ weston_log("warning: EGL_EXT_platform_base not supported.\n");
+}
+
+static int
+gl_renderer_setup_egl_extensions(struct weston_compositor *ec)
+{
+ struct gl_renderer *gr = get_renderer(ec);
+ const char *extensions;
+ EGLBoolean ret;
+
+ gr->create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
+ gr->destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
+ gr->bind_display =
+ (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
+ gr->unbind_display =
+ (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
+ gr->query_buffer =
+ (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
+
+ extensions =
+ (const char *) eglQueryString(gr->egl_display, EGL_EXTENSIONS);
+ if (!extensions) {
+ weston_log("Retrieving EGL extension string failed.\n");
+ return -1;
+ }
+
+ if (check_extension(extensions, "EGL_WL_bind_wayland_display"))
+ gr->has_bind_display = 1;
+ if (gr->has_bind_display) {
+ ret = gr->bind_display(gr->egl_display, ec->wl_display);
+ if (!ret)
+ gr->has_bind_display = 0;
+ }
+
+ if (check_extension(extensions, "EGL_EXT_buffer_age"))
+ gr->has_egl_buffer_age = 1;
+ else
+ weston_log("warning: EGL_EXT_buffer_age not supported. "
+ "Performance could be affected.\n");
+
+#ifdef EGL_EXT_swap_buffers_with_damage
+ if (check_extension(extensions, "EGL_EXT_swap_buffers_with_damage"))
+ gr->swap_buffers_with_damage =
+ (void *) eglGetProcAddress("eglSwapBuffersWithDamageEXT");
+ else
+ weston_log("warning: EGL_EXT_swap_buffers_with_damage not "
+ "supported. Performance could be affected.\n");
+#endif
+
+#ifdef EGL_MESA_configless_context
+ if (check_extension(extensions, "EGL_MESA_configless_context"))
+ gr->has_configless_context = 1;
+#endif
+
+#ifdef EGL_EXT_image_dma_buf_import
+ if (check_extension(extensions, "EGL_EXT_image_dma_buf_import"))
+ gr->has_dmabuf_import = 1;
+#endif
+
+ renderer_setup_egl_client_extensions(gr);
+
+ return 0;
+}
+
+static const EGLint gl_renderer_opaque_attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_ALPHA_SIZE, 0,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+};
+
+static const EGLint gl_renderer_alpha_attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_ALPHA_SIZE, 1,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+};
+
+/** Checks whether a platform EGL client extension is supported
+ *
+ * \param ec The weston compositor
+ * \param extension_suffix The EGL client extension suffix
+ * \return 1 if supported, 0 if using fallbacks, -1 unsupported
+ *
+ * This function checks whether a specific platform_* extension is supported
+ * by EGL.
+ *
+ * The extension suffix should be the suffix of the platform extension (that
+ * specifies a <platform> argument as defined in EGL_EXT_platform_base). For
+ * example, passing "foo" will check whether either "EGL_KHR_platform_foo",
+ * "EGL_EXT_platform_foo", or "EGL_MESA_platform_foo" is supported.
+ *
+ * The return value is 1:
+ * - if the supplied EGL client extension is supported.
+ * The return value is 0:
+ * - if the platform_base client extension isn't supported so will
+ * fallback to eglGetDisplay and friends.
+ * The return value is -1:
+ * - if the supplied EGL client extension is not supported.
+ */
+static int
+gl_renderer_supports(struct weston_compositor *ec,
+ const char *extension_suffix)
+{
+ static const char *extensions = NULL;
+ char s[64];
+
+ if (!extensions) {
+ extensions = (const char *) eglQueryString(
+ EGL_NO_DISPLAY, EGL_EXTENSIONS);
+
+ if (!extensions)
+ return 0;
+
+ log_extensions("EGL client extensions",
+ extensions);
+ }
+
+ if (!check_extension(extensions, "EGL_EXT_platform_base"))
+ return 0;
+
+ snprintf(s, sizeof s, "EGL_KHR_platform_%s", extension_suffix);
+ if (check_extension(extensions, s))
+ return 1;
+
+ snprintf(s, sizeof s, "EGL_EXT_platform_%s", extension_suffix);
+ if (check_extension(extensions, s))
+ return 1;
+
+ snprintf(s, sizeof s, "EGL_MESA_platform_%s", extension_suffix);
+ if (check_extension(extensions, s))
+ return 1;
+
+ /* at this point we definitely have some platform extensions but
+ * haven't found the supplied platform, so chances are it's
+ * not supported. */
+
+ return -1;
+}
+
+static const char *
+platform_to_extension(EGLenum platform)
+{
+ switch (platform) {
+ case EGL_PLATFORM_GBM_KHR:
+ return "gbm";
+ case EGL_PLATFORM_WAYLAND_KHR:
+ return "wayland";
+ case EGL_PLATFORM_X11_KHR:
+ return "x11";
+ default:
+ assert(0 && "bad EGL platform enum");
+ }
+}
+
+static int
+gl_renderer_create(struct weston_compositor *ec, EGLenum platform,
+ void *native_window, const EGLint *attribs,
+ const EGLint *visual_id, int n_ids)
+{
+ struct gl_renderer *gr;
+ EGLint major, minor;
+ int supports = 0;
+
+ if (platform) {
+ supports = gl_renderer_supports(
+ ec, platform_to_extension(platform));
+ if (supports < 0)
+ return -1;
+ }
+
+ gr = zalloc(sizeof *gr);
+ if (gr == NULL)
+ return -1;
+
+ gr->base.read_pixels = gl_renderer_read_pixels;
+ gr->base.repaint_output = gl_renderer_repaint_output;
+ gr->base.flush_damage = gl_renderer_flush_damage;
+ gr->base.attach = gl_renderer_attach;
+ gr->base.surface_set_color = gl_renderer_surface_set_color;
+ gr->base.destroy = gl_renderer_destroy;
+ gr->base.surface_get_content_size =
+ gl_renderer_surface_get_content_size;
+ gr->base.surface_copy_content = gl_renderer_surface_copy_content;
+ gr->egl_display = NULL;
+
+ /* extension_suffix is supported */
+ if (supports) {
+ if (!get_platform_display) {
+ get_platform_display = (void *) eglGetProcAddress(
+ "eglGetPlatformDisplayEXT");
+ }
+
+ /* also wrap this in the supports check because
+ * eglGetProcAddress can return non-NULL and still not
+ * support the feature at runtime, so ensure the
+ * appropriate extension checks have been done. */
+ if (get_platform_display && platform) {
+ gr->egl_display = get_platform_display(platform,
+ native_window,
+ NULL);
+ }
+ }
+
+ if (!gr->egl_display) {
+ weston_log("warning: either no EGL_EXT_platform_base "
+ "support or specific platform support; "
+ "falling back to eglGetDisplay.\n");
+ gr->egl_display = eglGetDisplay(native_window);
+ }
+
+ if (gr->egl_display == EGL_NO_DISPLAY) {
+ weston_log("failed to create display\n");
+ goto fail;
+ }
+
+ if (!eglInitialize(gr->egl_display, &major, &minor)) {
+ weston_log("failed to initialize display\n");
+ goto fail_with_error;
+ }
+
+ if (egl_choose_config(gr, attribs, visual_id,
+ n_ids, &gr->egl_config) < 0) {
+ weston_log("failed to choose EGL config\n");
+ goto fail_terminate;
+ }
+
+ ec->renderer = &gr->base;
+ ec->capabilities |= WESTON_CAP_ROTATION_ANY;
+ ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP;
+ ec->capabilities |= WESTON_CAP_VIEW_CLIP_MASK;
+
+ if (gl_renderer_setup_egl_extensions(ec) < 0)
+ goto fail_with_error;
+
+ wl_list_init(&gr->dmabuf_images);
+ if (gr->has_dmabuf_import)
+ gr->base.import_dmabuf = gl_renderer_import_dmabuf;
+
+ wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565);
+
+ wl_signal_init(&gr->destroy_signal);
+
+ return 0;
+
+fail_with_error:
+ gl_renderer_print_egl_error_state();
+fail_terminate:
+ eglTerminate(gr->egl_display);
+fail:
+ free(gr);
+ return -1;
+}
+
+static EGLDisplay
+gl_renderer_display(struct weston_compositor *ec)
+{
+ return get_renderer(ec)->egl_display;
+}
+
+static int
+compile_shaders(struct weston_compositor *ec)
+{
+ struct gl_renderer *gr = get_renderer(ec);
+
+ gr->texture_shader_rgba.vertex_source = vertex_shader;
+ gr->texture_shader_rgba.fragment_source = texture_fragment_shader_rgba;
+
+ gr->texture_shader_rgbx.vertex_source = vertex_shader;
+ gr->texture_shader_rgbx.fragment_source = texture_fragment_shader_rgbx;
+
+ gr->texture_shader_egl_external.vertex_source = vertex_shader;
+ gr->texture_shader_egl_external.fragment_source =
+ texture_fragment_shader_egl_external;
+
+ gr->texture_shader_y_uv.vertex_source = vertex_shader;
+ gr->texture_shader_y_uv.fragment_source = texture_fragment_shader_y_uv;
+
+ gr->texture_shader_y_u_v.vertex_source = vertex_shader;
+ gr->texture_shader_y_u_v.fragment_source =
+ texture_fragment_shader_y_u_v;
+
+ gr->texture_shader_y_xuxv.vertex_source = vertex_shader;
+ gr->texture_shader_y_xuxv.fragment_source =
+ texture_fragment_shader_y_xuxv;
+
+ gr->solid_shader.vertex_source = vertex_shader;
+ gr->solid_shader.fragment_source = solid_fragment_shader;
+
+ return 0;
+}
+
+static void
+fragment_debug_binding(struct weston_keyboard *keyboard, uint32_t time,
+ uint32_t key, void *data)
+{
+ struct weston_compositor *ec = data;
+ struct gl_renderer *gr = get_renderer(ec);
+ struct weston_output *output;
+
+ gr->fragment_shader_debug ^= 1;
+
+ shader_release(&gr->texture_shader_rgba);
+ shader_release(&gr->texture_shader_rgbx);
+ shader_release(&gr->texture_shader_egl_external);
+ shader_release(&gr->texture_shader_y_uv);
+ shader_release(&gr->texture_shader_y_u_v);
+ shader_release(&gr->texture_shader_y_xuxv);
+ shader_release(&gr->solid_shader);
+
+ /* Force use_shader() to call glUseProgram(), since we need to use
+ * the recompiled version of the shader. */
+ gr->current_shader = NULL;
+
+ wl_list_for_each(output, &ec->output_list, link)
+ weston_output_damage(output);
+}
+
+static void
+fan_debug_repaint_binding(struct weston_keyboard *keyboard, uint32_t time,
+ uint32_t key, void *data)
+{
+ struct weston_compositor *compositor = data;
+ struct gl_renderer *gr = get_renderer(compositor);
+
+ gr->fan_debug = !gr->fan_debug;
+ weston_compositor_damage_all(compositor);
+}
+
+static int
+gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface)
+{
+ struct gl_renderer *gr = get_renderer(ec);
+ const char *extensions;
+ EGLConfig context_config;
+ EGLBoolean ret;
+
+ static const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+ weston_log("failed to bind EGL_OPENGL_ES_API\n");
+ gl_renderer_print_egl_error_state();
+ return -1;
+ }
+
+ context_config = gr->egl_config;
+
+#ifdef EGL_MESA_configless_context
+ if (gr->has_configless_context)
+ context_config = EGL_NO_CONFIG_MESA;
+#endif
+
+ gr->egl_context = eglCreateContext(gr->egl_display, context_config,
+ EGL_NO_CONTEXT, context_attribs);
+ if (gr->egl_context == NULL) {
+ weston_log("failed to create context\n");
+ gl_renderer_print_egl_error_state();
+ return -1;
+ }
+
+ ret = eglMakeCurrent(gr->egl_display, egl_surface,
+ egl_surface, gr->egl_context);
+ if (ret == EGL_FALSE) {
+ weston_log("Failed to make EGL context current.\n");
+ gl_renderer_print_egl_error_state();
+ return -1;
+ }
+
+ log_egl_gl_info(gr->egl_display);
+
+ gr->image_target_texture_2d =
+ (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
+
+ extensions = (const char *) glGetString(GL_EXTENSIONS);
+ if (!extensions) {
+ weston_log("Retrieving GL extension string failed.\n");
+ return -1;
+ }
+
+ if (!check_extension(extensions, "GL_EXT_texture_format_BGRA8888")) {
+ weston_log("GL_EXT_texture_format_BGRA8888 not available\n");
+ return -1;
+ }
+
+ if (check_extension(extensions, "GL_EXT_read_format_bgra"))
+ ec->read_format = PIXMAN_a8r8g8b8;
+ else
+ ec->read_format = PIXMAN_a8b8g8r8;
+
+#ifdef GL_EXT_unpack_subimage
+ if (check_extension(extensions, "GL_EXT_unpack_subimage"))
+ gr->has_unpack_subimage = 1;
+#endif
+
+ if (check_extension(extensions, "GL_OES_EGL_image_external"))
+ gr->has_egl_image_external = 1;
+
+ glActiveTexture(GL_TEXTURE0);
+
+ if (compile_shaders(ec))
+ return -1;
+
+ gr->fragment_binding =
+ weston_compositor_add_debug_binding(ec, KEY_S,
+ fragment_debug_binding,
+ ec);
+ gr->fan_binding =
+ weston_compositor_add_debug_binding(ec, KEY_F,
+ fan_debug_repaint_binding,
+ ec);
+
+ weston_log("GL ES 2 renderer features:\n");
+ weston_log_continue(STAMP_SPACE "read-back format: %s\n",
+ ec->read_format == PIXMAN_a8r8g8b8 ? "BGRA" : "RGBA");
+ weston_log_continue(STAMP_SPACE "wl_shm sub-image to texture: %s\n",
+ gr->has_unpack_subimage ? "yes" : "no");
+ weston_log_continue(STAMP_SPACE "EGL Wayland extension: %s\n",
+ gr->has_bind_display ? "yes" : "no");
+
+
+ return 0;
+}
+
+WL_EXPORT struct gl_renderer_interface gl_renderer_interface = {
+ .opaque_attribs = gl_renderer_opaque_attribs,
+ .alpha_attribs = gl_renderer_alpha_attribs,
+
+ .create = gl_renderer_create,
+ .display = gl_renderer_display,
+ .output_create = gl_renderer_output_create,
+ .output_destroy = gl_renderer_output_destroy,
+ .output_surface = gl_renderer_output_surface,
+ .output_set_border = gl_renderer_output_set_border,
+ .print_egl_error_state = gl_renderer_print_egl_error_state
+};
--- /dev/null
+/*
+ * Copyright © 2012 John Kåre Alsaker
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include "compositor.h"
+
+#ifdef ENABLE_EGL
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#else
+
+typedef int EGLint;
+typedef int EGLenum;
+typedef void *EGLDisplay;
+typedef void *EGLSurface;
+typedef void *EGLConfig;
+typedef intptr_t EGLNativeDisplayType;
+typedef intptr_t EGLNativeWindowType;
+#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0)
+
+#endif /* ENABLE_EGL */
+
+#ifndef EGL_EXT_platform_base
+typedef EGLDisplay (*PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
+typedef EGLSurface (*PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC) (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLint *attrib_list);
+#endif
+
+#ifndef EGL_PLATFORM_GBM_KHR
+#define EGL_PLATFORM_GBM_KHR 0x31D7
+#endif
+
+#ifndef EGL_PLATFORM_WAYLAND_KHR
+#define EGL_PLATFORM_WAYLAND_KHR 0x31D8
+#endif
+
+#ifndef EGL_PLATFORM_X11_KHR
+#define EGL_PLATFORM_X11_KHR 0x31D5
+#endif
+
+#define NO_EGL_PLATFORM 0
+
+enum gl_renderer_border_side {
+ GL_RENDERER_BORDER_TOP = 0,
+ GL_RENDERER_BORDER_LEFT = 1,
+ GL_RENDERER_BORDER_RIGHT = 2,
+ GL_RENDERER_BORDER_BOTTOM = 3,
+};
+
+struct gl_renderer_interface {
+ const EGLint *opaque_attribs;
+ const EGLint *alpha_attribs;
+
+ int (*create)(struct weston_compositor *ec,
+ EGLenum platform,
+ void *native_window,
+ const EGLint *attribs,
+ const EGLint *visual_id,
+ const int n_ids);
+
+ EGLDisplay (*display)(struct weston_compositor *ec);
+
+ int (*output_create)(struct weston_output *output,
+ EGLNativeWindowType window_for_legacy,
+ void *window_for_platform,
+ const EGLint *attribs,
+ const EGLint *visual_id,
+ const int n_ids);
+
+ void (*output_destroy)(struct weston_output *output);
+
+ EGLSurface (*output_surface)(struct weston_output *output);
+
+ /* Sets the output border.
+ *
+ * The side specifies the side for which we are setting the border.
+ * The width and height are the width and height of the border.
+ * The tex_width patemeter specifies the width of the actual
+ * texture; this may be larger than width if the data is not
+ * tightly packed.
+ *
+ * The top and bottom textures will extend over the sides to the
+ * full width of the bordered window. The right and left edges,
+ * however, will extend only to the top and bottom of the
+ * compositor surface. This is demonstrated by the picture below:
+ *
+ * +-----------------------+
+ * | TOP |
+ * +-+-------------------+-+
+ * | | | |
+ * |L| |R|
+ * |E| |I|
+ * |F| |G|
+ * |T| |H|
+ * | | |T|
+ * | | | |
+ * +-+-------------------+-+
+ * | BOTTOM |
+ * +-----------------------+
+ */
+ void (*output_set_border)(struct weston_output *output,
+ enum gl_renderer_border_side side,
+ int32_t width, int32_t height,
+ int32_t tex_width, unsigned char *data);
+
+ void (*print_egl_error_state)(void);
+};
+
--- /dev/null
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include "shared/helpers.h"
+#include "shared/os-compatibility.h"
+#include "compositor.h"
+
+static void
+empty_region(pixman_region32_t *region)
+{
+ pixman_region32_fini(region);
+ pixman_region32_init(region);
+}
+
+static struct weston_pointer_client *
+weston_pointer_client_create(struct wl_client *client)
+{
+ struct weston_pointer_client *pointer_client;
+
+ pointer_client = zalloc(sizeof *pointer_client);
+ if (!pointer_client)
+ return NULL;
+
+ pointer_client->client = client;
+ wl_list_init(&pointer_client->pointer_resources);
+
+ return pointer_client;
+}
+
+static void
+weston_pointer_client_destroy(struct weston_pointer_client *pointer_client)
+{
+ free(pointer_client);
+}
+
+static bool
+weston_pointer_client_is_empty(struct weston_pointer_client *pointer_client)
+{
+ return wl_list_empty(&pointer_client->pointer_resources);
+}
+
+static struct weston_pointer_client *
+weston_pointer_get_pointer_client(struct weston_pointer *pointer,
+ struct wl_client *client)
+{
+ struct weston_pointer_client *pointer_client;
+
+ wl_list_for_each(pointer_client, &pointer->pointer_clients, link) {
+ if (pointer_client->client == client)
+ return pointer_client;
+ }
+
+ return NULL;
+}
+
+static struct weston_pointer_client *
+weston_pointer_ensure_pointer_client(struct weston_pointer *pointer,
+ struct wl_client *client)
+{
+ struct weston_pointer_client *pointer_client;
+
+ pointer_client = weston_pointer_get_pointer_client(pointer, client);
+ if (pointer_client)
+ return pointer_client;
+
+ pointer_client = weston_pointer_client_create(client);
+ wl_list_insert(&pointer->pointer_clients, &pointer_client->link);
+
+ if (pointer->focus &&
+ pointer->focus->surface->resource &&
+ wl_resource_get_client(pointer->focus->surface->resource) == client) {
+ pointer->focus_client = pointer_client;
+ }
+
+ return pointer_client;
+}
+
+static void
+weston_pointer_cleanup_pointer_client(struct weston_pointer *pointer,
+ struct weston_pointer_client *pointer_client)
+{
+ if (weston_pointer_client_is_empty(pointer_client)) {
+ if (pointer->focus_client == pointer_client)
+ pointer->focus_client = NULL;
+ wl_list_remove(&pointer_client->link);
+ weston_pointer_client_destroy(pointer_client);
+ }
+}
+
+static void
+unbind_pointer_client_resource(struct wl_resource *resource)
+{
+ struct weston_pointer *pointer = wl_resource_get_user_data(resource);
+ struct wl_client *client = wl_resource_get_client(resource);
+ struct weston_pointer_client *pointer_client;
+
+ pointer_client = weston_pointer_get_pointer_client(pointer, client);
+ assert(pointer_client);
+
+ wl_list_remove(wl_resource_get_link(resource));
+ weston_pointer_cleanup_pointer_client(pointer, pointer_client);
+}
+
+static void unbind_resource(struct wl_resource *resource)
+{
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+WL_EXPORT void
+weston_seat_repick(struct weston_seat *seat)
+{
+ const struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+
+ if (!pointer)
+ return;
+
+ pointer->grab->interface->focus(pointer->grab);
+}
+
+static void
+weston_compositor_idle_inhibit(struct weston_compositor *compositor)
+{
+ weston_compositor_wake(compositor);
+ compositor->idle_inhibit++;
+}
+
+static void
+weston_compositor_idle_release(struct weston_compositor *compositor)
+{
+ compositor->idle_inhibit--;
+ weston_compositor_wake(compositor);
+}
+
+static void
+pointer_focus_view_destroyed(struct wl_listener *listener, void *data)
+{
+ struct weston_pointer *pointer =
+ container_of(listener, struct weston_pointer,
+ focus_view_listener);
+
+ weston_pointer_clear_focus(pointer);
+}
+
+static void
+pointer_focus_resource_destroyed(struct wl_listener *listener, void *data)
+{
+ struct weston_pointer *pointer =
+ container_of(listener, struct weston_pointer,
+ focus_resource_listener);
+
+ weston_pointer_clear_focus(pointer);
+}
+
+static void
+keyboard_focus_resource_destroyed(struct wl_listener *listener, void *data)
+{
+ struct weston_keyboard *keyboard =
+ container_of(listener, struct weston_keyboard,
+ focus_resource_listener);
+
+ weston_keyboard_set_focus(keyboard, NULL);
+}
+
+static void
+touch_focus_view_destroyed(struct wl_listener *listener, void *data)
+{
+ struct weston_touch *touch =
+ container_of(listener, struct weston_touch,
+ focus_view_listener);
+
+ weston_touch_set_focus(touch, NULL);
+}
+
+static void
+touch_focus_resource_destroyed(struct wl_listener *listener, void *data)
+{
+ struct weston_touch *touch =
+ container_of(listener, struct weston_touch,
+ focus_resource_listener);
+
+ weston_touch_set_focus(touch, NULL);
+}
+
+static void
+move_resources(struct wl_list *destination, struct wl_list *source)
+{
+ wl_list_insert_list(destination, source);
+ wl_list_init(source);
+}
+
+static void
+move_resources_for_client(struct wl_list *destination,
+ struct wl_list *source,
+ struct wl_client *client)
+{
+ struct wl_resource *resource, *tmp;
+ wl_resource_for_each_safe(resource, tmp, source) {
+ if (wl_resource_get_client(resource) == client) {
+ wl_list_remove(wl_resource_get_link(resource));
+ wl_list_insert(destination,
+ wl_resource_get_link(resource));
+ }
+ }
+}
+
+static void
+default_grab_pointer_focus(struct weston_pointer_grab *grab)
+{
+ struct weston_pointer *pointer = grab->pointer;
+ struct weston_view *view;
+ wl_fixed_t sx, sy;
+
+ if (pointer->button_count > 0)
+ return;
+
+ view = weston_compositor_pick_view(pointer->seat->compositor,
+ pointer->x, pointer->y,
+ &sx, &sy);
+
+ if (pointer->focus != view || pointer->sx != sx || pointer->sy != sy)
+ weston_pointer_set_focus(pointer, view, sx, sy);
+}
+
+static void
+default_grab_pointer_motion(struct weston_pointer_grab *grab, uint32_t time,
+ struct weston_pointer_motion_event *event)
+{
+ struct weston_pointer *pointer = grab->pointer;
+ struct wl_list *resource_list;
+ struct wl_resource *resource;
+ wl_fixed_t x, y;
+ wl_fixed_t old_sx = pointer->sx;
+ wl_fixed_t old_sy = pointer->sy;
+
+ if (pointer->focus) {
+ weston_pointer_motion_to_abs(pointer, event, &x, &y);
+ weston_view_from_global_fixed(pointer->focus, x, y,
+ &pointer->sx, &pointer->sy);
+ }
+
+ weston_pointer_move(pointer, event);
+
+ if (pointer->focus_client &&
+ (old_sx != pointer->sx || old_sy != pointer->sy)) {
+ resource_list = &pointer->focus_client->pointer_resources;
+ wl_resource_for_each(resource, resource_list) {
+ wl_pointer_send_motion(resource, time,
+ pointer->sx, pointer->sy);
+ }
+ }
+}
+
+static void
+default_grab_pointer_button(struct weston_pointer_grab *grab,
+ uint32_t time, uint32_t button, uint32_t state_w)
+{
+ struct weston_pointer *pointer = grab->pointer;
+ struct weston_compositor *compositor = pointer->seat->compositor;
+ struct weston_view *view;
+ struct wl_resource *resource;
+ uint32_t serial;
+ enum wl_pointer_button_state state = state_w;
+ struct wl_display *display = compositor->wl_display;
+ wl_fixed_t sx, sy;
+ struct wl_list *resource_list = NULL;
+
+ if (pointer->focus_client)
+ resource_list = &pointer->focus_client->pointer_resources;
+ if (resource_list && !wl_list_empty(resource_list)) {
+ resource_list = &pointer->focus_client->pointer_resources;
+ serial = wl_display_next_serial(display);
+ wl_resource_for_each(resource, resource_list)
+ wl_pointer_send_button(resource,
+ serial,
+ time,
+ button,
+ state_w);
+ }
+
+ if (pointer->button_count == 0 &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED) {
+ view = weston_compositor_pick_view(compositor,
+ pointer->x, pointer->y,
+ &sx, &sy);
+
+ weston_pointer_set_focus(pointer, view, sx, sy);
+ }
+}
+
+/** Send wl_pointer.axis events to focused resources.
+ *
+ * \param pointer The pointer where the axis events originates from.
+ * \param time The timestamp of the event
+ * \param axis The axis enum value of the event
+ * \param value The axis value of the event
+ *
+ * For every resource that is currently in focus, send a wl_pointer.axis event
+ * with the passed parameters. The focused resources are the wl_pointer
+ * resources of the client which currently has the surface with pointer focus.
+ */
+WL_EXPORT void
+weston_pointer_send_axis(struct weston_pointer *pointer,
+ uint32_t time,
+ struct weston_pointer_axis_event *event)
+{
+ struct wl_resource *resource;
+ struct wl_list *resource_list;
+
+ if (!pointer->focus_client)
+ return;
+
+ resource_list = &pointer->focus_client->pointer_resources;
+ wl_resource_for_each(resource, resource_list) {
+ if (event->has_discrete &&
+ wl_resource_get_version(resource) >=
+ WL_POINTER_AXIS_DISCRETE_SINCE_VERSION)
+ wl_pointer_send_axis_discrete(resource, event->axis,
+ event->discrete);
+
+ if (event->value)
+ wl_pointer_send_axis(resource, time,
+ event->axis,
+ wl_fixed_from_double(event->value));
+ else if (wl_resource_get_version(resource) >=
+ WL_POINTER_AXIS_STOP_SINCE_VERSION)
+ wl_pointer_send_axis_stop(resource, time,
+ event->axis);
+ }
+}
+
+WL_EXPORT void
+weston_pointer_send_axis_source(struct weston_pointer *pointer, uint32_t source)
+{
+ struct wl_resource *resource;
+ struct wl_list *resource_list;
+
+ if (!pointer->focus_client)
+ return;
+
+ resource_list = &pointer->focus_client->pointer_resources;
+ wl_resource_for_each(resource, resource_list) {
+ if (wl_resource_get_version(resource) >=
+ WL_POINTER_AXIS_SOURCE_SINCE_VERSION) {
+ wl_pointer_send_axis_source(resource, source);
+ }
+ }
+}
+
+static void
+pointer_send_frame(struct wl_resource *resource)
+{
+ if (wl_resource_get_version(resource) >=
+ WL_POINTER_FRAME_SINCE_VERSION) {
+ wl_pointer_send_frame(resource);
+ }
+}
+
+WL_EXPORT void
+weston_pointer_send_frame(struct weston_pointer *pointer)
+{
+ struct wl_resource *resource;
+ struct wl_list *resource_list;
+
+ if (!pointer->focus_client)
+ return;
+
+ resource_list = &pointer->focus_client->pointer_resources;
+ wl_resource_for_each(resource, resource_list)
+ pointer_send_frame(resource);
+}
+
+static void
+default_grab_pointer_axis(struct weston_pointer_grab *grab,
+ uint32_t time,
+ struct weston_pointer_axis_event *event)
+{
+ weston_pointer_send_axis(grab->pointer, time, event);
+}
+
+static void
+default_grab_pointer_axis_source(struct weston_pointer_grab *grab,
+ uint32_t source)
+{
+ weston_pointer_send_axis_source(grab->pointer, source);
+}
+
+static void
+default_grab_pointer_frame(struct weston_pointer_grab *grab)
+{
+ weston_pointer_send_frame(grab->pointer);
+}
+
+static void
+default_grab_pointer_cancel(struct weston_pointer_grab *grab)
+{
+}
+
+static const struct weston_pointer_grab_interface
+ default_pointer_grab_interface = {
+ default_grab_pointer_focus,
+ default_grab_pointer_motion,
+ default_grab_pointer_button,
+ default_grab_pointer_axis,
+ default_grab_pointer_axis_source,
+ default_grab_pointer_frame,
+ default_grab_pointer_cancel,
+};
+
+static void
+default_grab_touch_down(struct weston_touch_grab *grab, uint32_t time,
+ int touch_id, wl_fixed_t x, wl_fixed_t y)
+{
+ struct weston_touch *touch = grab->touch;
+ struct wl_display *display = touch->seat->compositor->wl_display;
+ uint32_t serial;
+ struct wl_resource *resource;
+ struct wl_list *resource_list;
+ wl_fixed_t sx, sy;
+
+ if (!touch->focus)
+ return;
+
+ weston_view_from_global_fixed(touch->focus, x, y, &sx, &sy);
+
+ resource_list = &touch->focus_resource_list;
+
+ if (!wl_list_empty(resource_list)) {
+ serial = wl_display_next_serial(display);
+ wl_resource_for_each(resource, resource_list)
+ wl_touch_send_down(resource, serial, time,
+ touch->focus->surface->resource,
+ touch_id, sx, sy);
+ }
+}
+
+static void
+default_grab_touch_up(struct weston_touch_grab *grab,
+ uint32_t time, int touch_id)
+{
+ struct weston_touch *touch = grab->touch;
+ struct wl_display *display = touch->seat->compositor->wl_display;
+ uint32_t serial;
+ struct wl_resource *resource;
+ struct wl_list *resource_list;
+
+ resource_list = &touch->focus_resource_list;
+
+ if (!wl_list_empty(resource_list)) {
+ serial = wl_display_next_serial(display);
+ wl_resource_for_each(resource, resource_list)
+ wl_touch_send_up(resource, serial, time, touch_id);
+ }
+}
+
+static void
+default_grab_touch_motion(struct weston_touch_grab *grab, uint32_t time,
+ int touch_id, wl_fixed_t x, wl_fixed_t y)
+{
+ struct weston_touch *touch = grab->touch;
+ struct wl_resource *resource;
+ struct wl_list *resource_list;
+ wl_fixed_t sx, sy;
+
+ weston_view_from_global_fixed(touch->focus, x, y, &sx, &sy);
+
+ resource_list = &touch->focus_resource_list;
+
+ wl_resource_for_each(resource, resource_list) {
+ wl_touch_send_motion(resource, time,
+ touch_id, sx, sy);
+ }
+}
+
+static void
+default_grab_touch_frame(struct weston_touch_grab *grab)
+{
+ struct wl_resource *resource;
+
+ wl_resource_for_each(resource, &grab->touch->focus_resource_list)
+ wl_touch_send_frame(resource);
+}
+
+static void
+default_grab_touch_cancel(struct weston_touch_grab *grab)
+{
+}
+
+static const struct weston_touch_grab_interface default_touch_grab_interface = {
+ default_grab_touch_down,
+ default_grab_touch_up,
+ default_grab_touch_motion,
+ default_grab_touch_frame,
+ default_grab_touch_cancel,
+};
+
+static void
+default_grab_keyboard_key(struct weston_keyboard_grab *grab,
+ uint32_t time, uint32_t key, uint32_t state)
+{
+ struct weston_keyboard *keyboard = grab->keyboard;
+ struct wl_resource *resource;
+ struct wl_display *display = keyboard->seat->compositor->wl_display;
+ uint32_t serial;
+ struct wl_list *resource_list;
+
+ resource_list = &keyboard->focus_resource_list;
+ if (!wl_list_empty(resource_list)) {
+ serial = wl_display_next_serial(display);
+ wl_resource_for_each(resource, resource_list)
+ wl_keyboard_send_key(resource,
+ serial,
+ time,
+ key,
+ state);
+ }
+}
+
+static void
+send_modifiers_to_resource(struct weston_keyboard *keyboard,
+ struct wl_resource *resource,
+ uint32_t serial)
+{
+ wl_keyboard_send_modifiers(resource,
+ serial,
+ keyboard->modifiers.mods_depressed,
+ keyboard->modifiers.mods_latched,
+ keyboard->modifiers.mods_locked,
+ keyboard->modifiers.group);
+}
+
+static void
+send_modifiers_to_client_in_list(struct wl_client *client,
+ struct wl_list *list,
+ uint32_t serial,
+ struct weston_keyboard *keyboard)
+{
+ struct wl_resource *resource;
+
+ wl_resource_for_each(resource, list) {
+ if (wl_resource_get_client(resource) == client)
+ send_modifiers_to_resource(keyboard,
+ resource,
+ serial);
+ }
+}
+
+static struct weston_pointer_client *
+find_pointer_client_for_surface(struct weston_pointer *pointer,
+ struct weston_surface *surface)
+{
+ struct wl_client *client;
+
+ if (!surface)
+ return NULL;
+
+ if (!surface->resource)
+ return NULL;
+
+ client = wl_resource_get_client(surface->resource);
+ return weston_pointer_get_pointer_client(pointer, client);
+}
+
+static struct weston_pointer_client *
+find_pointer_client_for_view(struct weston_pointer *pointer, struct weston_view *view)
+{
+ if (!view)
+ return NULL;
+
+ return find_pointer_client_for_surface(pointer, view->surface);
+}
+
+static struct wl_resource *
+find_resource_for_surface(struct wl_list *list, struct weston_surface *surface)
+{
+ if (!surface)
+ return NULL;
+
+ if (!surface->resource)
+ return NULL;
+
+ return wl_resource_find_for_client(list, wl_resource_get_client(surface->resource));
+}
+
+static void
+default_grab_keyboard_modifiers(struct weston_keyboard_grab *grab,
+ uint32_t serial, uint32_t mods_depressed,
+ uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group)
+{
+ struct weston_keyboard *keyboard = grab->keyboard;
+ struct weston_pointer *pointer =
+ weston_seat_get_pointer(grab->keyboard->seat);
+ struct wl_resource *resource;
+ struct wl_list *resource_list;
+
+ resource_list = &keyboard->focus_resource_list;
+
+ wl_resource_for_each(resource, resource_list) {
+ wl_keyboard_send_modifiers(resource, serial, mods_depressed,
+ mods_latched, mods_locked, group);
+ }
+ if (pointer && pointer->focus && pointer->focus->surface->resource &&
+ pointer->focus->surface != keyboard->focus) {
+ struct wl_client *pointer_client =
+ wl_resource_get_client(pointer->focus->surface->resource);
+ send_modifiers_to_client_in_list(pointer_client,
+ &keyboard->resource_list,
+ serial,
+ keyboard);
+ }
+}
+
+static void
+default_grab_keyboard_cancel(struct weston_keyboard_grab *grab)
+{
+}
+
+static const struct weston_keyboard_grab_interface
+ default_keyboard_grab_interface = {
+ default_grab_keyboard_key,
+ default_grab_keyboard_modifiers,
+ default_grab_keyboard_cancel,
+};
+
+static void
+pointer_unmap_sprite(struct weston_pointer *pointer)
+{
+ struct weston_surface *surface = pointer->sprite->surface;
+
+ if (weston_surface_is_mapped(surface))
+ weston_surface_unmap(surface);
+
+ wl_list_remove(&pointer->sprite_destroy_listener.link);
+ surface->configure = NULL;
+ surface->configure_private = NULL;
+ weston_surface_set_label_func(surface, NULL);
+ weston_view_destroy(pointer->sprite);
+ pointer->sprite = NULL;
+}
+
+static void
+pointer_handle_sprite_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_pointer *pointer =
+ container_of(listener, struct weston_pointer,
+ sprite_destroy_listener);
+
+ pointer->sprite = NULL;
+}
+
+static void
+weston_pointer_reset_state(struct weston_pointer *pointer)
+{
+ pointer->button_count = 0;
+}
+
+static void
+weston_pointer_handle_output_destroy(struct wl_listener *listener, void *data);
+
+WL_EXPORT struct weston_pointer *
+weston_pointer_create(struct weston_seat *seat)
+{
+ struct weston_pointer *pointer;
+
+ pointer = zalloc(sizeof *pointer);
+ if (pointer == NULL)
+ return NULL;
+
+ wl_list_init(&pointer->pointer_clients);
+ weston_pointer_set_default_grab(pointer,
+ seat->compositor->default_pointer_grab);
+ wl_list_init(&pointer->focus_resource_listener.link);
+ pointer->focus_resource_listener.notify = pointer_focus_resource_destroyed;
+ pointer->default_grab.pointer = pointer;
+ pointer->grab = &pointer->default_grab;
+ wl_signal_init(&pointer->motion_signal);
+ wl_signal_init(&pointer->focus_signal);
+ wl_list_init(&pointer->focus_view_listener.link);
+
+ pointer->sprite_destroy_listener.notify = pointer_handle_sprite_destroy;
+
+ /* FIXME: Pick better co-ords. */
+ pointer->x = wl_fixed_from_int(100);
+ pointer->y = wl_fixed_from_int(100);
+
+ pointer->output_destroy_listener.notify =
+ weston_pointer_handle_output_destroy;
+ wl_signal_add(&seat->compositor->output_destroyed_signal,
+ &pointer->output_destroy_listener);
+
+ pointer->sx = wl_fixed_from_int(-1000000);
+ pointer->sy = wl_fixed_from_int(-1000000);
+
+ return pointer;
+}
+
+WL_EXPORT void
+weston_pointer_destroy(struct weston_pointer *pointer)
+{
+ if (pointer->sprite)
+ pointer_unmap_sprite(pointer);
+
+ /* XXX: What about pointer->resource_list? */
+
+ wl_list_remove(&pointer->focus_resource_listener.link);
+ wl_list_remove(&pointer->focus_view_listener.link);
+ wl_list_remove(&pointer->output_destroy_listener.link);
+ free(pointer);
+}
+
+void
+weston_pointer_set_default_grab(struct weston_pointer *pointer,
+ const struct weston_pointer_grab_interface *interface)
+{
+ if (interface)
+ pointer->default_grab.interface = interface;
+ else
+ pointer->default_grab.interface =
+ &default_pointer_grab_interface;
+}
+
+WL_EXPORT struct weston_keyboard *
+weston_keyboard_create(void)
+{
+ struct weston_keyboard *keyboard;
+
+ keyboard = zalloc(sizeof *keyboard);
+ if (keyboard == NULL)
+ return NULL;
+
+ wl_list_init(&keyboard->resource_list);
+ wl_list_init(&keyboard->focus_resource_list);
+ wl_list_init(&keyboard->focus_resource_listener.link);
+ keyboard->focus_resource_listener.notify = keyboard_focus_resource_destroyed;
+ wl_array_init(&keyboard->keys);
+ keyboard->default_grab.interface = &default_keyboard_grab_interface;
+ keyboard->default_grab.keyboard = keyboard;
+ keyboard->grab = &keyboard->default_grab;
+ wl_signal_init(&keyboard->focus_signal);
+
+ return keyboard;
+}
+
+static void
+weston_xkb_info_destroy(struct weston_xkb_info *xkb_info);
+
+WL_EXPORT void
+weston_keyboard_destroy(struct weston_keyboard *keyboard)
+{
+ /* XXX: What about keyboard->resource_list? */
+
+#ifdef ENABLE_XKBCOMMON
+ if (keyboard->seat->compositor->use_xkbcommon) {
+ xkb_state_unref(keyboard->xkb_state.state);
+ if (keyboard->xkb_info)
+ weston_xkb_info_destroy(keyboard->xkb_info);
+ xkb_keymap_unref(keyboard->pending_keymap);
+ }
+#endif
+
+ wl_array_release(&keyboard->keys);
+ wl_list_remove(&keyboard->focus_resource_listener.link);
+ free(keyboard);
+}
+
+static void
+weston_touch_reset_state(struct weston_touch *touch)
+{
+ touch->num_tp = 0;
+}
+
+WL_EXPORT struct weston_touch *
+weston_touch_create(void)
+{
+ struct weston_touch *touch;
+
+ touch = zalloc(sizeof *touch);
+ if (touch == NULL)
+ return NULL;
+
+ wl_list_init(&touch->resource_list);
+ wl_list_init(&touch->focus_resource_list);
+ wl_list_init(&touch->focus_view_listener.link);
+ touch->focus_view_listener.notify = touch_focus_view_destroyed;
+ wl_list_init(&touch->focus_resource_listener.link);
+ touch->focus_resource_listener.notify = touch_focus_resource_destroyed;
+ touch->default_grab.interface = &default_touch_grab_interface;
+ touch->default_grab.touch = touch;
+ touch->grab = &touch->default_grab;
+ wl_signal_init(&touch->focus_signal);
+
+ return touch;
+}
+
+WL_EXPORT void
+weston_touch_destroy(struct weston_touch *touch)
+{
+ /* XXX: What about touch->resource_list? */
+
+ wl_list_remove(&touch->focus_view_listener.link);
+ wl_list_remove(&touch->focus_resource_listener.link);
+ free(touch);
+}
+
+static void
+seat_send_updated_caps(struct weston_seat *seat)
+{
+ enum wl_seat_capability caps = 0;
+ struct wl_resource *resource;
+
+ if (seat->pointer_device_count > 0)
+ caps |= WL_SEAT_CAPABILITY_POINTER;
+ if (seat->keyboard_device_count > 0)
+ caps |= WL_SEAT_CAPABILITY_KEYBOARD;
+ if (seat->touch_device_count > 0)
+ caps |= WL_SEAT_CAPABILITY_TOUCH;
+
+ wl_resource_for_each(resource, &seat->base_resource_list) {
+ wl_seat_send_capabilities(resource, caps);
+ }
+ wl_signal_emit(&seat->updated_caps_signal, seat);
+}
+
+
+/** Clear the pointer focus
+ *
+ * \param pointer the pointer to clear focus for.
+ *
+ * This can be used to unset pointer focus and set the co-ordinates to the
+ * arbitrary values we use for the no focus case.
+ *
+ * There's no requirement to use this function. For example, passing the
+ * results of a weston_compositor_pick_view() directly to
+ * weston_pointer_set_focus() will do the right thing when no view is found.
+ */
+WL_EXPORT void
+weston_pointer_clear_focus(struct weston_pointer *pointer)
+{
+ weston_pointer_set_focus(pointer, NULL,
+ wl_fixed_from_int(-1000000),
+ wl_fixed_from_int(-1000000));
+}
+
+WL_EXPORT void
+weston_pointer_set_focus(struct weston_pointer *pointer,
+ struct weston_view *view,
+ wl_fixed_t sx, wl_fixed_t sy)
+{
+ struct weston_pointer_client *pointer_client;
+ struct weston_keyboard *kbd = weston_seat_get_keyboard(pointer->seat);
+ struct wl_resource *resource;
+ struct wl_resource *surface_resource;
+ struct wl_display *display = pointer->seat->compositor->wl_display;
+ uint32_t serial;
+ struct wl_list *focus_resource_list;
+ int refocus = 0;
+
+ if ((!pointer->focus && view) ||
+ (pointer->focus && !view) ||
+ (pointer->focus && pointer->focus->surface != view->surface) ||
+ pointer->sx != sx || pointer->sy != sy)
+ refocus = 1;
+
+ if (pointer->focus_client && refocus) {
+ focus_resource_list = &pointer->focus_client->pointer_resources;
+ if (!wl_list_empty(focus_resource_list)) {
+ serial = wl_display_next_serial(display);
+ surface_resource = pointer->focus->surface->resource;
+ wl_resource_for_each(resource, focus_resource_list) {
+ wl_pointer_send_leave(resource, serial,
+ surface_resource);
+ pointer_send_frame(resource);
+ }
+ }
+
+ pointer->focus_client = NULL;
+ }
+
+ pointer_client = find_pointer_client_for_view(pointer, view);
+ if (pointer_client && refocus) {
+ struct wl_client *surface_client = pointer_client->client;
+
+ serial = wl_display_next_serial(display);
+
+ if (kbd && kbd->focus != view->surface)
+ send_modifiers_to_client_in_list(surface_client,
+ &kbd->resource_list,
+ serial,
+ kbd);
+
+ pointer->focus_client = pointer_client;
+
+ focus_resource_list = &pointer->focus_client->pointer_resources;
+ wl_resource_for_each(resource, focus_resource_list) {
+ wl_pointer_send_enter(resource,
+ serial,
+ view->surface->resource,
+ sx, sy);
+ pointer_send_frame(resource);
+ }
+
+ pointer->focus_serial = serial;
+ }
+
+ wl_list_remove(&pointer->focus_view_listener.link);
+ wl_list_init(&pointer->focus_view_listener.link);
+ wl_list_remove(&pointer->focus_resource_listener.link);
+ wl_list_init(&pointer->focus_resource_listener.link);
+ if (view)
+ wl_signal_add(&view->destroy_signal, &pointer->focus_view_listener);
+ if (view && view->surface->resource)
+ wl_resource_add_destroy_listener(view->surface->resource,
+ &pointer->focus_resource_listener);
+
+ pointer->focus = view;
+ pointer->focus_view_listener.notify = pointer_focus_view_destroyed;
+ pointer->sx = sx;
+ pointer->sy = sy;
+
+ assert(view || sx == wl_fixed_from_int(-1000000));
+ assert(view || sy == wl_fixed_from_int(-1000000));
+
+ wl_signal_emit(&pointer->focus_signal, pointer);
+}
+
+static void
+send_enter_to_resource_list(struct wl_list *list,
+ struct weston_keyboard *keyboard,
+ struct weston_surface *surface,
+ uint32_t serial)
+{
+ struct wl_resource *resource;
+
+ wl_resource_for_each(resource, list) {
+ send_modifiers_to_resource(keyboard, resource, serial);
+ wl_keyboard_send_enter(resource, serial,
+ surface->resource,
+ &keyboard->keys);
+ }
+}
+
+WL_EXPORT void
+weston_keyboard_set_focus(struct weston_keyboard *keyboard,
+ struct weston_surface *surface)
+{
+ struct wl_resource *resource;
+ struct wl_display *display = keyboard->seat->compositor->wl_display;
+ uint32_t serial;
+ struct wl_list *focus_resource_list;
+
+ focus_resource_list = &keyboard->focus_resource_list;
+
+ if (!wl_list_empty(focus_resource_list) && keyboard->focus != surface) {
+ serial = wl_display_next_serial(display);
+ wl_resource_for_each(resource, focus_resource_list) {
+ wl_keyboard_send_leave(resource, serial,
+ keyboard->focus->resource);
+ }
+ move_resources(&keyboard->resource_list, focus_resource_list);
+ }
+
+ if (find_resource_for_surface(&keyboard->resource_list, surface) &&
+ keyboard->focus != surface) {
+ struct wl_client *surface_client =
+ wl_resource_get_client(surface->resource);
+
+ serial = wl_display_next_serial(display);
+
+ move_resources_for_client(focus_resource_list,
+ &keyboard->resource_list,
+ surface_client);
+ send_enter_to_resource_list(focus_resource_list,
+ keyboard,
+ surface,
+ serial);
+ keyboard->focus_serial = serial;
+ }
+
+ wl_list_remove(&keyboard->focus_resource_listener.link);
+ wl_list_init(&keyboard->focus_resource_listener.link);
+ if (surface && surface->resource)
+ wl_resource_add_destroy_listener(surface->resource,
+ &keyboard->focus_resource_listener);
+
+ keyboard->focus = surface;
+ wl_signal_emit(&keyboard->focus_signal, keyboard);
+}
+
+/* Users of this function must manually manage the keyboard focus */
+WL_EXPORT void
+weston_keyboard_start_grab(struct weston_keyboard *keyboard,
+ struct weston_keyboard_grab *grab)
+{
+ keyboard->grab = grab;
+ grab->keyboard = keyboard;
+}
+
+WL_EXPORT void
+weston_keyboard_end_grab(struct weston_keyboard *keyboard)
+{
+ keyboard->grab = &keyboard->default_grab;
+}
+
+static void
+weston_keyboard_cancel_grab(struct weston_keyboard *keyboard)
+{
+ keyboard->grab->interface->cancel(keyboard->grab);
+}
+
+WL_EXPORT void
+weston_pointer_start_grab(struct weston_pointer *pointer,
+ struct weston_pointer_grab *grab)
+{
+ pointer->grab = grab;
+ grab->pointer = pointer;
+ pointer->grab->interface->focus(pointer->grab);
+}
+
+WL_EXPORT void
+weston_pointer_end_grab(struct weston_pointer *pointer)
+{
+ pointer->grab = &pointer->default_grab;
+ pointer->grab->interface->focus(pointer->grab);
+}
+
+static void
+weston_pointer_cancel_grab(struct weston_pointer *pointer)
+{
+ pointer->grab->interface->cancel(pointer->grab);
+}
+
+WL_EXPORT void
+weston_touch_start_grab(struct weston_touch *touch, struct weston_touch_grab *grab)
+{
+ touch->grab = grab;
+ grab->touch = touch;
+}
+
+WL_EXPORT void
+weston_touch_end_grab(struct weston_touch *touch)
+{
+ touch->grab = &touch->default_grab;
+}
+
+static void
+weston_touch_cancel_grab(struct weston_touch *touch)
+{
+ touch->grab->interface->cancel(touch->grab);
+}
+
+static void
+weston_pointer_clamp_for_output(struct weston_pointer *pointer,
+ struct weston_output *output,
+ wl_fixed_t *fx, wl_fixed_t *fy)
+{
+ int x, y;
+
+ x = wl_fixed_to_int(*fx);
+ y = wl_fixed_to_int(*fy);
+
+ if (x < output->x)
+ *fx = wl_fixed_from_int(output->x);
+ else if (x >= output->x + output->width)
+ *fx = wl_fixed_from_int(output->x +
+ output->width - 1);
+ if (y < output->y)
+ *fy = wl_fixed_from_int(output->y);
+ else if (y >= output->y + output->height)
+ *fy = wl_fixed_from_int(output->y +
+ output->height - 1);
+}
+
+WL_EXPORT void
+weston_pointer_clamp(struct weston_pointer *pointer, wl_fixed_t *fx, wl_fixed_t *fy)
+{
+ struct weston_compositor *ec = pointer->seat->compositor;
+ struct weston_output *output, *prev = NULL;
+ int x, y, old_x, old_y, valid = 0;
+
+ x = wl_fixed_to_int(*fx);
+ y = wl_fixed_to_int(*fy);
+ old_x = wl_fixed_to_int(pointer->x);
+ old_y = wl_fixed_to_int(pointer->y);
+
+ wl_list_for_each(output, &ec->output_list, link) {
+ if (pointer->seat->output && pointer->seat->output != output)
+ continue;
+ if (pixman_region32_contains_point(&output->region,
+ x, y, NULL))
+ valid = 1;
+ if (pixman_region32_contains_point(&output->region,
+ old_x, old_y, NULL))
+ prev = output;
+ }
+
+ if (!prev)
+ prev = pointer->seat->output;
+
+ if (prev && !valid)
+ weston_pointer_clamp_for_output(pointer, prev, fx, fy);
+}
+
+static void
+weston_pointer_move_to(struct weston_pointer *pointer,
+ wl_fixed_t x, wl_fixed_t y)
+{
+ int32_t ix, iy;
+
+ weston_pointer_clamp (pointer, &x, &y);
+
+ pointer->x = x;
+ pointer->y = y;
+
+ ix = wl_fixed_to_int(x);
+ iy = wl_fixed_to_int(y);
+
+ if (pointer->sprite) {
+ weston_view_set_position(pointer->sprite,
+ ix - pointer->hotspot_x,
+ iy - pointer->hotspot_y);
+ weston_view_schedule_repaint(pointer->sprite);
+ }
+
+ pointer->grab->interface->focus(pointer->grab);
+ wl_signal_emit(&pointer->motion_signal, pointer);
+}
+
+WL_EXPORT void
+weston_pointer_motion_to_abs(struct weston_pointer *pointer,
+ struct weston_pointer_motion_event *event,
+ wl_fixed_t *x, wl_fixed_t *y)
+{
+ if (event->mask & WESTON_POINTER_MOTION_ABS) {
+ *x = wl_fixed_from_double(event->x);
+ *y = wl_fixed_from_double(event->y);
+ } else if (event->mask & WESTON_POINTER_MOTION_REL) {
+ *x = pointer->x + wl_fixed_from_double(event->dx);
+ *y = pointer->y + wl_fixed_from_double(event->dy);
+ } else {
+ assert(!"invalid motion event");
+ *x = *y = 0;
+ }
+}
+
+WL_EXPORT void
+weston_pointer_move(struct weston_pointer *pointer,
+ struct weston_pointer_motion_event *event)
+{
+ wl_fixed_t x, y;
+
+ weston_pointer_motion_to_abs(pointer, event, &x, &y);
+ weston_pointer_move_to(pointer, x, y);
+}
+
+/** Verify if the pointer is in a valid position and move it if it isn't.
+ */
+static void
+weston_pointer_handle_output_destroy(struct wl_listener *listener, void *data)
+{
+ struct weston_pointer *pointer;
+ struct weston_compositor *ec;
+ struct weston_output *output, *closest = NULL;
+ int x, y, distance, min = INT_MAX;
+ wl_fixed_t fx, fy;
+
+ pointer = container_of(listener, struct weston_pointer,
+ output_destroy_listener);
+ ec = pointer->seat->compositor;
+
+ x = wl_fixed_to_int(pointer->x);
+ y = wl_fixed_to_int(pointer->y);
+
+ wl_list_for_each(output, &ec->output_list, link) {
+ if (pixman_region32_contains_point(&output->region,
+ x, y, NULL))
+ return;
+
+ /* Aproximante the distance from the pointer to the center of
+ * the output. */
+ distance = abs(output->x + output->width / 2 - x) +
+ abs(output->y + output->height / 2 - y);
+ if (distance < min) {
+ min = distance;
+ closest = output;
+ }
+ }
+
+ /* Nothing to do if there's no output left. */
+ if (!closest)
+ return;
+
+ fx = pointer->x;
+ fy = pointer->y;
+
+ weston_pointer_clamp_for_output(pointer, closest, &fx, &fy);
+ weston_pointer_move_to(pointer, fx, fy);
+}
+
+WL_EXPORT void
+notify_motion(struct weston_seat *seat,
+ uint32_t time,
+ struct weston_pointer_motion_event *event)
+{
+ struct weston_compositor *ec = seat->compositor;
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+
+ weston_compositor_wake(ec);
+ pointer->grab->interface->motion(pointer->grab, time, event);
+}
+
+static void
+run_modifier_bindings(struct weston_seat *seat, uint32_t old, uint32_t new)
+{
+ struct weston_compositor *compositor = seat->compositor;
+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
+ uint32_t diff;
+ unsigned int i;
+ struct {
+ uint32_t xkb;
+ enum weston_keyboard_modifier weston;
+ } mods[] = {
+ { keyboard->xkb_info->ctrl_mod, MODIFIER_CTRL },
+ { keyboard->xkb_info->alt_mod, MODIFIER_ALT },
+ { keyboard->xkb_info->super_mod, MODIFIER_SUPER },
+ { keyboard->xkb_info->shift_mod, MODIFIER_SHIFT },
+ };
+
+ diff = new & ~old;
+ for (i = 0; i < ARRAY_LENGTH(mods); i++) {
+ if (diff & (1 << mods[i].xkb))
+ weston_compositor_run_modifier_binding(compositor,
+ keyboard,
+ mods[i].weston,
+ WL_KEYBOARD_KEY_STATE_PRESSED);
+ }
+
+ diff = old & ~new;
+ for (i = 0; i < ARRAY_LENGTH(mods); i++) {
+ if (diff & (1 << mods[i].xkb))
+ weston_compositor_run_modifier_binding(compositor,
+ keyboard,
+ mods[i].weston,
+ WL_KEYBOARD_KEY_STATE_RELEASED);
+ }
+}
+
+WL_EXPORT void
+notify_motion_absolute(struct weston_seat *seat,
+ uint32_t time, double x, double y)
+{
+ struct weston_compositor *ec = seat->compositor;
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+ struct weston_pointer_motion_event event = { 0 };
+
+ weston_compositor_wake(ec);
+
+ event = (struct weston_pointer_motion_event) {
+ .mask = WESTON_POINTER_MOTION_ABS,
+ .x = x,
+ .y = y,
+ };
+
+ pointer->grab->interface->motion(pointer->grab, time, &event);
+}
+
+WL_EXPORT void
+weston_surface_activate(struct weston_surface *surface,
+ struct weston_seat *seat)
+{
+ struct weston_compositor *compositor = seat->compositor;
+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
+
+ if (keyboard) {
+ weston_keyboard_set_focus(keyboard, surface);
+ wl_data_device_set_keyboard_focus(seat);
+ }
+
+ wl_signal_emit(&compositor->activate_signal, surface);
+}
+
+WL_EXPORT void
+notify_button(struct weston_seat *seat, uint32_t time, int32_t button,
+ enum wl_pointer_button_state state)
+{
+ struct weston_compositor *compositor = seat->compositor;
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+
+ if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+ weston_compositor_idle_inhibit(compositor);
+ if (pointer->button_count == 0) {
+ pointer->grab_button = button;
+ pointer->grab_time = time;
+ pointer->grab_x = pointer->x;
+ pointer->grab_y = pointer->y;
+ }
+ pointer->button_count++;
+ } else {
+ weston_compositor_idle_release(compositor);
+ pointer->button_count--;
+ }
+
+ weston_compositor_run_button_binding(compositor, pointer, time, button,
+ state);
+
+ pointer->grab->interface->button(pointer->grab, time, button, state);
+
+ if (pointer->button_count == 1)
+ pointer->grab_serial =
+ wl_display_get_serial(compositor->wl_display);
+}
+
+WL_EXPORT void
+notify_axis(struct weston_seat *seat, uint32_t time,
+ struct weston_pointer_axis_event *event)
+{
+ struct weston_compositor *compositor = seat->compositor;
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+
+ weston_compositor_wake(compositor);
+
+ if (weston_compositor_run_axis_binding(compositor, pointer,
+ time, event))
+ return;
+
+ pointer->grab->interface->axis(pointer->grab, time, event);
+}
+
+WL_EXPORT void
+notify_axis_source(struct weston_seat *seat, uint32_t source)
+{
+ struct weston_compositor *compositor = seat->compositor;
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+
+ weston_compositor_wake(compositor);
+
+ pointer->grab->interface->axis_source(pointer->grab, source);
+}
+
+WL_EXPORT void
+notify_pointer_frame(struct weston_seat *seat)
+{
+ struct weston_compositor *compositor = seat->compositor;
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+
+ weston_compositor_wake(compositor);
+
+ pointer->grab->interface->frame(pointer->grab);
+}
+
+WL_EXPORT int
+weston_keyboard_set_locks(struct weston_keyboard *keyboard,
+ uint32_t mask, uint32_t value)
+{
+#ifdef ENABLE_XKBCOMMON
+ uint32_t serial;
+ xkb_mod_mask_t mods_depressed, mods_latched, mods_locked, group;
+ xkb_mod_mask_t num, caps;
+
+ /* We don't want the leds to go out of sync with the actual state
+ * so if the backend has no way to change the leds don't try to
+ * change the state */
+ if (!keyboard->seat->led_update)
+ return -1;
+
+ mods_depressed = xkb_state_serialize_mods(keyboard->xkb_state.state,
+ XKB_STATE_DEPRESSED);
+ mods_latched = xkb_state_serialize_mods(keyboard->xkb_state.state,
+ XKB_STATE_LATCHED);
+ mods_locked = xkb_state_serialize_mods(keyboard->xkb_state.state,
+ XKB_STATE_LOCKED);
+ group = xkb_state_serialize_group(keyboard->xkb_state.state,
+ XKB_STATE_EFFECTIVE);
+
+ num = (1 << keyboard->xkb_info->mod2_mod);
+ caps = (1 << keyboard->xkb_info->caps_mod);
+ if (mask & WESTON_NUM_LOCK) {
+ if (value & WESTON_NUM_LOCK)
+ mods_locked |= num;
+ else
+ mods_locked &= ~num;
+ }
+ if (mask & WESTON_CAPS_LOCK) {
+ if (value & WESTON_CAPS_LOCK)
+ mods_locked |= caps;
+ else
+ mods_locked &= ~caps;
+ }
+
+ xkb_state_update_mask(keyboard->xkb_state.state, mods_depressed,
+ mods_latched, mods_locked, 0, 0, group);
+
+ serial = wl_display_next_serial(
+ keyboard->seat->compositor->wl_display);
+ notify_modifiers(keyboard->seat, serial);
+
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+#ifdef ENABLE_XKBCOMMON
+WL_EXPORT void
+notify_modifiers(struct weston_seat *seat, uint32_t serial)
+{
+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
+ struct weston_keyboard_grab *grab = keyboard->grab;
+ uint32_t mods_depressed, mods_latched, mods_locked, group;
+ uint32_t mods_lookup;
+ enum weston_led leds = 0;
+ int changed = 0;
+
+ /* Serialize and update our internal state, checking to see if it's
+ * different to the previous state. */
+ mods_depressed = xkb_state_serialize_mods(keyboard->xkb_state.state,
+ XKB_STATE_MODS_DEPRESSED);
+ mods_latched = xkb_state_serialize_mods(keyboard->xkb_state.state,
+ XKB_STATE_MODS_LATCHED);
+ mods_locked = xkb_state_serialize_mods(keyboard->xkb_state.state,
+ XKB_STATE_MODS_LOCKED);
+ group = xkb_state_serialize_layout(keyboard->xkb_state.state,
+ XKB_STATE_LAYOUT_EFFECTIVE);
+
+ if (mods_depressed != keyboard->modifiers.mods_depressed ||
+ mods_latched != keyboard->modifiers.mods_latched ||
+ mods_locked != keyboard->modifiers.mods_locked ||
+ group != keyboard->modifiers.group)
+ changed = 1;
+
+ run_modifier_bindings(seat, keyboard->modifiers.mods_depressed,
+ mods_depressed);
+
+ keyboard->modifiers.mods_depressed = mods_depressed;
+ keyboard->modifiers.mods_latched = mods_latched;
+ keyboard->modifiers.mods_locked = mods_locked;
+ keyboard->modifiers.group = group;
+
+ /* And update the modifier_state for bindings. */
+ mods_lookup = mods_depressed | mods_latched;
+ seat->modifier_state = 0;
+ if (mods_lookup & (1 << keyboard->xkb_info->ctrl_mod))
+ seat->modifier_state |= MODIFIER_CTRL;
+ if (mods_lookup & (1 << keyboard->xkb_info->alt_mod))
+ seat->modifier_state |= MODIFIER_ALT;
+ if (mods_lookup & (1 << keyboard->xkb_info->super_mod))
+ seat->modifier_state |= MODIFIER_SUPER;
+ if (mods_lookup & (1 << keyboard->xkb_info->shift_mod))
+ seat->modifier_state |= MODIFIER_SHIFT;
+
+ /* Finally, notify the compositor that LEDs have changed. */
+ if (xkb_state_led_index_is_active(keyboard->xkb_state.state,
+ keyboard->xkb_info->num_led))
+ leds |= LED_NUM_LOCK;
+ if (xkb_state_led_index_is_active(keyboard->xkb_state.state,
+ keyboard->xkb_info->caps_led))
+ leds |= LED_CAPS_LOCK;
+ if (xkb_state_led_index_is_active(keyboard->xkb_state.state,
+ keyboard->xkb_info->scroll_led))
+ leds |= LED_SCROLL_LOCK;
+ if (leds != keyboard->xkb_state.leds && seat->led_update)
+ seat->led_update(seat, leds);
+ keyboard->xkb_state.leds = leds;
+
+ if (changed) {
+ grab->interface->modifiers(grab,
+ serial,
+ keyboard->modifiers.mods_depressed,
+ keyboard->modifiers.mods_latched,
+ keyboard->modifiers.mods_locked,
+ keyboard->modifiers.group);
+ }
+}
+
+static void
+update_modifier_state(struct weston_seat *seat, uint32_t serial, uint32_t key,
+ enum wl_keyboard_key_state state)
+{
+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
+ enum xkb_key_direction direction;
+
+ /* Keyboard modifiers don't exist in raw keyboard mode */
+ if (!seat->compositor->use_xkbcommon)
+ return;
+
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
+ direction = XKB_KEY_DOWN;
+ else
+ direction = XKB_KEY_UP;
+
+ /* Offset the keycode by 8, as the evdev XKB rules reflect X's
+ * broken keycode system, which starts at 8. */
+ xkb_state_update_key(keyboard->xkb_state.state, key + 8, direction);
+
+ notify_modifiers(seat, serial);
+}
+
+static void
+send_keymap(struct wl_resource *resource, struct weston_xkb_info *xkb_info)
+{
+ wl_keyboard_send_keymap(resource,
+ WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+ xkb_info->keymap_fd,
+ xkb_info->keymap_size);
+}
+
+static void
+send_modifiers(struct wl_resource *resource, uint32_t serial, struct weston_keyboard *keyboard)
+{
+ wl_keyboard_send_modifiers(resource, serial,
+ keyboard->modifiers.mods_depressed,
+ keyboard->modifiers.mods_latched,
+ keyboard->modifiers.mods_locked,
+ keyboard->modifiers.group);
+}
+
+static struct weston_xkb_info *
+weston_xkb_info_create(struct xkb_keymap *keymap);
+
+static void
+update_keymap(struct weston_seat *seat)
+{
+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
+ struct wl_resource *resource;
+ struct weston_xkb_info *xkb_info;
+ struct xkb_state *state;
+ xkb_mod_mask_t latched_mods;
+ xkb_mod_mask_t locked_mods;
+
+ xkb_info = weston_xkb_info_create(keyboard->pending_keymap);
+
+ xkb_keymap_unref(keyboard->pending_keymap);
+ keyboard->pending_keymap = NULL;
+
+ if (!xkb_info) {
+ weston_log("failed to create XKB info\n");
+ return;
+ }
+
+ state = xkb_state_new(xkb_info->keymap);
+ if (!state) {
+ weston_log("failed to initialise XKB state\n");
+ weston_xkb_info_destroy(xkb_info);
+ return;
+ }
+
+ latched_mods = xkb_state_serialize_mods(keyboard->xkb_state.state,
+ XKB_STATE_MODS_LATCHED);
+ locked_mods = xkb_state_serialize_mods(keyboard->xkb_state.state,
+ XKB_STATE_MODS_LOCKED);
+ xkb_state_update_mask(state,
+ 0, /* depressed */
+ latched_mods,
+ locked_mods,
+ 0, 0, 0);
+
+ weston_xkb_info_destroy(keyboard->xkb_info);
+ keyboard->xkb_info = xkb_info;
+
+ xkb_state_unref(keyboard->xkb_state.state);
+ keyboard->xkb_state.state = state;
+
+ wl_resource_for_each(resource, &keyboard->resource_list)
+ send_keymap(resource, xkb_info);
+ wl_resource_for_each(resource, &keyboard->focus_resource_list)
+ send_keymap(resource, xkb_info);
+
+ notify_modifiers(seat, wl_display_next_serial(seat->compositor->wl_display));
+
+ if (!latched_mods && !locked_mods)
+ return;
+
+ wl_resource_for_each(resource, &keyboard->resource_list)
+ send_modifiers(resource, wl_display_get_serial(seat->compositor->wl_display), keyboard);
+ wl_resource_for_each(resource, &keyboard->focus_resource_list)
+ send_modifiers(resource, wl_display_get_serial(seat->compositor->wl_display), keyboard);
+}
+#else
+WL_EXPORT void
+notify_modifiers(struct weston_seat *seat, uint32_t serial)
+{
+}
+
+static void
+update_modifier_state(struct weston_seat *seat, uint32_t serial, uint32_t key,
+ enum wl_keyboard_key_state state)
+{
+}
+
+static void
+update_keymap(struct weston_seat *seat)
+{
+}
+#endif
+
+WL_EXPORT void
+notify_key(struct weston_seat *seat, uint32_t time, uint32_t key,
+ enum wl_keyboard_key_state state,
+ enum weston_key_state_update update_state)
+{
+ struct weston_compositor *compositor = seat->compositor;
+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
+ struct weston_keyboard_grab *grab = keyboard->grab;
+ uint32_t *k, *end;
+
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ weston_compositor_idle_inhibit(compositor);
+ } else {
+ weston_compositor_idle_release(compositor);
+ }
+
+ end = keyboard->keys.data + keyboard->keys.size;
+ for (k = keyboard->keys.data; k < end; k++) {
+ if (*k == key) {
+ /* Ignore server-generated repeats. */
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
+ return;
+ *k = *--end;
+ }
+ }
+ keyboard->keys.size = (void *) end - keyboard->keys.data;
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ k = wl_array_add(&keyboard->keys, sizeof *k);
+ *k = key;
+ }
+
+ if (grab == &keyboard->default_grab ||
+ grab == &keyboard->input_method_grab) {
+ weston_compositor_run_key_binding(compositor, keyboard, time,
+ key, state);
+ grab = keyboard->grab;
+ }
+
+ grab->interface->key(grab, time, key, state);
+
+ if (keyboard->pending_keymap &&
+ keyboard->keys.size == 0)
+ update_keymap(seat);
+
+ if (update_state == STATE_UPDATE_AUTOMATIC) {
+ update_modifier_state(seat,
+ wl_display_get_serial(compositor->wl_display),
+ key,
+ state);
+ }
+
+ if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ keyboard->grab_serial =
+ wl_display_get_serial(compositor->wl_display);
+ keyboard->grab_time = time;
+ keyboard->grab_key = key;
+ }
+}
+
+WL_EXPORT void
+notify_pointer_focus(struct weston_seat *seat, struct weston_output *output,
+ double x, double y)
+{
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+
+ if (output) {
+ weston_pointer_move_to(pointer,
+ wl_fixed_from_double(x),
+ wl_fixed_from_double(y));
+ } else {
+ /* FIXME: We should call weston_pointer_set_focus(seat,
+ * NULL) here, but somehow that breaks re-entry... */
+ }
+}
+
+static void
+destroy_device_saved_kbd_focus(struct wl_listener *listener, void *data)
+{
+ struct weston_seat *ws;
+
+ ws = container_of(listener, struct weston_seat,
+ saved_kbd_focus_listener);
+
+ ws->saved_kbd_focus = NULL;
+}
+
+WL_EXPORT void
+notify_keyboard_focus_in(struct weston_seat *seat, struct wl_array *keys,
+ enum weston_key_state_update update_state)
+{
+ struct weston_compositor *compositor = seat->compositor;
+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
+ struct weston_surface *surface;
+ uint32_t *k, serial;
+
+ serial = wl_display_next_serial(compositor->wl_display);
+ wl_array_copy(&keyboard->keys, keys);
+ wl_array_for_each(k, &keyboard->keys) {
+ weston_compositor_idle_inhibit(compositor);
+ if (update_state == STATE_UPDATE_AUTOMATIC)
+ update_modifier_state(seat, serial, *k,
+ WL_KEYBOARD_KEY_STATE_PRESSED);
+ }
+
+ surface = seat->saved_kbd_focus;
+
+ if (surface) {
+ wl_list_remove(&seat->saved_kbd_focus_listener.link);
+ weston_keyboard_set_focus(keyboard, surface);
+ seat->saved_kbd_focus = NULL;
+ }
+}
+
+WL_EXPORT void
+notify_keyboard_focus_out(struct weston_seat *seat)
+{
+ struct weston_compositor *compositor = seat->compositor;
+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+ uint32_t *k, serial;
+
+ serial = wl_display_next_serial(compositor->wl_display);
+ wl_array_for_each(k, &keyboard->keys) {
+ weston_compositor_idle_release(compositor);
+ update_modifier_state(seat, serial, *k,
+ WL_KEYBOARD_KEY_STATE_RELEASED);
+ }
+
+ seat->modifier_state = 0;
+
+ if (keyboard->focus) {
+ seat->saved_kbd_focus = keyboard->focus;
+ seat->saved_kbd_focus_listener.notify =
+ destroy_device_saved_kbd_focus;
+ wl_signal_add(&keyboard->focus->destroy_signal,
+ &seat->saved_kbd_focus_listener);
+ }
+
+ weston_keyboard_set_focus(keyboard, NULL);
+ weston_keyboard_cancel_grab(keyboard);
+ if (pointer)
+ weston_pointer_cancel_grab(pointer);
+}
+
+WL_EXPORT void
+weston_touch_set_focus(struct weston_touch *touch, struct weston_view *view)
+{
+ struct wl_list *focus_resource_list;
+
+ focus_resource_list = &touch->focus_resource_list;
+
+ if (view && touch->focus &&
+ touch->focus->surface == view->surface) {
+ touch->focus = view;
+ return;
+ }
+
+ wl_list_remove(&touch->focus_resource_listener.link);
+ wl_list_init(&touch->focus_resource_listener.link);
+ wl_list_remove(&touch->focus_view_listener.link);
+ wl_list_init(&touch->focus_view_listener.link);
+
+ if (!wl_list_empty(focus_resource_list)) {
+ move_resources(&touch->resource_list,
+ focus_resource_list);
+ }
+
+ if (view) {
+ struct wl_client *surface_client;
+
+ if (!view->surface->resource) {
+ touch->focus = NULL;
+ return;
+ }
+
+ surface_client = wl_resource_get_client(view->surface->resource);
+ move_resources_for_client(focus_resource_list,
+ &touch->resource_list,
+ surface_client);
+ wl_resource_add_destroy_listener(view->surface->resource,
+ &touch->focus_resource_listener);
+ wl_signal_add(&view->destroy_signal, &touch->focus_view_listener);
+ }
+ touch->focus = view;
+}
+
+/**
+ * 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 weston_seat *seat, uint32_t time, int touch_id,
+ double double_x, double double_y, int touch_type)
+{
+ struct weston_compositor *ec = seat->compositor;
+ struct weston_touch *touch = weston_seat_get_touch(seat);
+ struct weston_touch_grab *grab = touch->grab;
+ struct weston_view *ev;
+ wl_fixed_t sx, sy;
+ wl_fixed_t x = wl_fixed_from_double(double_x);
+ wl_fixed_t y = wl_fixed_from_double(double_y);
+
+ /* Update grab's global coordinates. */
+ if (touch_id == touch->grab_touch_id && touch_type != WL_TOUCH_UP) {
+ touch->grab_x = x;
+ touch->grab_y = y;
+ }
+
+ switch (touch_type) {
+ case WL_TOUCH_DOWN:
+ weston_compositor_idle_inhibit(ec);
+
+ touch->num_tp++;
+
+ /* the first finger down picks the view, and all further go
+ * to that view for the remainder of the touch session i.e.
+ * until all touch points are up again. */
+ if (touch->num_tp == 1) {
+ ev = weston_compositor_pick_view(ec, x, y, &sx, &sy);
+ weston_touch_set_focus(touch, ev);
+ } else if (!touch->focus) {
+ /* Unexpected condition: We have non-initial touch but
+ * there is no focused surface.
+ */
+ weston_log("touch event received with %d points down "
+ "but no surface focused\n", touch->num_tp);
+ return;
+ }
+
+ weston_compositor_run_touch_binding(ec, touch,
+ time, touch_type);
+
+ grab->interface->down(grab, time, touch_id, x, y);
+ if (touch->num_tp == 1) {
+ touch->grab_serial =
+ wl_display_get_serial(ec->wl_display);
+ touch->grab_touch_id = touch_id;
+ touch->grab_time = time;
+ touch->grab_x = x;
+ touch->grab_y = y;
+ }
+
+ break;
+ case WL_TOUCH_MOTION:
+ ev = touch->focus;
+ if (!ev)
+ break;
+
+ grab->interface->motion(grab, time, touch_id, x, y);
+ break;
+ case WL_TOUCH_UP:
+ if (touch->num_tp == 0) {
+ /* This can happen if we start out with one or
+ * more fingers on the touch screen, in which
+ * case we didn't get the corresponding down
+ * event. */
+ weston_log("unmatched touch up event\n");
+ break;
+ }
+ weston_compositor_idle_release(ec);
+ touch->num_tp--;
+
+ grab->interface->up(grab, time, touch_id);
+ if (touch->num_tp == 0)
+ weston_touch_set_focus(touch, NULL);
+ break;
+ }
+}
+
+WL_EXPORT void
+notify_touch_frame(struct weston_seat *seat)
+{
+ struct weston_touch *touch = weston_seat_get_touch(seat);
+ struct weston_touch_grab *grab = touch->grab;
+
+ grab->interface->frame(grab);
+}
+
+WL_EXPORT void
+notify_touch_cancel(struct weston_seat *seat)
+{
+ struct weston_touch *touch = weston_seat_get_touch(seat);
+ struct weston_touch_grab *grab = touch->grab;
+
+ grab->interface->cancel(grab);
+}
+
+static int
+pointer_cursor_surface_get_label(struct weston_surface *surface,
+ char *buf, size_t len)
+{
+ return snprintf(buf, len, "cursor");
+}
+
+static void
+pointer_cursor_surface_configure(struct weston_surface *es,
+ int32_t dx, int32_t dy)
+{
+ struct weston_pointer *pointer = es->configure_private;
+ int x, y;
+
+ if (es->width == 0)
+ return;
+
+ assert(es == pointer->sprite->surface);
+
+ pointer->hotspot_x -= dx;
+ pointer->hotspot_y -= dy;
+
+ x = wl_fixed_to_int(pointer->x) - pointer->hotspot_x;
+ y = wl_fixed_to_int(pointer->y) - pointer->hotspot_y;
+
+ weston_view_set_position(pointer->sprite, x, y);
+
+ empty_region(&es->pending.input);
+ empty_region(&es->input);
+
+ if (!weston_surface_is_mapped(es)) {
+ weston_layer_entry_insert(&es->compositor->cursor_layer.view_list,
+ &pointer->sprite->layer_link);
+ weston_view_update_transform(pointer->sprite);
+ }
+}
+
+static void
+pointer_set_cursor(struct wl_client *client, struct wl_resource *resource,
+ uint32_t serial, struct wl_resource *surface_resource,
+ int32_t x, int32_t y)
+{
+ struct weston_pointer *pointer = wl_resource_get_user_data(resource);
+ struct weston_surface *surface = NULL;
+
+ if (surface_resource)
+ surface = wl_resource_get_user_data(surface_resource);
+
+ if (pointer->focus == NULL)
+ return;
+ /* pointer->focus->surface->resource can be NULL. Surfaces like the
+ black_surface used in shell.c for fullscreen don't have
+ a resource, but can still have focus */
+ if (pointer->focus->surface->resource == NULL)
+ return;
+ if (wl_resource_get_client(pointer->focus->surface->resource) != client)
+ return;
+ if (pointer->focus_serial - serial > UINT32_MAX / 2)
+ return;
+
+ if (!surface) {
+ if (pointer->sprite)
+ pointer_unmap_sprite(pointer);
+ return;
+ }
+
+ if (pointer->sprite && pointer->sprite->surface == surface &&
+ pointer->hotspot_x == x && pointer->hotspot_y == y)
+ return;
+
+ if (!pointer->sprite || pointer->sprite->surface != surface) {
+ if (weston_surface_set_role(surface, "wl_pointer-cursor",
+ resource,
+ WL_POINTER_ERROR_ROLE) < 0)
+ return;
+
+ if (pointer->sprite)
+ pointer_unmap_sprite(pointer);
+
+ wl_signal_add(&surface->destroy_signal,
+ &pointer->sprite_destroy_listener);
+
+ surface->configure = pointer_cursor_surface_configure;
+ surface->configure_private = pointer;
+ weston_surface_set_label_func(surface,
+ pointer_cursor_surface_get_label);
+ pointer->sprite = weston_view_create(surface);
+ }
+
+ pointer->hotspot_x = x;
+ pointer->hotspot_y = y;
+
+ if (surface->buffer_ref.buffer) {
+ pointer_cursor_surface_configure(surface, 0, 0);
+ weston_view_schedule_repaint(pointer->sprite);
+ }
+}
+
+static void
+pointer_release(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_pointer_interface pointer_interface = {
+ pointer_set_cursor,
+ pointer_release
+};
+
+static void
+seat_get_pointer(struct wl_client *client, struct wl_resource *resource,
+ uint32_t id)
+{
+ struct weston_seat *seat = wl_resource_get_user_data(resource);
+ /* We use the pointer_state directly, which means we'll
+ * give a wl_pointer if the seat has ever had one - even though
+ * the spec explicitly states that this request only takes effect
+ * if the seat has the pointer capability.
+ *
+ * This prevents a race between the compositor sending new
+ * capabilities and the client trying to use the old ones.
+ */
+ struct weston_pointer *pointer = seat->pointer_state;
+ struct wl_resource *cr;
+ struct weston_pointer_client *pointer_client;
+
+ if (!pointer)
+ return;
+
+ cr = wl_resource_create(client, &wl_pointer_interface,
+ wl_resource_get_version(resource), id);
+ if (cr == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ pointer_client = weston_pointer_ensure_pointer_client(pointer, client);
+ if (!pointer_client) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_list_insert(&pointer_client->pointer_resources,
+ wl_resource_get_link(cr));
+ wl_resource_set_implementation(cr, &pointer_interface, pointer,
+ unbind_pointer_client_resource);
+
+ if (pointer->focus && pointer->focus->surface->resource &&
+ wl_resource_get_client(pointer->focus->surface->resource) == client) {
+ wl_fixed_t sx, sy;
+
+ weston_view_from_global_fixed(pointer->focus,
+ pointer->x,
+ pointer->y,
+ &sx, &sy);
+
+ wl_pointer_send_enter(cr,
+ pointer->focus_serial,
+ pointer->focus->surface->resource,
+ sx, sy);
+ pointer_send_frame(cr);
+ }
+}
+
+static void
+keyboard_release(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_keyboard_interface keyboard_interface = {
+ keyboard_release
+};
+
+static bool
+should_send_modifiers_to_client(struct weston_seat *seat,
+ struct wl_client *client)
+{
+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+
+ if (keyboard &&
+ keyboard->focus &&
+ keyboard->focus->resource &&
+ wl_resource_get_client(keyboard->focus->resource) == client)
+ return true;
+
+ if (pointer &&
+ pointer->focus &&
+ pointer->focus->surface->resource &&
+ wl_resource_get_client(pointer->focus->surface->resource) == client)
+ return true;
+
+ return false;
+}
+
+static void
+seat_get_keyboard(struct wl_client *client, struct wl_resource *resource,
+ uint32_t id)
+{
+ struct weston_seat *seat = wl_resource_get_user_data(resource);
+ /* We use the keyboard_state directly, which means we'll
+ * give a wl_keyboard if the seat has ever had one - even though
+ * the spec explicitly states that this request only takes effect
+ * if the seat has the keyboard capability.
+ *
+ * This prevents a race between the compositor sending new
+ * capabilities and the client trying to use the old ones.
+ */
+ struct weston_keyboard *keyboard = seat->keyboard_state;
+ struct wl_resource *cr;
+
+ if (!keyboard)
+ return;
+
+ cr = wl_resource_create(client, &wl_keyboard_interface,
+ wl_resource_get_version(resource), id);
+ if (cr == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ /* May be moved to focused list later by either
+ * weston_keyboard_set_focus or directly if this client is already
+ * focused */
+ wl_list_insert(&keyboard->resource_list, wl_resource_get_link(cr));
+ wl_resource_set_implementation(cr, &keyboard_interface,
+ seat, unbind_resource);
+
+ if (wl_resource_get_version(cr) >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) {
+ wl_keyboard_send_repeat_info(cr,
+ seat->compositor->kb_repeat_rate,
+ seat->compositor->kb_repeat_delay);
+ }
+
+ if (seat->compositor->use_xkbcommon) {
+ wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+ keyboard->xkb_info->keymap_fd,
+ keyboard->xkb_info->keymap_size);
+ } else {
+ int null_fd = open("/dev/null", O_RDONLY);
+ wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP,
+ null_fd,
+ 0);
+ close(null_fd);
+ }
+
+ if (should_send_modifiers_to_client(seat, client)) {
+ send_modifiers_to_resource(keyboard,
+ cr,
+ keyboard->focus_serial);
+ }
+
+ if (keyboard->focus && keyboard->focus->resource &&
+ wl_resource_get_client(keyboard->focus->resource) == client) {
+ struct weston_surface *surface =
+ (struct weston_surface *)keyboard->focus;
+
+ wl_list_remove(wl_resource_get_link(cr));
+ wl_list_insert(&keyboard->focus_resource_list,
+ wl_resource_get_link(cr));
+ wl_keyboard_send_enter(cr,
+ keyboard->focus_serial,
+ surface->resource,
+ &keyboard->keys);
+
+ /* If this is the first keyboard resource for this
+ * client... */
+ if (keyboard->focus_resource_list.prev ==
+ wl_resource_get_link(cr))
+ wl_data_device_set_keyboard_focus(seat);
+ }
+}
+
+static void
+touch_release(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_touch_interface touch_interface = {
+ touch_release
+};
+
+static void
+seat_get_touch(struct wl_client *client, struct wl_resource *resource,
+ uint32_t id)
+{
+ struct weston_seat *seat = wl_resource_get_user_data(resource);
+ /* We use the touch_state directly, which means we'll
+ * give a wl_touch if the seat has ever had one - even though
+ * the spec explicitly states that this request only takes effect
+ * if the seat has the touch capability.
+ *
+ * This prevents a race between the compositor sending new
+ * capabilities and the client trying to use the old ones.
+ */
+ struct weston_touch *touch = seat->touch_state;
+ struct wl_resource *cr;
+
+ if (!touch)
+ return;
+
+ cr = wl_resource_create(client, &wl_touch_interface,
+ wl_resource_get_version(resource), id);
+ if (cr == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ if (touch->focus &&
+ wl_resource_get_client(touch->focus->surface->resource) == client) {
+ wl_list_insert(&touch->focus_resource_list,
+ wl_resource_get_link(cr));
+ } else {
+ wl_list_insert(&touch->resource_list,
+ wl_resource_get_link(cr));
+ }
+ wl_resource_set_implementation(cr, &touch_interface,
+ seat, unbind_resource);
+}
+
+static void
+seat_release(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_seat_interface seat_interface = {
+ seat_get_pointer,
+ seat_get_keyboard,
+ seat_get_touch,
+ seat_release,
+};
+
+static void
+bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct weston_seat *seat = data;
+ struct wl_resource *resource;
+ enum wl_seat_capability caps = 0;
+
+ resource = wl_resource_create(client,
+ &wl_seat_interface, version, id);
+ wl_list_insert(&seat->base_resource_list, wl_resource_get_link(resource));
+ wl_resource_set_implementation(resource, &seat_interface, data,
+ unbind_resource);
+
+ if (weston_seat_get_pointer(seat))
+ caps |= WL_SEAT_CAPABILITY_POINTER;
+ if (weston_seat_get_keyboard(seat))
+ caps |= WL_SEAT_CAPABILITY_KEYBOARD;
+ if (weston_seat_get_touch(seat))
+ caps |= WL_SEAT_CAPABILITY_TOUCH;
+
+ wl_seat_send_capabilities(resource, caps);
+ if (version >= WL_SEAT_NAME_SINCE_VERSION)
+ wl_seat_send_name(resource, seat->seat_name);
+}
+
+#ifdef ENABLE_XKBCOMMON
+WL_EXPORT int
+weston_compositor_set_xkb_rule_names(struct weston_compositor *ec,
+ struct xkb_rule_names *names)
+{
+ ec->use_xkbcommon = 1;
+
+ if (ec->xkb_context == NULL) {
+ ec->xkb_context = xkb_context_new(0);
+ if (ec->xkb_context == NULL) {
+ weston_log("failed to create XKB context\n");
+ return -1;
+ }
+ }
+
+ if (names)
+ ec->xkb_names = *names;
+ if (!ec->xkb_names.rules)
+ ec->xkb_names.rules = strdup("evdev");
+ if (!ec->xkb_names.model)
+ ec->xkb_names.model = strdup("pc105");
+ if (!ec->xkb_names.layout)
+ ec->xkb_names.layout = strdup("us");
+
+ return 0;
+}
+
+static void
+weston_xkb_info_destroy(struct weston_xkb_info *xkb_info)
+{
+ if (--xkb_info->ref_count > 0)
+ return;
+
+ xkb_keymap_unref(xkb_info->keymap);
+
+ if (xkb_info->keymap_area)
+ munmap(xkb_info->keymap_area, xkb_info->keymap_size);
+ if (xkb_info->keymap_fd >= 0)
+ close(xkb_info->keymap_fd);
+ free(xkb_info);
+}
+
+void
+weston_compositor_xkb_destroy(struct weston_compositor *ec)
+{
+ /*
+ * If we're operating in raw keyboard mode, we never initialized
+ * libxkbcommon so there's no cleanup to do either.
+ */
+ if (!ec->use_xkbcommon)
+ return;
+
+ free((char *) ec->xkb_names.rules);
+ free((char *) ec->xkb_names.model);
+ free((char *) ec->xkb_names.layout);
+ free((char *) ec->xkb_names.variant);
+ free((char *) ec->xkb_names.options);
+
+ if (ec->xkb_info)
+ weston_xkb_info_destroy(ec->xkb_info);
+ xkb_context_unref(ec->xkb_context);
+}
+
+static struct weston_xkb_info *
+weston_xkb_info_create(struct xkb_keymap *keymap)
+{
+ struct weston_xkb_info *xkb_info = zalloc(sizeof *xkb_info);
+ if (xkb_info == NULL)
+ return NULL;
+
+ xkb_info->keymap = xkb_keymap_ref(keymap);
+ xkb_info->ref_count = 1;
+
+ char *keymap_str;
+
+ xkb_info->shift_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
+ XKB_MOD_NAME_SHIFT);
+ xkb_info->caps_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
+ XKB_MOD_NAME_CAPS);
+ xkb_info->ctrl_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
+ XKB_MOD_NAME_CTRL);
+ xkb_info->alt_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
+ XKB_MOD_NAME_ALT);
+ xkb_info->mod2_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
+ "Mod2");
+ xkb_info->mod3_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
+ "Mod3");
+ xkb_info->super_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
+ XKB_MOD_NAME_LOGO);
+ xkb_info->mod5_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
+ "Mod5");
+
+ xkb_info->num_led = xkb_keymap_led_get_index(xkb_info->keymap,
+ XKB_LED_NAME_NUM);
+ xkb_info->caps_led = xkb_keymap_led_get_index(xkb_info->keymap,
+ XKB_LED_NAME_CAPS);
+ xkb_info->scroll_led = xkb_keymap_led_get_index(xkb_info->keymap,
+ XKB_LED_NAME_SCROLL);
+
+ keymap_str = xkb_keymap_get_as_string(xkb_info->keymap,
+ XKB_KEYMAP_FORMAT_TEXT_V1);
+ if (keymap_str == NULL) {
+ weston_log("failed to get string version of keymap\n");
+ goto err_keymap;
+ }
+ xkb_info->keymap_size = strlen(keymap_str) + 1;
+
+ xkb_info->keymap_fd = os_create_anonymous_file(xkb_info->keymap_size);
+ if (xkb_info->keymap_fd < 0) {
+ weston_log("creating a keymap file for %lu bytes failed: %m\n",
+ (unsigned long) xkb_info->keymap_size);
+ goto err_keymap_str;
+ }
+
+ xkb_info->keymap_area = mmap(NULL, xkb_info->keymap_size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, xkb_info->keymap_fd, 0);
+ if (xkb_info->keymap_area == MAP_FAILED) {
+ weston_log("failed to mmap() %lu bytes\n",
+ (unsigned long) xkb_info->keymap_size);
+ goto err_dev_zero;
+ }
+ strcpy(xkb_info->keymap_area, keymap_str);
+ free(keymap_str);
+
+ return xkb_info;
+
+err_dev_zero:
+ close(xkb_info->keymap_fd);
+err_keymap_str:
+ free(keymap_str);
+err_keymap:
+ xkb_keymap_unref(xkb_info->keymap);
+ free(xkb_info);
+ return NULL;
+}
+
+static int
+weston_compositor_build_global_keymap(struct weston_compositor *ec)
+{
+ struct xkb_keymap *keymap;
+
+ if (ec->xkb_info != NULL)
+ return 0;
+
+ keymap = xkb_keymap_new_from_names(ec->xkb_context,
+ &ec->xkb_names,
+ 0);
+ if (keymap == NULL) {
+ weston_log("failed to compile global XKB keymap\n");
+ weston_log(" tried rules %s, model %s, layout %s, variant %s, "
+ "options %s\n",
+ ec->xkb_names.rules, ec->xkb_names.model,
+ ec->xkb_names.layout, ec->xkb_names.variant,
+ ec->xkb_names.options);
+ return -1;
+ }
+
+ ec->xkb_info = weston_xkb_info_create(keymap);
+ xkb_keymap_unref(keymap);
+ if (ec->xkb_info == NULL)
+ return -1;
+
+ return 0;
+}
+#else
+WL_EXPORT int
+weston_compositor_set_xkb_rule_names(struct weston_compositor *ec,
+ struct xkb_rule_names *names)
+{
+ return 0;
+}
+
+void
+weston_compositor_xkb_destroy(struct weston_compositor *ec)
+{
+}
+#endif
+
+WL_EXPORT void
+weston_seat_update_keymap(struct weston_seat *seat, struct xkb_keymap *keymap)
+{
+ struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
+
+ if (!keyboard || !keymap)
+ return;
+
+#ifdef ENABLE_XKBCOMMON
+ if (!seat->compositor->use_xkbcommon)
+ return;
+
+ xkb_keymap_unref(keyboard->pending_keymap);
+ keyboard->pending_keymap = xkb_keymap_ref(keymap);
+
+ if (keyboard->keys.size == 0)
+ update_keymap(seat);
+#endif
+}
+
+WL_EXPORT int
+weston_seat_init_keyboard(struct weston_seat *seat, struct xkb_keymap *keymap)
+{
+ struct weston_keyboard *keyboard;
+
+ if (seat->keyboard_state) {
+ seat->keyboard_device_count += 1;
+ if (seat->keyboard_device_count == 1)
+ seat_send_updated_caps(seat);
+ return 0;
+ }
+
+ keyboard = weston_keyboard_create();
+ if (keyboard == NULL) {
+ weston_log("failed to allocate weston keyboard struct\n");
+ return -1;
+ }
+
+#ifdef ENABLE_XKBCOMMON
+ if (seat->compositor->use_xkbcommon) {
+ if (keymap != NULL) {
+ keyboard->xkb_info = weston_xkb_info_create(keymap);
+ if (keyboard->xkb_info == NULL)
+ goto err;
+ } else {
+ if (weston_compositor_build_global_keymap(seat->compositor) < 0)
+ goto err;
+ keyboard->xkb_info = seat->compositor->xkb_info;
+ keyboard->xkb_info->ref_count++;
+ }
+
+ keyboard->xkb_state.state = xkb_state_new(keyboard->xkb_info->keymap);
+ if (keyboard->xkb_state.state == NULL) {
+ weston_log("failed to initialise XKB state\n");
+ goto err;
+ }
+
+ keyboard->xkb_state.leds = 0;
+ }
+#endif
+
+ seat->keyboard_state = keyboard;
+ seat->keyboard_device_count = 1;
+ keyboard->seat = seat;
+
+ seat_send_updated_caps(seat);
+
+ return 0;
+
+err:
+ if (keyboard->xkb_info)
+ weston_xkb_info_destroy(keyboard->xkb_info);
+ free(keyboard);
+
+ return -1;
+}
+
+static void
+weston_keyboard_reset_state(struct weston_keyboard *keyboard)
+{
+ struct weston_seat *seat = keyboard->seat;
+ struct xkb_state *state;
+
+#ifdef ENABLE_XKBCOMMON
+ if (seat->compositor->use_xkbcommon) {
+ state = xkb_state_new(keyboard->xkb_info->keymap);
+ if (!state) {
+ weston_log("failed to reset XKB state\n");
+ return;
+ }
+ xkb_state_unref(keyboard->xkb_state.state);
+ keyboard->xkb_state.state = state;
+
+ keyboard->xkb_state.leds = 0;
+ }
+#endif
+
+ seat->modifier_state = 0;
+}
+
+WL_EXPORT void
+weston_seat_release_keyboard(struct weston_seat *seat)
+{
+ seat->keyboard_device_count--;
+ assert(seat->keyboard_device_count >= 0);
+ if (seat->keyboard_device_count == 0) {
+ weston_keyboard_set_focus(seat->keyboard_state, NULL);
+ weston_keyboard_cancel_grab(seat->keyboard_state);
+ weston_keyboard_reset_state(seat->keyboard_state);
+ seat_send_updated_caps(seat);
+ }
+}
+
+WL_EXPORT void
+weston_seat_init_pointer(struct weston_seat *seat)
+{
+ struct weston_pointer *pointer;
+
+ if (seat->pointer_state) {
+ seat->pointer_device_count += 1;
+ if (seat->pointer_device_count == 1)
+ seat_send_updated_caps(seat);
+ return;
+ }
+
+ pointer = weston_pointer_create(seat);
+ if (pointer == NULL)
+ return;
+
+ seat->pointer_state = pointer;
+ seat->pointer_device_count = 1;
+ pointer->seat = seat;
+
+ seat_send_updated_caps(seat);
+}
+
+WL_EXPORT void
+weston_seat_release_pointer(struct weston_seat *seat)
+{
+ struct weston_pointer *pointer = seat->pointer_state;
+
+ seat->pointer_device_count--;
+ if (seat->pointer_device_count == 0) {
+ weston_pointer_clear_focus(pointer);
+ weston_pointer_cancel_grab(pointer);
+
+ if (pointer->sprite)
+ pointer_unmap_sprite(pointer);
+
+ weston_pointer_reset_state(pointer);
+ seat_send_updated_caps(seat);
+
+ /* seat->pointer is intentionally not destroyed so that
+ * a newly attached pointer on this seat will retain
+ * the previous cursor co-ordinates.
+ */
+ }
+}
+
+WL_EXPORT void
+weston_seat_init_touch(struct weston_seat *seat)
+{
+ struct weston_touch *touch;
+
+ if (seat->touch_state) {
+ seat->touch_device_count += 1;
+ if (seat->touch_device_count == 1)
+ seat_send_updated_caps(seat);
+ return;
+ }
+
+ touch = weston_touch_create();
+ if (touch == NULL)
+ return;
+
+ seat->touch_state = touch;
+ seat->touch_device_count = 1;
+ touch->seat = seat;
+
+ seat_send_updated_caps(seat);
+}
+
+WL_EXPORT void
+weston_seat_release_touch(struct weston_seat *seat)
+{
+ seat->touch_device_count--;
+ if (seat->touch_device_count == 0) {
+ weston_touch_set_focus(seat->touch_state, NULL);
+ weston_touch_cancel_grab(seat->touch_state);
+ weston_touch_reset_state(seat->touch_state);
+ seat_send_updated_caps(seat);
+ }
+}
+
+WL_EXPORT void
+weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec,
+ const char *seat_name)
+{
+ memset(seat, 0, sizeof *seat);
+
+ seat->selection_data_source = NULL;
+ wl_list_init(&seat->base_resource_list);
+ wl_signal_init(&seat->selection_signal);
+ wl_list_init(&seat->drag_resource_list);
+ wl_signal_init(&seat->destroy_signal);
+ wl_signal_init(&seat->updated_caps_signal);
+
+ seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, 5,
+ seat, bind_seat);
+
+ seat->compositor = ec;
+ seat->modifier_state = 0;
+ seat->seat_name = strdup(seat_name);
+
+ wl_list_insert(ec->seat_list.prev, &seat->link);
+
+ clipboard_create(seat);
+
+ wl_signal_emit(&ec->seat_created_signal, seat);
+}
+
+WL_EXPORT void
+weston_seat_release(struct weston_seat *seat)
+{
+ wl_list_remove(&seat->link);
+
+ if (seat->saved_kbd_focus)
+ wl_list_remove(&seat->saved_kbd_focus_listener.link);
+
+ if (seat->pointer_state)
+ weston_pointer_destroy(seat->pointer_state);
+ if (seat->keyboard_state)
+ weston_keyboard_destroy(seat->keyboard_state);
+ if (seat->touch_state)
+ weston_touch_destroy(seat->touch_state);
+
+ free (seat->seat_name);
+
+ wl_global_destroy(seat->global);
+
+ wl_signal_emit(&seat->destroy_signal, seat);
+}
+
+/** Get a seat's keyboard pointer
+ *
+ * \param seat The seat to query
+ * \return The seat's keyboard pointer, or NULL if no keyboard is present
+ *
+ * The keyboard pointer for a seat isn't freed when all keyboards are removed,
+ * so it should only be used when the seat's keyboard_device_count is greater
+ * than zero. This function does that test and only returns a pointer
+ * when a keyboard is present.
+ */
+WL_EXPORT struct weston_keyboard *
+weston_seat_get_keyboard(struct weston_seat *seat)
+{
+ if (!seat)
+ return NULL;
+
+ if (seat->keyboard_device_count)
+ return seat->keyboard_state;
+
+ return NULL;
+}
+
+/** Get a seat's pointer pointer
+ *
+ * \param seat The seat to query
+ * \return The seat's pointer pointer, or NULL if no pointer device is present
+ *
+ * The pointer pointer for a seat isn't freed when all mice are removed,
+ * so it should only be used when the seat's pointer_device_count is greater
+ * than zero. This function does that test and only returns a pointer
+ * when a pointing device is present.
+ */
+WL_EXPORT struct weston_pointer *
+weston_seat_get_pointer(struct weston_seat *seat)
+{
+ if (!seat)
+ return NULL;
+
+ if (seat->pointer_device_count)
+ return seat->pointer_state;
+
+ return NULL;
+}
+
+/** Get a seat's touch pointer
+ *
+ * \param seat The seat to query
+ * \return The seat's touch pointer, or NULL if no touch device is present
+ *
+ * The touch pointer for a seat isn't freed when all touch devices are removed,
+ * so it should only be used when the seat's touch_device_count is greater
+ * than zero. This function does that test and only returns a pointer
+ * when a touch device is present.
+ */
+WL_EXPORT struct weston_touch *
+weston_seat_get_touch(struct weston_seat *seat)
+{
+ if (!seat)
+ return NULL;
+
+ if (seat->touch_device_count)
+ return seat->touch_state;
+
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright © 2012 Benjamin Franzke
+ * Copyright © 2013 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 "config.h"
+
+#include "compositor.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <linux/vt.h>
+#include <linux/kd.h>
+#include <linux/major.h>
+
+#include "launcher-impl.h"
+
+#define DRM_MAJOR 226
+
+#ifndef KDSKBMUTE
+#define KDSKBMUTE 0x4B51
+#endif
+
+#ifdef HAVE_LIBDRM
+
+#include <xf86drm.h>
+
+static inline int
+is_drm_master(int drm_fd)
+{
+ drm_magic_t magic;
+
+ return drmGetMagic(drm_fd, &magic) == 0 &&
+ drmAuthMagic(drm_fd, magic) == 0;
+}
+
+#else
+
+static inline int
+drmDropMaster(int drm_fd)
+{
+ return 0;
+}
+
+static inline int
+drmSetMaster(int drm_fd)
+{
+ return 0;
+}
+
+static inline int
+is_drm_master(int drm_fd)
+{
+ return 0;
+}
+
+#endif
+
+struct launcher_direct {
+ struct weston_launcher base;
+ struct weston_compositor *compositor;
+ int kb_mode, tty, drm_fd;
+ struct wl_event_source *vt_source;
+};
+
+static int
+vt_handler(int signal_number, void *data)
+{
+ struct launcher_direct *launcher = data;
+ struct weston_compositor *compositor = launcher->compositor;
+
+ if (compositor->session_active) {
+ compositor->session_active = 0;
+ wl_signal_emit(&compositor->session_signal, compositor);
+ drmDropMaster(launcher->drm_fd);
+ ioctl(launcher->tty, VT_RELDISP, 1);
+ } else {
+ ioctl(launcher->tty, VT_RELDISP, VT_ACKACQ);
+ drmSetMaster(launcher->drm_fd);
+ compositor->session_active = 1;
+ wl_signal_emit(&compositor->session_signal, compositor);
+ }
+
+ return 1;
+}
+
+static int
+setup_tty(struct launcher_direct *launcher, int tty)
+{
+ struct wl_event_loop *loop;
+ struct vt_mode mode = { 0 };
+ struct stat buf;
+ char tty_device[32] ="<stdin>";
+ int ret, kd_mode;
+
+ if (tty == 0) {
+ launcher->tty = dup(tty);
+ if (launcher->tty == -1) {
+ weston_log("couldn't dup stdin: %m\n");
+ return -1;
+ }
+ } else {
+ snprintf(tty_device, sizeof tty_device, "/dev/tty%d", tty);
+ launcher->tty = open(tty_device, O_RDWR | O_CLOEXEC);
+ if (launcher->tty == -1) {
+ weston_log("couldn't open tty %s: %m\n", tty_device);
+ return -1;
+ }
+ }
+
+ if (fstat(launcher->tty, &buf) == -1 ||
+ major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0) {
+ weston_log("%s not a vt\n", tty_device);
+ weston_log("if running weston from ssh, "
+ "use --tty to specify a tty\n");
+ goto err_close;
+ }
+
+ ret = ioctl(launcher->tty, KDGETMODE, &kd_mode);
+ if (ret) {
+ weston_log("failed to get VT mode: %m\n");
+ return -1;
+ }
+ if (kd_mode != KD_TEXT) {
+ weston_log("%s is already in graphics mode, "
+ "is another display server running?\n", tty_device);
+ goto err_close;
+ }
+
+ ioctl(launcher->tty, VT_ACTIVATE, minor(buf.st_rdev));
+ ioctl(launcher->tty, VT_WAITACTIVE, minor(buf.st_rdev));
+
+ if (ioctl(launcher->tty, KDGKBMODE, &launcher->kb_mode)) {
+ weston_log("failed to read keyboard mode: %m\n");
+ goto err_close;
+ }
+
+ if (ioctl(launcher->tty, KDSKBMUTE, 1) &&
+ ioctl(launcher->tty, KDSKBMODE, K_OFF)) {
+ weston_log("failed to set K_OFF keyboard mode: %m\n");
+ goto err_close;
+ }
+
+ ret = ioctl(launcher->tty, KDSETMODE, KD_GRAPHICS);
+ if (ret) {
+ weston_log("failed to set KD_GRAPHICS mode on tty: %m\n");
+ goto err_close;
+ }
+
+ /*
+ * SIGRTMIN is used as global VT-acquire+release signal. Note that
+ * SIGRT* must be tested on runtime, as their exact values are not
+ * known at compile-time. POSIX requires 32 of them to be available.
+ */
+ if (SIGRTMIN > SIGRTMAX) {
+ weston_log("not enough RT signals available: %u-%u\n",
+ SIGRTMIN, SIGRTMAX);
+ ret = -EINVAL;
+ goto err_close;
+ }
+
+ mode.mode = VT_PROCESS;
+ mode.relsig = SIGRTMIN;
+ mode.acqsig = SIGRTMIN;
+ if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0) {
+ weston_log("failed to take control of vt handling\n");
+ goto err_close;
+ }
+
+ loop = wl_display_get_event_loop(launcher->compositor->wl_display);
+ launcher->vt_source =
+ wl_event_loop_add_signal(loop, SIGRTMIN, vt_handler, launcher);
+ if (!launcher->vt_source)
+ goto err_close;
+
+ return 0;
+
+ err_close:
+ close(launcher->tty);
+ return -1;
+}
+
+static int
+launcher_direct_open(struct weston_launcher *launcher_base, const char *path, int flags)
+{
+ struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base);
+ struct stat s;
+ int fd;
+
+ fd = open(path, flags | O_CLOEXEC);
+ if (fd == -1)
+ return -1;
+
+ if (fstat(fd, &s) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ if (major(s.st_rdev) == DRM_MAJOR) {
+ launcher->drm_fd = fd;
+ if (!is_drm_master(fd)) {
+ weston_log("drm fd not master\n");
+ close(fd);
+ return -1;
+ }
+ }
+
+ return fd;
+}
+
+static void
+launcher_direct_close(struct weston_launcher *launcher_base, int fd)
+{
+ close(fd);
+}
+
+static void
+launcher_direct_restore(struct weston_launcher *launcher_base)
+{
+ struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base);
+ struct vt_mode mode = { 0 };
+
+ if (ioctl(launcher->tty, KDSKBMUTE, 0) &&
+ ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode))
+ weston_log("failed to restore kb mode: %m\n");
+
+ if (ioctl(launcher->tty, KDSETMODE, KD_TEXT))
+ weston_log("failed to set KD_TEXT mode on tty: %m\n");
+
+ /* We have to drop master before we switch the VT back in
+ * VT_AUTO, so we don't risk switching to a VT with another
+ * display server, that will then fail to set drm master. */
+ drmDropMaster(launcher->drm_fd);
+
+ mode.mode = VT_AUTO;
+ if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0)
+ weston_log("could not reset vt handling\n");
+}
+
+static int
+launcher_direct_activate_vt(struct weston_launcher *launcher_base, int vt)
+{
+ struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base);
+ return ioctl(launcher->tty, VT_ACTIVATE, vt);
+}
+
+static int
+launcher_direct_connect(struct weston_launcher **out, struct weston_compositor *compositor,
+ int tty, const char *seat_id, bool sync_drm)
+{
+ struct launcher_direct *launcher;
+
+ if (geteuid() != 0)
+ return -EINVAL;
+
+ launcher = zalloc(sizeof(*launcher));
+ if (launcher == NULL)
+ return -ENOMEM;
+
+ launcher->base.iface = &launcher_direct_iface;
+ launcher->compositor = compositor;
+
+ if (setup_tty(launcher, tty) == -1) {
+ free(launcher);
+ return -1;
+ }
+
+ * (struct launcher_direct **) out = launcher;
+ return 0;
+}
+
+static void
+launcher_direct_destroy(struct weston_launcher *launcher_base)
+{
+ struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base);
+
+ launcher_direct_restore(&launcher->base);
+ wl_event_source_remove(launcher->vt_source);
+
+ if (launcher->tty >= 0)
+ close(launcher->tty);
+
+ free(launcher);
+}
+
+struct launcher_interface launcher_direct_iface = {
+ launcher_direct_connect,
+ launcher_direct_destroy,
+ launcher_direct_open,
+ launcher_direct_close,
+ launcher_direct_activate_vt,
+ launcher_direct_restore,
+};
--- /dev/null
+/*
+ * Copyright © 2015 Jasper St. Pierre
+ *
+ * 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 "config.h"
+
+#include "compositor.h"
+
+struct weston_launcher;
+
+struct launcher_interface {
+ int (* connect) (struct weston_launcher **launcher_out, struct weston_compositor *compositor,
+ int tty, const char *seat_id, bool sync_drm);
+ void (* destroy) (struct weston_launcher *launcher);
+ int (* open) (struct weston_launcher *launcher, const char *path, int flags);
+ void (* close) (struct weston_launcher *launcher, int fd);
+ int (* activate_vt) (struct weston_launcher *launcher, int vt);
+ void (* restore) (struct weston_launcher *launcher);
+};
+
+struct weston_launcher {
+ struct launcher_interface *iface;
+};
+
+extern struct launcher_interface launcher_logind_iface;
+extern struct launcher_interface launcher_weston_launch_iface;
+extern struct launcher_interface launcher_direct_iface;
--- /dev/null
+/*
+ * Copyright © 2013 David Herrmann
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <systemd/sd-login.h>
+#include <unistd.h>
+
+#include "compositor.h"
+#include "dbus.h"
+#include "launcher-impl.h"
+
+#define DRM_MAJOR 226
+
+struct launcher_logind {
+ struct weston_launcher base;
+ struct weston_compositor *compositor;
+ bool sync_drm;
+ char *seat;
+ char *sid;
+ unsigned int vtnr;
+ int vt;
+ int kb_mode;
+
+ DBusConnection *dbus;
+ struct wl_event_source *dbus_ctx;
+ char *spath;
+ DBusPendingCall *pending_active;
+};
+
+static int
+launcher_logind_take_device(struct launcher_logind *wl, uint32_t major,
+ uint32_t minor, bool *paused_out)
+{
+ DBusMessage *m, *reply;
+ bool b;
+ int r, fd;
+ dbus_bool_t paused;
+
+ m = dbus_message_new_method_call("org.freedesktop.login1",
+ wl->spath,
+ "org.freedesktop.login1.Session",
+ "TakeDevice");
+ if (!m)
+ return -ENOMEM;
+
+ b = dbus_message_append_args(m,
+ DBUS_TYPE_UINT32, &major,
+ DBUS_TYPE_UINT32, &minor,
+ DBUS_TYPE_INVALID);
+ if (!b) {
+ r = -ENOMEM;
+ goto err_unref;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(wl->dbus, m,
+ -1, NULL);
+ if (!reply) {
+ r = -ENODEV;
+ goto err_unref;
+ }
+
+ b = dbus_message_get_args(reply, NULL,
+ DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_BOOLEAN, &paused,
+ DBUS_TYPE_INVALID);
+ if (!b) {
+ r = -ENODEV;
+ goto err_reply;
+ }
+
+ r = fd;
+ if (paused_out)
+ *paused_out = paused;
+
+err_reply:
+ dbus_message_unref(reply);
+err_unref:
+ dbus_message_unref(m);
+ return r;
+}
+
+static void
+launcher_logind_release_device(struct launcher_logind *wl, uint32_t major,
+ uint32_t minor)
+{
+ DBusMessage *m;
+ bool b;
+
+ m = dbus_message_new_method_call("org.freedesktop.login1",
+ wl->spath,
+ "org.freedesktop.login1.Session",
+ "ReleaseDevice");
+ if (m) {
+ b = dbus_message_append_args(m,
+ DBUS_TYPE_UINT32, &major,
+ DBUS_TYPE_UINT32, &minor,
+ DBUS_TYPE_INVALID);
+ if (b)
+ dbus_connection_send(wl->dbus, m, NULL);
+ dbus_message_unref(m);
+ }
+}
+
+static void
+launcher_logind_pause_device_complete(struct launcher_logind *wl, uint32_t major,
+ uint32_t minor)
+{
+ DBusMessage *m;
+ bool b;
+
+ m = dbus_message_new_method_call("org.freedesktop.login1",
+ wl->spath,
+ "org.freedesktop.login1.Session",
+ "PauseDeviceComplete");
+ if (m) {
+ b = dbus_message_append_args(m,
+ DBUS_TYPE_UINT32, &major,
+ DBUS_TYPE_UINT32, &minor,
+ DBUS_TYPE_INVALID);
+ if (b)
+ dbus_connection_send(wl->dbus, m, NULL);
+ dbus_message_unref(m);
+ }
+}
+
+static int
+launcher_logind_open(struct weston_launcher *launcher, const char *path, int flags)
+{
+ struct launcher_logind *wl = wl_container_of(launcher, wl, base);
+ struct stat st;
+ int fl, r, fd;
+
+ r = stat(path, &st);
+ if (r < 0)
+ return -1;
+ if (!S_ISCHR(st.st_mode)) {
+ errno = ENODEV;
+ return -1;
+ }
+
+ fd = launcher_logind_take_device(wl, major(st.st_rdev),
+ minor(st.st_rdev), NULL);
+ if (fd < 0)
+ return fd;
+
+ /* Compared to weston_launcher_open() we cannot specify the open-mode
+ * directly. Instead, logind passes us an fd with sane default modes.
+ * For DRM and evdev this means O_RDWR | O_CLOEXEC. If we want
+ * something else, we need to change it afterwards. We currently
+ * only support setting O_NONBLOCK. Changing access-modes is not
+ * possible so accept whatever logind passes us. */
+
+ fl = fcntl(fd, F_GETFL);
+ if (fl < 0) {
+ r = -errno;
+ goto err_close;
+ }
+
+ if (flags & O_NONBLOCK)
+ fl |= O_NONBLOCK;
+
+ r = fcntl(fd, F_SETFL, fl);
+ if (r < 0) {
+ r = -errno;
+ goto err_close;
+ }
+ return fd;
+
+err_close:
+ close(fd);
+ launcher_logind_release_device(wl, major(st.st_rdev),
+ minor(st.st_rdev));
+ errno = -r;
+ return -1;
+}
+
+static void
+launcher_logind_close(struct weston_launcher *launcher, int fd)
+{
+ struct launcher_logind *wl = wl_container_of(launcher, wl, base);
+ struct stat st;
+ int r;
+
+ r = fstat(fd, &st);
+ if (r < 0) {
+ weston_log("logind: cannot fstat fd: %m\n");
+ return;
+ }
+
+ if (!S_ISCHR(st.st_mode)) {
+ weston_log("logind: invalid device passed\n");
+ return;
+ }
+
+ launcher_logind_release_device(wl, major(st.st_rdev),
+ minor(st.st_rdev));
+}
+
+static void
+launcher_logind_restore(struct weston_launcher *launcher)
+{
+}
+
+static int
+launcher_logind_activate_vt(struct weston_launcher *launcher, int vt)
+{
+ struct launcher_logind *wl = wl_container_of(launcher, wl, base);
+ DBusMessage *m;
+ bool b;
+ int r;
+
+ m = dbus_message_new_method_call("org.freedesktop.login1",
+ "/org/freedesktop/login1/seat/self",
+ "org.freedesktop.login1.Seat",
+ "SwitchTo");
+ if (!m)
+ return -ENOMEM;
+
+ b = dbus_message_append_args(m,
+ DBUS_TYPE_UINT32, &vt,
+ DBUS_TYPE_INVALID);
+ if (!b) {
+ r = -ENOMEM;
+ goto err_unref;
+ }
+
+ dbus_connection_send(wl->dbus, m, NULL);
+ r = 0;
+
+ err_unref:
+ dbus_message_unref(m);
+ return r;
+}
+
+static void
+launcher_logind_set_active(struct launcher_logind *wl, bool active)
+{
+ if (!wl->compositor->session_active == !active)
+ return;
+
+ wl->compositor->session_active = active;
+
+ wl_signal_emit(&wl->compositor->session_signal,
+ wl->compositor);
+}
+
+static void
+parse_active(struct launcher_logind *wl, DBusMessage *m, DBusMessageIter *iter)
+{
+ DBusMessageIter sub;
+ dbus_bool_t b;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
+ return;
+
+ dbus_message_iter_recurse(iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+ return;
+
+ dbus_message_iter_get_basic(&sub, &b);
+
+ /* If the backend requested DRM master-device synchronization, we only
+ * wake-up the compositor once the master-device is up and running. For
+ * other backends, we immediately forward the Active-change event. */
+ if (!wl->sync_drm || !b)
+ launcher_logind_set_active(wl, b);
+}
+
+static void
+get_active_cb(DBusPendingCall *pending, void *data)
+{
+ struct launcher_logind *wl = data;
+ DBusMessageIter iter;
+ DBusMessage *m;
+ int type;
+
+ dbus_pending_call_unref(wl->pending_active);
+ wl->pending_active = NULL;
+
+ m = dbus_pending_call_steal_reply(pending);
+ if (!m)
+ return;
+
+ type = dbus_message_get_type(m);
+ if (type == DBUS_MESSAGE_TYPE_METHOD_RETURN &&
+ dbus_message_iter_init(m, &iter))
+ parse_active(wl, m, &iter);
+
+ dbus_message_unref(m);
+}
+
+static void
+launcher_logind_get_active(struct launcher_logind *wl)
+{
+ DBusPendingCall *pending;
+ DBusMessage *m;
+ bool b;
+ const char *iface, *name;
+
+ m = dbus_message_new_method_call("org.freedesktop.login1",
+ wl->spath,
+ "org.freedesktop.DBus.Properties",
+ "Get");
+ if (!m)
+ return;
+
+ iface = "org.freedesktop.login1.Session";
+ name = "Active";
+ b = dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &iface,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID);
+ if (!b)
+ goto err_unref;
+
+ b = dbus_connection_send_with_reply(wl->dbus, m, &pending, -1);
+ if (!b)
+ goto err_unref;
+
+ b = dbus_pending_call_set_notify(pending, get_active_cb, wl, NULL);
+ if (!b) {
+ dbus_pending_call_cancel(pending);
+ dbus_pending_call_unref(pending);
+ goto err_unref;
+ }
+
+ if (wl->pending_active) {
+ dbus_pending_call_cancel(wl->pending_active);
+ dbus_pending_call_unref(wl->pending_active);
+ }
+ wl->pending_active = pending;
+ return;
+
+err_unref:
+ dbus_message_unref(m);
+}
+
+static void
+disconnected_dbus(struct launcher_logind *wl)
+{
+ weston_log("logind: dbus connection lost, exiting..\n");
+ launcher_logind_restore(&wl->base);
+ exit(-1);
+}
+
+static void
+session_removed(struct launcher_logind *wl, DBusMessage *m)
+{
+ const char *name, *obj;
+ bool r;
+
+ r = dbus_message_get_args(m, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_OBJECT_PATH, &obj,
+ DBUS_TYPE_INVALID);
+ if (!r) {
+ weston_log("logind: cannot parse SessionRemoved dbus signal\n");
+ return;
+ }
+
+ if (!strcmp(name, wl->sid)) {
+ weston_log("logind: our session got closed, exiting..\n");
+ launcher_logind_restore(&wl->base);
+ exit(-1);
+ }
+}
+
+static void
+property_changed(struct launcher_logind *wl, DBusMessage *m)
+{
+ DBusMessageIter iter, sub, entry;
+ const char *interface, *name;
+
+ if (!dbus_message_iter_init(m, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ goto error;
+
+ dbus_message_iter_get_basic(&iter, &interface);
+
+ if (!dbus_message_iter_next(&iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ goto error;
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY) {
+ dbus_message_iter_recurse(&sub, &entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ goto error;
+
+ dbus_message_iter_get_basic(&entry, &name);
+ if (!dbus_message_iter_next(&entry))
+ goto error;
+
+ if (!strcmp(name, "Active")) {
+ parse_active(wl, m, &entry);
+ return;
+ }
+
+ dbus_message_iter_next(&sub);
+ }
+
+ if (!dbus_message_iter_next(&iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ goto error;
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
+ dbus_message_iter_get_basic(&sub, &name);
+
+ if (!strcmp(name, "Active")) {
+ launcher_logind_get_active(wl);
+ return;
+ }
+
+ dbus_message_iter_next(&sub);
+ }
+
+ return;
+
+error:
+ weston_log("logind: cannot parse PropertiesChanged dbus signal\n");
+}
+
+static void
+device_paused(struct launcher_logind *wl, DBusMessage *m)
+{
+ bool r;
+ const char *type;
+ uint32_t major, minor;
+
+ r = dbus_message_get_args(m, NULL,
+ DBUS_TYPE_UINT32, &major,
+ DBUS_TYPE_UINT32, &minor,
+ DBUS_TYPE_STRING, &type,
+ DBUS_TYPE_INVALID);
+ if (!r) {
+ weston_log("logind: cannot parse PauseDevice dbus signal\n");
+ return;
+ }
+
+ /* "pause" means synchronous pausing. Acknowledge it unconditionally
+ * as we support asynchronous device shutdowns, anyway.
+ * "force" means asynchronous pausing.
+ * "gone" means the device is gone. We handle it the same as "force" as
+ * a following udev event will be caught, too.
+ *
+ * If it's our main DRM device, tell the compositor to go asleep. */
+
+ if (!strcmp(type, "pause"))
+ launcher_logind_pause_device_complete(wl, major, minor);
+
+ if (wl->sync_drm && major == DRM_MAJOR)
+ launcher_logind_set_active(wl, false);
+}
+
+static void
+device_resumed(struct launcher_logind *wl, DBusMessage *m)
+{
+ bool r;
+ uint32_t major;
+
+ r = dbus_message_get_args(m, NULL,
+ DBUS_TYPE_UINT32, &major,
+ /*DBUS_TYPE_UINT32, &minor,
+ DBUS_TYPE_UNIX_FD, &fd,*/
+ DBUS_TYPE_INVALID);
+ if (!r) {
+ weston_log("logind: cannot parse ResumeDevice dbus signal\n");
+ return;
+ }
+
+ /* DeviceResumed messages provide us a new file-descriptor for
+ * resumed devices. For DRM devices it's the same as before, for evdev
+ * devices it's a new open-file. As we reopen evdev devices, anyway,
+ * there is no need for us to handle this event for evdev. For DRM, we
+ * notify the compositor to wake up. */
+
+ if (wl->sync_drm && major == DRM_MAJOR)
+ launcher_logind_set_active(wl, true);
+}
+
+static DBusHandlerResult
+filter_dbus(DBusConnection *c, DBusMessage *m, void *data)
+{
+ struct launcher_logind *wl = data;
+
+ if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) {
+ disconnected_dbus(wl);
+ } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Manager",
+ "SessionRemoved")) {
+ session_removed(wl, m);
+ } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties",
+ "PropertiesChanged")) {
+ property_changed(wl, m);
+ } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session",
+ "PauseDevice")) {
+ device_paused(wl, m);
+ } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session",
+ "ResumeDevice")) {
+ device_resumed(wl, m);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static int
+launcher_logind_setup_dbus(struct launcher_logind *wl)
+{
+ bool b;
+ int r;
+
+ r = asprintf(&wl->spath, "/org/freedesktop/login1/session/%s",
+ wl->sid);
+ if (r < 0)
+ return -ENOMEM;
+
+ b = dbus_connection_add_filter(wl->dbus, filter_dbus, wl, NULL);
+ if (!b) {
+ weston_log("logind: cannot add dbus filter\n");
+ r = -ENOMEM;
+ goto err_spath;
+ }
+
+ r = weston_dbus_add_match_signal(wl->dbus,
+ "org.freedesktop.login1",
+ "org.freedesktop.login1.Manager",
+ "SessionRemoved",
+ "/org/freedesktop/login1");
+ if (r < 0) {
+ weston_log("logind: cannot add dbus match\n");
+ goto err_spath;
+ }
+
+ r = weston_dbus_add_match_signal(wl->dbus,
+ "org.freedesktop.login1",
+ "org.freedesktop.login1.Session",
+ "PauseDevice",
+ wl->spath);
+ if (r < 0) {
+ weston_log("logind: cannot add dbus match\n");
+ goto err_spath;
+ }
+
+ r = weston_dbus_add_match_signal(wl->dbus,
+ "org.freedesktop.login1",
+ "org.freedesktop.login1.Session",
+ "ResumeDevice",
+ wl->spath);
+ if (r < 0) {
+ weston_log("logind: cannot add dbus match\n");
+ goto err_spath;
+ }
+
+ r = weston_dbus_add_match_signal(wl->dbus,
+ "org.freedesktop.login1",
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ wl->spath);
+ if (r < 0) {
+ weston_log("logind: cannot add dbus match\n");
+ goto err_spath;
+ }
+
+ return 0;
+
+err_spath:
+ /* don't remove any dbus-match as the connection is closed, anyway */
+ free(wl->spath);
+ return r;
+}
+
+static void
+launcher_logind_destroy_dbus(struct launcher_logind *wl)
+{
+ /* don't remove any dbus-match as the connection is closed, anyway */
+ free(wl->spath);
+}
+
+static int
+launcher_logind_take_control(struct launcher_logind *wl)
+{
+ DBusError err;
+ DBusMessage *m, *reply;
+ dbus_bool_t force;
+ bool b;
+ int r;
+
+ dbus_error_init(&err);
+
+ m = dbus_message_new_method_call("org.freedesktop.login1",
+ wl->spath,
+ "org.freedesktop.login1.Session",
+ "TakeControl");
+ if (!m)
+ return -ENOMEM;
+
+ force = false;
+ b = dbus_message_append_args(m,
+ DBUS_TYPE_BOOLEAN, &force,
+ DBUS_TYPE_INVALID);
+ if (!b) {
+ r = -ENOMEM;
+ goto err_unref;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(wl->dbus,
+ m, -1, &err);
+ if (!reply) {
+ if (dbus_error_has_name(&err, DBUS_ERROR_UNKNOWN_METHOD))
+ weston_log("logind: old systemd version detected\n");
+ else
+ weston_log("logind: cannot take control over session %s\n", wl->sid);
+
+ dbus_error_free(&err);
+ r = -EIO;
+ goto err_unref;
+ }
+
+ dbus_message_unref(reply);
+ dbus_message_unref(m);
+ return 0;
+
+err_unref:
+ dbus_message_unref(m);
+ return r;
+}
+
+static void
+launcher_logind_release_control(struct launcher_logind *wl)
+{
+ DBusMessage *m;
+
+ m = dbus_message_new_method_call("org.freedesktop.login1",
+ wl->spath,
+ "org.freedesktop.login1.Session",
+ "ReleaseControl");
+ if (m) {
+ dbus_connection_send(wl->dbus, m, NULL);
+ dbus_message_unref(m);
+ }
+}
+
+static int
+weston_sd_session_get_vt(const char *sid, unsigned int *out)
+{
+#ifdef HAVE_SYSTEMD_LOGIN_209
+ return sd_session_get_vt(sid, out);
+#else
+ int r;
+ char *tty;
+
+ r = sd_session_get_tty(sid, &tty);
+ if (r < 0)
+ return r;
+
+ r = sscanf(tty, "tty%u", out);
+ free(tty);
+
+ if (r != 1)
+ return -EINVAL;
+
+ return 0;
+#endif
+}
+
+static int
+launcher_logind_activate(struct launcher_logind *wl)
+{
+ DBusMessage *m;
+
+ m = dbus_message_new_method_call("org.freedesktop.login1",
+ wl->spath,
+ "org.freedesktop.login1.Session",
+ "Activate");
+ if (!m)
+ return -ENOMEM;
+
+ dbus_connection_send(wl->dbus, m, NULL);
+ return 0;
+}
+
+static int
+launcher_logind_connect(struct weston_launcher **out, struct weston_compositor *compositor,
+ int tty, const char *seat_id, bool sync_drm)
+{
+ struct launcher_logind *wl;
+ struct wl_event_loop *loop;
+ char *t;
+ int r;
+
+ wl = zalloc(sizeof(*wl));
+ if (wl == NULL) {
+ r = -ENOMEM;
+ goto err_out;
+ }
+
+ wl->base.iface = &launcher_logind_iface;
+ wl->compositor = compositor;
+ wl->sync_drm = sync_drm;
+
+ wl->seat = strdup(seat_id);
+ if (!wl->seat) {
+ r = -ENOMEM;
+ goto err_wl;
+ }
+
+ r = sd_pid_get_session(getpid(), &wl->sid);
+ if (r < 0) {
+ weston_log("logind: not running in a systemd session\n");
+ goto err_seat;
+ }
+
+ t = NULL;
+ r = sd_session_get_seat(wl->sid, &t);
+ if (r < 0) {
+ weston_log("logind: failed to get session seat\n");
+ free(t);
+ goto err_session;
+ } else if (strcmp(seat_id, t)) {
+ weston_log("logind: weston's seat '%s' differs from session-seat '%s'\n",
+ seat_id, t);
+ r = -EINVAL;
+ free(t);
+ goto err_session;
+ }
+ free(t);
+
+ r = weston_sd_session_get_vt(wl->sid, &wl->vtnr);
+ if (r < 0) {
+ weston_log("logind: session not running on a VT\n");
+ goto err_session;
+ } else if (tty > 0 && wl->vtnr != (unsigned int )tty) {
+ weston_log("logind: requested VT --tty=%d differs from real session VT %u\n",
+ tty, wl->vtnr);
+ r = -EINVAL;
+ goto err_session;
+ }
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+ r = weston_dbus_open(loop, DBUS_BUS_SYSTEM, &wl->dbus, &wl->dbus_ctx);
+ if (r < 0) {
+ weston_log("logind: cannot connect to system dbus\n");
+ goto err_session;
+ }
+
+ r = launcher_logind_setup_dbus(wl);
+ if (r < 0)
+ goto err_dbus;
+
+ r = launcher_logind_take_control(wl);
+ if (r < 0)
+ goto err_dbus_cleanup;
+
+ r = launcher_logind_activate(wl);
+ if (r < 0)
+ goto err_dbus_cleanup;
+
+ weston_log("logind: session control granted\n");
+ * (struct launcher_logind **) out = wl;
+ return 0;
+
+err_dbus_cleanup:
+ launcher_logind_destroy_dbus(wl);
+err_dbus:
+ weston_dbus_close(wl->dbus, wl->dbus_ctx);
+err_session:
+ free(wl->sid);
+err_seat:
+ free(wl->seat);
+err_wl:
+ free(wl);
+err_out:
+ weston_log("logind: cannot setup systemd-logind helper (%d), using legacy fallback\n", r);
+ errno = -r;
+ return -1;
+}
+
+static void
+launcher_logind_destroy(struct weston_launcher *launcher)
+{
+ struct launcher_logind *wl = wl_container_of(launcher, wl, base);
+
+ if (wl->pending_active) {
+ dbus_pending_call_cancel(wl->pending_active);
+ dbus_pending_call_unref(wl->pending_active);
+ }
+
+ launcher_logind_release_control(wl);
+ launcher_logind_destroy_dbus(wl);
+ weston_dbus_close(wl->dbus, wl->dbus_ctx);
+ free(wl->sid);
+ free(wl->seat);
+ free(wl);
+}
+
+struct launcher_interface launcher_logind_iface = {
+ launcher_logind_connect,
+ launcher_logind_destroy,
+ launcher_logind_open,
+ launcher_logind_close,
+ launcher_logind_activate_vt,
+ launcher_logind_restore,
+};
--- /dev/null
+/*
+ * Copyright © 2012 Benjamin Franzke
+ * Copyright © 2013 Intel Corporation
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include "compositor.h"
+
+#include "launcher-util.h"
+#include "launcher-impl.h"
+
+#include <unistd.h>
+#include <linux/input.h>
+
+static struct launcher_interface *ifaces[] = {
+#ifdef HAVE_SYSTEMD_LOGIN
+ &launcher_logind_iface,
+#endif
+ &launcher_weston_launch_iface,
+ &launcher_direct_iface,
+ NULL,
+};
+
+WL_EXPORT struct weston_launcher *
+weston_launcher_connect(struct weston_compositor *compositor, int tty,
+ const char *seat_id, bool sync_drm)
+{
+ struct launcher_interface **it;
+
+ for (it = ifaces; *it != NULL; it++) {
+ struct launcher_interface *iface = *it;
+ struct weston_launcher *launcher;
+
+ if (iface->connect(&launcher, compositor, tty, seat_id, sync_drm) == 0)
+ return launcher;
+ }
+
+ return NULL;
+}
+
+WL_EXPORT void
+weston_launcher_destroy(struct weston_launcher *launcher)
+{
+ launcher->iface->destroy(launcher);
+}
+
+WL_EXPORT int
+weston_launcher_open(struct weston_launcher *launcher,
+ const char *path, int flags)
+{
+ return launcher->iface->open(launcher, path, flags);
+}
+
+WL_EXPORT void
+weston_launcher_close(struct weston_launcher *launcher, int fd)
+{
+ launcher->iface->close(launcher, fd);
+}
+
+WL_EXPORT int
+weston_launcher_activate_vt(struct weston_launcher *launcher, int vt)
+{
+ return launcher->iface->activate_vt(launcher, vt);
+}
+
+WL_EXPORT void
+weston_launcher_restore(struct weston_launcher *launcher)
+{
+ launcher->iface->restore(launcher);
+}
+
+
+static void
+switch_vt_binding(struct weston_keyboard *keyboard,
+ uint32_t time, uint32_t key, void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ weston_launcher_activate_vt(compositor->launcher, key - KEY_F1 + 1);
+}
+
+WL_EXPORT void
+weston_setup_vt_switch_bindings(struct weston_compositor *compositor)
+{
+ uint32_t key;
+
+ if (compositor->vt_switching == false)
+ return;
+
+ for (key = KEY_F1; key < KEY_F9; key++)
+ weston_compositor_add_key_binding(compositor, key,
+ MODIFIER_CTRL | MODIFIER_ALT,
+ switch_vt_binding,
+ compositor);
+}
--- /dev/null
+/*
+ * Copyright © 2012 Benjamin Franzke
+ *
+ * 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.
+ */
+
+#ifndef _WESTON_LAUNCHER_UTIL_H_
+#define _WESTON_LAUNCHER_UTIL_H_
+
+#include "config.h"
+
+#include "compositor.h"
+
+struct weston_launcher;
+
+struct weston_launcher *
+weston_launcher_connect(struct weston_compositor *compositor, int tty,
+ const char *seat_id, bool sync_drm);
+
+void
+weston_launcher_destroy(struct weston_launcher *launcher);
+
+int
+weston_launcher_open(struct weston_launcher *launcher,
+ const char *path, int flags);
+
+void
+weston_launcher_close(struct weston_launcher *launcher, int fd);
+
+int
+weston_launcher_activate_vt(struct weston_launcher *launcher, int vt);
+
+void
+weston_launcher_restore(struct weston_launcher *launcher);
+
+void
+weston_setup_vt_switch_bindings(struct weston_compositor *compositor);
+
+#endif
--- /dev/null
+/*
+ * Copyright © 2012 Benjamin Franzke
+ * Copyright © 2013 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 "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/vt.h>
+#include <linux/kd.h>
+#include <linux/major.h>
+
+#include "compositor.h"
+#include "weston-launch.h"
+#include "launcher-impl.h"
+
+#define DRM_MAJOR 226
+
+#ifndef KDSKBMUTE
+#define KDSKBMUTE 0x4B51
+#endif
+
+#ifdef HAVE_LIBDRM
+
+#include <xf86drm.h>
+
+static inline int
+is_drm_master(int drm_fd)
+{
+ drm_magic_t magic;
+
+ return drmGetMagic(drm_fd, &magic) == 0 &&
+ drmAuthMagic(drm_fd, magic) == 0;
+}
+
+#else
+
+static inline int
+drmDropMaster(int drm_fd)
+{
+ return 0;
+}
+
+static inline int
+drmSetMaster(int drm_fd)
+{
+ return 0;
+}
+
+static inline int
+is_drm_master(int drm_fd)
+{
+ return 0;
+}
+
+#endif
+
+
+union cmsg_data { unsigned char b[4]; int fd; };
+
+struct launcher_weston_launch {
+ struct weston_launcher base;
+ struct weston_compositor *compositor;
+ struct wl_event_loop *loop;
+ int fd;
+ struct wl_event_source *source;
+
+ int kb_mode, tty, drm_fd;
+};
+
+static int
+launcher_weston_launch_open(struct weston_launcher *launcher_base,
+ const char *path, int flags)
+{
+ struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base);
+ int n, ret;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ union cmsg_data *data;
+ char control[CMSG_SPACE(sizeof data->fd)];
+ ssize_t len;
+ struct weston_launcher_open *message;
+
+ n = sizeof(*message) + strlen(path) + 1;
+ message = malloc(n);
+ if (!message)
+ return -1;
+
+ message->header.opcode = WESTON_LAUNCHER_OPEN;
+ message->flags = flags;
+ strcpy(message->path, path);
+
+ do {
+ len = send(launcher->fd, message, n, 0);
+ } while (len < 0 && errno == EINTR);
+ free(message);
+
+ memset(&msg, 0, sizeof msg);
+ iov.iov_base = &ret;
+ iov.iov_len = sizeof ret;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof control;
+
+ do {
+ len = recvmsg(launcher->fd, &msg, MSG_CMSG_CLOEXEC);
+ } while (len < 0 && errno == EINTR);
+
+ if (len != sizeof ret ||
+ ret < 0)
+ return -1;
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (!cmsg ||
+ cmsg->cmsg_level != SOL_SOCKET ||
+ cmsg->cmsg_type != SCM_RIGHTS) {
+ fprintf(stderr, "invalid control message\n");
+ return -1;
+ }
+
+ data = (union cmsg_data *) CMSG_DATA(cmsg);
+ if (data->fd == -1) {
+ fprintf(stderr, "missing drm fd in socket request\n");
+ return -1;
+ }
+
+ return data->fd;
+}
+
+static void
+launcher_weston_launch_close(struct weston_launcher *launcher_base, int fd)
+{
+ close(fd);
+}
+
+static void
+launcher_weston_launch_restore(struct weston_launcher *launcher_base)
+{
+ struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base);
+ struct vt_mode mode = { 0 };
+
+ if (ioctl(launcher->tty, KDSKBMUTE, 0) &&
+ ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode))
+ weston_log("failed to restore kb mode: %m\n");
+
+ if (ioctl(launcher->tty, KDSETMODE, KD_TEXT))
+ weston_log("failed to set KD_TEXT mode on tty: %m\n");
+
+ /* We have to drop master before we switch the VT back in
+ * VT_AUTO, so we don't risk switching to a VT with another
+ * display server, that will then fail to set drm master. */
+ drmDropMaster(launcher->drm_fd);
+
+ mode.mode = VT_AUTO;
+ if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0)
+ weston_log("could not reset vt handling\n");
+}
+
+static int
+launcher_weston_launch_data(int fd, uint32_t mask, void *data)
+{
+ struct launcher_weston_launch *launcher = data;
+ int len, ret;
+
+ if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
+ weston_log("launcher socket closed, exiting\n");
+ /* Normally the weston-launch will reset the tty, but
+ * in this case it died or something, so do it here so
+ * we don't end up with a stuck vt. */
+ launcher_weston_launch_restore(&launcher->base);
+ exit(-1);
+ }
+
+ do {
+ len = recv(launcher->fd, &ret, sizeof ret, 0);
+ } while (len < 0 && errno == EINTR);
+
+ switch (ret) {
+ case WESTON_LAUNCHER_ACTIVATE:
+ launcher->compositor->session_active = 1;
+ wl_signal_emit(&launcher->compositor->session_signal,
+ launcher->compositor);
+ break;
+ case WESTON_LAUNCHER_DEACTIVATE:
+ launcher->compositor->session_active = 0;
+ wl_signal_emit(&launcher->compositor->session_signal,
+ launcher->compositor);
+ break;
+ default:
+ weston_log("unexpected event from weston-launch\n");
+ break;
+ }
+
+ return 1;
+}
+
+static int
+launcher_weston_launch_activate_vt(struct weston_launcher *launcher_base, int vt)
+{
+ struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base);
+ return ioctl(launcher->tty, VT_ACTIVATE, vt);
+}
+
+static int
+launcher_weston_launch_connect(struct weston_launcher **out, struct weston_compositor *compositor,
+ int tty, const char *seat_id, bool sync_drm)
+{
+ struct launcher_weston_launch *launcher;
+ struct wl_event_loop *loop;
+
+ launcher = malloc(sizeof *launcher);
+ if (launcher == NULL)
+ return -ENOMEM;
+
+ launcher->base.iface = &launcher_weston_launch_iface;
+ * (struct launcher_weston_launch **) out = launcher;
+ launcher->compositor = compositor;
+ launcher->drm_fd = -1;
+ launcher->fd = weston_environment_get_fd("WESTON_LAUNCHER_SOCK");
+ if (launcher->fd != -1) {
+ launcher->tty = weston_environment_get_fd("WESTON_TTY_FD");
+ /* We don't get a chance to read out the original kb
+ * mode for the tty, so just hard code K_UNICODE here
+ * in case we have to clean if weston-launch dies. */
+ launcher->kb_mode = K_UNICODE;
+
+ loop = wl_display_get_event_loop(compositor->wl_display);
+ launcher->source = wl_event_loop_add_fd(loop, launcher->fd,
+ WL_EVENT_READABLE,
+ launcher_weston_launch_data,
+ launcher);
+ if (launcher->source == NULL) {
+ free(launcher);
+ return -ENOMEM;
+ }
+
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static void
+launcher_weston_launch_destroy(struct weston_launcher *launcher_base)
+{
+ struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base);
+
+ if (launcher->fd != -1) {
+ close(launcher->fd);
+ wl_event_source_remove(launcher->source);
+ } else {
+ launcher_weston_launch_restore(&launcher->base);
+ }
+
+ if (launcher->tty >= 0)
+ close(launcher->tty);
+
+ free(launcher);
+}
+
+struct launcher_interface launcher_weston_launch_iface = {
+ launcher_weston_launch_connect,
+ launcher_weston_launch_destroy,
+ launcher_weston_launch_open,
+ launcher_weston_launch_close,
+ launcher_weston_launch_activate_vt,
+ launcher_weston_launch_restore,
+};
--- /dev/null
+/*
+ * libbacklight - userspace interface to Linux backlight control
+ *
+ * Copyright © 2012 Intel Corporation
+ * Copyright 2010 Red Hat <mjg@redhat.com>
+ *
+ * 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.
+ *
+ * Authors:
+ * Matthew Garrett <mjg@redhat.com>
+ * Tiago Vignatti <vignatti@freedesktop.org>
+ */
+
+#include "config.h"
+
+#include "libbacklight.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <linux/types.h>
+#include <dirent.h>
+#include <drm.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <string.h>
+#include <errno.h>
+
+static long backlight_get(struct backlight *backlight, char *node)
+{
+ char buffer[100];
+ char *path;
+ int fd;
+ long value, ret;
+
+ if (asprintf(&path, "%s/%s", backlight->path, node) < 0)
+ return -ENOMEM;
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = read(fd, &buffer, sizeof(buffer));
+ if (ret < 1) {
+ ret = -1;
+ goto out;
+ }
+
+ value = strtol(buffer, NULL, 10);
+ ret = value;
+out:
+ if (fd >= 0)
+ close(fd);
+ free(path);
+ return ret;
+}
+
+long backlight_get_brightness(struct backlight *backlight)
+{
+ return backlight_get(backlight, "brightness");
+}
+
+long backlight_get_max_brightness(struct backlight *backlight)
+{
+ return backlight_get(backlight, "max_brightness");
+}
+
+long backlight_get_actual_brightness(struct backlight *backlight)
+{
+ return backlight_get(backlight, "actual_brightness");
+}
+
+long backlight_set_brightness(struct backlight *backlight, long brightness)
+{
+ char *path;
+ char *buffer = NULL;
+ int fd;
+ long ret;
+
+ if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0)
+ return -ENOMEM;
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = read(fd, &buffer, sizeof(buffer));
+ if (ret < 1) {
+ ret = -1;
+ goto out;
+ }
+
+ if (asprintf(&buffer, "%ld", brightness) < 0) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = write(fd, buffer, strlen(buffer));
+ if (ret < 0) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = backlight_get_brightness(backlight);
+ backlight->brightness = ret;
+out:
+ free(buffer);
+ free(path);
+ if (fd >= 0)
+ close(fd);
+ return ret;
+}
+
+void backlight_destroy(struct backlight *backlight)
+{
+ if (!backlight)
+ return;
+
+ if (backlight->path)
+ free(backlight->path);
+
+ free(backlight);
+}
+
+struct backlight *backlight_init(struct udev_device *drm_device,
+ uint32_t connector_type)
+{
+ const char *syspath = NULL;
+ char *pci_name = NULL;
+ char *chosen_path = NULL;
+ char *path = NULL;
+ DIR *backlights = NULL;
+ struct dirent *entry;
+ enum backlight_type type = 0;
+ char buffer[100];
+ struct backlight *backlight = NULL;
+ int ret;
+
+ if (!drm_device)
+ return NULL;
+
+ syspath = udev_device_get_syspath(drm_device);
+ if (!syspath)
+ return NULL;
+
+ if (asprintf(&path, "%s/%s", syspath, "device") < 0)
+ return NULL;
+
+ ret = readlink(path, buffer, sizeof(buffer) - 1);
+ free(path);
+ if (ret < 0)
+ return NULL;
+
+ buffer[ret] = '\0';
+ pci_name = basename(buffer);
+
+ if (connector_type <= 0)
+ return NULL;
+
+ backlights = opendir("/sys/class/backlight");
+ if (!backlights)
+ return NULL;
+
+ /* Find the "best" backlight for the device. Firmware
+ interfaces are preferred over platform interfaces are
+ preferred over raw interfaces. For raw interfaces we'll
+ check if the device ID in the form of pci match, while
+ for firmware interfaces we require the pci ID to
+ match. It's assumed that platform interfaces always match,
+ since we can't actually associate them with IDs.
+
+ A further awkwardness is that, while it's theoretically
+ possible for an ACPI interface to include support for
+ changing the backlight of external devices, it's unlikely
+ to ever be done. It's effectively impossible for a platform
+ interface to do so. So if we get asked about anything that
+ isn't LVDS or eDP, we pretty much have to require that the
+ control be supplied via a raw interface */
+
+ while ((entry = readdir(backlights))) {
+ char *backlight_path;
+ char *parent;
+ enum backlight_type entry_type;
+ int fd;
+
+ if (entry->d_name[0] == '.')
+ continue;
+
+ if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight",
+ entry->d_name) < 0)
+ goto err;
+
+ if (asprintf(&path, "%s/%s", backlight_path, "type") < 0) {
+ free(backlight_path);
+ goto err;
+ }
+
+ fd = open(path, O_RDONLY);
+
+ if (fd < 0)
+ goto out;
+
+ ret = read (fd, &buffer, sizeof(buffer));
+ close (fd);
+
+ if (ret < 1)
+ goto out;
+
+ buffer[ret] = '\0';
+
+ if (!strncmp(buffer, "raw\n", sizeof(buffer)))
+ entry_type = BACKLIGHT_RAW;
+ else if (!strncmp(buffer, "platform\n", sizeof(buffer)))
+ entry_type = BACKLIGHT_PLATFORM;
+ else if (!strncmp(buffer, "firmware\n", sizeof(buffer)))
+ entry_type = BACKLIGHT_FIRMWARE;
+ else
+ goto out;
+
+ if (connector_type != DRM_MODE_CONNECTOR_LVDS &&
+ connector_type != DRM_MODE_CONNECTOR_eDP) {
+ /* External displays are assumed to require
+ gpu control at the moment */
+ if (entry_type != BACKLIGHT_RAW)
+ goto out;
+ }
+
+ free (path);
+
+ if (asprintf(&path, "%s/%s", backlight_path, "device") < 0)
+ goto err;
+
+ ret = readlink(path, buffer, sizeof(buffer) - 1);
+
+ if (ret < 0)
+ goto out;
+
+ buffer[ret] = '\0';
+
+ parent = basename(buffer);
+
+ /* Perform matching for raw and firmware backlights -
+ platform backlights have to be assumed to match */
+ if (entry_type == BACKLIGHT_RAW ||
+ entry_type == BACKLIGHT_FIRMWARE) {
+ if (!(pci_name && !strcmp(pci_name, parent)))
+ goto out;
+ }
+
+ if (entry_type < type)
+ goto out;
+
+ type = entry_type;
+
+ if (chosen_path)
+ free(chosen_path);
+ chosen_path = strdup(backlight_path);
+
+ out:
+ free(backlight_path);
+ free(path);
+ }
+
+ if (!chosen_path)
+ goto err;
+
+ backlight = malloc(sizeof(struct backlight));
+
+ if (!backlight)
+ goto err;
+
+ backlight->path = chosen_path;
+ backlight->type = type;
+
+ backlight->max_brightness = backlight_get_max_brightness(backlight);
+ if (backlight->max_brightness < 0)
+ goto err;
+
+ backlight->brightness = backlight_get_actual_brightness(backlight);
+ if (backlight->brightness < 0)
+ goto err;
+
+ closedir(backlights);
+ return backlight;
+err:
+ closedir(backlights);
+ free (chosen_path);
+ free (backlight);
+ return NULL;
+}
--- /dev/null
+/*
+ * libbacklight - userspace interface to Linux backlight control
+ *
+ * Copyright © 2012 Intel Corporation
+ * Copyright 2010 Red Hat <mjg@redhat.com>
+ *
+ * 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.
+ *
+ * Authors:
+ * Matthew Garrett <mjg@redhat.com>
+ * Tiago Vignatti <vignatti@freedesktop.org>
+ */
+#ifndef LIBBACKLIGHT_H
+#define LIBBACKLIGHT_H
+#include <libudev.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum backlight_type {
+ BACKLIGHT_RAW,
+ BACKLIGHT_PLATFORM,
+ BACKLIGHT_FIRMWARE
+};
+
+struct backlight {
+ char *path;
+ int max_brightness;
+ int brightness;
+ enum backlight_type type;
+};
+
+/*
+ * Find and set up a backlight for a valid udev connector device, i.e. one
+ * matching drm subsytem and with status of connected.
+ */
+struct backlight *backlight_init(struct udev_device *drm_device,
+ uint32_t connector_type);
+
+/* Free backlight resources */
+void backlight_destroy(struct backlight *backlight);
+
+/* Provide the maximum backlight value */
+long backlight_get_max_brightness(struct backlight *backlight);
+
+/* Provide the cached backlight value */
+long backlight_get_brightness(struct backlight *backlight);
+
+/* Provide the hardware backlight value */
+long backlight_get_actual_brightness(struct backlight *backlight);
+
+/* Set the backlight to a value between 0 and max */
+long backlight_set_brightness(struct backlight *backlight, long brightness);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBBACKLIGHT_H */
--- /dev/null
+/*
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2013 Jonas Ådahl
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <linux/input.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <mtdev.h>
+#include <assert.h>
+#include <libinput.h>
+
+#include "compositor.h"
+#include "libinput-device.h"
+#include "shared/helpers.h"
+
+void
+evdev_led_update(struct evdev_device *device, enum weston_led weston_leds)
+{
+ enum libinput_led leds = 0;
+
+ if (weston_leds & LED_NUM_LOCK)
+ leds |= LIBINPUT_LED_NUM_LOCK;
+ if (weston_leds & LED_CAPS_LOCK)
+ leds |= LIBINPUT_LED_CAPS_LOCK;
+ if (weston_leds & LED_SCROLL_LOCK)
+ leds |= LIBINPUT_LED_SCROLL_LOCK;
+
+ libinput_device_led_update(device->device, leds);
+}
+
+static void
+handle_keyboard_key(struct libinput_device *libinput_device,
+ struct libinput_event_keyboard *keyboard_event)
+{
+ struct evdev_device *device =
+ libinput_device_get_user_data(libinput_device);
+ int key_state =
+ libinput_event_keyboard_get_key_state(keyboard_event);
+ int seat_key_count =
+ libinput_event_keyboard_get_seat_key_count(keyboard_event);
+
+ /* Ignore key events that are not seat wide state changes. */
+ if ((key_state == LIBINPUT_KEY_STATE_PRESSED &&
+ seat_key_count != 1) ||
+ (key_state == LIBINPUT_KEY_STATE_RELEASED &&
+ seat_key_count != 0))
+ return;
+
+ notify_key(device->seat,
+ libinput_event_keyboard_get_time(keyboard_event),
+ libinput_event_keyboard_get_key(keyboard_event),
+ key_state, STATE_UPDATE_AUTOMATIC);
+}
+
+static bool
+handle_pointer_motion(struct libinput_device *libinput_device,
+ struct libinput_event_pointer *pointer_event)
+{
+ struct evdev_device *device =
+ libinput_device_get_user_data(libinput_device);
+ struct weston_pointer_motion_event event = { 0 };
+
+ event = (struct weston_pointer_motion_event) {
+ .mask = WESTON_POINTER_MOTION_REL,
+ .dx = libinput_event_pointer_get_dx(pointer_event),
+ .dy = libinput_event_pointer_get_dy(pointer_event),
+ };
+
+ notify_motion(device->seat,
+ libinput_event_pointer_get_time(pointer_event),
+ &event);
+
+ return true;
+}
+
+static bool
+handle_pointer_motion_absolute(
+ struct libinput_device *libinput_device,
+ struct libinput_event_pointer *pointer_event)
+{
+ struct evdev_device *device =
+ libinput_device_get_user_data(libinput_device);
+ struct weston_output *output = device->output;
+ uint32_t time;
+ double x, y;
+ uint32_t width, height;
+
+ if (!output)
+ return false;
+
+ time = libinput_event_pointer_get_time(pointer_event);
+ width = device->output->current_mode->width;
+ height = device->output->current_mode->height;
+
+ x = libinput_event_pointer_get_absolute_x_transformed(pointer_event,
+ width);
+ y = libinput_event_pointer_get_absolute_y_transformed(pointer_event,
+ height);
+
+ weston_output_transform_coordinate(device->output, x, y, &x, &y);
+ notify_motion_absolute(device->seat, time, x, y);
+
+ return true;
+}
+
+static bool
+handle_pointer_button(struct libinput_device *libinput_device,
+ struct libinput_event_pointer *pointer_event)
+{
+ struct evdev_device *device =
+ libinput_device_get_user_data(libinput_device);
+ int button_state =
+ libinput_event_pointer_get_button_state(pointer_event);
+ int seat_button_count =
+ libinput_event_pointer_get_seat_button_count(pointer_event);
+
+ /* Ignore button events that are not seat wide state changes. */
+ if ((button_state == LIBINPUT_BUTTON_STATE_PRESSED &&
+ seat_button_count != 1) ||
+ (button_state == LIBINPUT_BUTTON_STATE_RELEASED &&
+ seat_button_count != 0))
+ return false;
+
+ notify_button(device->seat,
+ libinput_event_pointer_get_time(pointer_event),
+ libinput_event_pointer_get_button(pointer_event),
+ button_state);
+
+ return true;
+}
+
+static double
+normalize_scroll(struct libinput_event_pointer *pointer_event,
+ enum libinput_pointer_axis axis)
+{
+ enum libinput_pointer_axis_source source;
+ double value = 0.0;
+
+ source = libinput_event_pointer_get_axis_source(pointer_event);
+ /* libinput < 0.8 sent wheel click events with value 10. Since 0.8
+ the value is the angle of the click in degrees. To keep
+ backwards-compat with existing clients, we just send multiples of
+ the click count.
+ */
+ switch (source) {
+ case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
+ value = 10 * libinput_event_pointer_get_axis_value_discrete(
+ pointer_event,
+ axis);
+ break;
+ case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
+ case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
+ value = libinput_event_pointer_get_axis_value(pointer_event,
+ axis);
+ break;
+ }
+
+ return value;
+}
+
+static int32_t
+get_axis_discrete(struct libinput_event_pointer *pointer_event,
+ enum libinput_pointer_axis axis)
+{
+ enum libinput_pointer_axis_source source;
+
+ source = libinput_event_pointer_get_axis_source(pointer_event);
+
+ if (source != LIBINPUT_POINTER_AXIS_SOURCE_WHEEL)
+ return 0;
+
+ return libinput_event_pointer_get_axis_value_discrete(pointer_event,
+ axis);
+}
+
+static bool
+handle_pointer_axis(struct libinput_device *libinput_device,
+ struct libinput_event_pointer *pointer_event)
+{
+ static int warned;
+ struct evdev_device *device =
+ libinput_device_get_user_data(libinput_device);
+ double vert, horiz;
+ int32_t vert_discrete, horiz_discrete;
+ enum libinput_pointer_axis axis;
+ struct weston_pointer_axis_event weston_event;
+ enum libinput_pointer_axis_source source;
+ uint32_t wl_axis_source;
+ bool has_vert, has_horiz;
+
+ has_vert = libinput_event_pointer_has_axis(pointer_event,
+ LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+ has_horiz = libinput_event_pointer_has_axis(pointer_event,
+ LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
+
+ if (!has_vert && !has_horiz)
+ return false;
+
+ source = libinput_event_pointer_get_axis_source(pointer_event);
+ switch (source) {
+ case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
+ wl_axis_source = WL_POINTER_AXIS_SOURCE_WHEEL;
+ break;
+ case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
+ wl_axis_source = WL_POINTER_AXIS_SOURCE_FINGER;
+ break;
+ case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
+ wl_axis_source = WL_POINTER_AXIS_SOURCE_CONTINUOUS;
+ break;
+ default:
+ if (warned < 5) {
+ weston_log("Unknown scroll source %d.\n", source);
+ warned++;
+ }
+ return false;
+ }
+
+ notify_axis_source(device->seat, wl_axis_source);
+
+ if (has_vert) {
+ axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
+ vert_discrete = get_axis_discrete(pointer_event, axis);
+ vert = normalize_scroll(pointer_event, axis);
+
+ weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
+ weston_event.value = vert;
+ weston_event.discrete = vert_discrete;
+ weston_event.has_discrete = (vert_discrete != 0);
+
+ notify_axis(device->seat,
+ libinput_event_pointer_get_time(pointer_event),
+ &weston_event);
+ }
+
+ if (has_horiz) {
+ axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
+ horiz_discrete = get_axis_discrete(pointer_event, axis);
+ horiz = normalize_scroll(pointer_event, axis);
+
+ weston_event.axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL;
+ weston_event.value = horiz;
+ weston_event.discrete = horiz_discrete;
+ weston_event.has_discrete = (horiz_discrete != 0);
+
+ notify_axis(device->seat,
+ libinput_event_pointer_get_time(pointer_event),
+ &weston_event);
+ }
+
+ return true;
+}
+
+static void
+handle_touch_with_coords(struct libinput_device *libinput_device,
+ struct libinput_event_touch *touch_event,
+ int touch_type)
+{
+ struct evdev_device *device =
+ libinput_device_get_user_data(libinput_device);
+ double x;
+ double y;
+ uint32_t width, height;
+ uint32_t time;
+ int32_t slot;
+
+ if (!device->output)
+ return;
+
+ time = libinput_event_touch_get_time(touch_event);
+ slot = libinput_event_touch_get_seat_slot(touch_event);
+
+ width = device->output->current_mode->width;
+ height = device->output->current_mode->height;
+ x = libinput_event_touch_get_x_transformed(touch_event, width);
+ y = libinput_event_touch_get_y_transformed(touch_event, height);
+
+ weston_output_transform_coordinate(device->output,
+ x, y, &x, &y);
+
+ notify_touch(device->seat, time, slot, x, y, touch_type);
+}
+
+static void
+handle_touch_down(struct libinput_device *device,
+ struct libinput_event_touch *touch_event)
+{
+ handle_touch_with_coords(device, touch_event, WL_TOUCH_DOWN);
+}
+
+static void
+handle_touch_motion(struct libinput_device *device,
+ struct libinput_event_touch *touch_event)
+{
+ handle_touch_with_coords(device, touch_event, WL_TOUCH_MOTION);
+}
+
+static void
+handle_touch_up(struct libinput_device *libinput_device,
+ struct libinput_event_touch *touch_event)
+{
+ struct evdev_device *device =
+ libinput_device_get_user_data(libinput_device);
+ uint32_t time = libinput_event_touch_get_time(touch_event);
+ int32_t slot = libinput_event_touch_get_seat_slot(touch_event);
+
+ notify_touch(device->seat, time, slot, 0, 0, WL_TOUCH_UP);
+}
+
+static void
+handle_touch_frame(struct libinput_device *libinput_device,
+ struct libinput_event_touch *touch_event)
+{
+ struct evdev_device *device =
+ libinput_device_get_user_data(libinput_device);
+ struct weston_seat *seat = device->seat;
+
+ notify_touch_frame(seat);
+}
+
+int
+evdev_device_process_event(struct libinput_event *event)
+{
+ struct libinput_device *libinput_device =
+ libinput_event_get_device(event);
+ struct evdev_device *device =
+ libinput_device_get_user_data(libinput_device);
+ int handled = 1;
+ bool need_frame = false;
+
+ switch (libinput_event_get_type(event)) {
+ case LIBINPUT_EVENT_KEYBOARD_KEY:
+ handle_keyboard_key(libinput_device,
+ libinput_event_get_keyboard_event(event));
+ break;
+ case LIBINPUT_EVENT_POINTER_MOTION:
+ need_frame = handle_pointer_motion(libinput_device,
+ libinput_event_get_pointer_event(event));
+ break;
+ case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
+ need_frame = handle_pointer_motion_absolute(
+ libinput_device,
+ libinput_event_get_pointer_event(event));
+ break;
+ case LIBINPUT_EVENT_POINTER_BUTTON:
+ need_frame = handle_pointer_button(libinput_device,
+ libinput_event_get_pointer_event(event));
+ break;
+ case LIBINPUT_EVENT_POINTER_AXIS:
+ need_frame = handle_pointer_axis(
+ libinput_device,
+ libinput_event_get_pointer_event(event));
+ break;
+ case LIBINPUT_EVENT_TOUCH_DOWN:
+ handle_touch_down(libinput_device,
+ libinput_event_get_touch_event(event));
+ break;
+ case LIBINPUT_EVENT_TOUCH_MOTION:
+ handle_touch_motion(libinput_device,
+ libinput_event_get_touch_event(event));
+ break;
+ case LIBINPUT_EVENT_TOUCH_UP:
+ handle_touch_up(libinput_device,
+ libinput_event_get_touch_event(event));
+ break;
+ case LIBINPUT_EVENT_TOUCH_FRAME:
+ handle_touch_frame(libinput_device,
+ libinput_event_get_touch_event(event));
+ break;
+ default:
+ handled = 0;
+ weston_log("unknown libinput event %d\n",
+ libinput_event_get_type(event));
+ }
+
+ if (need_frame)
+ notify_pointer_frame(device->seat);
+
+ return handled;
+}
+
+static void
+notify_output_destroy(struct wl_listener *listener, void *data)
+{
+ struct evdev_device *device =
+ container_of(listener,
+ struct evdev_device, output_destroy_listener);
+ struct weston_compositor *c = device->seat->compositor;
+ struct weston_output *output;
+
+ if (!device->output_name && !wl_list_empty(&c->output_list)) {
+ output = container_of(c->output_list.next,
+ struct weston_output, link);
+ evdev_device_set_output(device, output);
+ } else {
+ device->output = NULL;
+ }
+}
+
+/**
+ * The WL_CALIBRATION property requires a pixel-specific matrix to be
+ * applied after scaling device coordinates to screen coordinates. libinput
+ * can't do that, so we need to convert the calibration to the normalized
+ * format libinput expects.
+ */
+void
+evdev_device_set_calibration(struct evdev_device *device)
+{
+ struct udev *udev;
+ struct udev_device *udev_device = NULL;
+ const char *sysname = libinput_device_get_sysname(device->device);
+ const char *calibration_values;
+ uint32_t width, height;
+ float calibration[6];
+ enum libinput_config_status status;
+
+ if (!device->output)
+ return;
+
+ width = device->output->width;
+ height = device->output->height;
+ if (width == 0 || height == 0)
+ return;
+
+ /* If libinput has a pre-set calibration matrix, don't override it */
+ if (!libinput_device_config_calibration_has_matrix(device->device) ||
+ libinput_device_config_calibration_get_default_matrix(
+ device->device,
+ calibration) != 0)
+ return;
+
+ udev = udev_new();
+ if (!udev)
+ return;
+
+ udev_device = udev_device_new_from_subsystem_sysname(udev,
+ "input",
+ sysname);
+ if (!udev_device)
+ goto out;
+
+ calibration_values =
+ udev_device_get_property_value(udev_device,
+ "WL_CALIBRATION");
+
+ if (!calibration_values || sscanf(calibration_values,
+ "%f %f %f %f %f %f",
+ &calibration[0],
+ &calibration[1],
+ &calibration[2],
+ &calibration[3],
+ &calibration[4],
+ &calibration[5]) != 6)
+ goto out;
+
+ weston_log("Applying calibration: %f %f %f %f %f %f "
+ "(normalized %f %f)\n",
+ calibration[0],
+ calibration[1],
+ calibration[2],
+ calibration[3],
+ calibration[4],
+ calibration[5],
+ calibration[2] / width,
+ calibration[5] / height);
+
+ /* normalize to a format libinput can use. There is a chance of
+ this being wrong if the width/height don't match the device
+ width/height but I'm not sure how to fix that */
+ calibration[2] /= width;
+ calibration[5] /= height;
+
+ status = libinput_device_config_calibration_set_matrix(device->device,
+ calibration);
+ if (status != LIBINPUT_CONFIG_STATUS_SUCCESS)
+ weston_log("Failed to apply calibration.\n");
+
+out:
+ if (udev_device)
+ udev_device_unref(udev_device);
+ udev_unref(udev);
+}
+
+void
+evdev_device_set_output(struct evdev_device *device,
+ struct weston_output *output)
+{
+ if (device->output_destroy_listener.notify) {
+ wl_list_remove(&device->output_destroy_listener.link);
+ device->output_destroy_listener.notify = NULL;
+ }
+
+ device->output = output;
+ device->output_destroy_listener.notify = notify_output_destroy;
+ wl_signal_add(&output->destroy_signal,
+ &device->output_destroy_listener);
+ evdev_device_set_calibration(device);
+}
+
+struct evdev_device *
+evdev_device_create(struct libinput_device *libinput_device,
+ struct weston_seat *seat)
+{
+ struct evdev_device *device;
+
+ device = zalloc(sizeof *device);
+ if (device == NULL)
+ return NULL;
+
+ device->seat = seat;
+ wl_list_init(&device->link);
+ device->device = libinput_device;
+
+ if (libinput_device_has_capability(libinput_device,
+ LIBINPUT_DEVICE_CAP_KEYBOARD)) {
+ weston_seat_init_keyboard(seat, NULL);
+ device->seat_caps |= EVDEV_SEAT_KEYBOARD;
+ }
+ if (libinput_device_has_capability(libinput_device,
+ LIBINPUT_DEVICE_CAP_POINTER)) {
+ weston_seat_init_pointer(seat);
+ device->seat_caps |= EVDEV_SEAT_POINTER;
+ }
+ if (libinput_device_has_capability(libinput_device,
+ LIBINPUT_DEVICE_CAP_TOUCH)) {
+ weston_seat_init_touch(seat);
+ device->seat_caps |= EVDEV_SEAT_TOUCH;
+ }
+
+ libinput_device_set_user_data(libinput_device, device);
+ libinput_device_ref(libinput_device);
+
+ return device;
+}
+
+void
+evdev_device_destroy(struct evdev_device *device)
+{
+ if (device->seat_caps & EVDEV_SEAT_POINTER)
+ weston_seat_release_pointer(device->seat);
+ if (device->seat_caps & EVDEV_SEAT_KEYBOARD)
+ weston_seat_release_keyboard(device->seat);
+ if (device->seat_caps & EVDEV_SEAT_TOUCH)
+ weston_seat_release_touch(device->seat);
+
+ if (device->output)
+ wl_list_remove(&device->output_destroy_listener.link);
+ wl_list_remove(&device->link);
+ libinput_device_unref(device->device);
+ free(device->devnode);
+ free(device->output_name);
+ free(device);
+}
+
+void
+evdev_notify_keyboard_focus(struct weston_seat *seat,
+ struct wl_list *evdev_devices)
+{
+ struct wl_array keys;
+
+ if (seat->keyboard_device_count == 0)
+ return;
+
+ wl_array_init(&keys);
+ notify_keyboard_focus_in(seat, &keys, STATE_UPDATE_AUTOMATIC);
+ wl_array_release(&keys);
+}
--- /dev/null
+/*
+ * Copyright © 2011, 2012 Intel Corporation
+ *
+ * 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.
+ */
+
+#ifndef _LIBINPUT_DEVICE_H_
+#define _LIBINPUT_DEVICE_H_
+
+#include "config.h"
+
+#include <linux/input.h>
+#include <wayland-util.h>
+#include <libinput.h>
+
+#include "compositor.h"
+
+enum evdev_device_seat_capability {
+ EVDEV_SEAT_POINTER = (1 << 0),
+ EVDEV_SEAT_KEYBOARD = (1 << 1),
+ EVDEV_SEAT_TOUCH = (1 << 2)
+};
+
+struct evdev_device {
+ struct weston_seat *seat;
+ enum evdev_device_seat_capability seat_caps;
+ struct libinput_device *device;
+ struct wl_list link;
+ struct weston_output *output;
+ struct wl_listener output_destroy_listener;
+ char *devnode;
+ char *output_name;
+ int fd;
+};
+
+void
+evdev_led_update(struct evdev_device *device, enum weston_led leds);
+
+struct evdev_device *
+evdev_device_create(struct libinput_device *libinput_device,
+ struct weston_seat *seat);
+
+int
+evdev_device_process_event(struct libinput_event *event);
+
+void
+evdev_device_set_output(struct evdev_device *device,
+ struct weston_output *output);
+void
+evdev_device_destroy(struct evdev_device *device);
+
+void
+evdev_notify_keyboard_focus(struct weston_seat *seat,
+ struct wl_list *evdev_devices);
+void
+evdev_device_set_calibration(struct evdev_device *device);
+
+int
+dispatch_libinput(struct libinput *libinput);
+
+#endif /* _LIBINPUT_DEVICE_H_ */
--- /dev/null
+/*
+ * Copyright © 2013 Intel Corporation
+ * Copyright © 2013 Jonas Ådahl
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libinput.h>
+#include <libudev.h>
+
+#include "compositor.h"
+#include "launcher-util.h"
+#include "libinput-seat.h"
+#include "libinput-device.h"
+#include "shared/helpers.h"
+
+static void
+process_events(struct udev_input *input);
+static struct udev_seat *
+udev_seat_create(struct udev_input *input, const char *seat_name);
+static void
+udev_seat_destroy(struct udev_seat *seat);
+
+static struct udev_seat *
+get_udev_seat(struct udev_input *input, struct libinput_device *device)
+{
+ struct libinput_seat *libinput_seat;
+ const char *seat_name;
+
+ libinput_seat = libinput_device_get_seat(device);
+ seat_name = libinput_seat_get_logical_name(libinput_seat);
+ return udev_seat_get_named(input, seat_name);
+}
+
+static void
+device_added(struct udev_input *input, struct libinput_device *libinput_device)
+{
+ struct weston_compositor *c;
+ struct evdev_device *device;
+ struct weston_output *output;
+ const char *output_name;
+ struct weston_seat *seat;
+ struct udev_seat *udev_seat;
+ struct weston_pointer *pointer;
+
+ c = input->compositor;
+
+ udev_seat = get_udev_seat(input, libinput_device);
+ if (!udev_seat)
+ return;
+
+ seat = &udev_seat->base;
+ device = evdev_device_create(libinput_device, seat);
+ if (device == NULL)
+ return;
+
+ if (input->configure_device != NULL)
+ input->configure_device(c, device->device);
+ evdev_device_set_calibration(device);
+ udev_seat = (struct udev_seat *) seat;
+ wl_list_insert(udev_seat->devices_list.prev, &device->link);
+
+ pointer = weston_seat_get_pointer(seat);
+ if (seat->output && pointer)
+ weston_pointer_clamp(pointer,
+ &pointer->x,
+ &pointer->y);
+
+ output_name = libinput_device_get_output_name(libinput_device);
+ if (output_name) {
+ device->output_name = strdup(output_name);
+ wl_list_for_each(output, &c->output_list, link)
+ if (output->name &&
+ strcmp(output->name, device->output_name) == 0)
+ evdev_device_set_output(device, output);
+ } else if (device->output == NULL && !wl_list_empty(&c->output_list)) {
+ output = container_of(c->output_list.next,
+ struct weston_output, link);
+ evdev_device_set_output(device, output);
+ }
+
+ if (!input->suspended)
+ weston_seat_repick(seat);
+}
+
+static void
+device_removed(struct udev_input *input, struct libinput_device *libinput_device)
+{
+ struct evdev_device *device;
+
+ device = libinput_device_get_user_data(libinput_device);
+ evdev_device_destroy(device);
+}
+
+static void
+udev_seat_remove_devices(struct udev_seat *seat)
+{
+ struct evdev_device *device, *next;
+
+ wl_list_for_each_safe(device, next, &seat->devices_list, link) {
+ evdev_device_destroy(device);
+ }
+}
+
+void
+udev_input_disable(struct udev_input *input)
+{
+ if (input->suspended)
+ return;
+
+ libinput_suspend(input->libinput);
+ process_events(input);
+ input->suspended = 1;
+}
+
+static int
+udev_input_process_event(struct libinput_event *event)
+{
+ struct libinput *libinput = libinput_event_get_context(event);
+ struct libinput_device *libinput_device =
+ libinput_event_get_device(event);
+ struct udev_input *input = libinput_get_user_data(libinput);
+ int handled = 1;
+
+ switch (libinput_event_get_type(event)) {
+ case LIBINPUT_EVENT_DEVICE_ADDED:
+ device_added(input, libinput_device);
+ break;
+ case LIBINPUT_EVENT_DEVICE_REMOVED:
+ device_removed(input, libinput_device);
+ break;
+ default:
+ handled = 0;
+ }
+
+ return handled;
+}
+
+static void
+process_event(struct libinput_event *event)
+{
+ if (udev_input_process_event(event))
+ return;
+ if (evdev_device_process_event(event))
+ return;
+}
+
+static void
+process_events(struct udev_input *input)
+{
+ struct libinput_event *event;
+
+ while ((event = libinput_get_event(input->libinput))) {
+ process_event(event);
+ libinput_event_destroy(event);
+ }
+}
+
+static int
+udev_input_dispatch(struct udev_input *input)
+{
+ if (libinput_dispatch(input->libinput) != 0)
+ weston_log("libinput: Failed to dispatch libinput\n");
+
+ process_events(input);
+
+ return 0;
+}
+
+static int
+libinput_source_dispatch(int fd, uint32_t mask, void *data)
+{
+ struct udev_input *input = data;
+
+ return udev_input_dispatch(input) != 0;
+}
+
+static int
+open_restricted(const char *path, int flags, void *user_data)
+{
+ struct udev_input *input = user_data;
+ struct weston_launcher *launcher = input->compositor->launcher;
+
+ return weston_launcher_open(launcher, path, flags);
+}
+
+static void
+close_restricted(int fd, void *user_data)
+{
+ struct udev_input *input = user_data;
+ struct weston_launcher *launcher = input->compositor->launcher;
+
+ weston_launcher_close(launcher, fd);
+}
+
+const struct libinput_interface libinput_interface = {
+ open_restricted,
+ close_restricted,
+};
+
+int
+udev_input_enable(struct udev_input *input)
+{
+ struct wl_event_loop *loop;
+ struct weston_compositor *c = input->compositor;
+ int fd;
+ struct udev_seat *seat;
+ int devices_found = 0;
+
+ loop = wl_display_get_event_loop(c->wl_display);
+ fd = libinput_get_fd(input->libinput);
+ input->libinput_source =
+ wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
+ libinput_source_dispatch, input);
+ if (!input->libinput_source) {
+ return -1;
+ }
+
+ if (input->suspended) {
+ if (libinput_resume(input->libinput) != 0) {
+ wl_event_source_remove(input->libinput_source);
+ input->libinput_source = NULL;
+ return -1;
+ }
+ input->suspended = 0;
+ process_events(input);
+ }
+
+ wl_list_for_each(seat, &input->compositor->seat_list, base.link) {
+ evdev_notify_keyboard_focus(&seat->base, &seat->devices_list);
+
+ if (!wl_list_empty(&seat->devices_list))
+ devices_found = 1;
+ }
+
+ if (devices_found == 0) {
+ weston_log(
+ "warning: no input devices on entering Weston. "
+ "Possible causes:\n"
+ "\t- no permissions to read /dev/input/event*\n"
+ "\t- seats misconfigured "
+ "(Weston backend option 'seat', "
+ "udev device property ID_SEAT)\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+libinput_log_func(struct libinput *libinput,
+ enum libinput_log_priority priority,
+ const char *format, va_list args)
+{
+ weston_vlog(format, args);
+}
+
+int
+udev_input_init(struct udev_input *input, struct weston_compositor *c,
+ struct udev *udev, const char *seat_id,
+ udev_configure_device_t configure_device)
+{
+ enum libinput_log_priority priority = LIBINPUT_LOG_PRIORITY_INFO;
+ const char *log_priority = NULL;
+
+ memset(input, 0, sizeof *input);
+
+ input->compositor = c;
+ input->configure_device = configure_device;
+
+ log_priority = getenv("WESTON_LIBINPUT_LOG_PRIORITY");
+
+ input->libinput = libinput_udev_create_context(&libinput_interface,
+ input, udev);
+ if (!input->libinput) {
+ return -1;
+ }
+
+ libinput_log_set_handler(input->libinput, &libinput_log_func);
+
+ if (log_priority) {
+ if (strcmp(log_priority, "debug") == 0) {
+ priority = LIBINPUT_LOG_PRIORITY_DEBUG;
+ } else if (strcmp(log_priority, "info") == 0) {
+ priority = LIBINPUT_LOG_PRIORITY_INFO;
+ } else if (strcmp(log_priority, "error") == 0) {
+ priority = LIBINPUT_LOG_PRIORITY_ERROR;
+ }
+ }
+
+ libinput_log_set_priority(input->libinput, priority);
+
+ if (libinput_udev_assign_seat(input->libinput, seat_id) != 0) {
+ libinput_unref(input->libinput);
+ return -1;
+ }
+
+ process_events(input);
+
+ return udev_input_enable(input);
+}
+
+void
+udev_input_destroy(struct udev_input *input)
+{
+ struct udev_seat *seat, *next;
+
+ wl_event_source_remove(input->libinput_source);
+ wl_list_for_each_safe(seat, next, &input->compositor->seat_list, base.link)
+ udev_seat_destroy(seat);
+ libinput_unref(input->libinput);
+}
+
+static void
+udev_seat_led_update(struct weston_seat *seat_base, enum weston_led leds)
+{
+ struct udev_seat *seat = (struct udev_seat *) seat_base;
+ struct evdev_device *device;
+
+ wl_list_for_each(device, &seat->devices_list, link)
+ evdev_led_update(device, leds);
+}
+
+static void
+notify_output_create(struct wl_listener *listener, void *data)
+{
+ struct udev_seat *seat = container_of(listener, struct udev_seat,
+ output_create_listener);
+ struct evdev_device *device;
+ struct weston_output *output = data;
+
+ wl_list_for_each(device, &seat->devices_list, link) {
+ if (device->output_name &&
+ strcmp(output->name, device->output_name) == 0) {
+ evdev_device_set_output(device, output);
+ }
+
+ if (device->output_name == NULL && device->output == NULL)
+ evdev_device_set_output(device, output);
+ }
+}
+
+static struct udev_seat *
+udev_seat_create(struct udev_input *input, const char *seat_name)
+{
+ struct weston_compositor *c = input->compositor;
+ struct udev_seat *seat;
+
+ seat = zalloc(sizeof *seat);
+ if (!seat)
+ return NULL;
+
+ weston_seat_init(&seat->base, c, seat_name);
+ seat->base.led_update = udev_seat_led_update;
+
+ seat->output_create_listener.notify = notify_output_create;
+ wl_signal_add(&c->output_created_signal,
+ &seat->output_create_listener);
+
+ wl_list_init(&seat->devices_list);
+
+ return seat;
+}
+
+static void
+udev_seat_destroy(struct udev_seat *seat)
+{
+ struct weston_keyboard *keyboard =
+ weston_seat_get_keyboard(&seat->base);
+
+ if (keyboard)
+ notify_keyboard_focus_out(&seat->base);
+
+ udev_seat_remove_devices(seat);
+ weston_seat_release(&seat->base);
+ wl_list_remove(&seat->output_create_listener.link);
+ free(seat);
+}
+
+struct udev_seat *
+udev_seat_get_named(struct udev_input *input, const char *seat_name)
+{
+ struct udev_seat *seat;
+
+ wl_list_for_each(seat, &input->compositor->seat_list, base.link) {
+ if (strcmp(seat->base.seat_name, seat_name) == 0)
+ return seat;
+ }
+
+ return udev_seat_create(input, seat_name);
+}
--- /dev/null
+/*
+ * Copyright © 2013 Intel Corporation
+ * Copyright © 2013 Jonas Ådahl
+ *
+ * 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.
+ */
+
+#ifndef _LIBINPUT_SEAT_H_
+#define _LIBINPUT_SEAT_H_
+
+#include "config.h"
+
+#include <libudev.h>
+
+#include "compositor.h"
+
+struct libinput_device;
+
+struct udev_seat {
+ struct weston_seat base;
+ struct wl_list devices_list;
+ struct wl_listener output_create_listener;
+};
+
+typedef void (*udev_configure_device_t)(struct weston_compositor *compositor,
+ struct libinput_device *device);
+
+struct udev_input {
+ struct libinput *libinput;
+ struct wl_event_source *libinput_source;
+ struct weston_compositor *compositor;
+ int suspended;
+ udev_configure_device_t configure_device;
+};
+
+int
+udev_input_enable(struct udev_input *input);
+void
+udev_input_disable(struct udev_input *input);
+int
+udev_input_init(struct udev_input *input,
+ struct weston_compositor *c,
+ struct udev *udev,
+ const char *seat_id,
+ udev_configure_device_t configure_device);
+void
+udev_input_destroy(struct udev_input *input);
+
+struct udev_seat *
+udev_seat_get_named(struct udev_input *u,
+ const char *seat_name);
+
+#endif
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+pkgincludedir=${includedir}/libweston-@LIBWESTON_ABI_VERSION@
+
+Name: libweston API
+Description: Header files for libweston compositors development
+Version: @WESTON_VERSION@
+Requires.private: wayland-server pixman-1 xkbcommon
+Cflags: -I${pkgincludedir}
+Libs: -L${libdir} -lweston-@LIBWESTON_ABI_VERSION@
--- /dev/null
+/*
+ * Copyright © 2014, 2015 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 "config.h"
+
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "compositor.h"
+#include "linux-dmabuf.h"
+#include "linux-dmabuf-unstable-v1-server-protocol.h"
+
+static void
+linux_dmabuf_buffer_destroy(struct linux_dmabuf_buffer *buffer)
+{
+ int i;
+
+ for (i = 0; i < buffer->attributes.n_planes; i++) {
+ close(buffer->attributes.fd[i]);
+ buffer->attributes.fd[i] = -1;
+ }
+
+ buffer->attributes.n_planes = 0;
+ free(buffer);
+}
+
+static void
+destroy_params(struct wl_resource *params_resource)
+{
+ struct linux_dmabuf_buffer *buffer;
+
+ buffer = wl_resource_get_user_data(params_resource);
+
+ if (!buffer)
+ return;
+
+ linux_dmabuf_buffer_destroy(buffer);
+}
+
+static void
+params_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+params_add(struct wl_client *client,
+ struct wl_resource *params_resource,
+ int32_t name_fd,
+ uint32_t plane_idx,
+ uint32_t offset,
+ uint32_t stride,
+ uint32_t modifier_hi,
+ uint32_t modifier_lo)
+{
+ struct linux_dmabuf_buffer *buffer;
+
+ buffer = wl_resource_get_user_data(params_resource);
+ if (!buffer) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
+ "params was already used to create a wl_buffer");
+ close(name_fd);
+ return;
+ }
+
+ assert(buffer->params_resource == params_resource);
+ assert(!buffer->buffer_resource);
+
+ if (plane_idx >= MAX_DMABUF_PLANES) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
+ "plane index %u is too high", plane_idx);
+ close(name_fd);
+ return;
+ }
+
+ if (buffer->attributes.fd[plane_idx] != -1) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
+ "a dmabuf has already been added for plane %u",
+ plane_idx);
+ close(name_fd);
+ return;
+ }
+
+ buffer->attributes.fd[plane_idx] = name_fd;
+ buffer->attributes.offset[plane_idx] = offset;
+ buffer->attributes.stride[plane_idx] = stride;
+ buffer->attributes.modifier[plane_idx] = ((uint64_t)modifier_hi << 32) |
+ modifier_lo;
+ buffer->attributes.n_planes++;
+}
+
+static void
+linux_dmabuf_wl_buffer_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_buffer_interface linux_dmabuf_buffer_implementation = {
+ linux_dmabuf_wl_buffer_destroy
+};
+
+static void
+destroy_linux_dmabuf_wl_buffer(struct wl_resource *resource)
+{
+ struct linux_dmabuf_buffer *buffer;
+
+ buffer = wl_resource_get_user_data(resource);
+ assert(buffer->buffer_resource == resource);
+ assert(!buffer->params_resource);
+
+ if (buffer->user_data_destroy_func)
+ buffer->user_data_destroy_func(buffer);
+
+ linux_dmabuf_buffer_destroy(buffer);
+}
+
+static void
+params_create(struct wl_client *client,
+ struct wl_resource *params_resource,
+ int32_t width,
+ int32_t height,
+ uint32_t format,
+ uint32_t flags)
+{
+ struct linux_dmabuf_buffer *buffer;
+ int i;
+
+ buffer = wl_resource_get_user_data(params_resource);
+
+ if (!buffer) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
+ "params was already used to create a wl_buffer");
+ return;
+ }
+
+ assert(buffer->params_resource == params_resource);
+ assert(!buffer->buffer_resource);
+
+ /* Switch the linux_dmabuf_buffer object from params resource to
+ * eventually wl_buffer resource.
+ */
+ wl_resource_set_user_data(buffer->params_resource, NULL);
+ buffer->params_resource = NULL;
+
+ if (!buffer->attributes.n_planes) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+ "no dmabuf has been added to the params");
+ goto err_out;
+ }
+
+ /* Check for holes in the dmabufs set (e.g. [0, 1, 3]) */
+ for (i = 0; i < buffer->attributes.n_planes; i++) {
+ if (buffer->attributes.fd[i] == -1) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+ "no dmabuf has been added for plane %i", i);
+ goto err_out;
+ }
+ }
+
+ buffer->attributes.width = width;
+ buffer->attributes.height = height;
+ buffer->attributes.format = format;
+ buffer->attributes.flags = flags;
+
+ if (width < 1 || height < 1) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
+ "invalid width %d or height %d", width, height);
+ goto err_out;
+ }
+
+ for (i = 0; i < buffer->attributes.n_planes; i++) {
+ off_t size;
+
+ if ((uint64_t) buffer->attributes.offset[i] + buffer->attributes.stride[i] > UINT32_MAX) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "size overflow for plane %i", i);
+ goto err_out;
+ }
+
+ if (i == 0 &&
+ (uint64_t) buffer->attributes.offset[i] +
+ (uint64_t) buffer->attributes.stride[i] * height > UINT32_MAX) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "size overflow for plane %i", i);
+ goto err_out;
+ }
+
+ /* Don't report an error as it might be caused
+ * by the kernel not supporting seeking on dmabuf */
+ size = lseek(buffer->attributes.fd[i], 0, SEEK_END);
+ if (size == -1)
+ continue;
+
+ if (buffer->attributes.offset[i] >= size) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "invalid offset %i for plane %i",
+ buffer->attributes.offset[i], i);
+ goto err_out;
+ }
+
+ if (buffer->attributes.offset[i] + buffer->attributes.stride[i] > size) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "invalid stride %i for plane %i",
+ buffer->attributes.stride[i], i);
+ goto err_out;
+ }
+
+ /* Only valid for first plane as other planes might be
+ * sub-sampled according to fourcc format */
+ if (i == 0 &&
+ buffer->attributes.offset[i] + buffer->attributes.stride[i] * height > size) {
+ wl_resource_post_error(params_resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "invalid buffer stride or height for plane %i", i);
+ goto err_out;
+ }
+ }
+
+ /* XXX: Some additional sanity checks could be done with respect
+ * to the fourcc format. A centralized collection (kernel or
+ * libdrm) would be useful to avoid code duplication for these
+ * checks (e.g. drm_format_num_planes).
+ */
+
+ if (!weston_compositor_import_dmabuf(buffer->compositor, buffer))
+ goto err_failed;
+
+ buffer->buffer_resource = wl_resource_create(client,
+ &wl_buffer_interface,
+ 1, 0);
+ if (!buffer->buffer_resource) {
+ wl_resource_post_no_memory(params_resource);
+ goto err_buffer;
+ }
+
+ wl_resource_set_implementation(buffer->buffer_resource,
+ &linux_dmabuf_buffer_implementation,
+ buffer, destroy_linux_dmabuf_wl_buffer);
+
+ zwp_linux_buffer_params_v1_send_created(params_resource,
+ buffer->buffer_resource);
+
+ return;
+
+err_buffer:
+ if (buffer->user_data_destroy_func)
+ buffer->user_data_destroy_func(buffer);
+
+err_failed:
+ zwp_linux_buffer_params_v1_send_failed(params_resource);
+
+err_out:
+ linux_dmabuf_buffer_destroy(buffer);
+}
+
+static const struct zwp_linux_buffer_params_v1_interface
+zwp_linux_buffer_params_implementation = {
+ params_destroy,
+ params_add,
+ params_create
+};
+
+static void
+linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+linux_dmabuf_create_params(struct wl_client *client,
+ struct wl_resource *linux_dmabuf_resource,
+ uint32_t params_id)
+{
+ struct weston_compositor *compositor;
+ struct linux_dmabuf_buffer *buffer;
+ uint32_t version;
+ int i;
+
+ version = wl_resource_get_version(linux_dmabuf_resource);
+ compositor = wl_resource_get_user_data(linux_dmabuf_resource);
+
+ buffer = zalloc(sizeof *buffer);
+ if (!buffer)
+ goto err_out;
+
+ for (i = 0; i < MAX_DMABUF_PLANES; i++)
+ buffer->attributes.fd[i] = -1;
+
+ buffer->compositor = compositor;
+ buffer->params_resource =
+ wl_resource_create(client,
+ &zwp_linux_buffer_params_v1_interface,
+ version, params_id);
+ if (!buffer->params_resource)
+ goto err_dealloc;
+
+ wl_resource_set_implementation(buffer->params_resource,
+ &zwp_linux_buffer_params_implementation,
+ buffer, destroy_params);
+
+ return;
+
+err_dealloc:
+ free(buffer);
+
+err_out:
+ wl_resource_post_no_memory(linux_dmabuf_resource);
+}
+
+/** Get the linux_dmabuf_buffer from a wl_buffer resource
+ *
+ * If the given wl_buffer resource was created through the linux_dmabuf
+ * protocol interface, returns the linux_dmabuf_buffer object. This can
+ * be used as a type check for a wl_buffer.
+ *
+ * \param resource A wl_buffer resource.
+ * \return The linux_dmabuf_buffer if it exists, or NULL otherwise.
+ */
+WL_EXPORT struct linux_dmabuf_buffer *
+linux_dmabuf_buffer_get(struct wl_resource *resource)
+{
+ struct linux_dmabuf_buffer *buffer;
+
+ if (!resource)
+ return NULL;
+
+ if (!wl_resource_instance_of(resource, &wl_buffer_interface,
+ &linux_dmabuf_buffer_implementation))
+ return NULL;
+
+ buffer = wl_resource_get_user_data(resource);
+ assert(buffer);
+ assert(!buffer->params_resource);
+ assert(buffer->buffer_resource == resource);
+
+ return buffer;
+}
+
+/** Set renderer-private data
+ *
+ * Set the user data for the linux_dmabuf_buffer. It is invalid to overwrite
+ * a non-NULL user data with a new non-NULL pointer. This is meant to
+ * protect against renderers fighting over linux_dmabuf_buffer user data
+ * ownership.
+ *
+ * The renderer-private data is usually set from the
+ * weston_renderer::import_dmabuf hook.
+ *
+ * \param buffer The linux_dmabuf_buffer object to set for.
+ * \param data The new renderer-private data pointer.
+ * \param func Destructor function to be called for the renderer-private
+ * data when the linux_dmabuf_buffer gets destroyed.
+ *
+ * \sa weston_compositor_import_dmabuf
+ */
+WL_EXPORT void
+linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer,
+ void *data,
+ dmabuf_user_data_destroy_func func)
+{
+ assert(data == NULL || buffer->user_data == NULL);
+
+ buffer->user_data = data;
+ buffer->user_data_destroy_func = func;
+}
+
+/** Get renderer-private data
+ *
+ * Get the user data from the linux_dmabuf_buffer.
+ *
+ * \param buffer The linux_dmabuf_buffer to query.
+ * \return Renderer-private data pointer.
+ *
+ * \sa linux_dmabuf_buffer_set_user_data
+ */
+WL_EXPORT void *
+linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer)
+{
+ return buffer->user_data;
+}
+
+static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_implementation = {
+ linux_dmabuf_destroy,
+ linux_dmabuf_create_params
+};
+
+static void
+bind_linux_dmabuf(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct weston_compositor *compositor = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface,
+ version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &linux_dmabuf_implementation,
+ compositor, NULL);
+
+ /* EGL_EXT_image_dma_buf_import does not provide a way to query the
+ * supported pixel formats. */
+ /* XXX: send formats */
+}
+
+/** Advertise linux_dmabuf support
+ *
+ * Calling this initializes the zwp_linux_dmabuf protocol support, so that
+ * the interface will be advertised to clients. Essentially it creates a
+ * global. Do not call this function multiple times in the compositor's
+ * lifetime. There is no way to deinit explicitly, globals will be reaped
+ * when the wl_display gets destroyed.
+ *
+ * \param compositor The compositor to init for.
+ * \return Zero on success, -1 on failure.
+ */
+WL_EXPORT int
+linux_dmabuf_setup(struct weston_compositor *compositor)
+{
+ if (!wl_global_create(compositor->wl_display,
+ &zwp_linux_dmabuf_v1_interface, 1,
+ compositor, bind_linux_dmabuf))
+ return -1;
+
+ return 0;
+}
+
+/** Resolve an internal compositor error by disconnecting the client.
+ *
+ * This function is used in cases when the dmabuf-based wl_buffer
+ * turns out unusable and there is no fallback path. This is used by
+ * renderers which are the fallback path in the first place.
+ *
+ * It is possible the fault is caused by a compositor bug, the underlying
+ * graphics stack bug or normal behaviour, or perhaps a client mistake.
+ * In any case, the options are to either composite garbage or nothing,
+ * or disconnect the client. This is a helper function for the latter.
+ *
+ * The error is sent as a INVALID_OBJECT error on the client's wl_display.
+ *
+ * \param buffer The linux_dmabuf_buffer that is unusable.
+ * \param msg A custom error message attached to the protocol error.
+ */
+WL_EXPORT void
+linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer,
+ const char *msg)
+{
+ struct wl_client *client;
+ struct wl_resource *display_resource;
+ uint32_t id;
+
+ assert(buffer->buffer_resource);
+ id = wl_resource_get_id(buffer->buffer_resource);
+ client = wl_resource_get_client(buffer->buffer_resource);
+ display_resource = wl_client_get_object(client, 1);
+
+ assert(display_resource);
+ wl_resource_post_error(display_resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "linux_dmabuf server error with "
+ "wl_buffer@%u: %s", id, msg);
+}
--- /dev/null
+/*
+ * Copyright © 2014, 2015 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.
+ */
+
+#ifndef WESTON_LINUX_DMABUF_H
+#define WESTON_LINUX_DMABUF_H
+
+#include <stdint.h>
+
+#define MAX_DMABUF_PLANES 4
+
+struct linux_dmabuf_buffer;
+typedef void (*dmabuf_user_data_destroy_func)(
+ struct linux_dmabuf_buffer *buffer);
+
+struct dmabuf_attributes {
+ int32_t width;
+ int32_t height;
+ uint32_t format;
+ uint32_t flags; /* enum zlinux_buffer_params_flags */
+ int n_planes;
+ int fd[MAX_DMABUF_PLANES];
+ uint32_t offset[MAX_DMABUF_PLANES];
+ uint32_t stride[MAX_DMABUF_PLANES];
+ uint64_t modifier[MAX_DMABUF_PLANES];
+};
+
+struct linux_dmabuf_buffer {
+ struct wl_resource *buffer_resource;
+ struct wl_resource *params_resource;
+ struct weston_compositor *compositor;
+ struct dmabuf_attributes attributes;
+
+ void *user_data;
+ dmabuf_user_data_destroy_func user_data_destroy_func;
+
+ /* XXX:
+ *
+ * Add backend private data. This would be for the backend
+ * to do all additional imports it might ever use in advance.
+ * The basic principle, even if not implemented in drivers today,
+ * is that dmabufs are first attached, but the actual allocation
+ * is deferred to first use. This would allow the exporter and all
+ * attachers to agree on how to allocate.
+ *
+ * The DRM backend would use this to create drmFBs for each
+ * dmabuf_buffer, just in case at some point it would become
+ * feasible to scan it out directly. This would improve the
+ * possibilities to successfully scan out, avoiding compositing.
+ */
+};
+
+int
+linux_dmabuf_setup(struct weston_compositor *compositor);
+
+struct linux_dmabuf_buffer *
+linux_dmabuf_buffer_get(struct wl_resource *resource);
+
+void
+linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer,
+ void *data,
+ dmabuf_user_data_destroy_func func);
+void *
+linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer);
+
+void
+linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer,
+ const char *msg);
+
+#endif /* WESTON_LINUX_DMABUF_H */
--- /dev/null
+/*
+ * Copyright © 2012 Martin Minarik
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <wayland-util.h>
+
+#include "compositor.h"
+
+static log_func_t log_handler = 0;
+static log_func_t log_continue_handler = 0;
+
+/** Install the log handler
+ *
+ * The given functions will be called to output text as passed to the
+ * \a weston_log and \a weston_log_continue functions.
+ *
+ * \param log The log function. This function will be called when
+ * \a weston_log is called, and should begin a new line,
+ * with user defined line headers, if any.
+ * \param cont The continue log function. This function will be called
+ * when \a weston_log_continue is called, and should append
+ * its output to the current line, without any header or
+ * other content in between.
+ */
+WL_EXPORT void
+weston_log_set_handler(log_func_t log, log_func_t cont)
+{
+ log_handler = log;
+ log_continue_handler = cont;
+}
+
+WL_EXPORT int
+weston_vlog(const char *fmt, va_list ap)
+{
+ return log_handler(fmt, ap);
+}
+
+WL_EXPORT int
+weston_log(const char *fmt, ...)
+{
+ int l;
+ va_list argp;
+
+ va_start(argp, fmt);
+ l = weston_vlog(fmt, argp);
+ va_end(argp);
+
+ return l;
+}
+
+WL_EXPORT int
+weston_vlog_continue(const char *fmt, va_list argp)
+{
+ return log_continue_handler(fmt, argp);
+}
+
+WL_EXPORT int
+weston_log_continue(const char *fmt, ...)
+{
+ int l;
+ va_list argp;
+
+ va_start(argp, fmt);
+ l = weston_vlog_continue(fmt, argp);
+ va_end(argp);
+
+ return l;
+}
--- /dev/null
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "compositor.h"
+
+static int
+noop_renderer_read_pixels(struct weston_output *output,
+ pixman_format_code_t format, void *pixels,
+ uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height)
+{
+ return 0;
+}
+
+static void
+noop_renderer_repaint_output(struct weston_output *output,
+ pixman_region32_t *output_damage)
+{
+}
+
+static void
+noop_renderer_flush_damage(struct weston_surface *surface)
+{
+}
+
+static void
+noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
+{
+ struct wl_shm_buffer *shm_buffer;
+ uint8_t *data;
+ uint32_t size, i, width, height, stride;
+ volatile unsigned char unused = 0; /* volatile so it's not optimized out */
+
+ if (!buffer)
+ return;
+
+ shm_buffer = wl_shm_buffer_get(buffer->resource);
+
+ if (!shm_buffer) {
+ weston_log("No-op renderer supports only SHM buffers\n");
+ return;
+ }
+
+ data = wl_shm_buffer_get_data(shm_buffer);
+ stride = wl_shm_buffer_get_stride(shm_buffer);
+ width = wl_shm_buffer_get_width(shm_buffer);
+ height = wl_shm_buffer_get_height(shm_buffer);
+ size = stride * height;
+
+ /* Access the buffer data to make sure the buffer's client gets killed
+ * if the buffer size is invalid. This makes the bad_buffer test pass.
+ * This can be removed if we start reading the buffer contents
+ * somewhere else, e.g. in repaint_output(). */
+ wl_shm_buffer_begin_access(shm_buffer);
+ for (i = 0; i < size; i++)
+ unused ^= data[i];
+ wl_shm_buffer_end_access(shm_buffer);
+
+ buffer->shm_buffer = shm_buffer;
+ buffer->width = width;
+ buffer->height = height;
+}
+
+static void
+noop_renderer_surface_set_color(struct weston_surface *surface,
+ float red, float green, float blue, float alpha)
+{
+}
+
+static void
+noop_renderer_destroy(struct weston_compositor *ec)
+{
+ free(ec->renderer);
+ ec->renderer = NULL;
+}
+
+WL_EXPORT int
+noop_renderer_init(struct weston_compositor *ec)
+{
+ struct weston_renderer *renderer;
+
+ renderer = malloc(sizeof *renderer);
+ if (renderer == NULL)
+ return -1;
+
+ renderer->read_pixels = noop_renderer_read_pixels;
+ renderer->repaint_output = noop_renderer_repaint_output;
+ renderer->flush_damage = noop_renderer_flush_damage;
+ renderer->attach = noop_renderer_attach;
+ renderer->surface_set_color = noop_renderer_surface_set_color;
+ renderer->destroy = noop_renderer_destroy;
+ ec->renderer = renderer;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 2012 Intel Corporation
+ * Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
+ * Copyright © 2015 Collabora, Ltd.
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "pixman-renderer.h"
+#include "shared/helpers.h"
+
+#include <linux/input.h>
+
+struct pixman_output_state {
+ void *shadow_buffer;
+ pixman_image_t *shadow_image;
+ pixman_image_t *hw_buffer;
+};
+
+struct pixman_surface_state {
+ struct weston_surface *surface;
+
+ pixman_image_t *image;
+ struct weston_buffer_reference buffer_ref;
+
+ struct wl_listener buffer_destroy_listener;
+ struct wl_listener surface_destroy_listener;
+ struct wl_listener renderer_destroy_listener;
+};
+
+struct pixman_renderer {
+ struct weston_renderer base;
+
+ int repaint_debug;
+ pixman_image_t *debug_color;
+ struct weston_binding *debug_binding;
+
+ struct wl_signal destroy_signal;
+};
+
+static inline struct pixman_output_state *
+get_output_state(struct weston_output *output)
+{
+ return (struct pixman_output_state *)output->renderer_state;
+}
+
+static int
+pixman_renderer_create_surface(struct weston_surface *surface);
+
+static inline struct pixman_surface_state *
+get_surface_state(struct weston_surface *surface)
+{
+ if (!surface->renderer_state)
+ pixman_renderer_create_surface(surface);
+
+ return (struct pixman_surface_state *)surface->renderer_state;
+}
+
+static inline struct pixman_renderer *
+get_renderer(struct weston_compositor *ec)
+{
+ return (struct pixman_renderer *)ec->renderer;
+}
+
+static int
+pixman_renderer_read_pixels(struct weston_output *output,
+ pixman_format_code_t format, void *pixels,
+ uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height)
+{
+ struct pixman_output_state *po = get_output_state(output);
+ pixman_transform_t transform;
+ pixman_image_t *out_buf;
+
+ if (!po->hw_buffer) {
+ errno = ENODEV;
+ return -1;
+ }
+
+ out_buf = pixman_image_create_bits(format,
+ width,
+ height,
+ pixels,
+ (PIXMAN_FORMAT_BPP(format) / 8) * width);
+
+ /* Caller expects vflipped source image */
+ pixman_transform_init_translate(&transform,
+ pixman_int_to_fixed (x),
+ pixman_int_to_fixed (y - pixman_image_get_height (po->hw_buffer)));
+ pixman_transform_scale(&transform, NULL,
+ pixman_fixed_1,
+ pixman_fixed_minus_1);
+ pixman_image_set_transform(po->hw_buffer, &transform);
+
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ po->hw_buffer, /* src */
+ NULL /* mask */,
+ out_buf, /* dest */
+ 0, 0, /* src_x, src_y */
+ 0, 0, /* mask_x, mask_y */
+ 0, 0, /* dest_x, dest_y */
+ pixman_image_get_width (po->hw_buffer), /* width */
+ pixman_image_get_height (po->hw_buffer) /* height */);
+ pixman_image_set_transform(po->hw_buffer, NULL);
+
+ pixman_image_unref(out_buf);
+
+ return 0;
+}
+
+static void
+region_global_to_output(struct weston_output *output, pixman_region32_t *region)
+{
+ if (output->zoom.active) {
+ weston_matrix_transform_region(region, &output->matrix, region);
+ } else {
+ pixman_region32_translate(region, -output->x, -output->y);
+ weston_transformed_region(output->width, output->height,
+ output->transform,
+ output->current_scale,
+ region, region);
+ }
+}
+
+#define D2F(v) pixman_double_to_fixed((double)v)
+
+static void
+weston_matrix_to_pixman_transform(pixman_transform_t *pt,
+ const struct weston_matrix *wm)
+{
+ /* Pixman supports only 2D transform matrix, but Weston uses 3D, *
+ * so we're omitting Z coordinate here. */
+ pt->matrix[0][0] = pixman_double_to_fixed(wm->d[0]);
+ pt->matrix[0][1] = pixman_double_to_fixed(wm->d[4]);
+ pt->matrix[0][2] = pixman_double_to_fixed(wm->d[12]);
+ pt->matrix[1][0] = pixman_double_to_fixed(wm->d[1]);
+ pt->matrix[1][1] = pixman_double_to_fixed(wm->d[5]);
+ pt->matrix[1][2] = pixman_double_to_fixed(wm->d[13]);
+ pt->matrix[2][0] = pixman_double_to_fixed(wm->d[3]);
+ pt->matrix[2][1] = pixman_double_to_fixed(wm->d[7]);
+ pt->matrix[2][2] = pixman_double_to_fixed(wm->d[15]);
+}
+
+static void
+pixman_renderer_compute_transform(pixman_transform_t *transform_out,
+ struct weston_view *ev,
+ struct weston_output *output)
+{
+ struct weston_matrix matrix;
+
+ /* Set up the source transformation based on the surface
+ position, the output position/transform/scale and the client
+ specified buffer transform/scale */
+ matrix = output->inverse_matrix;
+
+ if (ev->transform.enabled) {
+ weston_matrix_multiply(&matrix, &ev->transform.inverse);
+ } else {
+ weston_matrix_translate(&matrix,
+ -ev->geometry.x, -ev->geometry.y, 0);
+ }
+
+ weston_matrix_multiply(&matrix, &ev->surface->surface_to_buffer_matrix);
+
+ weston_matrix_to_pixman_transform(transform_out, &matrix);
+}
+
+static bool
+view_transformation_is_translation(struct weston_view *view)
+{
+ if (!view->transform.enabled)
+ return true;
+
+ if (view->transform.matrix.type <= WESTON_MATRIX_TRANSFORM_TRANSLATE)
+ return true;
+
+ return false;
+}
+
+static void
+region_intersect_only_translation(pixman_region32_t *result_global,
+ pixman_region32_t *global,
+ pixman_region32_t *surf,
+ struct weston_view *view)
+{
+ float view_x, view_y;
+
+ assert(view_transformation_is_translation(view));
+
+ /* Convert from surface to global coordinates */
+ pixman_region32_copy(result_global, surf);
+ weston_view_to_global_float(view, 0, 0, &view_x, &view_y);
+ pixman_region32_translate(result_global, (int)view_x, (int)view_y);
+
+ pixman_region32_intersect(result_global, result_global, global);
+}
+
+static void
+composite_whole(pixman_op_t op,
+ pixman_image_t *src,
+ pixman_image_t *mask,
+ pixman_image_t *dest,
+ const pixman_transform_t *transform,
+ pixman_filter_t filter)
+{
+ int32_t dest_width;
+ int32_t dest_height;
+
+ dest_width = pixman_image_get_width(dest);
+ dest_height = pixman_image_get_height(dest);
+
+ pixman_image_set_transform(src, transform);
+ pixman_image_set_filter(src, filter, NULL, 0);
+
+ pixman_image_composite32(op, src, mask, dest,
+ 0, 0, /* src_x, src_y */
+ 0, 0, /* mask_x, mask_y */
+ 0, 0, /* dest_x, dest_y */
+ dest_width, dest_height);
+}
+
+static void
+composite_clipped(pixman_image_t *src,
+ pixman_image_t *mask,
+ pixman_image_t *dest,
+ const pixman_transform_t *transform,
+ pixman_filter_t filter,
+ pixman_region32_t *src_clip)
+{
+ int n_box;
+ pixman_box32_t *boxes;
+ int32_t dest_width;
+ int32_t dest_height;
+ int src_stride;
+ int bitspp;
+ pixman_format_code_t src_format;
+ void *src_data;
+ int i;
+
+ /* Hardcoded to use PIXMAN_OP_OVER, because sampling outside of
+ * a Pixman image produces (0,0,0,0) instead of discarding the
+ * fragment.
+ */
+
+ dest_width = pixman_image_get_width(dest);
+ dest_height = pixman_image_get_height(dest);
+ src_format = pixman_image_get_format(src);
+ src_stride = pixman_image_get_stride(src);
+ bitspp = PIXMAN_FORMAT_BPP(src_format);
+ src_data = pixman_image_get_data(src);
+
+ assert(src_format);
+
+ /* This would be massive overdraw, except when n_box is 1. */
+ boxes = pixman_region32_rectangles(src_clip, &n_box);
+ for (i = 0; i < n_box; i++) {
+ uint8_t *ptr = src_data;
+ pixman_image_t *boximg;
+ pixman_transform_t adj = *transform;
+
+ ptr += boxes[i].y1 * src_stride;
+ ptr += boxes[i].x1 * bitspp / 8;
+ boximg = pixman_image_create_bits_no_clear(src_format,
+ boxes[i].x2 - boxes[i].x1,
+ boxes[i].y2 - boxes[i].y1,
+ (uint32_t *)ptr, src_stride);
+
+ pixman_transform_translate(&adj, NULL,
+ pixman_int_to_fixed(-boxes[i].x1),
+ pixman_int_to_fixed(-boxes[i].y1));
+ pixman_image_set_transform(boximg, &adj);
+
+ pixman_image_set_filter(boximg, filter, NULL, 0);
+ pixman_image_composite32(PIXMAN_OP_OVER, boximg, mask, dest,
+ 0, 0, /* src_x, src_y */
+ 0, 0, /* mask_x, mask_y */
+ 0, 0, /* dest_x, dest_y */
+ dest_width, dest_height);
+
+ pixman_image_unref(boximg);
+ }
+
+ if (n_box > 1) {
+ static bool warned = false;
+
+ if (!warned)
+ weston_log("Pixman-renderer warning: %dx overdraw\n",
+ n_box);
+ warned = true;
+ }
+}
+
+/** Paint an intersected region
+ *
+ * \param ev The view to be painted.
+ * \param output The output being painted.
+ * \param repaint_output The region to be painted in output coordinates.
+ * \param source_clip The region of the source image to use, in source image
+ * coordinates. If NULL, use the whole source image.
+ * \param pixman_op Compositing operator, either SRC or OVER.
+ */
+static void
+repaint_region(struct weston_view *ev, struct weston_output *output,
+ pixman_region32_t *repaint_output,
+ pixman_region32_t *source_clip,
+ pixman_op_t pixman_op)
+{
+ struct pixman_renderer *pr =
+ (struct pixman_renderer *) output->compositor->renderer;
+ struct pixman_surface_state *ps = get_surface_state(ev->surface);
+ struct pixman_output_state *po = get_output_state(output);
+ struct weston_buffer_viewport *vp = &ev->surface->buffer_viewport;
+ pixman_transform_t transform;
+ pixman_filter_t filter;
+ pixman_image_t *mask_image;
+ pixman_color_t mask = { 0, };
+
+ /* Clip rendering to the damaged output region */
+ pixman_image_set_clip_region32(po->shadow_image, repaint_output);
+
+ pixman_renderer_compute_transform(&transform, ev, output);
+
+ if (ev->transform.enabled || output->current_scale != vp->buffer.scale)
+ filter = PIXMAN_FILTER_BILINEAR;
+ else
+ filter = PIXMAN_FILTER_NEAREST;
+
+ if (ps->buffer_ref.buffer)
+ wl_shm_buffer_begin_access(ps->buffer_ref.buffer->shm_buffer);
+
+ if (ev->alpha < 1.0) {
+ mask.alpha = 0xffff * ev->alpha;
+ mask_image = pixman_image_create_solid_fill(&mask);
+ } else {
+ mask_image = NULL;
+ }
+
+ if (source_clip)
+ composite_clipped(ps->image, mask_image, po->shadow_image,
+ &transform, filter, source_clip);
+ else
+ composite_whole(pixman_op, ps->image, mask_image,
+ po->shadow_image, &transform, filter);
+
+ if (mask_image)
+ pixman_image_unref(mask_image);
+
+ if (ps->buffer_ref.buffer)
+ wl_shm_buffer_end_access(ps->buffer_ref.buffer->shm_buffer);
+
+ if (pr->repaint_debug)
+ pixman_image_composite32(PIXMAN_OP_OVER,
+ pr->debug_color, /* src */
+ NULL /* mask */,
+ po->shadow_image, /* dest */
+ 0, 0, /* src_x, src_y */
+ 0, 0, /* mask_x, mask_y */
+ 0, 0, /* dest_x, dest_y */
+ pixman_image_get_width (po->shadow_image), /* width */
+ pixman_image_get_height (po->shadow_image) /* height */);
+
+ pixman_image_set_clip_region32 (po->shadow_image, NULL);
+}
+
+static void
+draw_view_translated(struct weston_view *view, struct weston_output *output,
+ pixman_region32_t *repaint_global)
+{
+ struct weston_surface *surface = view->surface;
+ /* non-opaque region in surface coordinates: */
+ pixman_region32_t surface_blend;
+ /* region to be painted in output coordinates: */
+ pixman_region32_t repaint_output;
+
+ pixman_region32_init(&repaint_output);
+
+ /* Blended region is whole surface minus opaque region,
+ * unless surface alpha forces us to blend all.
+ */
+ pixman_region32_init_rect(&surface_blend, 0, 0,
+ surface->width, surface->height);
+
+ if (!(view->alpha < 1.0)) {
+ pixman_region32_subtract(&surface_blend, &surface_blend,
+ &surface->opaque);
+
+ if (pixman_region32_not_empty(&surface->opaque)) {
+ region_intersect_only_translation(&repaint_output,
+ repaint_global,
+ &surface->opaque,
+ view);
+ region_global_to_output(output, &repaint_output);
+
+ repaint_region(view, output, &repaint_output, NULL,
+ PIXMAN_OP_SRC);
+ }
+ }
+
+ if (pixman_region32_not_empty(&surface_blend)) {
+ region_intersect_only_translation(&repaint_output,
+ repaint_global,
+ &surface_blend, view);
+ region_global_to_output(output, &repaint_output);
+
+ repaint_region(view, output, &repaint_output, NULL,
+ PIXMAN_OP_OVER);
+ }
+
+ pixman_region32_fini(&surface_blend);
+ pixman_region32_fini(&repaint_output);
+}
+
+static void
+draw_view_source_clipped(struct weston_view *view,
+ struct weston_output *output,
+ pixman_region32_t *repaint_global)
+{
+ struct weston_surface *surface = view->surface;
+ pixman_region32_t surf_region;
+ pixman_region32_t buffer_region;
+ pixman_region32_t repaint_output;
+
+ /* Do not bother separating the opaque region from non-opaque.
+ * Source clipping requires PIXMAN_OP_OVER in all cases, so painting
+ * opaque separately has no benefit.
+ */
+
+ pixman_region32_init_rect(&surf_region, 0, 0,
+ surface->width, surface->height);
+ if (view->geometry.scissor_enabled)
+ pixman_region32_intersect(&surf_region, &surf_region,
+ &view->geometry.scissor);
+
+ pixman_region32_init(&buffer_region);
+ weston_surface_to_buffer_region(surface, &surf_region, &buffer_region);
+
+ pixman_region32_init(&repaint_output);
+ pixman_region32_copy(&repaint_output, repaint_global);
+ region_global_to_output(output, &repaint_output);
+
+ repaint_region(view, output, &repaint_output, &buffer_region,
+ PIXMAN_OP_OVER);
+
+ pixman_region32_fini(&repaint_output);
+ pixman_region32_fini(&buffer_region);
+ pixman_region32_fini(&surf_region);
+}
+
+static void
+draw_view(struct weston_view *ev, struct weston_output *output,
+ pixman_region32_t *damage) /* in global coordinates */
+{
+ struct pixman_surface_state *ps = get_surface_state(ev->surface);
+ /* repaint bounding region in global coordinates: */
+ pixman_region32_t repaint;
+
+ /* No buffer attached */
+ if (!ps->image)
+ return;
+
+ pixman_region32_init(&repaint);
+ pixman_region32_intersect(&repaint,
+ &ev->transform.boundingbox, damage);
+ pixman_region32_subtract(&repaint, &repaint, &ev->clip);
+
+ if (!pixman_region32_not_empty(&repaint))
+ goto out;
+
+ if (view_transformation_is_translation(ev)) {
+ /* The simple case: The surface regions opaque, non-opaque,
+ * etc. are convertible to global coordinate space.
+ * There is no need to use a source clip region.
+ * It is possible to paint opaque region as PIXMAN_OP_SRC.
+ * Also the boundingbox is accurate rather than an
+ * approximation.
+ */
+ draw_view_translated(ev, output, &repaint);
+ } else {
+ /* The complex case: the view transformation does not allow
+ * converting opaque etc. regions into global coordinate space.
+ * Therefore we need source clipping to avoid sampling from
+ * unwanted source image areas, unless the source image is
+ * to be used whole. Source clipping does not work with
+ * PIXMAN_OP_SRC.
+ */
+ draw_view_source_clipped(ev, output, &repaint);
+ }
+
+out:
+ pixman_region32_fini(&repaint);
+}
+static void
+repaint_surfaces(struct weston_output *output, pixman_region32_t *damage)
+{
+ struct weston_compositor *compositor = output->compositor;
+ struct weston_view *view;
+
+ wl_list_for_each_reverse(view, &compositor->view_list, link)
+ if (view->plane == &compositor->primary_plane)
+ draw_view(view, output, damage);
+}
+
+static void
+copy_to_hw_buffer(struct weston_output *output, pixman_region32_t *region)
+{
+ struct pixman_output_state *po = get_output_state(output);
+ pixman_region32_t output_region;
+
+ pixman_region32_init(&output_region);
+ pixman_region32_copy(&output_region, region);
+
+ region_global_to_output(output, &output_region);
+
+ pixman_image_set_clip_region32 (po->hw_buffer, &output_region);
+ pixman_region32_fini(&output_region);
+
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ po->shadow_image, /* src */
+ NULL /* mask */,
+ po->hw_buffer, /* dest */
+ 0, 0, /* src_x, src_y */
+ 0, 0, /* mask_x, mask_y */
+ 0, 0, /* dest_x, dest_y */
+ pixman_image_get_width (po->hw_buffer), /* width */
+ pixman_image_get_height (po->hw_buffer) /* height */);
+
+ pixman_image_set_clip_region32 (po->hw_buffer, NULL);
+}
+
+static void
+pixman_renderer_repaint_output(struct weston_output *output,
+ pixman_region32_t *output_damage)
+{
+ struct pixman_output_state *po = get_output_state(output);
+
+ if (!po->hw_buffer)
+ return;
+
+ repaint_surfaces(output, output_damage);
+ copy_to_hw_buffer(output, output_damage);
+
+ pixman_region32_copy(&output->previous_damage, output_damage);
+ wl_signal_emit(&output->frame_signal, output);
+
+ /* Actual flip should be done by caller */
+}
+
+static void
+pixman_renderer_flush_damage(struct weston_surface *surface)
+{
+ /* No-op for pixman renderer */
+}
+
+static void
+buffer_state_handle_buffer_destroy(struct wl_listener *listener, void *data)
+{
+ struct pixman_surface_state *ps;
+
+ ps = container_of(listener, struct pixman_surface_state,
+ buffer_destroy_listener);
+
+ if (ps->image) {
+ pixman_image_unref(ps->image);
+ ps->image = NULL;
+ }
+
+ ps->buffer_destroy_listener.notify = NULL;
+}
+
+static void
+pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
+{
+ struct pixman_surface_state *ps = get_surface_state(es);
+ struct wl_shm_buffer *shm_buffer;
+ pixman_format_code_t pixman_format;
+
+ weston_buffer_reference(&ps->buffer_ref, buffer);
+
+ if (ps->buffer_destroy_listener.notify) {
+ wl_list_remove(&ps->buffer_destroy_listener.link);
+ ps->buffer_destroy_listener.notify = NULL;
+ }
+
+ if (ps->image) {
+ pixman_image_unref(ps->image);
+ ps->image = NULL;
+ }
+
+ if (!buffer)
+ return;
+
+ shm_buffer = wl_shm_buffer_get(buffer->resource);
+
+ if (! shm_buffer) {
+ weston_log("Pixman renderer supports only SHM buffers\n");
+ weston_buffer_reference(&ps->buffer_ref, NULL);
+ return;
+ }
+
+ switch (wl_shm_buffer_get_format(shm_buffer)) {
+ case WL_SHM_FORMAT_XRGB8888:
+ pixman_format = PIXMAN_x8r8g8b8;
+ break;
+ case WL_SHM_FORMAT_ARGB8888:
+ pixman_format = PIXMAN_a8r8g8b8;
+ break;
+ case WL_SHM_FORMAT_RGB565:
+ pixman_format = PIXMAN_r5g6b5;
+ break;
+ default:
+ weston_log("Unsupported SHM buffer format\n");
+ weston_buffer_reference(&ps->buffer_ref, NULL);
+ return;
+ break;
+ }
+
+ buffer->shm_buffer = shm_buffer;
+ buffer->width = wl_shm_buffer_get_width(shm_buffer);
+ buffer->height = wl_shm_buffer_get_height(shm_buffer);
+
+ ps->image = pixman_image_create_bits(pixman_format,
+ buffer->width, buffer->height,
+ wl_shm_buffer_get_data(shm_buffer),
+ wl_shm_buffer_get_stride(shm_buffer));
+
+ ps->buffer_destroy_listener.notify =
+ buffer_state_handle_buffer_destroy;
+ wl_signal_add(&buffer->destroy_signal,
+ &ps->buffer_destroy_listener);
+}
+
+static void
+pixman_renderer_surface_state_destroy(struct pixman_surface_state *ps)
+{
+ wl_list_remove(&ps->surface_destroy_listener.link);
+ wl_list_remove(&ps->renderer_destroy_listener.link);
+ if (ps->buffer_destroy_listener.notify) {
+ wl_list_remove(&ps->buffer_destroy_listener.link);
+ ps->buffer_destroy_listener.notify = NULL;
+ }
+
+ ps->surface->renderer_state = NULL;
+
+ if (ps->image) {
+ pixman_image_unref(ps->image);
+ ps->image = NULL;
+ }
+ weston_buffer_reference(&ps->buffer_ref, NULL);
+ free(ps);
+}
+
+static void
+surface_state_handle_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct pixman_surface_state *ps;
+
+ ps = container_of(listener, struct pixman_surface_state,
+ surface_destroy_listener);
+
+ pixman_renderer_surface_state_destroy(ps);
+}
+
+static void
+surface_state_handle_renderer_destroy(struct wl_listener *listener, void *data)
+{
+ struct pixman_surface_state *ps;
+
+ ps = container_of(listener, struct pixman_surface_state,
+ renderer_destroy_listener);
+
+ pixman_renderer_surface_state_destroy(ps);
+}
+
+static int
+pixman_renderer_create_surface(struct weston_surface *surface)
+{
+ struct pixman_surface_state *ps;
+ struct pixman_renderer *pr = get_renderer(surface->compositor);
+
+ ps = zalloc(sizeof *ps);
+ if (ps == NULL)
+ return -1;
+
+ surface->renderer_state = ps;
+
+ ps->surface = surface;
+
+ ps->surface_destroy_listener.notify =
+ surface_state_handle_surface_destroy;
+ wl_signal_add(&surface->destroy_signal,
+ &ps->surface_destroy_listener);
+
+ ps->renderer_destroy_listener.notify =
+ surface_state_handle_renderer_destroy;
+ wl_signal_add(&pr->destroy_signal,
+ &ps->renderer_destroy_listener);
+
+ return 0;
+}
+
+static void
+pixman_renderer_surface_set_color(struct weston_surface *es,
+ float red, float green, float blue, float alpha)
+{
+ struct pixman_surface_state *ps = get_surface_state(es);
+ pixman_color_t color;
+
+ color.red = red * 0xffff;
+ color.green = green * 0xffff;
+ color.blue = blue * 0xffff;
+ color.alpha = alpha * 0xffff;
+
+ if (ps->image) {
+ pixman_image_unref(ps->image);
+ ps->image = NULL;
+ }
+
+ ps->image = pixman_image_create_solid_fill(&color);
+}
+
+static void
+pixman_renderer_destroy(struct weston_compositor *ec)
+{
+ struct pixman_renderer *pr = get_renderer(ec);
+
+ wl_signal_emit(&pr->destroy_signal, pr);
+ weston_binding_destroy(pr->debug_binding);
+ free(pr);
+
+ ec->renderer = NULL;
+}
+
+static void
+pixman_renderer_surface_get_content_size(struct weston_surface *surface,
+ int *width, int *height)
+{
+ struct pixman_surface_state *ps = get_surface_state(surface);
+
+ if (ps->image) {
+ *width = pixman_image_get_width(ps->image);
+ *height = pixman_image_get_height(ps->image);
+ } else {
+ *width = 0;
+ *height = 0;
+ }
+}
+
+static int
+pixman_renderer_surface_copy_content(struct weston_surface *surface,
+ void *target, size_t size,
+ int src_x, int src_y,
+ int width, int height)
+{
+ const pixman_format_code_t format = PIXMAN_a8b8g8r8;
+ const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */
+ struct pixman_surface_state *ps = get_surface_state(surface);
+ pixman_image_t *out_buf;
+
+ if (!ps->image)
+ return -1;
+
+ out_buf = pixman_image_create_bits(format, width, height,
+ target, width * bytespp);
+
+ pixman_image_set_transform(ps->image, NULL);
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ ps->image, /* src */
+ NULL, /* mask */
+ out_buf, /* dest */
+ src_x, src_y, /* src_x, src_y */
+ 0, 0, /* mask_x, mask_y */
+ 0, 0, /* dest_x, dest_y */
+ width, height);
+
+ pixman_image_unref(out_buf);
+
+ return 0;
+}
+
+static void
+debug_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key,
+ void *data)
+{
+ struct weston_compositor *ec = data;
+ struct pixman_renderer *pr = (struct pixman_renderer *) ec->renderer;
+
+ pr->repaint_debug ^= 1;
+
+ if (pr->repaint_debug) {
+ pixman_color_t red = {
+ 0x3fff, 0x0000, 0x0000, 0x3fff
+ };
+
+ pr->debug_color = pixman_image_create_solid_fill(&red);
+ } else {
+ pixman_image_unref(pr->debug_color);
+ weston_compositor_damage_all(ec);
+ }
+}
+
+WL_EXPORT int
+pixman_renderer_init(struct weston_compositor *ec)
+{
+ struct pixman_renderer *renderer;
+
+ renderer = zalloc(sizeof *renderer);
+ if (renderer == NULL)
+ return -1;
+
+ renderer->repaint_debug = 0;
+ renderer->debug_color = NULL;
+ renderer->base.read_pixels = pixman_renderer_read_pixels;
+ renderer->base.repaint_output = pixman_renderer_repaint_output;
+ renderer->base.flush_damage = pixman_renderer_flush_damage;
+ renderer->base.attach = pixman_renderer_attach;
+ renderer->base.surface_set_color = pixman_renderer_surface_set_color;
+ renderer->base.destroy = pixman_renderer_destroy;
+ renderer->base.surface_get_content_size =
+ pixman_renderer_surface_get_content_size;
+ renderer->base.surface_copy_content =
+ pixman_renderer_surface_copy_content;
+ ec->renderer = &renderer->base;
+ ec->capabilities |= WESTON_CAP_ROTATION_ANY;
+ ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP;
+ ec->capabilities |= WESTON_CAP_VIEW_CLIP_MASK;
+
+ renderer->debug_binding =
+ weston_compositor_add_debug_binding(ec, KEY_R,
+ debug_binding, ec);
+
+ wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565);
+
+ wl_signal_init(&renderer->destroy_signal);
+
+ return 0;
+}
+
+WL_EXPORT void
+pixman_renderer_output_set_buffer(struct weston_output *output, pixman_image_t *buffer)
+{
+ struct pixman_output_state *po = get_output_state(output);
+
+ if (po->hw_buffer)
+ pixman_image_unref(po->hw_buffer);
+ po->hw_buffer = buffer;
+
+ if (po->hw_buffer) {
+ output->compositor->read_format = pixman_image_get_format(po->hw_buffer);
+ pixman_image_ref(po->hw_buffer);
+ }
+}
+
+WL_EXPORT int
+pixman_renderer_output_create(struct weston_output *output)
+{
+ struct pixman_output_state *po;
+ int w, h;
+
+ po = zalloc(sizeof *po);
+ if (po == NULL)
+ return -1;
+
+ /* set shadow image transformation */
+ w = output->current_mode->width;
+ h = output->current_mode->height;
+
+ po->shadow_buffer = malloc(w * h * 4);
+
+ if (!po->shadow_buffer) {
+ free(po);
+ return -1;
+ }
+
+ po->shadow_image =
+ pixman_image_create_bits(PIXMAN_x8r8g8b8, w, h,
+ po->shadow_buffer, w * 4);
+
+ if (!po->shadow_image) {
+ free(po->shadow_buffer);
+ free(po);
+ return -1;
+ }
+
+ output->renderer_state = po;
+
+ return 0;
+}
+
+WL_EXPORT void
+pixman_renderer_output_destroy(struct weston_output *output)
+{
+ struct pixman_output_state *po = get_output_state(output);
+
+ pixman_image_unref(po->shadow_image);
+
+ if (po->hw_buffer)
+ pixman_image_unref(po->hw_buffer);
+
+ free(po->shadow_buffer);
+
+ po->shadow_buffer = NULL;
+ po->shadow_image = NULL;
+ po->hw_buffer = NULL;
+
+ free(po);
+}
--- /dev/null
+/*
+ * Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include "compositor.h"
+
+int
+pixman_renderer_init(struct weston_compositor *ec);
+
+int
+pixman_renderer_output_create(struct weston_output *output);
+
+void
+pixman_renderer_output_set_buffer(struct weston_output *output, pixman_image_t *buffer);
+
+void
+pixman_renderer_output_destroy(struct weston_output *output);
--- /dev/null
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <linux/input.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/uio.h>
+
+#include "compositor.h"
+#include "shared/helpers.h"
+
+#include "wcap/wcap-decode.h"
+
+struct screenshooter_frame_listener {
+ struct wl_listener listener;
+ struct weston_buffer *buffer;
+ weston_screenshooter_done_func_t done;
+ void *data;
+};
+
+static void
+copy_bgra_yflip(uint8_t *dst, uint8_t *src, int height, int stride)
+{
+ uint8_t *end;
+
+ end = dst + height * stride;
+ while (dst < end) {
+ memcpy(dst, src, stride);
+ dst += stride;
+ src -= stride;
+ }
+}
+
+static void
+copy_bgra(uint8_t *dst, uint8_t *src, int height, int stride)
+{
+ /* TODO: optimize this out */
+ memcpy(dst, src, height * stride);
+}
+
+static void
+copy_row_swap_RB(void *vdst, void *vsrc, int bytes)
+{
+ uint32_t *dst = vdst;
+ uint32_t *src = vsrc;
+ uint32_t *end = dst + bytes / 4;
+
+ while (dst < end) {
+ uint32_t v = *src++;
+ /* A R G B */
+ uint32_t tmp = v & 0xff00ff00;
+ tmp |= (v >> 16) & 0x000000ff;
+ tmp |= (v << 16) & 0x00ff0000;
+ *dst++ = tmp;
+ }
+}
+
+static void
+copy_rgba_yflip(uint8_t *dst, uint8_t *src, int height, int stride)
+{
+ uint8_t *end;
+
+ end = dst + height * stride;
+ while (dst < end) {
+ copy_row_swap_RB(dst, src, stride);
+ dst += stride;
+ src -= stride;
+ }
+}
+
+static void
+copy_rgba(uint8_t *dst, uint8_t *src, int height, int stride)
+{
+ uint8_t *end;
+
+ end = dst + height * stride;
+ while (dst < end) {
+ copy_row_swap_RB(dst, src, stride);
+ dst += stride;
+ src += stride;
+ }
+}
+
+static void
+screenshooter_frame_notify(struct wl_listener *listener, void *data)
+{
+ struct screenshooter_frame_listener *l =
+ container_of(listener,
+ struct screenshooter_frame_listener, listener);
+ struct weston_output *output = data;
+ struct weston_compositor *compositor = output->compositor;
+ int32_t stride;
+ uint8_t *pixels, *d, *s;
+
+ output->disable_planes--;
+ wl_list_remove(&listener->link);
+ stride = l->buffer->width * (PIXMAN_FORMAT_BPP(compositor->read_format) / 8);
+ pixels = malloc(stride * l->buffer->height);
+
+ if (pixels == NULL) {
+ l->done(l->data, WESTON_SCREENSHOOTER_NO_MEMORY);
+ free(l);
+ return;
+ }
+
+ compositor->renderer->read_pixels(output,
+ compositor->read_format, pixels,
+ 0, 0, output->current_mode->width,
+ output->current_mode->height);
+
+ stride = wl_shm_buffer_get_stride(l->buffer->shm_buffer);
+
+ d = wl_shm_buffer_get_data(l->buffer->shm_buffer);
+ s = pixels + stride * (l->buffer->height - 1);
+
+ wl_shm_buffer_begin_access(l->buffer->shm_buffer);
+
+ switch (compositor->read_format) {
+ case PIXMAN_a8r8g8b8:
+ case PIXMAN_x8r8g8b8:
+ if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP)
+ copy_bgra_yflip(d, s, output->current_mode->height, stride);
+ else
+ copy_bgra(d, pixels, output->current_mode->height, stride);
+ break;
+ case PIXMAN_x8b8g8r8:
+ case PIXMAN_a8b8g8r8:
+ if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP)
+ copy_rgba_yflip(d, s, output->current_mode->height, stride);
+ else
+ copy_rgba(d, pixels, output->current_mode->height, stride);
+ break;
+ default:
+ break;
+ }
+
+ wl_shm_buffer_end_access(l->buffer->shm_buffer);
+
+ l->done(l->data, WESTON_SCREENSHOOTER_SUCCESS);
+ free(pixels);
+ free(l);
+}
+
+WL_EXPORT int
+weston_screenshooter_shoot(struct weston_output *output,
+ struct weston_buffer *buffer,
+ weston_screenshooter_done_func_t done, void *data)
+{
+ struct screenshooter_frame_listener *l;
+
+ if (!wl_shm_buffer_get(buffer->resource)) {
+ done(data, WESTON_SCREENSHOOTER_BAD_BUFFER);
+ return -1;
+ }
+
+ buffer->shm_buffer = wl_shm_buffer_get(buffer->resource);
+ buffer->width = wl_shm_buffer_get_width(buffer->shm_buffer);
+ buffer->height = wl_shm_buffer_get_height(buffer->shm_buffer);
+
+ if (buffer->width < output->current_mode->width ||
+ buffer->height < output->current_mode->height) {
+ done(data, WESTON_SCREENSHOOTER_BAD_BUFFER);
+ return -1;
+ }
+
+ l = malloc(sizeof *l);
+ if (l == NULL) {
+ done(data, WESTON_SCREENSHOOTER_NO_MEMORY);
+ return -1;
+ }
+
+ l->buffer = buffer;
+ l->done = done;
+ l->data = data;
+ l->listener.notify = screenshooter_frame_notify;
+ wl_signal_add(&output->frame_signal, &l->listener);
+ output->disable_planes++;
+ weston_output_schedule_repaint(output);
+
+ return 0;
+}
+
+struct weston_recorder {
+ struct weston_output *output;
+ uint32_t *frame, *rect;
+ uint32_t *tmpbuf;
+ uint32_t total;
+ int fd;
+ struct wl_listener frame_listener;
+ int count, destroying;
+};
+
+static uint32_t *
+output_run(uint32_t *p, uint32_t delta, int run)
+{
+ int i;
+
+ while (run > 0) {
+ if (run <= 0xe0) {
+ *p++ = delta | ((run - 1) << 24);
+ break;
+ }
+
+ i = 24 - __builtin_clz(run);
+ *p++ = delta | ((i + 0xe0) << 24);
+ run -= 1 << (7 + i);
+ }
+
+ return p;
+}
+
+static uint32_t
+component_delta(uint32_t next, uint32_t prev)
+{
+ unsigned char dr, dg, db;
+
+ dr = (next >> 16) - (prev >> 16);
+ dg = (next >> 8) - (prev >> 8);
+ db = (next >> 0) - (prev >> 0);
+
+ return (dr << 16) | (dg << 8) | (db << 0);
+}
+
+static void
+weston_recorder_destroy(struct weston_recorder *recorder);
+
+static void
+weston_recorder_frame_notify(struct wl_listener *listener, void *data)
+{
+ struct weston_recorder *recorder =
+ container_of(listener, struct weston_recorder, frame_listener);
+ struct weston_output *output = data;
+ struct weston_compositor *compositor = output->compositor;
+ uint32_t msecs = output->frame_time;
+ pixman_box32_t *r;
+ pixman_region32_t damage, transformed_damage;
+ int i, j, k, n, width, height, run, stride;
+ uint32_t delta, prev, *d, *s, *p, next;
+ struct {
+ uint32_t msecs;
+ uint32_t nrects;
+ } header;
+ struct iovec v[2];
+ int do_yflip;
+ int y_orig;
+ uint32_t *outbuf;
+
+ do_yflip = !!(compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP);
+ if (do_yflip)
+ outbuf = recorder->rect;
+ else
+ outbuf = recorder->tmpbuf;
+
+ pixman_region32_init(&damage);
+ pixman_region32_init(&transformed_damage);
+ pixman_region32_intersect(&damage, &output->region,
+ &output->previous_damage);
+ pixman_region32_translate(&damage, -output->x, -output->y);
+ weston_transformed_region(output->width, output->height,
+ output->transform, output->current_scale,
+ &damage, &transformed_damage);
+ pixman_region32_fini(&damage);
+
+ r = pixman_region32_rectangles(&transformed_damage, &n);
+ if (n == 0) {
+ pixman_region32_fini(&transformed_damage);
+ return;
+ }
+
+ header.msecs = msecs;
+ header.nrects = n;
+ v[0].iov_base = &header;
+ v[0].iov_len = sizeof header;
+ v[1].iov_base = r;
+ v[1].iov_len = n * sizeof *r;
+ recorder->total += writev(recorder->fd, v, 2);
+ stride = output->current_mode->width;
+
+ for (i = 0; i < n; i++) {
+ width = r[i].x2 - r[i].x1;
+ height = r[i].y2 - r[i].y1;
+
+ if (do_yflip)
+ y_orig = output->current_mode->height - r[i].y2;
+ else
+ y_orig = r[i].y1;
+
+ compositor->renderer->read_pixels(output,
+ compositor->read_format, recorder->rect,
+ r[i].x1, y_orig, width, height);
+
+ p = outbuf;
+ run = prev = 0; /* quiet gcc */
+ for (j = 0; j < height; j++) {
+ if (do_yflip)
+ s = recorder->rect + width * j;
+ else
+ s = recorder->rect + width * (height - j - 1);
+ y_orig = r[i].y2 - j - 1;
+ d = recorder->frame + stride * y_orig + r[i].x1;
+
+ for (k = 0; k < width; k++) {
+ next = *s++;
+ delta = component_delta(next, *d);
+ *d++ = next;
+ if (run == 0 || delta == prev) {
+ run++;
+ } else {
+ p = output_run(p, prev, run);
+ run = 1;
+ }
+ prev = delta;
+ }
+ }
+
+ p = output_run(p, prev, run);
+
+ recorder->total += write(recorder->fd,
+ outbuf, (p - outbuf) * 4);
+
+#if 0
+ fprintf(stderr,
+ "%dx%d at %d,%d rle from %d to %d bytes (%f) total %dM\n",
+ width, height, r[i].x1, r[i].y1,
+ width * height * 4, (int) (p - outbuf) * 4,
+ (float) (p - outbuf) / (width * height),
+ recorder->total / 1024 / 1024);
+#endif
+ }
+
+ pixman_region32_fini(&transformed_damage);
+ recorder->count++;
+
+ if (recorder->destroying)
+ weston_recorder_destroy(recorder);
+}
+
+static void
+weston_recorder_free(struct weston_recorder *recorder)
+{
+ if (recorder == NULL)
+ return;
+
+ free(recorder->tmpbuf);
+ free(recorder->rect);
+ free(recorder->frame);
+ free(recorder);
+}
+
+static struct weston_recorder *
+weston_recorder_create(struct weston_output *output, const char *filename)
+{
+ struct weston_compositor *compositor = output->compositor;
+ struct weston_recorder *recorder;
+ int stride, size;
+ struct { uint32_t magic, format, width, height; } header;
+ int do_yflip;
+
+ do_yflip = !!(compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP);
+
+ recorder = zalloc(sizeof *recorder);
+ if (recorder == NULL) {
+ weston_log("%s: out of memory\n", __func__);
+ return NULL;
+ }
+
+ stride = output->current_mode->width;
+ size = stride * 4 * output->current_mode->height;
+ recorder->frame = zalloc(size);
+ recorder->rect = malloc(size);
+ recorder->output = output;
+
+ if ((recorder->frame == NULL) || (recorder->rect == NULL)) {
+ weston_log("%s: out of memory\n", __func__);
+ goto err_recorder;
+ }
+
+ if (!do_yflip) {
+ recorder->tmpbuf = malloc(size);
+ if (recorder->tmpbuf == NULL) {
+ weston_log("%s: out of memory\n", __func__);
+ goto err_recorder;
+ }
+ }
+
+ header.magic = WCAP_HEADER_MAGIC;
+
+ switch (compositor->read_format) {
+ case PIXMAN_x8r8g8b8:
+ case PIXMAN_a8r8g8b8:
+ header.format = WCAP_FORMAT_XRGB8888;
+ break;
+ case PIXMAN_a8b8g8r8:
+ header.format = WCAP_FORMAT_XBGR8888;
+ break;
+ default:
+ weston_log("unknown recorder format\n");
+ goto err_recorder;
+ }
+
+ recorder->fd = open(filename,
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
+
+ if (recorder->fd < 0) {
+ weston_log("problem opening output file %s: %m\n", filename);
+ goto err_recorder;
+ }
+
+ header.width = output->current_mode->width;
+ header.height = output->current_mode->height;
+ recorder->total += write(recorder->fd, &header, sizeof header);
+
+ recorder->frame_listener.notify = weston_recorder_frame_notify;
+ wl_signal_add(&output->frame_signal, &recorder->frame_listener);
+ output->disable_planes++;
+ weston_output_damage(output);
+
+ return recorder;
+
+err_recorder:
+ weston_recorder_free(recorder);
+ return NULL;
+}
+
+static void
+weston_recorder_destroy(struct weston_recorder *recorder)
+{
+ wl_list_remove(&recorder->frame_listener.link);
+ close(recorder->fd);
+ recorder->output->disable_planes--;
+ weston_recorder_free(recorder);
+}
+
+WL_EXPORT struct weston_recorder *
+weston_recorder_start(struct weston_output *output, const char *filename)
+{
+ struct wl_listener *listener;
+
+ listener = wl_signal_get(&output->frame_signal,
+ weston_recorder_frame_notify);
+ if (listener) {
+ weston_log("a recorder on output %s is already running\n",
+ output->name);
+ return NULL;
+ }
+
+ weston_log("starting recorder for output %s, file %s\n",
+ output->name, filename);
+ return weston_recorder_create(output, filename);
+}
+
+WL_EXPORT void
+weston_recorder_stop(struct weston_recorder *recorder)
+{
+ weston_log("stopping recorder, total file size %dM, %d frames\n",
+ recorder->total / (1024 * 1024), recorder->count);
+
+ recorder->destroying = 1;
+ weston_output_schedule_repaint(recorder->output);
+}
--- /dev/null
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include "compositor.h"
+
+WL_EXPORT void
+weston_view_geometry_dirty(struct weston_view *view)
+{
+}
+
+WL_EXPORT int
+weston_log(const char *fmt, ...)
+{
+ return 0;
+}
+
+WL_EXPORT void
+weston_view_schedule_repaint(struct weston_view *view)
+{
+}
+
+WL_EXPORT void
+weston_compositor_schedule_repaint(struct weston_compositor *compositor)
+{
+}
+
+int
+main(int argc, char *argv[])
+{
+ const double k = 300.0;
+ const double current = 0.5;
+ const double target = 1.0;
+ const double friction = 1400;
+
+ struct weston_spring spring;
+ uint32_t time = 0;
+
+ weston_spring_init(&spring, k, current, target);
+ spring.friction = friction;
+ spring.previous = 0.48;
+ spring.timestamp = 0;
+
+ while (!weston_spring_done(&spring)) {
+ printf("\t%d\t%f\n", time, spring.current);
+ weston_spring_update(&spring, time);
+ time += 16;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 2014 Pekka Paalanen <pq@iki.fi>
+ * Copyright © 2014 Collabora, Ltd.
+ *
+ * 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.
+ */
+
+#ifndef WESTON_TIMELINE_OBJECT_H
+#define WESTON_TIMELINE_OBJECT_H
+
+/*
+ * This struct can be embedded in objects related to timeline output.
+ * It must be initialized to all-zero. Afterwards, the timeline code
+ * will handle it alone. No clean-up is necessary.
+ */
+struct weston_timeline_object {
+ /*
+ * Timeline series gets bumped every time a new log is opened.
+ * This triggers id allocation and object info emission.
+ * 0 is an invalid series value.
+ */
+ unsigned series;
+
+ /* Object id in the timeline JSON output. 0 is invalid. */
+ unsigned id;
+
+ /*
+ * If non-zero, forces a re-emission of object description.
+ * Should be set to non-zero, when changing long-lived
+ * object state that is not emitted on normal timeline
+ * events.
+ */
+ unsigned force_refresh;
+};
+
+#endif /* WESTON_TIMELINE_OBJECT_H */
--- /dev/null
+/*
+ * Copyright © 2014 Pekka Paalanen <pq@iki.fi>
+ * Copyright © 2014 Collabora, Ltd.
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+
+#include "timeline.h"
+#include "compositor.h"
+#include "file-util.h"
+
+struct timeline_log {
+ clock_t clk_id;
+ FILE *file;
+ unsigned series;
+ struct wl_listener compositor_destroy_listener;
+};
+
+WL_EXPORT int weston_timeline_enabled_;
+static struct timeline_log timeline_ = { CLOCK_MONOTONIC, NULL, 0 };
+
+static int
+weston_timeline_do_open(void)
+{
+ const char *prefix = "weston-timeline-";
+ const char *suffix = ".log";
+ char fname[1000];
+
+ timeline_.file = file_create_dated(prefix, suffix,
+ fname, sizeof(fname));
+ if (!timeline_.file) {
+ const char *msg;
+
+ switch (errno) {
+ case ETIME:
+ msg = "failure in datetime formatting";
+ break;
+ default:
+ msg = strerror(errno);
+ }
+
+ weston_log("Cannot open '%s*%s' for writing: %s\n",
+ prefix, suffix, msg);
+ return -1;
+ }
+
+ weston_log("Opened timeline file '%s'\n", fname);
+
+ return 0;
+}
+
+static void
+timeline_notify_destroy(struct wl_listener *listener, void *data)
+{
+ weston_timeline_close();
+}
+
+void
+weston_timeline_open(struct weston_compositor *compositor)
+{
+ if (weston_timeline_enabled_)
+ return;
+
+ if (weston_timeline_do_open() < 0)
+ return;
+
+ timeline_.compositor_destroy_listener.notify = timeline_notify_destroy;
+ wl_signal_add(&compositor->destroy_signal,
+ &timeline_.compositor_destroy_listener);
+
+ if (++timeline_.series == 0)
+ ++timeline_.series;
+
+ weston_timeline_enabled_ = 1;
+}
+
+void
+weston_timeline_close(void)
+{
+ if (!weston_timeline_enabled_)
+ return;
+
+ weston_timeline_enabled_ = 0;
+
+ wl_list_remove(&timeline_.compositor_destroy_listener.link);
+
+ fclose(timeline_.file);
+ timeline_.file = NULL;
+ weston_log("Timeline log file closed.\n");
+}
+
+struct timeline_emit_context {
+ FILE *cur;
+ FILE *out;
+ unsigned series;
+};
+
+static unsigned
+timeline_new_id(void)
+{
+ static unsigned idc;
+
+ if (++idc == 0)
+ ++idc;
+
+ return idc;
+}
+
+static int
+check_series(struct timeline_emit_context *ctx,
+ struct weston_timeline_object *to)
+{
+ if (to->series == 0 || to->series != ctx->series) {
+ to->series = ctx->series;
+ to->id = timeline_new_id();
+ return 1;
+ }
+
+ if (to->force_refresh) {
+ to->force_refresh = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+fprint_quoted_string(FILE *fp, const char *str)
+{
+ if (!str) {
+ fprintf(fp, "null");
+ return;
+ }
+
+ fprintf(fp, "\"%s\"", str);
+}
+
+static int
+emit_weston_output(struct timeline_emit_context *ctx, void *obj)
+{
+ struct weston_output *o = obj;
+
+ if (check_series(ctx, &o->timeline)) {
+ fprintf(ctx->out, "{ \"id\":%u, "
+ "\"type\":\"weston_output\", \"name\":",
+ o->timeline.id);
+ fprint_quoted_string(ctx->out, o->name);
+ fprintf(ctx->out, " }\n");
+ }
+
+ fprintf(ctx->cur, "\"wo\":%u", o->timeline.id);
+
+ return 1;
+}
+
+static void
+check_weston_surface_description(struct timeline_emit_context *ctx,
+ struct weston_surface *s)
+{
+ struct weston_surface *mains;
+ char d[512];
+ char mainstr[32];
+
+ if (!check_series(ctx, &s->timeline))
+ return;
+
+ mains = weston_surface_get_main_surface(s);
+ if (mains != s) {
+ check_weston_surface_description(ctx, mains);
+ if (snprintf(mainstr, sizeof(mainstr),
+ ", \"main_surface\":%u", mains->timeline.id) < 0)
+ mainstr[0] = '\0';
+ } else {
+ mainstr[0] = '\0';
+ }
+
+ if (!s->get_label || s->get_label(s, d, sizeof(d)) < 0)
+ d[0] = '\0';
+
+ fprintf(ctx->out, "{ \"id\":%u, "
+ "\"type\":\"weston_surface\", \"desc\":", s->timeline.id);
+ fprint_quoted_string(ctx->out, d[0] ? d : NULL);
+ fprintf(ctx->out, "%s }\n", mainstr);
+}
+
+static int
+emit_weston_surface(struct timeline_emit_context *ctx, void *obj)
+{
+ struct weston_surface *s = obj;
+
+ check_weston_surface_description(ctx, s);
+ fprintf(ctx->cur, "\"ws\":%u", s->timeline.id);
+
+ return 1;
+}
+
+static int
+emit_vblank_timestamp(struct timeline_emit_context *ctx, void *obj)
+{
+ struct timespec *ts = obj;
+
+ fprintf(ctx->cur, "\"vblank\":[%" PRId64 ", %ld]",
+ (int64_t)ts->tv_sec, ts->tv_nsec);
+
+ return 1;
+}
+
+typedef int (*type_func)(struct timeline_emit_context *ctx, void *obj);
+
+static const type_func type_dispatch[] = {
+ [TLT_OUTPUT] = emit_weston_output,
+ [TLT_SURFACE] = emit_weston_surface,
+ [TLT_VBLANK] = emit_vblank_timestamp,
+};
+
+WL_EXPORT void
+weston_timeline_point(const char *name, ...)
+{
+ va_list argp;
+ struct timespec ts;
+ enum timeline_type otype;
+ void *obj;
+ char buf[512];
+ struct timeline_emit_context ctx;
+
+ clock_gettime(timeline_.clk_id, &ts);
+
+ ctx.out = timeline_.file;
+ ctx.cur = fmemopen(buf, sizeof(buf), "w");
+ ctx.series = timeline_.series;
+
+ if (!ctx.cur) {
+ weston_log("Timeline error in fmemopen, closing.\n");
+ weston_timeline_close();
+ return;
+ }
+
+ fprintf(ctx.cur, "{ \"T\":[%" PRId64 ", %ld], \"N\":\"%s\"",
+ (int64_t)ts.tv_sec, ts.tv_nsec, name);
+
+ va_start(argp, name);
+ while (1) {
+ otype = va_arg(argp, enum timeline_type);
+ if (otype == TLT_END)
+ break;
+
+ obj = va_arg(argp, void *);
+ if (type_dispatch[otype]) {
+ fprintf(ctx.cur, ", ");
+ type_dispatch[otype](&ctx, obj);
+ }
+ }
+ va_end(argp);
+
+ fprintf(ctx.cur, " }\n");
+ fflush(ctx.cur);
+ if (ferror(ctx.cur)) {
+ weston_log("Timeline error in constructing entry, closing.\n");
+ weston_timeline_close();
+ } else {
+ fprintf(ctx.out, "%s", buf);
+ }
+
+ fclose(ctx.cur);
+}
--- /dev/null
+/*
+ * Copyright © 2014 Pekka Paalanen <pq@iki.fi>
+ * Copyright © 2014 Collabora, Ltd.
+ *
+ * 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.
+ */
+
+#ifndef WESTON_TIMELINE_H
+#define WESTON_TIMELINE_H
+
+extern int weston_timeline_enabled_;
+
+struct weston_compositor;
+
+void
+weston_timeline_open(struct weston_compositor *compositor);
+
+void
+weston_timeline_close(void);
+
+enum timeline_type {
+ TLT_END = 0,
+ TLT_OUTPUT,
+ TLT_SURFACE,
+ TLT_VBLANK,
+};
+
+#define TYPEVERIFY(type, arg) ({ \
+ typeof(arg) tmp___ = (arg); \
+ (void)((type)0 == tmp___); \
+ tmp___; })
+
+#define TLP_END TLT_END, NULL
+#define TLP_OUTPUT(o) TLT_OUTPUT, TYPEVERIFY(struct weston_output *, (o))
+#define TLP_SURFACE(s) TLT_SURFACE, TYPEVERIFY(struct weston_surface *, (s))
+#define TLP_VBLANK(t) TLT_VBLANK, TYPEVERIFY(const struct timespec *, (t))
+
+#define TL_POINT(...) do { \
+ if (weston_timeline_enabled_) \
+ weston_timeline_point(__VA_ARGS__); \
+} while (0)
+
+void
+weston_timeline_point(const char *name, ...);
+
+#endif /* WESTON_TIMELINE_H */
--- /dev/null
+/*
+ * Copyright (c) 2012 Intel Corporation. All Rights Reserved.
+ * Copyright © 2013 Intel Corporation
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <pthread.h>
+
+#include <va/va.h>
+#include <va/va_drm.h>
+#include <va/va_drmcommon.h>
+#include <va/va_enc_h264.h>
+#include <va/va_vpp.h>
+
+#include "compositor.h"
+#include "vaapi-recorder.h"
+
+#define NAL_REF_IDC_NONE 0
+#define NAL_REF_IDC_LOW 1
+#define NAL_REF_IDC_MEDIUM 2
+#define NAL_REF_IDC_HIGH 3
+
+#define NAL_NON_IDR 1
+#define NAL_IDR 5
+#define NAL_SPS 7
+#define NAL_PPS 8
+#define NAL_SEI 6
+
+#define SLICE_TYPE_P 0
+#define SLICE_TYPE_B 1
+#define SLICE_TYPE_I 2
+
+#define ENTROPY_MODE_CAVLC 0
+#define ENTROPY_MODE_CABAC 1
+
+#define PROFILE_IDC_BASELINE 66
+#define PROFILE_IDC_MAIN 77
+#define PROFILE_IDC_HIGH 100
+
+struct vaapi_recorder {
+ int drm_fd, output_fd;
+ int width, height;
+ int frame_count;
+
+ int error;
+ int destroying;
+ pthread_t worker_thread;
+ pthread_mutex_t mutex;
+ pthread_cond_t input_cond;
+
+ struct {
+ int valid;
+ int prime_fd, stride;
+ } input;
+
+ VADisplay va_dpy;
+
+ /* video post processing is used for colorspace conversion */
+ struct {
+ VAConfigID cfg;
+ VAContextID ctx;
+ VABufferID pipeline_buf;
+ VASurfaceID output;
+ } vpp;
+
+ struct {
+ VAConfigID cfg;
+ VAContextID ctx;
+ VASurfaceID reference_picture[3];
+
+ int intra_period;
+ int output_size;
+ int constraint_set_flag;
+
+ struct {
+ VAEncSequenceParameterBufferH264 seq;
+ VAEncPictureParameterBufferH264 pic;
+ VAEncSliceParameterBufferH264 slice;
+ } param;
+ } encoder;
+};
+
+static void *
+worker_thread_function(void *);
+
+/* bistream code used for writing the packed headers */
+
+#define BITSTREAM_ALLOCATE_STEPPING 4096
+
+struct bitstream {
+ unsigned int *buffer;
+ int bit_offset;
+ int max_size_in_dword;
+};
+
+static unsigned int
+va_swap32(unsigned int val)
+{
+ unsigned char *pval = (unsigned char *)&val;
+
+ return ((pval[0] << 24) |
+ (pval[1] << 16) |
+ (pval[2] << 8) |
+ (pval[3] << 0));
+}
+
+static void
+bitstream_start(struct bitstream *bs)
+{
+ bs->max_size_in_dword = BITSTREAM_ALLOCATE_STEPPING;
+ bs->buffer = calloc(bs->max_size_in_dword * sizeof(unsigned int), 1);
+ bs->bit_offset = 0;
+}
+
+static void
+bitstream_end(struct bitstream *bs)
+{
+ int pos = (bs->bit_offset >> 5);
+ int bit_offset = (bs->bit_offset & 0x1f);
+ int bit_left = 32 - bit_offset;
+
+ if (bit_offset) {
+ bs->buffer[pos] = va_swap32((bs->buffer[pos] << bit_left));
+ }
+}
+
+static void
+bitstream_put_ui(struct bitstream *bs, unsigned int val, int size_in_bits)
+{
+ int pos = (bs->bit_offset >> 5);
+ int bit_offset = (bs->bit_offset & 0x1f);
+ int bit_left = 32 - bit_offset;
+
+ if (!size_in_bits)
+ return;
+
+ bs->bit_offset += size_in_bits;
+
+ if (bit_left > size_in_bits) {
+ bs->buffer[pos] = (bs->buffer[pos] << size_in_bits | val);
+ return;
+ }
+
+ size_in_bits -= bit_left;
+ bs->buffer[pos] =
+ (bs->buffer[pos] << bit_left) | (val >> size_in_bits);
+ bs->buffer[pos] = va_swap32(bs->buffer[pos]);
+
+ if (pos + 1 == bs->max_size_in_dword) {
+ bs->max_size_in_dword += BITSTREAM_ALLOCATE_STEPPING;
+ bs->buffer =
+ realloc(bs->buffer,
+ bs->max_size_in_dword * sizeof(unsigned int));
+ }
+
+ bs->buffer[pos + 1] = val;
+}
+
+static void
+bitstream_put_ue(struct bitstream *bs, unsigned int val)
+{
+ int size_in_bits = 0;
+ int tmp_val = ++val;
+
+ while (tmp_val) {
+ tmp_val >>= 1;
+ size_in_bits++;
+ }
+
+ bitstream_put_ui(bs, 0, size_in_bits - 1); /* leading zero */
+ bitstream_put_ui(bs, val, size_in_bits);
+}
+
+static void
+bitstream_put_se(struct bitstream *bs, int val)
+{
+ unsigned int new_val;
+
+ if (val <= 0)
+ new_val = -2 * val;
+ else
+ new_val = 2 * val - 1;
+
+ bitstream_put_ue(bs, new_val);
+}
+
+static void
+bitstream_byte_aligning(struct bitstream *bs, int bit)
+{
+ int bit_offset = (bs->bit_offset & 0x7);
+ int bit_left = 8 - bit_offset;
+ int new_val;
+
+ if (!bit_offset)
+ return;
+
+ if (bit)
+ new_val = (1 << bit_left) - 1;
+ else
+ new_val = 0;
+
+ bitstream_put_ui(bs, new_val, bit_left);
+}
+
+static VAStatus
+encoder_create_config(struct vaapi_recorder *r)
+{
+ VAConfigAttrib attrib[2];
+ VAStatus status;
+
+ /* FIXME: should check if VAEntrypointEncSlice is supported */
+
+ /* FIXME: should check if specified attributes are supported */
+
+ attrib[0].type = VAConfigAttribRTFormat;
+ attrib[0].value = VA_RT_FORMAT_YUV420;
+
+ attrib[1].type = VAConfigAttribRateControl;
+ attrib[1].value = VA_RC_CQP;
+
+ status = vaCreateConfig(r->va_dpy, VAProfileH264Main,
+ VAEntrypointEncSlice, attrib, 2,
+ &r->encoder.cfg);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ status = vaCreateContext(r->va_dpy, r->encoder.cfg,
+ r->width, r->height, VA_PROGRESSIVE, 0, 0,
+ &r->encoder.ctx);
+ if (status != VA_STATUS_SUCCESS) {
+ vaDestroyConfig(r->va_dpy, r->encoder.cfg);
+ return status;
+ }
+
+ return VA_STATUS_SUCCESS;
+}
+
+static void
+encoder_destroy_config(struct vaapi_recorder *r)
+{
+ vaDestroyContext(r->va_dpy, r->encoder.ctx);
+ vaDestroyConfig(r->va_dpy, r->encoder.cfg);
+}
+
+static void
+encoder_init_seq_parameters(struct vaapi_recorder *r)
+{
+ int width_in_mbs, height_in_mbs;
+ int frame_cropping_flag = 0;
+ int frame_crop_bottom_offset = 0;
+
+ width_in_mbs = (r->width + 15) / 16;
+ height_in_mbs = (r->height + 15) / 16;
+
+ r->encoder.param.seq.level_idc = 41;
+ r->encoder.param.seq.intra_period = r->encoder.intra_period;
+ r->encoder.param.seq.max_num_ref_frames = 4;
+ r->encoder.param.seq.picture_width_in_mbs = width_in_mbs;
+ r->encoder.param.seq.picture_height_in_mbs = height_in_mbs;
+ r->encoder.param.seq.seq_fields.bits.frame_mbs_only_flag = 1;
+
+ /* Tc = num_units_in_tick / time_scale */
+ r->encoder.param.seq.time_scale = 1800;
+ r->encoder.param.seq.num_units_in_tick = 15;
+
+ if (height_in_mbs * 16 - r->height > 0) {
+ frame_cropping_flag = 1;
+ frame_crop_bottom_offset = (height_in_mbs * 16 - r->height) / 2;
+ }
+
+ r->encoder.param.seq.frame_cropping_flag = frame_cropping_flag;
+ r->encoder.param.seq.frame_crop_bottom_offset = frame_crop_bottom_offset;
+
+ r->encoder.param.seq.seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = 2;
+}
+
+static VABufferID
+encoder_update_seq_parameters(struct vaapi_recorder *r)
+{
+ VABufferID seq_buf;
+ VAStatus status;
+
+ status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
+ VAEncSequenceParameterBufferType,
+ sizeof(r->encoder.param.seq),
+ 1, &r->encoder.param.seq,
+ &seq_buf);
+
+ if (status == VA_STATUS_SUCCESS)
+ return seq_buf;
+ else
+ return VA_INVALID_ID;
+}
+
+static void
+encoder_init_pic_parameters(struct vaapi_recorder *r)
+{
+ VAEncPictureParameterBufferH264 *pic = &r->encoder.param.pic;
+
+ pic->pic_init_qp = 0;
+
+ /* ENTROPY_MODE_CABAC */
+ pic->pic_fields.bits.entropy_coding_mode_flag = 1;
+
+ pic->pic_fields.bits.deblocking_filter_control_present_flag = 1;
+}
+
+static VABufferID
+encoder_update_pic_parameters(struct vaapi_recorder *r,
+ VABufferID output_buf)
+{
+ VAEncPictureParameterBufferH264 *pic = &r->encoder.param.pic;
+ VAStatus status;
+ VABufferID pic_param_buf;
+ VASurfaceID curr_pic, pic0;
+
+ curr_pic = r->encoder.reference_picture[r->frame_count % 2];
+ pic0 = r->encoder.reference_picture[(r->frame_count + 1) % 2];
+
+ pic->CurrPic.picture_id = curr_pic;
+ pic->CurrPic.TopFieldOrderCnt = r->frame_count * 2;
+ pic->ReferenceFrames[0].picture_id = pic0;
+ pic->ReferenceFrames[1].picture_id = r->encoder.reference_picture[2];
+ pic->ReferenceFrames[2].picture_id = VA_INVALID_ID;
+
+ pic->coded_buf = output_buf;
+ pic->frame_num = r->frame_count;
+
+ pic->pic_fields.bits.idr_pic_flag = (r->frame_count == 0);
+ pic->pic_fields.bits.reference_pic_flag = 1;
+
+ status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
+ VAEncPictureParameterBufferType,
+ sizeof(VAEncPictureParameterBufferH264), 1,
+ pic, &pic_param_buf);
+
+ if (status == VA_STATUS_SUCCESS)
+ return pic_param_buf;
+ else
+ return VA_INVALID_ID;
+}
+
+static VABufferID
+encoder_update_slice_parameter(struct vaapi_recorder *r, int slice_type)
+{
+ VABufferID slice_param_buf;
+ VAStatus status;
+
+ int width_in_mbs = (r->width + 15) / 16;
+ int height_in_mbs = (r->height + 15) / 16;
+
+ memset(&r->encoder.param.slice, 0, sizeof r->encoder.param.slice);
+
+ r->encoder.param.slice.num_macroblocks = width_in_mbs * height_in_mbs;
+ r->encoder.param.slice.slice_type = slice_type;
+
+ r->encoder.param.slice.slice_alpha_c0_offset_div2 = 2;
+ r->encoder.param.slice.slice_beta_offset_div2 = 2;
+
+ status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
+ VAEncSliceParameterBufferType,
+ sizeof(r->encoder.param.slice), 1,
+ &r->encoder.param.slice,
+ &slice_param_buf);
+
+ if (status == VA_STATUS_SUCCESS)
+ return slice_param_buf;
+ else
+ return VA_INVALID_ID;
+}
+
+static VABufferID
+encoder_update_misc_hdr_parameter(struct vaapi_recorder *r)
+{
+ VAEncMiscParameterBuffer *misc_param;
+ VAEncMiscParameterHRD *hrd;
+ VABufferID buffer;
+ VAStatus status;
+
+ int total_size =
+ sizeof(VAEncMiscParameterBuffer) +
+ sizeof(VAEncMiscParameterRateControl);
+
+ status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
+ VAEncMiscParameterBufferType, total_size,
+ 1, NULL, &buffer);
+ if (status != VA_STATUS_SUCCESS)
+ return VA_INVALID_ID;
+
+ status = vaMapBuffer(r->va_dpy, buffer, (void **) &misc_param);
+ if (status != VA_STATUS_SUCCESS) {
+ vaDestroyBuffer(r->va_dpy, buffer);
+ return VA_INVALID_ID;
+ }
+
+ misc_param->type = VAEncMiscParameterTypeHRD;
+ hrd = (VAEncMiscParameterHRD *) misc_param->data;
+
+ hrd->initial_buffer_fullness = 0;
+ hrd->buffer_size = 0;
+
+ vaUnmapBuffer(r->va_dpy, buffer);
+
+ return buffer;
+}
+
+static int
+setup_encoder(struct vaapi_recorder *r)
+{
+ VAStatus status;
+
+ status = encoder_create_config(r);
+ if (status != VA_STATUS_SUCCESS) {
+ return -1;
+ }
+
+ status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_YUV420,
+ r->width, r->height,
+ r->encoder.reference_picture, 3,
+ NULL, 0);
+ if (status != VA_STATUS_SUCCESS) {
+ encoder_destroy_config(r);
+ return -1;
+ }
+
+ /* VAProfileH264Main */
+ r->encoder.constraint_set_flag |= (1 << 1); /* Annex A.2.2 */
+
+ r->encoder.output_size = r->width * r->height;
+
+ r->encoder.intra_period = 30;
+
+ encoder_init_seq_parameters(r);
+ encoder_init_pic_parameters(r);
+
+ return 0;
+}
+
+static void
+encoder_destroy(struct vaapi_recorder *r)
+{
+ vaDestroySurfaces(r->va_dpy, r->encoder.reference_picture, 3);
+
+ encoder_destroy_config(r);
+}
+
+static void
+nal_start_code_prefix(struct bitstream *bs)
+{
+ bitstream_put_ui(bs, 0x00000001, 32);
+}
+
+static void
+nal_header(struct bitstream *bs, int nal_ref_idc, int nal_unit_type)
+{
+ /* forbidden_zero_bit: 0 */
+ bitstream_put_ui(bs, 0, 1);
+
+ bitstream_put_ui(bs, nal_ref_idc, 2);
+ bitstream_put_ui(bs, nal_unit_type, 5);
+}
+
+static void
+rbsp_trailing_bits(struct bitstream *bs)
+{
+ bitstream_put_ui(bs, 1, 1);
+ bitstream_byte_aligning(bs, 0);
+}
+
+static void sps_rbsp(struct bitstream *bs,
+ VAEncSequenceParameterBufferH264 *seq,
+ int constraint_set_flag)
+{
+ int i;
+
+ bitstream_put_ui(bs, PROFILE_IDC_MAIN, 8);
+
+ /* constraint_set[0-3] flag */
+ for (i = 0; i < 4; i++) {
+ int set = (constraint_set_flag & (1 << i)) ? 1 : 0;
+ bitstream_put_ui(bs, set, 1);
+ }
+
+ /* reserved_zero_4bits */
+ bitstream_put_ui(bs, 0, 4);
+ bitstream_put_ui(bs, seq->level_idc, 8);
+ bitstream_put_ue(bs, seq->seq_parameter_set_id);
+
+ bitstream_put_ue(bs, seq->seq_fields.bits.log2_max_frame_num_minus4);
+ bitstream_put_ue(bs, seq->seq_fields.bits.pic_order_cnt_type);
+ bitstream_put_ue(bs,
+ seq->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4);
+
+ bitstream_put_ue(bs, seq->max_num_ref_frames);
+
+ /* gaps_in_frame_num_value_allowed_flag */
+ bitstream_put_ui(bs, 0, 1);
+
+ /* pic_width_in_mbs_minus1, pic_height_in_map_units_minus1 */
+ bitstream_put_ue(bs, seq->picture_width_in_mbs - 1);
+ bitstream_put_ue(bs, seq->picture_height_in_mbs - 1);
+
+ bitstream_put_ui(bs, seq->seq_fields.bits.frame_mbs_only_flag, 1);
+ bitstream_put_ui(bs, seq->seq_fields.bits.direct_8x8_inference_flag, 1);
+
+ bitstream_put_ui(bs, seq->frame_cropping_flag, 1);
+
+ if (seq->frame_cropping_flag) {
+ bitstream_put_ue(bs, seq->frame_crop_left_offset);
+ bitstream_put_ue(bs, seq->frame_crop_right_offset);
+ bitstream_put_ue(bs, seq->frame_crop_top_offset);
+ bitstream_put_ue(bs, seq->frame_crop_bottom_offset);
+ }
+
+ /* vui_parameters_present_flag */
+ bitstream_put_ui(bs, 1, 1);
+
+ /* aspect_ratio_info_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+ /* overscan_info_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+
+ /* video_signal_type_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+ /* chroma_loc_info_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+
+ /* timing_info_present_flag */
+ bitstream_put_ui(bs, 1, 1);
+ bitstream_put_ui(bs, seq->num_units_in_tick, 32);
+ bitstream_put_ui(bs, seq->time_scale, 32);
+ /* fixed_frame_rate_flag */
+ bitstream_put_ui(bs, 1, 1);
+
+ /* nal_hrd_parameters_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+
+ /* vcl_hrd_parameters_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+
+ /* low_delay_hrd_flag */
+ bitstream_put_ui(bs, 0, 1);
+
+ /* pic_struct_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+ /* bitstream_restriction_flag */
+ bitstream_put_ui(bs, 0, 1);
+
+ rbsp_trailing_bits(bs);
+}
+
+static void pps_rbsp(struct bitstream *bs,
+ VAEncPictureParameterBufferH264 *pic)
+{
+ /* pic_parameter_set_id, seq_parameter_set_id */
+ bitstream_put_ue(bs, pic->pic_parameter_set_id);
+ bitstream_put_ue(bs, pic->seq_parameter_set_id);
+
+ bitstream_put_ui(bs, pic->pic_fields.bits.entropy_coding_mode_flag, 1);
+
+ /* pic_order_present_flag: 0 */
+ bitstream_put_ui(bs, 0, 1);
+
+ /* num_slice_groups_minus1 */
+ bitstream_put_ue(bs, 0);
+
+ bitstream_put_ue(bs, pic->num_ref_idx_l0_active_minus1);
+ bitstream_put_ue(bs, pic->num_ref_idx_l1_active_minus1);
+
+ bitstream_put_ui(bs, pic->pic_fields.bits.weighted_pred_flag, 1);
+ bitstream_put_ui(bs, pic->pic_fields.bits.weighted_bipred_idc, 2);
+
+ /* pic_init_qp_minus26, pic_init_qs_minus26, chroma_qp_index_offset */
+ bitstream_put_se(bs, pic->pic_init_qp - 26);
+ bitstream_put_se(bs, 0);
+ bitstream_put_se(bs, 0);
+
+ bitstream_put_ui(bs, pic->pic_fields.bits.deblocking_filter_control_present_flag, 1);
+
+ /* constrained_intra_pred_flag, redundant_pic_cnt_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+ bitstream_put_ui(bs, 0, 1);
+
+ bitstream_put_ui(bs, pic->pic_fields.bits.transform_8x8_mode_flag, 1);
+
+ /* pic_scaling_matrix_present_flag */
+ bitstream_put_ui(bs, 0, 1);
+ bitstream_put_se(bs, pic->second_chroma_qp_index_offset );
+
+ rbsp_trailing_bits(bs);
+}
+
+static int
+build_packed_pic_buffer(struct vaapi_recorder *r,
+ void **header_buffer)
+{
+ struct bitstream bs;
+
+ bitstream_start(&bs);
+ nal_start_code_prefix(&bs);
+ nal_header(&bs, NAL_REF_IDC_HIGH, NAL_PPS);
+ pps_rbsp(&bs, &r->encoder.param.pic);
+ bitstream_end(&bs);
+
+ *header_buffer = bs.buffer;
+ return bs.bit_offset;
+}
+
+static int
+build_packed_seq_buffer(struct vaapi_recorder *r,
+ void **header_buffer)
+{
+ struct bitstream bs;
+
+ bitstream_start(&bs);
+ nal_start_code_prefix(&bs);
+ nal_header(&bs, NAL_REF_IDC_HIGH, NAL_SPS);
+ sps_rbsp(&bs, &r->encoder.param.seq, r->encoder.constraint_set_flag);
+ bitstream_end(&bs);
+
+ *header_buffer = bs.buffer;
+ return bs.bit_offset;
+}
+
+static int
+create_packed_header_buffers(struct vaapi_recorder *r, VABufferID *buffers,
+ VAEncPackedHeaderType type,
+ void *data, int bit_length)
+{
+ VAEncPackedHeaderParameterBuffer packed_header;
+ VAStatus status;
+
+ packed_header.type = type;
+ packed_header.bit_length = bit_length;
+ packed_header.has_emulation_bytes = 0;
+
+ status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
+ VAEncPackedHeaderParameterBufferType,
+ sizeof packed_header, 1, &packed_header,
+ &buffers[0]);
+ if (status != VA_STATUS_SUCCESS)
+ return 0;
+
+ status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
+ VAEncPackedHeaderDataBufferType,
+ (bit_length + 7) / 8, 1, data, &buffers[1]);
+ if (status != VA_STATUS_SUCCESS) {
+ vaDestroyBuffer(r->va_dpy, buffers[0]);
+ return 0;
+ }
+
+ return 2;
+}
+
+static int
+encoder_prepare_headers(struct vaapi_recorder *r, VABufferID *buffers)
+{
+ VABufferID *p;
+
+ int bit_length;
+ void *data;
+
+ p = buffers;
+
+ bit_length = build_packed_seq_buffer(r, &data);
+ p += create_packed_header_buffers(r, p, VAEncPackedHeaderSequence,
+ data, bit_length);
+ free(data);
+
+ bit_length = build_packed_pic_buffer(r, &data);
+ p += create_packed_header_buffers(r, p, VAEncPackedHeaderPicture,
+ data, bit_length);
+ free(data);
+
+ return p - buffers;
+}
+
+static VAStatus
+encoder_render_picture(struct vaapi_recorder *r, VASurfaceID input,
+ VABufferID *buffers, int count)
+{
+ VAStatus status;
+
+ status = vaBeginPicture(r->va_dpy, r->encoder.ctx, input);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ status = vaRenderPicture(r->va_dpy, r->encoder.ctx, buffers, count);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ status = vaEndPicture(r->va_dpy, r->encoder.ctx);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ return vaSyncSurface(r->va_dpy, input);
+}
+
+static VABufferID
+encoder_create_output_buffer(struct vaapi_recorder *r)
+{
+ VABufferID output_buf;
+ VAStatus status;
+
+ status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
+ VAEncCodedBufferType, r->encoder.output_size,
+ 1, NULL, &output_buf);
+ if (status == VA_STATUS_SUCCESS)
+ return output_buf;
+ else
+ return VA_INVALID_ID;
+}
+
+enum output_write_status {
+ OUTPUT_WRITE_SUCCESS,
+ OUTPUT_WRITE_OVERFLOW,
+ OUTPUT_WRITE_FATAL
+};
+
+static enum output_write_status
+encoder_write_output(struct vaapi_recorder *r, VABufferID output_buf)
+{
+ VACodedBufferSegment *segment;
+ VAStatus status;
+ int count;
+
+ status = vaMapBuffer(r->va_dpy, output_buf, (void **) &segment);
+ if (status != VA_STATUS_SUCCESS)
+ return OUTPUT_WRITE_FATAL;
+
+ if (segment->status & VA_CODED_BUF_STATUS_SLICE_OVERFLOW_MASK) {
+ r->encoder.output_size *= 2;
+ vaUnmapBuffer(r->va_dpy, output_buf);
+ return OUTPUT_WRITE_OVERFLOW;
+ }
+
+ count = write(r->output_fd, segment->buf, segment->size);
+
+ vaUnmapBuffer(r->va_dpy, output_buf);
+
+ if (count < 0)
+ return OUTPUT_WRITE_FATAL;
+
+ return OUTPUT_WRITE_SUCCESS;
+}
+
+static void
+encoder_encode(struct vaapi_recorder *r, VASurfaceID input)
+{
+ VABufferID output_buf = VA_INVALID_ID;
+
+ VABufferID buffers[8];
+ int count = 0;
+ int i, slice_type;
+ enum output_write_status ret;
+
+ if ((r->frame_count % r->encoder.intra_period) == 0)
+ slice_type = SLICE_TYPE_I;
+ else
+ slice_type = SLICE_TYPE_P;
+
+ buffers[count++] = encoder_update_seq_parameters(r);
+ buffers[count++] = encoder_update_misc_hdr_parameter(r);
+ buffers[count++] = encoder_update_slice_parameter(r, slice_type);
+
+ for (i = 0; i < count; i++)
+ if (buffers[i] == VA_INVALID_ID)
+ goto bail;
+
+ if (r->frame_count == 0)
+ count += encoder_prepare_headers(r, buffers + count);
+
+ do {
+ output_buf = encoder_create_output_buffer(r);
+ if (output_buf == VA_INVALID_ID)
+ goto bail;
+
+ buffers[count++] =
+ encoder_update_pic_parameters(r, output_buf);
+ if (buffers[count - 1] == VA_INVALID_ID)
+ goto bail;
+
+ encoder_render_picture(r, input, buffers, count);
+ ret = encoder_write_output(r, output_buf);
+
+ vaDestroyBuffer(r->va_dpy, output_buf);
+ output_buf = VA_INVALID_ID;
+
+ vaDestroyBuffer(r->va_dpy, buffers[--count]);
+ } while (ret == OUTPUT_WRITE_OVERFLOW);
+
+ if (ret == OUTPUT_WRITE_FATAL)
+ r->error = errno;
+
+ for (i = 0; i < count; i++)
+ vaDestroyBuffer(r->va_dpy, buffers[i]);
+
+ r->frame_count++;
+ return;
+
+bail:
+ for (i = 0; i < count; i++)
+ vaDestroyBuffer(r->va_dpy, buffers[i]);
+ if (output_buf != VA_INVALID_ID)
+ vaDestroyBuffer(r->va_dpy, output_buf);
+}
+
+
+static int
+setup_vpp(struct vaapi_recorder *r)
+{
+ VAStatus status;
+
+ status = vaCreateConfig(r->va_dpy, VAProfileNone,
+ VAEntrypointVideoProc, NULL, 0,
+ &r->vpp.cfg);
+ if (status != VA_STATUS_SUCCESS) {
+ weston_log("vaapi: failed to create VPP config\n");
+ return -1;
+ }
+
+ status = vaCreateContext(r->va_dpy, r->vpp.cfg, r->width, r->height,
+ 0, NULL, 0, &r->vpp.ctx);
+ if (status != VA_STATUS_SUCCESS) {
+ weston_log("vaapi: failed to create VPP context\n");
+ goto err_cfg;
+ }
+
+ status = vaCreateBuffer(r->va_dpy, r->vpp.ctx,
+ VAProcPipelineParameterBufferType,
+ sizeof(VAProcPipelineParameterBuffer),
+ 1, NULL, &r->vpp.pipeline_buf);
+ if (status != VA_STATUS_SUCCESS) {
+ weston_log("vaapi: failed to create VPP pipeline buffer\n");
+ goto err_ctx;
+ }
+
+ status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_YUV420,
+ r->width, r->height, &r->vpp.output, 1,
+ NULL, 0);
+ if (status != VA_STATUS_SUCCESS) {
+ weston_log("vaapi: failed to create YUV surface\n");
+ goto err_buf;
+ }
+
+ return 0;
+
+err_buf:
+ vaDestroyBuffer(r->va_dpy, r->vpp.pipeline_buf);
+err_ctx:
+ vaDestroyConfig(r->va_dpy, r->vpp.ctx);
+err_cfg:
+ vaDestroyConfig(r->va_dpy, r->vpp.cfg);
+
+ return -1;
+}
+
+static void
+vpp_destroy(struct vaapi_recorder *r)
+{
+ vaDestroySurfaces(r->va_dpy, &r->vpp.output, 1);
+ vaDestroyBuffer(r->va_dpy, r->vpp.pipeline_buf);
+ vaDestroyConfig(r->va_dpy, r->vpp.ctx);
+ vaDestroyConfig(r->va_dpy, r->vpp.cfg);
+}
+
+static int
+setup_worker_thread(struct vaapi_recorder *r)
+{
+ pthread_mutex_init(&r->mutex, NULL);
+ pthread_cond_init(&r->input_cond, NULL);
+ pthread_create(&r->worker_thread, NULL, worker_thread_function, r);
+
+ return 1;
+}
+
+static void
+destroy_worker_thread(struct vaapi_recorder *r)
+{
+ pthread_mutex_lock(&r->mutex);
+
+ /* Make sure the worker thread finishes */
+ r->destroying = 1;
+ pthread_cond_signal(&r->input_cond);
+
+ pthread_mutex_unlock(&r->mutex);
+
+ pthread_join(r->worker_thread, NULL);
+
+ pthread_mutex_destroy(&r->mutex);
+ pthread_cond_destroy(&r->input_cond);
+}
+
+struct vaapi_recorder *
+vaapi_recorder_create(int drm_fd, int width, int height, const char *filename)
+{
+ struct vaapi_recorder *r;
+ VAStatus status;
+ int major, minor;
+ int flags;
+
+ r = zalloc(sizeof *r);
+ if (r == NULL)
+ return NULL;
+
+ r->width = width;
+ r->height = height;
+ r->drm_fd = drm_fd;
+
+ if (setup_worker_thread(r) < 0)
+ goto err_free;
+
+ flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
+ r->output_fd = open(filename, flags, 0644);
+ if (r->output_fd < 0)
+ goto err_thread;
+
+ r->va_dpy = vaGetDisplayDRM(drm_fd);
+ if (!r->va_dpy) {
+ weston_log("failed to create VA display\n");
+ goto err_fd;
+ }
+
+ status = vaInitialize(r->va_dpy, &major, &minor);
+ if (status != VA_STATUS_SUCCESS) {
+ weston_log("vaapi: failed to initialize display\n");
+ goto err_fd;
+ }
+
+ if (setup_vpp(r) < 0) {
+ weston_log("vaapi: failed to initialize VPP pipeline\n");
+ goto err_va_dpy;
+ }
+
+ if (setup_encoder(r) < 0) {
+ goto err_vpp;
+ }
+
+ return r;
+
+err_vpp:
+ vpp_destroy(r);
+err_va_dpy:
+ vaTerminate(r->va_dpy);
+err_fd:
+ close(r->output_fd);
+err_thread:
+ destroy_worker_thread(r);
+err_free:
+ free(r);
+
+ return NULL;
+}
+
+void
+vaapi_recorder_destroy(struct vaapi_recorder *r)
+{
+ destroy_worker_thread(r);
+
+ encoder_destroy(r);
+ vpp_destroy(r);
+
+ vaTerminate(r->va_dpy);
+
+ close(r->output_fd);
+ close(r->drm_fd);
+
+ free(r);
+}
+
+static VAStatus
+create_surface_from_fd(struct vaapi_recorder *r, int prime_fd,
+ int stride, VASurfaceID *surface)
+{
+ VASurfaceAttrib va_attribs[2];
+ VASurfaceAttribExternalBuffers va_attrib_extbuf;
+ VAStatus status;
+
+ unsigned long buffer_fd = prime_fd;
+
+ va_attrib_extbuf.pixel_format = VA_FOURCC_BGRX;
+ va_attrib_extbuf.width = r->width;
+ va_attrib_extbuf.height = r->height;
+ va_attrib_extbuf.data_size = r->height * stride;
+ va_attrib_extbuf.num_planes = 1;
+ va_attrib_extbuf.pitches[0] = stride;
+ va_attrib_extbuf.offsets[0] = 0;
+ va_attrib_extbuf.buffers = &buffer_fd;
+ va_attrib_extbuf.num_buffers = 1;
+ va_attrib_extbuf.flags = 0;
+ va_attrib_extbuf.private_data = NULL;
+
+ va_attribs[0].type = VASurfaceAttribMemoryType;
+ va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE;
+ va_attribs[0].value.type = VAGenericValueTypeInteger;
+ va_attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
+
+ va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor;
+ va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE;
+ va_attribs[1].value.type = VAGenericValueTypePointer;
+ va_attribs[1].value.value.p = &va_attrib_extbuf;
+
+ status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_RGB32,
+ r->width, r->height, surface, 1,
+ va_attribs, 2);
+
+ return status;
+}
+
+static VAStatus
+convert_rgb_to_yuv(struct vaapi_recorder *r, VASurfaceID rgb_surface)
+{
+ VAProcPipelineParameterBuffer *pipeline_param;
+ VAStatus status;
+
+ status = vaMapBuffer(r->va_dpy, r->vpp.pipeline_buf,
+ (void **) &pipeline_param);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ memset(pipeline_param, 0, sizeof *pipeline_param);
+
+ pipeline_param->surface = rgb_surface;
+ pipeline_param->surface_color_standard = VAProcColorStandardNone;
+
+ pipeline_param->output_background_color = 0xff000000;
+ pipeline_param->output_color_standard = VAProcColorStandardNone;
+
+ status = vaUnmapBuffer(r->va_dpy, r->vpp.pipeline_buf);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ status = vaBeginPicture(r->va_dpy, r->vpp.ctx, r->vpp.output);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ status = vaRenderPicture(r->va_dpy, r->vpp.ctx,
+ &r->vpp.pipeline_buf, 1);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ status = vaEndPicture(r->va_dpy, r->vpp.ctx);
+ if (status != VA_STATUS_SUCCESS)
+ return status;
+
+ return status;
+}
+
+static void
+recorder_frame(struct vaapi_recorder *r)
+{
+ VASurfaceID rgb_surface;
+ VAStatus status;
+
+ status = create_surface_from_fd(r, r->input.prime_fd,
+ r->input.stride, &rgb_surface);
+ if (status != VA_STATUS_SUCCESS) {
+ weston_log("[libva recorder] "
+ "failed to create surface from bo\n");
+ return;
+ }
+
+ close(r->input.prime_fd);
+
+ status = convert_rgb_to_yuv(r, rgb_surface);
+ if (status != VA_STATUS_SUCCESS) {
+ weston_log("[libva recorder] "
+ "color space conversion failed\n");
+ return;
+ }
+
+ encoder_encode(r, r->vpp.output);
+
+ vaDestroySurfaces(r->va_dpy, &rgb_surface, 1);
+}
+
+static void *
+worker_thread_function(void *data)
+{
+ struct vaapi_recorder *r = data;
+
+ pthread_mutex_lock(&r->mutex);
+
+ while (!r->destroying) {
+ if (!r->input.valid)
+ pthread_cond_wait(&r->input_cond, &r->mutex);
+
+ /* If the thread is awaken by destroy_worker_thread(),
+ * there might not be valid input */
+ if (!r->input.valid)
+ continue;
+
+ recorder_frame(r);
+ r->input.valid = 0;
+ }
+
+ pthread_mutex_unlock(&r->mutex);
+
+ return NULL;
+}
+
+int
+vaapi_recorder_frame(struct vaapi_recorder *r, int prime_fd, int stride)
+{
+ int ret = 0;
+
+ pthread_mutex_lock(&r->mutex);
+
+ if (r->error) {
+ errno = r->error;
+ ret = -1;
+ goto unlock;
+ }
+
+ /* The mutex is never released while encoding, so this point should
+ * never be reached if input.valid is true. */
+ assert(!r->input.valid);
+
+ r->input.prime_fd = prime_fd;
+ r->input.stride = stride;
+ r->input.valid = 1;
+ pthread_cond_signal(&r->input_cond);
+
+unlock:
+ pthread_mutex_unlock(&r->mutex);
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * 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.
+ */
+
+#ifndef _VAAPI_RECORDER_H_
+#define _VAAPI_RECORDER_H_
+
+struct vaapi_recorder;
+
+struct vaapi_recorder *
+vaapi_recorder_create(int drm_fd, int width, int height, const char *filename);
+void
+vaapi_recorder_destroy(struct vaapi_recorder *r);
+int
+vaapi_recorder_frame(struct vaapi_recorder *r, int fd, int stride);
+
+#endif /* _VAAPI_RECORDER_H_ */
--- /dev/null
+/*
+ * Copyright © 2013 Intel Corporation
+ *
+ * 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.
+ */
+
+#ifndef WESTON_VERSION_H
+#define WESTON_VERSION_H
+
+#define WESTON_VERSION_MAJOR @WESTON_VERSION_MAJOR@
+#define WESTON_VERSION_MINOR @WESTON_VERSION_MINOR@
+#define WESTON_VERSION_MICRO @WESTON_VERSION_MICRO@
+#define WESTON_VERSION "@WESTON_VERSION@"
+
+/* This macro may not do what you expect. Weston doesn't guarantee
+ * a stable API between 1.X and 1.Y, and thus this macro will return
+ * FALSE on any WESTON_VERSION_AT_LEAST(1,X,0) if the actual version
+ * is 1.Y.0 and X != Y). In particular, it fails if X < Y, that is,
+ * 1.3.0 is considered to not be "at least" 1.4.0.
+ *
+ * If you want to test for the version number being 1.3.0 or above or
+ * maybe in a range (eg 1.2.0 to 1.4.0), just use the WESTON_VERSION_*
+ * defines above directly.
+ */
+
+#define WESTON_VERSION_AT_LEAST(major, minor, micro) \
+ (WESTON_VERSION_MAJOR == (major) && \
+ WESTON_VERSION_MINOR == (minor) && \
+ WESTON_VERSION_MICRO >= (micro))
+
+#endif
--- /dev/null
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * 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.
+ */
+#include <assert.h>
+#include <float.h>
+#include <math.h>
+
+#include "vertex-clipping.h"
+
+float
+float_difference(float a, float b)
+{
+ /* http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ */
+ static const float max_diff = 4.0f * FLT_MIN;
+ static const float max_rel_diff = 4.0e-5;
+ float diff = a - b;
+ float adiff = fabsf(diff);
+
+ if (adiff <= max_diff)
+ return 0.0f;
+
+ a = fabsf(a);
+ b = fabsf(b);
+ if (adiff <= (a > b ? a : b) * max_rel_diff)
+ return 0.0f;
+
+ return diff;
+}
+
+/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line x = x_arg.
+ * Compute the y coordinate of the intersection.
+ */
+static float
+clip_intersect_y(float p1x, float p1y, float p2x, float p2y,
+ float x_arg)
+{
+ float a;
+ float diff = float_difference(p1x, p2x);
+
+ /* Practically vertical line segment, yet the end points have already
+ * been determined to be on different sides of the line. Therefore
+ * the line segment is part of the line and intersects everywhere.
+ * Return the end point, so we use the whole line segment.
+ */
+ if (diff == 0.0f)
+ return p2y;
+
+ a = (x_arg - p2x) / diff;
+ return p2y + (p1y - p2y) * a;
+}
+
+/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line y = y_arg.
+ * Compute the x coordinate of the intersection.
+ */
+static float
+clip_intersect_x(float p1x, float p1y, float p2x, float p2y,
+ float y_arg)
+{
+ float a;
+ float diff = float_difference(p1y, p2y);
+
+ /* Practically horizontal line segment, yet the end points have already
+ * been determined to be on different sides of the line. Therefore
+ * the line segment is part of the line and intersects everywhere.
+ * Return the end point, so we use the whole line segment.
+ */
+ if (diff == 0.0f)
+ return p2x;
+
+ a = (y_arg - p2y) / diff;
+ return p2x + (p1x - p2x) * a;
+}
+
+enum path_transition {
+ PATH_TRANSITION_OUT_TO_OUT = 0,
+ PATH_TRANSITION_OUT_TO_IN = 1,
+ PATH_TRANSITION_IN_TO_OUT = 2,
+ PATH_TRANSITION_IN_TO_IN = 3,
+};
+
+static void
+clip_append_vertex(struct clip_context *ctx, float x, float y)
+{
+ *ctx->vertices.x++ = x;
+ *ctx->vertices.y++ = y;
+}
+
+static enum path_transition
+path_transition_left_edge(struct clip_context *ctx, float x, float y)
+{
+ return ((ctx->prev.x >= ctx->clip.x1) << 1) | (x >= ctx->clip.x1);
+}
+
+static enum path_transition
+path_transition_right_edge(struct clip_context *ctx, float x, float y)
+{
+ return ((ctx->prev.x < ctx->clip.x2) << 1) | (x < ctx->clip.x2);
+}
+
+static enum path_transition
+path_transition_top_edge(struct clip_context *ctx, float x, float y)
+{
+ return ((ctx->prev.y >= ctx->clip.y1) << 1) | (y >= ctx->clip.y1);
+}
+
+static enum path_transition
+path_transition_bottom_edge(struct clip_context *ctx, float x, float y)
+{
+ return ((ctx->prev.y < ctx->clip.y2) << 1) | (y < ctx->clip.y2);
+}
+
+static void
+clip_polygon_leftright(struct clip_context *ctx,
+ enum path_transition transition,
+ float x, float y, float clip_x)
+{
+ float yi;
+
+ switch (transition) {
+ case PATH_TRANSITION_IN_TO_IN:
+ clip_append_vertex(ctx, x, y);
+ break;
+ case PATH_TRANSITION_IN_TO_OUT:
+ yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x);
+ clip_append_vertex(ctx, clip_x, yi);
+ break;
+ case PATH_TRANSITION_OUT_TO_IN:
+ yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x);
+ clip_append_vertex(ctx, clip_x, yi);
+ clip_append_vertex(ctx, x, y);
+ break;
+ case PATH_TRANSITION_OUT_TO_OUT:
+ /* nothing */
+ break;
+ default:
+ assert(0 && "bad enum path_transition");
+ }
+
+ ctx->prev.x = x;
+ ctx->prev.y = y;
+}
+
+static void
+clip_polygon_topbottom(struct clip_context *ctx,
+ enum path_transition transition,
+ float x, float y, float clip_y)
+{
+ float xi;
+
+ switch (transition) {
+ case PATH_TRANSITION_IN_TO_IN:
+ clip_append_vertex(ctx, x, y);
+ break;
+ case PATH_TRANSITION_IN_TO_OUT:
+ xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y);
+ clip_append_vertex(ctx, xi, clip_y);
+ break;
+ case PATH_TRANSITION_OUT_TO_IN:
+ xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y);
+ clip_append_vertex(ctx, xi, clip_y);
+ clip_append_vertex(ctx, x, y);
+ break;
+ case PATH_TRANSITION_OUT_TO_OUT:
+ /* nothing */
+ break;
+ default:
+ assert(0 && "bad enum path_transition");
+ }
+
+ ctx->prev.x = x;
+ ctx->prev.y = y;
+}
+
+static void
+clip_context_prepare(struct clip_context *ctx, const struct polygon8 *src,
+ float *dst_x, float *dst_y)
+{
+ ctx->prev.x = src->x[src->n - 1];
+ ctx->prev.y = src->y[src->n - 1];
+ ctx->vertices.x = dst_x;
+ ctx->vertices.y = dst_y;
+}
+
+static int
+clip_polygon_left(struct clip_context *ctx, const struct polygon8 *src,
+ float *dst_x, float *dst_y)
+{
+ enum path_transition trans;
+ int i;
+
+ if (src->n < 2)
+ return 0;
+
+ clip_context_prepare(ctx, src, dst_x, dst_y);
+ for (i = 0; i < src->n; i++) {
+ trans = path_transition_left_edge(ctx, src->x[i], src->y[i]);
+ clip_polygon_leftright(ctx, trans, src->x[i], src->y[i],
+ ctx->clip.x1);
+ }
+ return ctx->vertices.x - dst_x;
+}
+
+static int
+clip_polygon_right(struct clip_context *ctx, const struct polygon8 *src,
+ float *dst_x, float *dst_y)
+{
+ enum path_transition trans;
+ int i;
+
+ if (src->n < 2)
+ return 0;
+
+ clip_context_prepare(ctx, src, dst_x, dst_y);
+ for (i = 0; i < src->n; i++) {
+ trans = path_transition_right_edge(ctx, src->x[i], src->y[i]);
+ clip_polygon_leftright(ctx, trans, src->x[i], src->y[i],
+ ctx->clip.x2);
+ }
+ return ctx->vertices.x - dst_x;
+}
+
+static int
+clip_polygon_top(struct clip_context *ctx, const struct polygon8 *src,
+ float *dst_x, float *dst_y)
+{
+ enum path_transition trans;
+ int i;
+
+ if (src->n < 2)
+ return 0;
+
+ clip_context_prepare(ctx, src, dst_x, dst_y);
+ for (i = 0; i < src->n; i++) {
+ trans = path_transition_top_edge(ctx, src->x[i], src->y[i]);
+ clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i],
+ ctx->clip.y1);
+ }
+ return ctx->vertices.x - dst_x;
+}
+
+static int
+clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src,
+ float *dst_x, float *dst_y)
+{
+ enum path_transition trans;
+ int i;
+
+ if (src->n < 2)
+ return 0;
+
+ clip_context_prepare(ctx, src, dst_x, dst_y);
+ for (i = 0; i < src->n; i++) {
+ trans = path_transition_bottom_edge(ctx, src->x[i], src->y[i]);
+ clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i],
+ ctx->clip.y2);
+ }
+ return ctx->vertices.x - dst_x;
+}
+
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+#define min(a, b) (((a) > (b)) ? (b) : (a))
+#define clip(x, a, b) min(max(x, a), b)
+
+int
+clip_simple(struct clip_context *ctx,
+ struct polygon8 *surf,
+ float *ex,
+ float *ey)
+{
+ int i;
+ for (i = 0; i < surf->n; i++) {
+ ex[i] = clip(surf->x[i], ctx->clip.x1, ctx->clip.x2);
+ ey[i] = clip(surf->y[i], ctx->clip.y1, ctx->clip.y2);
+ }
+ return surf->n;
+}
+
+int
+clip_transformed(struct clip_context *ctx,
+ struct polygon8 *surf,
+ float *ex,
+ float *ey)
+{
+ struct polygon8 polygon;
+ int i, n;
+
+ polygon.n = clip_polygon_left(ctx, surf, polygon.x, polygon.y);
+ surf->n = clip_polygon_right(ctx, &polygon, surf->x, surf->y);
+ polygon.n = clip_polygon_top(ctx, surf, polygon.x, polygon.y);
+ surf->n = clip_polygon_bottom(ctx, &polygon, surf->x, surf->y);
+
+ /* Get rid of duplicate vertices */
+ ex[0] = surf->x[0];
+ ey[0] = surf->y[0];
+ n = 1;
+ for (i = 1; i < surf->n; i++) {
+ if (float_difference(ex[n - 1], surf->x[i]) == 0.0f &&
+ float_difference(ey[n - 1], surf->y[i]) == 0.0f)
+ continue;
+ ex[n] = surf->x[i];
+ ey[n] = surf->y[i];
+ n++;
+ }
+ if (float_difference(ex[n - 1], surf->x[0]) == 0.0f &&
+ float_difference(ey[n - 1], surf->y[0]) == 0.0f)
+ n--;
+
+ return n;
+}
--- /dev/null
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * 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.
+ */
+#ifndef _WESTON_VERTEX_CLIPPING_H
+#define _WESTON_VERTEX_CLIPPING_H
+
+struct polygon8 {
+ float x[8];
+ float y[8];
+ int n;
+};
+
+struct clip_context {
+ struct {
+ float x;
+ float y;
+ } prev;
+
+ struct {
+ float x1, y1;
+ float x2, y2;
+ } clip;
+
+ struct {
+ float *x;
+ float *y;
+ } vertices;
+};
+
+float
+float_difference(float a, float b);
+
+int
+clip_simple(struct clip_context *ctx,
+ struct polygon8 *surf,
+ float *ex,
+ float *ey);
+
+int
+clip_transformed(struct clip_context *ctx,
+ struct polygon8 *surf,
+ float *ex,
+ float *ey);\
+
+#endif
--- /dev/null
+/*
+ * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/* Extensions used by Weston, copied from Mesa's eglmesaext.h, */
+
+#ifndef WESTON_EGL_EXT_H
+#define WESTON_EGL_EXT_H
+
+#ifndef EGL_WL_bind_wayland_display
+#define EGL_WL_bind_wayland_display 1
+
+struct wl_display;
+
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglBindWaylandDisplayWL(EGLDisplay dpy, struct wl_display *display);
+EGLAPI EGLBoolean EGLAPIENTRY eglUnbindWaylandDisplayWL(EGLDisplay dpy, struct wl_display *display);
+#endif
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
+#endif
+
+/*
+ * This is a little different to the tests shipped with EGL implementations,
+ * which wrap the entire thing in #ifndef EGL_WL_bind_wayland_display, then go
+ * on to define both BindWaylandDisplay and QueryWaylandBuffer.
+ *
+ * Unfortunately, some implementations (particularly the version of Mesa shipped
+ * in Ubuntu 12.04) define EGL_WL_bind_wayland_display, but then only provide
+ * prototypes for (Un)BindWaylandDisplay, completely omitting
+ * QueryWaylandBuffer.
+ *
+ * Detect this, and provide our own definitions if necessary.
+ */
+#ifndef EGL_WAYLAND_BUFFER_WL
+#define EGL_WAYLAND_BUFFER_WL 0x31D5 /* eglCreateImageKHR target */
+#define EGL_WAYLAND_PLANE_WL 0x31D6 /* eglCreateImageKHR target */
+
+#define EGL_TEXTURE_Y_U_V_WL 0x31D7
+#define EGL_TEXTURE_Y_UV_WL 0x31D8
+#define EGL_TEXTURE_Y_XUXV_WL 0x31D9
+#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
+
+struct wl_resource;
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryWaylandBufferWL(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
+#endif
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
+#endif
+
+#ifndef EGL_WL_create_wayland_buffer_from_image
+#define EGL_WL_create_wayland_buffer_from_image 1
+
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI struct wl_buffer * EGLAPIENTRY eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, EGLImageKHR image);
+#endif
+typedef struct wl_buffer * (EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL) (EGLDisplay dpy, EGLImageKHR image);
+#endif
+
+#ifndef EGL_TEXTURE_EXTERNAL_WL
+#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
+#endif
+
+#ifndef EGL_BUFFER_AGE_EXT
+#define EGL_BUFFER_AGE_EXT 0x313D
+#endif
+
+#ifndef EGL_WAYLAND_Y_INVERTED_WL
+#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB /* eglQueryWaylandBufferWL attribute */
+#endif
+
+/* Mesas gl2ext.h and probably Khronos upstream defined
+ * GL_EXT_unpack_subimage with non _EXT suffixed GL_UNPACK_* tokens.
+ * In case we're using that mess, manually define the _EXT versions
+ * of the tokens here.*/
+#if defined(GL_EXT_unpack_subimage) && !defined(GL_UNPACK_ROW_LENGTH_EXT)
+#define GL_UNPACK_ROW_LENGTH_EXT 0x0CF2
+#define GL_UNPACK_SKIP_ROWS_EXT 0x0CF3
+#define GL_UNPACK_SKIP_PIXELS_EXT 0x0CF4
+#endif
+
+/* Define needed tokens from EGL_EXT_image_dma_buf_import extension
+ * here to avoid having to add ifdefs everywhere.*/
+#ifndef EGL_EXT_image_dma_buf_import
+#define EGL_LINUX_DMA_BUF_EXT 0x3270
+#define EGL_LINUX_DRM_FOURCC_EXT 0x3271
+#define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272
+#define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273
+#define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274
+#define EGL_DMA_BUF_PLANE1_FD_EXT 0x3275
+#define EGL_DMA_BUF_PLANE1_OFFSET_EXT 0x3276
+#define EGL_DMA_BUF_PLANE1_PITCH_EXT 0x3277
+#define EGL_DMA_BUF_PLANE2_FD_EXT 0x3278
+#define EGL_DMA_BUF_PLANE2_OFFSET_EXT 0x3279
+#define EGL_DMA_BUF_PLANE2_PITCH_EXT 0x327A
+#endif
+
+
+#endif
--- /dev/null
+/*
+ * Copyright © 2012 Benjamin Franzke
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <poll.h>
+#include <errno.h>
+
+#include <error.h>
+#include <getopt.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/signalfd.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <linux/vt.h>
+#include <linux/major.h>
+#include <linux/kd.h>
+
+#include <pwd.h>
+#include <grp.h>
+#include <security/pam_appl.h>
+
+#ifdef HAVE_SYSTEMD_LOGIN
+#include <systemd/sd-login.h>
+#endif
+
+#include "weston-launch.h"
+
+#define DRM_MAJOR 226
+
+#ifndef KDSKBMUTE
+#define KDSKBMUTE 0x4B51
+#endif
+
+#ifndef EVIOCREVOKE
+#define EVIOCREVOKE _IOW('E', 0x91, int)
+#endif
+
+#define MAX_ARGV_SIZE 256
+
+#ifdef HAVE_LIBDRM
+
+#include <xf86drm.h>
+
+#else
+
+static inline int
+drmDropMaster(int drm_fd)
+{
+ return 0;
+}
+
+static inline int
+drmSetMaster(int drm_fd)
+{
+ return 0;
+}
+
+#endif
+
+struct weston_launch {
+ struct pam_conv pc;
+ pam_handle_t *ph;
+ int tty;
+ int ttynr;
+ int sock[2];
+ int drm_fd;
+ int last_input_fd;
+ int kb_mode;
+ struct passwd *pw;
+
+ int signalfd;
+
+ pid_t child;
+ int verbose;
+ char *new_user;
+};
+
+union cmsg_data { unsigned char b[4]; int fd; };
+
+static gid_t *
+read_groups(void)
+{
+ int n;
+ gid_t *groups;
+
+ n = getgroups(0, NULL);
+
+ if (n < 0) {
+ fprintf(stderr, "Unable to retrieve groups: %m\n");
+ return NULL;
+ }
+
+ groups = malloc(n * sizeof(gid_t));
+ if (!groups)
+ return NULL;
+
+ if (getgroups(n, groups) < 0) {
+ fprintf(stderr, "Unable to retrieve groups: %m\n");
+ free(groups);
+ return NULL;
+ }
+ return groups;
+}
+
+static bool
+weston_launch_allowed(struct weston_launch *wl)
+{
+ struct group *gr;
+ gid_t *groups;
+ int i;
+#ifdef HAVE_SYSTEMD_LOGIN
+ char *session, *seat;
+ int err;
+#endif
+
+ if (getuid() == 0)
+ return true;
+
+ gr = getgrnam("weston-launch");
+ if (gr) {
+ groups = read_groups();
+ if (groups) {
+ for (i = 0; groups[i]; ++i) {
+ if (groups[i] == gr->gr_gid) {
+ free(groups);
+ return true;
+ }
+ }
+ free(groups);
+ }
+ }
+
+#ifdef HAVE_SYSTEMD_LOGIN
+ err = sd_pid_get_session(getpid(), &session);
+ if (err == 0 && session) {
+ if (sd_session_is_active(session) &&
+ sd_session_get_seat(session, &seat) == 0) {
+ free(seat);
+ free(session);
+ return true;
+ }
+ free(session);
+ }
+#endif
+
+ return false;
+}
+
+static int
+pam_conversation_fn(int msg_count,
+ const struct pam_message **messages,
+ struct pam_response **responses,
+ void *user_data)
+{
+ return PAM_SUCCESS;
+}
+
+static int
+setup_pam(struct weston_launch *wl)
+{
+ int err;
+
+ wl->pc.conv = pam_conversation_fn;
+ wl->pc.appdata_ptr = wl;
+
+ err = pam_start("login", wl->pw->pw_name, &wl->pc, &wl->ph);
+ if (err != PAM_SUCCESS) {
+ fprintf(stderr, "failed to start pam transaction: %d: %s\n",
+ err, pam_strerror(wl->ph, err));
+ return -1;
+ }
+
+ err = pam_set_item(wl->ph, PAM_TTY, ttyname(wl->tty));
+ if (err != PAM_SUCCESS) {
+ fprintf(stderr, "failed to set PAM_TTY item: %d: %s\n",
+ err, pam_strerror(wl->ph, err));
+ return -1;
+ }
+
+ err = pam_open_session(wl->ph, 0);
+ if (err != PAM_SUCCESS) {
+ fprintf(stderr, "failed to open pam session: %d: %s\n",
+ err, pam_strerror(wl->ph, err));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+setup_launcher_socket(struct weston_launch *wl)
+{
+ if (socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, wl->sock) < 0)
+ error(1, errno, "socketpair failed");
+
+ if (fcntl(wl->sock[0], F_SETFD, FD_CLOEXEC) < 0)
+ error(1, errno, "fcntl failed");
+
+ return 0;
+}
+
+static int
+setup_signals(struct weston_launch *wl)
+{
+ int ret;
+ sigset_t mask;
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof sa);
+ sa.sa_handler = SIG_DFL;
+ sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+ ret = sigaction(SIGCHLD, &sa, NULL);
+ assert(ret == 0);
+
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = 0;
+ sigaction(SIGHUP, &sa, NULL);
+
+ ret = sigemptyset(&mask);
+ assert(ret == 0);
+ sigaddset(&mask, SIGCHLD);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+ sigaddset(&mask, SIGUSR1);
+ sigaddset(&mask, SIGUSR2);
+ ret = sigprocmask(SIG_BLOCK, &mask, NULL);
+ assert(ret == 0);
+
+ wl->signalfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
+ if (wl->signalfd < 0)
+ return -errno;
+
+ return 0;
+}
+
+static void
+setenv_fd(const char *env, int fd)
+{
+ char buf[32];
+
+ snprintf(buf, sizeof buf, "%d", fd);
+ setenv(env, buf, 1);
+}
+
+static int
+send_reply(struct weston_launch *wl, int reply)
+{
+ int len;
+
+ do {
+ len = send(wl->sock[0], &reply, sizeof reply, 0);
+ } while (len < 0 && errno == EINTR);
+
+ return len;
+}
+
+static int
+handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
+{
+ int fd = -1, ret = -1;
+ char control[CMSG_SPACE(sizeof(fd))];
+ struct cmsghdr *cmsg;
+ struct stat s;
+ struct msghdr nmsg;
+ struct iovec iov;
+ struct weston_launcher_open *message;
+ union cmsg_data *data;
+
+ message = msg->msg_iov->iov_base;
+ if ((size_t)len < sizeof(*message))
+ goto err0;
+
+ /* Ensure path is null-terminated */
+ ((char *) message)[len-1] = '\0';
+
+ fd = open(message->path, message->flags);
+ if (fd < 0) {
+ fprintf(stderr, "Error opening device %s: %m\n",
+ message->path);
+ goto err0;
+ }
+
+ if (fstat(fd, &s) < 0) {
+ close(fd);
+ fd = -1;
+ fprintf(stderr, "Failed to stat %s\n", message->path);
+ goto err0;
+ }
+
+ if (major(s.st_rdev) != INPUT_MAJOR &&
+ major(s.st_rdev) != DRM_MAJOR) {
+ close(fd);
+ fd = -1;
+ fprintf(stderr, "Device %s is not an input or drm device\n",
+ message->path);
+ goto err0;
+ }
+
+err0:
+ memset(&nmsg, 0, sizeof nmsg);
+ nmsg.msg_iov = &iov;
+ nmsg.msg_iovlen = 1;
+ if (fd != -1) {
+ nmsg.msg_control = control;
+ nmsg.msg_controllen = sizeof control;
+ cmsg = CMSG_FIRSTHDR(&nmsg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ data = (union cmsg_data *) CMSG_DATA(cmsg);
+ data->fd = fd;
+ nmsg.msg_controllen = cmsg->cmsg_len;
+ ret = 0;
+ }
+ iov.iov_base = &ret;
+ iov.iov_len = sizeof ret;
+
+ if (wl->verbose)
+ fprintf(stderr, "weston-launch: opened %s: ret: %d, fd: %d\n",
+ message->path, ret, fd);
+ do {
+ len = sendmsg(wl->sock[0], &nmsg, 0);
+ } while (len < 0 && errno == EINTR);
+
+ if (len < 0)
+ return -1;
+
+ if (fd != -1 && major(s.st_rdev) == DRM_MAJOR)
+ wl->drm_fd = fd;
+ if (fd != -1 && major(s.st_rdev) == INPUT_MAJOR &&
+ wl->last_input_fd < fd)
+ wl->last_input_fd = fd;
+
+ return 0;
+}
+
+static int
+handle_socket_msg(struct weston_launch *wl)
+{
+ char control[CMSG_SPACE(sizeof(int))];
+ char buf[BUFSIZ];
+ struct msghdr msg;
+ struct iovec iov;
+ int ret = -1;
+ ssize_t len;
+ struct weston_launcher_message *message;
+
+ memset(&msg, 0, sizeof(msg));
+ iov.iov_base = buf;
+ iov.iov_len = sizeof buf;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof control;
+
+ do {
+ len = recvmsg(wl->sock[0], &msg, 0);
+ } while (len < 0 && errno == EINTR);
+
+ if (len < 1)
+ return -1;
+
+ message = (void *) buf;
+ switch (message->opcode) {
+ case WESTON_LAUNCHER_OPEN:
+ ret = handle_open(wl, &msg, len);
+ break;
+ }
+
+ return ret;
+}
+
+static void
+quit(struct weston_launch *wl, int status)
+{
+ struct vt_mode mode = { 0 };
+ int err;
+
+ close(wl->signalfd);
+ close(wl->sock[0]);
+
+ if (wl->new_user) {
+ err = pam_close_session(wl->ph, 0);
+ if (err)
+ fprintf(stderr, "pam_close_session failed: %d: %s\n",
+ err, pam_strerror(wl->ph, err));
+ pam_end(wl->ph, err);
+ }
+
+ if (ioctl(wl->tty, KDSKBMUTE, 0) &&
+ ioctl(wl->tty, KDSKBMODE, wl->kb_mode))
+ fprintf(stderr, "failed to restore keyboard mode: %m\n");
+
+ if (ioctl(wl->tty, KDSETMODE, KD_TEXT))
+ fprintf(stderr, "failed to set KD_TEXT mode on tty: %m\n");
+
+ /* We have to drop master before we switch the VT back in
+ * VT_AUTO, so we don't risk switching to a VT with another
+ * display server, that will then fail to set drm master. */
+ drmDropMaster(wl->drm_fd);
+
+ mode.mode = VT_AUTO;
+ if (ioctl(wl->tty, VT_SETMODE, &mode) < 0)
+ fprintf(stderr, "could not reset vt handling\n");
+
+ exit(status);
+}
+
+static void
+close_input_fds(struct weston_launch *wl)
+{
+ struct stat s;
+ int fd;
+
+ for (fd = 3; fd <= wl->last_input_fd; fd++) {
+ if (fstat(fd, &s) == 0 && major(s.st_rdev) == INPUT_MAJOR) {
+ /* EVIOCREVOKE may fail if the kernel doesn't
+ * support it, but all we can do is ignore it. */
+ ioctl(fd, EVIOCREVOKE, 0);
+ close(fd);
+ }
+ }
+}
+
+static int
+handle_signal(struct weston_launch *wl)
+{
+ struct signalfd_siginfo sig;
+ int pid, status, ret;
+
+ if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) {
+ error(0, errno, "reading signalfd failed");
+ return -1;
+ }
+
+ switch (sig.ssi_signo) {
+ case SIGCHLD:
+ pid = waitpid(-1, &status, 0);
+ if (pid == wl->child) {
+ wl->child = 0;
+ if (WIFEXITED(status))
+ ret = WEXITSTATUS(status);
+ else if (WIFSIGNALED(status))
+ /*
+ * If weston dies because of signal N, we
+ * return 10+N. This is distinct from
+ * weston-launch dying because of a signal
+ * (128+N).
+ */
+ ret = 10 + WTERMSIG(status);
+ else
+ ret = 0;
+ quit(wl, ret);
+ }
+ break;
+ case SIGTERM:
+ case SIGINT:
+ if (wl->child)
+ kill(wl->child, sig.ssi_signo);
+ break;
+ case SIGUSR1:
+ send_reply(wl, WESTON_LAUNCHER_DEACTIVATE);
+ close_input_fds(wl);
+ drmDropMaster(wl->drm_fd);
+ ioctl(wl->tty, VT_RELDISP, 1);
+ break;
+ case SIGUSR2:
+ ioctl(wl->tty, VT_RELDISP, VT_ACKACQ);
+ drmSetMaster(wl->drm_fd);
+ send_reply(wl, WESTON_LAUNCHER_ACTIVATE);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+setup_tty(struct weston_launch *wl, const char *tty)
+{
+ struct stat buf;
+ struct vt_mode mode = { 0 };
+ char *t;
+
+ if (!wl->new_user) {
+ wl->tty = STDIN_FILENO;
+ } else if (tty) {
+ t = ttyname(STDIN_FILENO);
+ if (t && strcmp(t, tty) == 0)
+ wl->tty = STDIN_FILENO;
+ else
+ wl->tty = open(tty, O_RDWR | O_NOCTTY);
+ } else {
+ int tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
+ char filename[16];
+
+ if (tty0 < 0)
+ error(1, errno, "could not open tty0");
+
+ if (ioctl(tty0, VT_OPENQRY, &wl->ttynr) < 0 || wl->ttynr == -1)
+ error(1, errno, "failed to find non-opened console");
+
+ snprintf(filename, sizeof filename, "/dev/tty%d", wl->ttynr);
+ wl->tty = open(filename, O_RDWR | O_NOCTTY);
+ close(tty0);
+ }
+
+ if (wl->tty < 0)
+ error(1, errno, "failed to open tty");
+
+ if (fstat(wl->tty, &buf) == -1 ||
+ major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0)
+ error(1, 0, "weston-launch must be run from a virtual terminal");
+
+ if (tty) {
+ if (fstat(wl->tty, &buf) < 0)
+ error(1, errno, "stat %s failed", tty);
+
+ if (major(buf.st_rdev) != TTY_MAJOR)
+ error(1, 0, "invalid tty device: %s", tty);
+
+ wl->ttynr = minor(buf.st_rdev);
+ }
+
+ if (ioctl(wl->tty, KDGKBMODE, &wl->kb_mode))
+ error(1, errno, "failed to get current keyboard mode: %m\n");
+
+ if (ioctl(wl->tty, KDSKBMUTE, 1) &&
+ ioctl(wl->tty, KDSKBMODE, K_OFF))
+ error(1, errno, "failed to set K_OFF keyboard mode: %m\n");
+
+ if (ioctl(wl->tty, KDSETMODE, KD_GRAPHICS))
+ error(1, errno, "failed to set KD_GRAPHICS mode on tty: %m\n");
+
+ mode.mode = VT_PROCESS;
+ mode.relsig = SIGUSR1;
+ mode.acqsig = SIGUSR2;
+ if (ioctl(wl->tty, VT_SETMODE, &mode) < 0)
+ error(1, errno, "failed to take control of vt handling\n");
+
+ return 0;
+}
+
+static void
+setup_session(struct weston_launch *wl)
+{
+ char **env;
+ char *term;
+ int i;
+
+ if (wl->tty != STDIN_FILENO) {
+ if (setsid() < 0)
+ error(1, errno, "setsid failed");
+ if (ioctl(wl->tty, TIOCSCTTY, 0) < 0)
+ error(1, errno, "TIOCSCTTY failed - tty is in use");
+ }
+
+ term = getenv("TERM");
+ clearenv();
+ if (term)
+ setenv("TERM", term, 1);
+ setenv("USER", wl->pw->pw_name, 1);
+ setenv("LOGNAME", wl->pw->pw_name, 1);
+ setenv("HOME", wl->pw->pw_dir, 1);
+ setenv("SHELL", wl->pw->pw_shell, 1);
+
+ env = pam_getenvlist(wl->ph);
+ if (env) {
+ for (i = 0; env[i]; ++i) {
+ if (putenv(env[i]) != 0)
+ error(0, 0, "putenv %s failed", env[i]);
+ }
+ free(env);
+ }
+}
+
+static void
+drop_privileges(struct weston_launch *wl)
+{
+ if (setgid(wl->pw->pw_gid) < 0 ||
+#ifdef HAVE_INITGROUPS
+ initgroups(wl->pw->pw_name, wl->pw->pw_gid) < 0 ||
+#endif
+ setuid(wl->pw->pw_uid) < 0)
+ error(1, errno, "dropping privileges failed");
+}
+
+static void
+launch_compositor(struct weston_launch *wl, int argc, char *argv[])
+{
+ char *child_argv[MAX_ARGV_SIZE];
+ sigset_t mask;
+ int i;
+
+ if (wl->verbose)
+ printf("weston-launch: spawned weston with pid: %d\n", getpid());
+ if (wl->new_user)
+ setup_session(wl);
+
+ if (geteuid() == 0)
+ drop_privileges(wl);
+
+ setenv_fd("WESTON_TTY_FD", wl->tty);
+ setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]);
+
+ unsetenv("DISPLAY");
+
+ /* Do not give our signal mask to the new process. */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGTERM);
+ sigaddset(&mask, SIGCHLD);
+ sigaddset(&mask, SIGINT);
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+
+ child_argv[0] = "/bin/sh";
+ child_argv[1] = "-l";
+ child_argv[2] = "-c";
+ child_argv[3] = BINDIR "/weston \"$@\"";
+ child_argv[4] = "weston";
+ for (i = 0; i < argc; ++i)
+ child_argv[5 + i] = argv[i];
+ child_argv[5 + i] = NULL;
+
+ execv(child_argv[0], child_argv);
+ error(1, errno, "exec failed");
+}
+
+static void
+help(const char *name)
+{
+ fprintf(stderr, "Usage: %s [args...] [-- [weston args..]]\n", name);
+ fprintf(stderr, " -u, --user Start session as specified username\n");
+ fprintf(stderr, " -t, --tty Start session on alternative tty\n");
+ fprintf(stderr, " -v, --verbose Be verbose\n");
+ fprintf(stderr, " -h, --help Display this help message\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct weston_launch wl;
+ int i, c;
+ char *tty = NULL;
+ struct option opts[] = {
+ { "user", required_argument, NULL, 'u' },
+ { "tty", required_argument, NULL, 't' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { 0, 0, NULL, 0 }
+ };
+
+ memset(&wl, 0, sizeof wl);
+
+ while ((c = getopt_long(argc, argv, "u:t::vh", opts, &i)) != -1) {
+ switch (c) {
+ case 'u':
+ wl.new_user = optarg;
+ if (getuid() != 0)
+ error(1, 0, "Permission denied. -u allowed for root only");
+ break;
+ case 't':
+ tty = optarg;
+ break;
+ case 'v':
+ wl.verbose = 1;
+ break;
+ case 'h':
+ help("weston-launch");
+ exit(EXIT_FAILURE);
+ default:
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if ((argc - optind) > (MAX_ARGV_SIZE - 6))
+ error(1, E2BIG, "Too many arguments to pass to weston");
+
+ if (wl.new_user)
+ wl.pw = getpwnam(wl.new_user);
+ else
+ wl.pw = getpwuid(getuid());
+ if (wl.pw == NULL)
+ error(1, errno, "failed to get username");
+
+ if (!weston_launch_allowed(&wl))
+ error(1, 0, "Permission denied. You should either:\n"
+#ifdef HAVE_SYSTEMD_LOGIN
+ " - run from an active and local (systemd) session.\n"
+#else
+ " - enable systemd session support for weston-launch.\n"
+#endif
+ " - or add yourself to the 'weston-launch' group.");
+
+ if (setup_tty(&wl, tty) < 0)
+ exit(EXIT_FAILURE);
+
+ if (wl.new_user && setup_pam(&wl) < 0)
+ exit(EXIT_FAILURE);
+
+ if (setup_launcher_socket(&wl) < 0)
+ exit(EXIT_FAILURE);
+
+ if (setup_signals(&wl) < 0)
+ exit(EXIT_FAILURE);
+
+ wl.child = fork();
+ if (wl.child == -1)
+ error(EXIT_FAILURE, errno, "fork failed");
+
+ if (wl.child == 0)
+ launch_compositor(&wl, argc - optind, argv + optind);
+
+ close(wl.sock[1]);
+ if (wl.tty != STDIN_FILENO)
+ close(wl.tty);
+
+ while (1) {
+ struct pollfd fds[2];
+ int n;
+
+ fds[0].fd = wl.sock[0];
+ fds[0].events = POLLIN;
+ fds[1].fd = wl.signalfd;
+ fds[1].events = POLLIN;
+
+ n = poll(fds, 2, -1);
+ if (n < 0)
+ error(0, errno, "poll failed");
+ if (fds[0].revents & POLLIN)
+ handle_socket_msg(&wl);
+ if (fds[1].revents)
+ handle_signal(&wl);
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 2012 Benjamin Franzke
+ *
+ * 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.
+ */
+
+#ifndef _WESTON_LAUNCH_H_
+#define _WESTON_LAUNCH_H_
+
+enum weston_launcher_opcode {
+ WESTON_LAUNCHER_OPEN,
+};
+
+enum weston_launcher_event {
+ WESTON_LAUNCHER_SUCCESS,
+ WESTON_LAUNCHER_ACTIVATE,
+ WESTON_LAUNCHER_DEACTIVATE
+};
+
+struct weston_launcher_message {
+ int opcode;
+};
+
+struct weston_launcher_open {
+ struct weston_launcher_message header;
+ int flags;
+ char path[0];
+};
+
+#endif
--- /dev/null
+/*
+ * Copyright © 2012 Scott Moreau
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "compositor.h"
+#include "text-cursor-position-server-protocol.h"
+#include "shared/helpers.h"
+
+static void
+weston_zoom_frame_z(struct weston_animation *animation,
+ struct weston_output *output, uint32_t msecs)
+{
+ if (animation->frame_counter <= 1)
+ output->zoom.spring_z.timestamp = msecs;
+
+ weston_spring_update(&output->zoom.spring_z, msecs);
+
+ if (output->zoom.spring_z.current > output->zoom.max_level)
+ output->zoom.spring_z.current = output->zoom.max_level;
+ else if (output->zoom.spring_z.current < 0.0)
+ output->zoom.spring_z.current = 0.0;
+
+ if (weston_spring_done(&output->zoom.spring_z)) {
+ if (output->zoom.active && output->zoom.level <= 0.0) {
+ output->zoom.active = false;
+ output->zoom.seat = NULL;
+ output->disable_planes--;
+ wl_list_remove(&output->zoom.motion_listener.link);
+ }
+ output->zoom.spring_z.current = output->zoom.level;
+ wl_list_remove(&animation->link);
+ wl_list_init(&animation->link);
+ }
+
+ output->dirty = 1;
+ weston_output_damage(output);
+}
+
+static void
+zoom_area_center_from_point(struct weston_output *output,
+ double *x, double *y)
+{
+ float level = output->zoom.spring_z.current;
+
+ *x = (*x - output->x) * level + output->width / 2.;
+ *y = (*y - output->y) * level + output->height / 2.;
+}
+
+static void
+weston_output_update_zoom_transform(struct weston_output *output)
+{
+ double x = output->zoom.current.x; /* global pointer coords */
+ double y = output->zoom.current.y;
+ float level;
+
+ level = output->zoom.spring_z.current;
+
+ if (!output->zoom.active || level > output->zoom.max_level ||
+ level == 0.0f)
+ return;
+
+ zoom_area_center_from_point(output, &x, &y);
+
+ output->zoom.trans_x = x - output->width / 2;
+ output->zoom.trans_y = y - output->height / 2;
+
+ if (output->zoom.trans_x < 0)
+ output->zoom.trans_x = 0;
+ if (output->zoom.trans_y < 0)
+ output->zoom.trans_y = 0;
+ if (output->zoom.trans_x > level * output->width)
+ output->zoom.trans_x = level * output->width;
+ if (output->zoom.trans_y > level * output->height)
+ output->zoom.trans_y = level * output->height;
+}
+
+static void
+weston_zoom_transition(struct weston_output *output)
+{
+ if (output->zoom.level != output->zoom.spring_z.current) {
+ output->zoom.spring_z.target = output->zoom.level;
+ if (wl_list_empty(&output->zoom.animation_z.link)) {
+ output->zoom.animation_z.frame_counter = 0;
+ wl_list_insert(output->animation_list.prev,
+ &output->zoom.animation_z.link);
+ }
+ }
+
+ output->dirty = 1;
+ weston_output_damage(output);
+}
+
+WL_EXPORT void
+weston_output_update_zoom(struct weston_output *output)
+{
+ struct weston_seat *seat = output->zoom.seat;
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+
+ assert(output->zoom.active);
+
+ output->zoom.current.x = wl_fixed_to_double(pointer->x);
+ output->zoom.current.y = wl_fixed_to_double(pointer->y);
+
+ weston_zoom_transition(output);
+ weston_output_update_zoom_transform(output);
+}
+
+static void
+motion(struct wl_listener *listener, void *data)
+{
+ struct weston_output_zoom *zoom =
+ container_of(listener, struct weston_output_zoom, motion_listener);
+ struct weston_output *output =
+ container_of(zoom, struct weston_output, zoom);
+
+ weston_output_update_zoom(output);
+}
+
+WL_EXPORT void
+weston_output_activate_zoom(struct weston_output *output,
+ struct weston_seat *seat)
+{
+ struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+
+ if (output->zoom.active)
+ return;
+
+ output->zoom.active = true;
+ output->zoom.seat = seat;
+ output->disable_planes++;
+ wl_signal_add(&pointer->motion_signal,
+ &output->zoom.motion_listener);
+}
+
+WL_EXPORT void
+weston_output_init_zoom(struct weston_output *output)
+{
+ output->zoom.active = false;
+ output->zoom.seat = NULL;
+ output->zoom.increment = 0.07;
+ output->zoom.max_level = 0.95;
+ output->zoom.level = 0.0;
+ output->zoom.trans_x = 0.0;
+ output->zoom.trans_y = 0.0;
+ weston_spring_init(&output->zoom.spring_z, 250.0, 0.0, 0.0);
+ output->zoom.spring_z.friction = 1000;
+ output->zoom.animation_z.frame = weston_zoom_frame_z;
+ wl_list_init(&output->zoom.animation_z.link);
+ output->zoom.motion_listener.notify = motion;
+}
+++ /dev/null
-/*
- * Copyright © 2011 Intel Corporation
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <math.h>
-
-#include <unistd.h>
-#include <fcntl.h>
-
-#include "compositor.h"
-#include "shared/helpers.h"
-
-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;
- spring->clip = WESTON_SPRING_OVERSHOOT;
- spring->min = 0.0;
- spring->max = 1.0;
-}
-
-WL_EXPORT void
-weston_spring_update(struct weston_spring *spring, uint32_t msec)
-{
- double force, v, current, step;
-
- /* Limit the number of executions of the loop below by ensuring that
- * the timestamp for last update of the spring is no more than 1s ago.
- * This handles the case where time moves backwards or forwards in
- * large jumps.
- */
- if (msec - spring->timestamp > 1000) {
- weston_log("unexpectedly large timestamp jump (from %u to %u)\n",
- spring->timestamp, msec);
- spring->timestamp = msec - 1000;
- }
-
- 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;
-
- switch (spring->clip) {
- case WESTON_SPRING_OVERSHOOT:
- break;
-
- case WESTON_SPRING_CLAMP:
- if (spring->current > spring->max) {
- spring->current = spring->max;
- spring->previous = spring->max;
- } else if (spring->current < 0.0) {
- spring->current = spring->min;
- spring->previous = spring->min;
- }
- break;
-
- case WESTON_SPRING_BOUNCE:
- if (spring->current > spring->max) {
- spring->current =
- 2 * spring->max - spring->current;
- spring->previous =
- 2 * spring->max - spring->previous;
- } else if (spring->current < spring->min) {
- spring->current =
- 2 * spring->min - spring->current;
- spring->previous =
- 2 * spring->min - spring->previous;
- }
- break;
- }
-
- spring->timestamp += 4;
- }
-}
-
-WL_EXPORT int
-weston_spring_done(struct weston_spring *spring)
-{
- return fabs(spring->previous - spring->target) < 0.002 &&
- fabs(spring->current - spring->target) < 0.002;
-}
-
-typedef void (*weston_view_animation_frame_func_t)(struct weston_view_animation *animation);
-
-struct weston_view_animation {
- struct weston_view *view;
- struct weston_animation animation;
- struct weston_spring spring;
- struct weston_transform transform;
- struct wl_listener listener;
- float start, stop;
- weston_view_animation_frame_func_t frame;
- weston_view_animation_frame_func_t reset;
- weston_view_animation_done_func_t done;
- void *data;
- void *private;
-};
-
-WL_EXPORT void
-weston_view_animation_destroy(struct weston_view_animation *animation)
-{
- wl_list_remove(&animation->animation.link);
- wl_list_remove(&animation->listener.link);
- wl_list_remove(&animation->transform.link);
- if (animation->reset)
- animation->reset(animation);
- weston_view_geometry_dirty(animation->view);
- if (animation->done)
- animation->done(animation, animation->data);
- free(animation);
-}
-
-static void
-handle_animation_view_destroy(struct wl_listener *listener, void *data)
-{
- struct weston_view_animation *animation =
- container_of(listener,
- struct weston_view_animation, listener);
-
- weston_view_animation_destroy(animation);
-}
-
-static void
-weston_view_animation_frame(struct weston_animation *base,
- struct weston_output *output, uint32_t msecs)
-{
- struct weston_view_animation *animation =
- container_of(base,
- struct weston_view_animation, animation);
- struct weston_compositor *compositor =
- animation->view->surface->compositor;
-
- if (base->frame_counter <= 1)
- animation->spring.timestamp = msecs;
-
- weston_spring_update(&animation->spring, msecs);
-
- if (weston_spring_done(&animation->spring)) {
- weston_view_schedule_repaint(animation->view);
- weston_view_animation_destroy(animation);
- return;
- }
-
- if (animation->frame)
- animation->frame(animation);
-
- weston_view_geometry_dirty(animation->view);
- weston_view_schedule_repaint(animation->view);
-
- /* The view's output_mask will be zero if its position is
- * offscreen. Animations should always run but as they are also
- * run off the repaint cycle, if there's nothing to repaint
- * the animation stops running. Therefore if we catch this situation
- * and schedule a repaint on all outputs it will be avoided.
- */
- if (animation->view->output_mask == 0)
- weston_compositor_schedule_repaint(compositor);
-}
-
-static struct weston_view_animation *
-weston_view_animation_create(struct weston_view *view,
- float start, float stop,
- weston_view_animation_frame_func_t frame,
- weston_view_animation_frame_func_t reset,
- weston_view_animation_done_func_t done,
- void *data,
- void *private)
-{
- struct weston_view_animation *animation;
-
- animation = malloc(sizeof *animation);
- if (!animation)
- return NULL;
-
- animation->view = view;
- animation->frame = frame;
- animation->reset = reset;
- animation->done = done;
- animation->data = data;
- animation->start = start;
- animation->stop = stop;
- animation->private = private;
-
- weston_matrix_init(&animation->transform.matrix);
- wl_list_insert(&view->geometry.transformation_list,
- &animation->transform.link);
-
- animation->animation.frame = weston_view_animation_frame;
-
- animation->listener.notify = handle_animation_view_destroy;
- wl_signal_add(&view->destroy_signal, &animation->listener);
-
- wl_list_insert(&view->output->animation_list,
- &animation->animation.link);
-
- return animation;
-}
-
-static void
-weston_view_animation_run(struct weston_view_animation *animation)
-{
- animation->animation.frame_counter = 0;
- weston_view_animation_frame(&animation->animation, NULL, 0);
-}
-
-static void
-reset_alpha(struct weston_view_animation *animation)
-{
- struct weston_view *view = animation->view;
-
- view->alpha = animation->stop;
-}
-
-static void
-zoom_frame(struct weston_view_animation *animation)
-{
- struct weston_view *es = animation->view;
- float scale;
-
- scale = animation->start +
- (animation->stop - animation->start) *
- animation->spring.current;
- weston_matrix_init(&animation->transform.matrix);
- weston_matrix_translate(&animation->transform.matrix,
- -0.5f * es->surface->width,
- -0.5f * es->surface->height, 0);
- weston_matrix_scale(&animation->transform.matrix, scale, scale, scale);
- weston_matrix_translate(&animation->transform.matrix,
- 0.5f * es->surface->width,
- 0.5f * es->surface->height, 0);
-
- es->alpha = animation->spring.current;
- if (es->alpha > 1.0)
- es->alpha = 1.0;
-}
-
-WL_EXPORT struct weston_view_animation *
-weston_zoom_run(struct weston_view *view, float start, float stop,
- weston_view_animation_done_func_t done, void *data)
-{
- struct weston_view_animation *zoom;
-
- zoom = weston_view_animation_create(view, start, stop,
- zoom_frame, reset_alpha,
- done, data, NULL);
-
- if (zoom == NULL)
- return NULL;
-
- weston_spring_init(&zoom->spring, 300.0, start, stop);
- zoom->spring.friction = 1400;
- zoom->spring.previous = start - (stop - start) * 0.03;
-
- weston_view_animation_run(zoom);
-
- return zoom;
-}
-
-static void
-fade_frame(struct weston_view_animation *animation)
-{
- if (animation->spring.current > 0.999)
- animation->view->alpha = 1;
- else if (animation->spring.current < 0.001 )
- animation->view->alpha = 0;
- else
- animation->view->alpha = animation->spring.current;
-}
-
-WL_EXPORT struct weston_view_animation *
-weston_fade_run(struct weston_view *view,
- float start, float end, float k,
- weston_view_animation_done_func_t done, void *data)
-{
- struct weston_view_animation *fade;
-
- fade = weston_view_animation_create(view, start, end,
- fade_frame, reset_alpha,
- done, data, NULL);
-
- if (fade == NULL)
- return NULL;
-
- weston_spring_init(&fade->spring, 1000.0, start, end);
- fade->spring.friction = 4000;
- fade->spring.previous = start - (end - start) * 0.1;
-
- view->alpha = start;
-
- weston_view_animation_run(fade);
-
- return fade;
-}
-
-WL_EXPORT void
-weston_fade_update(struct weston_view_animation *fade, float target)
-{
- fade->spring.target = target;
- fade->stop = target;
-}
-
-static void
-stable_fade_frame(struct weston_view_animation *animation)
-{
- struct weston_view *back_view;
-
- if (animation->spring.current > 0.999)
- animation->view->alpha = 1;
- else if (animation->spring.current < 0.001 )
- animation->view->alpha = 0;
- else
- animation->view->alpha = animation->spring.current;
-
- back_view = (struct weston_view *) animation->private;
- back_view->alpha =
- (animation->spring.target - animation->view->alpha) /
- (1.0 - animation->view->alpha);
- weston_view_geometry_dirty(back_view);
-}
-
-WL_EXPORT struct weston_view_animation *
-weston_stable_fade_run(struct weston_view *front_view, float start,
- struct weston_view *back_view, float end,
- weston_view_animation_done_func_t done, void *data)
-{
- struct weston_view_animation *fade;
-
- fade = weston_view_animation_create(front_view, 0, 0,
- stable_fade_frame, NULL,
- done, data, back_view);
-
- if (fade == NULL)
- return NULL;
-
- weston_spring_init(&fade->spring, 400, start, end);
- fade->spring.friction = 1150;
-
- front_view->alpha = start;
- back_view->alpha = end;
-
- weston_view_animation_run(fade);
-
- return fade;
-}
-
-static void
-slide_frame(struct weston_view_animation *animation)
-{
- float scale;
-
- scale = animation->start +
- (animation->stop - animation->start) *
- animation->spring.current;
- weston_matrix_init(&animation->transform.matrix);
- weston_matrix_translate(&animation->transform.matrix, 0, scale, 0);
-}
-
-WL_EXPORT struct weston_view_animation *
-weston_slide_run(struct weston_view *view, float start, float stop,
- weston_view_animation_done_func_t done, void *data)
-{
- struct weston_view_animation *animation;
-
- animation = weston_view_animation_create(view, start, stop,
- slide_frame, NULL, done,
- data, NULL);
- if (!animation)
- return NULL;
-
- weston_spring_init(&animation->spring, 400.0, 0.0, 1.0);
- animation->spring.friction = 600;
- animation->spring.clip = WESTON_SPRING_BOUNCE;
-
- weston_view_animation_run(animation);
-
- return animation;
-}
-
-struct weston_move_animation {
- int dx;
- int dy;
- int reverse;
- weston_view_animation_done_func_t done;
-};
-
-static void
-move_frame(struct weston_view_animation *animation)
-{
- struct weston_move_animation *move = animation->private;
- float scale;
- float progress = animation->spring.current;
-
- if (move->reverse)
- progress = 1.0 - progress;
-
- scale = animation->start +
- (animation->stop - animation->start) *
- progress;
- weston_matrix_init(&animation->transform.matrix);
- weston_matrix_scale(&animation->transform.matrix, scale, scale, 1.0f);
- weston_matrix_translate(&animation->transform.matrix,
- move->dx * progress, move->dy * progress,
- 0);
-}
-
-static void
-move_done(struct weston_view_animation *animation, void *data)
-{
- struct weston_move_animation *move = animation->private;
-
- if (move->done)
- move->done(animation, data);
-
- free(move);
-}
-
-WL_EXPORT struct weston_view_animation *
-weston_move_scale_run(struct weston_view *view, int dx, int dy,
- float start, float end, int reverse,
- weston_view_animation_done_func_t done, void *data)
-{
- struct weston_move_animation *move;
- struct weston_view_animation *animation;
-
- move = malloc(sizeof(*move));
- if (!move)
- return NULL;
- move->dx = dx;
- move->dy = dy;
- move->reverse = reverse;
- move->done = done;
-
- animation = weston_view_animation_create(view, start, end, move_frame,
- NULL, move_done, data, move);
-
- if (animation == NULL){
- free(move);
- return NULL;
- }
-
- weston_spring_init(&animation->spring, 400.0, 0.0, 1.0);
- animation->spring.friction = 1150;
-
- weston_view_animation_run(animation);
-
- return animation;
-}
+++ /dev/null
-/*
- * Copyright © 2011-2012 Intel Corporation
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <linux/input.h>
-
-#include "compositor.h"
-#include "shared/helpers.h"
-
-struct weston_binding {
- uint32_t key;
- uint32_t button;
- uint32_t axis;
- uint32_t modifier;
- void *handler;
- void *data;
- struct wl_list link;
-};
-
-static struct weston_binding *
-weston_compositor_add_binding(struct weston_compositor *compositor,
- uint32_t key, uint32_t button, uint32_t axis,
- uint32_t modifier, void *handler, void *data)
-{
- struct weston_binding *binding;
-
- binding = malloc(sizeof *binding);
- if (binding == NULL)
- return NULL;
-
- binding->key = key;
- binding->button = button;
- binding->axis = axis;
- binding->modifier = modifier;
- binding->handler = handler;
- binding->data = data;
-
- return binding;
-}
-
-WL_EXPORT struct weston_binding *
-weston_compositor_add_key_binding(struct weston_compositor *compositor,
- uint32_t key, uint32_t modifier,
- weston_key_binding_handler_t handler,
- void *data)
-{
- struct weston_binding *binding;
-
- binding = weston_compositor_add_binding(compositor, key, 0, 0,
- modifier, handler, data);
- if (binding == NULL)
- return NULL;
-
- wl_list_insert(compositor->key_binding_list.prev, &binding->link);
-
- return binding;
-}
-
-WL_EXPORT struct weston_binding *
-weston_compositor_add_modifier_binding(struct weston_compositor *compositor,
- uint32_t modifier,
- weston_modifier_binding_handler_t handler,
- void *data)
-{
- struct weston_binding *binding;
-
- binding = weston_compositor_add_binding(compositor, 0, 0, 0,
- modifier, handler, data);
- if (binding == NULL)
- return NULL;
-
- wl_list_insert(compositor->modifier_binding_list.prev, &binding->link);
-
- return binding;
-}
-
-WL_EXPORT struct weston_binding *
-weston_compositor_add_button_binding(struct weston_compositor *compositor,
- uint32_t button, uint32_t modifier,
- weston_button_binding_handler_t handler,
- void *data)
-{
- struct weston_binding *binding;
-
- binding = weston_compositor_add_binding(compositor, 0, button, 0,
- modifier, handler, data);
- if (binding == NULL)
- return NULL;
-
- wl_list_insert(compositor->button_binding_list.prev, &binding->link);
-
- return binding;
-}
-
-WL_EXPORT struct weston_binding *
-weston_compositor_add_touch_binding(struct weston_compositor *compositor,
- uint32_t modifier,
- weston_touch_binding_handler_t handler,
- void *data)
-{
- struct weston_binding *binding;
-
- binding = weston_compositor_add_binding(compositor, 0, 0, 0,
- modifier, handler, data);
- if (binding == NULL)
- return NULL;
-
- wl_list_insert(compositor->touch_binding_list.prev, &binding->link);
-
- return binding;
-}
-
-WL_EXPORT struct weston_binding *
-weston_compositor_add_axis_binding(struct weston_compositor *compositor,
- uint32_t axis, uint32_t modifier,
- weston_axis_binding_handler_t handler,
- void *data)
-{
- struct weston_binding *binding;
-
- binding = weston_compositor_add_binding(compositor, 0, 0, axis,
- modifier, handler, data);
- if (binding == NULL)
- return NULL;
-
- wl_list_insert(compositor->axis_binding_list.prev, &binding->link);
-
- return binding;
-}
-
-WL_EXPORT struct weston_binding *
-weston_compositor_add_debug_binding(struct weston_compositor *compositor,
- uint32_t key,
- weston_key_binding_handler_t handler,
- void *data)
-{
- struct weston_binding *binding;
-
- binding = weston_compositor_add_binding(compositor, key, 0, 0, 0,
- handler, data);
-
- wl_list_insert(compositor->debug_binding_list.prev, &binding->link);
-
- return binding;
-}
-
-WL_EXPORT void
-weston_binding_destroy(struct weston_binding *binding)
-{
- wl_list_remove(&binding->link);
- free(binding);
-}
-
-void
-weston_binding_list_destroy_all(struct wl_list *list)
-{
- struct weston_binding *binding, *tmp;
-
- wl_list_for_each_safe(binding, tmp, list, link)
- weston_binding_destroy(binding);
-}
-
-struct binding_keyboard_grab {
- uint32_t key;
- struct weston_keyboard_grab grab;
-};
-
-static void
-binding_key(struct weston_keyboard_grab *grab,
- uint32_t time, uint32_t key, uint32_t state_w)
-{
- struct binding_keyboard_grab *b =
- container_of(grab, struct binding_keyboard_grab, grab);
- struct wl_resource *resource;
- enum wl_keyboard_key_state state = state_w;
- uint32_t serial;
- struct weston_keyboard *keyboard = grab->keyboard;
- struct wl_display *display = keyboard->seat->compositor->wl_display;
-
- if (key == b->key) {
- if (state == WL_KEYBOARD_KEY_STATE_RELEASED) {
- weston_keyboard_end_grab(grab->keyboard);
- if (keyboard->input_method_resource)
- keyboard->grab = &keyboard->input_method_grab;
- free(b);
- } else {
- /* Don't send the key press event for the binding key */
- return;
- }
- }
- if (!wl_list_empty(&keyboard->focus_resource_list)) {
- serial = wl_display_next_serial(display);
- wl_resource_for_each(resource, &keyboard->focus_resource_list) {
- wl_keyboard_send_key(resource,
- serial,
- time,
- key,
- state);
- }
- }
-}
-
-static void
-binding_modifiers(struct weston_keyboard_grab *grab, uint32_t serial,
- uint32_t mods_depressed, uint32_t mods_latched,
- uint32_t mods_locked, uint32_t group)
-{
- struct wl_resource *resource;
-
- wl_resource_for_each(resource, &grab->keyboard->focus_resource_list) {
- wl_keyboard_send_modifiers(resource, serial, mods_depressed,
- mods_latched, mods_locked, group);
- }
-}
-
-static void
-binding_cancel(struct weston_keyboard_grab *grab)
-{
- struct binding_keyboard_grab *binding_grab =
- container_of(grab, struct binding_keyboard_grab, grab);
-
- weston_keyboard_end_grab(grab->keyboard);
- free(binding_grab);
-}
-
-static const struct weston_keyboard_grab_interface binding_grab = {
- binding_key,
- binding_modifiers,
- binding_cancel,
-};
-
-static void
-install_binding_grab(struct weston_keyboard *keyboard, uint32_t time,
- uint32_t key, struct weston_surface *focus)
-{
- struct binding_keyboard_grab *grab;
-
- grab = malloc(sizeof *grab);
- grab->key = key;
- grab->grab.interface = &binding_grab;
- weston_keyboard_start_grab(keyboard, &grab->grab);
-
- /* Notify the surface which had the focus before this binding
- * triggered that we stole a keypress from under it, by forcing
- * a wl_keyboard leave/enter pair. The enter event will contain
- * the pressed key in the keys array, so the client will know
- * the exact state of the keyboard.
- * If the old focus surface is different than the new one it
- * means it was changed in the binding handler, so it received
- * the enter event already. */
- if (focus && keyboard->focus == focus) {
- weston_keyboard_set_focus(keyboard, NULL);
- weston_keyboard_set_focus(keyboard, focus);
- }
-}
-
-void
-weston_compositor_run_key_binding(struct weston_compositor *compositor,
- struct weston_keyboard *keyboard,
- uint32_t time, uint32_t key,
- enum wl_keyboard_key_state state)
-{
- struct weston_binding *b, *tmp;
- struct weston_surface *focus;
- struct weston_seat *seat = keyboard->seat;
-
- if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
- return;
-
- /* Invalidate all active modifier bindings. */
- wl_list_for_each(b, &compositor->modifier_binding_list, link)
- b->key = key;
-
- wl_list_for_each_safe(b, tmp, &compositor->key_binding_list, link) {
- if (b->key == key && b->modifier == seat->modifier_state) {
- weston_key_binding_handler_t handler = b->handler;
- focus = keyboard->focus;
- handler(keyboard, time, key, b->data);
-
- /* If this was a key binding and it didn't
- * install a keyboard grab, install one now to
- * swallow the key press. */
- if (keyboard->grab ==
- &keyboard->default_grab)
- install_binding_grab(keyboard,
- time,
- key,
- focus);
- }
- }
-}
-
-void
-weston_compositor_run_modifier_binding(struct weston_compositor *compositor,
- struct weston_keyboard *keyboard,
- enum weston_keyboard_modifier modifier,
- enum wl_keyboard_key_state state)
-{
- struct weston_binding *b, *tmp;
-
- if (keyboard->grab != &keyboard->default_grab)
- return;
-
- wl_list_for_each_safe(b, tmp, &compositor->modifier_binding_list, link) {
- weston_modifier_binding_handler_t handler = b->handler;
-
- if (b->modifier != modifier)
- continue;
-
- /* Prime the modifier binding. */
- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
- b->key = 0;
- continue;
- }
- /* Ignore the binding if a key was pressed in between. */
- else if (b->key != 0) {
- return;
- }
-
- handler(keyboard, modifier, b->data);
- }
-}
-
-void
-weston_compositor_run_button_binding(struct weston_compositor *compositor,
- struct weston_pointer *pointer,
- uint32_t time, uint32_t button,
- enum wl_pointer_button_state state)
-{
- struct weston_binding *b, *tmp;
-
- if (state == WL_POINTER_BUTTON_STATE_RELEASED)
- return;
-
- /* Invalidate all active modifier bindings. */
- wl_list_for_each(b, &compositor->modifier_binding_list, link)
- b->key = button;
-
- wl_list_for_each_safe(b, tmp, &compositor->button_binding_list, link) {
- if (b->button == button &&
- b->modifier == pointer->seat->modifier_state) {
- weston_button_binding_handler_t handler = b->handler;
- handler(pointer, time, button, b->data);
- }
- }
-}
-
-void
-weston_compositor_run_touch_binding(struct weston_compositor *compositor,
- struct weston_touch *touch, uint32_t time,
- int touch_type)
-{
- struct weston_binding *b, *tmp;
-
- if (touch->num_tp != 1 || touch_type != WL_TOUCH_DOWN)
- return;
-
- wl_list_for_each_safe(b, tmp, &compositor->touch_binding_list, link) {
- if (b->modifier == touch->seat->modifier_state) {
- weston_touch_binding_handler_t handler = b->handler;
- handler(touch, time, b->data);
- }
- }
-}
-
-int
-weston_compositor_run_axis_binding(struct weston_compositor *compositor,
- struct weston_pointer *pointer,
- uint32_t time,
- struct weston_pointer_axis_event *event)
-{
- struct weston_binding *b, *tmp;
-
- /* Invalidate all active modifier bindings. */
- wl_list_for_each(b, &compositor->modifier_binding_list, link)
- b->key = event->axis;
-
- wl_list_for_each_safe(b, tmp, &compositor->axis_binding_list, link) {
- if (b->axis == event->axis &&
- b->modifier == pointer->seat->modifier_state) {
- weston_axis_binding_handler_t handler = b->handler;
- handler(pointer, time, event, b->data);
- return 1;
- }
- }
-
- return 0;
-}
-
-int
-weston_compositor_run_debug_binding(struct weston_compositor *compositor,
- struct weston_keyboard *keyboard,
- uint32_t time, uint32_t key,
- enum wl_keyboard_key_state state)
-{
- weston_key_binding_handler_t handler;
- struct weston_binding *binding, *tmp;
- int count = 0;
-
- wl_list_for_each_safe(binding, tmp, &compositor->debug_binding_list, link) {
- if (key != binding->key)
- continue;
-
- count++;
- handler = binding->handler;
- handler(keyboard, time, key, binding->data);
- }
-
- return count;
-}
-
-struct debug_binding_grab {
- struct weston_keyboard_grab grab;
- struct weston_seat *seat;
- uint32_t key[2];
- int key_released[2];
-};
-
-static void
-debug_binding_key(struct weston_keyboard_grab *grab, uint32_t time,
- uint32_t key, uint32_t state)
-{
- struct debug_binding_grab *db = (struct debug_binding_grab *) grab;
- struct weston_compositor *ec = db->seat->compositor;
- struct wl_display *display = ec->wl_display;
- struct wl_resource *resource;
- uint32_t serial;
- int send = 0, terminate = 0;
- int check_binding = 1;
- int i;
- struct wl_list *resource_list;
-
- if (state == WL_KEYBOARD_KEY_STATE_RELEASED) {
- /* Do not run bindings on key releases */
- check_binding = 0;
-
- for (i = 0; i < 2; i++)
- if (key == db->key[i])
- db->key_released[i] = 1;
-
- if (db->key_released[0] && db->key_released[1]) {
- /* All key releases been swalled so end the grab */
- terminate = 1;
- } else if (key != db->key[0] && key != db->key[1]) {
- /* Should not swallow release of other keys */
- send = 1;
- }
- } else if (key == db->key[0] && !db->key_released[0]) {
- /* Do not check bindings for the first press of the binding
- * key. This allows it to be used as a debug shortcut.
- * We still need to swallow this event. */
- check_binding = 0;
- } else if (db->key[1]) {
- /* If we already ran a binding don't process another one since
- * we can't keep track of all the binding keys that were
- * pressed in order to swallow the release events. */
- send = 1;
- check_binding = 0;
- }
-
- if (check_binding) {
- if (weston_compositor_run_debug_binding(ec, grab->keyboard,
- time, key, state)) {
- /* We ran a binding so swallow the press and keep the
- * grab to swallow the released too. */
- send = 0;
- terminate = 0;
- db->key[1] = key;
- } else {
- /* Terminate the grab since the key pressed is not a
- * debug binding key. */
- send = 1;
- terminate = 1;
- }
- }
-
- if (send) {
- serial = wl_display_next_serial(display);
- resource_list = &grab->keyboard->focus_resource_list;
- wl_resource_for_each(resource, resource_list) {
- wl_keyboard_send_key(resource, serial, time, key, state);
- }
- }
-
- if (terminate) {
- weston_keyboard_end_grab(grab->keyboard);
- if (grab->keyboard->input_method_resource)
- grab->keyboard->grab = &grab->keyboard->input_method_grab;
- free(db);
- }
-}
-
-static void
-debug_binding_modifiers(struct weston_keyboard_grab *grab, uint32_t serial,
- uint32_t mods_depressed, uint32_t mods_latched,
- uint32_t mods_locked, uint32_t group)
-{
- struct wl_resource *resource;
- struct wl_list *resource_list;
-
- resource_list = &grab->keyboard->focus_resource_list;
-
- wl_resource_for_each(resource, resource_list) {
- wl_keyboard_send_modifiers(resource, serial, mods_depressed,
- mods_latched, mods_locked, group);
- }
-}
-
-static void
-debug_binding_cancel(struct weston_keyboard_grab *grab)
-{
- struct debug_binding_grab *db = (struct debug_binding_grab *) grab;
-
- weston_keyboard_end_grab(grab->keyboard);
- free(db);
-}
-
-struct weston_keyboard_grab_interface debug_binding_keyboard_grab = {
- debug_binding_key,
- debug_binding_modifiers,
- debug_binding_cancel,
-};
-
-static void
-debug_binding(struct weston_keyboard *keyboard, uint32_t time,
- uint32_t key, void *data)
-{
- struct debug_binding_grab *grab;
-
- grab = calloc(1, sizeof *grab);
- if (!grab)
- return;
-
- grab->seat = keyboard->seat;
- grab->key[0] = key;
- grab->grab.interface = &debug_binding_keyboard_grab;
- weston_keyboard_start_grab(keyboard, &grab->grab);
-}
-
-/** Install the trigger binding for debug bindings.
- *
- * \param compositor The compositor.
- * \param mod The modifier.
- *
- * This will add a key binding for modifier+SHIFT+SPACE that will trigger
- * debug key bindings.
- */
-WL_EXPORT void
-weston_install_debug_key_binding(struct weston_compositor *compositor,
- uint32_t mod)
-{
- weston_compositor_add_key_binding(compositor, KEY_SPACE,
- mod | MODIFIER_SHIFT,
- debug_binding, NULL);
-}
+++ /dev/null
-/*
- * Copyright © 2012 Intel Corporation
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <linux/input.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/uio.h>
-
-#include "compositor.h"
-#include "shared/helpers.h"
-
-struct clipboard_source {
- struct weston_data_source base;
- struct wl_array contents;
- struct clipboard *clipboard;
- struct wl_event_source *event_source;
- uint32_t serial;
- int refcount;
- int fd;
-};
-
-struct clipboard {
- struct weston_seat *seat;
- struct wl_listener selection_listener;
- struct wl_listener destroy_listener;
- struct clipboard_source *source;
-};
-
-static void clipboard_client_create(struct clipboard_source *source, int fd);
-
-static void
-clipboard_source_unref(struct clipboard_source *source)
-{
- char **s;
-
- source->refcount--;
- if (source->refcount > 0)
- return;
-
- if (source->event_source) {
- wl_event_source_remove(source->event_source);
- close(source->fd);
- }
- wl_signal_emit(&source->base.destroy_signal,
- &source->base);
- s = source->base.mime_types.data;
- free(*s);
- wl_array_release(&source->base.mime_types);
- wl_array_release(&source->contents);
- free(source);
-}
-
-static int
-clipboard_source_data(int fd, uint32_t mask, void *data)
-{
- struct clipboard_source *source = data;
- struct clipboard *clipboard = source->clipboard;
- char *p;
- int len, size;
-
- if (source->contents.alloc - source->contents.size < 1024) {
- wl_array_add(&source->contents, 1024);
- source->contents.size -= 1024;
- }
-
- p = source->contents.data + source->contents.size;
- size = source->contents.alloc - source->contents.size;
- len = read(fd, p, size);
- if (len == 0) {
- wl_event_source_remove(source->event_source);
- close(fd);
- source->event_source = NULL;
- } else if (len < 0) {
- clipboard_source_unref(source);
- clipboard->source = NULL;
- } else {
- source->contents.size += len;
- }
-
- return 1;
-}
-
-static void
-clipboard_source_accept(struct weston_data_source *source,
- uint32_t time, const char *mime_type)
-{
-}
-
-static void
-clipboard_source_send(struct weston_data_source *base,
- const char *mime_type, int32_t fd)
-{
- struct clipboard_source *source =
- container_of(base, struct clipboard_source, base);
- char **s;
-
- s = source->base.mime_types.data;
- if (strcmp(mime_type, s[0]) == 0)
- clipboard_client_create(source, fd);
- else
- close(fd);
-}
-
-static void
-clipboard_source_cancel(struct weston_data_source *source)
-{
-}
-
-static struct clipboard_source *
-clipboard_source_create(struct clipboard *clipboard,
- const char *mime_type, uint32_t serial, int fd)
-{
- struct wl_display *display = clipboard->seat->compositor->wl_display;
- struct wl_event_loop *loop = wl_display_get_event_loop(display);
- struct clipboard_source *source;
- char **s;
-
- source = zalloc(sizeof *source);
- if (source == NULL)
- return NULL;
-
- wl_array_init(&source->contents);
- wl_array_init(&source->base.mime_types);
- source->base.resource = NULL;
- source->base.accept = clipboard_source_accept;
- source->base.send = clipboard_source_send;
- source->base.cancel = clipboard_source_cancel;
- wl_signal_init(&source->base.destroy_signal);
- source->refcount = 1;
- source->clipboard = clipboard;
- source->serial = serial;
- source->fd = fd;
-
- s = wl_array_add(&source->base.mime_types, sizeof *s);
- if (s == NULL)
- goto err_add;
- *s = strdup(mime_type);
- if (*s == NULL)
- goto err_strdup;
- source->event_source =
- wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
- clipboard_source_data, source);
- if (source->event_source == NULL)
- goto err_source;
-
- return source;
-
- err_source:
- free(*s);
- err_strdup:
- wl_array_release(&source->base.mime_types);
- err_add:
- free(source);
-
- return NULL;
-}
-
-struct clipboard_client {
- struct wl_event_source *event_source;
- size_t offset;
- struct clipboard_source *source;
-};
-
-static int
-clipboard_client_data(int fd, uint32_t mask, void *data)
-{
- struct clipboard_client *client = data;
- char *p;
- size_t size;
- int len;
-
- size = client->source->contents.size;
- p = client->source->contents.data;
- len = write(fd, p + client->offset, size - client->offset);
- if (len > 0)
- client->offset += len;
-
- if (client->offset == size || len <= 0) {
- close(fd);
- wl_event_source_remove(client->event_source);
- clipboard_source_unref(client->source);
- free(client);
- }
-
- return 1;
-}
-
-static void
-clipboard_client_create(struct clipboard_source *source, int fd)
-{
- struct weston_seat *seat = source->clipboard->seat;
- struct clipboard_client *client;
- struct wl_event_loop *loop =
- wl_display_get_event_loop(seat->compositor->wl_display);
-
- client = zalloc(sizeof *client);
- if (client == NULL)
- return;
-
- client->source = source;
- source->refcount++;
- client->event_source =
- wl_event_loop_add_fd(loop, fd, WL_EVENT_WRITABLE,
- clipboard_client_data, client);
-}
-
-static void
-clipboard_set_selection(struct wl_listener *listener, void *data)
-{
- struct clipboard *clipboard =
- container_of(listener, struct clipboard, selection_listener);
- struct weston_seat *seat = data;
- struct weston_data_source *source = seat->selection_data_source;
- const char **mime_types;
- int p[2];
-
- if (source == NULL) {
- if (clipboard->source)
- weston_seat_set_selection(seat,
- &clipboard->source->base,
- clipboard->source->serial);
- return;
- } else if (source->accept == clipboard_source_accept) {
- /* Callback for our data source. */
- return;
- }
-
- if (clipboard->source)
- clipboard_source_unref(clipboard->source);
-
- clipboard->source = NULL;
-
- mime_types = source->mime_types.data;
-
- if (!mime_types || pipe2(p, O_CLOEXEC) == -1)
- return;
-
- source->send(source, mime_types[0], p[1]);
-
- clipboard->source =
- clipboard_source_create(clipboard, mime_types[0],
- seat->selection_serial, p[0]);
- if (clipboard->source == NULL) {
- close(p[0]);
- return;
- }
-}
-
-static void
-clipboard_destroy(struct wl_listener *listener, void *data)
-{
- struct clipboard *clipboard =
- container_of(listener, struct clipboard, destroy_listener);
-
- wl_list_remove(&clipboard->selection_listener.link);
- wl_list_remove(&clipboard->destroy_listener.link);
-
- free(clipboard);
-}
-
-struct clipboard *
-clipboard_create(struct weston_seat *seat)
-{
- struct clipboard *clipboard;
-
- clipboard = zalloc(sizeof *clipboard);
- if (clipboard == NULL)
- return NULL;
-
- clipboard->seat = seat;
- clipboard->selection_listener.notify = clipboard_set_selection;
- clipboard->destroy_listener.notify = clipboard_destroy;
-
- wl_signal_add(&seat->selection_signal,
- &clipboard->selection_listener);
- wl_signal_add(&seat->destroy_signal,
- &clipboard->destroy_listener);
-
- return clipboard;
-}
+++ /dev/null
-/*
- * Copyright © 2008-2011 Kristian Høgsberg
- * Copyright © 2011 Intel Corporation
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <errno.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <linux/input.h>
-#include <linux/vt.h>
-#include <assert.h>
-#include <sys/mman.h>
-#include <dlfcn.h>
-#include <time.h>
-
-#include <xf86drm.h>
-#include <xf86drmMode.h>
-#include <drm_fourcc.h>
-
-#include <gbm.h>
-#include <libudev.h>
-
-#include "compositor.h"
-#include "compositor-drm.h"
-#include "shared/helpers.h"
-#include "shared/timespec-util.h"
-#include "gl-renderer.h"
-#include "pixman-renderer.h"
-#include "libbacklight.h"
-#include "libinput-seat.h"
-#include "launcher-util.h"
-#include "vaapi-recorder.h"
-#include "presentation-time-server-protocol.h"
-#include "linux-dmabuf.h"
-
-#ifndef DRM_CAP_TIMESTAMP_MONOTONIC
-#define DRM_CAP_TIMESTAMP_MONOTONIC 0x6
-#endif
-
-#ifndef DRM_CAP_CURSOR_WIDTH
-#define DRM_CAP_CURSOR_WIDTH 0x8
-#endif
-
-#ifndef DRM_CAP_CURSOR_HEIGHT
-#define DRM_CAP_CURSOR_HEIGHT 0x9
-#endif
-
-#ifndef GBM_BO_USE_CURSOR
-#define GBM_BO_USE_CURSOR GBM_BO_USE_CURSOR_64X64
-#endif
-
-struct drm_backend {
- struct weston_backend base;
- struct weston_compositor *compositor;
-
- struct udev *udev;
- struct wl_event_source *drm_source;
-
- struct udev_monitor *udev_monitor;
- struct wl_event_source *udev_drm_source;
-
- struct {
- int id;
- int fd;
- char *filename;
- } drm;
- struct gbm_device *gbm;
- uint32_t *crtcs;
- int num_crtcs;
- uint32_t crtc_allocator;
- uint32_t connector_allocator;
- struct wl_listener session_listener;
- uint32_t gbm_format;
-
- /* we need these parameters in order to not fail drmModeAddFB2()
- * due to out of bounds dimensions, and then mistakenly set
- * sprites_are_broken:
- */
- uint32_t min_width, max_width;
- uint32_t min_height, max_height;
- int no_addfb2;
-
- struct wl_list sprite_list;
- int sprites_are_broken;
- int sprites_hidden;
-
- int cursors_are_broken;
-
- int use_pixman;
-
- uint32_t prev_state;
-
- struct udev_input input;
-
- int32_t cursor_width;
- int32_t cursor_height;
-
- /** Callback used to configure the outputs.
- *
- * This function will be called by the backend when a new DRM
- * output needs to be configured.
- */
- enum weston_drm_backend_output_mode
- (*configure_output)(struct weston_compositor *compositor,
- bool use_current_mode,
- const char *name,
- struct weston_drm_backend_output_config *output_config);
- bool use_current_mode;
-};
-
-struct drm_mode {
- struct weston_mode base;
- drmModeModeInfo mode_info;
-};
-
-struct drm_fb {
- uint32_t fb_id, stride, handle, size;
- int fd;
- int is_client_buffer;
- struct weston_buffer_reference buffer_ref;
-
- /* Used by gbm fbs */
- struct gbm_bo *bo;
-
- /* Used by dumb fbs */
- void *map;
-};
-
-struct drm_edid {
- char eisa_id[13];
- char monitor_name[13];
- char pnp_id[5];
- char serial_number[13];
-};
-
-struct drm_output {
- struct weston_output base;
-
- uint32_t crtc_id;
- int pipe;
- uint32_t connector_id;
- drmModeCrtcPtr original_crtc;
- struct drm_edid edid;
- drmModePropertyPtr dpms_prop;
- uint32_t gbm_format;
-
- enum dpms_enum dpms;
-
- int vblank_pending;
- int page_flip_pending;
- int destroy_pending;
-
- struct gbm_surface *gbm_surface;
- struct gbm_bo *gbm_cursor_bo[2];
- struct weston_plane cursor_plane;
- struct weston_plane fb_plane;
- struct weston_view *cursor_view;
- int current_cursor;
- struct drm_fb *current, *next;
- struct backlight *backlight;
-
- struct drm_fb *dumb[2];
- pixman_image_t *image[2];
- int current_image;
- pixman_region32_t previous_damage;
-
- struct vaapi_recorder *recorder;
- struct wl_listener recorder_frame_listener;
-};
-
-/*
- * An output has a primary display plane plus zero or more sprites for
- * blending display contents.
- */
-struct drm_sprite {
- struct wl_list link;
-
- struct weston_plane plane;
-
- struct drm_fb *current, *next;
- struct drm_output *output;
- struct drm_backend *backend;
-
- uint32_t possible_crtcs;
- uint32_t plane_id;
- uint32_t count_formats;
-
- int32_t src_x, src_y;
- uint32_t src_w, src_h;
- uint32_t dest_x, dest_y;
- uint32_t dest_w, dest_h;
-
- uint32_t formats[];
-};
-
-static struct gl_renderer_interface *gl_renderer;
-
-static const char default_seat[] = "seat0";
-
-static void
-drm_output_set_cursor(struct drm_output *output);
-
-static void
-drm_output_update_msc(struct drm_output *output, unsigned int seq);
-
-static int
-drm_sprite_crtc_supported(struct drm_output *output, uint32_t supported)
-{
- struct weston_compositor *ec = output->base.compositor;
- struct drm_backend *b =(struct drm_backend *)ec->backend;
- int crtc;
-
- for (crtc = 0; crtc < b->num_crtcs; crtc++) {
- if (b->crtcs[crtc] != output->crtc_id)
- continue;
-
- if (supported & (1 << crtc))
- return -1;
- }
-
- return 0;
-}
-
-static void
-drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
-{
- struct drm_fb *fb = data;
- struct gbm_device *gbm = gbm_bo_get_device(bo);
-
- if (fb->fb_id)
- drmModeRmFB(gbm_device_get_fd(gbm), fb->fb_id);
-
- weston_buffer_reference(&fb->buffer_ref, NULL);
-
- free(data);
-}
-
-static struct drm_fb *
-drm_fb_create_dumb(struct drm_backend *b, unsigned width, unsigned height,
- uint32_t format)
-{
- struct drm_fb *fb;
- int ret;
- uint32_t bpp, depth;
-
- struct drm_mode_create_dumb create_arg;
- struct drm_mode_destroy_dumb destroy_arg;
- struct drm_mode_map_dumb map_arg;
-
- fb = zalloc(sizeof *fb);
- if (!fb)
- return NULL;
-
- switch (format) {
- case GBM_FORMAT_XRGB8888:
- bpp = 32;
- depth = 24;
- break;
- case GBM_FORMAT_RGB565:
- bpp = depth = 16;
- break;
- default:
- return NULL;
- }
-
- memset(&create_arg, 0, sizeof create_arg);
- create_arg.bpp = bpp;
- create_arg.width = width;
- create_arg.height = height;
-
- ret = drmIoctl(b->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg);
- if (ret)
- goto err_fb;
-
- fb->handle = create_arg.handle;
- fb->stride = create_arg.pitch;
- fb->size = create_arg.size;
- fb->fd = b->drm.fd;
-
- ret = -1;
-
- if (!b->no_addfb2) {
- uint32_t handles[4], pitches[4], offsets[4];
-
- handles[0] = fb->handle;
- pitches[0] = fb->stride;
- offsets[0] = 0;
-
- ret = drmModeAddFB2(b->drm.fd, width, height,
- format, handles, pitches, offsets,
- &fb->fb_id, 0);
- if (ret) {
- weston_log("addfb2 failed: %m\n");
- b->no_addfb2 = 1;
- }
- }
-
- if (ret) {
- ret = drmModeAddFB(b->drm.fd, width, height, depth, bpp,
- fb->stride, fb->handle, &fb->fb_id);
- }
-
- if (ret)
- goto err_bo;
-
- memset(&map_arg, 0, sizeof map_arg);
- map_arg.handle = fb->handle;
- ret = drmIoctl(fb->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_arg);
- if (ret)
- goto err_add_fb;
-
- fb->map = mmap(NULL, fb->size, PROT_WRITE,
- MAP_SHARED, b->drm.fd, map_arg.offset);
- if (fb->map == MAP_FAILED)
- goto err_add_fb;
-
- return fb;
-
-err_add_fb:
- drmModeRmFB(b->drm.fd, fb->fb_id);
-err_bo:
- memset(&destroy_arg, 0, sizeof(destroy_arg));
- destroy_arg.handle = create_arg.handle;
- drmIoctl(b->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
-err_fb:
- free(fb);
- return NULL;
-}
-
-static void
-drm_fb_destroy_dumb(struct drm_fb *fb)
-{
- struct drm_mode_destroy_dumb destroy_arg;
-
- if (!fb->map)
- return;
-
- if (fb->fb_id)
- drmModeRmFB(fb->fd, fb->fb_id);
-
- weston_buffer_reference(&fb->buffer_ref, NULL);
-
- munmap(fb->map, fb->size);
-
- memset(&destroy_arg, 0, sizeof(destroy_arg));
- destroy_arg.handle = fb->handle;
- drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
-
- free(fb);
-}
-
-static struct drm_fb *
-drm_fb_get_from_bo(struct gbm_bo *bo,
- struct drm_backend *backend, uint32_t format)
-{
- struct drm_fb *fb = gbm_bo_get_user_data(bo);
- uint32_t width, height;
- uint32_t handles[4], pitches[4], offsets[4];
- int ret;
-
- if (fb)
- return fb;
-
- fb = zalloc(sizeof *fb);
- if (fb == NULL)
- return NULL;
-
- fb->bo = bo;
-
- width = gbm_bo_get_width(bo);
- height = gbm_bo_get_height(bo);
- fb->stride = gbm_bo_get_stride(bo);
- fb->handle = gbm_bo_get_handle(bo).u32;
- fb->size = fb->stride * height;
- fb->fd = backend->drm.fd;
-
- if (backend->min_width > width || width > backend->max_width ||
- backend->min_height > height ||
- height > backend->max_height) {
- weston_log("bo geometry out of bounds\n");
- goto err_free;
- }
-
- ret = -1;
-
- if (format && !backend->no_addfb2) {
- handles[0] = fb->handle;
- pitches[0] = fb->stride;
- offsets[0] = 0;
-
- ret = drmModeAddFB2(backend->drm.fd, width, height,
- format, handles, pitches, offsets,
- &fb->fb_id, 0);
- if (ret) {
- weston_log("addfb2 failed: %m\n");
- backend->no_addfb2 = 1;
- backend->sprites_are_broken = 1;
- }
- }
-
- if (ret)
- ret = drmModeAddFB(backend->drm.fd, width, height, 24, 32,
- fb->stride, fb->handle, &fb->fb_id);
-
- if (ret) {
- weston_log("failed to create kms fb: %m\n");
- goto err_free;
- }
-
- gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
-
- return fb;
-
-err_free:
- free(fb);
- return NULL;
-}
-
-static void
-drm_fb_set_buffer(struct drm_fb *fb, struct weston_buffer *buffer)
-{
- assert(fb->buffer_ref.buffer == NULL);
-
- fb->is_client_buffer = 1;
-
- weston_buffer_reference(&fb->buffer_ref, buffer);
-}
-
-static void
-drm_output_release_fb(struct drm_output *output, struct drm_fb *fb)
-{
- if (!fb)
- return;
-
- if (fb->map &&
- (fb != output->dumb[0] && fb != output->dumb[1])) {
- drm_fb_destroy_dumb(fb);
- } else if (fb->bo) {
- if (fb->is_client_buffer)
- gbm_bo_destroy(fb->bo);
- else
- gbm_surface_release_buffer(output->gbm_surface,
- fb->bo);
- }
-}
-
-static uint32_t
-drm_output_check_scanout_format(struct drm_output *output,
- struct weston_surface *es, struct gbm_bo *bo)
-{
- uint32_t format;
- pixman_region32_t r;
-
- format = gbm_bo_get_format(bo);
-
- if (format == GBM_FORMAT_ARGB8888) {
- /* We can scanout an ARGB buffer if the surface's
- * opaque region covers the whole output, but we have
- * to use XRGB as the KMS format code. */
- pixman_region32_init_rect(&r, 0, 0,
- output->base.width,
- output->base.height);
- pixman_region32_subtract(&r, &r, &es->opaque);
-
- if (!pixman_region32_not_empty(&r))
- format = GBM_FORMAT_XRGB8888;
-
- pixman_region32_fini(&r);
- }
-
- if (output->gbm_format == format)
- return format;
-
- return 0;
-}
-
-static struct weston_plane *
-drm_output_prepare_scanout_view(struct drm_output *output,
- struct weston_view *ev)
-{
- struct drm_backend *b =
- (struct drm_backend *)output->base.compositor->backend;
- struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
- struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
- struct gbm_bo *bo;
- uint32_t format;
-
- if (ev->geometry.x != output->base.x ||
- ev->geometry.y != output->base.y ||
- buffer == NULL || b->gbm == NULL ||
- buffer->width != output->base.current_mode->width ||
- buffer->height != output->base.current_mode->height ||
- output->base.transform != viewport->buffer.transform ||
- ev->transform.enabled)
- return NULL;
-
- if (ev->geometry.scissor_enabled)
- return NULL;
-
- bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
- buffer->resource, GBM_BO_USE_SCANOUT);
-
- /* Unable to use the buffer for scanout */
- if (!bo)
- return NULL;
-
- format = drm_output_check_scanout_format(output, ev->surface, bo);
- if (format == 0) {
- gbm_bo_destroy(bo);
- return NULL;
- }
-
- output->next = drm_fb_get_from_bo(bo, b, format);
- if (!output->next) {
- gbm_bo_destroy(bo);
- return NULL;
- }
-
- drm_fb_set_buffer(output->next, buffer);
-
- return &output->fb_plane;
-}
-
-static void
-drm_output_render_gl(struct drm_output *output, pixman_region32_t *damage)
-{
- struct drm_backend *b =
- (struct drm_backend *)output->base.compositor->backend;
- struct gbm_bo *bo;
-
- output->base.compositor->renderer->repaint_output(&output->base,
- damage);
-
- bo = gbm_surface_lock_front_buffer(output->gbm_surface);
- if (!bo) {
- weston_log("failed to lock front buffer: %m\n");
- return;
- }
-
- output->next = drm_fb_get_from_bo(bo, b, output->gbm_format);
- if (!output->next) {
- weston_log("failed to get drm_fb for bo\n");
- gbm_surface_release_buffer(output->gbm_surface, bo);
- return;
- }
-}
-
-static void
-drm_output_render_pixman(struct drm_output *output, pixman_region32_t *damage)
-{
- struct weston_compositor *ec = output->base.compositor;
- pixman_region32_t total_damage, previous_damage;
-
- pixman_region32_init(&total_damage);
- pixman_region32_init(&previous_damage);
-
- pixman_region32_copy(&previous_damage, damage);
-
- pixman_region32_union(&total_damage, damage, &output->previous_damage);
- pixman_region32_copy(&output->previous_damage, &previous_damage);
-
- output->current_image ^= 1;
-
- output->next = output->dumb[output->current_image];
- pixman_renderer_output_set_buffer(&output->base,
- output->image[output->current_image]);
-
- ec->renderer->repaint_output(&output->base, &total_damage);
-
- pixman_region32_fini(&total_damage);
- pixman_region32_fini(&previous_damage);
-}
-
-static void
-drm_output_render(struct drm_output *output, pixman_region32_t *damage)
-{
- struct weston_compositor *c = output->base.compositor;
- struct drm_backend *b = (struct drm_backend *)c->backend;
-
- if (b->use_pixman)
- drm_output_render_pixman(output, damage);
- else
- drm_output_render_gl(output, damage);
-
- pixman_region32_subtract(&c->primary_plane.damage,
- &c->primary_plane.damage, damage);
-}
-
-static void
-drm_output_set_gamma(struct weston_output *output_base,
- uint16_t size, uint16_t *r, uint16_t *g, uint16_t *b)
-{
- int rc;
- struct drm_output *output = (struct drm_output *) output_base;
- struct drm_backend *backend =
- (struct drm_backend *) output->base.compositor->backend;
-
- /* check */
- if (output_base->gamma_size != size)
- return;
- if (!output->original_crtc)
- return;
-
- rc = drmModeCrtcSetGamma(backend->drm.fd,
- output->crtc_id,
- size, r, g, b);
- if (rc)
- weston_log("set gamma failed: %m\n");
-}
-
-/* Determine the type of vblank synchronization to use for the output.
- *
- * The pipe parameter indicates which CRTC is in use. Knowing this, we
- * can determine which vblank sequence type to use for it. Traditional
- * cards had only two CRTCs, with CRTC 0 using no special flags, and
- * CRTC 1 using DRM_VBLANK_SECONDARY. The first bit of the pipe
- * parameter indicates this.
- *
- * Bits 1-5 of the pipe parameter are 5 bit wide pipe number between
- * 0-31. If this is non-zero it indicates we're dealing with a
- * multi-gpu situation and we need to calculate the vblank sync
- * using DRM_BLANK_HIGH_CRTC_MASK.
- */
-static unsigned int
-drm_waitvblank_pipe(struct drm_output *output)
-{
- if (output->pipe > 1)
- return (output->pipe << DRM_VBLANK_HIGH_CRTC_SHIFT) &
- DRM_VBLANK_HIGH_CRTC_MASK;
- else if (output->pipe > 0)
- return DRM_VBLANK_SECONDARY;
- else
- return 0;
-}
-
-static int
-drm_output_repaint(struct weston_output *output_base,
- pixman_region32_t *damage)
-{
- struct drm_output *output = (struct drm_output *) output_base;
- struct drm_backend *backend =
- (struct drm_backend *)output->base.compositor->backend;
- struct drm_sprite *s;
- struct drm_mode *mode;
- int ret = 0;
-
- if (output->destroy_pending)
- return -1;
-
- if (!output->next)
- drm_output_render(output, damage);
- if (!output->next)
- return -1;
-
- mode = container_of(output->base.current_mode, struct drm_mode, base);
- if (!output->current ||
- output->current->stride != output->next->stride) {
- ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
- output->next->fb_id, 0, 0,
- &output->connector_id, 1,
- &mode->mode_info);
- if (ret) {
- weston_log("set mode failed: %m\n");
- goto err_pageflip;
- }
- output_base->set_dpms(output_base, WESTON_DPMS_ON);
- }
-
- if (drmModePageFlip(backend->drm.fd, output->crtc_id,
- output->next->fb_id,
- DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
- weston_log("queueing pageflip failed: %m\n");
- goto err_pageflip;
- }
-
- output->page_flip_pending = 1;
-
- drm_output_set_cursor(output);
-
- /*
- * Now, update all the sprite surfaces
- */
- wl_list_for_each(s, &backend->sprite_list, link) {
- uint32_t flags = 0, fb_id = 0;
- drmVBlank vbl = {
- .request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
- .request.sequence = 1,
- };
-
- if ((!s->current && !s->next) ||
- !drm_sprite_crtc_supported(output, s->possible_crtcs))
- continue;
-
- if (s->next && !backend->sprites_hidden)
- fb_id = s->next->fb_id;
-
- ret = drmModeSetPlane(backend->drm.fd, s->plane_id,
- output->crtc_id, fb_id, flags,
- s->dest_x, s->dest_y,
- s->dest_w, s->dest_h,
- s->src_x, s->src_y,
- s->src_w, s->src_h);
- if (ret)
- weston_log("setplane failed: %d: %s\n",
- ret, strerror(errno));
-
- vbl.request.type |= drm_waitvblank_pipe(output);
-
- /*
- * Queue a vblank signal so we know when the surface
- * becomes active on the display or has been replaced.
- */
- vbl.request.signal = (unsigned long)s;
- ret = drmWaitVBlank(backend->drm.fd, &vbl);
- if (ret) {
- weston_log("vblank event request failed: %d: %s\n",
- ret, strerror(errno));
- }
-
- s->output = output;
- output->vblank_pending = 1;
- }
-
- return 0;
-
-err_pageflip:
- output->cursor_view = NULL;
- if (output->next) {
- drm_output_release_fb(output, output->next);
- output->next = NULL;
- }
-
- return -1;
-}
-
-static void
-drm_output_start_repaint_loop(struct weston_output *output_base)
-{
- struct drm_output *output = (struct drm_output *) output_base;
- struct drm_backend *backend = (struct drm_backend *)
- output_base->compositor->backend;
- uint32_t fb_id;
- struct timespec ts, tnow;
- struct timespec vbl2now;
- int64_t refresh_nsec;
- int ret;
- drmVBlank vbl = {
- .request.type = DRM_VBLANK_RELATIVE,
- .request.sequence = 0,
- .request.signal = 0,
- };
-
- if (output->destroy_pending)
- return;
-
- if (!output->current) {
- /* We can't page flip if there's no mode set */
- goto finish_frame;
- }
-
- /* Try to get current msc and timestamp via instant query */
- vbl.request.type |= drm_waitvblank_pipe(output);
- ret = drmWaitVBlank(backend->drm.fd, &vbl);
-
- /* Error ret or zero timestamp means failure to get valid timestamp */
- if ((ret == 0) && (vbl.reply.tval_sec > 0 || vbl.reply.tval_usec > 0)) {
- ts.tv_sec = vbl.reply.tval_sec;
- ts.tv_nsec = vbl.reply.tval_usec * 1000;
-
- /* Valid timestamp for most recent vblank - not stale?
- * Stale ts could happen on Linux 3.17+, so make sure it
- * is not older than 1 refresh duration since now.
- */
- weston_compositor_read_presentation_clock(backend->compositor,
- &tnow);
- timespec_sub(&vbl2now, &tnow, &ts);
- refresh_nsec =
- millihz_to_nsec(output->base.current_mode->refresh);
- if (timespec_to_nsec(&vbl2now) < refresh_nsec) {
- drm_output_update_msc(output, vbl.reply.sequence);
- weston_output_finish_frame(output_base, &ts,
- WP_PRESENTATION_FEEDBACK_INVALID);
- return;
- }
- }
-
- /* Immediate query didn't provide valid timestamp.
- * Use pageflip fallback.
- */
- fb_id = output->current->fb_id;
-
- if (drmModePageFlip(backend->drm.fd, output->crtc_id, fb_id,
- DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
- weston_log("queueing pageflip failed: %m\n");
- goto finish_frame;
- }
-
- return;
-
-finish_frame:
- /* if we cannot page-flip, immediately finish frame */
- weston_compositor_read_presentation_clock(output_base->compositor, &ts);
- weston_output_finish_frame(output_base, &ts,
- WP_PRESENTATION_FEEDBACK_INVALID);
-}
-
-static void
-drm_output_update_msc(struct drm_output *output, unsigned int seq)
-{
- uint64_t msc_hi = output->base.msc >> 32;
-
- if (seq < (output->base.msc & 0xffffffff))
- msc_hi++;
-
- output->base.msc = (msc_hi << 32) + seq;
-}
-
-static void
-vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
- void *data)
-{
- struct drm_sprite *s = (struct drm_sprite *)data;
- struct drm_output *output = s->output;
- struct timespec ts;
- uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
- WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
-
- drm_output_update_msc(output, frame);
- output->vblank_pending = 0;
-
- drm_output_release_fb(output, s->current);
- s->current = s->next;
- s->next = NULL;
-
- if (!output->page_flip_pending) {
- ts.tv_sec = sec;
- ts.tv_nsec = usec * 1000;
- weston_output_finish_frame(&output->base, &ts, flags);
- }
-}
-
-static void
-drm_output_destroy(struct weston_output *output_base);
-
-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 timespec ts;
- uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
- WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
- WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
-
- drm_output_update_msc(output, frame);
-
- /* We don't set page_flip_pending on start_repaint_loop, in that case
- * we just want to page flip to the current buffer to get an accurate
- * timestamp */
- if (output->page_flip_pending) {
- drm_output_release_fb(output, output->current);
- output->current = output->next;
- output->next = NULL;
- }
-
- output->page_flip_pending = 0;
-
- if (output->destroy_pending)
- drm_output_destroy(&output->base);
- else if (!output->vblank_pending) {
- ts.tv_sec = sec;
- ts.tv_nsec = usec * 1000;
- weston_output_finish_frame(&output->base, &ts, flags);
-
- /* We can't call this from frame_notify, because the output's
- * repaint needed flag is cleared just after that */
- if (output->recorder)
- weston_output_schedule_repaint(&output->base);
- }
-}
-
-static uint32_t
-drm_output_check_sprite_format(struct drm_sprite *s,
- struct weston_view *ev, struct gbm_bo *bo)
-{
- uint32_t i, format;
-
- format = gbm_bo_get_format(bo);
-
- if (format == GBM_FORMAT_ARGB8888) {
- pixman_region32_t r;
-
- pixman_region32_init_rect(&r, 0, 0,
- ev->surface->width,
- ev->surface->height);
- pixman_region32_subtract(&r, &r, &ev->surface->opaque);
-
- if (!pixman_region32_not_empty(&r))
- format = GBM_FORMAT_XRGB8888;
-
- pixman_region32_fini(&r);
- }
-
- for (i = 0; i < s->count_formats; i++)
- if (s->formats[i] == format)
- return format;
-
- return 0;
-}
-
-static int
-drm_view_transform_supported(struct weston_view *ev)
-{
- return !ev->transform.enabled ||
- (ev->transform.matrix.type < WESTON_MATRIX_TRANSFORM_ROTATE);
-}
-
-static struct weston_plane *
-drm_output_prepare_overlay_view(struct drm_output *output,
- struct weston_view *ev)
-{
- struct weston_compositor *ec = output->base.compositor;
- struct drm_backend *b = (struct drm_backend *)ec->backend;
- struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
- struct wl_resource *buffer_resource;
- struct drm_sprite *s;
- struct linux_dmabuf_buffer *dmabuf;
- int found = 0;
- struct gbm_bo *bo;
- pixman_region32_t dest_rect, src_rect;
- pixman_box32_t *box, tbox;
- uint32_t format;
- wl_fixed_t sx1, sy1, sx2, sy2;
-
- if (b->gbm == NULL)
- return NULL;
-
- if (viewport->buffer.transform != output->base.transform)
- return NULL;
-
- if (viewport->buffer.scale != output->base.current_scale)
- return NULL;
-
- if (b->sprites_are_broken)
- return NULL;
-
- if (ev->output_mask != (1u << output->base.id))
- return NULL;
-
- if (ev->surface->buffer_ref.buffer == NULL)
- return NULL;
- buffer_resource = ev->surface->buffer_ref.buffer->resource;
-
- if (ev->alpha != 1.0f)
- return NULL;
-
- if (wl_shm_buffer_get(buffer_resource))
- return NULL;
-
- if (!drm_view_transform_supported(ev))
- return NULL;
-
- wl_list_for_each(s, &b->sprite_list, link) {
- if (!drm_sprite_crtc_supported(output, s->possible_crtcs))
- continue;
-
- if (!s->next) {
- found = 1;
- break;
- }
- }
-
- /* No sprites available */
- if (!found)
- return NULL;
-
- if ((dmabuf = linux_dmabuf_buffer_get(buffer_resource))) {
-#ifdef HAVE_GBM_FD_IMPORT
- /* XXX: TODO:
- *
- * Use AddFB2 directly, do not go via GBM.
- * Add support for multiplanar formats.
- * Both require refactoring in the DRM-backend to
- * support a mix of gbm_bos and drmfbs.
- */
- struct gbm_import_fd_data gbm_dmabuf = {
- .fd = dmabuf->attributes.fd[0],
- .width = dmabuf->attributes.width,
- .height = dmabuf->attributes.height,
- .stride = dmabuf->attributes.stride[0],
- .format = dmabuf->attributes.format
- };
-
- if (dmabuf->attributes.n_planes != 1 || dmabuf->attributes.offset[0] != 0)
- return NULL;
-
- bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_FD, &gbm_dmabuf,
- GBM_BO_USE_SCANOUT);
-#else
- return NULL;
-#endif
- } else {
- bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
- buffer_resource, GBM_BO_USE_SCANOUT);
- }
- if (!bo)
- return NULL;
-
- format = drm_output_check_sprite_format(s, ev, bo);
- if (format == 0) {
- gbm_bo_destroy(bo);
- return NULL;
- }
-
- s->next = drm_fb_get_from_bo(bo, b, format);
- if (!s->next) {
- gbm_bo_destroy(bo);
- return NULL;
- }
-
- drm_fb_set_buffer(s->next, ev->surface->buffer_ref.buffer);
-
- box = pixman_region32_extents(&ev->transform.boundingbox);
- s->plane.x = box->x1;
- s->plane.y = box->y1;
-
- /*
- * Calculate the source & dest rects properly based on actual
- * position (note the caller has called weston_view_update_transform()
- * for us already).
- */
- pixman_region32_init(&dest_rect);
- pixman_region32_intersect(&dest_rect, &ev->transform.boundingbox,
- &output->base.region);
- pixman_region32_translate(&dest_rect, -output->base.x, -output->base.y);
- box = pixman_region32_extents(&dest_rect);
- tbox = weston_transformed_rect(output->base.width,
- output->base.height,
- output->base.transform,
- output->base.current_scale,
- *box);
- s->dest_x = tbox.x1;
- s->dest_y = tbox.y1;
- s->dest_w = tbox.x2 - tbox.x1;
- s->dest_h = tbox.y2 - tbox.y1;
- pixman_region32_fini(&dest_rect);
-
- pixman_region32_init(&src_rect);
- pixman_region32_intersect(&src_rect, &ev->transform.boundingbox,
- &output->base.region);
- box = pixman_region32_extents(&src_rect);
-
- weston_view_from_global_fixed(ev,
- wl_fixed_from_int(box->x1),
- wl_fixed_from_int(box->y1),
- &sx1, &sy1);
- weston_view_from_global_fixed(ev,
- wl_fixed_from_int(box->x2),
- wl_fixed_from_int(box->y2),
- &sx2, &sy2);
-
- if (sx1 < 0)
- sx1 = 0;
- if (sy1 < 0)
- sy1 = 0;
- if (sx2 > wl_fixed_from_int(ev->surface->width))
- sx2 = wl_fixed_from_int(ev->surface->width);
- if (sy2 > wl_fixed_from_int(ev->surface->height))
- sy2 = wl_fixed_from_int(ev->surface->height);
-
- tbox.x1 = sx1;
- tbox.y1 = sy1;
- tbox.x2 = sx2;
- tbox.y2 = sy2;
-
- tbox = weston_transformed_rect(wl_fixed_from_int(ev->surface->width),
- wl_fixed_from_int(ev->surface->height),
- viewport->buffer.transform,
- viewport->buffer.scale,
- tbox);
-
- s->src_x = tbox.x1 << 8;
- s->src_y = tbox.y1 << 8;
- s->src_w = (tbox.x2 - tbox.x1) << 8;
- s->src_h = (tbox.y2 - tbox.y1) << 8;
- pixman_region32_fini(&src_rect);
-
- return &s->plane;
-}
-
-static struct weston_plane *
-drm_output_prepare_cursor_view(struct drm_output *output,
- struct weston_view *ev)
-{
- struct drm_backend *b =
- (struct drm_backend *)output->base.compositor->backend;
- struct weston_buffer_viewport *viewport = &ev->surface->buffer_viewport;
- struct wl_shm_buffer *shmbuf;
-
- if (ev->transform.enabled &&
- (ev->transform.matrix.type > WESTON_MATRIX_TRANSFORM_TRANSLATE))
- return NULL;
- if (b->gbm == NULL)
- return NULL;
- if (output->base.transform != WL_OUTPUT_TRANSFORM_NORMAL)
- return NULL;
- if (viewport->buffer.scale != output->base.current_scale)
- return NULL;
- if (output->cursor_view)
- return NULL;
- if (ev->output_mask != (1u << output->base.id))
- return NULL;
- if (b->cursors_are_broken)
- return NULL;
- if (ev->geometry.scissor_enabled)
- return NULL;
- if (ev->surface->buffer_ref.buffer == NULL)
- return NULL;
- shmbuf = wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource);
- if (!shmbuf)
- return NULL;
- if (wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888)
- return NULL;
- if (ev->surface->width > b->cursor_width ||
- ev->surface->height > b->cursor_height)
- return NULL;
-
- output->cursor_view = ev;
-
- return &output->cursor_plane;
-}
-
-/**
- * Update the image for the current cursor surface
- *
- * @param b DRM backend structure
- * @param bo GBM buffer object to write into
- * @param ev View to use for cursor image
- */
-static void
-cursor_bo_update(struct drm_backend *b, struct gbm_bo *bo,
- struct weston_view *ev)
-{
- struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
- uint32_t buf[b->cursor_width * b->cursor_height];
- int32_t stride;
- uint8_t *s;
- int i;
-
- assert(buffer && buffer->shm_buffer);
- assert(buffer->shm_buffer == wl_shm_buffer_get(buffer->resource));
- assert(ev->surface->width <= b->cursor_width);
- assert(ev->surface->height <= b->cursor_height);
-
- memset(buf, 0, sizeof buf);
- stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
- s = wl_shm_buffer_get_data(buffer->shm_buffer);
-
- wl_shm_buffer_begin_access(buffer->shm_buffer);
- for (i = 0; i < ev->surface->height; i++)
- memcpy(buf + i * b->cursor_width,
- s + i * stride,
- ev->surface->width * 4);
- wl_shm_buffer_end_access(buffer->shm_buffer);
-
- if (gbm_bo_write(bo, buf, sizeof buf) < 0)
- weston_log("failed update cursor: %m\n");
-}
-
-static void
-drm_output_set_cursor(struct drm_output *output)
-{
- struct weston_view *ev = output->cursor_view;
- struct weston_buffer *buffer;
- struct drm_backend *b =
- (struct drm_backend *) output->base.compositor->backend;
- EGLint handle;
- struct gbm_bo *bo;
- float x, y;
-
- output->cursor_view = NULL;
- if (ev == NULL) {
- drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
- output->cursor_plane.x = INT32_MIN;
- output->cursor_plane.y = INT32_MIN;
- return;
- }
-
- buffer = ev->surface->buffer_ref.buffer;
-
- if (buffer &&
- pixman_region32_not_empty(&output->cursor_plane.damage)) {
- pixman_region32_fini(&output->cursor_plane.damage);
- pixman_region32_init(&output->cursor_plane.damage);
- output->current_cursor ^= 1;
- bo = output->gbm_cursor_bo[output->current_cursor];
-
- cursor_bo_update(b, bo, ev);
- handle = gbm_bo_get_handle(bo).s32;
- if (drmModeSetCursor(b->drm.fd, output->crtc_id, handle,
- b->cursor_width, b->cursor_height)) {
- weston_log("failed to set cursor: %m\n");
- b->cursors_are_broken = 1;
- }
- }
-
- weston_view_to_global_float(ev, 0, 0, &x, &y);
-
- /* From global to output space, output transform is guaranteed to be
- * NORMAL by drm_output_prepare_cursor_view().
- */
- x = (x - output->base.x) * output->base.current_scale;
- y = (y - output->base.y) * output->base.current_scale;
-
- if (output->cursor_plane.x != x || output->cursor_plane.y != y) {
- if (drmModeMoveCursor(b->drm.fd, output->crtc_id, x, y)) {
- weston_log("failed to move cursor: %m\n");
- b->cursors_are_broken = 1;
- }
-
- output->cursor_plane.x = x;
- output->cursor_plane.y = y;
- }
-}
-
-static void
-drm_assign_planes(struct weston_output *output_base)
-{
- struct drm_backend *b =
- (struct drm_backend *)output_base->compositor->backend;
- struct drm_output *output = (struct drm_output *)output_base;
- struct weston_view *ev, *next;
- pixman_region32_t overlap, surface_overlap;
- struct weston_plane *primary, *next_plane;
-
- /*
- * Find a surface for each sprite in the output using some heuristics:
- * 1) size
- * 2) frequency of update
- * 3) opacity (though some hw might support alpha blending)
- * 4) clipping (this can be fixed with color keys)
- *
- * The idea is to save on blitting since this should save power.
- * If we can get a large video surface on the sprite for example,
- * the main display surface may not need to update at all, and
- * the client buffer can be used directly for the sprite surface
- * as we do for flipping full screen surfaces.
- */
- pixman_region32_init(&overlap);
- primary = &output_base->compositor->primary_plane;
-
- wl_list_for_each_safe(ev, next, &output_base->compositor->view_list, link) {
- struct weston_surface *es = ev->surface;
-
- /* Test whether this buffer can ever go into a plane:
- * non-shm, or small enough to be a cursor.
- *
- * Also, keep a reference when using the pixman renderer.
- * That makes it possible to do a seamless switch to the GL
- * renderer and since the pixman renderer keeps a reference
- * to the buffer anyway, there is no side effects.
- */
- if (b->use_pixman ||
- (es->buffer_ref.buffer &&
- (!wl_shm_buffer_get(es->buffer_ref.buffer->resource) ||
- (ev->surface->width <= b->cursor_width &&
- ev->surface->height <= b->cursor_height))))
- es->keep_buffer = true;
- else
- es->keep_buffer = false;
-
- pixman_region32_init(&surface_overlap);
- pixman_region32_intersect(&surface_overlap, &overlap,
- &ev->transform.boundingbox);
-
- next_plane = NULL;
- if (pixman_region32_not_empty(&surface_overlap))
- next_plane = primary;
- if (next_plane == NULL)
- next_plane = drm_output_prepare_cursor_view(output, ev);
- if (next_plane == NULL)
- next_plane = drm_output_prepare_scanout_view(output, ev);
- if (next_plane == NULL)
- next_plane = drm_output_prepare_overlay_view(output, ev);
- if (next_plane == NULL)
- next_plane = primary;
-
- weston_view_move_to_plane(ev, next_plane);
-
- if (next_plane == primary)
- pixman_region32_union(&overlap, &overlap,
- &ev->transform.boundingbox);
-
- if (next_plane == primary ||
- next_plane == &output->cursor_plane) {
- /* cursor plane involves a copy */
- ev->psf_flags = 0;
- } else {
- /* All other planes are a direct scanout of a
- * single client buffer.
- */
- ev->psf_flags = WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY;
- }
-
- pixman_region32_fini(&surface_overlap);
- }
- pixman_region32_fini(&overlap);
-}
-
-static void
-drm_output_fini_pixman(struct drm_output *output);
-
-static void
-drm_output_destroy(struct weston_output *output_base)
-{
- struct drm_output *output = (struct drm_output *) output_base;
- struct drm_backend *b =
- (struct drm_backend *)output->base.compositor->backend;
- drmModeCrtcPtr origcrtc = output->original_crtc;
-
- if (output->page_flip_pending) {
- output->destroy_pending = 1;
- weston_log("destroy output while page flip pending\n");
- return;
- }
-
- if (output->backlight)
- backlight_destroy(output->backlight);
-
- drmModeFreeProperty(output->dpms_prop);
-
- /* Turn off hardware cursor */
- drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
-
- /* Restore original CRTC state */
- drmModeSetCrtc(b->drm.fd, origcrtc->crtc_id, origcrtc->buffer_id,
- origcrtc->x, origcrtc->y,
- &output->connector_id, 1, &origcrtc->mode);
- drmModeFreeCrtc(origcrtc);
-
- b->crtc_allocator &= ~(1 << output->crtc_id);
- b->connector_allocator &= ~(1 << output->connector_id);
-
- if (b->use_pixman) {
- drm_output_fini_pixman(output);
- } else {
- gl_renderer->output_destroy(output_base);
- gbm_surface_destroy(output->gbm_surface);
- }
-
- weston_plane_release(&output->fb_plane);
- weston_plane_release(&output->cursor_plane);
-
- weston_output_destroy(&output->base);
-
- free(output);
-}
-
-/**
- * Find the closest-matching mode for a given target
- *
- * Given a target mode, find the most suitable mode amongst the output's
- * current mode list to use, preferring the current mode if possible, to
- * avoid an expensive mode switch.
- *
- * @param output DRM output
- * @param target_mode Mode to attempt to match
- * @returns Pointer to a mode from the output's mode list
- */
-static struct drm_mode *
-choose_mode (struct drm_output *output, struct weston_mode *target_mode)
-{
- struct drm_mode *tmp_mode = NULL, *mode;
-
- if (output->base.current_mode->width == target_mode->width &&
- output->base.current_mode->height == target_mode->height &&
- (output->base.current_mode->refresh == target_mode->refresh ||
- target_mode->refresh == 0))
- return (struct drm_mode *)output->base.current_mode;
-
- wl_list_for_each(mode, &output->base.mode_list, base.link) {
- if (mode->mode_info.hdisplay == target_mode->width &&
- mode->mode_info.vdisplay == target_mode->height) {
- if (mode->base.refresh == target_mode->refresh ||
- target_mode->refresh == 0) {
- return mode;
- } else if (!tmp_mode)
- tmp_mode = mode;
- }
- }
-
- return tmp_mode;
-}
-
-static int
-drm_output_init_egl(struct drm_output *output, struct drm_backend *b);
-static int
-drm_output_init_pixman(struct drm_output *output, struct drm_backend *b);
-
-static int
-drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode)
-{
- struct drm_output *output;
- struct drm_mode *drm_mode;
- struct drm_backend *b;
-
- if (output_base == NULL) {
- weston_log("output is NULL.\n");
- return -1;
- }
-
- if (mode == NULL) {
- weston_log("mode is NULL.\n");
- return -1;
- }
-
- b = (struct drm_backend *)output_base->compositor->backend;
- output = (struct drm_output *)output_base;
- drm_mode = choose_mode (output, mode);
-
- if (!drm_mode) {
- weston_log("%s, invalid resolution:%dx%d\n", __func__, mode->width, mode->height);
- return -1;
- }
-
- if (&drm_mode->base == output->base.current_mode)
- return 0;
-
- output->base.current_mode->flags = 0;
-
- output->base.current_mode = &drm_mode->base;
- output->base.current_mode->flags =
- WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
-
- /* reset rendering stuff. */
- drm_output_release_fb(output, output->current);
- drm_output_release_fb(output, output->next);
- output->current = output->next = NULL;
-
- if (b->use_pixman) {
- drm_output_fini_pixman(output);
- if (drm_output_init_pixman(output, b) < 0) {
- weston_log("failed to init output pixman state with "
- "new mode\n");
- return -1;
- }
- } else {
- gl_renderer->output_destroy(&output->base);
- gbm_surface_destroy(output->gbm_surface);
-
- if (drm_output_init_egl(output, b) < 0) {
- weston_log("failed to init output egl state with "
- "new mode");
- return -1;
- }
- }
-
- return 0;
-}
-
-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;
- evctx.vblank_handler = vblank_handler;
- drmHandleEvent(fd, &evctx);
-
- return 1;
-}
-
-static int
-init_drm(struct drm_backend *b, struct udev_device *device)
-{
- const char *filename, *sysnum;
- uint64_t cap;
- int fd, ret;
- clockid_t clk_id;
-
- sysnum = udev_device_get_sysnum(device);
- if (sysnum)
- b->drm.id = atoi(sysnum);
- if (!sysnum || b->drm.id < 0) {
- weston_log("cannot get device sysnum\n");
- return -1;
- }
-
- filename = udev_device_get_devnode(device);
- fd = weston_launcher_open(b->compositor->launcher, filename, O_RDWR);
- if (fd < 0) {
- /* Probably permissions error */
- weston_log("couldn't open %s, skipping\n",
- udev_device_get_devnode(device));
- return -1;
- }
-
- weston_log("using %s\n", filename);
-
- b->drm.fd = fd;
- b->drm.filename = strdup(filename);
-
- ret = drmGetCap(fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
- if (ret == 0 && cap == 1)
- clk_id = CLOCK_MONOTONIC;
- else
- clk_id = CLOCK_REALTIME;
-
- if (weston_compositor_set_presentation_clock(b->compositor, clk_id) < 0) {
- weston_log("Error: failed to set presentation clock %d.\n",
- clk_id);
- return -1;
- }
-
- ret = drmGetCap(fd, DRM_CAP_CURSOR_WIDTH, &cap);
- if (ret == 0)
- b->cursor_width = cap;
- else
- b->cursor_width = 64;
-
- ret = drmGetCap(fd, DRM_CAP_CURSOR_HEIGHT, &cap);
- if (ret == 0)
- b->cursor_height = cap;
- else
- b->cursor_height = 64;
-
- return 0;
-}
-
-static struct gbm_device *
-create_gbm_device(int fd)
-{
- struct gbm_device *gbm;
-
- gl_renderer = weston_load_module("gl-renderer.so",
- "gl_renderer_interface");
- if (!gl_renderer)
- return NULL;
-
- /* GBM will load a dri driver, but even though they need symbols from
- * libglapi, in some version of Mesa they are not linked to it. Since
- * only the gl-renderer module links to it, the call above won't make
- * these symbols globally available, and loading the DRI driver fails.
- * Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL. */
- dlopen("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL);
-
- gbm = gbm_create_device(fd);
-
- return gbm;
-}
-
-/* When initializing EGL, if the preferred buffer format isn't available
- * we may be able to substitute an ARGB format for an XRGB one.
- *
- * This returns 0 if substitution isn't possible, but 0 might be a
- * legitimate format for other EGL platforms, so the caller is
- * responsible for checking for 0 before calling gl_renderer->create().
- *
- * This works around https://bugs.freedesktop.org/show_bug.cgi?id=89689
- * but it's entirely possible we'll see this again on other implementations.
- */
-static int
-fallback_format_for(uint32_t format)
-{
- switch (format) {
- case GBM_FORMAT_XRGB8888:
- return GBM_FORMAT_ARGB8888;
- case GBM_FORMAT_XRGB2101010:
- return GBM_FORMAT_ARGB2101010;
- default:
- return 0;
- }
-}
-
-static int
-drm_backend_create_gl_renderer(struct drm_backend *b)
-{
- EGLint format[3] = {
- b->gbm_format,
- fallback_format_for(b->gbm_format),
- 0,
- };
- int n_formats = 2;
-
- if (format[1])
- n_formats = 3;
- if (gl_renderer->create(b->compositor,
- EGL_PLATFORM_GBM_KHR,
- (void *)b->gbm,
- gl_renderer->opaque_attribs,
- format,
- n_formats) < 0) {
- return -1;
- }
-
- return 0;
-}
-
-static int
-init_egl(struct drm_backend *b)
-{
- b->gbm = create_gbm_device(b->drm.fd);
-
- if (!b->gbm)
- return -1;
-
- if (drm_backend_create_gl_renderer(b) < 0) {
- gbm_device_destroy(b->gbm);
- return -1;
- }
-
- return 0;
-}
-
-static int
-init_pixman(struct drm_backend *b)
-{
- return pixman_renderer_init(b->compositor);
-}
-
-/**
- * Add a mode to output's mode list
- *
- * Copy the supplied DRM mode into a Weston mode structure, and add it to the
- * output's mode list.
- *
- * @param output DRM output to add mode to
- * @param info DRM mode structure to add
- * @returns Newly-allocated Weston/DRM mode structure
- */
-static struct drm_mode *
-drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info)
-{
- struct drm_mode *mode;
- uint64_t refresh;
-
- mode = malloc(sizeof *mode);
- if (mode == NULL)
- return NULL;
-
- mode->base.flags = 0;
- mode->base.width = info->hdisplay;
- mode->base.height = info->vdisplay;
-
- /* Calculate higher precision (mHz) refresh rate */
- refresh = (info->clock * 1000000LL / info->htotal +
- info->vtotal / 2) / info->vtotal;
-
- if (info->flags & DRM_MODE_FLAG_INTERLACE)
- refresh *= 2;
- if (info->flags & DRM_MODE_FLAG_DBLSCAN)
- refresh /= 2;
- if (info->vscan > 1)
- refresh /= info->vscan;
-
- mode->base.refresh = refresh;
- mode->mode_info = *info;
-
- if (info->type & DRM_MODE_TYPE_PREFERRED)
- mode->base.flags |= WL_OUTPUT_MODE_PREFERRED;
-
- wl_list_insert(output->base.mode_list.prev, &mode->base.link);
-
- return mode;
-}
-
-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;
- }
-}
-
-/* returns a value between 0-255 range, where higher is brighter */
-static uint32_t
-drm_get_backlight(struct drm_output *output)
-{
- long brightness, max_brightness, norm;
-
- brightness = backlight_get_brightness(output->backlight);
- max_brightness = backlight_get_max_brightness(output->backlight);
-
- /* convert it on a scale of 0 to 255 */
- norm = (brightness * 255)/(max_brightness);
-
- return (uint32_t) norm;
-}
-
-/* values accepted are between 0-255 range */
-static void
-drm_set_backlight(struct weston_output *output_base, uint32_t value)
-{
- struct drm_output *output = (struct drm_output *) output_base;
- long max_brightness, new_brightness;
-
- if (!output->backlight)
- return;
-
- if (value > 255)
- return;
-
- max_brightness = backlight_get_max_brightness(output->backlight);
-
- /* get denormalized value */
- new_brightness = (value * max_brightness) / 255;
-
- backlight_set_brightness(output->backlight, new_brightness);
-}
-
-static drmModePropertyPtr
-drm_get_prop(int fd, drmModeConnectorPtr connector, const char *name)
-{
- drmModePropertyPtr props;
- int i;
-
- for (i = 0; i < connector->count_props; i++) {
- props = drmModeGetProperty(fd, connector->props[i]);
- if (!props)
- continue;
-
- if (!strcmp(props->name, name))
- return props;
-
- drmModeFreeProperty(props);
- }
-
- return NULL;
-}
-
-static void
-drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
-{
- struct drm_output *output = (struct drm_output *) output_base;
- struct weston_compositor *ec = output_base->compositor;
- struct drm_backend *b = (struct drm_backend *)ec->backend;
- int ret;
-
- if (!output->dpms_prop)
- return;
-
- ret = drmModeConnectorSetProperty(b->drm.fd, output->connector_id,
- output->dpms_prop->prop_id, level);
- if (ret) {
- weston_log("DRM: DPMS: failed property set for %s\n",
- output->base.name);
- return;
- }
-
- output->dpms = level;
-}
-
-static const char * const connector_type_names[] = {
- [DRM_MODE_CONNECTOR_Unknown] = "Unknown",
- [DRM_MODE_CONNECTOR_VGA] = "VGA",
- [DRM_MODE_CONNECTOR_DVII] = "DVI-I",
- [DRM_MODE_CONNECTOR_DVID] = "DVI-D",
- [DRM_MODE_CONNECTOR_DVIA] = "DVI-A",
- [DRM_MODE_CONNECTOR_Composite] = "Composite",
- [DRM_MODE_CONNECTOR_SVIDEO] = "SVIDEO",
- [DRM_MODE_CONNECTOR_LVDS] = "LVDS",
- [DRM_MODE_CONNECTOR_Component] = "Component",
- [DRM_MODE_CONNECTOR_9PinDIN] = "DIN",
- [DRM_MODE_CONNECTOR_DisplayPort] = "DP",
- [DRM_MODE_CONNECTOR_HDMIA] = "HDMI-A",
- [DRM_MODE_CONNECTOR_HDMIB] = "HDMI-B",
- [DRM_MODE_CONNECTOR_TV] = "TV",
- [DRM_MODE_CONNECTOR_eDP] = "eDP",
-#ifdef DRM_MODE_CONNECTOR_DSI
- [DRM_MODE_CONNECTOR_VIRTUAL] = "Virtual",
- [DRM_MODE_CONNECTOR_DSI] = "DSI",
-#endif
-};
-
-static char *
-make_connector_name(const drmModeConnector *con)
-{
- char name[32];
- const char *type_name = NULL;
-
- if (con->connector_type < ARRAY_LENGTH(connector_type_names))
- type_name = connector_type_names[con->connector_type];
-
- if (!type_name)
- type_name = "UNNAMED";
-
- snprintf(name, sizeof name, "%s-%d", type_name, con->connector_type_id);
-
- return strdup(name);
-}
-
-static int
-find_crtc_for_connector(struct drm_backend *b,
- drmModeRes *resources, drmModeConnector *connector)
-{
- drmModeEncoder *encoder;
- uint32_t possible_crtcs;
- int i, j;
-
- for (j = 0; j < connector->count_encoders; j++) {
- encoder = drmModeGetEncoder(b->drm.fd, connector->encoders[j]);
- if (encoder == NULL) {
- weston_log("Failed to get encoder.\n");
- return -1;
- }
- possible_crtcs = encoder->possible_crtcs;
- drmModeFreeEncoder(encoder);
-
- for (i = 0; i < resources->count_crtcs; i++) {
- if (possible_crtcs & (1 << i) &&
- !(b->crtc_allocator & (1 << resources->crtcs[i])))
- return i;
- }
- }
-
- return -1;
-}
-
-/* Init output state that depends on gl or gbm */
-static int
-drm_output_init_egl(struct drm_output *output, struct drm_backend *b)
-{
- EGLint format[2] = {
- output->gbm_format,
- fallback_format_for(output->gbm_format),
- };
- int i, flags, n_formats = 1;
-
- output->gbm_surface = gbm_surface_create(b->gbm,
- output->base.current_mode->width,
- output->base.current_mode->height,
- format[0],
- GBM_BO_USE_SCANOUT |
- GBM_BO_USE_RENDERING);
- if (!output->gbm_surface) {
- weston_log("failed to create gbm surface\n");
- return -1;
- }
-
- if (format[1])
- n_formats = 2;
- if (gl_renderer->output_create(&output->base,
- (EGLNativeWindowType)output->gbm_surface,
- output->gbm_surface,
- gl_renderer->opaque_attribs,
- format,
- n_formats) < 0) {
- weston_log("failed to create gl renderer output state\n");
- gbm_surface_destroy(output->gbm_surface);
- return -1;
- }
-
- flags = GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE;
-
- for (i = 0; i < 2; i++) {
- if (output->gbm_cursor_bo[i])
- continue;
-
- output->gbm_cursor_bo[i] =
- gbm_bo_create(b->gbm, b->cursor_width, b->cursor_height,
- GBM_FORMAT_ARGB8888, flags);
- }
-
- if (output->gbm_cursor_bo[0] == NULL || output->gbm_cursor_bo[1] == NULL) {
- weston_log("cursor buffers unavailable, using gl cursors\n");
- b->cursors_are_broken = 1;
- }
-
- return 0;
-}
-
-static int
-drm_output_init_pixman(struct drm_output *output, struct drm_backend *b)
-{
- int w = output->base.current_mode->width;
- int h = output->base.current_mode->height;
- uint32_t format = output->gbm_format;
- uint32_t pixman_format;
- unsigned int i;
-
- switch (format) {
- case GBM_FORMAT_XRGB8888:
- pixman_format = PIXMAN_x8r8g8b8;
- break;
- case GBM_FORMAT_RGB565:
- pixman_format = PIXMAN_r5g6b5;
- break;
- default:
- weston_log("Unsupported pixman format 0x%x\n", format);
- return -1;
- }
-
- /* FIXME error checking */
- for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
- output->dumb[i] = drm_fb_create_dumb(b, w, h, format);
- if (!output->dumb[i])
- goto err;
-
- output->image[i] =
- pixman_image_create_bits(pixman_format, w, h,
- output->dumb[i]->map,
- output->dumb[i]->stride);
- if (!output->image[i])
- goto err;
- }
-
- if (pixman_renderer_output_create(&output->base) < 0)
- goto err;
-
- pixman_region32_init_rect(&output->previous_damage,
- output->base.x, output->base.y, output->base.width, output->base.height);
-
- return 0;
-
-err:
- for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
- if (output->dumb[i])
- drm_fb_destroy_dumb(output->dumb[i]);
- if (output->image[i])
- pixman_image_unref(output->image[i]);
-
- output->dumb[i] = NULL;
- output->image[i] = NULL;
- }
-
- return -1;
-}
-
-static void
-drm_output_fini_pixman(struct drm_output *output)
-{
- unsigned int i;
-
- pixman_renderer_output_destroy(&output->base);
- pixman_region32_fini(&output->previous_damage);
-
- for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) {
- drm_fb_destroy_dumb(output->dumb[i]);
- pixman_image_unref(output->image[i]);
- output->dumb[i] = NULL;
- output->image[i] = NULL;
- }
-}
-
-static void
-edid_parse_string(const uint8_t *data, char text[])
-{
- int i;
- int replaced = 0;
-
- /* this is always 12 bytes, but we can't guarantee it's null
- * terminated or not junk. */
- strncpy(text, (const char *) data, 12);
-
- /* guarantee our new string is null-terminated */
- text[12] = '\0';
-
- /* remove insane chars */
- for (i = 0; text[i] != '\0'; i++) {
- if (text[i] == '\n' ||
- text[i] == '\r') {
- text[i] = '\0';
- break;
- }
- }
-
- /* ensure string is printable */
- for (i = 0; text[i] != '\0'; i++) {
- if (!isprint(text[i])) {
- text[i] = '-';
- replaced++;
- }
- }
-
- /* if the string is random junk, ignore the string */
- if (replaced > 4)
- text[0] = '\0';
-}
-
-#define EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING 0xfe
-#define EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME 0xfc
-#define EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER 0xff
-#define EDID_OFFSET_DATA_BLOCKS 0x36
-#define EDID_OFFSET_LAST_BLOCK 0x6c
-#define EDID_OFFSET_PNPID 0x08
-#define EDID_OFFSET_SERIAL 0x0c
-
-static int
-edid_parse(struct drm_edid *edid, const uint8_t *data, size_t length)
-{
- int i;
- uint32_t serial_number;
-
- /* check header */
- if (length < 128)
- return -1;
- if (data[0] != 0x00 || data[1] != 0xff)
- return -1;
-
- /* decode the PNP ID from three 5 bit words packed into 2 bytes
- * /--08--\/--09--\
- * 7654321076543210
- * |\---/\---/\---/
- * R C1 C2 C3 */
- edid->pnp_id[0] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x7c) / 4) - 1;
- edid->pnp_id[1] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x3) * 8) + ((data[EDID_OFFSET_PNPID + 1] & 0xe0) / 32) - 1;
- edid->pnp_id[2] = 'A' + (data[EDID_OFFSET_PNPID + 1] & 0x1f) - 1;
- edid->pnp_id[3] = '\0';
-
- /* maybe there isn't a ASCII serial number descriptor, so use this instead */
- serial_number = (uint32_t) data[EDID_OFFSET_SERIAL + 0];
- serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 1] * 0x100;
- serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 2] * 0x10000;
- serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 3] * 0x1000000;
- if (serial_number > 0)
- sprintf(edid->serial_number, "%lu", (unsigned long) serial_number);
-
- /* parse EDID data */
- for (i = EDID_OFFSET_DATA_BLOCKS;
- i <= EDID_OFFSET_LAST_BLOCK;
- i += 18) {
- /* ignore pixel clock data */
- if (data[i] != 0)
- continue;
- if (data[i+2] != 0)
- continue;
-
- /* any useful blocks? */
- if (data[i+3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME) {
- edid_parse_string(&data[i+5],
- edid->monitor_name);
- } else if (data[i+3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER) {
- edid_parse_string(&data[i+5],
- edid->serial_number);
- } else if (data[i+3] == EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING) {
- edid_parse_string(&data[i+5],
- edid->eisa_id);
- }
- }
- return 0;
-}
-
-static void
-find_and_parse_output_edid(struct drm_backend *b,
- struct drm_output *output,
- drmModeConnector *connector)
-{
- drmModePropertyBlobPtr edid_blob = NULL;
- drmModePropertyPtr property;
- int i;
- int rc;
-
- for (i = 0; i < connector->count_props && !edid_blob; i++) {
- property = drmModeGetProperty(b->drm.fd, connector->props[i]);
- if (!property)
- continue;
- if ((property->flags & DRM_MODE_PROP_BLOB) &&
- !strcmp(property->name, "EDID")) {
- edid_blob = drmModeGetPropertyBlob(b->drm.fd,
- connector->prop_values[i]);
- }
- drmModeFreeProperty(property);
- }
- if (!edid_blob)
- return;
-
- rc = edid_parse(&output->edid,
- edid_blob->data,
- edid_blob->length);
- if (!rc) {
- weston_log("EDID data '%s', '%s', '%s'\n",
- output->edid.pnp_id,
- output->edid.monitor_name,
- output->edid.serial_number);
- if (output->edid.pnp_id[0] != '\0')
- output->base.make = output->edid.pnp_id;
- if (output->edid.monitor_name[0] != '\0')
- output->base.model = output->edid.monitor_name;
- if (output->edid.serial_number[0] != '\0')
- output->base.serial_number = output->edid.serial_number;
- }
- drmModeFreePropertyBlob(edid_blob);
-}
-
-
-
-static int
-parse_modeline(const char *s, drmModeModeInfo *mode)
-{
- char hsync[16];
- char vsync[16];
- float fclock;
-
- mode->type = DRM_MODE_TYPE_USERDEF;
- mode->hskew = 0;
- mode->vscan = 0;
- mode->vrefresh = 0;
- mode->flags = 0;
-
- if (sscanf(s, "%f %hd %hd %hd %hd %hd %hd %hd %hd %15s %15s",
- &fclock,
- &mode->hdisplay,
- &mode->hsync_start,
- &mode->hsync_end,
- &mode->htotal,
- &mode->vdisplay,
- &mode->vsync_start,
- &mode->vsync_end,
- &mode->vtotal, hsync, vsync) != 11)
- return -1;
-
- mode->clock = fclock * 1000;
- if (strcmp(hsync, "+hsync") == 0)
- mode->flags |= DRM_MODE_FLAG_PHSYNC;
- else if (strcmp(hsync, "-hsync") == 0)
- mode->flags |= DRM_MODE_FLAG_NHSYNC;
- else
- return -1;
-
- if (strcmp(vsync, "+vsync") == 0)
- mode->flags |= DRM_MODE_FLAG_PVSYNC;
- else if (strcmp(vsync, "-vsync") == 0)
- mode->flags |= DRM_MODE_FLAG_NVSYNC;
- else
- return -1;
-
- snprintf(mode->name, sizeof mode->name, "%dx%d@%.3f",
- mode->hdisplay, mode->vdisplay, fclock);
-
- return 0;
-}
-
-static void
-setup_output_seat_constraint(struct drm_backend *b,
- struct weston_output *output,
- const char *s)
-{
- if (strcmp(s, "") != 0) {
- struct weston_pointer *pointer;
- struct udev_seat *seat;
-
- seat = udev_seat_get_named(&b->input, s);
- if (!seat)
- return;
-
- seat->base.output = output;
-
- pointer = weston_seat_get_pointer(&seat->base);
- if (pointer)
- weston_pointer_clamp(pointer,
- &pointer->x,
- &pointer->y);
- }
-}
-
-static int
-parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format)
-{
- int ret = 0;
-
- if (s == NULL)
- *gbm_format = default_value;
- else if (strcmp(s, "xrgb8888") == 0)
- *gbm_format = GBM_FORMAT_XRGB8888;
- else if (strcmp(s, "rgb565") == 0)
- *gbm_format = GBM_FORMAT_RGB565;
- else if (strcmp(s, "xrgb2101010") == 0)
- *gbm_format = GBM_FORMAT_XRGB2101010;
- else {
- weston_log("fatal: unrecognized pixel format: %s\n", s);
- ret = -1;
- }
-
- return ret;
-}
-
-/**
- * Choose suitable mode for an output
- *
- * Find the most suitable mode to use for initial setup (or reconfiguration on
- * hotplug etc) for a DRM output.
- *
- * @param output DRM output to choose mode for
- * @param kind Strategy and preference to use when choosing mode
- * @param width Desired width for this output
- * @param height Desired height for this output
- * @param current_mode Mode currently being displayed on this output
- * @param modeline Manually-entered mode (may be NULL)
- * @returns A mode from the output's mode list, or NULL if none available
- */
-static struct drm_mode *
-drm_output_choose_initial_mode(struct drm_backend *backend,
- struct drm_output *output,
- enum weston_drm_backend_output_mode mode,
- struct weston_drm_backend_output_config *config,
- const drmModeModeInfo *current_mode)
-{
- struct drm_mode *preferred = NULL;
- struct drm_mode *current = NULL;
- struct drm_mode *configured = NULL;
- struct drm_mode *best = NULL;
- struct drm_mode *drm_mode;
- drmModeModeInfo modeline;
- int32_t width = 0;
- int32_t height = 0;
-
- if (mode == WESTON_DRM_BACKEND_OUTPUT_PREFERRED && config->modeline) {
- if (sscanf(config->modeline, "%dx%d", &width, &height) != 2) {
- width = -1;
-
- if (parse_modeline(config->modeline, &modeline) == 0) {
- configured = drm_output_add_mode(output, &modeline);
- if (!configured)
- return NULL;
- } else {
- weston_log("Invalid modeline \"%s\" for output %s\n",
- config->modeline, output->base.name);
- }
- }
- }
-
- wl_list_for_each_reverse(drm_mode, &output->base.mode_list, base.link) {
- if (width == drm_mode->base.width &&
- height == drm_mode->base.height)
- configured = drm_mode;
-
- if (memcmp(current_mode, &drm_mode->mode_info,
- sizeof *current_mode) == 0)
- current = drm_mode;
-
- if (drm_mode->base.flags & WL_OUTPUT_MODE_PREFERRED)
- preferred = drm_mode;
-
- best = drm_mode;
- }
-
- if (current == NULL && current_mode->clock != 0) {
- current = drm_output_add_mode(output, current_mode);
- if (!current)
- return NULL;
- }
-
- if (mode == WESTON_DRM_BACKEND_OUTPUT_CURRENT)
- configured = current;
-
- if (configured)
- return configured;
-
- if (preferred)
- return preferred;
-
- if (current)
- return current;
-
- if (best)
- return best;
-
- weston_log("no available modes for %s\n", output->base.name);
- return NULL;
-}
-
-static int
-connector_get_current_mode(drmModeConnector *connector, int drm_fd,
- drmModeModeInfo *mode)
-{
- drmModeEncoder *encoder;
- drmModeCrtc *crtc;
-
- /* Get the current mode on the crtc that's currently driving
- * this connector. */
- encoder = drmModeGetEncoder(drm_fd, connector->encoder_id);
- memset(mode, 0, sizeof *mode);
- if (encoder != NULL) {
- crtc = drmModeGetCrtc(drm_fd, encoder->crtc_id);
- drmModeFreeEncoder(encoder);
- if (crtc == NULL)
- return -1;
- if (crtc->mode_valid)
- *mode = crtc->mode;
- drmModeFreeCrtc(crtc);
- }
-
- return 0;
-}
-
-/**
- * Create and configure a Weston output structure
- *
- * Given a DRM connector, create a matching drm_output structure and add it
- * to Weston's output list.
- *
- * @param b Weston backend structure structure
- * @param resources DRM resources for this device
- * @param connector DRM connector to use for this new output
- * @param x Horizontal offset to use into global co-ordinate space
- * @param y Vertical offset to use into global co-ordinate space
- * @param drm_device udev device pointer
- * @returns 0 on success, or -1 on failure
- */
-static int
-create_output_for_connector(struct drm_backend *b,
- drmModeRes *resources,
- drmModeConnector *connector,
- int x, int y, struct udev_device *drm_device)
-{
- struct drm_output *output;
- struct drm_mode *drm_mode, *next, *current;
- struct weston_mode *m;
-
- drmModeModeInfo crtc_mode;
- int i;
- enum weston_drm_backend_output_mode mode;
- struct weston_drm_backend_output_config config = {{ 0 }};
-
- i = find_crtc_for_connector(b, resources, connector);
- if (i < 0) {
- weston_log("No usable crtc/encoder pair for connector.\n");
- return -1;
- }
-
- output = zalloc(sizeof *output);
- if (output == NULL)
- return -1;
-
- output->base.subpixel = drm_subpixel_to_wayland(connector->subpixel);
- output->base.name = make_connector_name(connector);
- output->base.make = "unknown";
- output->base.model = "unknown";
- output->base.serial_number = "unknown";
- wl_list_init(&output->base.mode_list);
-
- mode = b->configure_output(b->compositor, b->use_current_mode,
- output->base.name, &config);
- if (parse_gbm_format(config.gbm_format, b->gbm_format, &output->gbm_format) == -1)
- output->gbm_format = b->gbm_format;
-
- setup_output_seat_constraint(b, &output->base,
- config.seat ? config.seat : "");
- free(config.seat);
-
- output->crtc_id = resources->crtcs[i];
- output->pipe = i;
- b->crtc_allocator |= (1 << output->crtc_id);
- output->connector_id = connector->connector_id;
- b->connector_allocator |= (1 << output->connector_id);
-
- output->original_crtc = drmModeGetCrtc(b->drm.fd, output->crtc_id);
- output->dpms_prop = drm_get_prop(b->drm.fd, connector, "DPMS");
-
- if (connector_get_current_mode(connector, b->drm.fd, &crtc_mode) < 0)
- goto err_free;
-
- for (i = 0; i < connector->count_modes; i++) {
- drm_mode = drm_output_add_mode(output, &connector->modes[i]);
- if (!drm_mode)
- goto err_free;
- }
-
- if (mode == WESTON_DRM_BACKEND_OUTPUT_OFF) {
- weston_log("Disabling output %s\n", output->base.name);
- drmModeSetCrtc(b->drm.fd, output->crtc_id,
- 0, 0, 0, 0, 0, NULL);
- goto err_free;
- }
-
- current = drm_output_choose_initial_mode(b, output, mode, &config,
- &crtc_mode);
- if (!current)
- goto err_free;
- output->base.current_mode = ¤t->base;
- output->base.current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
-
- weston_output_init(&output->base, b->compositor, x, y,
- connector->mmWidth, connector->mmHeight,
- config.base.transform, config.base.scale);
-
- if (b->use_pixman) {
- if (drm_output_init_pixman(output, b) < 0) {
- weston_log("Failed to init output pixman state\n");
- goto err_output;
- }
- } else if (drm_output_init_egl(output, b) < 0) {
- weston_log("Failed to init output gl state\n");
- goto err_output;
- }
-
- output->backlight = backlight_init(drm_device,
- connector->connector_type);
- if (output->backlight) {
- weston_log("Initialized backlight, device %s\n",
- output->backlight->path);
- output->base.set_backlight = drm_set_backlight;
- output->base.backlight_current = drm_get_backlight(output);
- } else {
- weston_log("Failed to initialize backlight\n");
- }
-
- weston_compositor_add_output(b->compositor, &output->base);
-
- find_and_parse_output_edid(b, output, connector);
- if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
- output->base.connection_internal = 1;
-
- output->base.start_repaint_loop = drm_output_start_repaint_loop;
- output->base.repaint = drm_output_repaint;
- output->base.destroy = drm_output_destroy;
- output->base.assign_planes = drm_assign_planes;
- output->base.set_dpms = drm_set_dpms;
- output->base.switch_mode = drm_output_switch_mode;
-
- output->base.gamma_size = output->original_crtc->gamma_size;
- output->base.set_gamma = drm_output_set_gamma;
-
- weston_plane_init(&output->cursor_plane, b->compositor,
- INT32_MIN, INT32_MIN);
- weston_plane_init(&output->fb_plane, b->compositor, 0, 0);
-
- weston_compositor_stack_plane(b->compositor, &output->cursor_plane, NULL);
- weston_compositor_stack_plane(b->compositor, &output->fb_plane,
- &b->compositor->primary_plane);
-
- weston_log("Output %s, (connector %d, crtc %d)\n",
- output->base.name, output->connector_id, output->crtc_id);
- wl_list_for_each(m, &output->base.mode_list, link)
- weston_log_continue(STAMP_SPACE "mode %dx%d@%.1f%s%s%s\n",
- m->width, m->height, m->refresh / 1000.0,
- m->flags & WL_OUTPUT_MODE_PREFERRED ?
- ", preferred" : "",
- m->flags & WL_OUTPUT_MODE_CURRENT ?
- ", current" : "",
- connector->count_modes == 0 ?
- ", built-in" : "");
-
- /* Set native_ fields, so weston_output_mode_switch_to_native() works */
- output->base.native_mode = output->base.current_mode;
- output->base.native_scale = output->base.current_scale;
-
- return 0;
-
-err_output:
- weston_output_destroy(&output->base);
-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);
- b->crtc_allocator &= ~(1 << output->crtc_id);
- b->connector_allocator &= ~(1 << output->connector_id);
- free(output);
- free(config.modeline);
-
- return -1;
-}
-
-static void
-create_sprites(struct drm_backend *b)
-{
- struct drm_sprite *sprite;
- drmModePlaneRes *plane_res;
- drmModePlane *plane;
- uint32_t i;
-
- plane_res = drmModeGetPlaneResources(b->drm.fd);
- if (!plane_res) {
- weston_log("failed to get plane resources: %s\n",
- strerror(errno));
- return;
- }
-
- for (i = 0; i < plane_res->count_planes; i++) {
- plane = drmModeGetPlane(b->drm.fd, plane_res->planes[i]);
- if (!plane)
- continue;
-
- sprite = zalloc(sizeof(*sprite) + ((sizeof(uint32_t)) *
- plane->count_formats));
- if (!sprite) {
- weston_log("%s: out of memory\n",
- __func__);
- drmModeFreePlane(plane);
- continue;
- }
-
- sprite->possible_crtcs = plane->possible_crtcs;
- sprite->plane_id = plane->plane_id;
- sprite->current = NULL;
- sprite->next = NULL;
- sprite->backend = b;
- sprite->count_formats = plane->count_formats;
- memcpy(sprite->formats, plane->formats,
- plane->count_formats * sizeof(plane->formats[0]));
- drmModeFreePlane(plane);
- weston_plane_init(&sprite->plane, b->compositor, 0, 0);
- weston_compositor_stack_plane(b->compositor, &sprite->plane,
- &b->compositor->primary_plane);
-
- wl_list_insert(&b->sprite_list, &sprite->link);
- }
-
- drmModeFreePlaneResources(plane_res);
-}
-
-static void
-destroy_sprites(struct drm_backend *backend)
-{
- struct drm_sprite *sprite, *next;
- struct drm_output *output;
-
- output = container_of(backend->compositor->output_list.next,
- struct drm_output, base.link);
-
- wl_list_for_each_safe(sprite, next, &backend->sprite_list, link) {
- drmModeSetPlane(backend->drm.fd,
- sprite->plane_id,
- output->crtc_id, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0);
- drm_output_release_fb(output, sprite->current);
- drm_output_release_fb(output, sprite->next);
- weston_plane_release(&sprite->plane);
- free(sprite);
- }
-}
-
-static int
-create_outputs(struct drm_backend *b, uint32_t option_connector,
- struct udev_device *drm_device)
-{
- drmModeConnector *connector;
- drmModeRes *resources;
- int i;
- int x = 0, y = 0;
-
- resources = drmModeGetResources(b->drm.fd);
- if (!resources) {
- weston_log("drmModeGetResources failed\n");
- return -1;
- }
-
- b->crtcs = calloc(resources->count_crtcs, sizeof(uint32_t));
- if (!b->crtcs) {
- drmModeFreeResources(resources);
- return -1;
- }
-
- b->min_width = resources->min_width;
- b->max_width = resources->max_width;
- b->min_height = resources->min_height;
- b->max_height = resources->max_height;
-
- b->num_crtcs = resources->count_crtcs;
- memcpy(b->crtcs, resources->crtcs, sizeof(uint32_t) * b->num_crtcs);
-
- for (i = 0; i < resources->count_connectors; i++) {
- connector = drmModeGetConnector(b->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(b, resources,
- connector, x, y,
- drm_device) < 0) {
- drmModeFreeConnector(connector);
- continue;
- }
-
- x += container_of(b->compositor->output_list.prev,
- struct weston_output,
- link)->width;
- }
-
- drmModeFreeConnector(connector);
- }
-
- if (wl_list_empty(&b->compositor->output_list)) {
- weston_log("No currently active connector found.\n");
- drmModeFreeResources(resources);
- return -1;
- }
-
- drmModeFreeResources(resources);
-
- return 0;
-}
-
-static void
-update_outputs(struct drm_backend *b, struct udev_device *drm_device)
-{
- drmModeConnector *connector;
- drmModeRes *resources;
- struct drm_output *output, *next;
- int x = 0, y = 0;
- uint32_t connected = 0, disconnects = 0;
- int i;
-
- resources = drmModeGetResources(b->drm.fd);
- if (!resources) {
- weston_log("drmModeGetResources failed\n");
- return;
- }
-
- /* collect new connects */
- for (i = 0; i < resources->count_connectors; i++) {
- int connector_id = resources->connectors[i];
-
- connector = drmModeGetConnector(b->drm.fd, connector_id);
- if (connector == NULL)
- continue;
-
- if (connector->connection != DRM_MODE_CONNECTED) {
- drmModeFreeConnector(connector);
- continue;
- }
-
- connected |= (1 << connector_id);
-
- if (!(b->connector_allocator & (1 << connector_id))) {
- struct weston_output *last =
- container_of(b->compositor->output_list.prev,
- struct weston_output, link);
-
- /* XXX: not yet needed, we die with 0 outputs */
- if (!wl_list_empty(&b->compositor->output_list))
- x = last->x + last->width;
- else
- x = 0;
- y = 0;
- create_output_for_connector(b, resources,
- connector, x, y,
- drm_device);
- weston_log("connector %d connected\n", connector_id);
-
- }
- drmModeFreeConnector(connector);
- }
- drmModeFreeResources(resources);
-
- disconnects = b->connector_allocator & ~connected;
- if (disconnects) {
- wl_list_for_each_safe(output, next, &b->compositor->output_list,
- base.link) {
- if (disconnects & (1 << output->connector_id)) {
- disconnects &= ~(1 << output->connector_id);
- weston_log("connector %d disconnected\n",
- output->connector_id);
- drm_output_destroy(&output->base);
- }
- }
- }
-
- /* FIXME: handle zero outputs, without terminating */
- if (b->connector_allocator == 0)
- weston_compositor_exit(b->compositor);
-}
-
-static int
-udev_event_is_hotplug(struct drm_backend *b, struct udev_device *device)
-{
- const char *sysnum;
- const char *val;
-
- sysnum = udev_device_get_sysnum(device);
- if (!sysnum || atoi(sysnum) != b->drm.id)
- return 0;
-
- val = udev_device_get_property_value(device, "HOTPLUG");
- if (!val)
- return 0;
-
- return strcmp(val, "1") == 0;
-}
-
-static int
-udev_drm_event(int fd, uint32_t mask, void *data)
-{
- struct drm_backend *b = data;
- struct udev_device *event;
-
- event = udev_monitor_receive_device(b->udev_monitor);
-
- if (udev_event_is_hotplug(b, event))
- update_outputs(b, event);
-
- udev_device_unref(event);
-
- return 1;
-}
-
-static void
-drm_restore(struct weston_compositor *ec)
-{
- weston_launcher_restore(ec->launcher);
-}
-
-static void
-drm_destroy(struct weston_compositor *ec)
-{
- struct drm_backend *b = (struct drm_backend *) ec->backend;
-
- udev_input_destroy(&b->input);
-
- wl_event_source_remove(b->udev_drm_source);
- wl_event_source_remove(b->drm_source);
-
- destroy_sprites(b);
-
- weston_compositor_shutdown(ec);
-
- if (b->gbm)
- gbm_device_destroy(b->gbm);
-
- weston_launcher_destroy(ec->launcher);
-
- close(b->drm.fd);
- free(b);
-}
-
-static void
-drm_backend_set_modes(struct drm_backend *backend)
-{
- struct drm_output *output;
- struct drm_mode *drm_mode;
- int ret;
-
- wl_list_for_each(output, &backend->compositor->output_list, base.link) {
- if (!output->current) {
- /* If something that would cause the output to
- * switch mode happened while in another vt, we
- * might not have a current drm_fb. In that case,
- * schedule a repaint and let drm_output_repaint
- * handle setting the mode. */
- weston_output_schedule_repaint(&output->base);
- continue;
- }
-
- drm_mode = (struct drm_mode *) output->base.current_mode;
- ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id,
- output->current->fb_id, 0, 0,
- &output->connector_id, 1,
- &drm_mode->mode_info);
- if (ret < 0) {
- weston_log(
- "failed to set mode %dx%d for output at %d,%d: %m\n",
- drm_mode->base.width, drm_mode->base.height,
- output->base.x, output->base.y);
- }
- }
-}
-
-static void
-session_notify(struct wl_listener *listener, void *data)
-{
- struct weston_compositor *compositor = data;
- struct drm_backend *b = (struct drm_backend *)compositor->backend;
- struct drm_sprite *sprite;
- struct drm_output *output;
-
- if (compositor->session_active) {
- weston_log("activating session\n");
- compositor->state = b->prev_state;
- drm_backend_set_modes(b);
- weston_compositor_damage_all(compositor);
- udev_input_enable(&b->input);
- } else {
- weston_log("deactivating session\n");
- udev_input_disable(&b->input);
-
- b->prev_state = compositor->state;
- weston_compositor_offscreen(compositor);
-
- /* If we have a repaint scheduled (either from a
- * pending pageflip or the idle handler), make sure we
- * cancel that so we don't try to pageflip when we're
- * vt switched away. The OFFSCREEN state will prevent
- * further attemps at repainting. When we switch
- * back, we schedule a repaint, which will process
- * pending frame callbacks. */
-
- wl_list_for_each(output, &compositor->output_list, base.link) {
- output->base.repaint_needed = 0;
- drmModeSetCursor(b->drm.fd, output->crtc_id, 0, 0, 0);
- }
-
- output = container_of(compositor->output_list.next,
- struct drm_output, base.link);
-
- wl_list_for_each(sprite, &b->sprite_list, link)
- drmModeSetPlane(b->drm.fd,
- sprite->plane_id,
- output->crtc_id, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0);
- };
-}
-
-/*
- * Find primary GPU
- * Some systems may have multiple DRM devices attached to a single seat. This
- * function loops over all devices and tries to find a PCI device with the
- * boot_vga sysfs attribute set to 1.
- * If no such device is found, the first DRM device reported by udev is used.
- */
-static struct udev_device*
-find_primary_gpu(struct drm_backend *b, const char *seat)
-{
- struct udev_enumerate *e;
- struct udev_list_entry *entry;
- const char *path, *device_seat, *id;
- struct udev_device *device, *drm_device, *pci;
-
- e = udev_enumerate_new(b->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(b->udev, path);
- if (!device)
- continue;
- device_seat = udev_device_get_property_value(device, "ID_SEAT");
- if (!device_seat)
- device_seat = default_seat;
- if (strcmp(device_seat, seat)) {
- udev_device_unref(device);
- continue;
- }
-
- pci = udev_device_get_parent_with_subsystem_devtype(device,
- "pci", NULL);
- if (pci) {
- id = udev_device_get_sysattr_value(pci, "boot_vga");
- if (id && !strcmp(id, "1")) {
- if (drm_device)
- udev_device_unref(drm_device);
- drm_device = device;
- break;
- }
- }
-
- if (!drm_device)
- drm_device = device;
- else
- udev_device_unref(device);
- }
-
- udev_enumerate_unref(e);
- return drm_device;
-}
-
-static void
-planes_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key,
- void *data)
-{
- struct drm_backend *b = data;
-
- switch (key) {
- case KEY_C:
- b->cursors_are_broken ^= 1;
- break;
- case KEY_V:
- b->sprites_are_broken ^= 1;
- break;
- case KEY_O:
- b->sprites_hidden ^= 1;
- break;
- default:
- break;
- }
-}
-
-#ifdef BUILD_VAAPI_RECORDER
-static void
-recorder_destroy(struct drm_output *output)
-{
- vaapi_recorder_destroy(output->recorder);
- output->recorder = NULL;
-
- output->base.disable_planes--;
-
- wl_list_remove(&output->recorder_frame_listener.link);
- weston_log("[libva recorder] done\n");
-}
-
-static void
-recorder_frame_notify(struct wl_listener *listener, void *data)
-{
- struct drm_output *output;
- struct drm_backend *b;
- int fd, ret;
-
- output = container_of(listener, struct drm_output,
- recorder_frame_listener);
- b = (struct drm_backend *)output->base.compositor->backend;
-
- if (!output->recorder)
- return;
-
- ret = drmPrimeHandleToFD(b->drm.fd, output->current->handle,
- DRM_CLOEXEC, &fd);
- if (ret) {
- weston_log("[libva recorder] "
- "failed to create prime fd for front buffer\n");
- return;
- }
-
- ret = vaapi_recorder_frame(output->recorder, fd,
- output->current->stride);
- if (ret < 0) {
- weston_log("[libva recorder] aborted: %m\n");
- recorder_destroy(output);
- }
-}
-
-static void *
-create_recorder(struct drm_backend *b, int width, int height,
- const char *filename)
-{
- int fd;
- drm_magic_t magic;
-
- fd = open(b->drm.filename, O_RDWR | O_CLOEXEC);
- if (fd < 0)
- return NULL;
-
- drmGetMagic(fd, &magic);
- drmAuthMagic(b->drm.fd, magic);
-
- return vaapi_recorder_create(fd, width, height, filename);
-}
-
-static void
-recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key,
- void *data)
-{
- struct drm_backend *b = data;
- struct drm_output *output;
- int width, height;
-
- output = container_of(b->compositor->output_list.next,
- struct drm_output, base.link);
-
- if (!output->recorder) {
- if (output->gbm_format != GBM_FORMAT_XRGB8888) {
- weston_log("failed to start vaapi recorder: "
- "output format not supported\n");
- return;
- }
-
- width = output->base.current_mode->width;
- height = output->base.current_mode->height;
-
- output->recorder =
- create_recorder(b, width, height, "capture.h264");
- if (!output->recorder) {
- weston_log("failed to create vaapi recorder\n");
- return;
- }
-
- output->base.disable_planes++;
-
- output->recorder_frame_listener.notify = recorder_frame_notify;
- wl_signal_add(&output->base.frame_signal,
- &output->recorder_frame_listener);
-
- weston_output_schedule_repaint(&output->base);
-
- weston_log("[libva recorder] initialized\n");
- } else {
- recorder_destroy(output);
- }
-}
-#else
-static void
-recorder_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key,
- void *data)
-{
- weston_log("Compiled without libva support\n");
-}
-#endif
-
-static void
-switch_to_gl_renderer(struct drm_backend *b)
-{
- struct drm_output *output;
- bool dmabuf_support_inited;
-
- if (!b->use_pixman)
- return;
-
- dmabuf_support_inited = !!b->compositor->renderer->import_dmabuf;
-
- weston_log("Switching to GL renderer\n");
-
- b->gbm = create_gbm_device(b->drm.fd);
- if (!b->gbm) {
- weston_log("Failed to create gbm device. "
- "Aborting renderer switch\n");
- return;
- }
-
- wl_list_for_each(output, &b->compositor->output_list, base.link)
- pixman_renderer_output_destroy(&output->base);
-
- b->compositor->renderer->destroy(b->compositor);
-
- if (drm_backend_create_gl_renderer(b) < 0) {
- gbm_device_destroy(b->gbm);
- weston_log("Failed to create GL renderer. Quitting.\n");
- /* FIXME: we need a function to shutdown cleanly */
- assert(0);
- }
-
- wl_list_for_each(output, &b->compositor->output_list, base.link)
- drm_output_init_egl(output, b);
-
- b->use_pixman = 0;
-
- if (!dmabuf_support_inited && b->compositor->renderer->import_dmabuf) {
- if (linux_dmabuf_setup(b->compositor) < 0)
- weston_log("Error: initializing dmabuf "
- "support failed.\n");
- }
-}
-
-static void
-renderer_switch_binding(struct weston_keyboard *keyboard, uint32_t time,
- uint32_t key, void *data)
-{
- struct drm_backend *b =
- (struct drm_backend *) keyboard->seat->compositor;
-
- switch_to_gl_renderer(b);
-}
-
-static struct drm_backend *
-drm_backend_create(struct weston_compositor *compositor,
- struct weston_drm_backend_config *config)
-{
- struct drm_backend *b;
- struct udev_device *drm_device;
- struct wl_event_loop *loop;
- const char *path;
- const char *seat_id = default_seat;
-
- weston_log("initializing drm backend\n");
-
- b = zalloc(sizeof *b);
- if (b == NULL)
- return NULL;
-
- /*
- * KMS support for hardware planes cannot properly synchronize
- * without nuclear page flip. Without nuclear/atomic, hw plane
- * and cursor plane updates would either tear or cause extra
- * waits for vblanks which means dropping the compositor framerate
- * to a fraction. For cursors, it's not so bad, so they are
- * enabled.
- *
- * These can be enabled again when nuclear/atomic support lands.
- */
- b->sprites_are_broken = 1;
- b->compositor = compositor;
- b->use_pixman = config->use_pixman;
- b->configure_output = config->configure_output;
- b->use_current_mode = config->use_current_mode;
-
- if (parse_gbm_format(config->gbm_format, GBM_FORMAT_XRGB8888, &b->gbm_format) < 0)
- goto err_compositor;
-
- if (config->seat_id)
- seat_id = config->seat_id;
-
- /* Check if we run drm-backend using weston-launch */
- compositor->launcher = weston_launcher_connect(compositor, config->tty,
- seat_id, true);
- if (compositor->launcher == NULL) {
- weston_log("fatal: drm backend should be run "
- "using weston-launch binary or as root\n");
- goto err_compositor;
- }
-
- b->udev = udev_new();
- if (b->udev == NULL) {
- weston_log("failed to initialize udev context\n");
- goto err_launcher;
- }
-
- b->session_listener.notify = session_notify;
- wl_signal_add(&compositor->session_signal, &b->session_listener);
-
- drm_device = find_primary_gpu(b, seat_id);
- if (drm_device == NULL) {
- weston_log("no drm device found\n");
- goto err_udev;
- }
- path = udev_device_get_syspath(drm_device);
-
- if (init_drm(b, drm_device) < 0) {
- weston_log("failed to initialize kms\n");
- goto err_udev_dev;
- }
-
- if (b->use_pixman) {
- if (init_pixman(b) < 0) {
- weston_log("failed to initialize pixman renderer\n");
- goto err_udev_dev;
- }
- } else {
- if (init_egl(b) < 0) {
- weston_log("failed to initialize egl\n");
- goto err_udev_dev;
- }
- }
-
- b->base.destroy = drm_destroy;
- b->base.restore = drm_restore;
-
- b->prev_state = WESTON_COMPOSITOR_ACTIVE;
-
- weston_setup_vt_switch_bindings(compositor);
-
- wl_list_init(&b->sprite_list);
- create_sprites(b);
-
- if (udev_input_init(&b->input,
- compositor, b->udev, seat_id,
- config->configure_device) < 0) {
- weston_log("failed to create input devices\n");
- goto err_sprite;
- }
-
- if (create_outputs(b, config->connector, drm_device) < 0) {
- weston_log("failed to create output for %s\n", path);
- goto err_udev_input;
- }
-
- /* A this point we have some idea of whether or not we have a working
- * cursor plane. */
- if (!b->cursors_are_broken)
- compositor->capabilities |= WESTON_CAP_CURSOR_PLANE;
-
- path = NULL;
-
- loop = wl_display_get_event_loop(compositor->wl_display);
- b->drm_source =
- wl_event_loop_add_fd(loop, b->drm.fd,
- WL_EVENT_READABLE, on_drm_input, b);
-
- b->udev_monitor = udev_monitor_new_from_netlink(b->udev, "udev");
- if (b->udev_monitor == NULL) {
- weston_log("failed to intialize udev monitor\n");
- goto err_drm_source;
- }
- udev_monitor_filter_add_match_subsystem_devtype(b->udev_monitor,
- "drm", NULL);
- b->udev_drm_source =
- wl_event_loop_add_fd(loop,
- udev_monitor_get_fd(b->udev_monitor),
- WL_EVENT_READABLE, udev_drm_event, b);
-
- if (udev_monitor_enable_receiving(b->udev_monitor) < 0) {
- weston_log("failed to enable udev-monitor receiving\n");
- goto err_udev_monitor;
- }
-
- udev_device_unref(drm_device);
-
- weston_compositor_add_debug_binding(compositor, KEY_O,
- planes_binding, b);
- weston_compositor_add_debug_binding(compositor, KEY_C,
- planes_binding, b);
- weston_compositor_add_debug_binding(compositor, KEY_V,
- planes_binding, b);
- weston_compositor_add_debug_binding(compositor, KEY_Q,
- recorder_binding, b);
- weston_compositor_add_debug_binding(compositor, KEY_W,
- renderer_switch_binding, b);
-
- if (compositor->renderer->import_dmabuf) {
- if (linux_dmabuf_setup(compositor) < 0)
- weston_log("Error: initializing dmabuf "
- "support failed.\n");
- }
-
- compositor->backend = &b->base;
-
- return b;
-
-err_udev_monitor:
- wl_event_source_remove(b->udev_drm_source);
- udev_monitor_unref(b->udev_monitor);
-err_drm_source:
- wl_event_source_remove(b->drm_source);
-err_udev_input:
- udev_input_destroy(&b->input);
-err_sprite:
- if (b->gbm)
- gbm_device_destroy(b->gbm);
- destroy_sprites(b);
-err_udev_dev:
- udev_device_unref(drm_device);
-err_launcher:
- weston_launcher_destroy(compositor->launcher);
-err_udev:
- udev_unref(b->udev);
-err_compositor:
- weston_compositor_shutdown(compositor);
- free(b);
- return NULL;
-}
-
-static void
-config_init_to_defaults(struct weston_drm_backend_config *config)
-{
-}
-
-WL_EXPORT int
-backend_init(struct weston_compositor *compositor,
- struct weston_backend_config *config_base)
-{
- struct drm_backend *b;
- struct weston_drm_backend_config config = {{ 0, }};
-
- if (config_base == NULL ||
- config_base->struct_version != WESTON_DRM_BACKEND_CONFIG_VERSION ||
- config_base->struct_size > sizeof(struct weston_drm_backend_config)) {
- weston_log("drm backend config structure is invalid\n");
- return -1;
- }
-
- config_init_to_defaults(&config);
- memcpy(&config, config_base, config_base->struct_size);
-
- b = drm_backend_create(compositor, &config);
- if (b == NULL)
- return -1;
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright © 2008-2011 Kristian Høgsberg
- * Copyright © 2011 Intel Corporation
- * Copyright © 2015 Giulio Camuffo
- *
- * 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.
- */
-
-#ifndef WESTON_COMPOSITOR_DRM_H
-#define WESTON_COMPOSITOR_DRM_H
-
-#include "compositor.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define WESTON_DRM_BACKEND_CONFIG_VERSION 1
-
-struct libinput_device;
-
-enum weston_drm_backend_output_mode {
- /** The output is disabled */
- WESTON_DRM_BACKEND_OUTPUT_OFF,
- /** The output will use the current active mode */
- WESTON_DRM_BACKEND_OUTPUT_CURRENT,
- /** The output will use the preferred mode. A modeline can be provided
- * by setting weston_backend_output_config::modeline in the form of
- * "WIDTHxHEIGHT" or in the form of an explicit modeline calculated
- * using e.g. the cvt tool. If a valid modeline is supplied it will be
- * used, if invalid or NULL the preferred available mode will be used. */
- WESTON_DRM_BACKEND_OUTPUT_PREFERRED,
-};
-
-struct weston_drm_backend_output_config {
- struct weston_backend_output_config base;
-
- /** The pixel format to be used by the output. Valid values are:
- * - NULL - The format set at backend creation time will be used;
- * - "xrgb8888";
- * - "rgb565"
- * - "xrgb2101010"
- */
- char *gbm_format;
- /** The seat to be used by the output. Set to NULL to use the
- * default seat. */
- char *seat;
- /** The modeline to be used by the output. Refer to the documentation
- * of WESTON_DRM_BACKEND_OUTPUT_PREFERRED for details. */
- char *modeline;
-};
-
-/** The backend configuration struct.
- *
- * weston_drm_backend_config contains the configuration used by a DRM
- * backend.
- */
-struct weston_drm_backend_config {
- struct weston_backend_config base;
-
- /** The connector id of the output to be initialized.
- *
- * A value of 0 will enable all available outputs.
- */
- int connector;
-
- /** The tty to be used. Set to 0 to use the current tty. */
- int tty;
-
- /** Whether to use the pixman renderer instead of the OpenGL ES renderer. */
- bool use_pixman;
-
- /** The seat to be used for input and output.
- *
- * If NULL the default "seat0" will be used. The backend will
- * take ownership of the seat_id pointer and will free it on
- * backend destruction.
- */
- char *seat_id;
-
- /** The pixel format of the framebuffer to be used.
- *
- * Valid values are:
- * - NULL - The default format ("xrgb8888") will be used;
- * - "xrgb8888";
- * - "rgb565"
- * - "xrgb2101010"
- * The backend will take ownership of the format pointer and will free
- * it on backend destruction.
- */
- char *gbm_format;
-
- /** Callback used to configure the outputs.
- *
- * This function will be called by the backend when a new DRM
- * output needs to be configured.
- */
- enum weston_drm_backend_output_mode
- (*configure_output)(struct weston_compositor *compositor,
- bool use_current_mode,
- const char *name,
- struct weston_drm_backend_output_config *output_config);
-
- /** Callback used to configure input devices.
- *
- * This function will be called by the backend when a new input device
- * needs to be configured.
- * If NULL the device will use the default configuration.
- */
- void (*configure_device)(struct weston_compositor *compositor,
- struct libinput_device *device);
- bool use_current_mode;
-};
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* WESTON_COMPOSITOR_DRM_H */
+++ /dev/null
-/*
- * Copyright © 2008-2011 Kristian Høgsberg
- * Copyright © 2011 Intel Corporation
- * Copyright © 2012 Raspberry Pi Foundation
- * Copyright © 2013 Philip Withnall
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <linux/fb.h>
-#include <linux/input.h>
-
-#include <libudev.h>
-
-#include "shared/helpers.h"
-#include "compositor.h"
-#include "compositor-fbdev.h"
-#include "launcher-util.h"
-#include "pixman-renderer.h"
-#include "libinput-seat.h"
-#include "presentation-time-server-protocol.h"
-
-struct fbdev_backend {
- struct weston_backend base;
- struct weston_compositor *compositor;
- uint32_t prev_state;
-
- struct udev *udev;
- struct udev_input input;
- uint32_t output_transform;
- struct wl_listener session_listener;
-};
-
-struct fbdev_screeninfo {
- unsigned int x_resolution; /* pixels, visible area */
- unsigned int y_resolution; /* pixels, visible area */
- unsigned int width_mm; /* visible screen width in mm */
- unsigned int height_mm; /* visible screen height in mm */
- unsigned int bits_per_pixel;
-
- size_t buffer_length; /* length of frame buffer memory in bytes */
- size_t line_length; /* length of a line in bytes */
- char id[16]; /* screen identifier */
-
- pixman_format_code_t pixel_format; /* frame buffer pixel format */
- unsigned int refresh_rate; /* Hertz */
-};
-
-struct fbdev_output {
- struct fbdev_backend *backend;
- struct weston_output base;
-
- struct weston_mode mode;
- struct wl_event_source *finish_frame_timer;
-
- /* Frame buffer details. */
- char *device;
- struct fbdev_screeninfo fb_info;
- void *fb; /* length is fb_info.buffer_length */
-
- /* pixman details. */
- pixman_image_t *hw_surface;
- uint8_t depth;
-};
-
-static const char default_seat[] = "seat0";
-
-static inline struct fbdev_output *
-to_fbdev_output(struct weston_output *base)
-{
- return container_of(base, struct fbdev_output, base);
-}
-
-static inline struct fbdev_backend *
-to_fbdev_backend(struct weston_compositor *base)
-{
- return container_of(base->backend, struct fbdev_backend, base);
-}
-
-static void
-fbdev_output_start_repaint_loop(struct weston_output *output)
-{
- struct timespec ts;
-
- weston_compositor_read_presentation_clock(output->compositor, &ts);
- weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
-}
-
-static int
-fbdev_output_repaint(struct weston_output *base, pixman_region32_t *damage)
-{
- struct fbdev_output *output = to_fbdev_output(base);
- struct weston_compositor *ec = output->base.compositor;
-
- /* Repaint the damaged region onto the back buffer. */
- pixman_renderer_output_set_buffer(base, output->hw_surface);
- ec->renderer->repaint_output(base, damage);
-
- /* Update the damage region. */
- pixman_region32_subtract(&ec->primary_plane.damage,
- &ec->primary_plane.damage, damage);
-
- /* Schedule the end of the frame. We do not sync this to the frame
- * buffer clock because users who want that should be using the DRM
- * compositor. FBIO_WAITFORVSYNC blocks and FB_ACTIVATE_VBL requires
- * panning, which is broken in most kernel drivers.
- *
- * Finish the frame synchronised to the specified refresh rate. The
- * refresh rate is given in mHz and the interval in ms. */
- wl_event_source_timer_update(output->finish_frame_timer,
- 1000000 / output->mode.refresh);
-
- return 0;
-}
-
-static int
-finish_frame_handler(void *data)
-{
- struct fbdev_output *output = data;
- struct timespec ts;
-
- weston_compositor_read_presentation_clock(output->base.compositor, &ts);
- weston_output_finish_frame(&output->base, &ts, 0);
-
- return 1;
-}
-
-static pixman_format_code_t
-calculate_pixman_format(struct fb_var_screeninfo *vinfo,
- struct fb_fix_screeninfo *finfo)
-{
- /* Calculate the pixman format supported by the frame buffer from the
- * buffer's metadata. Return 0 if no known pixman format is supported
- * (since this has depth 0 it's guaranteed to not conflict with any
- * actual pixman format).
- *
- * Documentation on the vinfo and finfo structures:
- * http://www.mjmwired.net/kernel/Documentation/fb/api.txt
- *
- * TODO: Try a bit harder to support other formats, including setting
- * the preferred format in the hardware. */
- int type;
-
- weston_log("Calculating pixman format from:\n"
- STAMP_SPACE " - type: %i (aux: %i)\n"
- STAMP_SPACE " - visual: %i\n"
- STAMP_SPACE " - bpp: %i (grayscale: %i)\n"
- STAMP_SPACE " - red: offset: %i, length: %i, MSB: %i\n"
- STAMP_SPACE " - green: offset: %i, length: %i, MSB: %i\n"
- STAMP_SPACE " - blue: offset: %i, length: %i, MSB: %i\n"
- STAMP_SPACE " - transp: offset: %i, length: %i, MSB: %i\n",
- finfo->type, finfo->type_aux, finfo->visual,
- vinfo->bits_per_pixel, vinfo->grayscale,
- vinfo->red.offset, vinfo->red.length, vinfo->red.msb_right,
- vinfo->green.offset, vinfo->green.length,
- vinfo->green.msb_right,
- vinfo->blue.offset, vinfo->blue.length,
- vinfo->blue.msb_right,
- vinfo->transp.offset, vinfo->transp.length,
- vinfo->transp.msb_right);
-
- /* We only handle packed formats at the moment. */
- if (finfo->type != FB_TYPE_PACKED_PIXELS)
- return 0;
-
- /* We only handle true-colour frame buffers at the moment. */
- switch(finfo->visual) {
- case FB_VISUAL_TRUECOLOR:
- case FB_VISUAL_DIRECTCOLOR:
- if (vinfo->grayscale != 0)
- return 0;
- break;
- default:
- return 0;
- }
-
- /* We only support formats with MSBs on the left. */
- if (vinfo->red.msb_right != 0 || vinfo->green.msb_right != 0 ||
- vinfo->blue.msb_right != 0)
- return 0;
-
- /* Work out the format type from the offsets. We only support RGBA and
- * ARGB at the moment. */
- type = PIXMAN_TYPE_OTHER;
-
- if ((vinfo->transp.offset >= vinfo->red.offset ||
- vinfo->transp.length == 0) &&
- vinfo->red.offset >= vinfo->green.offset &&
- vinfo->green.offset >= vinfo->blue.offset)
- type = PIXMAN_TYPE_ARGB;
- else if (vinfo->red.offset >= vinfo->green.offset &&
- vinfo->green.offset >= vinfo->blue.offset &&
- vinfo->blue.offset >= vinfo->transp.offset)
- type = PIXMAN_TYPE_RGBA;
-
- if (type == PIXMAN_TYPE_OTHER)
- return 0;
-
- /* Build the format. */
- return PIXMAN_FORMAT(vinfo->bits_per_pixel, type,
- vinfo->transp.length,
- vinfo->red.length,
- vinfo->green.length,
- vinfo->blue.length);
-}
-
-static int
-calculate_refresh_rate(struct fb_var_screeninfo *vinfo)
-{
- uint64_t quot;
-
- /* Calculate monitor refresh rate. Default is 60 Hz. Units are mHz. */
- quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres);
- quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres);
- quot *= vinfo->pixclock;
-
- if (quot > 0) {
- uint64_t refresh_rate;
-
- refresh_rate = 1000000000000000LLU / quot;
- if (refresh_rate > 200000)
- refresh_rate = 200000; /* cap at 200 Hz */
-
- return refresh_rate;
- }
-
- return 60 * 1000; /* default to 60 Hz */
-}
-
-static int
-fbdev_query_screen_info(struct fbdev_output *output, int fd,
- struct fbdev_screeninfo *info)
-{
- struct fb_var_screeninfo varinfo;
- struct fb_fix_screeninfo fixinfo;
-
- /* Probe the device for screen information. */
- if (ioctl(fd, FBIOGET_FSCREENINFO, &fixinfo) < 0 ||
- ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
- return -1;
- }
-
- /* Store the pertinent data. */
- info->x_resolution = varinfo.xres;
- info->y_resolution = varinfo.yres;
- info->width_mm = varinfo.width;
- info->height_mm = varinfo.height;
- info->bits_per_pixel = varinfo.bits_per_pixel;
-
- info->buffer_length = fixinfo.smem_len;
- info->line_length = fixinfo.line_length;
- strncpy(info->id, fixinfo.id, sizeof(info->id));
- info->id[sizeof(info->id)-1] = '\0';
-
- info->pixel_format = calculate_pixman_format(&varinfo, &fixinfo);
- info->refresh_rate = calculate_refresh_rate(&varinfo);
-
- if (info->pixel_format == 0) {
- weston_log("Frame buffer uses an unsupported format.\n");
- return -1;
- }
-
- return 1;
-}
-
-static int
-fbdev_set_screen_info(struct fbdev_output *output, int fd,
- struct fbdev_screeninfo *info)
-{
- struct fb_var_screeninfo varinfo;
-
- /* Grab the current screen information. */
- if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
- return -1;
- }
-
- /* Update the information. */
- varinfo.xres = info->x_resolution;
- varinfo.yres = info->y_resolution;
- varinfo.width = info->width_mm;
- varinfo.height = info->height_mm;
- varinfo.bits_per_pixel = info->bits_per_pixel;
-
- /* Try to set up an ARGB (x8r8g8b8) pixel format. */
- varinfo.grayscale = 0;
- varinfo.transp.offset = 24;
- varinfo.transp.length = 0;
- varinfo.transp.msb_right = 0;
- varinfo.red.offset = 16;
- varinfo.red.length = 8;
- varinfo.red.msb_right = 0;
- varinfo.green.offset = 8;
- varinfo.green.length = 8;
- varinfo.green.msb_right = 0;
- varinfo.blue.offset = 0;
- varinfo.blue.length = 8;
- varinfo.blue.msb_right = 0;
-
- /* Set the device's screen information. */
- if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) {
- return -1;
- }
-
- return 1;
-}
-
-static void fbdev_frame_buffer_destroy(struct fbdev_output *output);
-
-/* Returns an FD for the frame buffer device. */
-static int
-fbdev_frame_buffer_open(struct fbdev_output *output, const char *fb_dev,
- struct fbdev_screeninfo *screen_info)
-{
- int fd = -1;
-
- weston_log("Opening fbdev frame buffer.\n");
-
- /* Open the frame buffer device. */
- fd = open(fb_dev, O_RDWR | O_CLOEXEC);
- if (fd < 0) {
- weston_log("Failed to open frame buffer device ‘%s’: %s\n",
- fb_dev, strerror(errno));
- return -1;
- }
-
- /* Grab the screen info. */
- if (fbdev_query_screen_info(output, fd, screen_info) < 0) {
- weston_log("Failed to get frame buffer info: %s\n",
- strerror(errno));
-
- close(fd);
- return -1;
- }
-
- return fd;
-}
-
-/* Closes the FD on success or failure. */
-static int
-fbdev_frame_buffer_map(struct fbdev_output *output, int fd)
-{
- int retval = -1;
-
- weston_log("Mapping fbdev frame buffer.\n");
-
- /* Map the frame buffer. Write-only mode, since we don't want to read
- * anything back (because it's slow). */
- output->fb = mmap(NULL, output->fb_info.buffer_length,
- PROT_WRITE, MAP_SHARED, fd, 0);
- if (output->fb == MAP_FAILED) {
- weston_log("Failed to mmap frame buffer: %s\n",
- strerror(errno));
- goto out_close;
- }
-
- /* Create a pixman image to wrap the memory mapped frame buffer. */
- output->hw_surface =
- pixman_image_create_bits(output->fb_info.pixel_format,
- output->fb_info.x_resolution,
- output->fb_info.y_resolution,
- output->fb,
- output->fb_info.line_length);
- if (output->hw_surface == NULL) {
- weston_log("Failed to create surface for frame buffer.\n");
- goto out_unmap;
- }
-
- /* Success! */
- retval = 0;
-
-out_unmap:
- if (retval != 0 && output->fb != NULL)
- fbdev_frame_buffer_destroy(output);
-
-out_close:
- if (fd >= 0)
- close(fd);
-
- return retval;
-}
-
-static void
-fbdev_frame_buffer_destroy(struct fbdev_output *output)
-{
- weston_log("Destroying fbdev frame buffer.\n");
-
- if (munmap(output->fb, output->fb_info.buffer_length) < 0)
- weston_log("Failed to munmap frame buffer: %s\n",
- strerror(errno));
-
- output->fb = NULL;
-}
-
-static void fbdev_output_destroy(struct weston_output *base);
-static void fbdev_output_disable(struct weston_output *base);
-
-static int
-fbdev_output_create(struct fbdev_backend *backend,
- const char *device)
-{
- struct fbdev_output *output;
- int fb_fd;
- struct wl_event_loop *loop;
-
- weston_log("Creating fbdev output.\n");
-
- output = zalloc(sizeof *output);
- if (output == NULL)
- return -1;
-
- output->backend = backend;
- output->device = strdup(device);
-
- /* Create the frame buffer. */
- fb_fd = fbdev_frame_buffer_open(output, device, &output->fb_info);
- if (fb_fd < 0) {
- weston_log("Creating frame buffer failed.\n");
- goto out_free;
- }
-
- if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
- weston_log("Mapping frame buffer failed.\n");
- goto out_free;
- }
-
- output->base.start_repaint_loop = fbdev_output_start_repaint_loop;
- output->base.repaint = fbdev_output_repaint;
- output->base.destroy = fbdev_output_destroy;
-
- /* only one static mode in list */
- output->mode.flags =
- WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
- output->mode.width = output->fb_info.x_resolution;
- output->mode.height = output->fb_info.y_resolution;
- output->mode.refresh = output->fb_info.refresh_rate;
- wl_list_init(&output->base.mode_list);
- wl_list_insert(&output->base.mode_list, &output->mode.link);
-
- output->base.current_mode = &output->mode;
- output->base.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
- output->base.make = "unknown";
- output->base.model = output->fb_info.id;
- output->base.name = strdup("fbdev");
-
- weston_output_init(&output->base, backend->compositor,
- 0, 0, output->fb_info.width_mm,
- output->fb_info.height_mm,
- backend->output_transform,
- 1);
-
- if (pixman_renderer_output_create(&output->base) < 0)
- goto out_hw_surface;
-
- loop = wl_display_get_event_loop(backend->compositor->wl_display);
- output->finish_frame_timer =
- wl_event_loop_add_timer(loop, finish_frame_handler, output);
-
- weston_compositor_add_output(backend->compositor, &output->base);
-
- weston_log("fbdev output %d×%d px\n",
- output->mode.width, output->mode.height);
- weston_log_continue(STAMP_SPACE "guessing %d Hz and 96 dpi\n",
- output->mode.refresh / 1000);
-
- return 0;
-
-out_hw_surface:
- pixman_image_unref(output->hw_surface);
- output->hw_surface = NULL;
- weston_output_destroy(&output->base);
- fbdev_frame_buffer_destroy(output);
-out_free:
- free(output->device);
- free(output);
-
- return -1;
-}
-
-static void
-fbdev_output_destroy(struct weston_output *base)
-{
- struct fbdev_output *output = to_fbdev_output(base);
-
- weston_log("Destroying fbdev output.\n");
-
- /* Close the frame buffer. */
- fbdev_output_disable(base);
-
- if (base->renderer_state != NULL)
- pixman_renderer_output_destroy(base);
-
- /* Remove the output. */
- weston_output_destroy(&output->base);
-
- free(output->device);
- free(output);
-}
-
-/* strcmp()-style return values. */
-static int
-compare_screen_info (const struct fbdev_screeninfo *a,
- const struct fbdev_screeninfo *b)
-{
- if (a->x_resolution == b->x_resolution &&
- a->y_resolution == b->y_resolution &&
- a->width_mm == b->width_mm &&
- a->height_mm == b->height_mm &&
- a->bits_per_pixel == b->bits_per_pixel &&
- a->pixel_format == b->pixel_format &&
- a->refresh_rate == b->refresh_rate)
- return 0;
-
- return 1;
-}
-
-static int
-fbdev_output_reenable(struct fbdev_backend *backend,
- struct weston_output *base)
-{
- struct fbdev_output *output = to_fbdev_output(base);
- struct fbdev_screeninfo new_screen_info;
- int fb_fd;
- char *device;
-
- weston_log("Re-enabling fbdev output.\n");
-
- /* Create the frame buffer. */
- fb_fd = fbdev_frame_buffer_open(output, output->device,
- &new_screen_info);
- if (fb_fd < 0) {
- weston_log("Creating frame buffer failed.\n");
- goto err;
- }
-
- /* Check whether the frame buffer details have changed since we were
- * disabled. */
- if (compare_screen_info (&output->fb_info, &new_screen_info) != 0) {
- /* Perform a mode-set to restore the old mode. */
- if (fbdev_set_screen_info(output, fb_fd,
- &output->fb_info) < 0) {
- weston_log("Failed to restore mode settings. "
- "Attempting to re-open output anyway.\n");
- }
-
- close(fb_fd);
-
- /* Remove and re-add the output so that resources depending on
- * the frame buffer X/Y resolution (such as the shadow buffer)
- * are re-initialised. */
- device = strdup(output->device);
- fbdev_output_destroy(&output->base);
- fbdev_output_create(backend, device);
- free(device);
-
- return 0;
- }
-
- /* Map the device if it has the same details as before. */
- if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
- weston_log("Mapping frame buffer failed.\n");
- goto err;
- }
-
- return 0;
-
-err:
- return -1;
-}
-
-/* NOTE: This leaves output->fb_info populated, caching data so that if
- * fbdev_output_reenable() is called again, it can determine whether a mode-set
- * is needed. */
-static void
-fbdev_output_disable(struct weston_output *base)
-{
- struct fbdev_output *output = to_fbdev_output(base);
-
- weston_log("Disabling fbdev output.\n");
-
- if (output->hw_surface != NULL) {
- pixman_image_unref(output->hw_surface);
- output->hw_surface = NULL;
- }
-
- fbdev_frame_buffer_destroy(output);
-}
-
-static void
-fbdev_backend_destroy(struct weston_compositor *base)
-{
- struct fbdev_backend *backend = to_fbdev_backend(base);
-
- udev_input_destroy(&backend->input);
-
- /* Destroy the output. */
- weston_compositor_shutdown(base);
-
- /* Chain up. */
- weston_launcher_destroy(base->launcher);
-
- free(backend);
-}
-
-static void
-session_notify(struct wl_listener *listener, void *data)
-{
- struct weston_compositor *compositor = data;
- struct fbdev_backend *backend = to_fbdev_backend(compositor);
- struct weston_output *output;
-
- if (compositor->session_active) {
- weston_log("entering VT\n");
- compositor->state = backend->prev_state;
-
- wl_list_for_each(output, &compositor->output_list, link) {
- fbdev_output_reenable(backend, output);
- }
-
- weston_compositor_damage_all(compositor);
-
- udev_input_enable(&backend->input);
- } else {
- weston_log("leaving VT\n");
- udev_input_disable(&backend->input);
-
- wl_list_for_each(output, &compositor->output_list, link) {
- fbdev_output_disable(output);
- }
-
- backend->prev_state = compositor->state;
- weston_compositor_offscreen(compositor);
-
- /* If we have a repaint scheduled (from the idle handler), make
- * sure we cancel that so we don't try to pageflip when we're
- * vt switched away. The OFFSCREEN state will prevent
- * further attempts at repainting. When we switch
- * back, we schedule a repaint, which will process
- * pending frame callbacks. */
-
- wl_list_for_each(output,
- &compositor->output_list, link) {
- output->repaint_needed = 0;
- }
- }
-}
-
-static void
-fbdev_restore(struct weston_compositor *compositor)
-{
- weston_launcher_restore(compositor->launcher);
-}
-
-static struct fbdev_backend *
-fbdev_backend_create(struct weston_compositor *compositor,
- struct weston_fbdev_backend_config *param)
-{
- struct fbdev_backend *backend;
- const char *seat_id = default_seat;
-
- weston_log("initializing fbdev backend\n");
-
- backend = zalloc(sizeof *backend);
- if (backend == NULL)
- return NULL;
-
- backend->compositor = compositor;
- if (weston_compositor_set_presentation_clock_software(
- compositor) < 0)
- goto out_compositor;
-
- backend->udev = udev_new();
- if (backend->udev == NULL) {
- weston_log("Failed to initialize udev context.\n");
- goto out_compositor;
- }
-
- /* Set up the TTY. */
- backend->session_listener.notify = session_notify;
- wl_signal_add(&compositor->session_signal,
- &backend->session_listener);
- compositor->launcher =
- weston_launcher_connect(compositor, param->tty, "seat0", false);
- if (!compositor->launcher) {
- weston_log("fatal: fbdev backend should be run "
- "using weston-launch binary or as root\n");
- goto out_udev;
- }
-
- backend->base.destroy = fbdev_backend_destroy;
- backend->base.restore = fbdev_restore;
-
- backend->prev_state = WESTON_COMPOSITOR_ACTIVE;
- backend->output_transform = param->output_transform;
-
- weston_setup_vt_switch_bindings(compositor);
-
- if (pixman_renderer_init(compositor) < 0)
- goto out_launcher;
-
- if (fbdev_output_create(backend, param->device) < 0)
- goto out_launcher;
-
- udev_input_init(&backend->input, compositor, backend->udev,
- seat_id, param->configure_device);
-
- compositor->backend = &backend->base;
- return backend;
-
-out_launcher:
- weston_launcher_destroy(compositor->launcher);
-
-out_udev:
- udev_unref(backend->udev);
-
-out_compositor:
- weston_compositor_shutdown(compositor);
- free(backend);
-
- return NULL;
-}
-
-static void
-config_init_to_defaults(struct weston_fbdev_backend_config *config)
-{
- /* TODO: Ideally, available frame buffers should be enumerated using
- * udev, rather than passing a device node in as a parameter. */
- config->tty = 0; /* default to current tty */
- config->device = "/dev/fb0"; /* default frame buffer */
- config->output_transform = WL_OUTPUT_TRANSFORM_NORMAL;
-}
-
-WL_EXPORT int
-backend_init(struct weston_compositor *compositor,
- struct weston_backend_config *config_base)
-{
- struct fbdev_backend *b;
- struct weston_fbdev_backend_config config = {{ 0, }};
-
- if (config_base == NULL ||
- config_base->struct_version != WESTON_FBDEV_BACKEND_CONFIG_VERSION ||
- config_base->struct_size > sizeof(struct weston_fbdev_backend_config)) {
- weston_log("fbdev backend config structure is invalid\n");
- return -1;
- }
-
- config_init_to_defaults(&config);
- memcpy(&config, config_base, config_base->struct_size);
-
- b = fbdev_backend_create(compositor, &config);
- if (b == NULL)
- return -1;
- return 0;
-}
+++ /dev/null
-/*
- * Copyright © 2016 Benoit Gschwind
- *
- * 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.
- */
-
-#ifndef WESTON_COMPOSITOR_FBDEV_H
-#define WESTON_COMPOSITOR_FBDEV_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "compositor.h"
-
-#define WESTON_FBDEV_BACKEND_CONFIG_VERSION 1
-
-struct libinput_device;
-
-struct weston_fbdev_backend_config {
- struct weston_backend_config base;
-
- int tty;
- char *device;
-
- uint32_t output_transform;
-
- /** Callback used to configure input devices.
- *
- * This function will be called by the backend when a new input device
- * needs to be configured.
- * If NULL the device will use the default configuration.
- */
- void (*configure_device)(struct weston_compositor *compositor,
- struct libinput_device *device);
-};
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* WESTON_COMPOSITOR_FBDEV_H */
+++ /dev/null
-/*
- * Copyright © 2010-2011 Benjamin Franzke
- * Copyright © 2012 Intel Corporation
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-#include <stdbool.h>
-
-#include "compositor.h"
-#include "compositor-headless.h"
-#include "shared/helpers.h"
-#include "pixman-renderer.h"
-#include "presentation-time-server-protocol.h"
-
-struct headless_backend {
- struct weston_backend base;
- struct weston_compositor *compositor;
-
- struct weston_seat fake_seat;
- bool use_pixman;
-};
-
-struct headless_output {
- struct weston_output base;
-
- struct weston_mode mode;
- struct wl_event_source *finish_frame_timer;
- uint32_t *image_buf;
- pixman_image_t *image;
-};
-
-static void
-headless_output_start_repaint_loop(struct weston_output *output)
-{
- struct timespec ts;
-
- weston_compositor_read_presentation_clock(output->compositor, &ts);
- weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
-}
-
-static int
-finish_frame_handler(void *data)
-{
- struct headless_output *output = data;
- struct timespec ts;
-
- weston_compositor_read_presentation_clock(output->base.compositor, &ts);
- weston_output_finish_frame(&output->base, &ts, 0);
-
- return 1;
-}
-
-static int
-headless_output_repaint(struct weston_output *output_base,
- pixman_region32_t *damage)
-{
- struct headless_output *output = (struct headless_output *) output_base;
- struct weston_compositor *ec = output->base.compositor;
-
- ec->renderer->repaint_output(&output->base, damage);
-
- pixman_region32_subtract(&ec->primary_plane.damage,
- &ec->primary_plane.damage, damage);
-
- wl_event_source_timer_update(output->finish_frame_timer, 16);
-
- return 0;
-}
-
-static void
-headless_output_destroy(struct weston_output *output_base)
-{
- struct headless_output *output = (struct headless_output *) output_base;
- struct headless_backend *b =
- (struct headless_backend *) output->base.compositor->backend;
-
- wl_event_source_remove(output->finish_frame_timer);
-
- if (b->use_pixman) {
- pixman_renderer_output_destroy(&output->base);
- pixman_image_unref(output->image);
- free(output->image_buf);
- }
-
- weston_output_destroy(&output->base);
-
- free(output);
-
- return;
-}
-
-static int
-headless_backend_create_output(struct headless_backend *b,
- struct weston_headless_backend_config *config)
-{
- struct weston_compositor *c = b->compositor;
- struct headless_output *output;
- struct wl_event_loop *loop;
-
- output = zalloc(sizeof *output);
- if (output == NULL)
- return -1;
-
- output->mode.flags =
- WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
- output->mode.width = config->width;
- output->mode.height = config->height;
- output->mode.refresh = 60000;
- wl_list_init(&output->base.mode_list);
- wl_list_insert(&output->base.mode_list, &output->mode.link);
-
- output->base.current_mode = &output->mode;
- weston_output_init(&output->base, c, 0, 0, config->width,
- config->height, config->transform, 1);
-
- output->base.make = "weston";
- output->base.model = "headless";
-
- loop = wl_display_get_event_loop(c->wl_display);
- output->finish_frame_timer =
- wl_event_loop_add_timer(loop, finish_frame_handler, output);
-
- output->base.start_repaint_loop = headless_output_start_repaint_loop;
- output->base.repaint = headless_output_repaint;
- output->base.destroy = headless_output_destroy;
- output->base.assign_planes = NULL;
- output->base.set_backlight = NULL;
- output->base.set_dpms = NULL;
- output->base.switch_mode = NULL;
-
- if (b->use_pixman) {
- output->image_buf = malloc(config->width * config->height * 4);
- if (!output->image_buf)
- return -1;
-
- output->image = pixman_image_create_bits(PIXMAN_x8r8g8b8,
- config->width,
- config->height,
- output->image_buf,
- config->width * 4);
-
- if (pixman_renderer_output_create(&output->base) < 0)
- return -1;
-
- pixman_renderer_output_set_buffer(&output->base,
- output->image);
- }
-
- weston_compositor_add_output(c, &output->base);
-
- return 0;
-}
-
-static void
-headless_restore(struct weston_compositor *ec)
-{
-}
-
-static void
-headless_destroy(struct weston_compositor *ec)
-{
- struct headless_backend *b = (struct headless_backend *) ec->backend;
-
- weston_compositor_shutdown(ec);
-
- free(b);
-}
-
-static struct headless_backend *
-headless_backend_create(struct weston_compositor *compositor,
- struct weston_headless_backend_config *config)
-{
- struct headless_backend *b;
-
- b = zalloc(sizeof *b);
- if (b == NULL)
- return NULL;
-
- b->compositor = compositor;
- if (weston_compositor_set_presentation_clock_software(compositor) < 0)
- goto err_free;
-
- b->base.destroy = headless_destroy;
- b->base.restore = headless_restore;
-
- b->use_pixman = config->use_pixman;
- if (b->use_pixman) {
- pixman_renderer_init(compositor);
- }
- if (headless_backend_create_output(b, config) < 0)
- goto err_input;
-
- if (!b->use_pixman && noop_renderer_init(compositor) < 0)
- goto err_input;
-
- compositor->backend = &b->base;
- return b;
-
-err_input:
- weston_compositor_shutdown(compositor);
-err_free:
- free(b);
- return NULL;
-}
-
-static void
-config_init_to_defaults(struct weston_headless_backend_config *config)
-{
-}
-
-WL_EXPORT int
-backend_init(struct weston_compositor *compositor,
- struct weston_backend_config *config_base)
-{
- struct headless_backend *b;
- struct weston_headless_backend_config config = {{ 0, }};
-
- if (config_base == NULL ||
- config_base->struct_version != WESTON_HEADLESS_BACKEND_CONFIG_VERSION ||
- config_base->struct_size > sizeof(struct weston_headless_backend_config)) {
- weston_log("headless backend config structure is invalid\n");
- return -1;
- }
-
- config_init_to_defaults(&config);
- memcpy(&config, config_base, config_base->struct_size);
-
- b = headless_backend_create(compositor, &config);
- if (b == NULL)
- return -1;
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright © 2016 Benoit Gschwind
- *
- * 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.
- */
-
-#ifndef WESTON_COMPOSITOR_HEADLESS_H
-#define WESTON_COMPOSITOR_HEADLESS_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "compositor.h"
-
-#define WESTON_HEADLESS_BACKEND_CONFIG_VERSION 1
-
-struct weston_headless_backend_config {
- struct weston_backend_config base;
-
- int width;
- int height;
-
- /** Whether to use the pixman renderer instead of the OpenGL ES renderer. */
- int use_pixman;
-
- uint32_t transform;
-};
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* WESTON_COMPOSITOR_HEADLESS_H */
+++ /dev/null
-/*
- * Copyright © 2013 Hardening <rdp.effort@gmail.com>
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <linux/input.h>
-
-#if HAVE_FREERDP_VERSION_H
-#include <freerdp/version.h>
-#else
-/* assume it's a early 1.1 version */
-#define FREERDP_VERSION_MAJOR 1
-#define FREERDP_VERSION_MINOR 1
-#define FREERDP_VERSION_REVISION 0
-#endif
-
-#define FREERDP_VERSION_NUMBER ((FREERDP_VERSION_MAJOR * 0x10000) + \
- (FREERDP_VERSION_MINOR * 0x100) + FREERDP_VERSION_REVISION)
-
-
-#if FREERDP_VERSION_NUMBER >= 0x10201
-#define HAVE_SKIP_COMPRESSION
-#endif
-
-#if FREERDP_VERSION_NUMBER < 0x10202
-# define FREERDP_CB_RET_TYPE void
-# define FREERDP_CB_RETURN(V) return
-# define NSC_RESET(C, W, H)
-# define RFX_RESET(C, W, H) do { rfx_context_reset(C); C->width = W; C->height = H; } while(0)
-#else
-#if FREERDP_VERSION_MAJOR >= 2
-# define NSC_RESET(C, W, H) nsc_context_reset(C, W, H)
-# define RFX_RESET(C, W, H) rfx_context_reset(C, W, H)
-#else
-# define NSC_RESET(C, W, H) do { nsc_context_reset(C); C->width = W; C->height = H; } while(0)
-# define RFX_RESET(C, W, H) do { rfx_context_reset(C); C->width = W; C->height = H; } while(0)
-#endif
-#define FREERDP_CB_RET_TYPE BOOL
-#define FREERDP_CB_RETURN(V) return TRUE
-#endif
-
-#include <freerdp/freerdp.h>
-#include <freerdp/listener.h>
-#include <freerdp/update.h>
-#include <freerdp/input.h>
-#include <freerdp/codec/color.h>
-#include <freerdp/codec/rfx.h>
-#include <freerdp/codec/nsc.h>
-#include <freerdp/locale/keyboard.h>
-#include <winpr/input.h>
-
-#include "shared/helpers.h"
-#include "compositor.h"
-#include "compositor-rdp.h"
-#include "pixman-renderer.h"
-
-#define MAX_FREERDP_FDS 32
-#define DEFAULT_AXIS_STEP_DISTANCE 10
-#define RDP_MODE_FREQ 60 * 1000
-
-
-struct rdp_output;
-
-struct rdp_backend {
- struct weston_backend base;
- struct weston_compositor *compositor;
-
- freerdp_listener *listener;
- struct wl_event_source *listener_events[MAX_FREERDP_FDS];
- struct rdp_output *output;
-
- char *server_cert;
- char *server_key;
- char *rdp_key;
- int tls_enabled;
- int no_clients_resize;
-};
-
-enum peer_item_flags {
- RDP_PEER_ACTIVATED = (1 << 0),
- RDP_PEER_OUTPUT_ENABLED = (1 << 1),
-};
-
-struct rdp_peers_item {
- int flags;
- freerdp_peer *peer;
- struct weston_seat *seat;
-
- struct wl_list link;
-};
-
-struct rdp_output {
- struct weston_output base;
- struct wl_event_source *finish_frame_timer;
- pixman_image_t *shadow_surface;
-
- struct wl_list peers;
-};
-
-struct rdp_peer_context {
- rdpContext _p;
-
- struct rdp_backend *rdpBackend;
- struct wl_event_source *events[MAX_FREERDP_FDS];
- RFX_CONTEXT *rfx_context;
- wStream *encode_stream;
- RFX_RECT *rfx_rects;
- NSC_CONTEXT *nsc_context;
-
- struct rdp_peers_item item;
-};
-typedef struct rdp_peer_context RdpPeerContext;
-
-static void
-rdp_peer_refresh_rfx(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer)
-{
- int width, height, nrects, i;
- pixman_box32_t *region, *rects;
- uint32_t *ptr;
- RFX_RECT *rfxRect;
- rdpUpdate *update = peer->update;
- SURFACE_BITS_COMMAND *cmd = &update->surface_bits_command;
- RdpPeerContext *context = (RdpPeerContext *)peer->context;
-
- Stream_Clear(context->encode_stream);
- Stream_SetPosition(context->encode_stream, 0);
-
- width = (damage->extents.x2 - damage->extents.x1);
- height = (damage->extents.y2 - damage->extents.y1);
-
-#ifdef HAVE_SKIP_COMPRESSION
- cmd->skipCompression = TRUE;
-#else
- memset(cmd, 0, sizeof(*cmd));
-#endif
- cmd->destLeft = damage->extents.x1;
- cmd->destTop = damage->extents.y1;
- cmd->destRight = damage->extents.x2;
- cmd->destBottom = damage->extents.y2;
- cmd->bpp = 32;
- cmd->codecID = peer->settings->RemoteFxCodecId;
- cmd->width = width;
- cmd->height = height;
-
- ptr = pixman_image_get_data(image) + damage->extents.x1 +
- damage->extents.y1 * (pixman_image_get_stride(image) / sizeof(uint32_t));
-
- rects = pixman_region32_rectangles(damage, &nrects);
- context->rfx_rects = realloc(context->rfx_rects, nrects * sizeof *rfxRect);
-
- for (i = 0; i < nrects; i++) {
- region = &rects[i];
- rfxRect = &context->rfx_rects[i];
-
- rfxRect->x = (region->x1 - damage->extents.x1);
- rfxRect->y = (region->y1 - damage->extents.y1);
- rfxRect->width = (region->x2 - region->x1);
- rfxRect->height = (region->y2 - region->y1);
- }
-
- rfx_compose_message(context->rfx_context, context->encode_stream, context->rfx_rects, nrects,
- (BYTE *)ptr, width, height,
- pixman_image_get_stride(image)
- );
-
- cmd->bitmapDataLength = Stream_GetPosition(context->encode_stream);
- cmd->bitmapData = Stream_Buffer(context->encode_stream);
-
- update->SurfaceBits(update->context, cmd);
-}
-
-
-static void
-rdp_peer_refresh_nsc(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer)
-{
- int width, height;
- uint32_t *ptr;
- rdpUpdate *update = peer->update;
- SURFACE_BITS_COMMAND *cmd = &update->surface_bits_command;
- RdpPeerContext *context = (RdpPeerContext *)peer->context;
-
- Stream_Clear(context->encode_stream);
- Stream_SetPosition(context->encode_stream, 0);
-
- width = (damage->extents.x2 - damage->extents.x1);
- height = (damage->extents.y2 - damage->extents.y1);
-
-#ifdef HAVE_SKIP_COMPRESSION
- cmd->skipCompression = TRUE;
-#else
- memset(cmd, 0, sizeof(*cmd));
-#endif
- cmd->destLeft = damage->extents.x1;
- cmd->destTop = damage->extents.y1;
- cmd->destRight = damage->extents.x2;
- cmd->destBottom = damage->extents.y2;
- cmd->bpp = 32;
- cmd->codecID = peer->settings->NSCodecId;
- cmd->width = width;
- cmd->height = height;
-
- ptr = pixman_image_get_data(image) + damage->extents.x1 +
- damage->extents.y1 * (pixman_image_get_stride(image) / sizeof(uint32_t));
-
- nsc_compose_message(context->nsc_context, context->encode_stream, (BYTE *)ptr,
- cmd->width, cmd->height,
- pixman_image_get_stride(image));
- cmd->bitmapDataLength = Stream_GetPosition(context->encode_stream);
- cmd->bitmapData = Stream_Buffer(context->encode_stream);
- update->SurfaceBits(update->context, cmd);
-}
-
-static void
-pixman_image_flipped_subrect(const pixman_box32_t *rect, pixman_image_t *img, BYTE *dest)
-{
- int stride = pixman_image_get_stride(img);
- int h;
- int toCopy = (rect->x2 - rect->x1) * 4;
- int height = (rect->y2 - rect->y1);
- const BYTE *src = (const BYTE *)pixman_image_get_data(img);
- src += ((rect->y2-1) * stride) + (rect->x1 * 4);
-
- for (h = 0; h < height; h++, src -= stride, dest += toCopy)
- memcpy(dest, src, toCopy);
-}
-
-static void
-rdp_peer_refresh_raw(pixman_region32_t *region, pixman_image_t *image, freerdp_peer *peer)
-{
- rdpUpdate *update = peer->update;
- SURFACE_BITS_COMMAND *cmd = &update->surface_bits_command;
- SURFACE_FRAME_MARKER *marker = &update->surface_frame_marker;
- pixman_box32_t *rect, subrect;
- int nrects, i;
- int heightIncrement, remainingHeight, top;
-
- rect = pixman_region32_rectangles(region, &nrects);
- if (!nrects)
- return;
-
- marker->frameId++;
- marker->frameAction = SURFACECMD_FRAMEACTION_BEGIN;
- update->SurfaceFrameMarker(peer->context, marker);
-
- memset(cmd, 0, sizeof(*cmd));
- cmd->bpp = 32;
- cmd->codecID = 0;
-
- for (i = 0; i < nrects; i++, rect++) {
- /*weston_log("rect(%d,%d, %d,%d)\n", rect->x1, rect->y1, rect->x2, rect->y2);*/
- cmd->destLeft = rect->x1;
- cmd->destRight = rect->x2;
- cmd->width = rect->x2 - rect->x1;
-
- heightIncrement = peer->settings->MultifragMaxRequestSize / (16 + cmd->width * 4);
- remainingHeight = rect->y2 - rect->y1;
- top = rect->y1;
-
- subrect.x1 = rect->x1;
- subrect.x2 = rect->x2;
-
- while (remainingHeight) {
- cmd->height = (remainingHeight > heightIncrement) ? heightIncrement : remainingHeight;
- cmd->destTop = top;
- cmd->destBottom = top + cmd->height;
- cmd->bitmapDataLength = cmd->width * cmd->height * 4;
- cmd->bitmapData = (BYTE *)realloc(cmd->bitmapData, cmd->bitmapDataLength);
-
- subrect.y1 = top;
- subrect.y2 = top + cmd->height;
- pixman_image_flipped_subrect(&subrect, image, cmd->bitmapData);
-
- /*weston_log("* sending (%d,%d, %d,%d)\n", subrect.x1, subrect.y1, subrect.x2, subrect.y2); */
- update->SurfaceBits(peer->context, cmd);
-
- remainingHeight -= cmd->height;
- top += cmd->height;
- }
- }
-
- marker->frameAction = SURFACECMD_FRAMEACTION_END;
- update->SurfaceFrameMarker(peer->context, marker);
-}
-
-static void
-rdp_peer_refresh_region(pixman_region32_t *region, freerdp_peer *peer)
-{
- RdpPeerContext *context = (RdpPeerContext *)peer->context;
- struct rdp_output *output = context->rdpBackend->output;
- rdpSettings *settings = peer->settings;
-
- if (settings->RemoteFxCodec)
- rdp_peer_refresh_rfx(region, output->shadow_surface, peer);
- else if (settings->NSCodec)
- rdp_peer_refresh_nsc(region, output->shadow_surface, peer);
- else
- rdp_peer_refresh_raw(region, output->shadow_surface, peer);
-}
-
-static void
-rdp_output_start_repaint_loop(struct weston_output *output)
-{
- struct timespec ts;
-
- weston_compositor_read_presentation_clock(output->compositor, &ts);
- weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
-}
-
-static int
-rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage)
-{
- struct rdp_output *output = container_of(output_base, struct rdp_output, base);
- struct weston_compositor *ec = output->base.compositor;
- struct rdp_peers_item *outputPeer;
-
- pixman_renderer_output_set_buffer(output_base, output->shadow_surface);
- ec->renderer->repaint_output(&output->base, damage);
-
- if (pixman_region32_not_empty(damage)) {
- wl_list_for_each(outputPeer, &output->peers, link) {
- if ((outputPeer->flags & RDP_PEER_ACTIVATED) &&
- (outputPeer->flags & RDP_PEER_OUTPUT_ENABLED))
- {
- rdp_peer_refresh_region(damage, outputPeer->peer);
- }
- }
- }
-
- pixman_region32_subtract(&ec->primary_plane.damage,
- &ec->primary_plane.damage, damage);
-
- wl_event_source_timer_update(output->finish_frame_timer, 16);
- return 0;
-}
-
-static void
-rdp_output_destroy(struct weston_output *output_base)
-{
- struct rdp_output *output = (struct rdp_output *)output_base;
-
- wl_event_source_remove(output->finish_frame_timer);
- free(output);
-}
-
-static int
-finish_frame_handler(void *data)
-{
- struct rdp_output *output = data;
- struct timespec ts;
-
- weston_compositor_read_presentation_clock(output->base.compositor, &ts);
- weston_output_finish_frame(&output->base, &ts, 0);
-
- return 1;
-}
-
-static struct weston_mode *
-rdp_insert_new_mode(struct weston_output *output, int width, int height, int rate)
-{
- struct weston_mode *ret;
- ret = zalloc(sizeof *ret);
- if (!ret)
- return NULL;
- ret->width = width;
- ret->height = height;
- ret->refresh = rate;
- wl_list_insert(&output->mode_list, &ret->link);
- return ret;
-}
-
-static struct weston_mode *
-ensure_matching_mode(struct weston_output *output, struct weston_mode *target)
-{
- struct weston_mode *local;
-
- wl_list_for_each(local, &output->mode_list, link) {
- if ((local->width == target->width) && (local->height == target->height))
- return local;
- }
-
- return rdp_insert_new_mode(output, target->width, target->height, RDP_MODE_FREQ);
-}
-
-static int
-rdp_switch_mode(struct weston_output *output, struct weston_mode *target_mode)
-{
- struct rdp_output *rdpOutput = container_of(output, struct rdp_output, base);
- struct rdp_peers_item *rdpPeer;
- rdpSettings *settings;
- pixman_image_t *new_shadow_buffer;
- struct weston_mode *local_mode;
-
- local_mode = ensure_matching_mode(output, target_mode);
- if (!local_mode) {
- weston_log("mode %dx%d not available\n", target_mode->width, target_mode->height);
- return -ENOENT;
- }
-
- if (local_mode == output->current_mode)
- return 0;
-
- output->current_mode->flags &= ~WL_OUTPUT_MODE_CURRENT;
-
- output->current_mode = local_mode;
- output->current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
-
- pixman_renderer_output_destroy(output);
- pixman_renderer_output_create(output);
-
- new_shadow_buffer = pixman_image_create_bits(PIXMAN_x8r8g8b8, target_mode->width,
- target_mode->height, 0, target_mode->width * 4);
- pixman_image_composite32(PIXMAN_OP_SRC, rdpOutput->shadow_surface, 0, new_shadow_buffer,
- 0, 0, 0, 0, 0, 0, target_mode->width, target_mode->height);
- pixman_image_unref(rdpOutput->shadow_surface);
- rdpOutput->shadow_surface = new_shadow_buffer;
-
- wl_list_for_each(rdpPeer, &rdpOutput->peers, link) {
- settings = rdpPeer->peer->settings;
- if (settings->DesktopWidth == (UINT32)target_mode->width &&
- settings->DesktopHeight == (UINT32)target_mode->height)
- continue;
-
- if (!settings->DesktopResize) {
- /* too bad this peer does not support desktop resize */
- rdpPeer->peer->Close(rdpPeer->peer);
- } else {
- settings->DesktopWidth = target_mode->width;
- settings->DesktopHeight = target_mode->height;
- rdpPeer->peer->update->DesktopResize(rdpPeer->peer->context);
- }
- }
- return 0;
-}
-
-static int
-rdp_backend_create_output(struct rdp_backend *b, int width, int height)
-{
- struct rdp_output *output;
- struct wl_event_loop *loop;
- struct weston_mode *currentMode;
- struct weston_mode initMode;
-
- output = zalloc(sizeof *output);
- if (output == NULL)
- return -1;
-
- wl_list_init(&output->peers);
- wl_list_init(&output->base.mode_list);
-
- initMode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
- initMode.width = width;
- initMode.height = height;
- initMode.refresh = RDP_MODE_FREQ;
-
- currentMode = ensure_matching_mode(&output->base, &initMode);
- if (!currentMode)
- goto out_free_output;
-
- output->base.current_mode = output->base.native_mode = currentMode;
- weston_output_init(&output->base, b->compositor, 0, 0, width, height,
- WL_OUTPUT_TRANSFORM_NORMAL, 1);
-
- output->base.make = "weston";
- output->base.model = "rdp";
- output->shadow_surface = pixman_image_create_bits(PIXMAN_x8r8g8b8,
- width, height,
- NULL,
- width * 4);
- if (output->shadow_surface == NULL) {
- weston_log("Failed to create surface for frame buffer.\n");
- goto out_output;
- }
-
- if (pixman_renderer_output_create(&output->base) < 0)
- goto out_shadow_surface;
-
- loop = wl_display_get_event_loop(b->compositor->wl_display);
- output->finish_frame_timer = wl_event_loop_add_timer(loop, finish_frame_handler, output);
-
- output->base.start_repaint_loop = rdp_output_start_repaint_loop;
- output->base.repaint = rdp_output_repaint;
- output->base.destroy = rdp_output_destroy;
- output->base.assign_planes = NULL;
- output->base.set_backlight = NULL;
- output->base.set_dpms = NULL;
- output->base.switch_mode = rdp_switch_mode;
- b->output = output;
-
- weston_compositor_add_output(b->compositor, &output->base);
- return 0;
-
-out_shadow_surface:
- pixman_image_unref(output->shadow_surface);
-out_output:
- weston_output_destroy(&output->base);
-out_free_output:
- free(output);
- return -1;
-}
-
-static void
-rdp_restore(struct weston_compositor *ec)
-{
-}
-
-static void
-rdp_destroy(struct weston_compositor *ec)
-{
- struct rdp_backend *b = (struct rdp_backend *) ec->backend;
- int i;
-
- weston_compositor_shutdown(ec);
- for (i = 0; i < MAX_FREERDP_FDS; i++)
- if (b->listener_events[i])
- wl_event_source_remove(b->listener_events[i]);
-
- freerdp_listener_free(b->listener);
-
- free(b->server_cert);
- free(b->server_key);
- free(b->rdp_key);
- free(b);
-}
-
-static
-int rdp_listener_activity(int fd, uint32_t mask, void *data)
-{
- freerdp_listener* instance = (freerdp_listener *)data;
-
- if (!(mask & WL_EVENT_READABLE))
- return 0;
- if (!instance->CheckFileDescriptor(instance)) {
- weston_log("failed to check FreeRDP file descriptor\n");
- return -1;
- }
- return 0;
-}
-
-static
-int rdp_implant_listener(struct rdp_backend *b, freerdp_listener* instance)
-{
- int i, fd;
- int rcount = 0;
- void* rfds[MAX_FREERDP_FDS];
- struct wl_event_loop *loop;
-
- if (!instance->GetFileDescriptor(instance, rfds, &rcount)) {
- weston_log("Failed to get FreeRDP file descriptor\n");
- return -1;
- }
-
- loop = wl_display_get_event_loop(b->compositor->wl_display);
- for (i = 0; i < rcount; i++) {
- fd = (int)(long)(rfds[i]);
- b->listener_events[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
- rdp_listener_activity, instance);
- }
-
- for ( ; i < MAX_FREERDP_FDS; i++)
- b->listener_events[i] = 0;
- return 0;
-}
-
-
-static FREERDP_CB_RET_TYPE
-rdp_peer_context_new(freerdp_peer* client, RdpPeerContext* context)
-{
- context->item.peer = client;
- context->item.flags = RDP_PEER_OUTPUT_ENABLED;
-
-#if FREERDP_VERSION_MAJOR == 1 && FREERDP_VERSION_MINOR == 1
- context->rfx_context = rfx_context_new();
-#else
- context->rfx_context = rfx_context_new(TRUE);
-#endif
- if (!context->rfx_context) {
- FREERDP_CB_RETURN(FALSE);
- }
-
- context->rfx_context->mode = RLGR3;
- context->rfx_context->width = client->settings->DesktopWidth;
- context->rfx_context->height = client->settings->DesktopHeight;
- rfx_context_set_pixel_format(context->rfx_context, RDP_PIXEL_FORMAT_B8G8R8A8);
-
- context->nsc_context = nsc_context_new();
- if (!context->nsc_context)
- goto out_error_nsc;
-
- nsc_context_set_pixel_format(context->nsc_context, RDP_PIXEL_FORMAT_B8G8R8A8);
-
- context->encode_stream = Stream_New(NULL, 65536);
- if (!context->encode_stream)
- goto out_error_stream;
-
- FREERDP_CB_RETURN(TRUE);
-
-out_error_nsc:
- rfx_context_free(context->rfx_context);
-out_error_stream:
- nsc_context_free(context->nsc_context);
- FREERDP_CB_RETURN(FALSE);
-}
-
-static void
-rdp_peer_context_free(freerdp_peer* client, RdpPeerContext* context)
-{
- int i;
- if (!context)
- return;
-
- wl_list_remove(&context->item.link);
- for (i = 0; i < MAX_FREERDP_FDS; i++) {
- if (context->events[i])
- wl_event_source_remove(context->events[i]);
- }
-
- if (context->item.flags & RDP_PEER_ACTIVATED) {
- weston_seat_release_keyboard(context->item.seat);
- weston_seat_release_pointer(context->item.seat);
- /* XXX we should weston_seat_release(context->item.seat); here
- * but it would crash on reconnect */
- }
-
- Stream_Free(context->encode_stream, TRUE);
- nsc_context_free(context->nsc_context);
- rfx_context_free(context->rfx_context);
- free(context->rfx_rects);
-}
-
-
-static int
-rdp_client_activity(int fd, uint32_t mask, void *data)
-{
- freerdp_peer* client = (freerdp_peer *)data;
-
- if (!client->CheckFileDescriptor(client)) {
- weston_log("unable to checkDescriptor for %p\n", client);
- goto out_clean;
- }
- return 0;
-
-out_clean:
- freerdp_peer_context_free(client);
- freerdp_peer_free(client);
- return 0;
-}
-
-static BOOL
-xf_peer_capabilities(freerdp_peer* client)
-{
- return TRUE;
-}
-
-struct rdp_to_xkb_keyboard_layout {
- UINT32 rdpLayoutCode;
- const char *xkbLayout;
- const char *xkbVariant;
-};
-
-/* table reversed from
- https://github.com/awakecoding/FreeRDP/blob/master/libfreerdp/locale/xkb_layout_ids.c#L811 */
-static
-struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = {
- {KBD_ARABIC_101, "ara", 0},
- {KBD_BULGARIAN, 0, 0},
- {KBD_CHINESE_TRADITIONAL_US, 0},
- {KBD_CZECH, "cz", 0},
- {KBD_CZECH_PROGRAMMERS, "cz", "bksl"},
- {KBD_CZECH_QWERTY, "cz", "qwerty"},
- {KBD_DANISH, "dk", 0},
- {KBD_GERMAN, "de", 0},
- {KBD_GERMAN_NEO, "de", "neo"},
- {KBD_GERMAN_IBM, "de", "qwerty"},
- {KBD_GREEK, "gr", 0},
- {KBD_GREEK_220, "gr", "simple"},
- {KBD_GREEK_319, "gr", "extended"},
- {KBD_GREEK_POLYTONIC, "gr", "polytonic"},
- {KBD_US, "us", 0},
- {KBD_US_ENGLISH_TABLE_FOR_IBM_ARABIC_238_L, "ara", "buckwalter"},
- {KBD_SPANISH, "es", 0},
- {KBD_SPANISH_VARIATION, "es", "nodeadkeys"},
- {KBD_FINNISH, "fi", 0},
- {KBD_FRENCH, "fr", 0},
- {KBD_HEBREW, "il", 0},
- {KBD_HUNGARIAN, "hu", 0},
- {KBD_HUNGARIAN_101_KEY, "hu", "standard"},
- {KBD_ICELANDIC, "is", 0},
- {KBD_ITALIAN, "it", 0},
- {KBD_ITALIAN_142, "it", "nodeadkeys"},
- {KBD_JAPANESE, "jp", 0},
- {KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002, "jp", "kana"},
- {KBD_KOREAN, "kr", 0},
- {KBD_KOREAN_INPUT_SYSTEM_IME_2000, "kr", "kr104"},
- {KBD_DUTCH, "nl", 0},
- {KBD_NORWEGIAN, "no", 0},
- {KBD_POLISH_PROGRAMMERS, "pl", 0},
- {KBD_POLISH_214, "pl", "qwertz"},
- {KBD_ROMANIAN, "ro", 0},
- {KBD_RUSSIAN, "ru", 0},
- {KBD_RUSSIAN_TYPEWRITER, "ru", "typewriter"},
- {KBD_CROATIAN, "hr", 0},
- {KBD_SLOVAK, "sk", 0},
- {KBD_SLOVAK_QWERTY, "sk", "qwerty"},
- {KBD_ALBANIAN, 0, 0},
- {KBD_SWEDISH, "se", 0},
- {KBD_THAI_KEDMANEE, "th", 0},
- {KBD_THAI_KEDMANEE_NON_SHIFTLOCK, "th", "tis"},
- {KBD_TURKISH_Q, "tr", 0},
- {KBD_TURKISH_F, "tr", "f"},
- {KBD_URDU, "in", "urd-phonetic3"},
- {KBD_UKRAINIAN, "ua", 0},
- {KBD_BELARUSIAN, "by", 0},
- {KBD_SLOVENIAN, "si", 0},
- {KBD_ESTONIAN, "ee", 0},
- {KBD_LATVIAN, "lv", 0},
- {KBD_LITHUANIAN_IBM, "lt", "ibm"},
- {KBD_FARSI, "af", 0},
- {KBD_VIETNAMESE, "vn", 0},
- {KBD_ARMENIAN_EASTERN, "am", 0},
- {KBD_AZERI_LATIN, 0, 0},
- {KBD_FYRO_MACEDONIAN, "mk", 0},
- {KBD_GEORGIAN, "ge", 0},
- {KBD_FAEROESE, 0, 0},
- {KBD_DEVANAGARI_INSCRIPT, 0, 0},
- {KBD_MALTESE_47_KEY, 0, 0},
- {KBD_NORWEGIAN_WITH_SAMI, "no", "smi"},
- {KBD_KAZAKH, "kz", 0},
- {KBD_KYRGYZ_CYRILLIC, "kg", "phonetic"},
- {KBD_TATAR, "ru", "tt"},
- {KBD_BENGALI, "bd", 0},
- {KBD_BENGALI_INSCRIPT, "bd", "probhat"},
- {KBD_PUNJABI, 0, 0},
- {KBD_GUJARATI, "in", "guj"},
- {KBD_TAMIL, "in", "tam"},
- {KBD_TELUGU, "in", "tel"},
- {KBD_KANNADA, "in", "kan"},
- {KBD_MALAYALAM, "in", "mal"},
- {KBD_HINDI_TRADITIONAL, "in", 0},
- {KBD_MARATHI, 0, 0},
- {KBD_MONGOLIAN_CYRILLIC, "mn", 0},
- {KBD_UNITED_KINGDOM_EXTENDED, "gb", "intl"},
- {KBD_SYRIAC, "syc", 0},
- {KBD_SYRIAC_PHONETIC, "syc", "syc_phonetic"},
- {KBD_NEPALI, "np", 0},
- {KBD_PASHTO, "af", "ps"},
- {KBD_DIVEHI_PHONETIC, 0, 0},
- {KBD_LUXEMBOURGISH, 0, 0},
- {KBD_MAORI, "mao", 0},
- {KBD_CHINESE_SIMPLIFIED_US, 0, 0},
- {KBD_SWISS_GERMAN, "ch", "de_nodeadkeys"},
- {KBD_UNITED_KINGDOM, "gb", 0},
- {KBD_LATIN_AMERICAN, "latam", 0},
- {KBD_BELGIAN_FRENCH, "be", 0},
- {KBD_BELGIAN_PERIOD, "be", "oss_sundeadkeys"},
- {KBD_PORTUGUESE, "pt", 0},
- {KBD_SERBIAN_LATIN, "rs", 0},
- {KBD_AZERI_CYRILLIC, "az", "cyrillic"},
- {KBD_SWEDISH_WITH_SAMI, "se", "smi"},
- {KBD_UZBEK_CYRILLIC, "af", "uz"},
- {KBD_INUKTITUT_LATIN, "ca", "ike"},
- {KBD_CANADIAN_FRENCH_LEGACY, "ca", "fr-legacy"},
- {KBD_SERBIAN_CYRILLIC, "rs", 0},
- {KBD_CANADIAN_FRENCH, "ca", "fr-legacy"},
- {KBD_SWISS_FRENCH, "ch", "fr"},
- {KBD_BOSNIAN, "ba", 0},
- {KBD_IRISH, 0, 0},
- {KBD_BOSNIAN_CYRILLIC, "ba", "us"},
- {KBD_UNITED_STATES_DVORAK, "us", "dvorak"},
- {KBD_PORTUGUESE_BRAZILIAN_ABNT2, "br", "nativo"},
- {KBD_CANADIAN_MULTILINGUAL_STANDARD, "ca", "multix"},
- {KBD_GAELIC, "ie", "CloGaelach"},
-
- {0x00000000, 0, 0},
-};
-
-/* taken from 2.2.7.1.6 Input Capability Set (TS_INPUT_CAPABILITYSET) */
-static char *rdp_keyboard_types[] = {
- "", /* 0: unused */
- "", /* 1: IBM PC/XT or compatible (83-key) keyboard */
- "", /* 2: Olivetti "ICO" (102-key) keyboard */
- "", /* 3: IBM PC/AT (84-key) or similar keyboard */
- "pc102",/* 4: IBM enhanced (101- or 102-key) keyboard */
- "", /* 5: Nokia 1050 and similar keyboards */
- "", /* 6: Nokia 9140 and similar keyboards */
- "" /* 7: Japanese keyboard */
-};
-
-static BOOL
-xf_peer_activate(freerdp_peer* client)
-{
- RdpPeerContext *peerCtx;
- struct rdp_backend *b;
- struct rdp_output *output;
- rdpSettings *settings;
- rdpPointerUpdate *pointer;
- struct rdp_peers_item *peersItem;
- struct xkb_context *xkbContext;
- struct xkb_rule_names xkbRuleNames;
- struct xkb_keymap *keymap;
- struct weston_output *weston_output;
- int i;
- pixman_box32_t box;
- pixman_region32_t damage;
- char seat_name[50];
-
-
- peerCtx = (RdpPeerContext *)client->context;
- b = peerCtx->rdpBackend;
- peersItem = &peerCtx->item;
- output = b->output;
- settings = client->settings;
-
- if (!settings->SurfaceCommandsEnabled) {
- weston_log("client doesn't support required SurfaceCommands\n");
- return FALSE;
- }
-
- if (output->base.width != (int)settings->DesktopWidth ||
- output->base.height != (int)settings->DesktopHeight)
- {
- if (b->no_clients_resize) {
- /* RDP peers don't dictate their resolution to weston */
- if (!settings->DesktopResize) {
- /* peer does not support desktop resize */
- weston_log("%s: client doesn't support resizing, closing connection\n", __FUNCTION__);
- return FALSE;
- } else {
- settings->DesktopWidth = output->base.width;
- settings->DesktopHeight = output->base.height;
- client->update->DesktopResize(client->context);
- }
- } else {
- /* ask weston to adjust size */
- struct weston_mode new_mode;
- struct weston_mode *target_mode;
- new_mode.width = (int)settings->DesktopWidth;
- new_mode.height = (int)settings->DesktopHeight;
- target_mode = ensure_matching_mode(&output->base, &new_mode);
- if (!target_mode) {
- weston_log("client mode not found\n");
- return FALSE;
- }
- weston_output_mode_set_native(&output->base, target_mode, 1);
- output->base.width = new_mode.width;
- output->base.height = new_mode.height;
- }
- }
-
- weston_output = &output->base;
- RFX_RESET(peerCtx->rfx_context, weston_output->width, weston_output->height);
- NSC_RESET(peerCtx->nsc_context, weston_output->width, weston_output->height);
-
- if (peersItem->flags & RDP_PEER_ACTIVATED)
- return TRUE;
-
- /* when here it's the first reactivation, we need to setup a little more */
- weston_log("kbd_layout:0x%x kbd_type:0x%x kbd_subType:0x%x kbd_functionKeys:0x%x\n",
- settings->KeyboardLayout, settings->KeyboardType, settings->KeyboardSubType,
- settings->KeyboardFunctionKey);
-
- memset(&xkbRuleNames, 0, sizeof(xkbRuleNames));
- if (settings->KeyboardType <= 7)
- xkbRuleNames.model = rdp_keyboard_types[settings->KeyboardType];
- for (i = 0; rdp_keyboards[i].rdpLayoutCode; i++) {
- if (rdp_keyboards[i].rdpLayoutCode == settings->KeyboardLayout) {
- xkbRuleNames.layout = rdp_keyboards[i].xkbLayout;
- xkbRuleNames.variant = rdp_keyboards[i].xkbVariant;
- weston_log("%s: matching layout=%s variant=%s\n", __FUNCTION__,
- xkbRuleNames.layout, xkbRuleNames.variant);
- break;
- }
- }
-
- keymap = NULL;
- if (xkbRuleNames.layout) {
- xkbContext = xkb_context_new(0);
- if (!xkbContext) {
- weston_log("unable to create a xkb_context\n");
- return FALSE;
- }
-
- keymap = xkb_keymap_new_from_names(xkbContext, &xkbRuleNames, 0);
- }
-
- if (settings->ClientHostname)
- snprintf(seat_name, sizeof(seat_name), "RDP %s", settings->ClientHostname);
- else
- snprintf(seat_name, sizeof(seat_name), "RDP peer @%s", settings->ClientAddress);
-
- peersItem->seat = zalloc(sizeof(*peersItem->seat));
- if (!peersItem->seat) {
- xkb_keymap_unref(keymap);
- weston_log("unable to create a weston_seat\n");
- return FALSE;
- }
-
- weston_seat_init(peersItem->seat, b->compositor, seat_name);
- weston_seat_init_keyboard(peersItem->seat, keymap);
- weston_seat_init_pointer(peersItem->seat);
-
- peersItem->flags |= RDP_PEER_ACTIVATED;
-
- /* disable pointer on the client side */
- pointer = client->update->pointer;
- pointer->pointer_system.type = SYSPTR_NULL;
- pointer->PointerSystem(client->context, &pointer->pointer_system);
-
- /* sends a full refresh */
- box.x1 = 0;
- box.y1 = 0;
- box.x2 = output->base.width;
- box.y2 = output->base.height;
- pixman_region32_init_with_extents(&damage, &box);
-
- rdp_peer_refresh_region(&damage, client);
-
- pixman_region32_fini(&damage);
-
- return TRUE;
-}
-
-static BOOL xf_peer_post_connect(freerdp_peer *client)
-{
- return TRUE;
-}
-
-static FREERDP_CB_RET_TYPE
-xf_mouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y)
-{
- RdpPeerContext *peerContext = (RdpPeerContext *)input->context;
- struct rdp_output *output;
- uint32_t button = 0;
- bool need_frame = false;
-
- if (flags & PTR_FLAGS_MOVE) {
- output = peerContext->rdpBackend->output;
- if (x < output->base.width && y < output->base.height) {
- notify_motion_absolute(peerContext->item.seat, weston_compositor_get_time(),
- x, y);
- need_frame = true;
- }
- }
-
- if (flags & PTR_FLAGS_BUTTON1)
- button = BTN_LEFT;
- else if (flags & PTR_FLAGS_BUTTON2)
- button = BTN_RIGHT;
- else if (flags & PTR_FLAGS_BUTTON3)
- button = BTN_MIDDLE;
-
- if (button) {
- notify_button(peerContext->item.seat, weston_compositor_get_time(), button,
- (flags & PTR_FLAGS_DOWN) ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED
- );
- need_frame = true;
- }
-
- if (flags & PTR_FLAGS_WHEEL) {
- struct weston_pointer_axis_event weston_event;
- double value;
-
- /* DEFAULT_AXIS_STEP_DISTANCE is stolen from compositor-x11.c
- * The RDP specs says the lower bits of flags contains the "the number of rotation
- * units the mouse wheel was rotated".
- *
- * https://blogs.msdn.microsoft.com/oldnewthing/20130123-00/?p=5473 explains the 120 value
- */
- value = (flags & 0xff) / 120.0;
- if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
- value = -value;
-
- weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
- weston_event.value = DEFAULT_AXIS_STEP_DISTANCE * value;
- weston_event.discrete = (int)value;
- weston_event.has_discrete = true;
-
- notify_axis(peerContext->item.seat, weston_compositor_get_time(),
- &weston_event);
- need_frame = true;
- }
-
- if (need_frame)
- notify_pointer_frame(peerContext->item.seat);
-
- FREERDP_CB_RETURN(TRUE);
-}
-
-static FREERDP_CB_RET_TYPE
-xf_extendedMouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y)
-{
- RdpPeerContext *peerContext = (RdpPeerContext *)input->context;
- struct rdp_output *output;
-
- output = peerContext->rdpBackend->output;
- if (x < output->base.width && y < output->base.height) {
- notify_motion_absolute(peerContext->item.seat, weston_compositor_get_time(),
- x, y);
- }
-
- FREERDP_CB_RETURN(TRUE);
-}
-
-
-static FREERDP_CB_RET_TYPE
-xf_input_synchronize_event(rdpInput *input, UINT32 flags)
-{
- freerdp_peer *client = input->context->peer;
- RdpPeerContext *peerCtx = (RdpPeerContext *)input->context;
- struct rdp_output *output = peerCtx->rdpBackend->output;
- pixman_box32_t box;
- pixman_region32_t damage;
-
- /* sends a full refresh */
- box.x1 = 0;
- box.y1 = 0;
- box.x2 = output->base.width;
- box.y2 = output->base.height;
- pixman_region32_init_with_extents(&damage, &box);
-
- rdp_peer_refresh_region(&damage, client);
-
- pixman_region32_fini(&damage);
- FREERDP_CB_RETURN(TRUE);
-}
-
-
-static FREERDP_CB_RET_TYPE
-xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code)
-{
- uint32_t scan_code, vk_code, full_code;
- enum wl_keyboard_key_state keyState;
- RdpPeerContext *peerContext = (RdpPeerContext *)input->context;
- int notify = 0;
-
- if (!(peerContext->item.flags & RDP_PEER_ACTIVATED))
- FREERDP_CB_RETURN(TRUE);
-
- if (flags & KBD_FLAGS_DOWN) {
- keyState = WL_KEYBOARD_KEY_STATE_PRESSED;
- notify = 1;
- } else if (flags & KBD_FLAGS_RELEASE) {
- keyState = WL_KEYBOARD_KEY_STATE_RELEASED;
- notify = 1;
- }
-
- if (notify) {
- full_code = code;
- if (flags & KBD_FLAGS_EXTENDED)
- full_code |= KBD_FLAGS_EXTENDED;
-
- vk_code = GetVirtualKeyCodeFromVirtualScanCode(full_code, 4);
- if (flags & KBD_FLAGS_EXTENDED)
- vk_code |= KBDEXT;
-
- scan_code = GetKeycodeFromVirtualKeyCode(vk_code, KEYCODE_TYPE_EVDEV);
-
- /*weston_log("code=%x ext=%d vk_code=%x scan_code=%x\n", code, (flags & KBD_FLAGS_EXTENDED) ? 1 : 0,
- vk_code, scan_code);*/
- notify_key(peerContext->item.seat, weston_compositor_get_time(),
- scan_code - 8, keyState, STATE_UPDATE_AUTOMATIC);
- }
-
- FREERDP_CB_RETURN(TRUE);
-}
-
-static FREERDP_CB_RET_TYPE
-xf_input_unicode_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code)
-{
- weston_log("Client sent a unicode keyboard event (flags:0x%X code:0x%X)\n", flags, code);
- FREERDP_CB_RETURN(TRUE);
-}
-
-
-static FREERDP_CB_RET_TYPE
-xf_suppress_output(rdpContext *context, BYTE allow, RECTANGLE_16 *area)
-{
- RdpPeerContext *peerContext = (RdpPeerContext *)context;
-
- if (allow)
- peerContext->item.flags |= RDP_PEER_OUTPUT_ENABLED;
- else
- peerContext->item.flags &= (~RDP_PEER_OUTPUT_ENABLED);
-
- FREERDP_CB_RETURN(TRUE);
-}
-
-static int
-rdp_peer_init(freerdp_peer *client, struct rdp_backend *b)
-{
- int rcount = 0;
- void *rfds[MAX_FREERDP_FDS];
- int i, fd;
- struct wl_event_loop *loop;
- rdpSettings *settings;
- rdpInput *input;
- RdpPeerContext *peerCtx;
-
- client->ContextSize = sizeof(RdpPeerContext);
- client->ContextNew = (psPeerContextNew)rdp_peer_context_new;
- client->ContextFree = (psPeerContextFree)rdp_peer_context_free;
- freerdp_peer_context_new(client);
-
- peerCtx = (RdpPeerContext *) client->context;
- peerCtx->rdpBackend = b;
-
- settings = client->settings;
- /* configure security settings */
- if (b->rdp_key)
- settings->RdpKeyFile = strdup(b->rdp_key);
- if (b->tls_enabled) {
- settings->CertificateFile = strdup(b->server_cert);
- settings->PrivateKeyFile = strdup(b->server_key);
- } else {
- settings->TlsSecurity = FALSE;
- }
- settings->NlaSecurity = FALSE;
-
- if (!client->Initialize(client)) {
- weston_log("peer initialization failed\n");
- goto error_initialize;
- }
-
- settings->OsMajorType = OSMAJORTYPE_UNIX;
- settings->OsMinorType = OSMINORTYPE_PSEUDO_XSERVER;
- settings->ColorDepth = 32;
- settings->RefreshRect = TRUE;
- settings->RemoteFxCodec = TRUE;
- settings->NSCodec = TRUE;
- settings->FrameMarkerCommandEnabled = TRUE;
- settings->SurfaceFrameMarkerEnabled = TRUE;
-
- client->Capabilities = xf_peer_capabilities;
- client->PostConnect = xf_peer_post_connect;
- client->Activate = xf_peer_activate;
-
- client->update->SuppressOutput = xf_suppress_output;
-
- input = client->input;
- input->SynchronizeEvent = xf_input_synchronize_event;
- input->MouseEvent = xf_mouseEvent;
- input->ExtendedMouseEvent = xf_extendedMouseEvent;
- input->KeyboardEvent = xf_input_keyboard_event;
- input->UnicodeKeyboardEvent = xf_input_unicode_keyboard_event;
-
- if (!client->GetFileDescriptor(client, rfds, &rcount)) {
- weston_log("unable to retrieve client fds\n");
- goto error_initialize;
- }
-
- loop = wl_display_get_event_loop(b->compositor->wl_display);
- for (i = 0; i < rcount; i++) {
- fd = (int)(long)(rfds[i]);
-
- peerCtx->events[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
- rdp_client_activity, client);
- }
- for ( ; i < MAX_FREERDP_FDS; i++)
- peerCtx->events[i] = 0;
-
- wl_list_insert(&b->output->peers, &peerCtx->item.link);
- return 0;
-
-error_initialize:
- client->Close(client);
- return -1;
-}
-
-
-static FREERDP_CB_RET_TYPE
-rdp_incoming_peer(freerdp_listener *instance, freerdp_peer *client)
-{
- struct rdp_backend *b = (struct rdp_backend *)instance->param4;
- if (rdp_peer_init(client, b) < 0) {
- weston_log("error when treating incoming peer\n");
- FREERDP_CB_RETURN(FALSE);
- }
-
- FREERDP_CB_RETURN(TRUE);
-}
-
-static struct rdp_backend *
-rdp_backend_create(struct weston_compositor *compositor,
- struct weston_rdp_backend_config *config)
-{
- struct rdp_backend *b;
- char *fd_str;
- int fd;
-
- b = zalloc(sizeof *b);
- if (b == NULL)
- return NULL;
-
- b->compositor = compositor;
- b->base.destroy = rdp_destroy;
- b->base.restore = rdp_restore;
- b->rdp_key = config->rdp_key ? strdup(config->rdp_key) : NULL;
- b->no_clients_resize = config->no_clients_resize;
-
- /* activate TLS only if certificate/key are available */
- if (config->server_cert && config->server_key) {
- weston_log("TLS support activated\n");
- b->server_cert = strdup(config->server_cert);
- b->server_key = strdup(config->server_key);
- if (!b->server_cert || !b->server_key)
- goto err_free_strings;
- b->tls_enabled = 1;
- }
-
- if (weston_compositor_set_presentation_clock_software(compositor) < 0)
- goto err_compositor;
-
- if (pixman_renderer_init(compositor) < 0)
- goto err_compositor;
-
- if (rdp_backend_create_output(b, config->width, config->height) < 0)
- goto err_compositor;
-
- compositor->capabilities |= WESTON_CAP_ARBITRARY_MODES;
-
- if (!config->env_socket) {
- b->listener = freerdp_listener_new();
- b->listener->PeerAccepted = rdp_incoming_peer;
- b->listener->param4 = b;
- if (!b->listener->Open(b->listener, config->bind_address, config->port)) {
- weston_log("unable to bind rdp socket\n");
- goto err_listener;
- }
-
- if (rdp_implant_listener(b, b->listener) < 0)
- goto err_compositor;
- } else {
- /* get the socket from RDP_FD var */
- fd_str = getenv("RDP_FD");
- if (!fd_str) {
- weston_log("RDP_FD env variable not set\n");
- goto err_output;
- }
-
- fd = strtoul(fd_str, NULL, 10);
- if (rdp_peer_init(freerdp_peer_new(fd), b))
- goto err_output;
- }
-
- compositor->backend = &b->base;
- return b;
-
-err_listener:
- freerdp_listener_free(b->listener);
-err_output:
- weston_output_destroy(&b->output->base);
-err_compositor:
- weston_compositor_shutdown(compositor);
-err_free_strings:
- free(b->rdp_key);
- free(b->server_cert);
- free(b->server_key);
- free(b);
- return NULL;
-}
-
-static void
-config_init_to_defaults(struct weston_rdp_backend_config *config)
-{
- config->width = 640;
- config->height = 480;
- config->bind_address = NULL;
- config->port = 3389;
- config->rdp_key = NULL;
- config->server_cert = NULL;
- config->server_key = NULL;
- config->env_socket = 0;
- config->no_clients_resize = 0;
-}
-
-WL_EXPORT int
-backend_init(struct weston_compositor *compositor,
- struct weston_backend_config *config_base)
-{
- struct rdp_backend *b;
- struct weston_rdp_backend_config config = {{ 0, }};
- int major, minor, revision;
-
- freerdp_get_version(&major, &minor, &revision);
- weston_log("using FreeRDP version %d.%d.%d\n", major, minor, revision);
-
- if (config_base == NULL ||
- config_base->struct_version != WESTON_RDP_BACKEND_CONFIG_VERSION ||
- config_base->struct_size > sizeof(struct weston_rdp_backend_config)) {
- weston_log("RDP backend config structure is invalid\n");
- return -1;
- }
-
- config_init_to_defaults(&config);
- memcpy(&config, config_base, config_base->struct_size);
-
- if (!config.rdp_key && (!config.server_cert || !config.server_key)) {
- weston_log("the RDP compositor requires keys and an optional certificate for RDP or TLS security ("
- "--rdp4-key or --rdp-tls-cert/--rdp-tls-key)\n");
- return -1;
- }
-
- b = rdp_backend_create(compositor, &config);
- if (b == NULL)
- return -1;
- return 0;
-}
+++ /dev/null
-/*
- * Copyright © 2016 Benoit Gschwind
- *
- * 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.
- */
-
-#ifndef WESTON_COMPOSITOR_RDP_H
-#define WESTON_COMPOSITOR_RDP_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "compositor.h"
-
-#define WESTON_RDP_BACKEND_CONFIG_VERSION 1
-
-struct weston_rdp_backend_config {
- struct weston_backend_config base;
- int width;
- int height;
- char *bind_address;
- int port;
- char *rdp_key;
- char *server_cert;
- char *server_key;
- int env_socket;
- int no_clients_resize;
-};
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* WESTON_COMPOSITOR_RDP_H */
+++ /dev/null
-/*
- * Copyright © 2010-2011 Benjamin Franzke
- * Copyright © 2013 Jason Ekstrand
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <linux/input.h>
-
-#include <wayland-client.h>
-#include <wayland-egl.h>
-#include <wayland-cursor.h>
-
-#include "compositor.h"
-#include "compositor-wayland.h"
-#include "gl-renderer.h"
-#include "pixman-renderer.h"
-#include "shared/helpers.h"
-#include "shared/image-loader.h"
-#include "shared/os-compatibility.h"
-#include "shared/cairo-util.h"
-#include "fullscreen-shell-unstable-v1-client-protocol.h"
-#include "presentation-time-server-protocol.h"
-#include "linux-dmabuf.h"
-
-#define WINDOW_TITLE "Weston Compositor"
-
-struct wayland_backend {
- struct weston_backend base;
- struct weston_compositor *compositor;
-
- struct {
- struct wl_display *wl_display;
- struct wl_registry *registry;
- struct wl_compositor *compositor;
- struct wl_shell *shell;
- struct zwp_fullscreen_shell_v1 *fshell;
- struct wl_shm *shm;
-
- struct wl_list output_list;
-
- struct wl_event_source *wl_source;
- uint32_t event_mask;
- } parent;
-
- int use_pixman;
- int sprawl_across_outputs;
-
- struct theme *theme;
- cairo_device_t *frame_device;
- struct wl_cursor_theme *cursor_theme;
- struct wl_cursor *cursor;
-
- struct wl_list input_list;
-};
-
-struct wayland_output {
- struct weston_output base;
-
- struct {
- int draw_initial_frame;
- struct wl_surface *surface;
-
- struct wl_output *output;
- uint32_t global_id;
-
- struct wl_shell_surface *shell_surface;
- int configure_width, configure_height;
- } parent;
-
- int keyboard_count;
-
- char *name;
- struct frame *frame;
-
- struct {
- struct wl_egl_window *egl_window;
- struct {
- cairo_surface_t *top;
- cairo_surface_t *left;
- cairo_surface_t *right;
- cairo_surface_t *bottom;
- } border;
- } gl;
-
- struct {
- struct wl_list buffers;
- struct wl_list free_buffers;
- } shm;
-
- struct weston_mode mode;
- uint32_t scale;
-};
-
-struct wayland_parent_output {
- struct wayland_output *output;
- struct wl_list link;
-
- struct wl_output *global;
- uint32_t id;
-
- struct {
- char *make;
- char *model;
- int32_t width, height;
- uint32_t subpixel;
- } physical;
-
- int32_t x, y;
- uint32_t transform;
- uint32_t scale;
-
- struct wl_list mode_list;
- struct weston_mode *preferred_mode;
- struct weston_mode *current_mode;
-};
-
-struct wayland_shm_buffer {
- struct wayland_output *output;
- struct wl_list link;
- struct wl_list free_link;
-
- struct wl_buffer *buffer;
- void *data;
- size_t size;
- pixman_region32_t damage;
- int frame_damaged;
-
- pixman_image_t *pm_image;
- cairo_surface_t *c_surface;
-};
-
-struct wayland_input {
- struct weston_seat base;
- struct wayland_backend *backend;
- struct wl_list link;
-
- struct {
- struct wl_seat *seat;
- struct wl_pointer *pointer;
- struct wl_keyboard *keyboard;
- struct wl_touch *touch;
-
- struct {
- struct wl_surface *surface;
- int32_t hx, hy;
- } cursor;
- } parent;
-
- enum weston_key_state_update keyboard_state_update;
- uint32_t key_serial;
- uint32_t enter_serial;
- uint32_t touch_points;
- bool touch_active;
- bool has_focus;
- int seat_version;
-
- struct wayland_output *output;
- struct wayland_output *touch_focus;
- struct wayland_output *keyboard_focus;
-
- struct weston_pointer_axis_event vert, horiz;
-};
-
-struct gl_renderer_interface *gl_renderer;
-
-static void
-wayland_shm_buffer_destroy(struct wayland_shm_buffer *buffer)
-{
- cairo_surface_destroy(buffer->c_surface);
- pixman_image_unref(buffer->pm_image);
-
- wl_buffer_destroy(buffer->buffer);
- munmap(buffer->data, buffer->size);
-
- pixman_region32_fini(&buffer->damage);
-
- wl_list_remove(&buffer->link);
- wl_list_remove(&buffer->free_link);
- free(buffer);
-}
-
-static void
-buffer_release(void *data, struct wl_buffer *buffer)
-{
- struct wayland_shm_buffer *sb = data;
-
- if (sb->output) {
- wl_list_insert(&sb->output->shm.free_buffers, &sb->free_link);
- } else {
- wayland_shm_buffer_destroy(sb);
- }
-}
-
-static const struct wl_buffer_listener buffer_listener = {
- buffer_release
-};
-
-static struct wayland_shm_buffer *
-wayland_output_get_shm_buffer(struct wayland_output *output)
-{
- struct wayland_backend *b =
- (struct wayland_backend *) output->base.compositor->backend;
- struct wl_shm *shm = b->parent.shm;
- struct wayland_shm_buffer *sb;
-
- struct wl_shm_pool *pool;
- int width, height, stride;
- int32_t fx, fy;
- int fd;
- unsigned char *data;
-
- if (!wl_list_empty(&output->shm.free_buffers)) {
- sb = container_of(output->shm.free_buffers.next,
- struct wayland_shm_buffer, free_link);
- wl_list_remove(&sb->free_link);
- wl_list_init(&sb->free_link);
-
- return sb;
- }
-
- if (output->frame) {
- width = frame_width(output->frame);
- height = frame_height(output->frame);
- } else {
- width = output->base.current_mode->width;
- height = output->base.current_mode->height;
- }
-
- stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
-
- fd = os_create_anonymous_file(height * stride);
- if (fd < 0) {
- weston_log("could not create an anonymous file buffer: %m\n");
- return NULL;
- }
-
- data = mmap(NULL, height * stride, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (data == MAP_FAILED) {
- weston_log("could not mmap %d memory for data: %m\n", height * stride);
- close(fd);
- return NULL;
- }
-
- sb = zalloc(sizeof *sb);
- if (sb == NULL) {
- weston_log("could not zalloc %zu memory for sb: %m\n", sizeof *sb);
- close(fd);
- free(data);
- return NULL;
- }
-
- sb->output = output;
- wl_list_init(&sb->free_link);
- wl_list_insert(&output->shm.buffers, &sb->link);
-
- pixman_region32_init_rect(&sb->damage, 0, 0,
- output->base.width, output->base.height);
- sb->frame_damaged = 1;
-
- sb->data = data;
- sb->size = height * stride;
-
- pool = wl_shm_create_pool(shm, fd, sb->size);
-
- sb->buffer = wl_shm_pool_create_buffer(pool, 0,
- width, height,
- stride,
- WL_SHM_FORMAT_ARGB8888);
- wl_buffer_add_listener(sb->buffer, &buffer_listener, sb);
- wl_shm_pool_destroy(pool);
- close(fd);
-
- memset(data, 0, sb->size);
-
- sb->c_surface =
- cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32,
- width, height, stride);
-
- fx = 0;
- fy = 0;
- if (output->frame)
- frame_interior(output->frame, &fx, &fy, 0, 0);
- sb->pm_image =
- pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height,
- (uint32_t *)(data + fy * stride) + fx,
- stride);
-
- return sb;
-}
-
-static void
-frame_done(void *data, struct wl_callback *callback, uint32_t time)
-{
- struct weston_output *output = data;
- struct timespec ts;
-
- wl_callback_destroy(callback);
-
- /* XXX: use the presentation extension for proper timings */
-
- /*
- * This is the fallback case, where Presentation extension is not
- * available from the parent compositor. We do not know the base for
- * 'time', so we cannot feed it to finish_frame(). Do the only thing
- * we can, and pretend finish_frame time is when we process this
- * event.
- */
- weston_compositor_read_presentation_clock(output->compositor, &ts);
- weston_output_finish_frame(output, &ts, 0);
-}
-
-static const struct wl_callback_listener frame_listener = {
- frame_done
-};
-
-static void
-draw_initial_frame(struct wayland_output *output)
-{
- struct wayland_shm_buffer *sb;
-
- sb = wayland_output_get_shm_buffer(output);
-
- /* If we are rendering with GL, then orphan it so that it gets
- * destroyed immediately */
- if (output->gl.egl_window)
- sb->output = NULL;
-
- wl_surface_attach(output->parent.surface, sb->buffer, 0, 0);
- wl_surface_damage(output->parent.surface, 0, 0,
- output->base.current_mode->width,
- output->base.current_mode->height);
-}
-
-static void
-wayland_output_update_gl_border(struct wayland_output *output)
-{
- int32_t ix, iy, iwidth, iheight, fwidth, fheight;
- cairo_t *cr;
-
- if (!output->frame)
- return;
- if (!(frame_status(output->frame) & FRAME_STATUS_REPAINT))
- return;
-
- fwidth = frame_width(output->frame);
- fheight = frame_height(output->frame);
- frame_interior(output->frame, &ix, &iy, &iwidth, &iheight);
-
- if (!output->gl.border.top)
- output->gl.border.top =
- cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
- fwidth, iy);
- cr = cairo_create(output->gl.border.top);
- frame_repaint(output->frame, cr);
- cairo_destroy(cr);
- gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_TOP,
- fwidth, iy,
- cairo_image_surface_get_stride(output->gl.border.top) / 4,
- cairo_image_surface_get_data(output->gl.border.top));
-
-
- if (!output->gl.border.left)
- output->gl.border.left =
- cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
- ix, 1);
- cr = cairo_create(output->gl.border.left);
- cairo_translate(cr, 0, -iy);
- frame_repaint(output->frame, cr);
- cairo_destroy(cr);
- gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_LEFT,
- ix, 1,
- cairo_image_surface_get_stride(output->gl.border.left) / 4,
- cairo_image_surface_get_data(output->gl.border.left));
-
-
- if (!output->gl.border.right)
- output->gl.border.right =
- cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
- fwidth - (ix + iwidth), 1);
- cr = cairo_create(output->gl.border.right);
- cairo_translate(cr, -(iwidth + ix), -iy);
- frame_repaint(output->frame, cr);
- cairo_destroy(cr);
- gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_RIGHT,
- fwidth - (ix + iwidth), 1,
- cairo_image_surface_get_stride(output->gl.border.right) / 4,
- cairo_image_surface_get_data(output->gl.border.right));
-
-
- if (!output->gl.border.bottom)
- output->gl.border.bottom =
- cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
- fwidth, fheight - (iy + iheight));
- cr = cairo_create(output->gl.border.bottom);
- cairo_translate(cr, 0, -(iy + iheight));
- frame_repaint(output->frame, cr);
- cairo_destroy(cr);
- gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_BOTTOM,
- fwidth, fheight - (iy + iheight),
- cairo_image_surface_get_stride(output->gl.border.bottom) / 4,
- cairo_image_surface_get_data(output->gl.border.bottom));
-}
-
-static void
-wayland_output_start_repaint_loop(struct weston_output *output_base)
-{
- struct wayland_output *output = (struct wayland_output *) output_base;
- struct wayland_backend *wb =
- (struct wayland_backend *)output->base.compositor->backend;
- struct wl_callback *callback;
-
- /* If this is the initial frame, we need to attach a buffer so that
- * the compositor can map the surface and include it in its render
- * loop. If the surface doesn't end up in the render loop, the frame
- * callback won't be invoked. The buffer is transparent and of the
- * same size as the future real output buffer. */
- if (output->parent.draw_initial_frame) {
- output->parent.draw_initial_frame = 0;
-
- draw_initial_frame(output);
- }
-
- callback = wl_surface_frame(output->parent.surface);
- wl_callback_add_listener(callback, &frame_listener, output);
- wl_surface_commit(output->parent.surface);
- wl_display_flush(wb->parent.wl_display);
-}
-
-static int
-wayland_output_repaint_gl(struct weston_output *output_base,
- pixman_region32_t *damage)
-{
- struct wayland_output *output = (struct wayland_output *) output_base;
- struct weston_compositor *ec = output->base.compositor;
- struct wl_callback *callback;
-
- callback = wl_surface_frame(output->parent.surface);
- wl_callback_add_listener(callback, &frame_listener, output);
-
- wayland_output_update_gl_border(output);
-
- ec->renderer->repaint_output(&output->base, damage);
-
- pixman_region32_subtract(&ec->primary_plane.damage,
- &ec->primary_plane.damage, damage);
- return 0;
-}
-
-static void
-wayland_output_update_shm_border(struct wayland_shm_buffer *buffer)
-{
- int32_t ix, iy, iwidth, iheight, fwidth, fheight;
- cairo_t *cr;
-
- if (!buffer->output->frame || !buffer->frame_damaged)
- return;
-
- cr = cairo_create(buffer->c_surface);
-
- frame_interior(buffer->output->frame, &ix, &iy, &iwidth, &iheight);
- fwidth = frame_width(buffer->output->frame);
- fheight = frame_height(buffer->output->frame);
-
- /* Set the clip so we don't unnecisaraly damage the surface */
- cairo_move_to(cr, ix, iy);
- cairo_rel_line_to(cr, iwidth, 0);
- cairo_rel_line_to(cr, 0, iheight);
- cairo_rel_line_to(cr, -iwidth, 0);
- cairo_line_to(cr, ix, iy);
- cairo_line_to(cr, 0, iy);
- cairo_line_to(cr, 0, fheight);
- cairo_line_to(cr, fwidth, fheight);
- cairo_line_to(cr, fwidth, 0);
- cairo_line_to(cr, 0, 0);
- cairo_line_to(cr, 0, iy);
- cairo_close_path(cr);
- cairo_clip(cr);
-
- /* Draw using a pattern so that the final result gets clipped */
- cairo_push_group(cr);
- frame_repaint(buffer->output->frame, cr);
- cairo_pop_group_to_source(cr);
- cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
- cairo_paint(cr);
-
- cairo_destroy(cr);
-}
-
-static void
-wayland_shm_buffer_attach(struct wayland_shm_buffer *sb)
-{
- pixman_region32_t damage;
- pixman_box32_t *rects;
- int32_t ix, iy, iwidth, iheight, fwidth, fheight;
- int i, n;
-
- pixman_region32_init(&damage);
- weston_transformed_region(sb->output->base.width,
- sb->output->base.height,
- sb->output->base.transform,
- sb->output->base.current_scale,
- &sb->damage, &damage);
-
- if (sb->output->frame) {
- frame_interior(sb->output->frame, &ix, &iy, &iwidth, &iheight);
- fwidth = frame_width(sb->output->frame);
- fheight = frame_height(sb->output->frame);
-
- pixman_region32_translate(&damage, ix, iy);
-
- if (sb->frame_damaged) {
- pixman_region32_union_rect(&damage, &damage,
- 0, 0, fwidth, iy);
- pixman_region32_union_rect(&damage, &damage,
- 0, iy, ix, iheight);
- pixman_region32_union_rect(&damage, &damage,
- ix + iwidth, iy,
- fwidth - (ix + iwidth), iheight);
- pixman_region32_union_rect(&damage, &damage,
- 0, iy + iheight,
- fwidth, fheight - (iy + iheight));
- }
- }
-
- rects = pixman_region32_rectangles(&damage, &n);
- wl_surface_attach(sb->output->parent.surface, sb->buffer, 0, 0);
- for (i = 0; i < n; ++i)
- wl_surface_damage(sb->output->parent.surface, rects[i].x1,
- rects[i].y1, rects[i].x2 - rects[i].x1,
- rects[i].y2 - rects[i].y1);
-
- if (sb->output->frame)
- pixman_region32_fini(&damage);
-}
-
-static int
-wayland_output_repaint_pixman(struct weston_output *output_base,
- pixman_region32_t *damage)
-{
- struct wayland_output *output = (struct wayland_output *) output_base;
- struct wayland_backend *b =
- (struct wayland_backend *)output->base.compositor->backend;
- struct wl_callback *callback;
- struct wayland_shm_buffer *sb;
-
- if (output->frame) {
- if (frame_status(output->frame) & FRAME_STATUS_REPAINT)
- wl_list_for_each(sb, &output->shm.buffers, link)
- sb->frame_damaged = 1;
- }
-
- wl_list_for_each(sb, &output->shm.buffers, link)
- pixman_region32_union(&sb->damage, &sb->damage, damage);
-
- sb = wayland_output_get_shm_buffer(output);
-
- wayland_output_update_shm_border(sb);
- pixman_renderer_output_set_buffer(output_base, sb->pm_image);
- b->compositor->renderer->repaint_output(output_base, &sb->damage);
-
- wayland_shm_buffer_attach(sb);
-
- callback = wl_surface_frame(output->parent.surface);
- wl_callback_add_listener(callback, &frame_listener, output);
- wl_surface_commit(output->parent.surface);
- wl_display_flush(b->parent.wl_display);
-
- pixman_region32_fini(&sb->damage);
- pixman_region32_init(&sb->damage);
- sb->frame_damaged = 0;
-
- pixman_region32_subtract(&b->compositor->primary_plane.damage,
- &b->compositor->primary_plane.damage, damage);
- return 0;
-}
-
-static void
-wayland_output_destroy(struct weston_output *output_base)
-{
- struct wayland_output *output = (struct wayland_output *) output_base;
- struct wayland_backend *b =
- (struct wayland_backend *) output->base.compositor->backend;
-
- if (b->use_pixman) {
- pixman_renderer_output_destroy(output_base);
- } else {
- gl_renderer->output_destroy(output_base);
- }
-
- wl_egl_window_destroy(output->gl.egl_window);
- wl_surface_destroy(output->parent.surface);
- if (output->parent.shell_surface)
- wl_shell_surface_destroy(output->parent.shell_surface);
-
- if (output->frame)
- frame_destroy(output->frame);
-
- cairo_surface_destroy(output->gl.border.top);
- cairo_surface_destroy(output->gl.border.left);
- cairo_surface_destroy(output->gl.border.right);
- cairo_surface_destroy(output->gl.border.bottom);
-
- weston_output_destroy(&output->base);
- free(output);
-
- return;
-}
-
-static const struct wl_shell_surface_listener shell_surface_listener;
-
-static int
-wayland_output_init_gl_renderer(struct wayland_output *output)
-{
- int32_t fwidth = 0, fheight = 0;
-
- if (output->frame) {
- fwidth = frame_width(output->frame);
- fheight = frame_height(output->frame);
- } else {
- fwidth = output->base.current_mode->width;
- fheight = output->base.current_mode->height;
- }
-
- output->gl.egl_window =
- wl_egl_window_create(output->parent.surface,
- fwidth, fheight);
- if (!output->gl.egl_window) {
- weston_log("failure to create wl_egl_window\n");
- return -1;
- }
-
- if (gl_renderer->output_create(&output->base,
- output->gl.egl_window,
- output->gl.egl_window,
- gl_renderer->alpha_attribs,
- NULL,
- 0) < 0)
- goto cleanup_window;
-
- return 0;
-
-cleanup_window:
- wl_egl_window_destroy(output->gl.egl_window);
- return -1;
-}
-
-static int
-wayland_output_init_pixman_renderer(struct wayland_output *output)
-{
- return pixman_renderer_output_create(&output->base);
-}
-
-static void
-wayland_output_resize_surface(struct wayland_output *output)
-{
- struct wayland_backend *b =
- (struct wayland_backend *)output->base.compositor->backend;
- struct wayland_shm_buffer *buffer, *next;
- int32_t ix, iy, iwidth, iheight;
- int32_t width, height;
- struct wl_region *region;
-
- width = output->base.current_mode->width;
- height = output->base.current_mode->height;
-
- if (output->frame) {
- frame_resize_inside(output->frame, width, height);
-
- frame_input_rect(output->frame, &ix, &iy, &iwidth, &iheight);
- region = wl_compositor_create_region(b->parent.compositor);
- wl_region_add(region, ix, iy, iwidth, iheight);
- wl_surface_set_input_region(output->parent.surface, region);
- wl_region_destroy(region);
-
- frame_opaque_rect(output->frame, &ix, &iy, &iwidth, &iheight);
- region = wl_compositor_create_region(b->parent.compositor);
- wl_region_add(region, ix, iy, iwidth, iheight);
- wl_surface_set_opaque_region(output->parent.surface, region);
- wl_region_destroy(region);
-
- width = frame_width(output->frame);
- height = frame_height(output->frame);
- } else {
- region = wl_compositor_create_region(b->parent.compositor);
- wl_region_add(region, 0, 0, width, height);
- wl_surface_set_input_region(output->parent.surface, region);
- wl_region_destroy(region);
-
- region = wl_compositor_create_region(b->parent.compositor);
- wl_region_add(region, 0, 0, width, height);
- wl_surface_set_opaque_region(output->parent.surface, region);
- wl_region_destroy(region);
- }
-
- if (output->gl.egl_window) {
- wl_egl_window_resize(output->gl.egl_window,
- width, height, 0, 0);
-
- /* These will need to be re-created due to the resize */
- gl_renderer->output_set_border(&output->base,
- GL_RENDERER_BORDER_TOP,
- 0, 0, 0, NULL);
- cairo_surface_destroy(output->gl.border.top);
- output->gl.border.top = NULL;
- gl_renderer->output_set_border(&output->base,
- GL_RENDERER_BORDER_LEFT,
- 0, 0, 0, NULL);
- cairo_surface_destroy(output->gl.border.left);
- output->gl.border.left = NULL;
- gl_renderer->output_set_border(&output->base,
- GL_RENDERER_BORDER_RIGHT,
- 0, 0, 0, NULL);
- cairo_surface_destroy(output->gl.border.right);
- output->gl.border.right = NULL;
- gl_renderer->output_set_border(&output->base,
- GL_RENDERER_BORDER_BOTTOM,
- 0, 0, 0, NULL);
- cairo_surface_destroy(output->gl.border.bottom);
- output->gl.border.bottom = NULL;
- }
-
- /* Throw away any remaining SHM buffers */
- wl_list_for_each_safe(buffer, next, &output->shm.free_buffers, free_link)
- wayland_shm_buffer_destroy(buffer);
- /* These will get thrown away when they get released */
- wl_list_for_each(buffer, &output->shm.buffers, link)
- buffer->output = NULL;
-}
-
-static int
-wayland_output_set_windowed(struct wayland_output *output)
-{
- struct wayland_backend *b =
- (struct wayland_backend *)output->base.compositor->backend;
- int tlen;
- char *title;
-
- if (output->frame)
- return 0;
-
- if (output->name) {
- tlen = strlen(output->name) + strlen(WINDOW_TITLE " - ");
- title = malloc(tlen + 1);
- if (!title)
- return -1;
-
- snprintf(title, tlen + 1, WINDOW_TITLE " - %s", output->name);
- } else {
- title = strdup(WINDOW_TITLE);
- }
-
- if (!b->theme) {
- b->theme = theme_create();
- if (!b->theme) {
- free(title);
- return -1;
- }
- }
- output->frame = frame_create(b->theme, 100, 100,
- FRAME_BUTTON_CLOSE, title);
- free(title);
- if (!output->frame)
- return -1;
-
- if (output->keyboard_count)
- frame_set_flag(output->frame, FRAME_FLAG_ACTIVE);
-
- wayland_output_resize_surface(output);
-
- wl_shell_surface_set_toplevel(output->parent.shell_surface);
-
- return 0;
-}
-
-static void
-wayland_output_set_fullscreen(struct wayland_output *output,
- enum wl_shell_surface_fullscreen_method method,
- uint32_t framerate, struct wl_output *target)
-{
- struct wayland_backend *b =
- (struct wayland_backend *)output->base.compositor->backend;
-
- if (output->frame) {
- frame_destroy(output->frame);
- output->frame = NULL;
- }
-
- wayland_output_resize_surface(output);
-
- if (output->parent.shell_surface) {
- wl_shell_surface_set_fullscreen(output->parent.shell_surface,
- method, framerate, target);
- } else if (b->parent.fshell) {
- zwp_fullscreen_shell_v1_present_surface(b->parent.fshell,
- output->parent.surface,
- method, target);
- }
-}
-
-static struct weston_mode *
-wayland_output_choose_mode(struct wayland_output *output,
- struct weston_mode *ref_mode)
-{
- struct weston_mode *mode;
-
- /* First look for an exact match */
- wl_list_for_each(mode, &output->base.mode_list, link)
- if (mode->width == ref_mode->width &&
- mode->height == ref_mode->height &&
- mode->refresh == ref_mode->refresh)
- return mode;
-
- /* If we can't find an exact match, ignore refresh and try again */
- wl_list_for_each(mode, &output->base.mode_list, link)
- if (mode->width == ref_mode->width &&
- mode->height == ref_mode->height)
- return mode;
-
- /* Yeah, we failed */
- return NULL;
-}
-
-enum mode_status {
- MODE_STATUS_UNKNOWN,
- MODE_STATUS_SUCCESS,
- MODE_STATUS_FAIL,
- MODE_STATUS_CANCEL,
-};
-
-static void
-mode_feedback_successful(void *data,
- struct zwp_fullscreen_shell_mode_feedback_v1 *fb)
-{
- enum mode_status *value = data;
-
- printf("Mode switch successful\n");
-
- *value = MODE_STATUS_SUCCESS;
-}
-
-static void
-mode_feedback_failed(void *data, struct zwp_fullscreen_shell_mode_feedback_v1 *fb)
-{
- enum mode_status *value = data;
-
- printf("Mode switch failed\n");
-
- *value = MODE_STATUS_FAIL;
-}
-
-static void
-mode_feedback_cancelled(void *data, struct zwp_fullscreen_shell_mode_feedback_v1 *fb)
-{
- enum mode_status *value = data;
-
- printf("Mode switch cancelled\n");
-
- *value = MODE_STATUS_CANCEL;
-}
-
-struct zwp_fullscreen_shell_mode_feedback_v1_listener mode_feedback_listener = {
- mode_feedback_successful,
- mode_feedback_failed,
- mode_feedback_cancelled,
-};
-
-static int
-wayland_output_switch_mode(struct weston_output *output_base,
- struct weston_mode *mode)
-{
- struct wayland_output *output = (struct wayland_output *) output_base;
- struct wayland_backend *b;
- struct wl_surface *old_surface;
- struct weston_mode *old_mode;
- struct zwp_fullscreen_shell_mode_feedback_v1 *mode_feedback;
- enum mode_status mode_status;
- int ret = 0;
-
- if (output_base == NULL) {
- weston_log("output is NULL.\n");
- return -1;
- }
-
- if (mode == NULL) {
- weston_log("mode is NULL.\n");
- return -1;
- }
-
- b = (struct wayland_backend *)output_base->compositor->backend;
-
- if (output->parent.shell_surface || !b->parent.fshell)
- return -1;
-
- mode = wayland_output_choose_mode(output, mode);
- if (mode == NULL)
- return -1;
-
- if (output->base.current_mode == mode)
- return 0;
-
- old_mode = output->base.current_mode;
- old_surface = output->parent.surface;
- output->base.current_mode = mode;
- output->parent.surface =
- wl_compositor_create_surface(b->parent.compositor);
- wl_surface_set_user_data(output->parent.surface, output);
-
- /* Blow the old buffers because we changed size/surfaces */
- wayland_output_resize_surface(output);
-
- mode_feedback =
- zwp_fullscreen_shell_v1_present_surface_for_mode(b->parent.fshell,
- output->parent.surface,
- output->parent.output,
- mode->refresh);
- zwp_fullscreen_shell_mode_feedback_v1_add_listener(mode_feedback,
- &mode_feedback_listener,
- &mode_status);
-
- /* This should kick-start things again */
- output->parent.draw_initial_frame = 1;
- wayland_output_start_repaint_loop(&output->base);
-
- mode_status = MODE_STATUS_UNKNOWN;
- while (mode_status == MODE_STATUS_UNKNOWN && ret >= 0)
- ret = wl_display_dispatch(b->parent.wl_display);
-
- zwp_fullscreen_shell_mode_feedback_v1_destroy(mode_feedback);
-
- if (mode_status == MODE_STATUS_FAIL) {
- output->base.current_mode = old_mode;
- wl_surface_destroy(output->parent.surface);
- output->parent.surface = old_surface;
- wayland_output_resize_surface(output);
-
- return -1;
- }
-
- old_mode->flags &= ~WL_OUTPUT_MODE_CURRENT;
- output->base.current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
-
- if (b->use_pixman) {
- pixman_renderer_output_destroy(output_base);
- if (wayland_output_init_pixman_renderer(output) < 0)
- goto err_output;
- } else {
- gl_renderer->output_destroy(output_base);
- wl_egl_window_destroy(output->gl.egl_window);
- if (wayland_output_init_gl_renderer(output) < 0)
- goto err_output;
- }
- wl_surface_destroy(old_surface);
-
- weston_output_schedule_repaint(&output->base);
-
- return 0;
-
-err_output:
- /* XXX */
- return -1;
-}
-
-static struct wayland_output *
-wayland_output_create(struct wayland_backend *b, int x, int y,
- int width, int height, const char *name, int fullscreen,
- uint32_t transform, int32_t scale)
-{
- struct wayland_output *output;
- int output_width, output_height;
-
- weston_log("Creating %dx%d wayland output at (%d, %d)\n",
- width, height, x, y);
-
- output = zalloc(sizeof *output);
- if (output == NULL)
- return NULL;
-
- output->name = name ? strdup(name) : NULL;
- output->base.make = "wayland";
- output->base.model = "none";
-
- output_width = width * scale;
- output_height = height * scale;
-
- output->parent.surface =
- wl_compositor_create_surface(b->parent.compositor);
- if (!output->parent.surface)
- goto err_name;
- wl_surface_set_user_data(output->parent.surface, output);
-
- output->parent.draw_initial_frame = 1;
-
- if (b->parent.shell) {
- output->parent.shell_surface =
- wl_shell_get_shell_surface(b->parent.shell,
- output->parent.surface);
- if (!output->parent.shell_surface)
- goto err_surface;
- wl_shell_surface_add_listener(output->parent.shell_surface,
- &shell_surface_listener, output);
- }
-
- if (fullscreen && b->parent.shell) {
- wl_shell_surface_set_fullscreen(output->parent.shell_surface,
- 0, 0, NULL);
- wl_display_roundtrip(b->parent.wl_display);
- if (!width)
- output_width = output->parent.configure_width;
- if (!height)
- output_height = output->parent.configure_height;
- }
-
- output->mode.flags =
- WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
- output->mode.width = output_width;
- output->mode.height = output_height;
- output->mode.refresh = 60000;
- output->scale = scale;
- wl_list_init(&output->base.mode_list);
- wl_list_insert(&output->base.mode_list, &output->mode.link);
- output->base.current_mode = &output->mode;
-
- wl_list_init(&output->shm.buffers);
- wl_list_init(&output->shm.free_buffers);
-
- weston_output_init(&output->base, b->compositor, x, y, width, height,
- transform, scale);
-
- if (b->use_pixman) {
- if (wayland_output_init_pixman_renderer(output) < 0)
- goto err_output;
- output->base.repaint = wayland_output_repaint_pixman;
- } else {
- if (wayland_output_init_gl_renderer(output) < 0)
- goto err_output;
- output->base.repaint = wayland_output_repaint_gl;
- }
-
- output->base.start_repaint_loop = wayland_output_start_repaint_loop;
- output->base.destroy = wayland_output_destroy;
- output->base.assign_planes = NULL;
- output->base.set_backlight = NULL;
- output->base.set_dpms = NULL;
- output->base.switch_mode = wayland_output_switch_mode;
-
- weston_compositor_add_output(b->compositor, &output->base);
-
- return output;
-
-err_output:
- weston_output_destroy(&output->base);
- if (output->parent.shell_surface)
- wl_shell_surface_destroy(output->parent.shell_surface);
-err_surface:
- wl_surface_destroy(output->parent.surface);
-err_name:
- free(output->name);
-
- /* FIXME: cleanup weston_output */
- free(output);
-
- return NULL;
-}
-
-static struct wayland_output *
-wayland_output_create_for_config(struct wayland_backend *b,
- struct weston_wayland_backend_output_config *oc,
- int fullscreen, int32_t x, int32_t y)
-{
- struct wayland_output *output;
-
- output = wayland_output_create(b, x, y, oc->width, oc->height, oc->name,
- fullscreen, oc->transform, oc->scale);
-
- return output;
-}
-
-static struct wayland_output *
-wayland_output_create_for_parent_output(struct wayland_backend *b,
- struct wayland_parent_output *poutput)
-{
- struct wayland_output *output;
- struct weston_mode *mode;
- int32_t x;
-
- if (poutput->current_mode) {
- mode = poutput->current_mode;
- } else if (poutput->preferred_mode) {
- mode = poutput->preferred_mode;
- } else if (!wl_list_empty(&poutput->mode_list)) {
- mode = container_of(poutput->mode_list.next,
- struct weston_mode, link);
- } else {
- weston_log("No valid modes found. Skipping output\n");
- return NULL;
- }
-
- if (!wl_list_empty(&b->compositor->output_list)) {
- output = container_of(b->compositor->output_list.prev,
- struct wayland_output, base.link);
- x = output->base.x + output->base.current_mode->width;
- } else {
- x = 0;
- }
-
- output = wayland_output_create(b, x, 0, mode->width, mode->height,
- NULL, 0,
- WL_OUTPUT_TRANSFORM_NORMAL, 1);
- if (!output)
- return NULL;
-
- output->parent.output = poutput->global;
-
- output->base.make = poutput->physical.make;
- output->base.model = poutput->physical.model;
- wl_list_init(&output->base.mode_list);
- wl_list_insert_list(&output->base.mode_list, &poutput->mode_list);
- wl_list_init(&poutput->mode_list);
-
- wayland_output_set_fullscreen(output,
- WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER,
- mode->refresh, poutput->global);
-
- if (output->parent.shell_surface) {
- wl_shell_surface_set_fullscreen(output->parent.shell_surface,
- WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER,
- mode->refresh, poutput->global);
- } else if (b->parent.fshell) {
- zwp_fullscreen_shell_v1_present_surface(b->parent.fshell,
- output->parent.surface,
- ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_CENTER,
- poutput->global);
- zwp_fullscreen_shell_mode_feedback_v1_destroy(
- zwp_fullscreen_shell_v1_present_surface_for_mode(b->parent.fshell,
- output->parent.surface,
- poutput->global,
- mode->refresh));
- }
-
- return output;
-}
-
-static void
-shell_surface_ping(void *data, struct wl_shell_surface *shell_surface,
- uint32_t serial)
-{
- wl_shell_surface_pong(shell_surface, serial);
-}
-
-static void
-shell_surface_configure(void *data, struct wl_shell_surface *shell_surface,
- uint32_t edges, int32_t width, int32_t height)
-{
- struct wayland_output *output = data;
-
- output->parent.configure_width = width;
- output->parent.configure_height = height;
-
- /* FIXME: implement resizing */
-}
-
-static void
-shell_surface_popup_done(void *data, struct wl_shell_surface *shell_surface)
-{
-}
-
-static const struct wl_shell_surface_listener shell_surface_listener = {
- shell_surface_ping,
- shell_surface_configure,
- shell_surface_popup_done
-};
-
-/* Events received from the wayland-server this compositor is client of: */
-
-/* parent input interface */
-static void
-input_set_cursor(struct wayland_input *input)
-{
-
- struct wl_buffer *buffer;
- struct wl_cursor_image *image;
-
- if (!input->backend->cursor)
- return; /* Couldn't load the cursor. Can't set it */
-
- image = input->backend->cursor->images[0];
- buffer = wl_cursor_image_get_buffer(image);
- if (!buffer)
- return;
-
- wl_pointer_set_cursor(input->parent.pointer, input->enter_serial,
- input->parent.cursor.surface,
- image->hotspot_x, image->hotspot_y);
-
- wl_surface_attach(input->parent.cursor.surface, buffer, 0, 0);
- wl_surface_damage(input->parent.cursor.surface, 0, 0,
- image->width, image->height);
- wl_surface_commit(input->parent.cursor.surface);
-}
-
-static void
-input_handle_pointer_enter(void *data, struct wl_pointer *pointer,
- uint32_t serial, struct wl_surface *surface,
- wl_fixed_t fixed_x, wl_fixed_t fixed_y)
-{
- struct wayland_input *input = data;
- int32_t fx, fy;
- enum theme_location location;
- double x, y;
-
- x = wl_fixed_to_double(fixed_x);
- y = wl_fixed_to_double(fixed_y);
-
- /* XXX: If we get a modifier event immediately before the focus,
- * we should try to keep the same serial. */
- input->enter_serial = serial;
- input->output = wl_surface_get_user_data(surface);
-
- if (input->output->frame) {
- location = frame_pointer_enter(input->output->frame, input,
- x, y);
- frame_interior(input->output->frame, &fx, &fy, NULL, NULL);
- x -= fx;
- y -= fy;
-
- if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT)
- weston_output_schedule_repaint(&input->output->base);
- } else {
- location = THEME_LOCATION_CLIENT_AREA;
- }
-
- weston_output_transform_coordinate(&input->output->base, x, y, &x, &y);
-
- if (location == THEME_LOCATION_CLIENT_AREA) {
- input->has_focus = true;
- notify_pointer_focus(&input->base, &input->output->base, x, y);
- wl_pointer_set_cursor(input->parent.pointer,
- input->enter_serial, NULL, 0, 0);
- } else {
- input->has_focus = false;
- notify_pointer_focus(&input->base, NULL, 0, 0);
- input_set_cursor(input);
- }
-}
-
-static void
-input_handle_pointer_leave(void *data, struct wl_pointer *pointer,
- uint32_t serial, struct wl_surface *surface)
-{
- struct wayland_input *input = data;
-
- if (!input->output)
- return;
-
- if (input->output->frame) {
- frame_pointer_leave(input->output->frame, input);
-
- if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT)
- weston_output_schedule_repaint(&input->output->base);
- }
-
- notify_pointer_focus(&input->base, NULL, 0, 0);
- input->output = NULL;
- input->has_focus = false;
-}
-
-static void
-input_handle_motion(void *data, struct wl_pointer *pointer,
- uint32_t time, wl_fixed_t fixed_x, wl_fixed_t fixed_y)
-{
- struct wayland_input *input = data;
- int32_t fx, fy;
- enum theme_location location;
- bool want_frame = false;
- double x, y;
-
- if (!input->output)
- return;
-
- x = wl_fixed_to_double(fixed_x);
- y = wl_fixed_to_double(fixed_y);
-
- if (input->output->frame) {
- location = frame_pointer_motion(input->output->frame, input,
- x, y);
- frame_interior(input->output->frame, &fx, &fy, NULL, NULL);
- x -= fx;
- y -= fy;
-
- if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT)
- weston_output_schedule_repaint(&input->output->base);
- } else {
- location = THEME_LOCATION_CLIENT_AREA;
- }
-
- weston_output_transform_coordinate(&input->output->base, x, y, &x, &y);
-
- if (input->has_focus && location != THEME_LOCATION_CLIENT_AREA) {
- input_set_cursor(input);
- notify_pointer_focus(&input->base, NULL, 0, 0);
- input->has_focus = false;
- want_frame = true;
- } else if (!input->has_focus &&
- location == THEME_LOCATION_CLIENT_AREA) {
- wl_pointer_set_cursor(input->parent.pointer,
- input->enter_serial, NULL, 0, 0);
- notify_pointer_focus(&input->base, &input->output->base, x, y);
- input->has_focus = true;
- want_frame = true;
- }
-
- if (location == THEME_LOCATION_CLIENT_AREA) {
- notify_motion_absolute(&input->base, time, x, y);
- want_frame = true;
- }
-
- if (want_frame && input->seat_version < WL_POINTER_FRAME_SINCE_VERSION)
- notify_pointer_frame(&input->base);
-}
-
-static void
-input_handle_button(void *data, struct wl_pointer *pointer,
- uint32_t serial, uint32_t time, uint32_t button,
- uint32_t state_w)
-{
- struct wayland_input *input = data;
- enum wl_pointer_button_state state = state_w;
- enum frame_button_state fstate;
- enum theme_location location;
-
- if (!input->output)
- return;
-
- if (input->output->frame) {
- fstate = state == WL_POINTER_BUTTON_STATE_PRESSED ?
- FRAME_BUTTON_PRESSED : FRAME_BUTTON_RELEASED;
-
- location = frame_pointer_button(input->output->frame, input,
- button, fstate);
-
- if (frame_status(input->output->frame) & FRAME_STATUS_MOVE) {
-
- wl_shell_surface_move(input->output->parent.shell_surface,
- input->parent.seat, serial);
- frame_status_clear(input->output->frame,
- FRAME_STATUS_MOVE);
- return;
- }
-
- if (frame_status(input->output->frame) & FRAME_STATUS_CLOSE) {
- wayland_output_destroy(&input->output->base);
- input->output = NULL;
- input->keyboard_focus = NULL;
-
- if (wl_list_empty(&input->backend->compositor->output_list))
- weston_compositor_exit(input->backend->compositor);
-
- return;
- }
-
- if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT)
- weston_output_schedule_repaint(&input->output->base);
- } else {
- location = THEME_LOCATION_CLIENT_AREA;
- }
-
- if (location == THEME_LOCATION_CLIENT_AREA) {
- notify_button(&input->base, time, button, state);
- if (input->seat_version < WL_POINTER_FRAME_SINCE_VERSION)
- notify_pointer_frame(&input->base);
- }
-}
-
-static void
-input_handle_axis(void *data, struct wl_pointer *pointer,
- uint32_t time, uint32_t axis, wl_fixed_t value)
-{
- struct wayland_input *input = data;
- struct weston_pointer_axis_event weston_event;
-
- weston_event.axis = axis;
- weston_event.value = wl_fixed_to_double(value);
-
- if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL &&
- input->vert.has_discrete) {
- weston_event.has_discrete = true;
- weston_event.discrete = input->vert.discrete;
- input->vert.has_discrete = false;
- } else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL &&
- input->horiz.has_discrete) {
- weston_event.has_discrete = true;
- weston_event.discrete = input->horiz.discrete;
- input->horiz.has_discrete = false;
- }
-
- notify_axis(&input->base, time, &weston_event);
-
- if (input->seat_version < WL_POINTER_FRAME_SINCE_VERSION)
- notify_pointer_frame(&input->base);
-}
-
-static void
-input_handle_frame(void *data, struct wl_pointer *pointer)
-{
- struct wayland_input *input = data;
-
- notify_pointer_frame(&input->base);
-}
-
-static void
-input_handle_axis_source(void *data, struct wl_pointer *pointer,
- uint32_t source)
-{
- struct wayland_input *input = data;
-
- notify_axis_source(&input->base, source);
-}
-
-static void
-input_handle_axis_stop(void *data, struct wl_pointer *pointer,
- uint32_t time, uint32_t axis)
-{
- struct wayland_input *input = data;
- struct weston_pointer_axis_event weston_event;
-
- weston_event.axis = axis;
- weston_event.value = 0;
-
- notify_axis(&input->base, time, &weston_event);
-}
-
-static void
-input_handle_axis_discrete(void *data, struct wl_pointer *pointer,
- uint32_t axis, int32_t discrete)
-{
- struct wayland_input *input = data;
-
- if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
- input->vert.has_discrete = true;
- input->vert.discrete = discrete;
- } else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
- input->horiz.has_discrete = true;
- input->horiz.discrete = discrete;
- }
-}
-
-static const struct wl_pointer_listener pointer_listener = {
- input_handle_pointer_enter,
- input_handle_pointer_leave,
- input_handle_motion,
- input_handle_button,
- input_handle_axis,
- input_handle_frame,
- input_handle_axis_source,
- input_handle_axis_stop,
- input_handle_axis_discrete,
-};
-
-static void
-input_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format,
- int fd, uint32_t size)
-{
- struct wayland_input *input = data;
- struct xkb_keymap *keymap;
- char *map_str;
-
- if (!data) {
- close(fd);
- return;
- }
-
- if (format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
- map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
- if (map_str == MAP_FAILED) {
- weston_log("mmap failed: %m\n");
- goto error;
- }
-
- keymap = xkb_keymap_new_from_string(input->backend->compositor->xkb_context,
- map_str,
- XKB_KEYMAP_FORMAT_TEXT_V1,
- 0);
- munmap(map_str, size);
-
- if (!keymap) {
- weston_log("failed to compile keymap\n");
- goto error;
- }
-
- input->keyboard_state_update = STATE_UPDATE_NONE;
- } else if (format == WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP) {
- weston_log("No keymap provided; falling back to defalt\n");
- keymap = NULL;
- input->keyboard_state_update = STATE_UPDATE_AUTOMATIC;
- } else {
- weston_log("Invalid keymap\n");
- goto error;
- }
-
- close(fd);
-
- if (weston_seat_get_keyboard(&input->base))
- weston_seat_update_keymap(&input->base, keymap);
- else
- weston_seat_init_keyboard(&input->base, keymap);
-
- xkb_keymap_unref(keymap);
-
- return;
-
-error:
- wl_keyboard_release(input->parent.keyboard);
- close(fd);
-}
-
-static void
-input_handle_keyboard_enter(void *data,
- struct wl_keyboard *keyboard,
- uint32_t serial,
- struct wl_surface *surface,
- struct wl_array *keys)
-{
- struct wayland_input *input = data;
- struct wayland_output *focus;
-
- focus = input->keyboard_focus;
- if (focus) {
- /* This shouldn't happen */
- focus->keyboard_count--;
- if (!focus->keyboard_count && focus->frame)
- frame_unset_flag(focus->frame, FRAME_FLAG_ACTIVE);
- if (frame_status(focus->frame) & FRAME_STATUS_REPAINT)
- weston_output_schedule_repaint(&focus->base);
- }
-
- input->keyboard_focus = wl_surface_get_user_data(surface);
- input->keyboard_focus->keyboard_count++;
-
- focus = input->keyboard_focus;
- if (focus->frame) {
- frame_set_flag(focus->frame, FRAME_FLAG_ACTIVE);
- if (frame_status(focus->frame) & FRAME_STATUS_REPAINT)
- weston_output_schedule_repaint(&focus->base);
- }
-
-
- /* XXX: If we get a modifier event immediately before the focus,
- * we should try to keep the same serial. */
- notify_keyboard_focus_in(&input->base, keys,
- STATE_UPDATE_AUTOMATIC);
-}
-
-static void
-input_handle_keyboard_leave(void *data,
- struct wl_keyboard *keyboard,
- uint32_t serial,
- struct wl_surface *surface)
-{
- struct wayland_input *input = data;
- struct wayland_output *focus;
-
- notify_keyboard_focus_out(&input->base);
-
- focus = input->keyboard_focus;
- if (!focus)
- return;
-
- focus->keyboard_count--;
- if (!focus->keyboard_count && focus->frame) {
- frame_unset_flag(focus->frame, FRAME_FLAG_ACTIVE);
- if (frame_status(focus->frame) & FRAME_STATUS_REPAINT)
- weston_output_schedule_repaint(&focus->base);
- }
-
- input->keyboard_focus = NULL;
-}
-
-static void
-input_handle_key(void *data, struct wl_keyboard *keyboard,
- uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
-{
- struct wayland_input *input = data;
-
- input->key_serial = serial;
- notify_key(&input->base, time, key,
- state ? WL_KEYBOARD_KEY_STATE_PRESSED :
- WL_KEYBOARD_KEY_STATE_RELEASED,
- input->keyboard_state_update);
-}
-
-static void
-input_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,
- uint32_t serial_in, uint32_t mods_depressed,
- uint32_t mods_latched, uint32_t mods_locked,
- uint32_t group)
-{
- struct weston_keyboard *keyboard;
- struct wayland_input *input = data;
- struct wayland_backend *b = input->backend;
- uint32_t serial_out;
-
- /* If we get a key event followed by a modifier event with the
- * same serial number, then we try to preserve those semantics by
- * reusing the same serial number on the way out too. */
- if (serial_in == input->key_serial)
- serial_out = wl_display_get_serial(b->compositor->wl_display);
- else
- serial_out = wl_display_next_serial(b->compositor->wl_display);
-
- keyboard = weston_seat_get_keyboard(&input->base);
- xkb_state_update_mask(keyboard->xkb_state.state,
- mods_depressed, mods_latched,
- mods_locked, 0, 0, group);
- notify_modifiers(&input->base, serial_out);
-}
-
-static void
-input_handle_repeat_info(void *data, struct wl_keyboard *keyboard,
- int32_t rate, int32_t delay)
-{
- struct wayland_input *input = data;
- struct wayland_backend *b = input->backend;
-
- b->compositor->kb_repeat_rate = rate;
- b->compositor->kb_repeat_delay = delay;
-}
-
-static const struct wl_keyboard_listener keyboard_listener = {
- input_handle_keymap,
- input_handle_keyboard_enter,
- input_handle_keyboard_leave,
- input_handle_key,
- input_handle_modifiers,
- input_handle_repeat_info,
-};
-
-static void
-input_handle_touch_down(void *data, struct wl_touch *wl_touch,
- uint32_t serial, uint32_t time,
- struct wl_surface *surface, int32_t id,
- wl_fixed_t fixed_x, wl_fixed_t fixed_y)
-{
- struct wayland_input *input = data;
- struct wayland_output *output;
- enum theme_location location;
- bool first_touch;
- int32_t fx, fy;
- double x, y;
-
- x = wl_fixed_to_double(fixed_x);
- y = wl_fixed_to_double(fixed_y);
-
- first_touch = (input->touch_points == 0);
- input->touch_points++;
-
- input->touch_focus = wl_surface_get_user_data(surface);
- output = input->touch_focus;
- if (!first_touch && !input->touch_active)
- return;
-
- if (output->frame) {
- location = frame_touch_down(output->frame, input, id, x, y);
-
- frame_interior(output->frame, &fx, &fy, NULL, NULL);
- x -= fx;
- y -= fy;
-
- if (frame_status(output->frame) & FRAME_STATUS_REPAINT)
- weston_output_schedule_repaint(&output->base);
-
- if (first_touch && (frame_status(output->frame) & FRAME_STATUS_MOVE)) {
- input->touch_points--;
- wl_shell_surface_move(output->parent.shell_surface,
- input->parent.seat, serial);
- frame_status_clear(output->frame,
- FRAME_STATUS_MOVE);
- return;
- }
-
- if (first_touch && location != THEME_LOCATION_CLIENT_AREA)
- return;
- }
-
- weston_output_transform_coordinate(&output->base, x, y, &x, &y);
-
- notify_touch(&input->base, time, id, x, y, WL_TOUCH_DOWN);
- input->touch_active = true;
-}
-
-static void
-input_handle_touch_up(void *data, struct wl_touch *wl_touch,
- uint32_t serial, uint32_t time, int32_t id)
-{
- struct wayland_input *input = data;
- struct wayland_output *output = input->touch_focus;
- bool active = input->touch_active;
-
- input->touch_points--;
- if (input->touch_points == 0) {
- input->touch_focus = NULL;
- input->touch_active = false;
- }
-
- if (!output)
- return;
-
- if (output->frame) {
- frame_touch_up(output->frame, input, id);
-
- if (frame_status(output->frame) & FRAME_STATUS_CLOSE) {
- wayland_output_destroy(&output->base);
- input->touch_focus = NULL;
- input->keyboard_focus = NULL;
- if (wl_list_empty(&input->backend->compositor->output_list))
- weston_compositor_exit(input->backend->compositor);
-
- return;
- }
- if (frame_status(output->frame) & FRAME_STATUS_REPAINT)
- weston_output_schedule_repaint(&output->base);
- }
-
- if (active)
- notify_touch(&input->base, time, id, 0, 0, WL_TOUCH_UP);
-}
-
-static void
-input_handle_touch_motion(void *data, struct wl_touch *wl_touch,
- uint32_t time, int32_t id,
- wl_fixed_t fixed_x, wl_fixed_t fixed_y)
-{
- struct wayland_input *input = data;
- struct wayland_output *output = input->touch_focus;
- int32_t fx, fy;
- double x, y;
-
- x = wl_fixed_to_double(fixed_x);
- y = wl_fixed_to_double(fixed_y);
-
- if (!output || !input->touch_active)
- return;
-
- if (output->frame) {
- frame_interior(output->frame, &fx, &fy, NULL, NULL);
- x -= fx;
- y -= fy;
- }
-
- weston_output_transform_coordinate(&output->base, x, y, &x, &y);
-
- notify_touch(&input->base, time, id, x, y, WL_TOUCH_MOTION);
-}
-
-static void
-input_handle_touch_frame(void *data, struct wl_touch *wl_touch)
-{
- struct wayland_input *input = data;
-
- if (!input->touch_focus || !input->touch_active)
- return;
-
- notify_touch_frame(&input->base);
-}
-
-static void
-input_handle_touch_cancel(void *data, struct wl_touch *wl_touch)
-{
- struct wayland_input *input = data;
-
- if (!input->touch_focus || !input->touch_active)
- return;
-
- notify_touch_cancel(&input->base);
-}
-
-static const struct wl_touch_listener touch_listener = {
- input_handle_touch_down,
- input_handle_touch_up,
- input_handle_touch_motion,
- input_handle_touch_frame,
- input_handle_touch_cancel,
-};
-
-
-static void
-input_handle_capabilities(void *data, struct wl_seat *seat,
- enum wl_seat_capability caps)
-{
- struct wayland_input *input = data;
-
- if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->parent.pointer) {
- input->parent.pointer = wl_seat_get_pointer(seat);
- wl_pointer_set_user_data(input->parent.pointer, input);
- wl_pointer_add_listener(input->parent.pointer,
- &pointer_listener, input);
- weston_seat_init_pointer(&input->base);
- } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->parent.pointer) {
- if (input->seat_version >= WL_POINTER_RELEASE_SINCE_VERSION)
- wl_pointer_release(input->parent.pointer);
- else
- wl_pointer_destroy(input->parent.pointer);
- input->parent.pointer = NULL;
- weston_seat_release_pointer(&input->base);
- }
-
- if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->parent.keyboard) {
- input->parent.keyboard = wl_seat_get_keyboard(seat);
- wl_keyboard_set_user_data(input->parent.keyboard, input);
- wl_keyboard_add_listener(input->parent.keyboard,
- &keyboard_listener, input);
- } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->parent.keyboard) {
- if (input->seat_version >= WL_KEYBOARD_RELEASE_SINCE_VERSION)
- wl_keyboard_release(input->parent.keyboard);
- else
- wl_keyboard_destroy(input->parent.keyboard);
- input->parent.keyboard = NULL;
- weston_seat_release_keyboard(&input->base);
- }
-
- if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->parent.touch) {
- input->parent.touch = wl_seat_get_touch(seat);
- wl_touch_set_user_data(input->parent.touch, input);
- wl_touch_add_listener(input->parent.touch,
- &touch_listener, input);
- weston_seat_init_touch(&input->base);
- } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->parent.touch) {
- if (input->seat_version >= WL_TOUCH_RELEASE_SINCE_VERSION)
- wl_touch_release(input->parent.touch);
- else
- wl_touch_destroy(input->parent.touch);
- input->parent.touch = NULL;
- weston_seat_release_touch(&input->base);
- }
-}
-
-static void
-input_handle_name(void *data, struct wl_seat *seat,
- const char *name)
-{
-}
-
-static const struct wl_seat_listener seat_listener = {
- input_handle_capabilities,
- input_handle_name,
-};
-
-static void
-display_add_seat(struct wayland_backend *b, uint32_t id, uint32_t available_version)
-{
- struct wayland_input *input;
- uint32_t version = MIN(available_version, 4);
-
- input = zalloc(sizeof *input);
- if (input == NULL)
- return;
-
- weston_seat_init(&input->base, b->compositor, "default");
- input->backend = b;
- input->parent.seat = wl_registry_bind(b->parent.registry, id,
- &wl_seat_interface, version);
- input->seat_version = version;
- wl_list_insert(b->input_list.prev, &input->link);
-
- wl_seat_add_listener(input->parent.seat, &seat_listener, input);
- wl_seat_set_user_data(input->parent.seat, input);
-
- input->parent.cursor.surface =
- wl_compositor_create_surface(b->parent.compositor);
-
- input->vert.axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
- input->horiz.axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL;
-}
-
-static void
-wayland_parent_output_geometry(void *data, struct wl_output *output_proxy,
- int32_t x, int32_t y,
- int32_t physical_width, int32_t physical_height,
- int32_t subpixel, const char *make,
- const char *model, int32_t transform)
-{
- struct wayland_parent_output *output = data;
-
- output->x = x;
- output->y = y;
- output->physical.width = physical_width;
- output->physical.height = physical_height;
- output->physical.subpixel = subpixel;
-
- free(output->physical.make);
- output->physical.make = strdup(make);
- free(output->physical.model);
- output->physical.model = strdup(model);
-
- output->transform = transform;
-}
-
-static struct weston_mode *
-find_mode(struct wl_list *list, int32_t width, int32_t height, uint32_t refresh)
-{
- struct weston_mode *mode;
-
- wl_list_for_each(mode, list, link) {
- if (mode->width == width && mode->height == height &&
- mode->refresh == refresh)
- return mode;
- }
-
- mode = zalloc(sizeof *mode);
- if (!mode)
- return NULL;
-
- mode->width = width;
- mode->height = height;
- mode->refresh = refresh;
- wl_list_insert(list, &mode->link);
-
- return mode;
-}
-
-static void
-wayland_parent_output_mode(void *data, struct wl_output *wl_output_proxy,
- uint32_t flags, int32_t width, int32_t height,
- int32_t refresh)
-{
- struct wayland_parent_output *output = data;
- struct weston_mode *mode;
-
- if (output->output) {
- mode = find_mode(&output->output->base.mode_list,
- width, height, refresh);
- if (!mode)
- return;
- mode->flags = flags;
- /* Do a mode-switch on current mode change? */
- } else {
- mode = find_mode(&output->mode_list, width, height, refresh);
- if (!mode)
- return;
- mode->flags = flags;
- if (flags & WL_OUTPUT_MODE_CURRENT)
- output->current_mode = mode;
- if (flags & WL_OUTPUT_MODE_PREFERRED)
- output->preferred_mode = mode;
- }
-}
-
-static const struct wl_output_listener output_listener = {
- wayland_parent_output_geometry,
- wayland_parent_output_mode
-};
-
-static void
-wayland_backend_register_output(struct wayland_backend *b, uint32_t id)
-{
- struct wayland_parent_output *output;
-
- output = zalloc(sizeof *output);
- if (!output)
- return;
-
- output->id = id;
- output->global = wl_registry_bind(b->parent.registry, id,
- &wl_output_interface, 1);
- if (!output->global) {
- free(output);
- return;
- }
-
- wl_output_add_listener(output->global, &output_listener, output);
-
- output->scale = 0;
- output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
- output->physical.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
- wl_list_init(&output->mode_list);
- wl_list_insert(&b->parent.output_list, &output->link);
-
- if (b->sprawl_across_outputs) {
- wl_display_roundtrip(b->parent.wl_display);
- wayland_output_create_for_parent_output(b, output);
- }
-}
-
-static void
-wayland_parent_output_destroy(struct wayland_parent_output *output)
-{
- struct weston_mode *mode, *next;
-
- if (output->output)
- wayland_output_destroy(&output->output->base);
-
- wl_output_destroy(output->global);
- free(output->physical.make);
- free(output->physical.model);
-
- wl_list_for_each_safe(mode, next, &output->mode_list, link) {
- wl_list_remove(&mode->link);
- free(mode);
- }
-}
-
-static void
-registry_handle_global(void *data, struct wl_registry *registry, uint32_t name,
- const char *interface, uint32_t version)
-{
- struct wayland_backend *b = data;
-
- if (strcmp(interface, "wl_compositor") == 0) {
- b->parent.compositor =
- wl_registry_bind(registry, name,
- &wl_compositor_interface, 1);
- } else if (strcmp(interface, "wl_shell") == 0) {
- b->parent.shell =
- wl_registry_bind(registry, name,
- &wl_shell_interface, 1);
- } else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
- b->parent.fshell =
- wl_registry_bind(registry, name,
- &zwp_fullscreen_shell_v1_interface, 1);
- } else if (strcmp(interface, "wl_seat") == 0) {
- display_add_seat(b, name, version);
- } else if (strcmp(interface, "wl_output") == 0) {
- wayland_backend_register_output(b, name);
- } else if (strcmp(interface, "wl_shm") == 0) {
- b->parent.shm =
- wl_registry_bind(registry, name, &wl_shm_interface, 1);
- }
-}
-
-static void
-registry_handle_global_remove(void *data, struct wl_registry *registry,
- uint32_t name)
-{
- struct wayland_backend *b = data;
- struct wayland_parent_output *output;
-
- wl_list_for_each(output, &b->parent.output_list, link)
- if (output->id == name)
- wayland_parent_output_destroy(output);
-}
-
-static const struct wl_registry_listener registry_listener = {
- registry_handle_global,
- registry_handle_global_remove
-};
-
-static int
-wayland_backend_handle_event(int fd, uint32_t mask, void *data)
-{
- struct wayland_backend *b = data;
- int count = 0;
-
- if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
- weston_compositor_exit(b->compositor);
- return 0;
- }
-
- if (mask & WL_EVENT_READABLE)
- count = wl_display_dispatch(b->parent.wl_display);
- if (mask & WL_EVENT_WRITABLE)
- wl_display_flush(b->parent.wl_display);
-
- if (mask == 0) {
- count = wl_display_dispatch_pending(b->parent.wl_display);
- wl_display_flush(b->parent.wl_display);
- }
-
- return count;
-}
-
-static void
-wayland_restore(struct weston_compositor *ec)
-{
-}
-
-static void
-wayland_destroy(struct weston_compositor *ec)
-{
- struct wayland_backend *b = (struct wayland_backend *) ec->backend;
-
- weston_compositor_shutdown(ec);
-
- if (b->parent.shm)
- wl_shm_destroy(b->parent.shm);
-
- free(b);
-}
-
-static const char *left_ptrs[] = {
- "left_ptr",
- "default",
- "top_left_arrow",
- "left-arrow"
-};
-
-static void
-create_cursor(struct wayland_backend *b,
- struct weston_wayland_backend_config *config)
-{
- unsigned int i;
-
- b->cursor_theme = wl_cursor_theme_load(config->cursor_theme,
- config->cursor_size,
- b->parent.shm);
- if (!b->cursor_theme) {
- fprintf(stderr, "could not load cursor theme\n");
- return;
- }
-
- b->cursor = NULL;
- for (i = 0; !b->cursor && i < ARRAY_LENGTH(left_ptrs); ++i)
- b->cursor = wl_cursor_theme_get_cursor(b->cursor_theme,
- left_ptrs[i]);
- if (!b->cursor) {
- fprintf(stderr, "could not load left cursor\n");
- return;
- }
-}
-
-static void
-fullscreen_binding(struct weston_keyboard *keyboard, uint32_t time,
- uint32_t key, void *data)
-{
- struct wayland_backend *b = data;
- struct wayland_input *input = NULL;
-
- wl_list_for_each(input, &b->input_list, link)
- if (&input->base == keyboard->seat)
- break;
-
- if (!input || !input->output)
- return;
-
- if (input->output->frame)
- wayland_output_set_fullscreen(input->output, 0, 0, NULL);
- else
- wayland_output_set_windowed(input->output);
-
- weston_output_schedule_repaint(&input->output->base);
-}
-
-static struct wayland_backend *
-wayland_backend_create(struct weston_compositor *compositor,
- struct weston_wayland_backend_config *new_config)
-{
- struct wayland_backend *b;
- struct wl_event_loop *loop;
- int fd;
-
- b = zalloc(sizeof *b);
- if (b == NULL)
- return NULL;
-
- b->compositor = compositor;
- if (weston_compositor_set_presentation_clock_software(compositor) < 0)
- goto err_compositor;
-
- b->parent.wl_display = wl_display_connect(new_config->display_name);
- if (b->parent.wl_display == NULL) {
- weston_log("failed to create display: %m\n");
- goto err_compositor;
- }
-
- wl_list_init(&b->parent.output_list);
- wl_list_init(&b->input_list);
- b->parent.registry = wl_display_get_registry(b->parent.wl_display);
- wl_registry_add_listener(b->parent.registry, ®istry_listener, b);
- wl_display_roundtrip(b->parent.wl_display);
-
- create_cursor(b, new_config);
-
- b->use_pixman = new_config->use_pixman;
-
- if (!b->use_pixman) {
- gl_renderer = weston_load_module("gl-renderer.so",
- "gl_renderer_interface");
- if (!gl_renderer)
- b->use_pixman = 1;
- }
-
- if (!b->use_pixman) {
- if (gl_renderer->create(compositor,
- EGL_PLATFORM_WAYLAND_KHR,
- b->parent.wl_display,
- gl_renderer->alpha_attribs,
- NULL,
- 0) < 0) {
- weston_log("Failed to initialize the GL renderer; "
- "falling back to pixman.\n");
- b->use_pixman = 1;
- }
- }
-
- if (b->use_pixman) {
- if (pixman_renderer_init(compositor) < 0) {
- weston_log("Failed to initialize pixman renderer\n");
- goto err_display;
- }
- }
-
- b->base.destroy = wayland_destroy;
- b->base.restore = wayland_restore;
-
- loop = wl_display_get_event_loop(compositor->wl_display);
-
- fd = wl_display_get_fd(b->parent.wl_display);
- b->parent.wl_source =
- wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
- wayland_backend_handle_event, b);
- if (b->parent.wl_source == NULL)
- goto err_display;
-
- wl_event_source_check(b->parent.wl_source);
-
- if (compositor->renderer->import_dmabuf) {
- if (linux_dmabuf_setup(compositor) < 0)
- weston_log("Error: initializing dmabuf "
- "support failed.\n");
- }
-
- compositor->backend = &b->base;
- return b;
-err_display:
- wl_display_disconnect(b->parent.wl_display);
-err_compositor:
- weston_compositor_shutdown(compositor);
- free(b);
- return NULL;
-}
-
-static void
-wayland_backend_destroy(struct wayland_backend *b)
-{
- wl_display_disconnect(b->parent.wl_display);
-
- if (b->theme)
- theme_destroy(b->theme);
- if (b->frame_device)
- cairo_device_destroy(b->frame_device);
- wl_cursor_theme_destroy(b->cursor_theme);
-
- weston_compositor_shutdown(b->compositor);
- free(b);
-}
-
-static void
-config_init_to_defaults(struct weston_wayland_backend_config *config)
-{
-}
-
-WL_EXPORT int
-backend_init(struct weston_compositor *compositor,
- struct weston_backend_config *config_base)
-{
- struct wayland_backend *b;
- struct wayland_output *output;
- struct wayland_parent_output *poutput;
- struct weston_wayland_backend_config new_config;
- int x, count;
-
- if (config_base == NULL ||
- config_base->struct_version != WESTON_WAYLAND_BACKEND_CONFIG_VERSION ||
- config_base->struct_size > sizeof(struct weston_wayland_backend_config)) {
- weston_log("wayland backend config structure is invalid\n");
- return -1;
- }
-
- config_init_to_defaults(&new_config);
- memcpy(&new_config, config_base, config_base->struct_size);
-
- b = wayland_backend_create(compositor, &new_config);
-
- if (!b)
- return -1;
-
- if (new_config.sprawl || b->parent.fshell) {
- b->sprawl_across_outputs = 1;
- wl_display_roundtrip(b->parent.wl_display);
-
- wl_list_for_each(poutput, &b->parent.output_list, link)
- wayland_output_create_for_parent_output(b, poutput);
-
- return 0;
- }
-
- if (new_config.fullscreen) {
- if (new_config.num_outputs != 1 || !new_config.outputs)
- goto err_outputs;
-
- output = wayland_output_create_for_config(b,
- &new_config.outputs[0],
- 1, 0, 0);
- if (!output)
- goto err_outputs;
-
- wayland_output_set_fullscreen(output, 0, 0, NULL);
- return 0;
- }
-
- x = 0;
- for (count = 0; count < new_config.num_outputs; ++count) {
- output = wayland_output_create_for_config(b, &new_config.outputs[count],
- 0, x, 0);
- if (!output)
- goto err_outputs;
- if (wayland_output_set_windowed(output))
- goto err_outputs;
-
- x += output->base.width;
- }
-
- weston_compositor_add_key_binding(compositor, KEY_F,
- MODIFIER_CTRL | MODIFIER_ALT,
- fullscreen_binding, b);
- return 0;
-
-err_outputs:
- wayland_backend_destroy(b);
- return -1;
-}
+++ /dev/null
-/*
- * Copyright © 2016 Benoit Gschwind
- *
- * 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.
- */
-
-#ifndef WESTON_COMPOSITOR_WAYLAND_H
-#define WESTON_COMPOSITOR_WAYLAND_H
-
-#include "compositor.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define WESTON_WAYLAND_BACKEND_CONFIG_VERSION 1
-
-struct weston_wayland_backend_output_config {
- int width;
- int height;
- char *name;
- uint32_t transform;
- int32_t scale;
-};
-
-struct weston_wayland_backend_config {
- struct weston_backend_config base;
- int use_pixman;
- int sprawl;
- char *display_name;
- int fullscreen;
- char *cursor_theme;
- int cursor_size;
- int num_outputs;
- struct weston_wayland_backend_output_config *outputs;
-};
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* WESTON_COMPOSITOR_WAYLAND_H */
+++ /dev/null
-/*
- * Copyright © 2008-2011 Kristian Høgsberg
- * Copyright © 2010-2011 Intel Corporation
- * Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <assert.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <sys/time.h>
-#include <sys/shm.h>
-#include <linux/input.h>
-
-#include <xcb/xcb.h>
-#include <xcb/shm.h>
-#ifdef HAVE_XCB_XKB
-#include <xcb/xkb.h>
-#endif
-
-#include <X11/Xlib.h>
-#include <X11/Xlib-xcb.h>
-
-#include <xkbcommon/xkbcommon.h>
-
-#include "compositor.h"
-#include "compositor-x11.h"
-#include "shared/config-parser.h"
-#include "shared/helpers.h"
-#include "shared/image-loader.h"
-#include "gl-renderer.h"
-#include "pixman-renderer.h"
-#include "presentation-time-server-protocol.h"
-#include "linux-dmabuf.h"
-
-#define DEFAULT_AXIS_STEP_DISTANCE 10
-
-struct x11_backend {
- struct weston_backend base;
- struct weston_compositor *compositor;
-
- 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 xkb_keymap *xkb_keymap;
- unsigned int has_xkb;
- uint8_t xkb_event_base;
- int use_pixman;
-
- int has_net_wm_state_fullscreen;
-
- /* We could map multi-pointer X to multiple wayland seats, but
- * for now we only support core X input. */
- struct weston_seat core_seat;
- double prev_x;
- double prev_y;
-
- 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_supporting_wm_check;
- xcb_atom_t net_supported;
- 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;
- xcb_atom_t xkb_names;
- } atom;
-};
-
-struct x11_output {
- struct weston_output base;
-
- xcb_window_t window;
- struct weston_mode mode;
- struct wl_event_source *finish_frame_timer;
-
- xcb_gc_t gc;
- xcb_shm_seg_t segment;
- pixman_image_t *hw_surface;
- int shm_id;
- void *buf;
- uint8_t depth;
- int32_t scale;
-};
-
-struct window_delete_data {
- struct x11_backend *backend;
- xcb_window_t window;
-};
-
-struct gl_renderer_interface *gl_renderer;
-
-static xcb_screen_t *
-x11_compositor_get_default_screen(struct x11_backend *b)
-{
- xcb_screen_iterator_t iter;
- int i, screen_nbr = XDefaultScreen(b->dpy);
-
- iter = xcb_setup_roots_iterator(xcb_get_setup(b->conn));
- for (i = 0; iter.rem; xcb_screen_next(&iter), i++)
- if (i == screen_nbr)
- return iter.data;
-
- return xcb_setup_roots_iterator(xcb_get_setup(b->conn)).data;
-}
-
-static struct xkb_keymap *
-x11_backend_get_keymap(struct x11_backend *b)
-{
- xcb_get_property_cookie_t cookie;
- xcb_get_property_reply_t *reply;
- struct xkb_rule_names names;
- struct xkb_keymap *ret;
- const char *value_all, *value_part;
- int length_all, length_part;
-
- memset(&names, 0, sizeof(names));
-
- cookie = xcb_get_property(b->conn, 0, b->screen->root,
- b->atom.xkb_names, b->atom.string, 0, 1024);
- reply = xcb_get_property_reply(b->conn, cookie, NULL);
- if (reply == NULL)
- return NULL;
-
- value_all = xcb_get_property_value(reply);
- length_all = xcb_get_property_value_length(reply);
- value_part = value_all;
-
-#define copy_prop_value(to) \
- length_part = strlen(value_part); \
- if (value_part + length_part < (value_all + length_all) && \
- length_part > 0) \
- names.to = value_part; \
- value_part += length_part + 1;
-
- copy_prop_value(rules);
- copy_prop_value(model);
- copy_prop_value(layout);
- copy_prop_value(variant);
- copy_prop_value(options);
-#undef copy_prop_value
-
- ret = xkb_keymap_new_from_names(b->compositor->xkb_context, &names, 0);
-
- free(reply);
- return ret;
-}
-
-static uint32_t
-get_xkb_mod_mask(struct x11_backend *b, uint32_t in)
-{
- struct weston_keyboard *keyboard =
- weston_seat_get_keyboard(&b->core_seat);
- struct weston_xkb_info *info = keyboard->xkb_info;
- uint32_t ret = 0;
-
- if ((in & ShiftMask) && info->shift_mod != XKB_MOD_INVALID)
- ret |= (1 << info->shift_mod);
- if ((in & LockMask) && info->caps_mod != XKB_MOD_INVALID)
- ret |= (1 << info->caps_mod);
- if ((in & ControlMask) && info->ctrl_mod != XKB_MOD_INVALID)
- ret |= (1 << info->ctrl_mod);
- if ((in & Mod1Mask) && info->alt_mod != XKB_MOD_INVALID)
- ret |= (1 << info->alt_mod);
- if ((in & Mod2Mask) && info->mod2_mod != XKB_MOD_INVALID)
- ret |= (1 << info->mod2_mod);
- if ((in & Mod3Mask) && info->mod3_mod != XKB_MOD_INVALID)
- ret |= (1 << info->mod3_mod);
- if ((in & Mod4Mask) && info->super_mod != XKB_MOD_INVALID)
- ret |= (1 << info->super_mod);
- if ((in & Mod5Mask) && info->mod5_mod != XKB_MOD_INVALID)
- ret |= (1 << info->mod5_mod);
-
- return ret;
-}
-
-static void
-x11_backend_setup_xkb(struct x11_backend *b)
-{
-#ifndef HAVE_XCB_XKB
- weston_log("XCB-XKB not available during build\n");
- b->has_xkb = 0;
- b->xkb_event_base = 0;
- return;
-#else
- struct weston_keyboard *keyboard;
- const xcb_query_extension_reply_t *ext;
- xcb_generic_error_t *error;
- xcb_void_cookie_t select;
- xcb_xkb_use_extension_cookie_t use_ext;
- xcb_xkb_use_extension_reply_t *use_ext_reply;
- xcb_xkb_per_client_flags_cookie_t pcf;
- xcb_xkb_per_client_flags_reply_t *pcf_reply;
- xcb_xkb_get_state_cookie_t state;
- xcb_xkb_get_state_reply_t *state_reply;
- uint32_t values[1] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
-
- b->has_xkb = 0;
- b->xkb_event_base = 0;
-
- ext = xcb_get_extension_data(b->conn, &xcb_xkb_id);
- if (!ext) {
- weston_log("XKB extension not available on host X11 server\n");
- return;
- }
- b->xkb_event_base = ext->first_event;
-
- select = xcb_xkb_select_events_checked(b->conn,
- XCB_XKB_ID_USE_CORE_KBD,
- XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
- 0,
- XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
- 0,
- 0,
- NULL);
- error = xcb_request_check(b->conn, select);
- if (error) {
- weston_log("error: failed to select for XKB state events\n");
- free(error);
- return;
- }
-
- use_ext = xcb_xkb_use_extension(b->conn,
- XCB_XKB_MAJOR_VERSION,
- XCB_XKB_MINOR_VERSION);
- use_ext_reply = xcb_xkb_use_extension_reply(b->conn, use_ext, NULL);
- if (!use_ext_reply) {
- weston_log("couldn't start using XKB extension\n");
- return;
- }
-
- if (!use_ext_reply->supported) {
- weston_log("XKB extension version on the server is too old "
- "(want %d.%d, has %d.%d)\n",
- XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION,
- use_ext_reply->serverMajor, use_ext_reply->serverMinor);
- free(use_ext_reply);
- return;
- }
- free(use_ext_reply);
-
- pcf = xcb_xkb_per_client_flags(b->conn,
- XCB_XKB_ID_USE_CORE_KBD,
- XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT,
- XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT,
- 0,
- 0,
- 0);
- pcf_reply = xcb_xkb_per_client_flags_reply(b->conn, pcf, NULL);
- if (!pcf_reply ||
- !(pcf_reply->value & XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT)) {
- weston_log("failed to set XKB per-client flags, not using "
- "detectable repeat\n");
- free(pcf_reply);
- return;
- }
- free(pcf_reply);
-
- state = xcb_xkb_get_state(b->conn, XCB_XKB_ID_USE_CORE_KBD);
- state_reply = xcb_xkb_get_state_reply(b->conn, state, NULL);
- if (!state_reply) {
- weston_log("failed to get initial XKB state\n");
- return;
- }
-
- keyboard = weston_seat_get_keyboard(&b->core_seat);
- xkb_state_update_mask(keyboard->xkb_state.state,
- get_xkb_mod_mask(b, state_reply->baseMods),
- get_xkb_mod_mask(b, state_reply->latchedMods),
- get_xkb_mod_mask(b, state_reply->lockedMods),
- 0,
- 0,
- state_reply->group);
-
- free(state_reply);
-
- xcb_change_window_attributes(b->conn, b->screen->root,
- XCB_CW_EVENT_MASK, values);
-
- b->has_xkb = 1;
-#endif
-}
-
-#ifdef HAVE_XCB_XKB
-static void
-update_xkb_keymap(struct x11_backend *b)
-{
- struct xkb_keymap *keymap;
-
- keymap = x11_backend_get_keymap(b);
- if (!keymap) {
- weston_log("failed to get XKB keymap\n");
- return;
- }
- weston_seat_update_keymap(&b->core_seat, keymap);
- xkb_keymap_unref(keymap);
-}
-#endif
-
-static int
-x11_input_create(struct x11_backend *b, int no_input)
-{
- struct xkb_keymap *keymap;
-
- weston_seat_init(&b->core_seat, b->compositor, "default");
-
- if (no_input)
- return 0;
-
- weston_seat_init_pointer(&b->core_seat);
-
- keymap = x11_backend_get_keymap(b);
- if (weston_seat_init_keyboard(&b->core_seat, keymap) < 0)
- return -1;
- xkb_keymap_unref(keymap);
-
- x11_backend_setup_xkb(b);
-
- return 0;
-}
-
-static void
-x11_input_destroy(struct x11_backend *b)
-{
- weston_seat_release(&b->core_seat);
-}
-
-static void
-x11_output_start_repaint_loop(struct weston_output *output)
-{
- struct timespec ts;
-
- weston_compositor_read_presentation_clock(output->compositor, &ts);
- weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
-}
-
-static int
-x11_output_repaint_gl(struct weston_output *output_base,
- pixman_region32_t *damage)
-{
- struct x11_output *output = (struct x11_output *)output_base;
- struct weston_compositor *ec = output->base.compositor;
-
- ec->renderer->repaint_output(output_base, damage);
-
- pixman_region32_subtract(&ec->primary_plane.damage,
- &ec->primary_plane.damage, damage);
-
- wl_event_source_timer_update(output->finish_frame_timer, 10);
- return 0;
-}
-
-static void
-set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region)
-{
- struct x11_output *output = (struct x11_output *)output_base;
- struct weston_compositor *ec = output->base.compositor;
- struct x11_backend *b = (struct x11_backend *)ec->backend;
- pixman_region32_t transformed_region;
- pixman_box32_t *rects;
- xcb_rectangle_t *output_rects;
- xcb_void_cookie_t cookie;
- int nrects, i;
- xcb_generic_error_t *err;
-
- pixman_region32_init(&transformed_region);
- pixman_region32_copy(&transformed_region, region);
- pixman_region32_translate(&transformed_region,
- -output_base->x, -output_base->y);
- weston_transformed_region(output_base->width, output_base->height,
- output_base->transform,
- output_base->current_scale,
- &transformed_region, &transformed_region);
-
- rects = pixman_region32_rectangles(&transformed_region, &nrects);
- output_rects = calloc(nrects, sizeof(xcb_rectangle_t));
-
- if (output_rects == NULL) {
- pixman_region32_fini(&transformed_region);
- return;
- }
-
- for (i = 0; i < nrects; i++) {
- output_rects[i].x = rects[i].x1;
- output_rects[i].y = rects[i].y1;
- output_rects[i].width = rects[i].x2 - rects[i].x1;
- output_rects[i].height = rects[i].y2 - rects[i].y1;
- }
-
- pixman_region32_fini(&transformed_region);
-
- cookie = xcb_set_clip_rectangles_checked(b->conn, XCB_CLIP_ORDERING_UNSORTED,
- output->gc,
- 0, 0, nrects,
- output_rects);
- err = xcb_request_check(b->conn, cookie);
- if (err != NULL) {
- weston_log("Failed to set clip rects, err: %d\n", err->error_code);
- free(err);
- }
- free(output_rects);
-}
-
-
-static int
-x11_output_repaint_shm(struct weston_output *output_base,
- pixman_region32_t *damage)
-{
- struct x11_output *output = (struct x11_output *)output_base;
- struct weston_compositor *ec = output->base.compositor;
- struct x11_backend *b = (struct x11_backend *)ec->backend;
- xcb_void_cookie_t cookie;
- xcb_generic_error_t *err;
-
- pixman_renderer_output_set_buffer(output_base, output->hw_surface);
- ec->renderer->repaint_output(output_base, damage);
-
- pixman_region32_subtract(&ec->primary_plane.damage,
- &ec->primary_plane.damage, damage);
- set_clip_for_output(output_base, damage);
- cookie = xcb_shm_put_image_checked(b->conn, output->window, output->gc,
- pixman_image_get_width(output->hw_surface),
- pixman_image_get_height(output->hw_surface),
- 0, 0,
- pixman_image_get_width(output->hw_surface),
- pixman_image_get_height(output->hw_surface),
- 0, 0, output->depth, XCB_IMAGE_FORMAT_Z_PIXMAP,
- 0, output->segment, 0);
- err = xcb_request_check(b->conn, cookie);
- if (err != NULL) {
- weston_log("Failed to put shm image, err: %d\n", err->error_code);
- free(err);
- }
-
- wl_event_source_timer_update(output->finish_frame_timer, 10);
- return 0;
-}
-
-static int
-finish_frame_handler(void *data)
-{
- struct x11_output *output = data;
- struct timespec ts;
-
- weston_compositor_read_presentation_clock(output->base.compositor, &ts);
- weston_output_finish_frame(&output->base, &ts, 0);
-
- return 1;
-}
-
-static void
-x11_output_deinit_shm(struct x11_backend *b, struct x11_output *output)
-{
- xcb_void_cookie_t cookie;
- xcb_generic_error_t *err;
- xcb_free_gc(b->conn, output->gc);
-
- pixman_image_unref(output->hw_surface);
- output->hw_surface = NULL;
- cookie = xcb_shm_detach_checked(b->conn, output->segment);
- err = xcb_request_check(b->conn, cookie);
- if (err) {
- weston_log("xcb_shm_detach failed, error %d\n", err->error_code);
- free(err);
- }
- shmdt(output->buf);
-}
-
-static void
-x11_output_destroy(struct weston_output *output_base)
-{
- struct x11_output *output = (struct x11_output *)output_base;
- struct x11_backend *backend =
- (struct x11_backend *)output->base.compositor->backend;
-
- wl_event_source_remove(output->finish_frame_timer);
-
- if (backend->use_pixman) {
- pixman_renderer_output_destroy(output_base);
- x11_output_deinit_shm(backend, output);
- } else
- gl_renderer->output_destroy(output_base);
-
- xcb_destroy_window(backend->conn, output->window);
-
- weston_output_destroy(&output->base);
-
- free(output);
-}
-
-static void
-x11_output_set_wm_protocols(struct x11_backend *b,
- struct x11_output *output)
-{
- xcb_atom_t list[1];
-
- list[0] = b->atom.wm_delete_window;
- xcb_change_property (b->conn,
- XCB_PROP_MODE_REPLACE,
- output->window,
- b->atom.wm_protocols,
- XCB_ATOM_ATOM,
- 32,
- ARRAY_LENGTH(list),
- list);
-}
-
-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_backend *b,
- struct x11_output *output, const char *filename)
-{
- uint32_t *icon;
- int32_t width, height;
- pixman_image_t *image;
-
- image = load_image(filename);
- if (!image)
- return;
- width = pixman_image_get_width(image);
- height = pixman_image_get_height(image);
- icon = malloc(width * height * 4 + 8);
- if (!icon) {
- pixman_image_unref(image);
- return;
- }
-
- icon[0] = width;
- icon[1] = height;
- memcpy(icon + 2, pixman_image_get_data(image), width * height * 4);
- xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window,
- b->atom.net_wm_icon, b->atom.cardinal, 32,
- width * height + 2, icon);
- free(icon);
- pixman_image_unref(image);
-}
-
-static void
-x11_output_wait_for_map(struct x11_backend *b, struct x11_output *output)
-{
- xcb_map_notify_event_t *map_notify;
- xcb_configure_notify_event_t *configure_notify;
- xcb_generic_event_t *event;
- int mapped = 0, configured = 0;
- uint8_t response_type;
-
- /* This isn't the nicest way to do this. Ideally, we could
- * just go back to the main loop and once we get the configure
- * notify, we add the output to the compositor. While we do
- * support output hotplug, we can't start up with no outputs.
- * We could add the output and then resize once we get the
- * configure notify, but we don't want to start up and
- * immediately resize the output.
- *
- * Also, some window managers don't give us our final
- * fullscreen size before map_notify, so if we don't get a
- * configure_notify before map_notify, we just wait for the
- * first one and hope that's our size. */
-
- xcb_flush(b->conn);
-
- while (!mapped || !configured) {
- event = xcb_wait_for_event(b->conn);
- response_type = event->response_type & ~0x80;
-
- switch (response_type) {
- case XCB_MAP_NOTIFY:
- map_notify = (xcb_map_notify_event_t *) event;
- if (map_notify->window == output->window)
- mapped = 1;
- break;
-
- case XCB_CONFIGURE_NOTIFY:
- configure_notify =
- (xcb_configure_notify_event_t *) event;
-
-
- if (configure_notify->width % output->scale != 0 ||
- configure_notify->height % output->scale != 0)
- weston_log("Resolution is not a multiple of screen size, rounding\n");
- output->mode.width = configure_notify->width;
- output->mode.height = configure_notify->height;
- configured = 1;
- break;
- }
- }
-}
-
-static xcb_visualtype_t *
-find_visual_by_id(xcb_screen_t *screen,
- xcb_visualid_t id)
-{
- xcb_depth_iterator_t i;
- xcb_visualtype_iterator_t j;
- for (i = xcb_screen_allowed_depths_iterator(screen);
- i.rem;
- xcb_depth_next(&i)) {
- for (j = xcb_depth_visuals_iterator(i.data);
- j.rem;
- xcb_visualtype_next(&j)) {
- if (j.data->visual_id == id)
- return j.data;
- }
- }
- return 0;
-}
-
-static uint8_t
-get_depth_of_visual(xcb_screen_t *screen,
- xcb_visualid_t id)
-{
- xcb_depth_iterator_t i;
- xcb_visualtype_iterator_t j;
- for (i = xcb_screen_allowed_depths_iterator(screen);
- i.rem;
- xcb_depth_next(&i)) {
- for (j = xcb_depth_visuals_iterator(i.data);
- j.rem;
- xcb_visualtype_next(&j)) {
- if (j.data->visual_id == id)
- return i.data->depth;
- }
- }
- return 0;
-}
-
-static int
-x11_output_init_shm(struct x11_backend *b, struct x11_output *output,
- int width, int height)
-{
- xcb_visualtype_t *visual_type;
- xcb_screen_t *screen;
- xcb_format_iterator_t fmt;
- xcb_void_cookie_t cookie;
- xcb_generic_error_t *err;
- const xcb_query_extension_reply_t *ext;
- int bitsperpixel = 0;
- pixman_format_code_t pixman_format;
-
- /* Check if SHM is available */
- ext = xcb_get_extension_data(b->conn, &xcb_shm_id);
- if (ext == NULL || !ext->present) {
- /* SHM is missing */
- weston_log("SHM extension is not available\n");
- errno = ENOENT;
- return -1;
- }
-
- screen = x11_compositor_get_default_screen(b);
- visual_type = find_visual_by_id(screen, screen->root_visual);
- if (!visual_type) {
- weston_log("Failed to lookup visual for root window\n");
- errno = ENOENT;
- return -1;
- }
- weston_log("Found visual, bits per value: %d, red_mask: %.8x, green_mask: %.8x, blue_mask: %.8x\n",
- visual_type->bits_per_rgb_value,
- visual_type->red_mask,
- visual_type->green_mask,
- visual_type->blue_mask);
- output->depth = get_depth_of_visual(screen, screen->root_visual);
- weston_log("Visual depth is %d\n", output->depth);
-
- for (fmt = xcb_setup_pixmap_formats_iterator(xcb_get_setup(b->conn));
- fmt.rem;
- xcb_format_next(&fmt)) {
- if (fmt.data->depth == output->depth) {
- bitsperpixel = fmt.data->bits_per_pixel;
- break;
- }
- }
- weston_log("Found format for depth %d, bpp: %d\n",
- output->depth, bitsperpixel);
-
- if (bitsperpixel == 32 &&
- visual_type->red_mask == 0xff0000 &&
- visual_type->green_mask == 0x00ff00 &&
- visual_type->blue_mask == 0x0000ff) {
- weston_log("Will use x8r8g8b8 format for SHM surfaces\n");
- pixman_format = PIXMAN_x8r8g8b8;
- } else if (bitsperpixel == 16 &&
- visual_type->red_mask == 0x00f800 &&
- visual_type->green_mask == 0x0007e0 &&
- visual_type->blue_mask == 0x00001f) {
- weston_log("Will use r5g6b5 format for SHM surfaces\n");
- pixman_format = PIXMAN_r5g6b5;
- } else {
- weston_log("Can't find appropriate format for SHM pixmap\n");
- errno = ENOTSUP;
- return -1;
- }
-
-
- /* Create SHM segment and attach it */
- output->shm_id = shmget(IPC_PRIVATE, width * height * (bitsperpixel / 8), IPC_CREAT | S_IRWXU);
- if (output->shm_id == -1) {
- weston_log("x11shm: failed to allocate SHM segment\n");
- return -1;
- }
- output->buf = shmat(output->shm_id, NULL, 0 /* read/write */);
- if (-1 == (long)output->buf) {
- weston_log("x11shm: failed to attach SHM segment\n");
- return -1;
- }
- output->segment = xcb_generate_id(b->conn);
- cookie = xcb_shm_attach_checked(b->conn, output->segment, output->shm_id, 1);
- err = xcb_request_check(b->conn, cookie);
- if (err) {
- weston_log("x11shm: xcb_shm_attach error %d, op code %d, resource id %d\n",
- err->error_code, err->major_code, err->minor_code);
- free(err);
- return -1;
- }
-
- shmctl(output->shm_id, IPC_RMID, NULL);
-
- /* Now create pixman image */
- output->hw_surface = pixman_image_create_bits(pixman_format, width, height, output->buf,
- width * (bitsperpixel / 8));
-
- output->gc = xcb_generate_id(b->conn);
- xcb_create_gc(b->conn, output->gc, output->window, 0, NULL);
-
- return 0;
-}
-
-static struct x11_output *
-x11_backend_create_output(struct x11_backend *b, int x, int y,
- int width, int height, int fullscreen,
- int no_input, char *configured_name,
- uint32_t transform, int32_t scale)
-{
- static const char name[] = "Weston Compositor";
- static const char class[] = "weston-1\0Weston Compositor";
- char title[32];
- struct x11_output *output;
- xcb_screen_t *screen;
- struct wm_normal_hints normal_hints;
- struct wl_event_loop *loop;
- int output_width, output_height, width_mm, height_mm;
- int ret;
- uint32_t mask = XCB_CW_EVENT_MASK | XCB_CW_CURSOR;
- xcb_atom_t atom_list[1];
- uint32_t values[2] = {
- XCB_EVENT_MASK_EXPOSURE |
- XCB_EVENT_MASK_STRUCTURE_NOTIFY,
- 0
- };
-
- output_width = width * scale;
- output_height = height * scale;
-
- if (configured_name)
- sprintf(title, "%s - %s", name, configured_name);
- else
- strcpy(title, name);
-
- if (!no_input)
- values[0] |=
- 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_ENTER_WINDOW |
- XCB_EVENT_MASK_LEAVE_WINDOW |
- XCB_EVENT_MASK_KEYMAP_STATE |
- XCB_EVENT_MASK_FOCUS_CHANGE;
-
- output = zalloc(sizeof *output);
- if (output == NULL) {
- perror("zalloc");
- return NULL;
- }
-
- output->mode.flags =
- WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
-
- output->mode.width = output_width;
- output->mode.height = output_height;
- output->mode.refresh = 60000;
- output->scale = scale;
- wl_list_init(&output->base.mode_list);
- wl_list_insert(&output->base.mode_list, &output->mode.link);
-
- values[1] = b->null_cursor;
- output->window = xcb_generate_id(b->conn);
- screen = x11_compositor_get_default_screen(b);
- xcb_create_window(b->conn,
- XCB_COPY_FROM_PARENT,
- output->window,
- screen->root,
- 0, 0,
- output_width, output_height,
- 0,
- XCB_WINDOW_CLASS_INPUT_OUTPUT,
- screen->root_visual,
- mask, values);
-
- if (fullscreen) {
- atom_list[0] = b->atom.net_wm_state_fullscreen;
- xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE,
- output->window,
- b->atom.net_wm_state,
- XCB_ATOM_ATOM, 32,
- ARRAY_LENGTH(atom_list), atom_list);
- } else {
- /* 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 = output_width;
- normal_hints.min_height = output_height;
- normal_hints.max_width = output_width;
- normal_hints.max_height = output_height;
- xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window,
- b->atom.wm_normal_hints,
- b->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(b->conn, XCB_PROP_MODE_REPLACE, output->window,
- b->atom.net_wm_name, b->atom.utf8_string, 8,
- strlen(title), title);
- xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window,
- b->atom.wm_class, b->atom.string, 8,
- sizeof class, class);
-
- x11_output_set_icon(b, output, DATADIR "/weston/wayland.png");
-
- x11_output_set_wm_protocols(b, output);
-
- xcb_map_window(b->conn, output->window);
-
- if (fullscreen)
- x11_output_wait_for_map(b, output);
-
- output->base.start_repaint_loop = x11_output_start_repaint_loop;
- if (b->use_pixman)
- output->base.repaint = x11_output_repaint_shm;
- else
- output->base.repaint = x11_output_repaint_gl;
- output->base.destroy = x11_output_destroy;
- output->base.assign_planes = NULL;
- output->base.set_backlight = NULL;
- output->base.set_dpms = NULL;
- output->base.switch_mode = NULL;
- output->base.current_mode = &output->mode;
- output->base.make = "weston-X11";
- output->base.model = "none";
-
- if (configured_name)
- output->base.name = strdup(configured_name);
-
- width_mm = width * b->screen->width_in_millimeters /
- b->screen->width_in_pixels;
- height_mm = height * b->screen->height_in_millimeters /
- b->screen->height_in_pixels;
- weston_output_init(&output->base, b->compositor,
- x, y, width_mm, height_mm, transform, scale);
-
- if (b->use_pixman) {
- if (x11_output_init_shm(b, output,
- output->mode.width,
- output->mode.height) < 0) {
- weston_log("Failed to initialize SHM for the X11 output\n");
- return NULL;
- }
- if (pixman_renderer_output_create(&output->base) < 0) {
- weston_log("Failed to create pixman renderer for output\n");
- x11_output_deinit_shm(b, output);
- return NULL;
- }
- } else {
- /* eglCreatePlatformWindowSurfaceEXT takes a Window*
- * but eglCreateWindowSurface takes a Window. */
- Window xid = (Window) output->window;
-
- ret = gl_renderer->output_create(&output->base,
- (EGLNativeWindowType) output->window,
- &xid,
- gl_renderer->opaque_attribs,
- NULL,
- 0);
- if (ret < 0)
- return NULL;
- }
-
- loop = wl_display_get_event_loop(b->compositor->wl_display);
- output->finish_frame_timer =
- wl_event_loop_add_timer(loop, finish_frame_handler, output);
-
- weston_compositor_add_output(b->compositor, &output->base);
-
- weston_log("x11 output %dx%d, window id %d\n",
- width, height, output->window);
-
- return output;
-}
-
-static struct x11_output *
-x11_backend_find_output(struct x11_backend *b, xcb_window_t window)
-{
- struct x11_output *output;
-
- wl_list_for_each(output, &b->compositor->output_list, base.link) {
- if (output->window == window)
- return output;
- }
-
- return NULL;
-}
-
-static void
-x11_backend_delete_window(struct x11_backend *b, xcb_window_t window)
-{
- struct x11_output *output;
-
- output = x11_backend_find_output(b, window);
- if (output)
- x11_output_destroy(&output->base);
-
- xcb_flush(b->conn);
-
- if (wl_list_empty(&b->compositor->output_list))
- weston_compositor_exit(b->compositor);
-}
-
-static void delete_cb(void *data)
-{
- struct window_delete_data *wd = data;
-
- x11_backend_delete_window(wd->backend, wd->window);
- free(wd);
-}
-
-#ifdef HAVE_XCB_XKB
-static void
-update_xkb_state(struct x11_backend *b, xcb_xkb_state_notify_event_t *state)
-{
- struct weston_keyboard *keyboard =
- weston_seat_get_keyboard(&b->core_seat);
-
- xkb_state_update_mask(keyboard->xkb_state.state,
- get_xkb_mod_mask(b, state->baseMods),
- get_xkb_mod_mask(b, state->latchedMods),
- get_xkb_mod_mask(b, state->lockedMods),
- 0,
- 0,
- state->group);
-
- notify_modifiers(&b->core_seat,
- wl_display_next_serial(b->compositor->wl_display));
-}
-#endif
-
-/**
- * This is monumentally unpleasant. If we don't have XCB-XKB bindings,
- * the best we can do (given that XCB also lacks XI2 support), is to take
- * the state from the core key events. Unfortunately that only gives us
- * the effective (i.e. union of depressed/latched/locked) state, and we
- * need the granularity.
- *
- * So we still update the state with every key event we see, but also use
- * the state field from X11 events as a mask so we don't get any stuck
- * modifiers.
- */
-static void
-update_xkb_state_from_core(struct x11_backend *b, uint16_t x11_mask)
-{
- uint32_t mask = get_xkb_mod_mask(b, x11_mask);
- struct weston_keyboard *keyboard
- = weston_seat_get_keyboard(&b->core_seat);
-
- xkb_state_update_mask(keyboard->xkb_state.state,
- keyboard->modifiers.mods_depressed & mask,
- keyboard->modifiers.mods_latched & mask,
- keyboard->modifiers.mods_locked & mask,
- 0,
- 0,
- (x11_mask >> 13) & 3);
- notify_modifiers(&b->core_seat,
- wl_display_next_serial(b->compositor->wl_display));
-}
-
-static void
-x11_backend_deliver_button_event(struct x11_backend *b,
- xcb_generic_event_t *event, int state)
-{
- xcb_button_press_event_t *button_event =
- (xcb_button_press_event_t *) event;
- uint32_t button;
- struct x11_output *output;
- struct weston_pointer_axis_event weston_event;
-
- output = x11_backend_find_output(b, button_event->event);
- if (!output)
- return;
-
- if (state)
- xcb_grab_pointer(b->conn, 0, output->window,
- XCB_EVENT_MASK_BUTTON_PRESS |
- XCB_EVENT_MASK_BUTTON_RELEASE |
- XCB_EVENT_MASK_POINTER_MOTION |
- XCB_EVENT_MASK_ENTER_WINDOW |
- XCB_EVENT_MASK_LEAVE_WINDOW,
- XCB_GRAB_MODE_ASYNC,
- XCB_GRAB_MODE_ASYNC,
- output->window, XCB_CURSOR_NONE,
- button_event->time);
- else
- xcb_ungrab_pointer(b->conn, button_event->time);
-
- if (!b->has_xkb)
- update_xkb_state_from_core(b, button_event->state);
-
- switch (button_event->detail) {
- case 1:
- button = BTN_LEFT;
- break;
- case 2:
- button = BTN_MIDDLE;
- break;
- case 3:
- button = BTN_RIGHT;
- break;
- case 4:
- /* Axis are measured in pixels, but the xcb events are discrete
- * steps. Therefore move the axis by some pixels every step. */
- if (state) {
- weston_event.value = -DEFAULT_AXIS_STEP_DISTANCE;
- weston_event.discrete = -1;
- weston_event.has_discrete = true;
- weston_event.axis =
- WL_POINTER_AXIS_VERTICAL_SCROLL;
- notify_axis(&b->core_seat,
- weston_compositor_get_time(),
- &weston_event);
- notify_pointer_frame(&b->core_seat);
- }
- return;
- case 5:
- if (state) {
- weston_event.value = DEFAULT_AXIS_STEP_DISTANCE;
- weston_event.discrete = 1;
- weston_event.has_discrete = true;
- weston_event.axis =
- WL_POINTER_AXIS_VERTICAL_SCROLL;
- notify_axis(&b->core_seat,
- weston_compositor_get_time(),
- &weston_event);
- notify_pointer_frame(&b->core_seat);
- }
- return;
- case 6:
- if (state) {
- weston_event.value = -DEFAULT_AXIS_STEP_DISTANCE;
- weston_event.discrete = -1;
- weston_event.has_discrete = true;
- weston_event.axis =
- WL_POINTER_AXIS_HORIZONTAL_SCROLL;
- notify_axis(&b->core_seat,
- weston_compositor_get_time(),
- &weston_event);
- notify_pointer_frame(&b->core_seat);
- }
- return;
- case 7:
- if (state) {
- weston_event.value = DEFAULT_AXIS_STEP_DISTANCE;
- weston_event.discrete = 1;
- weston_event.has_discrete = true;
- weston_event.axis =
- WL_POINTER_AXIS_HORIZONTAL_SCROLL;
- notify_axis(&b->core_seat,
- weston_compositor_get_time(),
- &weston_event);
- notify_pointer_frame(&b->core_seat);
- }
- return;
- default:
- button = button_event->detail + BTN_SIDE - 8;
- break;
- }
-
- notify_button(&b->core_seat,
- weston_compositor_get_time(), button,
- state ? WL_POINTER_BUTTON_STATE_PRESSED :
- WL_POINTER_BUTTON_STATE_RELEASED);
- notify_pointer_frame(&b->core_seat);
-}
-
-static void
-x11_backend_deliver_motion_event(struct x11_backend *b,
- xcb_generic_event_t *event)
-{
- struct x11_output *output;
- double x, y;
- struct weston_pointer_motion_event motion_event = { 0 };
- xcb_motion_notify_event_t *motion_notify =
- (xcb_motion_notify_event_t *) event;
-
- if (!b->has_xkb)
- update_xkb_state_from_core(b, motion_notify->state);
- output = x11_backend_find_output(b, motion_notify->event);
- if (!output)
- return;
-
- weston_output_transform_coordinate(&output->base,
- motion_notify->event_x,
- motion_notify->event_y,
- &x, &y);
-
- motion_event = (struct weston_pointer_motion_event) {
- .mask = WESTON_POINTER_MOTION_REL,
- .dx = x - b->prev_x,
- .dy = y - b->prev_y
- };
-
- notify_motion(&b->core_seat, weston_compositor_get_time(),
- &motion_event);
- notify_pointer_frame(&b->core_seat);
-
- b->prev_x = x;
- b->prev_y = y;
-}
-
-static void
-x11_backend_deliver_enter_event(struct x11_backend *b,
- xcb_generic_event_t *event)
-{
- struct x11_output *output;
- double x, y;
-
- xcb_enter_notify_event_t *enter_notify =
- (xcb_enter_notify_event_t *) event;
- if (enter_notify->state >= Button1Mask)
- return;
- if (!b->has_xkb)
- update_xkb_state_from_core(b, enter_notify->state);
- output = x11_backend_find_output(b, enter_notify->event);
- if (!output)
- return;
-
- weston_output_transform_coordinate(&output->base,
- enter_notify->event_x,
- enter_notify->event_y, &x, &y);
-
- notify_pointer_focus(&b->core_seat, &output->base, x, y);
-
- b->prev_x = x;
- b->prev_y = y;
-}
-
-static int
-x11_backend_next_event(struct x11_backend *b,
- xcb_generic_event_t **event, uint32_t mask)
-{
- if (mask & WL_EVENT_READABLE) {
- *event = xcb_poll_for_event(b->conn);
- } else {
-#ifdef HAVE_XCB_POLL_FOR_QUEUED_EVENT
- *event = xcb_poll_for_queued_event(b->conn);
-#else
- *event = xcb_poll_for_event(b->conn);
-#endif
- }
-
- return *event != NULL;
-}
-
-static int
-x11_backend_handle_event(int fd, uint32_t mask, void *data)
-{
- struct x11_backend *b = data;
- struct x11_output *output;
- xcb_generic_event_t *event, *prev;
- xcb_client_message_event_t *client_message;
- 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_expose_event_t *expose;
- xcb_atom_t atom;
- xcb_window_t window;
- uint32_t *k;
- uint32_t i, set;
- uint8_t response_type;
- int count;
-
- prev = NULL;
- count = 0;
- while (x11_backend_next_event(b, &event, mask)) {
- response_type = event->response_type & ~0x80;
-
- switch (prev ? prev->response_type & ~0x80 : 0x80) {
- case XCB_KEY_RELEASE:
- /* Suppress key repeat events; this is only used if we
- * don't have XCB XKB support. */
- key_release = (xcb_key_press_event_t *) prev;
- key_press = (xcb_key_press_event_t *) event;
- if (response_type == 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. */
- update_xkb_state_from_core(b, key_release->state);
- notify_key(&b->core_seat,
- weston_compositor_get_time(),
- key_release->detail - 8,
- WL_KEYBOARD_KEY_STATE_RELEASED,
- STATE_UPDATE_AUTOMATIC);
- free(prev);
- prev = NULL;
- break;
- }
-
- case XCB_FOCUS_IN:
- assert(response_type == XCB_KEYMAP_NOTIFY);
- keymap_notify = (xcb_keymap_notify_event_t *) event;
- b->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(&b->keys, sizeof *k);
- *k = i;
- }
- }
-
- /* Unfortunately the state only comes with the enter
- * event, rather than with the focus event. I'm not
- * sure of the exact semantics around it and whether
- * we can ensure that we get both? */
- notify_keyboard_focus_in(&b->core_seat, &b->keys,
- STATE_UPDATE_AUTOMATIC);
-
- free(prev);
- prev = NULL;
- break;
-
- default:
- /* No previous event held */
- break;
- }
-
- switch (response_type) {
- case XCB_KEY_PRESS:
- key_press = (xcb_key_press_event_t *) event;
- if (!b->has_xkb)
- update_xkb_state_from_core(b, key_press->state);
- notify_key(&b->core_seat,
- weston_compositor_get_time(),
- key_press->detail - 8,
- WL_KEYBOARD_KEY_STATE_PRESSED,
- b->has_xkb ? STATE_UPDATE_NONE :
- STATE_UPDATE_AUTOMATIC);
- break;
- case XCB_KEY_RELEASE:
- /* If we don't have XKB, we need to use the lame
- * autorepeat detection above. */
- if (!b->has_xkb) {
- prev = event;
- break;
- }
- key_release = (xcb_key_press_event_t *) event;
- notify_key(&b->core_seat,
- weston_compositor_get_time(),
- key_release->detail - 8,
- WL_KEYBOARD_KEY_STATE_RELEASED,
- STATE_UPDATE_NONE);
- break;
- case XCB_BUTTON_PRESS:
- x11_backend_deliver_button_event(b, event, 1);
- break;
- case XCB_BUTTON_RELEASE:
- x11_backend_deliver_button_event(b, event, 0);
- break;
- case XCB_MOTION_NOTIFY:
- x11_backend_deliver_motion_event(b, event);
- break;
-
- case XCB_EXPOSE:
- expose = (xcb_expose_event_t *) event;
- output = x11_backend_find_output(b, expose->window);
- if (!output)
- break;
-
- weston_output_damage(&output->base);
- weston_output_schedule_repaint(&output->base);
- break;
-
- case XCB_ENTER_NOTIFY:
- x11_backend_deliver_enter_event(b, event);
- break;
-
- case XCB_LEAVE_NOTIFY:
- enter_notify = (xcb_enter_notify_event_t *) event;
- if (enter_notify->state >= Button1Mask)
- break;
- if (!b->has_xkb)
- update_xkb_state_from_core(b, enter_notify->state);
- notify_pointer_focus(&b->core_seat, NULL, 0, 0);
- break;
-
- case XCB_CLIENT_MESSAGE:
- client_message = (xcb_client_message_event_t *) event;
- atom = client_message->data.data32[0];
- window = client_message->window;
- if (atom == b->atom.wm_delete_window) {
- struct wl_event_loop *loop;
- struct window_delete_data *data = malloc(sizeof *data);
-
- /* if malloc failed we should at least try to
- * delete the window, even if it may result in
- * a crash.
- */
- if (!data) {
- x11_backend_delete_window(b, window);
- break;
- }
- data->backend = b;
- data->window = window;
- loop = wl_display_get_event_loop(b->compositor->wl_display);
- wl_event_loop_add_idle(loop, delete_cb, data);
- }
- 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_out(&b->core_seat);
- break;
-
- default:
- break;
- }
-
-#ifdef HAVE_XCB_XKB
- if (b->has_xkb) {
- if (response_type == b->xkb_event_base) {
- xcb_xkb_state_notify_event_t *state =
- (xcb_xkb_state_notify_event_t *) event;
- if (state->xkbType == XCB_XKB_STATE_NOTIFY)
- update_xkb_state(b, state);
- } else if (response_type == XCB_PROPERTY_NOTIFY) {
- xcb_property_notify_event_t *prop_notify =
- (xcb_property_notify_event_t *) event;
- if (prop_notify->window == b->screen->root &&
- prop_notify->atom == b->atom.xkb_names &&
- prop_notify->state == XCB_PROPERTY_NEW_VALUE)
- update_xkb_keymap(b);
- }
- }
-#endif
-
- count++;
- if (prev != event)
- free (event);
- }
-
- switch (prev ? prev->response_type & ~0x80 : 0x80) {
- case XCB_KEY_RELEASE:
- key_release = (xcb_key_press_event_t *) prev;
- update_xkb_state_from_core(b, key_release->state);
- notify_key(&b->core_seat,
- weston_compositor_get_time(),
- key_release->detail - 8,
- WL_KEYBOARD_KEY_STATE_RELEASED,
- STATE_UPDATE_AUTOMATIC);
- free(prev);
- prev = NULL;
- break;
- default:
- break;
- }
-
- return count;
-}
-
-#define F(field) offsetof(struct x11_backend, field)
-
-static void
-x11_backend_get_resources(struct x11_backend *b)
-{
- 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) },
- { "_NET_SUPPORTING_WM_CHECK",
- F(atom.net_supporting_wm_check) },
- { "_NET_SUPPORTED", F(atom.net_supported) },
- { "STRING", F(atom.string) },
- { "UTF8_STRING", F(atom.utf8_string) },
- { "CARDINAL", F(atom.cardinal) },
- { "_XKB_RULES_NAMES", F(atom.xkb_names) },
- };
-
- xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
- xcb_intern_atom_reply_t *reply;
- xcb_pixmap_t pixmap;
- xcb_gc_t gc;
- unsigned int i;
- uint8_t data[] = { 0, 0, 0, 0 };
-
- for (i = 0; i < ARRAY_LENGTH(atoms); i++)
- cookies[i] = xcb_intern_atom (b->conn, 0,
- strlen(atoms[i].name),
- atoms[i].name);
-
- for (i = 0; i < ARRAY_LENGTH(atoms); i++) {
- reply = xcb_intern_atom_reply (b->conn, cookies[i], NULL);
- *(xcb_atom_t *) ((char *) b + atoms[i].offset) = reply->atom;
- free(reply);
- }
-
- pixmap = xcb_generate_id(b->conn);
- gc = xcb_generate_id(b->conn);
- xcb_create_pixmap(b->conn, 1, pixmap, b->screen->root, 1, 1);
- xcb_create_gc(b->conn, gc, pixmap, 0, NULL);
- xcb_put_image(b->conn, XCB_IMAGE_FORMAT_XY_PIXMAP,
- pixmap, gc, 1, 1, 0, 0, 0, 32, sizeof data, data);
- b->null_cursor = xcb_generate_id(b->conn);
- xcb_create_cursor (b->conn, b->null_cursor,
- pixmap, pixmap, 0, 0, 0, 0, 0, 0, 1, 1);
- xcb_free_gc(b->conn, gc);
- xcb_free_pixmap(b->conn, pixmap);
-}
-
-static void
-x11_backend_get_wm_info(struct x11_backend *c)
-{
- xcb_get_property_cookie_t cookie;
- xcb_get_property_reply_t *reply;
- xcb_atom_t *atom;
- unsigned int i;
-
- cookie = xcb_get_property(c->conn, 0, c->screen->root,
- c->atom.net_supported,
- XCB_ATOM_ATOM, 0, 1024);
- reply = xcb_get_property_reply(c->conn, cookie, NULL);
- if (reply == NULL)
- return;
-
- atom = (xcb_atom_t *) xcb_get_property_value(reply);
-
- for (i = 0; i < reply->value_len; i++) {
- if (atom[i] == c->atom.net_wm_state_fullscreen)
- c->has_net_wm_state_fullscreen = 1;
- }
-
- free(reply);
-}
-
-static void
-x11_restore(struct weston_compositor *ec)
-{
-}
-
-static void
-x11_destroy(struct weston_compositor *ec)
-{
- struct x11_backend *backend = (struct x11_backend *)ec->backend;
-
- wl_event_source_remove(backend->xcb_source);
- x11_input_destroy(backend);
-
- weston_compositor_shutdown(ec); /* destroys outputs, too */
-
- XCloseDisplay(backend->dpy);
- free(backend);
-}
-
-static int
-init_gl_renderer(struct x11_backend *b)
-{
- int ret;
-
- gl_renderer = weston_load_module("gl-renderer.so",
- "gl_renderer_interface");
- if (!gl_renderer)
- return -1;
-
- ret = gl_renderer->create(b->compositor, EGL_PLATFORM_X11_KHR, (void *) b->dpy,
- gl_renderer->opaque_attribs, NULL, 0);
-
- return ret;
-}
-
-static struct x11_backend *
-x11_backend_create(struct weston_compositor *compositor,
- struct weston_x11_backend_config *config)
-{
- struct x11_backend *b;
- struct x11_output *output;
- struct wl_event_loop *loop;
- int x = 0;
- unsigned i;
-
- b = zalloc(sizeof *b);
- if (b == NULL)
- return NULL;
-
- b->compositor = compositor;
- if (weston_compositor_set_presentation_clock_software(compositor) < 0)
- goto err_free;
-
- b->dpy = XOpenDisplay(NULL);
- if (b->dpy == NULL)
- goto err_free;
-
- b->conn = XGetXCBConnection(b->dpy);
- XSetEventQueueOwner(b->dpy, XCBOwnsEventQueue);
-
- if (xcb_connection_has_error(b->conn))
- goto err_xdisplay;
-
- b->screen = x11_compositor_get_default_screen(b);
- wl_array_init(&b->keys);
-
- x11_backend_get_resources(b);
- x11_backend_get_wm_info(b);
-
- if (!b->has_net_wm_state_fullscreen && config->fullscreen) {
- weston_log("Can not fullscreen without window manager support"
- "(need _NET_WM_STATE_FULLSCREEN)\n");
- config->fullscreen = 0;
- }
-
- b->use_pixman = config->use_pixman;
- if (b->use_pixman) {
- if (pixman_renderer_init(compositor) < 0) {
- weston_log("Failed to initialize pixman renderer for X11 backend\n");
- goto err_xdisplay;
- }
- }
- else if (init_gl_renderer(b) < 0) {
- goto err_xdisplay;
- }
- weston_log("Using %s renderer\n", config->use_pixman ? "pixman" : "gl");
-
- b->base.destroy = x11_destroy;
- b->base.restore = x11_restore;
-
- if (x11_input_create(b, config->no_input) < 0) {
- weston_log("Failed to create X11 input\n");
- goto err_renderer;
- }
-
- for (i = 0; i < config->num_outputs; ++i) {
- struct weston_x11_backend_output_config *output_iterator =
- &config->outputs[i];
-
- if (output_iterator->name == NULL) {
- continue;
- }
-
- if (output_iterator->width < 1) {
- weston_log("Invalid width \"%d\" for output %s\n",
- output_iterator->width, output_iterator->name);
- goto err_x11_input;
- }
-
- if (output_iterator->height < 1) {
- weston_log("Invalid height \"%d\" for output %s\n",
- output_iterator->height, output_iterator->name);
- goto err_x11_input;
- }
-
- output = x11_backend_create_output(b,
- x,
- 0,
- output_iterator->width,
- output_iterator->height,
- config->fullscreen,
- config->no_input,
- output_iterator->name,
- output_iterator->transform,
- output_iterator->scale);
- if (output == NULL) {
- weston_log("Failed to create configured x11 output\n");
- goto err_x11_input;
- }
-
- x = pixman_region32_extents(&output->base.region)->x2;
- }
-
- loop = wl_display_get_event_loop(compositor->wl_display);
- b->xcb_source =
- wl_event_loop_add_fd(loop,
- xcb_get_file_descriptor(b->conn),
- WL_EVENT_READABLE,
- x11_backend_handle_event, b);
- wl_event_source_check(b->xcb_source);
-
- if (compositor->renderer->import_dmabuf) {
- if (linux_dmabuf_setup(compositor) < 0)
- weston_log("Error: initializing dmabuf "
- "support failed.\n");
- }
-
- compositor->backend = &b->base;
-
- return b;
-
-err_x11_input:
- x11_input_destroy(b);
-err_renderer:
- compositor->renderer->destroy(compositor);
-err_xdisplay:
- XCloseDisplay(b->dpy);
-err_free:
- free(b);
- return NULL;
-}
-
-static void
-config_init_to_defaults(struct weston_x11_backend_config *config)
-{
-}
-
-WL_EXPORT int
-backend_init(struct weston_compositor *compositor,
- struct weston_backend_config *config_base)
-{
- struct x11_backend *b;
- struct weston_x11_backend_config config = {{ 0, }};
-
- if (config_base == NULL ||
- config_base->struct_version != WESTON_X11_BACKEND_CONFIG_VERSION ||
- config_base->struct_size > sizeof(struct weston_x11_backend_config)) {
- weston_log("X11 backend config structure is invalid\n");
- return -1;
- }
-
- config_init_to_defaults(&config);
- memcpy(&config, config_base, config_base->struct_size);
-
- b = x11_backend_create(compositor, &config);
- if (b == NULL)
- return -1;
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright © 2016 Benoit Gschwind
- *
- * 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.
- */
-
-#ifndef WESTON_COMPOSITOR_X11_H
-#define WESTON_COMPOSITOR_X11_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "compositor.h"
-
-#define WESTON_X11_BACKEND_CONFIG_VERSION 1
-
-struct weston_x11_backend_output_config {
- int width;
- int height;
- char *name;
- uint32_t transform;
- int32_t scale;
-};
-
-struct weston_x11_backend_config {
- struct weston_backend_config base;
-
- bool fullscreen;
- bool no_input;
-
- /** Whether to use the pixman renderer instead of the OpenGL ES renderer. */
- bool use_pixman;
-
- uint32_t num_outputs;
- struct weston_x11_backend_output_config *outputs;
-};
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* WESTON_COMPOSITOR_X11_H_ */
+++ /dev/null
-/*
- * Copyright © 2010-2011 Intel Corporation
- * Copyright © 2008-2011 Kristian Høgsberg
- * Copyright © 2012-2015 Collabora, Ltd.
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <fcntl.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/mman.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/utsname.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <math.h>
-#include <linux/input.h>
-#include <dlfcn.h>
-#include <signal.h>
-#include <setjmp.h>
-#include <sys/time.h>
-#include <time.h>
-#include <errno.h>
-
-#include "timeline.h"
-
-#include "compositor.h"
-#include "viewporter-server-protocol.h"
-#include "presentation-time-server-protocol.h"
-#include "shared/helpers.h"
-#include "shared/os-compatibility.h"
-#include "shared/timespec-util.h"
-#include "git-version.h"
-#include "version.h"
-
-#define DEFAULT_REPAINT_WINDOW 7 /* milliseconds */
-
-static void
-weston_output_transform_scale_init(struct weston_output *output,
- uint32_t transform, uint32_t scale);
-
-static void
-weston_compositor_build_view_list(struct weston_compositor *compositor);
-
-static void weston_mode_switch_finish(struct weston_output *output,
- int mode_changed,
- int scale_changed)
-{
- struct weston_seat *seat;
- struct wl_resource *resource;
- pixman_region32_t old_output_region;
- int version;
-
- pixman_region32_init(&old_output_region);
- pixman_region32_copy(&old_output_region, &output->region);
-
- /* Update output region and transformation matrix */
- weston_output_transform_scale_init(output, output->transform, output->current_scale);
-
- pixman_region32_init(&output->previous_damage);
- pixman_region32_init_rect(&output->region, output->x, output->y,
- output->width, output->height);
-
- weston_output_update_matrix(output);
-
- /* If a pointer falls outside the outputs new geometry, move it to its
- * lower-right corner */
- wl_list_for_each(seat, &output->compositor->seat_list, link) {
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
- int32_t x, y;
-
- if (!pointer)
- continue;
-
- x = wl_fixed_to_int(pointer->x);
- y = wl_fixed_to_int(pointer->y);
-
- if (!pixman_region32_contains_point(&old_output_region,
- x, y, NULL) ||
- pixman_region32_contains_point(&output->region,
- x, y, NULL))
- continue;
-
- if (x >= output->x + output->width)
- x = output->x + output->width - 1;
- if (y >= output->y + output->height)
- y = output->y + output->height - 1;
-
- pointer->x = wl_fixed_from_int(x);
- pointer->y = wl_fixed_from_int(y);
- }
-
- pixman_region32_fini(&old_output_region);
-
- if (!mode_changed && !scale_changed)
- return;
-
- /* notify clients of the changes */
- wl_resource_for_each(resource, &output->resource_list) {
- if (mode_changed) {
- wl_output_send_mode(resource,
- output->current_mode->flags,
- output->current_mode->width,
- output->current_mode->height,
- output->current_mode->refresh);
- }
-
- version = wl_resource_get_version(resource);
- if (version >= WL_OUTPUT_SCALE_SINCE_VERSION && scale_changed)
- wl_output_send_scale(resource, output->current_scale);
-
- if (version >= WL_OUTPUT_DONE_SINCE_VERSION)
- wl_output_send_done(resource);
- }
-}
-
-
-static void
-weston_compositor_reflow_outputs(struct weston_compositor *compositor,
- struct weston_output *resized_output, int delta_width);
-
-WL_EXPORT int
-weston_output_mode_set_native(struct weston_output *output,
- struct weston_mode *mode,
- int32_t scale)
-{
- int ret;
- int mode_changed = 0, scale_changed = 0;
- int32_t old_width;
-
- if (!output->switch_mode)
- return -1;
-
- if (!output->original_mode) {
- mode_changed = 1;
- ret = output->switch_mode(output, mode);
- if (ret < 0)
- return ret;
- if (output->current_scale != scale) {
- scale_changed = 1;
- output->current_scale = scale;
- }
- }
-
- old_width = output->width;
- output->native_mode = mode;
- output->native_scale = scale;
-
- weston_mode_switch_finish(output, mode_changed, scale_changed);
-
- if (mode_changed || scale_changed) {
- weston_compositor_reflow_outputs(output->compositor, output, output->width - old_width);
-
- wl_signal_emit(&output->compositor->output_resized_signal, output);
- }
- return 0;
-}
-
-WL_EXPORT int
-weston_output_mode_switch_to_native(struct weston_output *output)
-{
- int ret;
- int mode_changed = 0, scale_changed = 0;
-
- if (!output->switch_mode)
- return -1;
-
- if (!output->original_mode) {
- weston_log("already in the native mode\n");
- return -1;
- }
- /* the non fullscreen clients haven't seen a mode set since we
- * switched into a temporary, so we need to notify them if the
- * mode at that time is different from the native mode now.
- */
- mode_changed = (output->original_mode != output->native_mode);
- scale_changed = (output->original_scale != output->native_scale);
-
- ret = output->switch_mode(output, output->native_mode);
- if (ret < 0)
- return ret;
-
- output->current_scale = output->native_scale;
-
- output->original_mode = NULL;
- output->original_scale = 0;
-
- weston_mode_switch_finish(output, mode_changed, scale_changed);
-
- return 0;
-}
-
-WL_EXPORT int
-weston_output_mode_switch_to_temporary(struct weston_output *output,
- struct weston_mode *mode,
- int32_t scale)
-{
- int ret;
-
- if (!output->switch_mode)
- return -1;
-
- /* original_mode is the last mode non full screen clients have seen,
- * so we shouldn't change it if we already have one set.
- */
- if (!output->original_mode) {
- output->original_mode = output->native_mode;
- output->original_scale = output->native_scale;
- }
- ret = output->switch_mode(output, mode);
- if (ret < 0)
- return ret;
-
- output->current_scale = scale;
-
- weston_mode_switch_finish(output, 0, 0);
-
- return 0;
-}
-
-static void
-region_init_infinite(pixman_region32_t *region)
-{
- pixman_region32_init_rect(region, INT32_MIN, INT32_MIN,
- UINT32_MAX, UINT32_MAX);
-}
-
-static struct weston_subsurface *
-weston_surface_to_subsurface(struct weston_surface *surface);
-
-WL_EXPORT struct weston_view *
-weston_view_create(struct weston_surface *surface)
-{
- struct weston_view *view;
-
- view = zalloc(sizeof *view);
- if (view == NULL)
- return NULL;
-
- view->surface = surface;
-
- /* Assign to surface */
- wl_list_insert(&surface->views, &view->surface_link);
-
- wl_signal_init(&view->destroy_signal);
- wl_list_init(&view->link);
- wl_list_init(&view->layer_link.link);
-
- pixman_region32_init(&view->clip);
-
- view->alpha = 1.0;
- pixman_region32_init(&view->transform.opaque);
-
- wl_list_init(&view->geometry.transformation_list);
- wl_list_insert(&view->geometry.transformation_list,
- &view->transform.position.link);
- weston_matrix_init(&view->transform.position.matrix);
- wl_list_init(&view->geometry.child_list);
- pixman_region32_init(&view->geometry.scissor);
- pixman_region32_init(&view->transform.boundingbox);
- view->transform.dirty = 1;
-
- return view;
-}
-
-struct weston_frame_callback {
- struct wl_resource *resource;
- struct wl_list link;
-};
-
-struct weston_presentation_feedback {
- struct wl_resource *resource;
-
- /* XXX: could use just wl_resource_get_link() instead */
- struct wl_list link;
-
- /* The per-surface feedback flags */
- uint32_t psf_flags;
-};
-
-static void
-weston_presentation_feedback_discard(
- struct weston_presentation_feedback *feedback)
-{
- wp_presentation_feedback_send_discarded(feedback->resource);
- wl_resource_destroy(feedback->resource);
-}
-
-static void
-weston_presentation_feedback_discard_list(struct wl_list *list)
-{
- struct weston_presentation_feedback *feedback, *tmp;
-
- wl_list_for_each_safe(feedback, tmp, list, link)
- weston_presentation_feedback_discard(feedback);
-}
-
-static void
-weston_presentation_feedback_present(
- struct weston_presentation_feedback *feedback,
- struct weston_output *output,
- uint32_t refresh_nsec,
- const struct timespec *ts,
- uint64_t seq,
- uint32_t flags)
-{
- struct wl_client *client = wl_resource_get_client(feedback->resource);
- struct wl_resource *o;
- uint64_t secs;
-
- wl_resource_for_each(o, &output->resource_list) {
- if (wl_resource_get_client(o) != client)
- continue;
-
- wp_presentation_feedback_send_sync_output(feedback->resource, o);
- }
-
- secs = ts->tv_sec;
- wp_presentation_feedback_send_presented(feedback->resource,
- secs >> 32, secs & 0xffffffff,
- ts->tv_nsec,
- refresh_nsec,
- seq >> 32, seq & 0xffffffff,
- flags | feedback->psf_flags);
- wl_resource_destroy(feedback->resource);
-}
-
-static void
-weston_presentation_feedback_present_list(struct wl_list *list,
- struct weston_output *output,
- uint32_t refresh_nsec,
- const struct timespec *ts,
- uint64_t seq,
- uint32_t flags)
-{
- struct weston_presentation_feedback *feedback, *tmp;
-
- assert(!(flags & WP_PRESENTATION_FEEDBACK_INVALID) ||
- wl_list_empty(list));
-
- wl_list_for_each_safe(feedback, tmp, list, link)
- weston_presentation_feedback_present(feedback, output,
- refresh_nsec, ts, seq,
- flags);
-}
-
-static void
-surface_state_handle_buffer_destroy(struct wl_listener *listener, void *data)
-{
- struct weston_surface_state *state =
- container_of(listener, struct weston_surface_state,
- buffer_destroy_listener);
-
- state->buffer = NULL;
-}
-
-static void
-weston_surface_state_init(struct weston_surface_state *state)
-{
- state->newly_attached = 0;
- state->buffer = NULL;
- state->buffer_destroy_listener.notify =
- surface_state_handle_buffer_destroy;
- state->sx = 0;
- state->sy = 0;
-
- pixman_region32_init(&state->damage_surface);
- pixman_region32_init(&state->damage_buffer);
- pixman_region32_init(&state->opaque);
- region_init_infinite(&state->input);
-
- wl_list_init(&state->frame_callback_list);
- wl_list_init(&state->feedback_list);
-
- state->buffer_viewport.buffer.transform = WL_OUTPUT_TRANSFORM_NORMAL;
- state->buffer_viewport.buffer.scale = 1;
- state->buffer_viewport.buffer.src_width = wl_fixed_from_int(-1);
- state->buffer_viewport.surface.width = -1;
- state->buffer_viewport.changed = 0;
-}
-
-static void
-weston_surface_state_fini(struct weston_surface_state *state)
-{
- struct weston_frame_callback *cb, *next;
-
- wl_list_for_each_safe(cb, next,
- &state->frame_callback_list, link)
- wl_resource_destroy(cb->resource);
-
- weston_presentation_feedback_discard_list(&state->feedback_list);
-
- pixman_region32_fini(&state->input);
- pixman_region32_fini(&state->opaque);
- pixman_region32_fini(&state->damage_surface);
- pixman_region32_fini(&state->damage_buffer);
-
- if (state->buffer)
- wl_list_remove(&state->buffer_destroy_listener.link);
- state->buffer = NULL;
-}
-
-static void
-weston_surface_state_set_buffer(struct weston_surface_state *state,
- struct weston_buffer *buffer)
-{
- if (state->buffer == buffer)
- return;
-
- if (state->buffer)
- wl_list_remove(&state->buffer_destroy_listener.link);
- state->buffer = buffer;
- if (state->buffer)
- wl_signal_add(&state->buffer->destroy_signal,
- &state->buffer_destroy_listener);
-}
-
-WL_EXPORT struct weston_surface *
-weston_surface_create(struct weston_compositor *compositor)
-{
- struct weston_surface *surface;
-
- surface = zalloc(sizeof *surface);
- if (surface == NULL)
- return NULL;
-
- wl_signal_init(&surface->destroy_signal);
-
- surface->compositor = compositor;
- surface->ref_count = 1;
-
- surface->buffer_viewport.buffer.transform = WL_OUTPUT_TRANSFORM_NORMAL;
- surface->buffer_viewport.buffer.scale = 1;
- surface->buffer_viewport.buffer.src_width = wl_fixed_from_int(-1);
- surface->buffer_viewport.surface.width = -1;
-
- weston_surface_state_init(&surface->pending);
-
- pixman_region32_init(&surface->damage);
- pixman_region32_init(&surface->opaque);
- region_init_infinite(&surface->input);
-
- wl_list_init(&surface->views);
-
- wl_list_init(&surface->frame_callback_list);
- wl_list_init(&surface->feedback_list);
-
- wl_list_init(&surface->subsurface_list);
- wl_list_init(&surface->subsurface_list_pending);
-
- weston_matrix_init(&surface->buffer_to_surface_matrix);
- weston_matrix_init(&surface->surface_to_buffer_matrix);
-
- return surface;
-}
-
-WL_EXPORT void
-weston_surface_set_color(struct weston_surface *surface,
- float red, float green, float blue, float alpha)
-{
- surface->compositor->renderer->surface_set_color(surface, red, green, blue, alpha);
-}
-
-WL_EXPORT void
-weston_view_to_global_float(struct weston_view *view,
- float sx, float sy, float *x, float *y)
-{
- if (view->transform.enabled) {
- struct weston_vector v = { { sx, sy, 0.0f, 1.0f } };
-
- weston_matrix_transform(&view->transform.matrix, &v);
-
- if (fabsf(v.f[3]) < 1e-6) {
- weston_log("warning: numerical instability in "
- "%s(), divisor = %g\n", __func__,
- v.f[3]);
- *x = 0;
- *y = 0;
- return;
- }
-
- *x = v.f[0] / v.f[3];
- *y = v.f[1] / v.f[3];
- } else {
- *x = sx + view->geometry.x;
- *y = sy + view->geometry.y;
- }
-}
-
-WL_EXPORT void
-weston_transformed_coord(int width, int height,
- enum wl_output_transform transform,
- int32_t scale,
- float sx, float sy, float *bx, float *by)
-{
- switch (transform) {
- case WL_OUTPUT_TRANSFORM_NORMAL:
- default:
- *bx = sx;
- *by = sy;
- break;
- case WL_OUTPUT_TRANSFORM_FLIPPED:
- *bx = width - sx;
- *by = sy;
- break;
- case WL_OUTPUT_TRANSFORM_90:
- *bx = height - sy;
- *by = sx;
- break;
- case WL_OUTPUT_TRANSFORM_FLIPPED_90:
- *bx = height - sy;
- *by = width - sx;
- break;
- case WL_OUTPUT_TRANSFORM_180:
- *bx = width - sx;
- *by = height - sy;
- break;
- case WL_OUTPUT_TRANSFORM_FLIPPED_180:
- *bx = sx;
- *by = height - sy;
- break;
- case WL_OUTPUT_TRANSFORM_270:
- *bx = sy;
- *by = width - sx;
- break;
- case WL_OUTPUT_TRANSFORM_FLIPPED_270:
- *bx = sy;
- *by = sx;
- break;
- }
-
- *bx *= scale;
- *by *= scale;
-}
-
-WL_EXPORT pixman_box32_t
-weston_transformed_rect(int width, int height,
- enum wl_output_transform transform,
- int32_t scale,
- pixman_box32_t rect)
-{
- float x1, x2, y1, y2;
-
- pixman_box32_t ret;
-
- weston_transformed_coord(width, height, transform, scale,
- rect.x1, rect.y1, &x1, &y1);
- weston_transformed_coord(width, height, transform, scale,
- rect.x2, rect.y2, &x2, &y2);
-
- if (x1 <= x2) {
- ret.x1 = x1;
- ret.x2 = x2;
- } else {
- ret.x1 = x2;
- ret.x2 = x1;
- }
-
- if (y1 <= y2) {
- ret.y1 = y1;
- ret.y2 = y2;
- } else {
- ret.y1 = y2;
- ret.y2 = y1;
- }
-
- return ret;
-}
-
-/** Transform a region by a matrix, restricted to axis-aligned transformations
- *
- * Warning: This function does not work for projective, affine, or matrices
- * that encode arbitrary rotations. Only 90-degree step rotations are
- * supported.
- */
-WL_EXPORT void
-weston_matrix_transform_region(pixman_region32_t *dest,
- struct weston_matrix *matrix,
- pixman_region32_t *src)
-{
- pixman_box32_t *src_rects, *dest_rects;
- int nrects, i;
-
- src_rects = pixman_region32_rectangles(src, &nrects);
- dest_rects = malloc(nrects * sizeof(*dest_rects));
- if (!dest_rects)
- return;
-
- for (i = 0; i < nrects; i++) {
- struct weston_vector vec1 = {{
- src_rects[i].x1, src_rects[i].y1, 0, 1
- }};
- weston_matrix_transform(matrix, &vec1);
- vec1.f[0] /= vec1.f[3];
- vec1.f[1] /= vec1.f[3];
-
- struct weston_vector vec2 = {{
- src_rects[i].x2, src_rects[i].y2, 0, 1
- }};
- weston_matrix_transform(matrix, &vec2);
- vec2.f[0] /= vec2.f[3];
- vec2.f[1] /= vec2.f[3];
-
- if (vec1.f[0] < vec2.f[0]) {
- dest_rects[i].x1 = floor(vec1.f[0]);
- dest_rects[i].x2 = ceil(vec2.f[0]);
- } else {
- dest_rects[i].x1 = floor(vec2.f[0]);
- dest_rects[i].x2 = ceil(vec1.f[0]);
- }
-
- if (vec1.f[1] < vec2.f[1]) {
- dest_rects[i].y1 = floor(vec1.f[1]);
- dest_rects[i].y2 = ceil(vec2.f[1]);
- } else {
- dest_rects[i].y1 = floor(vec2.f[1]);
- dest_rects[i].y2 = ceil(vec1.f[1]);
- }
- }
-
- pixman_region32_clear(dest);
- pixman_region32_init_rects(dest, dest_rects, nrects);
- free(dest_rects);
-}
-
-WL_EXPORT void
-weston_transformed_region(int width, int height,
- enum wl_output_transform transform,
- int32_t scale,
- pixman_region32_t *src, pixman_region32_t *dest)
-{
- pixman_box32_t *src_rects, *dest_rects;
- int nrects, i;
-
- if (transform == WL_OUTPUT_TRANSFORM_NORMAL && scale == 1) {
- if (src != dest)
- pixman_region32_copy(dest, src);
- return;
- }
-
- src_rects = pixman_region32_rectangles(src, &nrects);
- dest_rects = malloc(nrects * sizeof(*dest_rects));
- if (!dest_rects)
- return;
-
- if (transform == WL_OUTPUT_TRANSFORM_NORMAL) {
- memcpy(dest_rects, src_rects, nrects * sizeof(*dest_rects));
- } else {
- for (i = 0; i < nrects; i++) {
- switch (transform) {
- default:
- case WL_OUTPUT_TRANSFORM_NORMAL:
- dest_rects[i].x1 = src_rects[i].x1;
- dest_rects[i].y1 = src_rects[i].y1;
- dest_rects[i].x2 = src_rects[i].x2;
- dest_rects[i].y2 = src_rects[i].y2;
- break;
- case WL_OUTPUT_TRANSFORM_90:
- dest_rects[i].x1 = height - src_rects[i].y2;
- dest_rects[i].y1 = src_rects[i].x1;
- dest_rects[i].x2 = height - src_rects[i].y1;
- dest_rects[i].y2 = src_rects[i].x2;
- break;
- case WL_OUTPUT_TRANSFORM_180:
- dest_rects[i].x1 = width - src_rects[i].x2;
- dest_rects[i].y1 = height - src_rects[i].y2;
- dest_rects[i].x2 = width - src_rects[i].x1;
- dest_rects[i].y2 = height - src_rects[i].y1;
- break;
- case WL_OUTPUT_TRANSFORM_270:
- dest_rects[i].x1 = src_rects[i].y1;
- dest_rects[i].y1 = width - src_rects[i].x2;
- dest_rects[i].x2 = src_rects[i].y2;
- dest_rects[i].y2 = width - src_rects[i].x1;
- break;
- case WL_OUTPUT_TRANSFORM_FLIPPED:
- dest_rects[i].x1 = width - src_rects[i].x2;
- dest_rects[i].y1 = src_rects[i].y1;
- dest_rects[i].x2 = width - src_rects[i].x1;
- dest_rects[i].y2 = src_rects[i].y2;
- break;
- case WL_OUTPUT_TRANSFORM_FLIPPED_90:
- dest_rects[i].x1 = height - src_rects[i].y2;
- dest_rects[i].y1 = width - src_rects[i].x2;
- dest_rects[i].x2 = height - src_rects[i].y1;
- dest_rects[i].y2 = width - src_rects[i].x1;
- break;
- case WL_OUTPUT_TRANSFORM_FLIPPED_180:
- dest_rects[i].x1 = src_rects[i].x1;
- dest_rects[i].y1 = height - src_rects[i].y2;
- dest_rects[i].x2 = src_rects[i].x2;
- dest_rects[i].y2 = height - src_rects[i].y1;
- break;
- case WL_OUTPUT_TRANSFORM_FLIPPED_270:
- dest_rects[i].x1 = src_rects[i].y1;
- dest_rects[i].y1 = src_rects[i].x1;
- dest_rects[i].x2 = src_rects[i].y2;
- dest_rects[i].y2 = src_rects[i].x2;
- break;
- }
- }
- }
-
- if (scale != 1) {
- for (i = 0; i < nrects; i++) {
- dest_rects[i].x1 *= scale;
- dest_rects[i].x2 *= scale;
- dest_rects[i].y1 *= scale;
- dest_rects[i].y2 *= scale;
- }
- }
-
- pixman_region32_clear(dest);
- pixman_region32_init_rects(dest, dest_rects, nrects);
- free(dest_rects);
-}
-
-static void
-viewport_surface_to_buffer(struct weston_surface *surface,
- float sx, float sy, float *bx, float *by)
-{
- struct weston_buffer_viewport *vp = &surface->buffer_viewport;
- double src_width, src_height;
- double src_x, src_y;
-
- if (vp->buffer.src_width == wl_fixed_from_int(-1)) {
- if (vp->surface.width == -1) {
- *bx = sx;
- *by = sy;
- return;
- }
-
- src_x = 0.0;
- src_y = 0.0;
- src_width = surface->width_from_buffer;
- src_height = surface->height_from_buffer;
- } else {
- src_x = wl_fixed_to_double(vp->buffer.src_x);
- src_y = wl_fixed_to_double(vp->buffer.src_y);
- src_width = wl_fixed_to_double(vp->buffer.src_width);
- src_height = wl_fixed_to_double(vp->buffer.src_height);
- }
-
- *bx = sx * src_width / surface->width + src_x;
- *by = sy * src_height / surface->height + src_y;
-}
-
-WL_EXPORT void
-weston_surface_to_buffer_float(struct weston_surface *surface,
- float sx, float sy, float *bx, float *by)
-{
- struct weston_buffer_viewport *vp = &surface->buffer_viewport;
-
- /* first transform coordinates if the viewport is set */
- viewport_surface_to_buffer(surface, sx, sy, bx, by);
-
- weston_transformed_coord(surface->width_from_buffer,
- surface->height_from_buffer,
- vp->buffer.transform, vp->buffer.scale,
- *bx, *by, bx, by);
-}
-
-/** Transform a rectangle from surface coordinates to buffer coordinates
- *
- * \param surface The surface to fetch wp_viewport and buffer transformation
- * from.
- * \param rect The rectangle to transform.
- * \return The transformed rectangle.
- *
- * Viewport and buffer transformations can only do translation, scaling,
- * and rotations in 90-degree steps. Therefore the only loss in the
- * conversion is coordinate rounding.
- *
- * However, some coordinate rounding takes place as an intermediate
- * step before the buffer scale factor is applied, so the rectangle
- * boundary may not be exactly as expected.
- *
- * This is OK for damage tracking since a little extra coverage is
- * not a problem.
- */
-WL_EXPORT pixman_box32_t
-weston_surface_to_buffer_rect(struct weston_surface *surface,
- pixman_box32_t rect)
-{
- struct weston_buffer_viewport *vp = &surface->buffer_viewport;
- float xf, yf;
-
- /* first transform box coordinates if the viewport is set */
- viewport_surface_to_buffer(surface, rect.x1, rect.y1, &xf, &yf);
- rect.x1 = floorf(xf);
- rect.y1 = floorf(yf);
-
- viewport_surface_to_buffer(surface, rect.x2, rect.y2, &xf, &yf);
- rect.x2 = ceilf(xf);
- rect.y2 = ceilf(yf);
-
- return weston_transformed_rect(surface->width_from_buffer,
- surface->height_from_buffer,
- vp->buffer.transform, vp->buffer.scale,
- rect);
-}
-
-/** Transform a region from surface coordinates to buffer coordinates
- *
- * \param surface The surface to fetch wp_viewport and buffer transformation
- * from.
- * \param surface_region[in] The region in surface coordinates.
- * \param buffer_region[out] The region converted to buffer coordinates.
- *
- * Buffer_region must be init'd, but will be completely overwritten.
- *
- * Viewport and buffer transformations can only do translation, scaling,
- * and rotations in 90-degree steps. Therefore the only loss in the
- * conversion is from the coordinate rounding that takes place in
- * \ref weston_surface_to_buffer_rect.
- */
-WL_EXPORT void
-weston_surface_to_buffer_region(struct weston_surface *surface,
- pixman_region32_t *surface_region,
- pixman_region32_t *buffer_region)
-{
- pixman_box32_t *src_rects, *dest_rects;
- int nrects, i;
-
- src_rects = pixman_region32_rectangles(surface_region, &nrects);
- dest_rects = malloc(nrects * sizeof(*dest_rects));
- if (!dest_rects)
- return;
-
- for (i = 0; i < nrects; i++) {
- dest_rects[i] = weston_surface_to_buffer_rect(surface,
- src_rects[i]);
- }
-
- pixman_region32_fini(buffer_region);
- pixman_region32_init_rects(buffer_region, dest_rects, nrects);
- free(dest_rects);
-}
-
-WL_EXPORT void
-weston_view_move_to_plane(struct weston_view *view,
- struct weston_plane *plane)
-{
- if (view->plane == plane)
- return;
-
- weston_view_damage_below(view);
- view->plane = plane;
- weston_surface_damage(view->surface);
-}
-
-/** Inflict damage on the plane where the view is visible.
- *
- * \param view The view that causes the damage.
- *
- * If the view is currently on a plane (including the primary plane),
- * take the view's boundingbox, subtract all the opaque views that cover it,
- * and add the remaining region as damage to the plane. This corresponds
- * to the damage inflicted to the plane if this view disappeared.
- *
- * A repaint is scheduled for this view.
- *
- * The region of all opaque views covering this view is stored in
- * weston_view::clip and updated by view_accumulate_damage() during
- * weston_output_repaint(). Specifically, that region matches the
- * scenegraph as it was last painted.
- */
-WL_EXPORT void
-weston_view_damage_below(struct weston_view *view)
-{
- pixman_region32_t damage;
-
- pixman_region32_init(&damage);
- pixman_region32_subtract(&damage, &view->transform.boundingbox,
- &view->clip);
- if (view->plane)
- pixman_region32_union(&view->plane->damage,
- &view->plane->damage, &damage);
- pixman_region32_fini(&damage);
- weston_view_schedule_repaint(view);
-}
-
-/**
- * \param es The surface
- * \param mask The new set of outputs for the surface
- *
- * Sets the surface's set of outputs to the ones specified by
- * the new output mask provided. Identifies the outputs that
- * have changed, the posts enter and leave events for these
- * outputs as appropriate.
- */
-static void
-weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask)
-{
- uint32_t different = es->output_mask ^ mask;
- uint32_t entered = mask & different;
- uint32_t left = es->output_mask & different;
- struct weston_output *output;
- struct wl_resource *resource = NULL;
- struct wl_client *client;
-
- es->output_mask = mask;
- if (es->resource == NULL)
- return;
- if (different == 0)
- return;
-
- client = wl_resource_get_client(es->resource);
-
- wl_list_for_each(output, &es->compositor->output_list, link) {
- if (1u << output->id & different)
- resource =
- wl_resource_find_for_client(&output->resource_list,
- client);
- if (resource == NULL)
- continue;
- if (1u << output->id & entered)
- wl_surface_send_enter(es->resource, resource);
- if (1u << output->id & left)
- wl_surface_send_leave(es->resource, resource);
- }
-}
-
-/** Recalculate which output(s) the surface has views displayed on
- *
- * \param es The surface to remap to outputs
- *
- * Finds the output that is showing the largest amount of one
- * of the surface's various views. This output becomes the
- * surface's primary output for vsync and frame callback purposes.
- *
- * Also notes all outputs of all of the surface's views
- * in the output_mask for the surface.
- */
-static void
-weston_surface_assign_output(struct weston_surface *es)
-{
- struct weston_output *new_output;
- struct weston_view *view;
- pixman_region32_t region;
- uint32_t max, area, mask;
- pixman_box32_t *e;
-
- new_output = NULL;
- max = 0;
- mask = 0;
- pixman_region32_init(®ion);
- wl_list_for_each(view, &es->views, surface_link) {
- if (!view->output)
- continue;
-
- pixman_region32_intersect(®ion, &view->transform.boundingbox,
- &view->output->region);
-
- e = pixman_region32_extents(®ion);
- area = (e->x2 - e->x1) * (e->y2 - e->y1);
-
- mask |= view->output_mask;
-
- if (area >= max) {
- new_output = view->output;
- max = area;
- }
- }
- pixman_region32_fini(®ion);
-
- es->output = new_output;
- weston_surface_update_output_mask(es, mask);
-}
-
-/** Recalculate which output(s) the view is displayed on
- *
- * \param ev The view to remap to outputs
- *
- * Identifies the set of outputs that the view is visible on,
- * noting them into the output_mask. The output that the view
- * is most visible on is set as the view's primary output.
- *
- * Also does the same for the view's surface. See
- * weston_surface_assign_output().
- */
-static void
-weston_view_assign_output(struct weston_view *ev)
-{
- struct weston_compositor *ec = ev->surface->compositor;
- struct weston_output *output, *new_output;
- pixman_region32_t region;
- uint32_t max, area, mask;
- pixman_box32_t *e;
-
- new_output = NULL;
- max = 0;
- mask = 0;
- pixman_region32_init(®ion);
- wl_list_for_each(output, &ec->output_list, link) {
- if (output->destroying)
- continue;
-
- pixman_region32_intersect(®ion, &ev->transform.boundingbox,
- &output->region);
-
- e = pixman_region32_extents(®ion);
- area = (e->x2 - e->x1) * (e->y2 - e->y1);
-
- if (area > 0)
- mask |= 1u << output->id;
-
- if (area >= max) {
- new_output = output;
- max = area;
- }
- }
- pixman_region32_fini(®ion);
-
- ev->output = new_output;
- ev->output_mask = mask;
-
- weston_surface_assign_output(ev->surface);
-}
-
-static void
-weston_view_to_view_map(struct weston_view *from, struct weston_view *to,
- int from_x, int from_y, int *to_x, int *to_y)
-{
- float x, y;
-
- weston_view_to_global_float(from, from_x, from_y, &x, &y);
- weston_view_from_global_float(to, x, y, &x, &y);
-
- *to_x = round(x);
- *to_y = round(y);
-}
-
-static void
-weston_view_transfer_scissor(struct weston_view *from, struct weston_view *to)
-{
- pixman_box32_t *a;
- pixman_box32_t b;
-
- a = pixman_region32_extents(&from->geometry.scissor);
-
- weston_view_to_view_map(from, to, a->x1, a->y1, &b.x1, &b.y1);
- weston_view_to_view_map(from, to, a->x2, a->y2, &b.x2, &b.y2);
-
- pixman_region32_fini(&to->geometry.scissor);
- pixman_region32_init_with_extents(&to->geometry.scissor, &b);
-}
-
-static void
-view_compute_bbox(struct weston_view *view, const pixman_box32_t *inbox,
- pixman_region32_t *bbox)
-{
- float min_x = HUGE_VALF, min_y = HUGE_VALF;
- float max_x = -HUGE_VALF, max_y = -HUGE_VALF;
- int32_t s[4][2] = {
- { inbox->x1, inbox->y1 },
- { inbox->x1, inbox->y2 },
- { inbox->x2, inbox->y1 },
- { inbox->x2, inbox->y2 },
- };
- float int_x, int_y;
- int i;
-
- if (inbox->x1 == inbox->x2 || inbox->y1 == inbox->y2) {
- /* avoid rounding empty bbox to 1x1 */
- pixman_region32_init(bbox);
- return;
- }
-
- for (i = 0; i < 4; ++i) {
- float x, y;
- weston_view_to_global_float(view, s[i][0], s[i][1], &x, &y);
- if (x < min_x)
- min_x = x;
- if (x > max_x)
- max_x = x;
- if (y < min_y)
- min_y = y;
- if (y > max_y)
- max_y = y;
- }
-
- int_x = floorf(min_x);
- int_y = floorf(min_y);
- pixman_region32_init_rect(bbox, int_x, int_y,
- ceilf(max_x) - int_x, ceilf(max_y) - int_y);
-}
-
-static void
-weston_view_update_transform_disable(struct weston_view *view)
-{
- view->transform.enabled = 0;
-
- /* round off fractions when not transformed */
- view->geometry.x = roundf(view->geometry.x);
- view->geometry.y = roundf(view->geometry.y);
-
- /* Otherwise identity matrix, but with x and y translation. */
- view->transform.position.matrix.type = WESTON_MATRIX_TRANSFORM_TRANSLATE;
- view->transform.position.matrix.d[12] = view->geometry.x;
- view->transform.position.matrix.d[13] = view->geometry.y;
-
- view->transform.matrix = view->transform.position.matrix;
-
- view->transform.inverse = view->transform.position.matrix;
- view->transform.inverse.d[12] = -view->geometry.x;
- view->transform.inverse.d[13] = -view->geometry.y;
-
- pixman_region32_init_rect(&view->transform.boundingbox,
- 0, 0,
- view->surface->width,
- view->surface->height);
- if (view->geometry.scissor_enabled)
- pixman_region32_intersect(&view->transform.boundingbox,
- &view->transform.boundingbox,
- &view->geometry.scissor);
-
- pixman_region32_translate(&view->transform.boundingbox,
- view->geometry.x, view->geometry.y);
-
- if (view->alpha == 1.0) {
- pixman_region32_copy(&view->transform.opaque,
- &view->surface->opaque);
- pixman_region32_translate(&view->transform.opaque,
- view->geometry.x,
- view->geometry.y);
- }
-}
-
-static int
-weston_view_update_transform_enable(struct weston_view *view)
-{
- struct weston_view *parent = view->geometry.parent;
- struct weston_matrix *matrix = &view->transform.matrix;
- struct weston_matrix *inverse = &view->transform.inverse;
- struct weston_transform *tform;
- pixman_region32_t surfregion;
- const pixman_box32_t *surfbox;
-
- view->transform.enabled = 1;
-
- /* Otherwise identity matrix, but with x and y translation. */
- view->transform.position.matrix.type = WESTON_MATRIX_TRANSFORM_TRANSLATE;
- view->transform.position.matrix.d[12] = view->geometry.x;
- view->transform.position.matrix.d[13] = view->geometry.y;
-
- weston_matrix_init(matrix);
- wl_list_for_each(tform, &view->geometry.transformation_list, link)
- weston_matrix_multiply(matrix, &tform->matrix);
-
- if (parent)
- weston_matrix_multiply(matrix, &parent->transform.matrix);
-
- if (weston_matrix_invert(inverse, matrix) < 0) {
- /* Oops, bad total transformation, not invertible */
- weston_log("error: weston_view %p"
- " transformation not invertible.\n", view);
- return -1;
- }
-
- pixman_region32_init_rect(&surfregion, 0, 0,
- view->surface->width, view->surface->height);
- if (view->geometry.scissor_enabled)
- pixman_region32_intersect(&surfregion, &surfregion,
- &view->geometry.scissor);
- surfbox = pixman_region32_extents(&surfregion);
-
- view_compute_bbox(view, surfbox, &view->transform.boundingbox);
- pixman_region32_fini(&surfregion);
-
- return 0;
-}
-
-static struct weston_layer *
-get_view_layer(struct weston_view *view)
-{
- if (view->parent_view)
- return get_view_layer(view->parent_view);
- return view->layer_link.layer;
-}
-
-WL_EXPORT void
-weston_view_update_transform(struct weston_view *view)
-{
- struct weston_view *parent = view->geometry.parent;
- struct weston_layer *layer;
- pixman_region32_t mask;
-
- if (!view->transform.dirty)
- return;
-
- if (parent)
- weston_view_update_transform(parent);
-
- view->transform.dirty = 0;
-
- weston_view_damage_below(view);
-
- pixman_region32_fini(&view->transform.boundingbox);
- pixman_region32_fini(&view->transform.opaque);
- pixman_region32_init(&view->transform.opaque);
-
- /* transform.position is always in transformation_list */
- if (view->geometry.transformation_list.next ==
- &view->transform.position.link &&
- view->geometry.transformation_list.prev ==
- &view->transform.position.link &&
- !parent) {
- weston_view_update_transform_disable(view);
- } else {
- if (weston_view_update_transform_enable(view) < 0)
- weston_view_update_transform_disable(view);
- }
-
- layer = get_view_layer(view);
- if (layer) {
- pixman_region32_init_with_extents(&mask, &layer->mask);
- pixman_region32_intersect(&view->transform.boundingbox,
- &view->transform.boundingbox, &mask);
- pixman_region32_intersect(&view->transform.opaque,
- &view->transform.opaque, &mask);
- pixman_region32_fini(&mask);
- }
-
- if (parent) {
- if (parent->geometry.scissor_enabled) {
- view->geometry.scissor_enabled = true;
- weston_view_transfer_scissor(parent, view);
- } else {
- view->geometry.scissor_enabled = false;
- }
- }
-
- weston_view_damage_below(view);
-
- weston_view_assign_output(view);
-
- wl_signal_emit(&view->surface->compositor->transform_signal,
- view->surface);
-}
-
-WL_EXPORT void
-weston_view_geometry_dirty(struct weston_view *view)
-{
- struct weston_view *child;
-
- /*
- * The invariant: if view->geometry.dirty, then all views
- * in view->geometry.child_list have geometry.dirty too.
- * Corollary: if not parent->geometry.dirty, then all ancestors
- * are not dirty.
- */
-
- if (view->transform.dirty)
- return;
-
- view->transform.dirty = 1;
-
- wl_list_for_each(child, &view->geometry.child_list,
- geometry.parent_link)
- weston_view_geometry_dirty(child);
-}
-
-WL_EXPORT void
-weston_view_to_global_fixed(struct weston_view *view,
- wl_fixed_t vx, wl_fixed_t vy,
- wl_fixed_t *x, wl_fixed_t *y)
-{
- float xf, yf;
-
- weston_view_to_global_float(view,
- wl_fixed_to_double(vx),
- wl_fixed_to_double(vy),
- &xf, &yf);
- *x = wl_fixed_from_double(xf);
- *y = wl_fixed_from_double(yf);
-}
-
-WL_EXPORT void
-weston_view_from_global_float(struct weston_view *view,
- float x, float y, float *vx, float *vy)
-{
- if (view->transform.enabled) {
- struct weston_vector v = { { x, y, 0.0f, 1.0f } };
-
- weston_matrix_transform(&view->transform.inverse, &v);
-
- if (fabsf(v.f[3]) < 1e-6) {
- weston_log("warning: numerical instability in "
- "weston_view_from_global(), divisor = %g\n",
- v.f[3]);
- *vx = 0;
- *vy = 0;
- return;
- }
-
- *vx = v.f[0] / v.f[3];
- *vy = v.f[1] / v.f[3];
- } else {
- *vx = x - view->geometry.x;
- *vy = y - view->geometry.y;
- }
-}
-
-WL_EXPORT void
-weston_view_from_global_fixed(struct weston_view *view,
- wl_fixed_t x, wl_fixed_t y,
- wl_fixed_t *vx, wl_fixed_t *vy)
-{
- float vxf, vyf;
-
- weston_view_from_global_float(view,
- wl_fixed_to_double(x),
- wl_fixed_to_double(y),
- &vxf, &vyf);
- *vx = wl_fixed_from_double(vxf);
- *vy = wl_fixed_from_double(vyf);
-}
-
-WL_EXPORT void
-weston_view_from_global(struct weston_view *view,
- int32_t x, int32_t y, int32_t *vx, int32_t *vy)
-{
- float vxf, vyf;
-
- weston_view_from_global_float(view, x, y, &vxf, &vyf);
- *vx = floorf(vxf);
- *vy = floorf(vyf);
-}
-
-/**
- * \param surface The surface to be repainted
- *
- * Marks the output(s) that the surface is shown on as needing to be
- * repainted. See weston_output_schedule_repaint().
- */
-WL_EXPORT void
-weston_surface_schedule_repaint(struct weston_surface *surface)
-{
- struct weston_output *output;
-
- wl_list_for_each(output, &surface->compositor->output_list, link)
- if (surface->output_mask & (1u << output->id))
- weston_output_schedule_repaint(output);
-}
-
-/**
- * \param view The view to be repainted
- *
- * Marks the output(s) that the view is shown on as needing to be
- * repainted. See weston_output_schedule_repaint().
- */
-WL_EXPORT void
-weston_view_schedule_repaint(struct weston_view *view)
-{
- struct weston_output *output;
-
- wl_list_for_each(output, &view->surface->compositor->output_list, link)
- if (view->output_mask & (1u << output->id))
- weston_output_schedule_repaint(output);
-}
-
-/**
- * XXX: This function does it the wrong way.
- * surface->damage is the damage from the client, and causes
- * surface_flush_damage() to copy pixels. No window management action can
- * cause damage to the client-provided content, warranting re-upload!
- *
- * Instead of surface->damage, this function should record the damage
- * with all the views for this surface to avoid extraneous texture
- * uploads.
- */
-WL_EXPORT void
-weston_surface_damage(struct weston_surface *surface)
-{
- pixman_region32_union_rect(&surface->damage, &surface->damage,
- 0, 0, surface->width,
- surface->height);
-
- weston_surface_schedule_repaint(surface);
-}
-
-WL_EXPORT void
-weston_view_set_position(struct weston_view *view, float x, float y)
-{
- if (view->geometry.x == x && view->geometry.y == y)
- return;
-
- view->geometry.x = x;
- view->geometry.y = y;
- weston_view_geometry_dirty(view);
-}
-
-static void
-transform_parent_handle_parent_destroy(struct wl_listener *listener,
- void *data)
-{
- struct weston_view *view =
- container_of(listener, struct weston_view,
- geometry.parent_destroy_listener);
-
- weston_view_set_transform_parent(view, NULL);
-}
-
-WL_EXPORT void
-weston_view_set_transform_parent(struct weston_view *view,
- struct weston_view *parent)
-{
- if (view->geometry.parent) {
- wl_list_remove(&view->geometry.parent_destroy_listener.link);
- wl_list_remove(&view->geometry.parent_link);
-
- if (!parent)
- view->geometry.scissor_enabled = false;
- }
-
- view->geometry.parent = parent;
-
- view->geometry.parent_destroy_listener.notify =
- transform_parent_handle_parent_destroy;
- if (parent) {
- wl_signal_add(&parent->destroy_signal,
- &view->geometry.parent_destroy_listener);
- wl_list_insert(&parent->geometry.child_list,
- &view->geometry.parent_link);
- }
-
- weston_view_geometry_dirty(view);
-}
-
-/** Set a clip mask rectangle on a view
- *
- * \param view The view to set the clip mask on.
- * \param x Top-left corner X coordinate of the clip rectangle.
- * \param y Top-left corner Y coordinate of the clip rectangle.
- * \param width Width of the clip rectangle, non-negative.
- * \param height Height of the clip rectangle, non-negative.
- *
- * A shell may set a clip mask rectangle on a view. Everything outside
- * the rectangle is cut away for input and output purposes: it is
- * not drawn and cannot be hit by hit-test based input like pointer
- * motion or touch-downs. Everything inside the rectangle will behave
- * normally. Clients are unaware of clipping.
- *
- * The rectangle is set in surface-local coordinates. Setting a clip
- * mask rectangle does not affect the view position, the view is positioned
- * as it would be without a clip. The clip also does not change
- * weston_surface::width,height.
- *
- * The clip mask rectangle is part of transformation inheritance
- * (weston_view_set_transform_parent()). A clip set in the root of the
- * transformation inheritance tree will affect all views in the tree.
- * A clip can be set only on the root view. Attempting to set a clip
- * on view that has a transformation parent will fail. Assigning a parent
- * to a view that has a clip set will cause the clip to be forgotten.
- *
- * Because the clip mask is an axis-aligned rectangle, it poses restrictions
- * on the additional transformations in the child views. These transformations
- * may not rotate the coordinate axes, i.e., only translation and scaling
- * are allowed. Violating this restriction causes the clipping to malfunction.
- * Furthermore, using scaling may cause rounding errors in child clipping.
- *
- * The clip mask rectangle is not automatically adjusted based on
- * wl_surface.attach dx and dy arguments.
- *
- * A clip mask rectangle can be set only if the compositor capability
- * WESTON_CAP_VIEW_CLIP_MASK is present.
- *
- * This function sets the clip mask rectangle and schedules a repaint for
- * the view.
- */
-WL_EXPORT void
-weston_view_set_mask(struct weston_view *view,
- int x, int y, int width, int height)
-{
- struct weston_compositor *compositor = view->surface->compositor;
-
- if (!(compositor->capabilities & WESTON_CAP_VIEW_CLIP_MASK)) {
- weston_log("%s not allowed without capability!\n", __func__);
- return;
- }
-
- if (view->geometry.parent) {
- weston_log("view %p has a parent, clip forbidden!\n", view);
- return;
- }
-
- if (width < 0 || height < 0) {
- weston_log("%s: illegal args %d, %d, %d, %d\n", __func__,
- x, y, width, height);
- return;
- }
-
- pixman_region32_fini(&view->geometry.scissor);
- pixman_region32_init_rect(&view->geometry.scissor, x, y, width, height);
- view->geometry.scissor_enabled = true;
- weston_view_geometry_dirty(view);
- weston_view_schedule_repaint(view);
-}
-
-/** Remove the clip mask from a view
- *
- * \param view The view to remove the clip mask from.
- *
- * Removed the clip mask rectangle and schedules a repaint.
- *
- * \sa weston_view_set_mask
- */
-WL_EXPORT void
-weston_view_set_mask_infinite(struct weston_view *view)
-{
- view->geometry.scissor_enabled = false;
- weston_view_geometry_dirty(view);
- weston_view_schedule_repaint(view);
-}
-
-WL_EXPORT bool
-weston_view_is_mapped(struct weston_view *view)
-{
- if (view->output)
- return true;
- else
- return false;
-}
-
-WL_EXPORT bool
-weston_surface_is_mapped(struct weston_surface *surface)
-{
- if (surface->output)
- return true;
- else
- return false;
-}
-
-static void
-surface_set_size(struct weston_surface *surface, int32_t width, int32_t height)
-{
- struct weston_view *view;
-
- if (surface->width == width && surface->height == height)
- return;
-
- surface->width = width;
- surface->height = height;
-
- wl_list_for_each(view, &surface->views, surface_link)
- weston_view_geometry_dirty(view);
-}
-
-WL_EXPORT void
-weston_surface_set_size(struct weston_surface *surface,
- int32_t width, int32_t height)
-{
- assert(!surface->resource);
- surface_set_size(surface, width, height);
-}
-
-static int
-fixed_round_up_to_int(wl_fixed_t f)
-{
- return wl_fixed_to_int(wl_fixed_from_int(1) - 1 + f);
-}
-
-static void
-convert_size_by_transform_scale(int32_t *width_out, int32_t *height_out,
- int32_t width, int32_t height,
- uint32_t transform,
- int32_t scale)
-{
- assert(scale > 0);
-
- switch (transform) {
- case WL_OUTPUT_TRANSFORM_NORMAL:
- case WL_OUTPUT_TRANSFORM_180:
- case WL_OUTPUT_TRANSFORM_FLIPPED:
- case WL_OUTPUT_TRANSFORM_FLIPPED_180:
- *width_out = width / scale;
- *height_out = height / scale;
- break;
- case WL_OUTPUT_TRANSFORM_90:
- case WL_OUTPUT_TRANSFORM_270:
- case WL_OUTPUT_TRANSFORM_FLIPPED_90:
- case WL_OUTPUT_TRANSFORM_FLIPPED_270:
- *width_out = height / scale;
- *height_out = width / scale;
- break;
- default:
- assert(0 && "invalid transform");
- }
-}
-
-static void
-weston_surface_calculate_size_from_buffer(struct weston_surface *surface)
-{
- struct weston_buffer_viewport *vp = &surface->buffer_viewport;
-
- if (!surface->buffer_ref.buffer) {
- surface->width_from_buffer = 0;
- surface->height_from_buffer = 0;
- return;
- }
-
- convert_size_by_transform_scale(&surface->width_from_buffer,
- &surface->height_from_buffer,
- surface->buffer_ref.buffer->width,
- surface->buffer_ref.buffer->height,
- vp->buffer.transform,
- vp->buffer.scale);
-}
-
-static void
-weston_surface_update_size(struct weston_surface *surface)
-{
- struct weston_buffer_viewport *vp = &surface->buffer_viewport;
- int32_t width, height;
-
- width = surface->width_from_buffer;
- height = surface->height_from_buffer;
-
- if (width != 0 && vp->surface.width != -1) {
- surface_set_size(surface,
- vp->surface.width, vp->surface.height);
- return;
- }
-
- if (width != 0 && vp->buffer.src_width != wl_fixed_from_int(-1)) {
- int32_t w = fixed_round_up_to_int(vp->buffer.src_width);
- int32_t h = fixed_round_up_to_int(vp->buffer.src_height);
-
- surface_set_size(surface, w ?: 1, h ?: 1);
- return;
- }
-
- surface_set_size(surface, width, height);
-}
-
-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 struct weston_view *
-weston_compositor_pick_view(struct weston_compositor *compositor,
- wl_fixed_t x, wl_fixed_t y,
- wl_fixed_t *vx, wl_fixed_t *vy)
-{
- struct weston_view *view;
- wl_fixed_t view_x, view_y;
- int view_ix, view_iy;
- int ix = wl_fixed_to_int(x);
- int iy = wl_fixed_to_int(y);
-
- wl_list_for_each(view, &compositor->view_list, link) {
- if (!pixman_region32_contains_point(
- &view->transform.boundingbox, ix, iy, NULL))
- continue;
-
- weston_view_from_global_fixed(view, x, y, &view_x, &view_y);
- view_ix = wl_fixed_to_int(view_x);
- view_iy = wl_fixed_to_int(view_y);
-
- if (!pixman_region32_contains_point(&view->surface->input,
- view_ix, view_iy, NULL))
- continue;
-
- if (view->geometry.scissor_enabled &&
- !pixman_region32_contains_point(&view->geometry.scissor,
- view_ix, view_iy, NULL))
- continue;
-
- *vx = view_x;
- *vy = view_y;
- return view;
- }
-
- *vx = wl_fixed_from_int(-1000000);
- *vy = wl_fixed_from_int(-1000000);
- return NULL;
-}
-
-static void
-weston_compositor_repick(struct weston_compositor *compositor)
-{
- struct weston_seat *seat;
-
- if (!compositor->session_active)
- return;
-
- wl_list_for_each(seat, &compositor->seat_list, link)
- weston_seat_repick(seat);
-}
-
-WL_EXPORT void
-weston_view_unmap(struct weston_view *view)
-{
- struct weston_seat *seat;
-
- if (!weston_view_is_mapped(view))
- return;
-
- weston_view_damage_below(view);
- view->output = NULL;
- view->plane = NULL;
- weston_layer_entry_remove(&view->layer_link);
- wl_list_remove(&view->link);
- wl_list_init(&view->link);
- view->output_mask = 0;
- weston_surface_assign_output(view->surface);
-
- if (weston_surface_is_mapped(view->surface))
- return;
-
- wl_list_for_each(seat, &view->surface->compositor->seat_list, link) {
- struct weston_touch *touch = weston_seat_get_touch(seat);
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
- struct weston_keyboard *keyboard =
- weston_seat_get_keyboard(seat);
-
- if (keyboard && keyboard->focus == view->surface)
- weston_keyboard_set_focus(keyboard, NULL);
- if (pointer && pointer->focus == view)
- weston_pointer_clear_focus(pointer);
- if (touch && touch->focus == view)
- weston_touch_set_focus(touch, NULL);
- }
-}
-
-WL_EXPORT void
-weston_surface_unmap(struct weston_surface *surface)
-{
- struct weston_view *view;
-
- wl_list_for_each(view, &surface->views, surface_link)
- weston_view_unmap(view);
- surface->output = NULL;
-}
-
-static void
-weston_surface_reset_pending_buffer(struct weston_surface *surface)
-{
- weston_surface_state_set_buffer(&surface->pending, NULL);
- surface->pending.sx = 0;
- surface->pending.sy = 0;
- surface->pending.newly_attached = 0;
- surface->pending.buffer_viewport.changed = 0;
-}
-
-WL_EXPORT void
-weston_view_destroy(struct weston_view *view)
-{
- wl_signal_emit(&view->destroy_signal, view);
-
- assert(wl_list_empty(&view->geometry.child_list));
-
- if (weston_view_is_mapped(view)) {
- weston_view_unmap(view);
- weston_compositor_build_view_list(view->surface->compositor);
- }
-
- wl_list_remove(&view->link);
- weston_layer_entry_remove(&view->layer_link);
-
- pixman_region32_fini(&view->clip);
- pixman_region32_fini(&view->geometry.scissor);
- pixman_region32_fini(&view->transform.boundingbox);
- pixman_region32_fini(&view->transform.opaque);
-
- weston_view_set_transform_parent(view, NULL);
-
- wl_list_remove(&view->surface_link);
-
- free(view);
-}
-
-WL_EXPORT void
-weston_surface_destroy(struct weston_surface *surface)
-{
- struct weston_frame_callback *cb, *next;
- struct weston_view *ev, *nv;
-
- if (--surface->ref_count > 0)
- return;
-
- assert(surface->resource == NULL);
-
- wl_signal_emit(&surface->destroy_signal, surface);
-
- assert(wl_list_empty(&surface->subsurface_list_pending));
- assert(wl_list_empty(&surface->subsurface_list));
-
- wl_list_for_each_safe(ev, nv, &surface->views, surface_link)
- weston_view_destroy(ev);
-
- weston_surface_state_fini(&surface->pending);
-
- weston_buffer_reference(&surface->buffer_ref, NULL);
-
- pixman_region32_fini(&surface->damage);
- pixman_region32_fini(&surface->opaque);
- pixman_region32_fini(&surface->input);
-
- wl_list_for_each_safe(cb, next, &surface->frame_callback_list, link)
- wl_resource_destroy(cb->resource);
-
- weston_presentation_feedback_discard_list(&surface->feedback_list);
-
- free(surface);
-}
-
-static void
-destroy_surface(struct wl_resource *resource)
-{
- struct weston_surface *surface = wl_resource_get_user_data(resource);
-
- assert(surface);
-
- /* Set the resource to NULL, since we don't want to leave a
- * dangling pointer if the surface was refcounted and survives
- * the weston_surface_destroy() call. */
- surface->resource = NULL;
-
- if (surface->viewport_resource)
- wl_resource_set_user_data(surface->viewport_resource, NULL);
-
- weston_surface_destroy(surface);
-}
-
-static void
-weston_buffer_destroy_handler(struct wl_listener *listener, void *data)
-{
- struct weston_buffer *buffer =
- container_of(listener, struct weston_buffer, destroy_listener);
-
- wl_signal_emit(&buffer->destroy_signal, buffer);
- free(buffer);
-}
-
-WL_EXPORT struct weston_buffer *
-weston_buffer_from_resource(struct wl_resource *resource)
-{
- struct weston_buffer *buffer;
- struct wl_listener *listener;
-
- listener = wl_resource_get_destroy_listener(resource,
- weston_buffer_destroy_handler);
-
- if (listener)
- return container_of(listener, struct weston_buffer,
- destroy_listener);
-
- buffer = zalloc(sizeof *buffer);
- if (buffer == NULL)
- return NULL;
-
- buffer->resource = resource;
- wl_signal_init(&buffer->destroy_signal);
- buffer->destroy_listener.notify = weston_buffer_destroy_handler;
- buffer->y_inverted = 1;
- wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);
-
- return buffer;
-}
-
-static void
-weston_buffer_reference_handle_destroy(struct wl_listener *listener,
- void *data)
-{
- struct weston_buffer_reference *ref =
- container_of(listener, struct weston_buffer_reference,
- destroy_listener);
-
- assert((struct weston_buffer *)data == ref->buffer);
- ref->buffer = NULL;
-}
-
-WL_EXPORT void
-weston_buffer_reference(struct weston_buffer_reference *ref,
- struct weston_buffer *buffer)
-{
- if (ref->buffer && buffer != ref->buffer) {
- ref->buffer->busy_count--;
- if (ref->buffer->busy_count == 0) {
- assert(wl_resource_get_client(ref->buffer->resource));
- wl_resource_queue_event(ref->buffer->resource,
- WL_BUFFER_RELEASE);
- }
- wl_list_remove(&ref->destroy_listener.link);
- }
-
- if (buffer && buffer != ref->buffer) {
- buffer->busy_count++;
- wl_signal_add(&buffer->destroy_signal,
- &ref->destroy_listener);
- }
-
- ref->buffer = buffer;
- ref->destroy_listener.notify = weston_buffer_reference_handle_destroy;
-}
-
-static void
-weston_surface_attach(struct weston_surface *surface,
- struct weston_buffer *buffer)
-{
- weston_buffer_reference(&surface->buffer_ref, buffer);
-
- if (!buffer) {
- if (weston_surface_is_mapped(surface))
- weston_surface_unmap(surface);
- }
-
- surface->compositor->renderer->attach(surface, buffer);
-
- weston_surface_calculate_size_from_buffer(surface);
- weston_presentation_feedback_discard_list(&surface->feedback_list);
-}
-
-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);
-}
-
-WL_EXPORT void
-weston_output_damage(struct weston_output *output)
-{
- struct weston_compositor *compositor = output->compositor;
-
- pixman_region32_union(&compositor->primary_plane.damage,
- &compositor->primary_plane.damage,
- &output->region);
- weston_output_schedule_repaint(output);
-}
-
-static void
-surface_flush_damage(struct weston_surface *surface)
-{
- if (surface->buffer_ref.buffer &&
- wl_shm_buffer_get(surface->buffer_ref.buffer->resource))
- surface->compositor->renderer->flush_damage(surface);
-
- if (weston_timeline_enabled_ &&
- pixman_region32_not_empty(&surface->damage))
- TL_POINT("core_flush_damage", TLP_SURFACE(surface),
- TLP_OUTPUT(surface->output), TLP_END);
-
- pixman_region32_clear(&surface->damage);
-}
-
-static void
-view_accumulate_damage(struct weston_view *view,
- pixman_region32_t *opaque)
-{
- pixman_region32_t damage;
-
- pixman_region32_init(&damage);
- if (view->transform.enabled) {
- pixman_box32_t *extents;
-
- extents = pixman_region32_extents(&view->surface->damage);
- view_compute_bbox(view, extents, &damage);
- } else {
- pixman_region32_copy(&damage, &view->surface->damage);
- pixman_region32_translate(&damage,
- view->geometry.x, view->geometry.y);
- }
-
- pixman_region32_intersect(&damage, &damage,
- &view->transform.boundingbox);
- pixman_region32_subtract(&damage, &damage, opaque);
- pixman_region32_union(&view->plane->damage,
- &view->plane->damage, &damage);
- pixman_region32_fini(&damage);
- pixman_region32_copy(&view->clip, opaque);
- pixman_region32_union(opaque, opaque, &view->transform.opaque);
-}
-
-static void
-compositor_accumulate_damage(struct weston_compositor *ec)
-{
- struct weston_plane *plane;
- struct weston_view *ev;
- pixman_region32_t opaque, clip;
-
- pixman_region32_init(&clip);
-
- wl_list_for_each(plane, &ec->plane_list, link) {
- pixman_region32_copy(&plane->clip, &clip);
-
- pixman_region32_init(&opaque);
-
- wl_list_for_each(ev, &ec->view_list, link) {
- if (ev->plane != plane)
- continue;
-
- view_accumulate_damage(ev, &opaque);
- }
-
- pixman_region32_union(&clip, &clip, &opaque);
- pixman_region32_fini(&opaque);
- }
-
- pixman_region32_fini(&clip);
-
- wl_list_for_each(ev, &ec->view_list, link)
- ev->surface->touched = false;
-
- wl_list_for_each(ev, &ec->view_list, link) {
- if (ev->surface->touched)
- continue;
- ev->surface->touched = true;
-
- surface_flush_damage(ev->surface);
-
- /* Both the renderer and the backend have seen the buffer
- * by now. If renderer needs the buffer, it has its own
- * reference set. If the backend wants to keep the buffer
- * around for migrating the surface into a non-primary plane
- * later, keep_buffer is true. Otherwise, drop the core
- * reference now, and allow early buffer release. This enables
- * clients to use single-buffering.
- */
- if (!ev->surface->keep_buffer)
- weston_buffer_reference(&ev->surface->buffer_ref, NULL);
- }
-}
-
-static void
-surface_stash_subsurface_views(struct weston_surface *surface)
-{
- struct weston_subsurface *sub;
-
- wl_list_for_each(sub, &surface->subsurface_list, parent_link) {
- if (sub->surface == surface)
- continue;
-
- wl_list_insert_list(&sub->unused_views, &sub->surface->views);
- wl_list_init(&sub->surface->views);
-
- surface_stash_subsurface_views(sub->surface);
- }
-}
-
-static void
-surface_free_unused_subsurface_views(struct weston_surface *surface)
-{
- struct weston_subsurface *sub;
- struct weston_view *view, *nv;
-
- wl_list_for_each(sub, &surface->subsurface_list, parent_link) {
- if (sub->surface == surface)
- continue;
-
- wl_list_for_each_safe(view, nv, &sub->unused_views, surface_link) {
- weston_view_unmap (view);
- weston_view_destroy(view);
- }
-
- surface_free_unused_subsurface_views(sub->surface);
- }
-}
-
-static void
-view_list_add_subsurface_view(struct weston_compositor *compositor,
- struct weston_subsurface *sub,
- struct weston_view *parent)
-{
- struct weston_subsurface *child;
- struct weston_view *view = NULL, *iv;
-
- if (!weston_surface_is_mapped(sub->surface))
- return;
-
- wl_list_for_each(iv, &sub->unused_views, surface_link) {
- if (iv->geometry.parent == parent) {
- view = iv;
- break;
- }
- }
-
- if (view) {
- /* Put it back in the surface's list of views */
- wl_list_remove(&view->surface_link);
- wl_list_insert(&sub->surface->views, &view->surface_link);
- } else {
- view = weston_view_create(sub->surface);
- weston_view_set_position(view,
- sub->position.x,
- sub->position.y);
- weston_view_set_transform_parent(view, parent);
- }
-
- view->parent_view = parent;
- weston_view_update_transform(view);
-
- if (wl_list_empty(&sub->surface->subsurface_list)) {
- wl_list_insert(compositor->view_list.prev, &view->link);
- return;
- }
-
- wl_list_for_each(child, &sub->surface->subsurface_list, parent_link) {
- if (child->surface == sub->surface)
- wl_list_insert(compositor->view_list.prev, &view->link);
- else
- view_list_add_subsurface_view(compositor, child, view);
- }
-}
-
-static void
-view_list_add(struct weston_compositor *compositor,
- struct weston_view *view)
-{
- struct weston_subsurface *sub;
-
- weston_view_update_transform(view);
-
- if (wl_list_empty(&view->surface->subsurface_list)) {
- wl_list_insert(compositor->view_list.prev, &view->link);
- return;
- }
-
- wl_list_for_each(sub, &view->surface->subsurface_list, parent_link) {
- if (sub->surface == view->surface)
- wl_list_insert(compositor->view_list.prev, &view->link);
- else
- view_list_add_subsurface_view(compositor, sub, view);
- }
-}
-
-static void
-weston_compositor_build_view_list(struct weston_compositor *compositor)
-{
- struct weston_view *view;
- struct weston_layer *layer;
-
- wl_list_for_each(layer, &compositor->layer_list, link)
- wl_list_for_each(view, &layer->view_list.link, layer_link.link)
- surface_stash_subsurface_views(view->surface);
-
- wl_list_init(&compositor->view_list);
- wl_list_for_each(layer, &compositor->layer_list, link) {
- wl_list_for_each(view, &layer->view_list.link, layer_link.link) {
- view_list_add(compositor, view);
- }
- }
-
- wl_list_for_each(layer, &compositor->layer_list, link)
- wl_list_for_each(view, &layer->view_list.link, layer_link.link)
- surface_free_unused_subsurface_views(view->surface);
-}
-
-static void
-weston_output_take_feedback_list(struct weston_output *output,
- struct weston_surface *surface)
-{
- struct weston_view *view;
- struct weston_presentation_feedback *feedback;
- uint32_t flags = 0xffffffff;
-
- if (wl_list_empty(&surface->feedback_list))
- return;
-
- /* All views must have the flag for the flag to survive. */
- wl_list_for_each(view, &surface->views, surface_link) {
- /* ignore views that are not on this output at all */
- if (view->output_mask & (1u << output->id))
- flags &= view->psf_flags;
- }
-
- wl_list_for_each(feedback, &surface->feedback_list, link)
- feedback->psf_flags = flags;
-
- wl_list_insert_list(&output->feedback_list, &surface->feedback_list);
- wl_list_init(&surface->feedback_list);
-}
-
-static int
-weston_output_repaint(struct weston_output *output)
-{
- struct weston_compositor *ec = output->compositor;
- struct weston_view *ev;
- struct weston_animation *animation, *next;
- struct weston_frame_callback *cb, *cnext;
- struct wl_list frame_callback_list;
- pixman_region32_t output_damage;
- int r;
-
- if (output->destroying)
- return 0;
-
- TL_POINT("core_repaint_begin", TLP_OUTPUT(output), TLP_END);
-
- /* Rebuild the surface list and update surface transforms up front. */
- weston_compositor_build_view_list(ec);
-
- if (output->assign_planes && !output->disable_planes) {
- output->assign_planes(output);
- } else {
- wl_list_for_each(ev, &ec->view_list, link) {
- weston_view_move_to_plane(ev, &ec->primary_plane);
- ev->psf_flags = 0;
- }
- }
-
- wl_list_init(&frame_callback_list);
- wl_list_for_each(ev, &ec->view_list, link) {
- /* Note: This operation is safe to do multiple times on the
- * same surface.
- */
- if (ev->surface->output == output) {
- wl_list_insert_list(&frame_callback_list,
- &ev->surface->frame_callback_list);
- wl_list_init(&ev->surface->frame_callback_list);
-
- weston_output_take_feedback_list(output, ev->surface);
- }
- }
-
- compositor_accumulate_damage(ec);
-
- pixman_region32_init(&output_damage);
- pixman_region32_intersect(&output_damage,
- &ec->primary_plane.damage, &output->region);
- pixman_region32_subtract(&output_damage,
- &output_damage, &ec->primary_plane.clip);
-
- if (output->dirty)
- weston_output_update_matrix(output);
-
- r = output->repaint(output, &output_damage);
-
- pixman_region32_fini(&output_damage);
-
- output->repaint_needed = 0;
-
- weston_compositor_repick(ec);
-
- wl_list_for_each_safe(cb, cnext, &frame_callback_list, link) {
- wl_callback_send_done(cb->resource, output->frame_time);
- wl_resource_destroy(cb->resource);
- }
-
- wl_list_for_each_safe(animation, next, &output->animation_list, link) {
- animation->frame_counter++;
- animation->frame(animation, output, output->frame_time);
- }
-
- TL_POINT("core_repaint_posted", TLP_OUTPUT(output), TLP_END);
-
- return r;
-}
-
-static void
-weston_output_schedule_repaint_reset(struct weston_output *output)
-{
- output->repaint_scheduled = 0;
- TL_POINT("core_repaint_exit_loop", TLP_OUTPUT(output), TLP_END);
-}
-
-static int
-output_repaint_timer_handler(void *data)
-{
- struct weston_output *output = data;
- struct weston_compositor *compositor = output->compositor;
-
- if (output->repaint_needed &&
- compositor->state != WESTON_COMPOSITOR_SLEEPING &&
- compositor->state != WESTON_COMPOSITOR_OFFSCREEN &&
- weston_output_repaint(output) == 0)
- return 0;
-
- weston_output_schedule_repaint_reset(output);
-
- return 0;
-}
-
-WL_EXPORT void
-weston_output_finish_frame(struct weston_output *output,
- const struct timespec *stamp,
- uint32_t presented_flags)
-{
- struct weston_compositor *compositor = output->compositor;
- int32_t refresh_nsec;
- struct timespec now;
- struct timespec gone;
- int msec;
-
- TL_POINT("core_repaint_finished", TLP_OUTPUT(output),
- TLP_VBLANK(stamp), TLP_END);
-
- refresh_nsec = millihz_to_nsec(output->current_mode->refresh);
- weston_presentation_feedback_present_list(&output->feedback_list,
- output, refresh_nsec, stamp,
- output->msc,
- presented_flags);
-
- output->frame_time = stamp->tv_sec * 1000 + stamp->tv_nsec / 1000000;
-
- weston_compositor_read_presentation_clock(compositor, &now);
- timespec_sub(&gone, &now, stamp);
- msec = (refresh_nsec - timespec_to_nsec(&gone)) / 1000000; /* floor */
- msec -= compositor->repaint_msec;
-
- if (msec < -1000 || msec > 1000) {
- static bool warned;
-
- if (!warned)
- weston_log("Warning: computed repaint delay is "
- "insane: %d msec\n", msec);
- warned = true;
-
- msec = 0;
- }
-
- /* Called from restart_repaint_loop and restart happens already after
- * the deadline given by repaint_msec? In that case we delay until
- * the deadline of the next frame, to give clients a more predictable
- * timing of the repaint cycle to lock on. */
- if (presented_flags == WP_PRESENTATION_FEEDBACK_INVALID && msec < 0)
- msec += refresh_nsec / 1000000;
-
- if (msec < 1)
- output_repaint_timer_handler(output);
- else
- wl_event_source_timer_update(output->repaint_timer, msec);
-}
-
-static void
-idle_repaint(void *data)
-{
- struct weston_output *output = data;
-
- output->start_repaint_loop(output);
-}
-
-WL_EXPORT void
-weston_layer_entry_insert(struct weston_layer_entry *list,
- struct weston_layer_entry *entry)
-{
- wl_list_insert(&list->link, &entry->link);
- entry->layer = list->layer;
-}
-
-WL_EXPORT void
-weston_layer_entry_remove(struct weston_layer_entry *entry)
-{
- wl_list_remove(&entry->link);
- wl_list_init(&entry->link);
- entry->layer = NULL;
-}
-
-WL_EXPORT void
-weston_layer_init(struct weston_layer *layer, struct wl_list *below)
-{
- wl_list_init(&layer->view_list.link);
- layer->view_list.layer = layer;
- weston_layer_set_mask_infinite(layer);
- if (below != NULL)
- wl_list_insert(below, &layer->link);
-}
-
-WL_EXPORT void
-weston_layer_set_mask(struct weston_layer *layer,
- int x, int y, int width, int height)
-{
- struct weston_view *view;
-
- layer->mask.x1 = x;
- layer->mask.x2 = x + width;
- layer->mask.y1 = y;
- layer->mask.y2 = y + height;
-
- wl_list_for_each(view, &layer->view_list.link, layer_link.link) {
- weston_view_geometry_dirty(view);
- }
-}
-
-WL_EXPORT void
-weston_layer_set_mask_infinite(struct weston_layer *layer)
-{
- weston_layer_set_mask(layer, INT32_MIN, INT32_MIN,
- UINT32_MAX, UINT32_MAX);
-}
-
-WL_EXPORT void
-weston_output_schedule_repaint(struct weston_output *output)
-{
- struct weston_compositor *compositor = output->compositor;
- struct wl_event_loop *loop;
-
- if (compositor->state == WESTON_COMPOSITOR_SLEEPING ||
- compositor->state == WESTON_COMPOSITOR_OFFSCREEN)
- return;
-
- if (!output->repaint_needed)
- TL_POINT("core_repaint_req", TLP_OUTPUT(output), TLP_END);
-
- loop = wl_display_get_event_loop(compositor->wl_display);
- output->repaint_needed = 1;
- if (output->repaint_scheduled)
- return;
-
- wl_event_loop_add_idle(loop, idle_repaint, output);
- output->repaint_scheduled = 1;
- TL_POINT("core_repaint_enter_loop", TLP_OUTPUT(output), TLP_END);
-}
-
-WL_EXPORT void
-weston_compositor_schedule_repaint(struct weston_compositor *compositor)
-{
- struct weston_output *output;
-
- wl_list_for_each(output, &compositor->output_list, link)
- weston_output_schedule_repaint(output);
-}
-
-static void
-surface_destroy(struct wl_client *client, struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static void
-surface_attach(struct wl_client *client,
- struct wl_resource *resource,
- struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
-{
- struct weston_surface *surface = wl_resource_get_user_data(resource);
- struct weston_buffer *buffer = NULL;
-
- if (buffer_resource) {
- buffer = weston_buffer_from_resource(buffer_resource);
- if (buffer == NULL) {
- wl_client_post_no_memory(client);
- return;
- }
- }
-
- /* Attach, attach, without commit in between does not send
- * wl_buffer.release. */
- weston_surface_state_set_buffer(&surface->pending, buffer);
-
- surface->pending.sx = sx;
- surface->pending.sy = sy;
- surface->pending.newly_attached = 1;
-}
-
-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 *surface = wl_resource_get_user_data(resource);
-
- if (width <= 0 || height <= 0)
- return;
-
- pixman_region32_union_rect(&surface->pending.damage_surface,
- &surface->pending.damage_surface,
- x, y, width, height);
-}
-
-static void
-surface_damage_buffer(struct wl_client *client,
- struct wl_resource *resource,
- int32_t x, int32_t y, int32_t width, int32_t height)
-{
- struct weston_surface *surface = wl_resource_get_user_data(resource);
-
- if (width <= 0 || height <= 0)
- return;
-
- pixman_region32_union_rect(&surface->pending.damage_buffer,
- &surface->pending.damage_buffer,
- x, y, width, height);
-}
-
-static void
-destroy_frame_callback(struct wl_resource *resource)
-{
- struct weston_frame_callback *cb = wl_resource_get_user_data(resource);
-
- 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 *surface = wl_resource_get_user_data(resource);
-
- cb = malloc(sizeof *cb);
- if (cb == NULL) {
- wl_resource_post_no_memory(resource);
- return;
- }
-
- cb->resource = wl_resource_create(client, &wl_callback_interface, 1,
- callback);
- if (cb->resource == NULL) {
- free(cb);
- wl_resource_post_no_memory(resource);
- return;
- }
-
- wl_resource_set_implementation(cb->resource, NULL, cb,
- destroy_frame_callback);
-
- wl_list_insert(surface->pending.frame_callback_list.prev, &cb->link);
-}
-
-static void
-surface_set_opaque_region(struct wl_client *client,
- struct wl_resource *resource,
- struct wl_resource *region_resource)
-{
- struct weston_surface *surface = wl_resource_get_user_data(resource);
- struct weston_region *region;
-
- if (region_resource) {
- region = wl_resource_get_user_data(region_resource);
- pixman_region32_copy(&surface->pending.opaque,
- ®ion->region);
- } else {
- pixman_region32_clear(&surface->pending.opaque);
- }
-}
-
-static void
-surface_set_input_region(struct wl_client *client,
- struct wl_resource *resource,
- struct wl_resource *region_resource)
-{
- struct weston_surface *surface = wl_resource_get_user_data(resource);
- struct weston_region *region;
-
- if (region_resource) {
- region = wl_resource_get_user_data(region_resource);
- pixman_region32_copy(&surface->pending.input,
- ®ion->region);
- } else {
- pixman_region32_fini(&surface->pending.input);
- region_init_infinite(&surface->pending.input);
- }
-}
-
-static void
-weston_surface_commit_subsurface_order(struct weston_surface *surface)
-{
- struct weston_subsurface *sub;
-
- wl_list_for_each_reverse(sub, &surface->subsurface_list_pending,
- parent_link_pending) {
- wl_list_remove(&sub->parent_link);
- wl_list_insert(&surface->subsurface_list, &sub->parent_link);
- }
-}
-
-static void
-weston_surface_build_buffer_matrix(const struct weston_surface *surface,
- struct weston_matrix *matrix)
-{
- const struct weston_buffer_viewport *vp = &surface->buffer_viewport;
- double src_width, src_height, dest_width, dest_height;
-
- weston_matrix_init(matrix);
-
- if (vp->buffer.src_width == wl_fixed_from_int(-1)) {
- src_width = surface->width_from_buffer;
- src_height = surface->height_from_buffer;
- } else {
- src_width = wl_fixed_to_double(vp->buffer.src_width);
- src_height = wl_fixed_to_double(vp->buffer.src_height);
- }
-
- if (vp->surface.width == -1) {
- dest_width = src_width;
- dest_height = src_height;
- } else {
- dest_width = vp->surface.width;
- dest_height = vp->surface.height;
- }
-
- if (src_width != dest_width || src_height != dest_height)
- weston_matrix_scale(matrix,
- src_width / dest_width,
- src_height / dest_height, 1);
-
- if (vp->buffer.src_width != wl_fixed_from_int(-1))
- weston_matrix_translate(matrix,
- wl_fixed_to_double(vp->buffer.src_x),
- wl_fixed_to_double(vp->buffer.src_y),
- 0);
-
- switch (vp->buffer.transform) {
- case WL_OUTPUT_TRANSFORM_FLIPPED:
- case WL_OUTPUT_TRANSFORM_FLIPPED_90:
- case WL_OUTPUT_TRANSFORM_FLIPPED_180:
- case WL_OUTPUT_TRANSFORM_FLIPPED_270:
- weston_matrix_scale(matrix, -1, 1, 1);
- weston_matrix_translate(matrix,
- surface->width_from_buffer, 0, 0);
- break;
- }
-
- switch (vp->buffer.transform) {
- default:
- case WL_OUTPUT_TRANSFORM_NORMAL:
- case WL_OUTPUT_TRANSFORM_FLIPPED:
- break;
- case WL_OUTPUT_TRANSFORM_90:
- case WL_OUTPUT_TRANSFORM_FLIPPED_90:
- weston_matrix_rotate_xy(matrix, 0, 1);
- weston_matrix_translate(matrix,
- surface->height_from_buffer, 0, 0);
- break;
- case WL_OUTPUT_TRANSFORM_180:
- case WL_OUTPUT_TRANSFORM_FLIPPED_180:
- weston_matrix_rotate_xy(matrix, -1, 0);
- weston_matrix_translate(matrix,
- surface->width_from_buffer,
- surface->height_from_buffer, 0);
- break;
- case WL_OUTPUT_TRANSFORM_270:
- case WL_OUTPUT_TRANSFORM_FLIPPED_270:
- weston_matrix_rotate_xy(matrix, 0, -1);
- weston_matrix_translate(matrix,
- 0, surface->width_from_buffer, 0);
- break;
- }
-
- weston_matrix_scale(matrix, vp->buffer.scale, vp->buffer.scale, 1);
-}
-
-/**
- * Compute a + b > c while being safe to overflows.
- */
-static bool
-fixed_sum_gt(wl_fixed_t a, wl_fixed_t b, wl_fixed_t c)
-{
- return (int64_t)a + (int64_t)b > (int64_t)c;
-}
-
-static bool
-weston_surface_is_pending_viewport_source_valid(
- const struct weston_surface *surface)
-{
- const struct weston_surface_state *pend = &surface->pending;
- const struct weston_buffer_viewport *vp = &pend->buffer_viewport;
- int width_from_buffer = 0;
- int height_from_buffer = 0;
- wl_fixed_t w;
- wl_fixed_t h;
-
- /* If viewport source rect is not set, it is always ok. */
- if (vp->buffer.src_width == wl_fixed_from_int(-1))
- return true;
-
- if (pend->newly_attached) {
- if (pend->buffer) {
- convert_size_by_transform_scale(&width_from_buffer,
- &height_from_buffer,
- pend->buffer->width,
- pend->buffer->height,
- vp->buffer.transform,
- vp->buffer.scale);
- } else {
- /* No buffer: viewport is irrelevant. */
- return true;
- }
- } else {
- width_from_buffer = surface->width_from_buffer;
- height_from_buffer = surface->height_from_buffer;
- }
-
- assert((width_from_buffer == 0) == (height_from_buffer == 0));
- assert(width_from_buffer >= 0 && height_from_buffer >= 0);
-
- /* No buffer: viewport is irrelevant. */
- if (width_from_buffer == 0 || height_from_buffer == 0)
- return true;
-
- /* overflow checks for wl_fixed_from_int() */
- if (width_from_buffer > wl_fixed_to_int(INT32_MAX))
- return false;
- if (height_from_buffer > wl_fixed_to_int(INT32_MAX))
- return false;
-
- w = wl_fixed_from_int(width_from_buffer);
- h = wl_fixed_from_int(height_from_buffer);
-
- if (fixed_sum_gt(vp->buffer.src_x, vp->buffer.src_width, w))
- return false;
- if (fixed_sum_gt(vp->buffer.src_y, vp->buffer.src_height, h))
- return false;
-
- return true;
-}
-
-static bool
-fixed_is_integer(wl_fixed_t v)
-{
- return (v & 0xff) == 0;
-}
-
-static bool
-weston_surface_is_pending_viewport_dst_size_int(
- const struct weston_surface *surface)
-{
- const struct weston_buffer_viewport *vp =
- &surface->pending.buffer_viewport;
-
- if (vp->surface.width != -1) {
- assert(vp->surface.width > 0 && vp->surface.height > 0);
- return true;
- }
-
- return fixed_is_integer(vp->buffer.src_width) &&
- fixed_is_integer(vp->buffer.src_height);
-}
-
-/* Translate pending damage in buffer co-ordinates to surface
- * co-ordinates and union it with a pixman_region32_t.
- * This should only be called after the buffer is attached.
- */
-static void
-apply_damage_buffer(pixman_region32_t *dest,
- struct weston_surface *surface,
- struct weston_surface_state *state)
-{
- struct weston_buffer *buffer = surface->buffer_ref.buffer;
-
- /* wl_surface.damage_buffer needs to be clipped to the buffer,
- * translated into surface co-ordinates and unioned with
- * any other surface damage.
- * None of this makes sense if there is no buffer though.
- */
- if (buffer && pixman_region32_not_empty(&state->damage_buffer)) {
- pixman_region32_t buffer_damage;
-
- pixman_region32_intersect_rect(&state->damage_buffer,
- &state->damage_buffer,
- 0, 0, buffer->width,
- buffer->height);
- pixman_region32_init(&buffer_damage);
- weston_matrix_transform_region(&buffer_damage,
- &surface->buffer_to_surface_matrix,
- &state->damage_buffer);
- pixman_region32_union(dest, dest, &buffer_damage);
- pixman_region32_fini(&buffer_damage);
- }
- /* We should clear this on commit even if there was no buffer */
- pixman_region32_clear(&state->damage_buffer);
-}
-
-static void
-weston_surface_commit_state(struct weston_surface *surface,
- struct weston_surface_state *state)
-{
- struct weston_view *view;
- pixman_region32_t opaque;
-
- /* wl_surface.set_buffer_transform */
- /* wl_surface.set_buffer_scale */
- /* wp_viewport.set_source */
- /* wp_viewport.set_destination */
- surface->buffer_viewport = state->buffer_viewport;
-
- /* wl_surface.attach */
- if (state->newly_attached)
- weston_surface_attach(surface, state->buffer);
- weston_surface_state_set_buffer(state, NULL);
-
- weston_surface_build_buffer_matrix(surface,
- &surface->surface_to_buffer_matrix);
- weston_matrix_invert(&surface->buffer_to_surface_matrix,
- &surface->surface_to_buffer_matrix);
-
- if (state->newly_attached || state->buffer_viewport.changed) {
- weston_surface_update_size(surface);
- if (surface->configure)
- surface->configure(surface, state->sx, state->sy);
- }
-
- state->sx = 0;
- state->sy = 0;
- state->newly_attached = 0;
- state->buffer_viewport.changed = 0;
-
- /* wl_surface.damage and wl_surface.damage_buffer */
- if (weston_timeline_enabled_ &&
- (pixman_region32_not_empty(&state->damage_surface) ||
- pixman_region32_not_empty(&state->damage_buffer)))
- TL_POINT("core_commit_damage", TLP_SURFACE(surface), TLP_END);
-
- pixman_region32_union(&surface->damage, &surface->damage,
- &state->damage_surface);
-
- apply_damage_buffer(&surface->damage, surface, state);
-
- pixman_region32_intersect_rect(&surface->damage, &surface->damage,
- 0, 0, surface->width, surface->height);
- pixman_region32_clear(&state->damage_surface);
-
- /* wl_surface.set_opaque_region */
- pixman_region32_init(&opaque);
- pixman_region32_intersect_rect(&opaque, &state->opaque,
- 0, 0, surface->width, surface->height);
-
- if (!pixman_region32_equal(&opaque, &surface->opaque)) {
- pixman_region32_copy(&surface->opaque, &opaque);
- wl_list_for_each(view, &surface->views, surface_link)
- weston_view_geometry_dirty(view);
- }
-
- pixman_region32_fini(&opaque);
-
- /* wl_surface.set_input_region */
- pixman_region32_intersect_rect(&surface->input, &state->input,
- 0, 0, surface->width, surface->height);
-
- /* wl_surface.frame */
- wl_list_insert_list(&surface->frame_callback_list,
- &state->frame_callback_list);
- wl_list_init(&state->frame_callback_list);
-
- /* XXX:
- * What should happen with a feedback request, if there
- * is no wl_buffer attached for this commit?
- */
-
- /* presentation.feedback */
- wl_list_insert_list(&surface->feedback_list,
- &state->feedback_list);
- wl_list_init(&state->feedback_list);
-}
-
-static void
-weston_surface_commit(struct weston_surface *surface)
-{
- weston_surface_commit_state(surface, &surface->pending);
-
- weston_surface_commit_subsurface_order(surface);
-
- weston_surface_schedule_repaint(surface);
-}
-
-static void
-weston_subsurface_commit(struct weston_subsurface *sub);
-
-static void
-weston_subsurface_parent_commit(struct weston_subsurface *sub,
- int parent_is_synchronized);
-
-static void
-surface_commit(struct wl_client *client, struct wl_resource *resource)
-{
- struct weston_surface *surface = wl_resource_get_user_data(resource);
- struct weston_subsurface *sub = weston_surface_to_subsurface(surface);
-
- if (!weston_surface_is_pending_viewport_source_valid(surface)) {
- assert(surface->viewport_resource);
-
- wl_resource_post_error(surface->viewport_resource,
- WP_VIEWPORT_ERROR_OUT_OF_BUFFER,
- "wl_surface@%d has viewport source outside buffer",
- wl_resource_get_id(resource));
- return;
- }
-
- if (!weston_surface_is_pending_viewport_dst_size_int(surface)) {
- assert(surface->viewport_resource);
-
- wl_resource_post_error(surface->viewport_resource,
- WP_VIEWPORT_ERROR_BAD_SIZE,
- "wl_surface@%d viewport dst size not integer",
- wl_resource_get_id(resource));
- return;
- }
-
- if (sub) {
- weston_subsurface_commit(sub);
- return;
- }
-
- weston_surface_commit(surface);
-
- wl_list_for_each(sub, &surface->subsurface_list, parent_link) {
- if (sub->surface != surface)
- weston_subsurface_parent_commit(sub, 0);
- }
-}
-
-static void
-surface_set_buffer_transform(struct wl_client *client,
- struct wl_resource *resource, int transform)
-{
- struct weston_surface *surface = wl_resource_get_user_data(resource);
-
- /* if wl_output.transform grows more members this will need to be updated. */
- if (transform < 0 ||
- transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) {
- wl_resource_post_error(resource,
- WL_SURFACE_ERROR_INVALID_TRANSFORM,
- "buffer transform must be a valid transform "
- "('%d' specified)", transform);
- return;
- }
-
- surface->pending.buffer_viewport.buffer.transform = transform;
- surface->pending.buffer_viewport.changed = 1;
-}
-
-static void
-surface_set_buffer_scale(struct wl_client *client,
- struct wl_resource *resource,
- int32_t scale)
-{
- struct weston_surface *surface = wl_resource_get_user_data(resource);
-
- if (scale < 1) {
- wl_resource_post_error(resource,
- WL_SURFACE_ERROR_INVALID_SCALE,
- "buffer scale must be at least one "
- "('%d' specified)", scale);
- return;
- }
-
- surface->pending.buffer_viewport.buffer.scale = scale;
- surface->pending.buffer_viewport.changed = 1;
-}
-
-static const struct wl_surface_interface surface_interface = {
- surface_destroy,
- surface_attach,
- surface_damage,
- surface_frame,
- surface_set_opaque_region,
- surface_set_input_region,
- surface_commit,
- surface_set_buffer_transform,
- surface_set_buffer_scale,
- surface_damage_buffer
-};
-
-static void
-compositor_create_surface(struct wl_client *client,
- struct wl_resource *resource, uint32_t id)
-{
- struct weston_compositor *ec = wl_resource_get_user_data(resource);
- struct weston_surface *surface;
-
- surface = weston_surface_create(ec);
- if (surface == NULL) {
- wl_resource_post_no_memory(resource);
- return;
- }
-
- surface->resource =
- wl_resource_create(client, &wl_surface_interface,
- wl_resource_get_version(resource), id);
- if (surface->resource == NULL) {
- weston_surface_destroy(surface);
- wl_resource_post_no_memory(resource);
- return;
- }
- wl_resource_set_implementation(surface->resource, &surface_interface,
- surface, destroy_surface);
-
- wl_signal_emit(&ec->create_surface_signal, surface);
-}
-
-static void
-destroy_region(struct wl_resource *resource)
-{
- struct weston_region *region = wl_resource_get_user_data(resource);
-
- pixman_region32_fini(®ion->region);
- free(region);
-}
-
-static void
-region_destroy(struct wl_client *client, struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static void
-region_add(struct wl_client *client, struct wl_resource *resource,
- int32_t x, int32_t y, int32_t width, int32_t height)
-{
- struct weston_region *region = wl_resource_get_user_data(resource);
-
- pixman_region32_union_rect(®ion->region, ®ion->region,
- x, y, width, height);
-}
-
-static void
-region_subtract(struct wl_client *client, struct wl_resource *resource,
- int32_t x, int32_t y, int32_t width, int32_t height)
-{
- struct weston_region *region = wl_resource_get_user_data(resource);
- pixman_region32_t rect;
-
- pixman_region32_init_rect(&rect, x, y, width, height);
- pixman_region32_subtract(®ion->region, ®ion->region, &rect);
- pixman_region32_fini(&rect);
-}
-
-static const struct wl_region_interface region_interface = {
- region_destroy,
- region_add,
- region_subtract
-};
-
-static void
-compositor_create_region(struct wl_client *client,
- struct wl_resource *resource, uint32_t id)
-{
- struct weston_region *region;
-
- region = malloc(sizeof *region);
- if (region == NULL) {
- wl_resource_post_no_memory(resource);
- return;
- }
-
- pixman_region32_init(®ion->region);
-
- region->resource =
- wl_resource_create(client, &wl_region_interface, 1, id);
- if (region->resource == NULL) {
- free(region);
- wl_resource_post_no_memory(resource);
- return;
- }
- wl_resource_set_implementation(region->resource, ®ion_interface,
- region, destroy_region);
-}
-
-static const struct wl_compositor_interface compositor_interface = {
- compositor_create_surface,
- compositor_create_region
-};
-
-static void
-weston_subsurface_commit_from_cache(struct weston_subsurface *sub)
-{
- struct weston_surface *surface = sub->surface;
-
- weston_surface_commit_state(surface, &sub->cached);
- weston_buffer_reference(&sub->cached_buffer_ref, NULL);
-
- weston_surface_commit_subsurface_order(surface);
-
- weston_surface_schedule_repaint(surface);
-
- sub->has_cached_data = 0;
-}
-
-static void
-weston_subsurface_commit_to_cache(struct weston_subsurface *sub)
-{
- struct weston_surface *surface = sub->surface;
-
- /*
- * If this commit would cause the surface to move by the
- * attach(dx, dy) parameters, the old damage region must be
- * translated to correspond to the new surface coordinate system
- * origin.
- */
- pixman_region32_translate(&sub->cached.damage_surface,
- -surface->pending.sx, -surface->pending.sy);
- pixman_region32_union(&sub->cached.damage_surface,
- &sub->cached.damage_surface,
- &surface->pending.damage_surface);
- pixman_region32_clear(&surface->pending.damage_surface);
-
- if (surface->pending.newly_attached) {
- sub->cached.newly_attached = 1;
- weston_surface_state_set_buffer(&sub->cached,
- surface->pending.buffer);
- weston_buffer_reference(&sub->cached_buffer_ref,
- surface->pending.buffer);
- weston_presentation_feedback_discard_list(
- &sub->cached.feedback_list);
- }
- sub->cached.sx += surface->pending.sx;
- sub->cached.sy += surface->pending.sy;
-
- apply_damage_buffer(&sub->cached.damage_surface, surface, &surface->pending);
-
- sub->cached.buffer_viewport.changed |=
- surface->pending.buffer_viewport.changed;
- sub->cached.buffer_viewport.buffer =
- surface->pending.buffer_viewport.buffer;
- sub->cached.buffer_viewport.surface =
- surface->pending.buffer_viewport.surface;
-
- weston_surface_reset_pending_buffer(surface);
-
- pixman_region32_copy(&sub->cached.opaque, &surface->pending.opaque);
-
- pixman_region32_copy(&sub->cached.input, &surface->pending.input);
-
- wl_list_insert_list(&sub->cached.frame_callback_list,
- &surface->pending.frame_callback_list);
- wl_list_init(&surface->pending.frame_callback_list);
-
- wl_list_insert_list(&sub->cached.feedback_list,
- &surface->pending.feedback_list);
- wl_list_init(&surface->pending.feedback_list);
-
- sub->has_cached_data = 1;
-}
-
-static bool
-weston_subsurface_is_synchronized(struct weston_subsurface *sub)
-{
- while (sub) {
- if (sub->synchronized)
- return true;
-
- if (!sub->parent)
- return false;
-
- sub = weston_surface_to_subsurface(sub->parent);
- }
-
- return false;
-}
-
-static void
-weston_subsurface_commit(struct weston_subsurface *sub)
-{
- struct weston_surface *surface = sub->surface;
- struct weston_subsurface *tmp;
-
- /* Recursive check for effectively synchronized. */
- if (weston_subsurface_is_synchronized(sub)) {
- weston_subsurface_commit_to_cache(sub);
- } else {
- if (sub->has_cached_data) {
- /* flush accumulated state from cache */
- weston_subsurface_commit_to_cache(sub);
- weston_subsurface_commit_from_cache(sub);
- } else {
- weston_surface_commit(surface);
- }
-
- wl_list_for_each(tmp, &surface->subsurface_list, parent_link) {
- if (tmp->surface != surface)
- weston_subsurface_parent_commit(tmp, 0);
- }
- }
-}
-
-static void
-weston_subsurface_synchronized_commit(struct weston_subsurface *sub)
-{
- struct weston_surface *surface = sub->surface;
- struct weston_subsurface *tmp;
-
- /* From now on, commit_from_cache the whole sub-tree, regardless of
- * the synchronized mode of each child. This sub-surface or some
- * of its ancestors were synchronized, so we are synchronized
- * all the way down.
- */
-
- if (sub->has_cached_data)
- weston_subsurface_commit_from_cache(sub);
-
- wl_list_for_each(tmp, &surface->subsurface_list, parent_link) {
- if (tmp->surface != surface)
- weston_subsurface_parent_commit(tmp, 1);
- }
-}
-
-static void
-weston_subsurface_parent_commit(struct weston_subsurface *sub,
- int parent_is_synchronized)
-{
- struct weston_view *view;
- if (sub->position.set) {
- wl_list_for_each(view, &sub->surface->views, surface_link)
- weston_view_set_position(view,
- sub->position.x,
- sub->position.y);
-
- sub->position.set = 0;
- }
-
- if (parent_is_synchronized || sub->synchronized)
- weston_subsurface_synchronized_commit(sub);
-}
-
-static int
-subsurface_get_label(struct weston_surface *surface, char *buf, size_t len)
-{
- return snprintf(buf, len, "sub-surface");
-}
-
-static void
-subsurface_configure(struct weston_surface *surface, int32_t dx, int32_t dy)
-{
- struct weston_compositor *compositor = surface->compositor;
- struct weston_view *view;
-
- wl_list_for_each(view, &surface->views, surface_link)
- weston_view_set_position(view,
- view->geometry.x + dx,
- view->geometry.y + dy);
-
- /* No need to check parent mappedness, because if parent is not
- * mapped, parent is not in a visible layer, so this sub-surface
- * will not be drawn either.
- */
- if (!weston_surface_is_mapped(surface)) {
- struct weston_output *output;
-
- /* Cannot call weston_view_update_transform(),
- * because that would call it also for the parent surface,
- * which might not be mapped yet. That would lead to
- * inconsistent state, where the window could never be
- * mapped.
- *
- * Instead just assign any output, to make
- * weston_surface_is_mapped() return true, so that when the
- * parent surface does get mapped, this one will get
- * included, too. See view_list_add().
- */
- assert(!wl_list_empty(&compositor->output_list));
- output = container_of(compositor->output_list.next,
- struct weston_output, link);
-
- surface->output = output;
- weston_surface_update_output_mask(surface, 1u << output->id);
- }
-}
-
-static struct weston_subsurface *
-weston_surface_to_subsurface(struct weston_surface *surface)
-{
- if (surface->configure == subsurface_configure)
- return surface->configure_private;
-
- return NULL;
-}
-
-WL_EXPORT struct weston_surface *
-weston_surface_get_main_surface(struct weston_surface *surface)
-{
- struct weston_subsurface *sub;
-
- while (surface && (sub = weston_surface_to_subsurface(surface)))
- surface = sub->parent;
-
- return surface;
-}
-
-WL_EXPORT int
-weston_surface_set_role(struct weston_surface *surface,
- const char *role_name,
- struct wl_resource *error_resource,
- uint32_t error_code)
-{
- assert(role_name);
-
- if (surface->role_name == NULL ||
- surface->role_name == role_name ||
- strcmp(surface->role_name, role_name) == 0) {
- surface->role_name = role_name;
-
- return 0;
- }
-
- wl_resource_post_error(error_resource, error_code,
- "Cannot assign role %s to wl_surface@%d,"
- " already has role %s\n",
- role_name,
- wl_resource_get_id(surface->resource),
- surface->role_name);
- return -1;
-}
-
-WL_EXPORT void
-weston_surface_set_label_func(struct weston_surface *surface,
- int (*desc)(struct weston_surface *,
- char *, size_t))
-{
- surface->get_label = desc;
- surface->timeline.force_refresh = 1;
-}
-
-/** Get the size of surface contents
- *
- * \param surface The surface to query.
- * \param width Returns the width of raw contents.
- * \param height Returns the height of raw contents.
- *
- * Retrieves the raw surface content size in pixels for the given surface.
- * This is the whole content size in buffer pixels. If the surface
- * has no content or the renderer does not implement this feature,
- * zeroes are returned.
- *
- * This function is used to determine the buffer size needed for
- * a weston_surface_copy_content() call.
- */
-WL_EXPORT void
-weston_surface_get_content_size(struct weston_surface *surface,
- int *width, int *height)
-{
- struct weston_renderer *rer = surface->compositor->renderer;
-
- if (!rer->surface_get_content_size) {
- *width = 0;
- *height = 0;
- return;
- }
-
- rer->surface_get_content_size(surface, width, height);
-}
-
-/** Copy surface contents to system memory.
- *
- * \param surface The surface to copy from.
- * \param target Pointer to the target memory buffer.
- * \param size Size of the target buffer in bytes.
- * \param src_x X location on contents to copy from.
- * \param src_y Y location on contents to copy from.
- * \param width Width in pixels of the area to copy.
- * \param height Height in pixels of the area to copy.
- * \return 0 for success, -1 for failure.
- *
- * Surface contents are maintained by the renderer. They can be in a
- * reserved weston_buffer or as a copy, e.g. a GL texture, or something
- * else.
- *
- * Surface contents are copied into memory pointed to by target,
- * which has size bytes of space available. The target memory
- * may be larger than needed, but being smaller returns an error.
- * The extra bytes in target may or may not be written; their content is
- * unspecified. Size must be large enough to hold the image.
- *
- * The image in the target memory will be arranged in rows from
- * top to bottom, and pixels on a row from left to right. The pixel
- * format is PIXMAN_a8b8g8r8, 4 bytes per pixel, and stride is exactly
- * width * 4.
- *
- * Parameters src_x and src_y define the upper-left corner in buffer
- * coordinates (pixels) to copy from. Parameters width and height
- * define the size of the area to copy in pixels.
- *
- * The rectangle defined by src_x, src_y, width, height must fit in
- * the surface contents. Otherwise an error is returned.
- *
- * Use surface_get_data_size to determine the content size; the
- * needed target buffer size and rectangle limits.
- *
- * CURRENT IMPLEMENTATION RESTRICTIONS:
- * - the machine must be little-endian due to Pixman formats.
- *
- * NOTE: Pixman formats are premultiplied.
- */
-WL_EXPORT int
-weston_surface_copy_content(struct weston_surface *surface,
- void *target, size_t size,
- int src_x, int src_y,
- int width, int height)
-{
- struct weston_renderer *rer = surface->compositor->renderer;
- int cw, ch;
- const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */
-
- if (!rer->surface_copy_content)
- return -1;
-
- weston_surface_get_content_size(surface, &cw, &ch);
-
- if (src_x < 0 || src_y < 0)
- return -1;
-
- if (width <= 0 || height <= 0)
- return -1;
-
- if (src_x + width > cw || src_y + height > ch)
- return -1;
-
- if (width * bytespp * height > size)
- return -1;
-
- return rer->surface_copy_content(surface, target, size,
- src_x, src_y, width, height);
-}
-
-static void
-subsurface_set_position(struct wl_client *client,
- struct wl_resource *resource, int32_t x, int32_t y)
-{
- struct weston_subsurface *sub = wl_resource_get_user_data(resource);
-
- if (!sub)
- return;
-
- sub->position.x = x;
- sub->position.y = y;
- sub->position.set = 1;
-}
-
-static struct weston_subsurface *
-subsurface_from_surface(struct weston_surface *surface)
-{
- struct weston_subsurface *sub;
-
- sub = weston_surface_to_subsurface(surface);
- if (sub)
- return sub;
-
- wl_list_for_each(sub, &surface->subsurface_list, parent_link)
- if (sub->surface == surface)
- return sub;
-
- return NULL;
-}
-
-static struct weston_subsurface *
-subsurface_sibling_check(struct weston_subsurface *sub,
- struct weston_surface *surface,
- const char *request)
-{
- struct weston_subsurface *sibling;
-
- sibling = subsurface_from_surface(surface);
-
- if (!sibling) {
- wl_resource_post_error(sub->resource,
- WL_SUBSURFACE_ERROR_BAD_SURFACE,
- "%s: wl_surface@%d is not a parent or sibling",
- request, wl_resource_get_id(surface->resource));
- return NULL;
- }
-
- if (sibling->parent != sub->parent) {
- wl_resource_post_error(sub->resource,
- WL_SUBSURFACE_ERROR_BAD_SURFACE,
- "%s: wl_surface@%d has a different parent",
- request, wl_resource_get_id(surface->resource));
- return NULL;
- }
-
- return sibling;
-}
-
-static void
-subsurface_place_above(struct wl_client *client,
- struct wl_resource *resource,
- struct wl_resource *sibling_resource)
-{
- struct weston_subsurface *sub = wl_resource_get_user_data(resource);
- struct weston_surface *surface =
- wl_resource_get_user_data(sibling_resource);
- struct weston_subsurface *sibling;
-
- if (!sub)
- return;
-
- sibling = subsurface_sibling_check(sub, surface, "place_above");
- if (!sibling)
- return;
-
- wl_list_remove(&sub->parent_link_pending);
- wl_list_insert(sibling->parent_link_pending.prev,
- &sub->parent_link_pending);
-}
-
-static void
-subsurface_place_below(struct wl_client *client,
- struct wl_resource *resource,
- struct wl_resource *sibling_resource)
-{
- struct weston_subsurface *sub = wl_resource_get_user_data(resource);
- struct weston_surface *surface =
- wl_resource_get_user_data(sibling_resource);
- struct weston_subsurface *sibling;
-
- if (!sub)
- return;
-
- sibling = subsurface_sibling_check(sub, surface, "place_below");
- if (!sibling)
- return;
-
- wl_list_remove(&sub->parent_link_pending);
- wl_list_insert(&sibling->parent_link_pending,
- &sub->parent_link_pending);
-}
-
-static void
-subsurface_set_sync(struct wl_client *client, struct wl_resource *resource)
-{
- struct weston_subsurface *sub = wl_resource_get_user_data(resource);
-
- if (sub)
- sub->synchronized = 1;
-}
-
-static void
-subsurface_set_desync(struct wl_client *client, struct wl_resource *resource)
-{
- struct weston_subsurface *sub = wl_resource_get_user_data(resource);
-
- if (sub && sub->synchronized) {
- sub->synchronized = 0;
-
- /* If sub became effectively desynchronized, flush. */
- if (!weston_subsurface_is_synchronized(sub))
- weston_subsurface_synchronized_commit(sub);
- }
-}
-
-static void
-weston_subsurface_unlink_parent(struct weston_subsurface *sub)
-{
- wl_list_remove(&sub->parent_link);
- wl_list_remove(&sub->parent_link_pending);
- wl_list_remove(&sub->parent_destroy_listener.link);
- sub->parent = NULL;
-}
-
-static void
-weston_subsurface_destroy(struct weston_subsurface *sub);
-
-static void
-subsurface_handle_surface_destroy(struct wl_listener *listener, void *data)
-{
- struct weston_subsurface *sub =
- container_of(listener, struct weston_subsurface,
- surface_destroy_listener);
- assert(data == sub->surface);
-
- /* The protocol object (wl_resource) is left inert. */
- if (sub->resource)
- wl_resource_set_user_data(sub->resource, NULL);
-
- weston_subsurface_destroy(sub);
-}
-
-static void
-subsurface_handle_parent_destroy(struct wl_listener *listener, void *data)
-{
- struct weston_subsurface *sub =
- container_of(listener, struct weston_subsurface,
- parent_destroy_listener);
- assert(data == sub->parent);
- assert(sub->surface != sub->parent);
-
- if (weston_surface_is_mapped(sub->surface))
- weston_surface_unmap(sub->surface);
-
- weston_subsurface_unlink_parent(sub);
-}
-
-static void
-subsurface_resource_destroy(struct wl_resource *resource)
-{
- struct weston_subsurface *sub = wl_resource_get_user_data(resource);
-
- if (sub)
- weston_subsurface_destroy(sub);
-}
-
-static void
-subsurface_destroy(struct wl_client *client, struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static void
-weston_subsurface_link_parent(struct weston_subsurface *sub,
- struct weston_surface *parent)
-{
- sub->parent = parent;
- sub->parent_destroy_listener.notify = subsurface_handle_parent_destroy;
- wl_signal_add(&parent->destroy_signal,
- &sub->parent_destroy_listener);
-
- wl_list_insert(&parent->subsurface_list, &sub->parent_link);
- wl_list_insert(&parent->subsurface_list_pending,
- &sub->parent_link_pending);
-}
-
-static void
-weston_subsurface_link_surface(struct weston_subsurface *sub,
- struct weston_surface *surface)
-{
- sub->surface = surface;
- sub->surface_destroy_listener.notify =
- subsurface_handle_surface_destroy;
- wl_signal_add(&surface->destroy_signal,
- &sub->surface_destroy_listener);
-}
-
-static void
-weston_subsurface_destroy(struct weston_subsurface *sub)
-{
- struct weston_view *view, *next;
-
- assert(sub->surface);
-
- if (sub->resource) {
- assert(weston_surface_to_subsurface(sub->surface) == sub);
- assert(sub->parent_destroy_listener.notify ==
- subsurface_handle_parent_destroy);
-
- wl_list_for_each_safe(view, next, &sub->surface->views, surface_link) {
- weston_view_unmap(view);
- weston_view_destroy(view);
- }
-
- if (sub->parent)
- weston_subsurface_unlink_parent(sub);
-
- weston_surface_state_fini(&sub->cached);
- weston_buffer_reference(&sub->cached_buffer_ref, NULL);
-
- sub->surface->configure = NULL;
- sub->surface->configure_private = NULL;
- weston_surface_set_label_func(sub->surface, NULL);
- } else {
- /* the dummy weston_subsurface for the parent itself */
- assert(sub->parent_destroy_listener.notify == NULL);
- wl_list_remove(&sub->parent_link);
- wl_list_remove(&sub->parent_link_pending);
- }
-
- wl_list_remove(&sub->surface_destroy_listener.link);
- free(sub);
-}
-
-static const struct wl_subsurface_interface subsurface_implementation = {
- subsurface_destroy,
- subsurface_set_position,
- subsurface_place_above,
- subsurface_place_below,
- subsurface_set_sync,
- subsurface_set_desync
-};
-
-static struct weston_subsurface *
-weston_subsurface_create(uint32_t id, struct weston_surface *surface,
- struct weston_surface *parent)
-{
- struct weston_subsurface *sub;
- struct wl_client *client = wl_resource_get_client(surface->resource);
-
- sub = zalloc(sizeof *sub);
- if (sub == NULL)
- return NULL;
-
- wl_list_init(&sub->unused_views);
-
- sub->resource =
- wl_resource_create(client, &wl_subsurface_interface, 1, id);
- if (!sub->resource) {
- free(sub);
- return NULL;
- }
-
- wl_resource_set_implementation(sub->resource,
- &subsurface_implementation,
- sub, subsurface_resource_destroy);
- weston_subsurface_link_surface(sub, surface);
- weston_subsurface_link_parent(sub, parent);
- weston_surface_state_init(&sub->cached);
- sub->cached_buffer_ref.buffer = NULL;
- sub->synchronized = 1;
-
- return sub;
-}
-
-/* Create a dummy subsurface for having the parent itself in its
- * sub-surface lists. Makes stacking order manipulation easy.
- */
-static struct weston_subsurface *
-weston_subsurface_create_for_parent(struct weston_surface *parent)
-{
- struct weston_subsurface *sub;
-
- sub = zalloc(sizeof *sub);
- if (sub == NULL)
- return NULL;
-
- weston_subsurface_link_surface(sub, parent);
- sub->parent = parent;
- wl_list_insert(&parent->subsurface_list, &sub->parent_link);
- wl_list_insert(&parent->subsurface_list_pending,
- &sub->parent_link_pending);
-
- return sub;
-}
-
-static void
-subcompositor_get_subsurface(struct wl_client *client,
- struct wl_resource *resource,
- uint32_t id,
- struct wl_resource *surface_resource,
- struct wl_resource *parent_resource)
-{
- struct weston_surface *surface =
- wl_resource_get_user_data(surface_resource);
- struct weston_surface *parent =
- wl_resource_get_user_data(parent_resource);
- struct weston_subsurface *sub;
- static const char where[] = "get_subsurface: wl_subsurface@";
-
- if (surface == parent) {
- wl_resource_post_error(resource,
- WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
- "%s%d: wl_surface@%d cannot be its own parent",
- where, id, wl_resource_get_id(surface_resource));
- return;
- }
-
- if (weston_surface_to_subsurface(surface)) {
- wl_resource_post_error(resource,
- WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
- "%s%d: wl_surface@%d is already a sub-surface",
- where, id, wl_resource_get_id(surface_resource));
- return;
- }
-
- if (weston_surface_set_role(surface, "wl_subsurface", resource,
- WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE) < 0)
- return;
-
- if (weston_surface_get_main_surface(parent) == surface) {
- wl_resource_post_error(resource,
- WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
- "%s%d: wl_surface@%d is an ancestor of parent",
- where, id, wl_resource_get_id(surface_resource));
- return;
- }
-
- /* make sure the parent is in its own list */
- if (wl_list_empty(&parent->subsurface_list)) {
- if (!weston_subsurface_create_for_parent(parent)) {
- wl_resource_post_no_memory(resource);
- return;
- }
- }
-
- sub = weston_subsurface_create(id, surface, parent);
- if (!sub) {
- wl_resource_post_no_memory(resource);
- return;
- }
-
- surface->configure = subsurface_configure;
- surface->configure_private = sub;
- weston_surface_set_label_func(surface, subsurface_get_label);
-}
-
-static void
-subcompositor_destroy(struct wl_client *client, struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static const struct wl_subcompositor_interface subcompositor_interface = {
- subcompositor_destroy,
- subcompositor_get_subsurface
-};
-
-static void
-bind_subcompositor(struct wl_client *client,
- void *data, uint32_t version, uint32_t id)
-{
- struct weston_compositor *compositor = data;
- struct wl_resource *resource;
-
- resource =
- wl_resource_create(client, &wl_subcompositor_interface, 1, id);
- if (resource == NULL) {
- wl_client_post_no_memory(client);
- return;
- }
- wl_resource_set_implementation(resource, &subcompositor_interface,
- compositor, NULL);
-}
-
-/** Set a DPMS mode on all of the compositor's outputs
- *
- * \param compositor The compositor instance
- * \param state The DPMS state the outputs will be set to
- */
-static void
-weston_compositor_dpms(struct weston_compositor *compositor,
- enum dpms_enum state)
-{
- struct weston_output *output;
-
- wl_list_for_each(output, &compositor->output_list, link)
- if (output->set_dpms)
- output->set_dpms(output, state);
-}
-
-/** Restores the compositor to active status
- *
- * \param compositor The compositor instance
- *
- * If the compositor was in a sleeping mode, all outputs are powered
- * back on via DPMS. Otherwise if the compositor was inactive
- * (idle/locked, offscreen, or sleeping) then the compositor's wake
- * signal will fire.
- *
- * Restarts the idle timer.
- */
-WL_EXPORT void
-weston_compositor_wake(struct weston_compositor *compositor)
-{
- uint32_t old_state = compositor->state;
-
- /* The state needs to be changed before emitting the wake
- * signal because that may try to schedule a repaint which
- * will not work if the compositor is still sleeping */
- compositor->state = WESTON_COMPOSITOR_ACTIVE;
-
- switch (old_state) {
- case WESTON_COMPOSITOR_SLEEPING:
- weston_compositor_dpms(compositor, WESTON_DPMS_ON);
- /* fall through */
- case WESTON_COMPOSITOR_IDLE:
- case WESTON_COMPOSITOR_OFFSCREEN:
- wl_signal_emit(&compositor->wake_signal, compositor);
- /* fall through */
- default:
- wl_event_source_timer_update(compositor->idle_source,
- compositor->idle_time * 1000);
- }
-}
-
-/** Turns off rendering and frame events for the compositor.
- *
- * \param compositor The compositor instance
- *
- * This is used for example to prevent further rendering while the
- * compositor is shutting down.
- *
- * \note When offscreen state is entered, outputs will be powered
- * back on if they were sleeping (in DPMS off mode), even though
- * no rendering will be performed.
- *
- * Stops the idle timer.
- */
-WL_EXPORT void
-weston_compositor_offscreen(struct weston_compositor *compositor)
-{
- switch (compositor->state) {
- case WESTON_COMPOSITOR_OFFSCREEN:
- return;
- case WESTON_COMPOSITOR_SLEEPING:
- weston_compositor_dpms(compositor, WESTON_DPMS_ON);
- /* fall through */
- default:
- compositor->state = WESTON_COMPOSITOR_OFFSCREEN;
- wl_event_source_timer_update(compositor->idle_source, 0);
- }
-}
-
-/** Powers down all attached output devices
- *
- * \param compositor The compositor instance
- *
- * Causes rendering to the outputs to cease, and no frame events to be
- * sent. Only powers down the outputs if the compositor is not already
- * in sleep mode.
- *
- * Stops the idle timer.
- */
-WL_EXPORT void
-weston_compositor_sleep(struct weston_compositor *compositor)
-{
- if (compositor->state == WESTON_COMPOSITOR_SLEEPING)
- return;
-
- wl_event_source_timer_update(compositor->idle_source, 0);
- compositor->state = WESTON_COMPOSITOR_SLEEPING;
- weston_compositor_dpms(compositor, WESTON_DPMS_OFF);
-}
-
-/** Sets compositor to idle mode
- *
- * \param data The compositor instance
- *
- * This is called when the idle timer fires. Once the compositor is in
- * idle mode it requires a wake action (e.g. via
- * weston_compositor_wake()) to restore it. The compositor's
- * idle_signal will be triggered when the idle event occurs.
- *
- * Idleness can be inhibited by setting the compositor's idle_inhibit
- * property.
- */
-static int
-idle_handler(void *data)
-{
- struct weston_compositor *compositor = data;
-
- if (compositor->idle_inhibit)
- return 1;
-
- compositor->state = WESTON_COMPOSITOR_IDLE;
- wl_signal_emit(&compositor->idle_signal, compositor);
-
- return 1;
-}
-
-WL_EXPORT void
-weston_plane_init(struct weston_plane *plane,
- struct weston_compositor *ec,
- int32_t x, int32_t y)
-{
- pixman_region32_init(&plane->damage);
- pixman_region32_init(&plane->clip);
- plane->x = x;
- plane->y = y;
- plane->compositor = ec;
-
- /* Init the link so that the call to wl_list_remove() when releasing
- * the plane without ever stacking doesn't lead to a crash */
- wl_list_init(&plane->link);
-}
-
-WL_EXPORT void
-weston_plane_release(struct weston_plane *plane)
-{
- struct weston_view *view;
-
- pixman_region32_fini(&plane->damage);
- pixman_region32_fini(&plane->clip);
-
- wl_list_for_each(view, &plane->compositor->view_list, link) {
- if (view->plane == plane)
- view->plane = NULL;
- }
-
- wl_list_remove(&plane->link);
-}
-
-WL_EXPORT void
-weston_compositor_stack_plane(struct weston_compositor *ec,
- struct weston_plane *plane,
- struct weston_plane *above)
-{
- if (above)
- wl_list_insert(above->link.prev, &plane->link);
- else
- wl_list_insert(&ec->plane_list, &plane->link);
-}
-
-static void unbind_resource(struct wl_resource *resource)
-{
- wl_list_remove(wl_resource_get_link(resource));
-}
-
-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_resource_create(client, &wl_output_interface,
- version, id);
- if (resource == NULL) {
- wl_client_post_no_memory(client);
- return;
- }
-
- wl_list_insert(&output->resource_list, wl_resource_get_link(resource));
- wl_resource_set_implementation(resource, NULL, data, unbind_resource);
-
- wl_output_send_geometry(resource,
- output->x,
- output->y,
- output->mm_width,
- output->mm_height,
- output->subpixel,
- output->make, output->model,
- output->transform);
- if (version >= WL_OUTPUT_SCALE_SINCE_VERSION)
- wl_output_send_scale(resource,
- output->current_scale);
-
- wl_list_for_each (mode, &output->mode_list, link) {
- wl_output_send_mode(resource,
- mode->flags,
- mode->width,
- mode->height,
- mode->refresh);
- }
-
- if (version >= WL_OUTPUT_DONE_SINCE_VERSION)
- wl_output_send_done(resource);
-}
-
-/* Move other outputs when one is resized so the space remains contiguous. */
-static void
-weston_compositor_reflow_outputs(struct weston_compositor *compositor,
- struct weston_output *resized_output, int delta_width)
-{
- struct weston_output *output;
- bool start_resizing = false;
-
- if (!delta_width)
- return;
-
- wl_list_for_each(output, &compositor->output_list, link) {
- if (output == resized_output) {
- start_resizing = true;
- continue;
- }
-
- if (start_resizing) {
- weston_output_move(output, output->x + delta_width, output->y);
- output->dirty = 1;
- }
- }
-}
-
-WL_EXPORT void
-weston_output_destroy(struct weston_output *output)
-{
- struct wl_resource *resource;
- struct weston_view *view;
-
- output->destroying = 1;
-
- wl_list_for_each(view, &output->compositor->view_list, link) {
- if (view->output_mask & (1u << output->id))
- weston_view_assign_output(view);
- }
-
- wl_event_source_remove(output->repaint_timer);
-
- weston_presentation_feedback_discard_list(&output->feedback_list);
-
- weston_compositor_reflow_outputs(output->compositor, output, output->width);
- wl_list_remove(&output->link);
-
- wl_signal_emit(&output->compositor->output_destroyed_signal, output);
- wl_signal_emit(&output->destroy_signal, output);
-
- free(output->name);
- pixman_region32_fini(&output->region);
- pixman_region32_fini(&output->previous_damage);
- output->compositor->output_id_pool &= ~(1u << output->id);
-
- wl_resource_for_each(resource, &output->resource_list) {
- wl_resource_set_destructor(resource, NULL);
- }
-
- wl_global_destroy(output->global);
-}
-
-WL_EXPORT void
-weston_output_update_matrix(struct weston_output *output)
-{
- float magnification;
-
- weston_matrix_init(&output->matrix);
- weston_matrix_translate(&output->matrix, -output->x, -output->y, 0);
-
- if (output->zoom.active) {
- magnification = 1 / (1 - output->zoom.spring_z.current);
- weston_output_update_zoom(output);
- weston_matrix_translate(&output->matrix, -output->zoom.trans_x,
- -output->zoom.trans_y, 0);
- weston_matrix_scale(&output->matrix, magnification,
- magnification, 1.0);
- }
-
- switch (output->transform) {
- case WL_OUTPUT_TRANSFORM_FLIPPED:
- case WL_OUTPUT_TRANSFORM_FLIPPED_90:
- case WL_OUTPUT_TRANSFORM_FLIPPED_180:
- case WL_OUTPUT_TRANSFORM_FLIPPED_270:
- weston_matrix_translate(&output->matrix, -output->width, 0, 0);
- weston_matrix_scale(&output->matrix, -1, 1, 1);
- break;
- }
-
- switch (output->transform) {
- default:
- case WL_OUTPUT_TRANSFORM_NORMAL:
- case WL_OUTPUT_TRANSFORM_FLIPPED:
- break;
- case WL_OUTPUT_TRANSFORM_90:
- case WL_OUTPUT_TRANSFORM_FLIPPED_90:
- weston_matrix_translate(&output->matrix, 0, -output->height, 0);
- weston_matrix_rotate_xy(&output->matrix, 0, 1);
- break;
- case WL_OUTPUT_TRANSFORM_180:
- case WL_OUTPUT_TRANSFORM_FLIPPED_180:
- weston_matrix_translate(&output->matrix,
- -output->width, -output->height, 0);
- weston_matrix_rotate_xy(&output->matrix, -1, 0);
- break;
- case WL_OUTPUT_TRANSFORM_270:
- case WL_OUTPUT_TRANSFORM_FLIPPED_270:
- weston_matrix_translate(&output->matrix, -output->width, 0, 0);
- weston_matrix_rotate_xy(&output->matrix, 0, -1);
- break;
- }
-
- if (output->current_scale != 1)
- weston_matrix_scale(&output->matrix,
- output->current_scale,
- output->current_scale, 1);
-
- output->dirty = 0;
-
- weston_matrix_invert(&output->inverse_matrix, &output->matrix);
-}
-
-static void
-weston_output_transform_scale_init(struct weston_output *output, uint32_t transform, uint32_t scale)
-{
- output->transform = transform;
- output->native_scale = scale;
- output->current_scale = scale;
-
- convert_size_by_transform_scale(&output->width, &output->height,
- output->current_mode->width,
- output->current_mode->height,
- transform, scale);
-}
-
-static void
-weston_output_init_geometry(struct weston_output *output, int x, int y)
-{
- output->x = x;
- output->y = y;
-
- pixman_region32_init(&output->previous_damage);
- pixman_region32_init_rect(&output->region, x, y,
- output->width,
- output->height);
-}
-
-WL_EXPORT void
-weston_output_move(struct weston_output *output, int x, int y)
-{
- struct wl_resource *resource;
-
- output->move_x = x - output->x;
- output->move_y = y - output->y;
-
- if (output->move_x == 0 && output->move_y == 0)
- return;
-
- weston_output_init_geometry(output, x, y);
-
- output->dirty = 1;
-
- /* Move views on this output. */
- wl_signal_emit(&output->compositor->output_moved_signal, output);
-
- /* Notify clients of the change for output position. */
- wl_resource_for_each(resource, &output->resource_list) {
- wl_output_send_geometry(resource,
- output->x,
- output->y,
- output->mm_width,
- output->mm_height,
- output->subpixel,
- output->make,
- output->model,
- output->transform);
-
- if (wl_resource_get_version(resource) >= WL_OUTPUT_DONE_SINCE_VERSION)
- wl_output_send_done(resource);
- }
-}
-
-/** Initialize a weston_output object's parameters
- *
- * \param output The weston_output object to initialize
- * \param c The output's compositor
- * \param x x coordinate for the output in global coordinate space
- * \param y y coordinate for the output in global coordinate space
- * \param mm_width Physical width of the output as reported by the backend
- * \param mm_height Physical height of the output as reported by the backend
- * \param transform Rotation of the output
- * \param scale Native scaling factor for the output
- *
- * Sets up the transformation, zoom, and geometry of the output using
- * the input properties.
- *
- * Establishes a repaint timer for the output with the relevant display
- * object's event loop. See output_repaint_timer_handler().
- *
- * The output is assigned an ID. Weston can support up to 32 distinct
- * outputs, with IDs numbered from 0-31; the compositor's output_id_pool
- * is referred to and used to find the first available ID number, and
- * then this ID is marked as used in output_id_pool.
- *
- * The output is also assigned a Wayland global with the wl_output
- * external interface.
- */
-WL_EXPORT void
-weston_output_init(struct weston_output *output, struct weston_compositor *c,
- int x, int y, int mm_width, int mm_height, uint32_t transform,
- int32_t scale)
-{
- struct wl_event_loop *loop;
-
- /* Verify we haven't reached the limit of 32 available output IDs */
- assert(ffs(~c->output_id_pool) > 0);
-
- output->compositor = c;
- output->x = x;
- output->y = y;
- output->mm_width = mm_width;
- output->mm_height = mm_height;
- output->dirty = 1;
- output->original_scale = scale;
-
- weston_output_transform_scale_init(output, transform, scale);
- weston_output_init_zoom(output);
-
- weston_output_init_geometry(output, x, y);
- weston_output_damage(output);
-
- wl_signal_init(&output->frame_signal);
- wl_signal_init(&output->destroy_signal);
- wl_list_init(&output->animation_list);
- wl_list_init(&output->resource_list);
- wl_list_init(&output->feedback_list);
- wl_list_init(&output->link);
-
- loop = wl_display_get_event_loop(c->wl_display);
- output->repaint_timer = wl_event_loop_add_timer(loop,
- output_repaint_timer_handler, output);
-
- /* Invert the output id pool and look for the lowest numbered
- * switch (the least significant bit). Take that bit's position
- * as our ID, and mark it used in the compositor's output_id_pool.
- */
- output->id = ffs(~output->compositor->output_id_pool) - 1;
- output->compositor->output_id_pool |= 1u << output->id;
-
- output->global =
- wl_global_create(c->wl_display, &wl_output_interface, 2,
- output, bind_output);
-}
-
-/** Adds an output to the compositor's output list and
- * send the compositor's output_created signal.
- *
- * \param compositor The compositor instance.
- * \param output The output to be added.
- */
-WL_EXPORT void
-weston_compositor_add_output(struct weston_compositor *compositor,
- struct weston_output *output)
-{
- wl_list_insert(compositor->output_list.prev, &output->link);
- wl_signal_emit(&compositor->output_created_signal, output);
-}
-
-WL_EXPORT void
-weston_output_transform_coordinate(struct weston_output *output,
- double device_x, double device_y,
- double *x, double *y)
-{
- struct weston_vector p = { {
- device_x,
- device_y,
- 0.0,
- 1.0 } };
-
- weston_matrix_transform(&output->inverse_matrix, &p);
-
- *x = p.f[0] / p.f[3];
- *y = p.f[1] / p.f[3];
-}
-
-static void
-destroy_viewport(struct wl_resource *resource)
-{
- struct weston_surface *surface =
- wl_resource_get_user_data(resource);
-
- if (!surface)
- return;
-
- surface->viewport_resource = NULL;
- surface->pending.buffer_viewport.buffer.src_width =
- wl_fixed_from_int(-1);
- surface->pending.buffer_viewport.surface.width = -1;
- surface->pending.buffer_viewport.changed = 1;
-}
-
-static void
-viewport_destroy(struct wl_client *client,
- struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static void
-viewport_set_source(struct wl_client *client,
- struct wl_resource *resource,
- wl_fixed_t src_x,
- wl_fixed_t src_y,
- wl_fixed_t src_width,
- wl_fixed_t src_height)
-{
- struct weston_surface *surface =
- wl_resource_get_user_data(resource);
-
- if (!surface) {
- wl_resource_post_error(resource,
- WP_VIEWPORT_ERROR_NO_SURFACE,
- "wl_surface for this viewport is no longer exists");
- return;
- }
-
- assert(surface->viewport_resource == resource);
- assert(surface->resource);
-
- if (src_width == wl_fixed_from_int(-1) &&
- src_height == wl_fixed_from_int(-1) &&
- src_x == wl_fixed_from_int(-1) &&
- src_y == wl_fixed_from_int(-1)) {
- /* unset source rect */
- surface->pending.buffer_viewport.buffer.src_width =
- wl_fixed_from_int(-1);
- surface->pending.buffer_viewport.changed = 1;
- return;
- }
-
- if (src_width <= 0 || src_height <= 0 || src_x < 0 || src_y < 0) {
- wl_resource_post_error(resource,
- WP_VIEWPORT_ERROR_BAD_VALUE,
- "wl_surface@%d viewport source "
- "w=%f <= 0, h=%f <= 0, x=%f < 0, or y=%f < 0",
- wl_resource_get_id(surface->resource),
- wl_fixed_to_double(src_width),
- wl_fixed_to_double(src_height),
- wl_fixed_to_double(src_x),
- wl_fixed_to_double(src_y));
- return;
- }
-
- surface->pending.buffer_viewport.buffer.src_x = src_x;
- surface->pending.buffer_viewport.buffer.src_y = src_y;
- surface->pending.buffer_viewport.buffer.src_width = src_width;
- surface->pending.buffer_viewport.buffer.src_height = src_height;
- surface->pending.buffer_viewport.changed = 1;
-}
-
-static void
-viewport_set_destination(struct wl_client *client,
- struct wl_resource *resource,
- int32_t dst_width,
- int32_t dst_height)
-{
- struct weston_surface *surface =
- wl_resource_get_user_data(resource);
-
- if (!surface) {
- wl_resource_post_error(resource,
- WP_VIEWPORT_ERROR_NO_SURFACE,
- "wl_surface for this viewport no longer exists");
- return;
- }
-
- assert(surface->viewport_resource == resource);
-
- if (dst_width == -1 && dst_height == -1) {
- /* unset destination size */
- surface->pending.buffer_viewport.surface.width = -1;
- surface->pending.buffer_viewport.changed = 1;
- return;
- }
-
- if (dst_width <= 0 || dst_height <= 0) {
- wl_resource_post_error(resource,
- WP_VIEWPORT_ERROR_BAD_VALUE,
- "destination size must be positive (%dx%d)",
- dst_width, dst_height);
- return;
- }
-
- surface->pending.buffer_viewport.surface.width = dst_width;
- surface->pending.buffer_viewport.surface.height = dst_height;
- surface->pending.buffer_viewport.changed = 1;
-}
-
-static const struct wp_viewport_interface viewport_interface = {
- viewport_destroy,
- viewport_set_source,
- viewport_set_destination
-};
-
-static void
-viewporter_destroy(struct wl_client *client,
- struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static void
-viewporter_get_viewport(struct wl_client *client,
- struct wl_resource *viewporter,
- uint32_t id,
- struct wl_resource *surface_resource)
-{
- int version = wl_resource_get_version(viewporter);
- struct weston_surface *surface =
- wl_resource_get_user_data(surface_resource);
- struct wl_resource *resource;
-
- if (surface->viewport_resource) {
- wl_resource_post_error(viewporter,
- WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS,
- "a viewport for that surface already exists");
- return;
- }
-
- resource = wl_resource_create(client, &wp_viewport_interface,
- version, id);
- if (resource == NULL) {
- wl_client_post_no_memory(client);
- return;
- }
-
- wl_resource_set_implementation(resource, &viewport_interface,
- surface, destroy_viewport);
-
- surface->viewport_resource = resource;
-}
-
-static const struct wp_viewporter_interface viewporter_interface = {
- viewporter_destroy,
- viewporter_get_viewport
-};
-
-static void
-bind_viewporter(struct wl_client *client,
- void *data, uint32_t version, uint32_t id)
-{
- struct wl_resource *resource;
-
- resource = wl_resource_create(client, &wp_viewporter_interface,
- version, id);
- if (resource == NULL) {
- wl_client_post_no_memory(client);
- return;
- }
-
- wl_resource_set_implementation(resource, &viewporter_interface,
- NULL, NULL);
-}
-
-static void
-destroy_presentation_feedback(struct wl_resource *feedback_resource)
-{
- struct weston_presentation_feedback *feedback;
-
- feedback = wl_resource_get_user_data(feedback_resource);
-
- wl_list_remove(&feedback->link);
- free(feedback);
-}
-
-static void
-presentation_destroy(struct wl_client *client, struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static void
-presentation_feedback(struct wl_client *client,
- struct wl_resource *presentation_resource,
- struct wl_resource *surface_resource,
- uint32_t callback)
-{
- struct weston_surface *surface;
- struct weston_presentation_feedback *feedback;
-
- surface = wl_resource_get_user_data(surface_resource);
-
- feedback = zalloc(sizeof *feedback);
- if (feedback == NULL)
- goto err_calloc;
-
- feedback->resource = wl_resource_create(client,
- &wp_presentation_feedback_interface,
- 1, callback);
- if (!feedback->resource)
- goto err_create;
-
- wl_resource_set_implementation(feedback->resource, NULL, feedback,
- destroy_presentation_feedback);
-
- wl_list_insert(&surface->pending.feedback_list, &feedback->link);
-
- return;
-
-err_create:
- free(feedback);
-
-err_calloc:
- wl_client_post_no_memory(client);
-}
-
-static const struct wp_presentation_interface presentation_implementation = {
- presentation_destroy,
- presentation_feedback
-};
-
-static void
-bind_presentation(struct wl_client *client,
- void *data, uint32_t version, uint32_t id)
-{
- struct weston_compositor *compositor = data;
- struct wl_resource *resource;
-
- resource = wl_resource_create(client, &wp_presentation_interface,
- version, id);
- if (resource == NULL) {
- wl_client_post_no_memory(client);
- return;
- }
-
- wl_resource_set_implementation(resource, &presentation_implementation,
- compositor, NULL);
- wp_presentation_send_clock_id(resource, compositor->presentation_clock);
-}
-
-static void
-compositor_bind(struct wl_client *client,
- void *data, uint32_t version, uint32_t id)
-{
- struct weston_compositor *compositor = data;
- struct wl_resource *resource;
-
- resource = wl_resource_create(client, &wl_compositor_interface,
- version, id);
- if (resource == NULL) {
- wl_client_post_no_memory(client);
- return;
- }
-
- wl_resource_set_implementation(resource, &compositor_interface,
- compositor, NULL);
-}
-
-WL_EXPORT int
-weston_environment_get_fd(const char *env)
-{
- char *e, *end;
- int fd, flags;
-
- e = getenv(env);
- if (!e)
- return -1;
- fd = strtol(e, &end, 0);
- if (*end != '\0')
- return -1;
-
- flags = fcntl(fd, F_GETFD);
- if (flags == -1)
- return -1;
-
- fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
- unsetenv(env);
-
- return fd;
-}
-
-static void
-timeline_key_binding_handler(struct weston_keyboard *keyboard, uint32_t time,
- uint32_t key, void *data)
-{
- struct weston_compositor *compositor = data;
-
- if (weston_timeline_enabled_)
- weston_timeline_close();
- else
- weston_timeline_open(compositor);
-}
-
-/** Create the compositor.
- *
- * This functions creates and initializes a compositor instance.
- *
- * \param display The Wayland display to be used.
- * \param user_data A pointer to an object that can later be retrieved
- * using the \ref weston_compositor_get_user_data function.
- * \return The compositor instance on success or NULL on failure.
- */
-WL_EXPORT struct weston_compositor *
-weston_compositor_create(struct wl_display *display, void *user_data)
-{
- struct weston_compositor *ec;
- struct wl_event_loop *loop;
-
- ec = zalloc(sizeof *ec);
- if (!ec)
- return NULL;
-
- ec->wl_display = display;
- ec->user_data = user_data;
- wl_signal_init(&ec->destroy_signal);
- wl_signal_init(&ec->create_surface_signal);
- wl_signal_init(&ec->activate_signal);
- wl_signal_init(&ec->transform_signal);
- wl_signal_init(&ec->kill_signal);
- wl_signal_init(&ec->idle_signal);
- wl_signal_init(&ec->wake_signal);
- wl_signal_init(&ec->show_input_panel_signal);
- wl_signal_init(&ec->hide_input_panel_signal);
- wl_signal_init(&ec->update_input_panel_signal);
- wl_signal_init(&ec->seat_created_signal);
- wl_signal_init(&ec->output_created_signal);
- wl_signal_init(&ec->output_destroyed_signal);
- wl_signal_init(&ec->output_moved_signal);
- wl_signal_init(&ec->output_resized_signal);
- wl_signal_init(&ec->session_signal);
- ec->session_active = 1;
-
- ec->output_id_pool = 0;
- ec->repaint_msec = DEFAULT_REPAINT_WINDOW;
-
- if (!wl_global_create(ec->wl_display, &wl_compositor_interface, 4,
- ec, compositor_bind))
- goto fail;
-
- if (!wl_global_create(ec->wl_display, &wl_subcompositor_interface, 1,
- ec, bind_subcompositor))
- goto fail;
-
- if (!wl_global_create(ec->wl_display, &wp_viewporter_interface, 1,
- ec, bind_viewporter))
- goto fail;
-
- if (!wl_global_create(ec->wl_display, &wp_presentation_interface, 1,
- ec, bind_presentation))
- goto fail;
-
- wl_list_init(&ec->view_list);
- wl_list_init(&ec->plane_list);
- wl_list_init(&ec->layer_list);
- wl_list_init(&ec->seat_list);
- wl_list_init(&ec->output_list);
- wl_list_init(&ec->key_binding_list);
- wl_list_init(&ec->modifier_binding_list);
- wl_list_init(&ec->button_binding_list);
- wl_list_init(&ec->touch_binding_list);
- wl_list_init(&ec->axis_binding_list);
- wl_list_init(&ec->debug_binding_list);
-
- weston_plane_init(&ec->primary_plane, ec, 0, 0);
- weston_compositor_stack_plane(ec, &ec->primary_plane, NULL);
-
- wl_data_device_manager_init(ec->wl_display);
-
- wl_display_init_shm(ec->wl_display);
-
- loop = wl_display_get_event_loop(ec->wl_display);
- ec->idle_source = wl_event_loop_add_timer(loop, idle_handler, ec);
-
- weston_layer_init(&ec->fade_layer, &ec->layer_list);
- weston_layer_init(&ec->cursor_layer, &ec->fade_layer.link);
-
- weston_compositor_add_debug_binding(ec, KEY_T,
- timeline_key_binding_handler, ec);
-
- return ec;
-
-fail:
- free(ec);
- return NULL;
-}
-
-WL_EXPORT void
-weston_compositor_shutdown(struct weston_compositor *ec)
-{
- struct weston_output *output, *next;
-
- wl_event_source_remove(ec->idle_source);
-
- /* Destroy all outputs associated with this compositor */
- wl_list_for_each_safe(output, next, &ec->output_list, link)
- output->destroy(output);
-
- if (ec->renderer)
- ec->renderer->destroy(ec);
-
- weston_binding_list_destroy_all(&ec->key_binding_list);
- weston_binding_list_destroy_all(&ec->modifier_binding_list);
- weston_binding_list_destroy_all(&ec->button_binding_list);
- weston_binding_list_destroy_all(&ec->touch_binding_list);
- weston_binding_list_destroy_all(&ec->axis_binding_list);
- weston_binding_list_destroy_all(&ec->debug_binding_list);
-
- weston_plane_release(&ec->primary_plane);
-}
-
-WL_EXPORT void
-weston_compositor_exit_with_code(struct weston_compositor *compositor,
- int exit_code)
-{
- if (compositor->exit_code == EXIT_SUCCESS)
- compositor->exit_code = exit_code;
-
- weston_compositor_exit(compositor);
-}
-
-WL_EXPORT void
-weston_compositor_set_default_pointer_grab(struct weston_compositor *ec,
- const struct weston_pointer_grab_interface *interface)
-{
- struct weston_seat *seat;
-
- ec->default_pointer_grab = interface;
- wl_list_for_each(seat, &ec->seat_list, link) {
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
-
- if (pointer)
- weston_pointer_set_default_grab(pointer, interface);
- }
-}
-
-WL_EXPORT int
-weston_compositor_set_presentation_clock(struct weston_compositor *compositor,
- clockid_t clk_id)
-{
- struct timespec ts;
-
- if (clock_gettime(clk_id, &ts) < 0)
- return -1;
-
- compositor->presentation_clock = clk_id;
-
- return 0;
-}
-
-/*
- * For choosing the software clock, when the display hardware or API
- * does not expose a compatible presentation timestamp.
- */
-WL_EXPORT int
-weston_compositor_set_presentation_clock_software(
- struct weston_compositor *compositor)
-{
- /* In order of preference */
- static const clockid_t clocks[] = {
- CLOCK_MONOTONIC_RAW, /* no jumps, no crawling */
- CLOCK_MONOTONIC_COARSE, /* no jumps, may crawl, fast & coarse */
- CLOCK_MONOTONIC, /* no jumps, may crawl */
- CLOCK_REALTIME_COARSE, /* may jump and crawl, fast & coarse */
- CLOCK_REALTIME /* may jump and crawl */
- };
- unsigned i;
-
- for (i = 0; i < ARRAY_LENGTH(clocks); i++)
- if (weston_compositor_set_presentation_clock(compositor,
- clocks[i]) == 0)
- return 0;
-
- weston_log("Error: no suitable presentation clock available.\n");
-
- return -1;
-}
-
-/** Read the current time from the Presentation clock
- *
- * \param compositor
- * \param ts[out] The current time.
- *
- * \note Reading the current time in user space is always imprecise to some
- * degree.
- *
- * This function is never meant to fail. If reading the clock does fail,
- * an error message is logged and a zero time is returned. Callers are not
- * supposed to detect or react to failures.
- */
-WL_EXPORT void
-weston_compositor_read_presentation_clock(
- const struct weston_compositor *compositor,
- struct timespec *ts)
-{
- static bool warned;
- int ret;
-
- ret = clock_gettime(compositor->presentation_clock, ts);
- if (ret < 0) {
- ts->tv_sec = 0;
- ts->tv_nsec = 0;
-
- if (!warned)
- weston_log("Error: failure to read "
- "the presentation clock %#x: '%m' (%d)\n",
- compositor->presentation_clock, errno);
- warned = true;
- }
-}
-
-/** Import dmabuf buffer into current renderer
- *
- * \param compositor
- * \param buffer the dmabuf buffer to import
- * \return true on usable buffers, false otherwise
- *
- * This function tests that the linux_dmabuf_buffer is usable
- * for the current renderer. Returns false on unusable buffers. Usually
- * usability is tested by importing the dmabufs for composition.
- *
- * This hook is also used for detecting if the renderer supports
- * dmabufs at all. If the renderer hook is NULL, dmabufs are not
- * supported.
- * */
-WL_EXPORT bool
-weston_compositor_import_dmabuf(struct weston_compositor *compositor,
- struct linux_dmabuf_buffer *buffer)
-{
- struct weston_renderer *renderer;
-
- renderer = compositor->renderer;
-
- if (renderer->import_dmabuf == NULL)
- return false;
-
- return renderer->import_dmabuf(compositor, buffer);
-}
-
-WL_EXPORT void
-weston_version(int *major, int *minor, int *micro)
-{
- *major = WESTON_VERSION_MAJOR;
- *minor = WESTON_VERSION_MINOR;
- *micro = WESTON_VERSION_MICRO;
-}
-
-WL_EXPORT void *
-weston_load_module(const char *name, const char *entrypoint)
-{
- const char *builddir = getenv("WESTON_BUILD_DIR");
- char path[PATH_MAX];
- void *module, *init;
-
- if (name == NULL)
- return NULL;
-
- if (name[0] != '/') {
- if (builddir)
- snprintf(path, sizeof path, "%s/.libs/%s", builddir, name);
- else
- snprintf(path, sizeof path, "%s/%s", LIBWESTON_MODULEDIR, name);
- } else {
- snprintf(path, sizeof path, "%s", name);
- }
-
- module = dlopen(path, RTLD_NOW | RTLD_NOLOAD);
- if (module) {
- weston_log("Module '%s' already loaded\n", path);
- dlclose(module);
- return NULL;
- }
-
- weston_log("Loading module '%s'\n", path);
- module = dlopen(path, RTLD_NOW);
- if (!module) {
- weston_log("Failed to load module: %s\n", dlerror());
- return NULL;
- }
-
- init = dlsym(module, entrypoint);
- if (!init) {
- weston_log("Failed to lookup init function: %s\n", dlerror());
- dlclose(module);
- return NULL;
- }
-
- return init;
-}
-
-
-/** Destroys the compositor.
- *
- * This function cleans up the compositor state and destroys it.
- *
- * \param compositor The compositor to be destroyed.
- */
-WL_EXPORT void
-weston_compositor_destroy(struct weston_compositor *compositor)
-{
- /* prevent further rendering while shutting down */
- compositor->state = WESTON_COMPOSITOR_OFFSCREEN;
-
- wl_signal_emit(&compositor->destroy_signal, compositor);
-
- weston_compositor_xkb_destroy(compositor);
-
- if (compositor->backend)
- compositor->backend->destroy(compositor);
- free(compositor);
-}
-
-/** Instruct the compositor to exit.
- *
- * This functions does not directly destroy the compositor object, it merely
- * command it to start the tear down process. It is not guaranteed that the
- * tear down will happen immediately.
- *
- * \param compositor The compositor to tear down.
- */
-WL_EXPORT void
-weston_compositor_exit(struct weston_compositor *compositor)
-{
- compositor->exit(compositor);
-}
-
-/** Return the user data stored in the compositor.
- *
- * This function returns the user data pointer set with user_data parameter
- * to the \ref weston_compositor_create function.
- */
-WL_EXPORT void *
-weston_compositor_get_user_data(struct weston_compositor *compositor)
-{
- return compositor->user_data;
-}
-
-static const char * const backend_map[] = {
- [WESTON_BACKEND_DRM] = "drm-backend.so",
- [WESTON_BACKEND_FBDEV] = "fbdev-backend.so",
- [WESTON_BACKEND_HEADLESS] = "headless-backend.so",
- [WESTON_BACKEND_RDP] = "rdp-backend.so",
- [WESTON_BACKEND_WAYLAND] = "wayland-backend.so",
- [WESTON_BACKEND_X11] = "x11-backend.so",
-};
-
-/** Load a backend into a weston_compositor
- *
- * A backend must be loaded to make a weston_compositor work. A backend
- * provides input and output capabilities, and determines the renderer to use.
- *
- * \param compositor A compositor that has not had a backend loaded yet.
- * \param backend Name of the backend file.
- * \param config_base A pointer to a backend-specific configuration
- * structure's 'base' member.
- *
- * \return 0 on success, or -1 on error.
- */
-WL_EXPORT int
-weston_compositor_load_backend(struct weston_compositor *compositor,
- enum weston_compositor_backend backend,
- struct weston_backend_config *config_base)
-{
- int (*backend_init)(struct weston_compositor *c,
- struct weston_backend_config *config_base);
-
- if (backend < 0 || backend >= ARRAY_LENGTH(backend_map))
- return -1;
-
- backend_init = weston_load_module(backend_map[backend], "backend_init");
- if (!backend_init)
- return -1;
-
- return backend_init(compositor, config_base);
-}
+++ /dev/null
-/*
- * Copyright © 2008-2011 Kristian Høgsberg
- * Copyright © 2012 Collabora, Ltd.
- *
- * 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.
- */
-
-#ifndef _WAYLAND_SYSTEM_COMPOSITOR_H_
-#define _WAYLAND_SYSTEM_COMPOSITOR_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdbool.h>
-#include <time.h>
-#include <pixman.h>
-#include <xkbcommon/xkbcommon.h>
-
-#define WL_HIDE_DEPRECATED
-#include <wayland-server.h>
-
-#include "version.h"
-#include "matrix.h"
-#include "config-parser.h"
-#include "zalloc.h"
-#include "timeline-object.h"
-
-struct weston_transform {
- struct weston_matrix matrix;
- struct wl_list link;
-};
-
-struct weston_surface;
-struct weston_buffer;
-struct shell_surface;
-struct weston_seat;
-struct weston_output;
-struct input_method;
-struct weston_pointer;
-struct linux_dmabuf_buffer;
-struct weston_recorder;
-
-enum weston_keyboard_modifier {
- MODIFIER_CTRL = (1 << 0),
- MODIFIER_ALT = (1 << 1),
- MODIFIER_SUPER = (1 << 2),
- MODIFIER_SHIFT = (1 << 3),
-};
-
-enum weston_keyboard_locks {
- WESTON_NUM_LOCK = (1 << 0),
- WESTON_CAPS_LOCK = (1 << 1),
-};
-
-enum weston_led {
- LED_NUM_LOCK = (1 << 0),
- LED_CAPS_LOCK = (1 << 1),
- LED_SCROLL_LOCK = (1 << 2),
-};
-
-struct weston_mode {
- uint32_t flags;
- int32_t width, height;
- uint32_t refresh;
- struct wl_list link;
-};
-
-struct weston_shell_client {
- void (*send_configure)(struct weston_surface *surface, int32_t width, int32_t height);
- void (*send_position)(struct weston_surface *surface, int32_t x, int32_t y);
-};
-
-struct weston_shell_interface {
- void *shell; /* either desktop or tablet */
-
- struct shell_surface *(*create_shell_surface)(void *shell,
- struct weston_surface *surface,
- const struct weston_shell_client *client);
- void (*set_toplevel)(struct shell_surface *shsurf);
-
- void (*set_transient)(struct shell_surface *shsurf,
- struct weston_surface *parent,
- int x, int y, uint32_t flags);
- void (*set_fullscreen)(struct shell_surface *shsurf,
- uint32_t method,
- uint32_t framerate,
- struct weston_output *output);
- void (*set_xwayland)(struct shell_surface *shsurf,
- int x, int y, uint32_t flags);
- int (*move)(struct shell_surface *shsurf, struct weston_pointer *pointer);
- int (*resize)(struct shell_surface *shsurf,
- struct weston_pointer *pointer, uint32_t edges);
- void (*set_title)(struct shell_surface *shsurf,
- const char *title);
- void (*set_window_geometry)(struct shell_surface *shsurf,
- int32_t x, int32_t y,
- int32_t width, int32_t height);
- void (*set_maximized)(struct shell_surface *shsurf);
- void (*set_pid)(struct shell_surface *shsurf, pid_t pid);
- void (*get_output_work_area)(void *shell, struct weston_output *output, pixman_rectangle32_t *area);
-};
-
-struct weston_animation {
- void (*frame)(struct weston_animation *animation,
- struct weston_output *output, uint32_t msecs);
- int frame_counter;
- struct wl_list link;
-};
-
-enum {
- WESTON_SPRING_OVERSHOOT,
- WESTON_SPRING_CLAMP,
- WESTON_SPRING_BOUNCE
-};
-
-struct weston_spring {
- double k;
- double friction;
- double current;
- double target;
- double previous;
- double min, max;
- uint32_t timestamp;
- uint32_t clip;
-};
-
-struct weston_output_zoom {
- bool active;
- float increment;
- float level;
- float max_level;
- float trans_x, trans_y;
- struct {
- double x, y;
- } current;
- struct weston_seat *seat;
- struct weston_animation animation_z;
- struct weston_spring spring_z;
- struct wl_listener motion_listener;
-};
-
-/* bit compatible with drm definitions. */
-enum dpms_enum {
- WESTON_DPMS_ON,
- WESTON_DPMS_STANDBY,
- WESTON_DPMS_SUSPEND,
- WESTON_DPMS_OFF
-};
-
-struct weston_output {
- uint32_t id;
- char *name;
-
- void *renderer_state;
-
- struct wl_list link;
- struct wl_list resource_list;
- struct wl_global *global;
- struct weston_compositor *compositor;
-
- /** From global to output buffer coordinates. */
- struct weston_matrix matrix;
- /** From output buffer to global coordinates. */
- struct weston_matrix inverse_matrix;
-
- struct wl_list animation_list;
- int32_t x, y, width, height;
- int32_t mm_width, mm_height;
-
- /** Output area in global coordinates, simple rect */
- pixman_region32_t region;
-
- pixman_region32_t previous_damage;
- int repaint_needed;
- int repaint_scheduled;
- struct wl_event_source *repaint_timer;
- struct weston_output_zoom zoom;
- int dirty;
- struct wl_signal frame_signal;
- struct wl_signal destroy_signal;
- int move_x, move_y;
- uint32_t frame_time; /* presentation timestamp in milliseconds */
- uint64_t msc; /* media stream counter */
- int disable_planes;
- int destroying;
- struct wl_list feedback_list;
-
- char *make, *model, *serial_number;
- uint32_t subpixel;
- uint32_t transform;
- int32_t native_scale;
- int32_t current_scale;
- int32_t original_scale;
-
- struct weston_mode *native_mode;
- struct weston_mode *current_mode;
- struct weston_mode *original_mode;
- struct wl_list mode_list;
-
- void (*start_repaint_loop)(struct weston_output *output);
- int (*repaint)(struct weston_output *output,
- pixman_region32_t *damage);
- void (*destroy)(struct weston_output *output);
- void (*assign_planes)(struct weston_output *output);
- int (*switch_mode)(struct weston_output *output, struct weston_mode *mode);
-
- /* backlight values are on 0-255 range, where higher is brighter */
- int32_t backlight_current;
- void (*set_backlight)(struct weston_output *output, uint32_t value);
- void (*set_dpms)(struct weston_output *output, enum dpms_enum level);
-
- int connection_internal;
- uint16_t gamma_size;
- void (*set_gamma)(struct weston_output *output,
- uint16_t size,
- uint16_t *r,
- uint16_t *g,
- uint16_t *b);
-
- struct weston_timeline_object timeline;
-};
-
-enum weston_pointer_motion_mask {
- WESTON_POINTER_MOTION_ABS = 1 << 0,
- WESTON_POINTER_MOTION_REL = 1 << 1,
-};
-
-struct weston_pointer_motion_event {
- uint32_t mask;
- double x;
- double y;
- double dx;
- double dy;
-};
-
-struct weston_pointer_axis_event {
- uint32_t axis;
- double value;
- bool has_discrete;
- int32_t discrete;
-};
-
-struct weston_pointer_grab;
-struct weston_pointer_grab_interface {
- void (*focus)(struct weston_pointer_grab *grab);
- void (*motion)(struct weston_pointer_grab *grab, uint32_t time,
- struct weston_pointer_motion_event *event);
- void (*button)(struct weston_pointer_grab *grab,
- uint32_t time, uint32_t button, uint32_t state);
- void (*axis)(struct weston_pointer_grab *grab,
- uint32_t time,
- struct weston_pointer_axis_event *event);
- void (*axis_source)(struct weston_pointer_grab *grab, uint32_t source);
- void (*frame)(struct weston_pointer_grab *grab);
- void (*cancel)(struct weston_pointer_grab *grab);
-};
-
-struct weston_pointer_grab {
- const struct weston_pointer_grab_interface *interface;
- struct weston_pointer *pointer;
-};
-
-struct weston_keyboard_grab;
-struct weston_keyboard_grab_interface {
- void (*key)(struct weston_keyboard_grab *grab, uint32_t time,
- uint32_t key, uint32_t state);
- void (*modifiers)(struct weston_keyboard_grab *grab, uint32_t serial,
- uint32_t mods_depressed, uint32_t mods_latched,
- uint32_t mods_locked, uint32_t group);
- void (*cancel)(struct weston_keyboard_grab *grab);
-};
-
-struct weston_keyboard_grab {
- const struct weston_keyboard_grab_interface *interface;
- struct weston_keyboard *keyboard;
-};
-
-struct weston_touch_grab;
-struct weston_touch_grab_interface {
- void (*down)(struct weston_touch_grab *grab,
- uint32_t time,
- int touch_id,
- wl_fixed_t sx,
- wl_fixed_t sy);
- void (*up)(struct weston_touch_grab *grab,
- uint32_t time,
- int touch_id);
- void (*motion)(struct weston_touch_grab *grab,
- uint32_t time,
- int touch_id,
- wl_fixed_t sx,
- wl_fixed_t sy);
- void (*frame)(struct weston_touch_grab *grab);
- void (*cancel)(struct weston_touch_grab *grab);
-};
-
-struct weston_touch_grab {
- const struct weston_touch_grab_interface *interface;
- struct weston_touch *touch;
-};
-
-struct weston_data_offer {
- struct wl_resource *resource;
- struct weston_data_source *source;
- struct wl_listener source_destroy_listener;
- uint32_t dnd_actions;
- enum wl_data_device_manager_dnd_action preferred_dnd_action;
- bool in_ask;
-};
-
-struct weston_data_source {
- struct wl_resource *resource;
- struct wl_signal destroy_signal;
- struct wl_array mime_types;
- struct weston_data_offer *offer;
- struct weston_seat *seat;
- bool accepted;
- bool actions_set;
- uint32_t dnd_actions;
- enum wl_data_device_manager_dnd_action current_dnd_action;
- enum wl_data_device_manager_dnd_action compositor_action;
-
- void (*accept)(struct weston_data_source *source,
- uint32_t serial, const char *mime_type);
- void (*send)(struct weston_data_source *source,
- const char *mime_type, int32_t fd);
- void (*cancel)(struct weston_data_source *source);
-};
-
-struct weston_pointer_client {
- struct wl_list link;
- struct wl_client *client;
- struct wl_list pointer_resources;
-};
-
-struct weston_pointer {
- struct weston_seat *seat;
-
- struct wl_list pointer_clients;
-
- struct weston_view *focus;
- struct weston_pointer_client *focus_client;
- uint32_t focus_serial;
- struct wl_listener focus_view_listener;
- struct wl_listener focus_resource_listener;
- struct wl_signal focus_signal;
- struct wl_signal motion_signal;
-
- struct weston_view *sprite;
- struct wl_listener sprite_destroy_listener;
- int32_t hotspot_x, hotspot_y;
-
- struct weston_pointer_grab *grab;
- struct weston_pointer_grab default_grab;
- wl_fixed_t grab_x, grab_y;
- uint32_t grab_button;
- uint32_t grab_serial;
- uint32_t grab_time;
-
- wl_fixed_t x, y;
- wl_fixed_t sx, sy;
- uint32_t button_count;
-
- struct wl_listener output_destroy_listener;
-};
-
-
-struct weston_touch {
- struct weston_seat *seat;
-
- struct wl_list resource_list;
- struct wl_list focus_resource_list;
- struct weston_view *focus;
- struct wl_listener focus_view_listener;
- struct wl_listener focus_resource_listener;
- uint32_t focus_serial;
- struct wl_signal focus_signal;
-
- uint32_t num_tp;
-
- struct weston_touch_grab *grab;
- struct weston_touch_grab default_grab;
- int grab_touch_id;
- wl_fixed_t grab_x, grab_y;
- uint32_t grab_serial;
- uint32_t grab_time;
-};
-
-void
-weston_pointer_motion_to_abs(struct weston_pointer *pointer,
- struct weston_pointer_motion_event *event,
- wl_fixed_t *x, wl_fixed_t *y);
-
-struct weston_pointer *
-weston_pointer_create(struct weston_seat *seat);
-void
-weston_pointer_destroy(struct weston_pointer *pointer);
-void
-weston_pointer_send_axis(struct weston_pointer *pointer,
- uint32_t time,
- struct weston_pointer_axis_event *event);
-void
-weston_pointer_send_axis_source(struct weston_pointer *pointer,
- uint32_t source);
-void
-weston_pointer_send_frame(struct weston_pointer *pointer);
-
-void
-weston_pointer_set_focus(struct weston_pointer *pointer,
- struct weston_view *view,
- wl_fixed_t sx, wl_fixed_t sy);
-void
-weston_pointer_clear_focus(struct weston_pointer *pointer);
-void
-weston_pointer_start_grab(struct weston_pointer *pointer,
- struct weston_pointer_grab *grab);
-void
-weston_pointer_end_grab(struct weston_pointer *pointer);
-void
-weston_pointer_clamp(struct weston_pointer *pointer,
- wl_fixed_t *fx, wl_fixed_t *fy);
-void
-weston_pointer_move(struct weston_pointer *pointer,
- struct weston_pointer_motion_event *event);
-void
-weston_pointer_set_default_grab(struct weston_pointer *pointer,
- const struct weston_pointer_grab_interface *interface);
-
-struct weston_keyboard *
-weston_keyboard_create(void);
-void
-weston_keyboard_destroy(struct weston_keyboard *keyboard);
-void
-weston_keyboard_set_focus(struct weston_keyboard *keyboard,
- struct weston_surface *surface);
-void
-weston_keyboard_start_grab(struct weston_keyboard *device,
- struct weston_keyboard_grab *grab);
-void
-weston_keyboard_end_grab(struct weston_keyboard *keyboard);
-int
-/*
- * 'mask' and 'value' should be a bitwise mask of one or more
- * valued of the weston_keyboard_locks enum.
- */
-weston_keyboard_set_locks(struct weston_keyboard *keyboard,
- uint32_t mask, uint32_t value);
-
-struct weston_touch *
-weston_touch_create(void);
-void
-weston_touch_destroy(struct weston_touch *touch);
-void
-weston_touch_set_focus(struct weston_touch *touch,
- struct weston_view *view);
-void
-weston_touch_start_grab(struct weston_touch *device,
- struct weston_touch_grab *grab);
-void
-weston_touch_end_grab(struct weston_touch *touch);
-
-void
-wl_data_device_set_keyboard_focus(struct weston_seat *seat);
-
-int
-wl_data_device_manager_init(struct wl_display *display);
-
-
-void
-weston_seat_set_selection(struct weston_seat *seat,
- struct weston_data_source *source, uint32_t serial);
-void
-weston_seat_send_selection(struct weston_seat *seat, struct wl_client *client);
-
-int
-weston_pointer_start_drag(struct weston_pointer *pointer,
- struct weston_data_source *source,
- struct weston_surface *icon,
- struct wl_client *client);
-int
-weston_touch_start_drag(struct weston_touch *touch,
- struct weston_data_source *source,
- struct weston_surface *icon,
- struct wl_client *client);
-
-struct weston_xkb_info {
- struct xkb_keymap *keymap;
- int keymap_fd;
- size_t keymap_size;
- char *keymap_area;
- int32_t ref_count;
- xkb_mod_index_t shift_mod;
- xkb_mod_index_t caps_mod;
- xkb_mod_index_t ctrl_mod;
- xkb_mod_index_t alt_mod;
- xkb_mod_index_t mod2_mod;
- xkb_mod_index_t mod3_mod;
- xkb_mod_index_t super_mod;
- xkb_mod_index_t mod5_mod;
- xkb_led_index_t num_led;
- xkb_led_index_t caps_led;
- xkb_led_index_t scroll_led;
-};
-
-struct weston_keyboard {
- struct weston_seat *seat;
-
- struct wl_list resource_list;
- struct wl_list focus_resource_list;
- struct weston_surface *focus;
- struct wl_listener focus_resource_listener;
- uint32_t focus_serial;
- struct wl_signal focus_signal;
-
- struct weston_keyboard_grab *grab;
- struct weston_keyboard_grab default_grab;
- uint32_t grab_key;
- uint32_t grab_serial;
- uint32_t grab_time;
-
- struct wl_array keys;
-
- struct {
- uint32_t mods_depressed;
- uint32_t mods_latched;
- uint32_t mods_locked;
- uint32_t group;
- } modifiers;
-
- struct weston_keyboard_grab input_method_grab;
- struct wl_resource *input_method_resource;
-
- struct weston_xkb_info *xkb_info;
- struct {
- struct xkb_state *state;
- enum weston_led leds;
- } xkb_state;
- struct xkb_keymap *pending_keymap;
-};
-
-struct weston_seat {
- struct wl_list base_resource_list;
-
- struct wl_global *global;
- struct weston_pointer *pointer_state;
- struct weston_keyboard *keyboard_state;
- struct weston_touch *touch_state;
- int pointer_device_count;
- int keyboard_device_count;
- int touch_device_count;
-
- struct weston_output *output; /* constraint */
-
- struct wl_signal destroy_signal;
- struct wl_signal updated_caps_signal;
-
- struct weston_compositor *compositor;
- struct wl_list link;
- enum weston_keyboard_modifier modifier_state;
- struct weston_surface *saved_kbd_focus;
- struct wl_listener saved_kbd_focus_listener;
- struct wl_list drag_resource_list;
-
- uint32_t selection_serial;
- struct weston_data_source *selection_data_source;
- struct wl_listener selection_data_source_listener;
- struct wl_signal selection_signal;
-
- void (*led_update)(struct weston_seat *ws, enum weston_led leds);
-
- struct input_method *input_method;
- char *seat_name;
-};
-
-enum {
- WESTON_COMPOSITOR_ACTIVE, /* normal rendering and events */
- WESTON_COMPOSITOR_IDLE, /* shell->unlock called on activity */
- WESTON_COMPOSITOR_OFFSCREEN, /* no rendering, no frame events */
- WESTON_COMPOSITOR_SLEEPING /* same as offscreen, but also set dpms
- * to off */
-};
-
-struct weston_layer_entry {
- struct wl_list link;
- struct weston_layer *layer;
-};
-
-struct weston_layer {
- struct weston_layer_entry view_list;
- struct wl_list link;
- pixman_box32_t mask;
-};
-
-struct weston_plane {
- struct weston_compositor *compositor;
- pixman_region32_t damage; /**< in global coords */
- pixman_region32_t clip;
- int32_t x, y;
- struct wl_list link;
-};
-
-struct weston_renderer {
- int (*read_pixels)(struct weston_output *output,
- pixman_format_code_t format, void *pixels,
- uint32_t x, uint32_t y,
- uint32_t width, uint32_t height);
- void (*repaint_output)(struct weston_output *output,
- pixman_region32_t *output_damage);
- void (*flush_damage)(struct weston_surface *surface);
- void (*attach)(struct weston_surface *es, struct weston_buffer *buffer);
- void (*surface_set_color)(struct weston_surface *surface,
- float red, float green,
- float blue, float alpha);
- void (*destroy)(struct weston_compositor *ec);
-
-
- /** See weston_surface_get_content_size() */
- void (*surface_get_content_size)(struct weston_surface *surface,
- int *width, int *height);
-
- /** See weston_surface_copy_content() */
- int (*surface_copy_content)(struct weston_surface *surface,
- void *target, size_t size,
- int src_x, int src_y,
- int width, int height);
-
- /** See weston_compositor_import_dmabuf() */
- bool (*import_dmabuf)(struct weston_compositor *ec,
- struct linux_dmabuf_buffer *buffer);
-};
-
-enum weston_capability {
- /* backend/renderer supports arbitrary rotation */
- WESTON_CAP_ROTATION_ANY = 0x0001,
-
- /* screencaptures need to be y-flipped */
- WESTON_CAP_CAPTURE_YFLIP = 0x0002,
-
- /* backend/renderer has a separate cursor plane */
- WESTON_CAP_CURSOR_PLANE = 0x0004,
-
- /* backend supports setting arbitrary resolutions */
- WESTON_CAP_ARBITRARY_MODES = 0x0008,
-
- /* renderer supports weston_view_set_mask() clipping */
- WESTON_CAP_VIEW_CLIP_MASK = 0x0010,
-};
-
-/* Configuration struct for an output.
- *
- * This struct is used to pass the configuration for an output
- * to the compositor backend when creating a new output.
- * The backend can subclass this struct to handle backend
- * specific data.
- */
-struct weston_backend_output_config {
- uint32_t transform;
- uint32_t width;
- uint32_t height;
- uint32_t scale;
-};
-
-/* Configuration struct for a backend.
- *
- * This struct carries the configuration for a backend, and it's
- * passed to the backend's init entry point. The backend will
- * likely want to subclass this in order to handle backend specific
- * data.
- *
- * NOTE: Alternate designs were proposed (Feb 2016) for using opaque
- * structures[1] and for section+key/value getter/setters[2]. The rationale
- * for selecting the transparent structure design is based on several
- * assumptions[3] which may require re-evaluating the design choice if they
- * fail to hold.
- *
- * 1: https://lists.freedesktop.org/archives/wayland-devel/2016-February/026989.html
- * 2: https://lists.freedesktop.org/archives/wayland-devel/2016-February/026929.html
- * 3: https://lists.freedesktop.org/archives/wayland-devel/2016-February/027228.html
- */
-struct weston_backend_config {
- /** Major version for the backend-specific config struct
- *
- * This version must match exactly what the backend expects, otherwise
- * the struct is incompatible.
- */
- uint32_t struct_version;
-
- /** Minor version of the backend-specific config struct
- *
- * This must be set to sizeof(struct backend-specific config).
- * If the value here is smaller than what the backend expects, the
- * extra config members will assume their default values.
- *
- * A value greater than what the backend expects is incompatible.
- */
- size_t struct_size;
-};
-
-struct weston_backend {
- void (*destroy)(struct weston_compositor *compositor);
- void (*restore)(struct weston_compositor *compositor);
-};
-
-struct weston_compositor {
- struct wl_signal destroy_signal;
-
- struct wl_display *wl_display;
- struct weston_shell_interface shell_interface;
-
- /* surface signals */
- struct wl_signal create_surface_signal;
- struct wl_signal activate_signal;
- struct wl_signal transform_signal;
-
- struct wl_signal kill_signal;
- struct wl_signal idle_signal;
- struct wl_signal wake_signal;
-
- struct wl_signal show_input_panel_signal;
- struct wl_signal hide_input_panel_signal;
- struct wl_signal update_input_panel_signal;
-
- struct wl_signal seat_created_signal;
- struct wl_signal output_created_signal;
- struct wl_signal output_destroyed_signal;
- struct wl_signal output_moved_signal;
- struct wl_signal output_resized_signal; /* callback argument: resized output */
-
- struct wl_signal session_signal;
- int session_active;
-
- struct weston_layer fade_layer;
- struct weston_layer cursor_layer;
-
- struct wl_list output_list;
- struct wl_list seat_list;
- struct wl_list layer_list;
- struct wl_list view_list; /* struct weston_view::link */
- struct wl_list plane_list;
- struct wl_list key_binding_list;
- struct wl_list modifier_binding_list;
- struct wl_list button_binding_list;
- struct wl_list touch_binding_list;
- struct wl_list axis_binding_list;
- struct wl_list debug_binding_list;
-
- uint32_t state;
- struct wl_event_source *idle_source;
- uint32_t idle_inhibit;
- int idle_time; /* timeout, s */
-
- const struct weston_pointer_grab_interface *default_pointer_grab;
-
- /* Repaint state. */
- struct weston_plane primary_plane;
- uint32_t capabilities; /* combination of enum weston_capability */
-
- struct weston_renderer *renderer;
-
- pixman_format_code_t read_format;
-
- struct weston_backend *backend;
-
- struct weston_launcher *launcher;
-
- uint32_t output_id_pool;
-
- struct xkb_rule_names xkb_names;
- struct xkb_context *xkb_context;
- struct weston_xkb_info *xkb_info;
-
- /* Raw keyboard processing (no libxkbcommon initialization or handling) */
- int use_xkbcommon;
-
- int32_t kb_repeat_rate;
- int32_t kb_repeat_delay;
-
- bool vt_switching;
-
- clockid_t presentation_clock;
- int32_t repaint_msec;
-
- int exit_code;
-
- void *user_data;
- void (*exit)(struct weston_compositor *c);
-};
-
-struct weston_buffer {
- struct wl_resource *resource;
- struct wl_signal destroy_signal;
- struct wl_listener destroy_listener;
-
- union {
- struct wl_shm_buffer *shm_buffer;
- void *legacy_buffer;
- };
- int32_t width, height;
- uint32_t busy_count;
- int y_inverted;
-};
-
-struct weston_buffer_reference {
- struct weston_buffer *buffer;
- struct wl_listener destroy_listener;
-};
-
-struct weston_buffer_viewport {
- struct {
- /* wl_surface.set_buffer_transform */
- uint32_t transform;
-
- /* wl_surface.set_scaling_factor */
- int32_t scale;
-
- /*
- * If src_width != wl_fixed_from_int(-1),
- * then and only then src_* are used.
- */
- wl_fixed_t src_x, src_y;
- wl_fixed_t src_width, src_height;
- } buffer;
-
- struct {
- /*
- * If width == -1, the size is inferred from the buffer.
- */
- int32_t width, height;
- } surface;
-
- int changed;
-};
-
-struct weston_region {
- struct wl_resource *resource;
- pixman_region32_t region;
-};
-
-/* Using weston_view transformations
- *
- * To add a transformation to a view, create a struct weston_transform, and
- * add it to the list view->geometry.transformation_list. Whenever you
- * change the list, anything under view->geometry, or anything in the
- * weston_transforms linked into the list, you must call
- * weston_view_geometry_dirty().
- *
- * The order in the list defines the order of transformations. Let the list
- * contain the transformation matrices M1, ..., Mn as head to tail. The
- * transformation is applied to view-local coordinate vector p as
- * P = Mn * ... * M2 * M1 * p
- * to produce the global coordinate vector P. The total transform
- * Mn * ... * M2 * M1
- * is cached in view->transform.matrix, and the inverse of it in
- * view->transform.inverse.
- *
- * The list always contains view->transform.position transformation, which
- * is the translation by view->geometry.x and y.
- *
- * If you want to apply a transformation in local coordinates, add your
- * weston_transform to the head of the list. If you want to apply a
- * transformation in global coordinates, add it to the tail of the list.
- *
- * If view->geometry.parent is set, the total transformation of this
- * view will be the parent's total transformation and this transformation
- * combined:
- * Mparent * Mn * ... * M2 * M1
- */
-
-struct weston_view {
- struct weston_surface *surface;
- struct wl_list surface_link;
- struct wl_signal destroy_signal;
-
- struct wl_list link; /* weston_compositor::view_list */
- struct weston_layer_entry layer_link; /* part of geometry */
- struct weston_plane *plane;
-
- /* For weston_layer inheritance from another view */
- struct weston_view *parent_view;
-
- pixman_region32_t clip; /* See weston_view_damage_below() */
- float alpha; /* part of geometry, see below */
-
- void *renderer_state;
-
- /* Surface geometry state, mutable.
- * If you change anything, call weston_surface_geometry_dirty().
- * That includes the transformations referenced from the list.
- */
- struct {
- float x, y; /* surface translation on display */
-
- /* struct weston_transform */
- struct wl_list transformation_list;
-
- /* managed by weston_surface_set_transform_parent() */
- struct weston_view *parent;
- struct wl_listener parent_destroy_listener;
- struct wl_list child_list; /* geometry.parent_link */
- struct wl_list parent_link;
-
- /* managed by weston_view_set_mask() */
- bool scissor_enabled;
- pixman_region32_t scissor; /* always a simple rect */
- } geometry;
-
- /* State derived from geometry state, read-only.
- * This is updated by weston_view_update_transform().
- */
- struct {
- int dirty;
-
- /* Approximations in global coordinates:
- * - boundingbox is guaranteed to include the whole view in
- * the smallest possible single rectangle.
- * - opaque is guaranteed to be fully opaque, though not
- * necessarily include all opaque areas.
- */
- pixman_region32_t boundingbox;
- pixman_region32_t opaque;
-
- /* matrix and inverse are used only if enabled = 1.
- * If enabled = 0, use x, y, width, height directly.
- */
- int enabled;
- struct weston_matrix matrix;
- struct weston_matrix inverse;
-
- struct weston_transform position; /* matrix from x, y */
- } transform;
-
- /*
- * The primary output for this view.
- * Used for picking the output for driving internal animations on the
- * view, inheriting the primary output for related views in shells, etc.
- */
- struct weston_output *output;
-
- /*
- * A more complete representation of all outputs this surface is
- * displayed on.
- */
- uint32_t output_mask;
-
- /* Per-surface Presentation feedback flags, controlled by backend. */
- uint32_t psf_flags;
-};
-
-struct weston_surface_state {
- /* wl_surface.attach */
- int newly_attached;
- struct weston_buffer *buffer;
- struct wl_listener buffer_destroy_listener;
- int32_t sx;
- int32_t sy;
-
- /* wl_surface.damage */
- pixman_region32_t damage_surface;
- /* wl_surface.damage_buffer */
- pixman_region32_t damage_buffer;
-
- /* wl_surface.set_opaque_region */
- pixman_region32_t opaque;
-
- /* wl_surface.set_input_region */
- pixman_region32_t input;
-
- /* wl_surface.frame */
- struct wl_list frame_callback_list;
-
- /* presentation.feedback */
- struct wl_list feedback_list;
-
- /* wl_surface.set_buffer_transform */
- /* wl_surface.set_scaling_factor */
- /* wp_viewport.set_source */
- /* wp_viewport.set_destination */
- struct weston_buffer_viewport buffer_viewport;
-};
-
-struct weston_surface {
- struct wl_resource *resource;
- struct wl_signal destroy_signal; /* callback argument: this surface */
- struct weston_compositor *compositor;
-
- /** Damage in local coordinates from the client, for tex upload. */
- pixman_region32_t damage;
-
- pixman_region32_t opaque; /* part of geometry, see below */
- pixman_region32_t input;
- int32_t width, height;
- int32_t ref_count;
-
- /* Not for long-term storage. This exists for book-keeping while
- * iterating over surfaces and views
- */
- bool touched;
-
- void *renderer_state;
-
- struct wl_list views;
-
- /*
- * Which output to vsync this surface to.
- * Used to determine whether to send or queue frame events, and for
- * other client-visible syncing/throttling tied to the output
- * repaint cycle.
- */
- struct weston_output *output;
-
- /*
- * A more complete representation of all outputs this surface is
- * displayed on.
- */
- uint32_t output_mask;
-
- struct wl_list frame_callback_list;
- struct wl_list feedback_list;
-
- struct weston_buffer_reference buffer_ref;
- struct weston_buffer_viewport buffer_viewport;
- int32_t width_from_buffer; /* before applying viewport */
- int32_t height_from_buffer;
- bool keep_buffer; /* for backends to prevent early release */
-
- /* wp_viewport resource for this surface */
- struct wl_resource *viewport_resource;
-
- /* All the pending state, that wl_surface.commit will apply. */
- struct weston_surface_state pending;
-
- /* Matrices representating of the full transformation between
- * buffer and surface coordinates. These matrices are updated
- * using the weston_surface_build_buffer_matrix function. */
- struct weston_matrix buffer_to_surface_matrix;
- struct weston_matrix surface_to_buffer_matrix;
-
- /*
- * If non-NULL, this function will be called on
- * wl_surface::commit after a new buffer has been set up for
- * this surface. The integer params are the sx and sy
- * parameters supplied to wl_surface::attach.
- */
- void (*configure)(struct weston_surface *es, int32_t sx, int32_t sy);
- void *configure_private;
- int (*get_label)(struct weston_surface *surface, char *buf, size_t len);
-
- /* Parent's list of its sub-surfaces, weston_subsurface:parent_link.
- * Contains also the parent itself as a dummy weston_subsurface,
- * if the list is not empty.
- */
- struct wl_list subsurface_list; /* weston_subsurface::parent_link */
- struct wl_list subsurface_list_pending; /* ...::parent_link_pending */
-
- /*
- * For tracking protocol role assignments. Different roles may
- * have the same configure hook, e.g. in shell.c. Configure hook
- * may get reset, this will not.
- * XXX: map configure functions 1:1 to roles, and never reset it,
- * and replace role_name with configure.
- */
- const char *role_name;
-
- struct weston_timeline_object timeline;
-};
-
-struct weston_subsurface {
- struct wl_resource *resource;
-
- /* guaranteed to be valid and non-NULL */
- struct weston_surface *surface;
- struct wl_listener surface_destroy_listener;
-
- /* can be NULL */
- struct weston_surface *parent;
- struct wl_listener parent_destroy_listener;
- struct wl_list parent_link;
- struct wl_list parent_link_pending;
-
- struct {
- int32_t x;
- int32_t y;
- int set;
- } position;
-
- int has_cached_data;
- struct weston_surface_state cached;
- struct weston_buffer_reference cached_buffer_ref;
-
- int synchronized;
-
- /* Used for constructing the view tree */
- struct wl_list unused_views;
-};
-
-enum weston_key_state_update {
- STATE_UPDATE_AUTOMATIC,
- STATE_UPDATE_NONE,
-};
-
-void
-weston_version(int *major, int *minor, int *micro);
-
-void
-weston_view_update_transform(struct weston_view *view);
-
-void
-weston_view_geometry_dirty(struct weston_view *view);
-
-void
-weston_view_to_global_fixed(struct weston_view *view,
- wl_fixed_t sx, wl_fixed_t sy,
- wl_fixed_t *x, wl_fixed_t *y);
-void
-weston_view_to_global_float(struct weston_view *view,
- float sx, float sy, float *x, float *y);
-
-void
-weston_view_from_global_float(struct weston_view *view,
- float x, float y, float *vx, float *vy);
-void
-weston_view_from_global(struct weston_view *view,
- int32_t x, int32_t y, int32_t *vx, int32_t *vy);
-void
-weston_view_from_global_fixed(struct weston_view *view,
- wl_fixed_t x, wl_fixed_t y,
- wl_fixed_t *vx, wl_fixed_t *vy);
-
-void
-weston_surface_to_buffer_float(struct weston_surface *surface,
- float x, float y, float *bx, float *by);
-pixman_box32_t
-weston_surface_to_buffer_rect(struct weston_surface *surface,
- pixman_box32_t rect);
-
-void
-weston_surface_to_buffer_region(struct weston_surface *surface,
- pixman_region32_t *surface_region,
- pixman_region32_t *buffer_region);
-
-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_seat *seat);
-void
-notify_motion(struct weston_seat *seat, uint32_t time,
- struct weston_pointer_motion_event *event);
-void
-notify_motion_absolute(struct weston_seat *seat, uint32_t time,
- double x, double y);
-void
-notify_button(struct weston_seat *seat, uint32_t time, int32_t button,
- enum wl_pointer_button_state state);
-void
-notify_axis(struct weston_seat *seat, uint32_t time,
- struct weston_pointer_axis_event *event);
-void
-notify_axis_source(struct weston_seat *seat, uint32_t source);
-
-void
-notify_pointer_frame(struct weston_seat *seat);
-
-void
-notify_key(struct weston_seat *seat, uint32_t time, uint32_t key,
- enum wl_keyboard_key_state state,
- enum weston_key_state_update update_state);
-void
-notify_modifiers(struct weston_seat *seat, uint32_t serial);
-
-void
-notify_pointer_focus(struct weston_seat *seat, struct weston_output *output,
- double x, double y);
-
-void
-notify_keyboard_focus_in(struct weston_seat *seat, struct wl_array *keys,
- enum weston_key_state_update update_state);
-void
-notify_keyboard_focus_out(struct weston_seat *seat);
-
-void
-notify_touch(struct weston_seat *seat, uint32_t time, int touch_id,
- double x, double y, int touch_type);
-void
-notify_touch_frame(struct weston_seat *seat);
-
-void
-notify_touch_cancel(struct weston_seat *seat);
-
-void
-weston_layer_entry_insert(struct weston_layer_entry *list,
- struct weston_layer_entry *entry);
-void
-weston_layer_entry_remove(struct weston_layer_entry *entry);
-void
-weston_layer_init(struct weston_layer *layer, struct wl_list *below);
-
-void
-weston_layer_set_mask(struct weston_layer *layer, int x, int y, int width, int height);
-
-void
-weston_layer_set_mask_infinite(struct weston_layer *layer);
-
-void
-weston_plane_init(struct weston_plane *plane,
- struct weston_compositor *ec,
- int32_t x, int32_t y);
-void
-weston_plane_release(struct weston_plane *plane);
-
-void
-weston_compositor_stack_plane(struct weston_compositor *ec,
- struct weston_plane *plane,
- struct weston_plane *above);
-
-/* An invalid flag in presented_flags to catch logic errors. */
-#define WP_PRESENTATION_FEEDBACK_INVALID (1U << 31)
-
-void
-weston_output_finish_frame(struct weston_output *output,
- const struct timespec *stamp,
- uint32_t presented_flags);
-void
-weston_output_schedule_repaint(struct weston_output *output);
-void
-weston_output_damage(struct weston_output *output);
-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_offscreen(struct weston_compositor *compositor);
-void
-weston_compositor_sleep(struct weston_compositor *compositor);
-struct weston_view *
-weston_compositor_pick_view(struct weston_compositor *compositor,
- wl_fixed_t x, wl_fixed_t y,
- wl_fixed_t *sx, wl_fixed_t *sy);
-
-
-struct weston_binding;
-typedef void (*weston_key_binding_handler_t)(struct weston_keyboard *keyboard,
- uint32_t time, uint32_t key,
- void *data);
-struct weston_binding *
-weston_compositor_add_key_binding(struct weston_compositor *compositor,
- uint32_t key,
- enum weston_keyboard_modifier modifier,
- weston_key_binding_handler_t binding,
- void *data);
-
-typedef void (*weston_modifier_binding_handler_t)(struct weston_keyboard *keyboard,
- enum weston_keyboard_modifier modifier,
- void *data);
-struct weston_binding *
-weston_compositor_add_modifier_binding(struct weston_compositor *compositor,
- enum weston_keyboard_modifier modifier,
- weston_modifier_binding_handler_t binding,
- void *data);
-
-typedef void (*weston_button_binding_handler_t)(struct weston_pointer *pointer,
- uint32_t time, uint32_t button,
- void *data);
-struct weston_binding *
-weston_compositor_add_button_binding(struct weston_compositor *compositor,
- uint32_t button,
- enum weston_keyboard_modifier modifier,
- weston_button_binding_handler_t binding,
- void *data);
-
-typedef void (*weston_touch_binding_handler_t)(struct weston_touch *touch,
- uint32_t time,
- void *data);
-struct weston_binding *
-weston_compositor_add_touch_binding(struct weston_compositor *compositor,
- enum weston_keyboard_modifier modifier,
- weston_touch_binding_handler_t binding,
- void *data);
-
-typedef void (*weston_axis_binding_handler_t)(struct weston_pointer *pointer,
- uint32_t time,
- struct weston_pointer_axis_event *event,
- void *data);
-struct weston_binding *
-weston_compositor_add_axis_binding(struct weston_compositor *compositor,
- uint32_t axis,
- enum weston_keyboard_modifier modifier,
- weston_axis_binding_handler_t binding,
- void *data);
-struct weston_binding *
-weston_compositor_add_debug_binding(struct weston_compositor *compositor,
- uint32_t key,
- weston_key_binding_handler_t binding,
- void *data);
-void
-weston_binding_destroy(struct weston_binding *binding);
-
-void
-weston_install_debug_key_binding(struct weston_compositor *compositor,
- uint32_t mod);
-
-void
-weston_binding_list_destroy_all(struct wl_list *list);
-
-void
-weston_compositor_run_key_binding(struct weston_compositor *compositor,
- struct weston_keyboard *keyboard,
- uint32_t time,
- uint32_t key,
- enum wl_keyboard_key_state state);
-
-void
-weston_compositor_run_modifier_binding(struct weston_compositor *compositor,
- struct weston_keyboard *keyboard,
- enum weston_keyboard_modifier modifier,
- enum wl_keyboard_key_state state);
-void
-weston_compositor_run_button_binding(struct weston_compositor *compositor,
- struct weston_pointer *pointer, uint32_t time,
- uint32_t button,
- enum wl_pointer_button_state value);
-void
-weston_compositor_run_touch_binding(struct weston_compositor *compositor,
- struct weston_touch *touch, uint32_t time,
- int touch_type);
-int
-weston_compositor_run_axis_binding(struct weston_compositor *compositor,
- struct weston_pointer *pointer, uint32_t time,
- struct weston_pointer_axis_event *event);
-int
-weston_compositor_run_debug_binding(struct weston_compositor *compositor,
- struct weston_keyboard *keyboard, uint32_t time,
- uint32_t key,
- enum wl_keyboard_key_state state);
-
-void
-weston_compositor_set_default_pointer_grab(struct weston_compositor *compositor,
- const struct weston_pointer_grab_interface *interface);
-
-int
-weston_environment_get_fd(const char *env);
-
-struct wl_list *
-weston_compositor_top(struct weston_compositor *compositor);
-
-struct weston_surface *
-weston_surface_create(struct weston_compositor *compositor);
-
-struct weston_view *
-weston_view_create(struct weston_surface *surface);
-
-void
-weston_view_destroy(struct weston_view *view);
-
-void
-weston_view_set_position(struct weston_view *view,
- float x, float y);
-
-void
-weston_view_set_transform_parent(struct weston_view *view,
- struct weston_view *parent);
-
-void
-weston_view_set_mask(struct weston_view *view,
- int x, int y, int width, int height);
-
-void
-weston_view_set_mask_infinite(struct weston_view *view);
-
-bool
-weston_view_is_mapped(struct weston_view *view);
-
-void
-weston_view_schedule_repaint(struct weston_view *view);
-
-bool
-weston_surface_is_mapped(struct weston_surface *surface);
-
-void
-weston_surface_set_size(struct weston_surface *surface,
- int32_t width, int32_t height);
-
-void
-weston_surface_schedule_repaint(struct weston_surface *surface);
-
-void
-weston_surface_damage(struct weston_surface *surface);
-
-void
-weston_view_damage_below(struct weston_view *view);
-
-void
-weston_view_move_to_plane(struct weston_view *view,
- struct weston_plane *plane);
-void
-weston_view_unmap(struct weston_view *view);
-
-void
-weston_surface_unmap(struct weston_surface *surface);
-
-struct weston_surface *
-weston_surface_get_main_surface(struct weston_surface *surface);
-
-int
-weston_surface_set_role(struct weston_surface *surface,
- const char *role_name,
- struct wl_resource *error_resource,
- uint32_t error_code);
-
-void
-weston_surface_set_label_func(struct weston_surface *surface,
- int (*desc)(struct weston_surface *,
- char *, size_t));
-
-void
-weston_surface_get_content_size(struct weston_surface *surface,
- int *width, int *height);
-
-int
-weston_surface_copy_content(struct weston_surface *surface,
- void *target, size_t size,
- int src_x, int src_y,
- int width, int height);
-
-struct weston_buffer *
-weston_buffer_from_resource(struct wl_resource *resource);
-
-void
-weston_buffer_reference(struct weston_buffer_reference *ref,
- struct weston_buffer *buffer);
-
-uint32_t
-weston_compositor_get_time(void);
-
-void
-weston_compositor_destroy(struct weston_compositor *ec);
-struct weston_compositor *
-weston_compositor_create(struct wl_display *display, void *user_data);
-
-enum weston_compositor_backend {
- WESTON_BACKEND_DRM,
- WESTON_BACKEND_FBDEV,
- WESTON_BACKEND_HEADLESS,
- WESTON_BACKEND_RDP,
- WESTON_BACKEND_WAYLAND,
- WESTON_BACKEND_X11,
-};
-
-int
-weston_compositor_load_backend(struct weston_compositor *compositor,
- enum weston_compositor_backend backend,
- struct weston_backend_config *config_base);
-void
-weston_compositor_exit(struct weston_compositor *ec);
-void *
-weston_compositor_get_user_data(struct weston_compositor *compositor);
-int
-weston_compositor_set_presentation_clock(struct weston_compositor *compositor,
- clockid_t clk_id);
-int
-weston_compositor_set_presentation_clock_software(
- struct weston_compositor *compositor);
-void
-weston_compositor_read_presentation_clock(
- const struct weston_compositor *compositor,
- struct timespec *ts);
-
-bool
-weston_compositor_import_dmabuf(struct weston_compositor *compositor,
- struct linux_dmabuf_buffer *buffer);
-
-void
-weston_compositor_shutdown(struct weston_compositor *ec);
-void
-weston_compositor_exit_with_code(struct weston_compositor *compositor,
- int exit_code);
-void
-weston_output_init_zoom(struct weston_output *output);
-void
-weston_output_update_zoom(struct weston_output *output);
-void
-weston_output_activate_zoom(struct weston_output *output,
- struct weston_seat *seat);
-void
-weston_output_update_matrix(struct weston_output *output);
-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 transform, int32_t scale);
-void
-weston_compositor_add_output(struct weston_compositor *compositor,
- struct weston_output *output);
-void
-weston_output_destroy(struct weston_output *output);
-void
-weston_output_transform_coordinate(struct weston_output *output,
- double device_x, double device_y,
- double *x, double *y);
-
-void
-weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec,
- const char *seat_name);
-void
-weston_seat_init_pointer(struct weston_seat *seat);
-void
-weston_seat_release_pointer(struct weston_seat *seat);
-int
-weston_seat_init_keyboard(struct weston_seat *seat, struct xkb_keymap *keymap);
-void
-weston_seat_release_keyboard(struct weston_seat *seat);
-void
-weston_seat_init_touch(struct weston_seat *seat);
-void
-weston_seat_release_touch(struct weston_seat *seat);
-void
-weston_seat_repick(struct weston_seat *seat);
-void
-weston_seat_update_keymap(struct weston_seat *seat, struct xkb_keymap *keymap);
-
-void
-weston_seat_release(struct weston_seat *seat);
-int
-weston_compositor_set_xkb_rule_names(struct weston_compositor *ec,
- struct xkb_rule_names *names);
-void
-weston_compositor_xkb_destroy(struct weston_compositor *ec);
-
-/* String literal of spaces, the same width as the timestamp. */
-#define STAMP_SPACE " "
-
-typedef int (*log_func_t)(const char *fmt, va_list ap);
-void
-weston_log_set_handler(log_func_t log, log_func_t cont);
-int
-weston_vlog(const char *fmt, va_list ap);
-int
-weston_vlog_continue(const char *fmt, va_list ap);
-int
-weston_log(const char *fmt, ...)
- __attribute__ ((format (printf, 1, 2)));
-int
-weston_log_continue(const char *fmt, ...)
- __attribute__ ((format (printf, 1, 2)));
-
-enum {
- TTY_ENTER_VT,
- TTY_LEAVE_VT
-};
-
-struct tty *
-tty_create(struct weston_compositor *compositor, int tty_nr);
-
-void
-tty_destroy(struct tty *tty);
-
-void
-tty_reset(struct tty *tty);
-
-int
-tty_activate_vt(struct tty *tty, int vt);
-
-enum weston_screenshooter_outcome {
- WESTON_SCREENSHOOTER_SUCCESS,
- WESTON_SCREENSHOOTER_NO_MEMORY,
- WESTON_SCREENSHOOTER_BAD_BUFFER
-};
-
-typedef void (*weston_screenshooter_done_func_t)(void *data,
- enum weston_screenshooter_outcome outcome);
-int
-weston_screenshooter_shoot(struct weston_output *output, struct weston_buffer *buffer,
- weston_screenshooter_done_func_t done, void *data);
-struct weston_recorder *
-weston_recorder_start(struct weston_output *output, const char *filename);
-void
-weston_recorder_stop(struct weston_recorder *recorder);
-
-struct clipboard *
-clipboard_create(struct weston_seat *seat);
-
-struct text_backend;
-
-struct text_backend *
-text_backend_init(struct weston_compositor *ec);
-
-void
-text_backend_destroy(struct text_backend *text_backend);
-
-struct weston_view_animation;
-typedef void (*weston_view_animation_done_func_t)(struct weston_view_animation *animation, void *data);
-
-void
-weston_view_animation_destroy(struct weston_view_animation *animation);
-
-struct weston_view_animation *
-weston_zoom_run(struct weston_view *view, float start, float stop,
- weston_view_animation_done_func_t done, void *data);
-
-struct weston_view_animation *
-weston_fade_run(struct weston_view *view,
- float start, float end, float k,
- weston_view_animation_done_func_t done, void *data);
-
-struct weston_view_animation *
-weston_move_scale_run(struct weston_view *view, int dx, int dy,
- float start, float end, int reverse,
- weston_view_animation_done_func_t done, void *data);
-
-void
-weston_fade_update(struct weston_view_animation *fade, float target);
-
-struct weston_view_animation *
-weston_stable_fade_run(struct weston_view *front_view, float start,
- struct weston_view *back_view, float end,
- weston_view_animation_done_func_t done, void *data);
-
-struct weston_view_animation *
-weston_slide_run(struct weston_view *view, float start, float stop,
- weston_view_animation_done_func_t done, void *data);
-
-void
-weston_surface_set_color(struct weston_surface *surface,
- float red, float green, float blue, float alpha);
-
-void
-weston_surface_destroy(struct weston_surface *surface);
-
-int
-weston_output_mode_set_native(struct weston_output *output,
- struct weston_mode *mode,
- int32_t scale);
-int
-weston_output_mode_switch_to_temporary(struct weston_output *output,
- struct weston_mode *mode,
- int32_t scale);
-int
-weston_output_mode_switch_to_native(struct weston_output *output);
-
-int
-noop_renderer_init(struct weston_compositor *ec);
-
-int
-backend_init(struct weston_compositor *c,
- struct weston_backend_config *config_base);
-int
-module_init(struct weston_compositor *compositor,
- int *argc, char *argv[]);
-
-void
-weston_transformed_coord(int width, int height,
- enum wl_output_transform transform,
- int32_t scale,
- float sx, float sy, float *bx, float *by);
-pixman_box32_t
-weston_transformed_rect(int width, int height,
- enum wl_output_transform transform,
- int32_t scale,
- pixman_box32_t rect);
-void
-weston_matrix_transform_region(pixman_region32_t *dest,
- struct weston_matrix *matrix,
- pixman_region32_t *src);
-void
-weston_transformed_region(int width, int height,
- enum wl_output_transform transform,
- int32_t scale,
- pixman_region32_t *src, pixman_region32_t *dest);
-
-void *
-weston_load_module(const char *name, const char *entrypoint);
-
-int
-weston_parse_transform(const char *transform, uint32_t *out);
-
-const char *
-weston_transform_to_string(uint32_t output_transform);
-
-struct weston_keyboard *
-weston_seat_get_keyboard(struct weston_seat *seat);
-
-struct weston_pointer *
-weston_seat_get_pointer(struct weston_seat *seat);
-
-struct weston_touch *
-weston_seat_get_touch(struct weston_seat *seat);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- * Copyright © 2011 Kristian Høgsberg
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <assert.h>
-
-#include "compositor.h"
-#include "shared/helpers.h"
-
-struct weston_drag {
- struct wl_client *client;
- struct weston_data_source *data_source;
- struct wl_listener data_source_listener;
- struct weston_view *focus;
- struct wl_resource *focus_resource;
- struct wl_listener focus_listener;
- struct weston_view *icon;
- struct wl_listener icon_destroy_listener;
- int32_t dx, dy;
- struct weston_keyboard_grab keyboard_grab;
-};
-
-struct weston_pointer_drag {
- struct weston_drag base;
- struct weston_pointer_grab grab;
-};
-
-struct weston_touch_drag {
- struct weston_drag base;
- struct weston_touch_grab grab;
-};
-
-#define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \
- WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \
- WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
-
-static void
-data_offer_accept(struct wl_client *client, struct wl_resource *resource,
- uint32_t serial, const char *mime_type)
-{
- struct weston_data_offer *offer = wl_resource_get_user_data(resource);
-
- /* Protect against untimely calls from older data offers */
- if (!offer->source || offer != offer->source->offer)
- return;
-
- /* 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? */
-
- offer->source->accept(offer->source, serial, mime_type);
- offer->source->accepted = mime_type != NULL;
-}
-
-static void
-data_offer_receive(struct wl_client *client, struct wl_resource *resource,
- const char *mime_type, int32_t fd)
-{
- struct weston_data_offer *offer = wl_resource_get_user_data(resource);
-
- if (offer->source && offer == offer->source->offer)
- offer->source->send(offer->source, mime_type, fd);
- else
- close(fd);
-}
-
-static void
-data_offer_destroy(struct wl_client *client, struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static void
-data_source_notify_finish(struct weston_data_source *source)
-{
- if (!source->actions_set)
- return;
-
- if (source->offer->in_ask &&
- wl_resource_get_version(source->resource) >=
- WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
- wl_data_source_send_action(source->resource,
- source->current_dnd_action);
- }
-
- if (wl_resource_get_version(source->resource) >=
- WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) {
- wl_data_source_send_dnd_finished(source->resource);
- }
-
- source->offer = NULL;
-}
-
-static uint32_t
-data_offer_choose_action(struct weston_data_offer *offer)
-{
- uint32_t available_actions, preferred_action = 0;
- uint32_t source_actions, offer_actions;
-
- if (wl_resource_get_version(offer->resource) >=
- WL_DATA_OFFER_ACTION_SINCE_VERSION) {
- offer_actions = offer->dnd_actions;
- preferred_action = offer->preferred_dnd_action;
- } else {
- offer_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
- }
-
- if (wl_resource_get_version(offer->source->resource) >=
- WL_DATA_SOURCE_ACTION_SINCE_VERSION)
- source_actions = offer->source->dnd_actions;
- else
- source_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
-
- available_actions = offer_actions & source_actions;
-
- if (!available_actions)
- return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
-
- if (offer->source->seat &&
- offer->source->compositor_action & available_actions)
- return offer->source->compositor_action;
-
- /* If the dest side has a preferred DnD action, use it */
- if ((preferred_action & available_actions) != 0)
- return preferred_action;
-
- /* Use the first found action, in bit order */
- return 1 << (ffs(available_actions) - 1);
-}
-
-static void
-data_offer_update_action(struct weston_data_offer *offer)
-{
- uint32_t action;
-
- if (!offer->source)
- return;
-
- action = data_offer_choose_action(offer);
-
- if (offer->source->current_dnd_action == action)
- return;
-
- offer->source->current_dnd_action = action;
-
- if (offer->in_ask)
- return;
-
- if (wl_resource_get_version(offer->source->resource) >=
- WL_DATA_SOURCE_ACTION_SINCE_VERSION)
- wl_data_source_send_action(offer->source->resource, action);
-
- if (wl_resource_get_version(offer->resource) >=
- WL_DATA_OFFER_ACTION_SINCE_VERSION)
- wl_data_offer_send_action(offer->resource, action);
-}
-
-static void
-data_offer_set_actions(struct wl_client *client,
- struct wl_resource *resource,
- uint32_t dnd_actions, uint32_t preferred_action)
-{
- struct weston_data_offer *offer = wl_resource_get_user_data(resource);
-
- if (dnd_actions & ~ALL_ACTIONS) {
- wl_resource_post_error(offer->resource,
- WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK,
- "invalid action mask %x", dnd_actions);
- return;
- }
-
- if (preferred_action &&
- (!(preferred_action & dnd_actions) ||
- __builtin_popcount(preferred_action) > 1)) {
- wl_resource_post_error(offer->resource,
- WL_DATA_OFFER_ERROR_INVALID_ACTION,
- "invalid action %x", preferred_action);
- return;
- }
-
- offer->dnd_actions = dnd_actions;
- offer->preferred_dnd_action = preferred_action;
- data_offer_update_action(offer);
-}
-
-static void
-data_offer_finish(struct wl_client *client, struct wl_resource *resource)
-{
- struct weston_data_offer *offer = wl_resource_get_user_data(resource);
-
- if (!offer->source || offer->source->offer != offer)
- return;
-
- /* Disallow finish while we have a grab driving drag-and-drop, or
- * if the negotiation is not at the right stage
- */
- if (offer->source->seat ||
- !offer->source->accepted) {
- wl_resource_post_error(offer->resource,
- WL_DATA_OFFER_ERROR_INVALID_FINISH,
- "premature finish request");
- return;
- }
-
- switch (offer->source->current_dnd_action) {
- case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE:
- case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK:
- wl_resource_post_error(offer->resource,
- WL_DATA_OFFER_ERROR_INVALID_OFFER,
- "offer finished with an invalid action");
- return;
- default:
- break;
- }
-
- data_source_notify_finish(offer->source);
-}
-
-static const struct wl_data_offer_interface data_offer_interface = {
- data_offer_accept,
- data_offer_receive,
- data_offer_destroy,
- data_offer_finish,
- data_offer_set_actions,
-};
-
-static void
-destroy_data_offer(struct wl_resource *resource)
-{
- struct weston_data_offer *offer = wl_resource_get_user_data(resource);
-
- if (!offer->source)
- goto out;
-
- wl_list_remove(&offer->source_destroy_listener.link);
-
- if (offer->source->offer != offer)
- goto out;
-
- /* If the drag destination has version < 3, wl_data_offer.finish
- * won't be called, so do this here as a safety net, because
- * we still want the version >=3 drag source to be happy.
- */
- if (wl_resource_get_version(offer->resource) <
- WL_DATA_OFFER_ACTION_SINCE_VERSION) {
- data_source_notify_finish(offer->source);
- } else if (offer->source->resource &&
- wl_resource_get_version(offer->source->resource) >=
- WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) {
- wl_data_source_send_cancelled(offer->source->resource);
- }
-
- offer->source->offer = NULL;
-out:
- free(offer);
-}
-
-static void
-destroy_offer_data_source(struct wl_listener *listener, void *data)
-{
- struct weston_data_offer *offer;
-
- offer = container_of(listener, struct weston_data_offer,
- source_destroy_listener);
-
- offer->source = NULL;
-}
-
-static struct weston_data_offer *
-weston_data_source_send_offer(struct weston_data_source *source,
- struct wl_resource *target)
-{
- struct weston_data_offer *offer;
- char **p;
-
- offer = malloc(sizeof *offer);
- if (offer == NULL)
- return NULL;
-
- offer->resource =
- wl_resource_create(wl_resource_get_client(target),
- &wl_data_offer_interface,
- wl_resource_get_version(target), 0);
- if (offer->resource == NULL) {
- free(offer);
- return NULL;
- }
-
- wl_resource_set_implementation(offer->resource, &data_offer_interface,
- offer, destroy_data_offer);
-
- offer->in_ask = false;
- offer->dnd_actions = 0;
- offer->preferred_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
- offer->source = source;
- offer->source_destroy_listener.notify = destroy_offer_data_source;
- wl_signal_add(&source->destroy_signal,
- &offer->source_destroy_listener);
-
- wl_data_device_send_data_offer(target, offer->resource);
-
- wl_array_for_each(p, &source->mime_types)
- wl_data_offer_send_offer(offer->resource, *p);
-
- source->offer = offer;
- source->accepted = false;
-
- return offer;
-}
-
-static void
-data_source_offer(struct wl_client *client,
- struct wl_resource *resource,
- const char *type)
-{
- struct weston_data_source *source =
- wl_resource_get_user_data(resource);
- 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);
-}
-
-static void
-data_source_set_actions(struct wl_client *client,
- struct wl_resource *resource,
- uint32_t dnd_actions)
-{
- struct weston_data_source *source =
- wl_resource_get_user_data(resource);
-
- if (source->actions_set) {
- wl_resource_post_error(source->resource,
- WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
- "cannot set actions more than once");
- return;
- }
-
- if (dnd_actions & ~ALL_ACTIONS) {
- wl_resource_post_error(source->resource,
- WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
- "invalid action mask %x", dnd_actions);
- return;
- }
-
- if (source->seat) {
- wl_resource_post_error(source->resource,
- WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
- "invalid action change after "
- "wl_data_device.start_drag");
- return;
- }
-
- source->dnd_actions = dnd_actions;
- source->actions_set = true;
-}
-
-static struct wl_data_source_interface data_source_interface = {
- data_source_offer,
- data_source_destroy,
- data_source_set_actions
-};
-
-static void
-drag_surface_configure(struct weston_drag *drag,
- struct weston_pointer *pointer,
- struct weston_touch *touch,
- struct weston_surface *es,
- int32_t sx, int32_t sy)
-{
- struct weston_layer_entry *list;
- float fx, fy;
-
- assert((pointer != NULL && touch == NULL) ||
- (pointer == NULL && touch != NULL));
-
- if (!weston_surface_is_mapped(es) && es->buffer_ref.buffer) {
- if (pointer && pointer->sprite &&
- weston_view_is_mapped(pointer->sprite))
- list = &pointer->sprite->layer_link;
- else
- list = &es->compositor->cursor_layer.view_list;
-
- weston_layer_entry_remove(&drag->icon->layer_link);
- weston_layer_entry_insert(list, &drag->icon->layer_link);
- weston_view_update_transform(drag->icon);
- pixman_region32_clear(&es->pending.input);
- }
-
- drag->dx += sx;
- drag->dy += sy;
-
- /* init to 0 for avoiding a compile warning */
- fx = fy = 0;
- if (pointer) {
- fx = wl_fixed_to_double(pointer->x) + drag->dx;
- fy = wl_fixed_to_double(pointer->y) + drag->dy;
- } else if (touch) {
- fx = wl_fixed_to_double(touch->grab_x) + drag->dx;
- fy = wl_fixed_to_double(touch->grab_y) + drag->dy;
- }
- weston_view_set_position(drag->icon, fx, fy);
-}
-
-static int
-pointer_drag_surface_get_label(struct weston_surface *surface,
- char *buf, size_t len)
-{
- return snprintf(buf, len, "pointer drag icon");
-}
-
-static void
-pointer_drag_surface_configure(struct weston_surface *es,
- int32_t sx, int32_t sy)
-{
- struct weston_pointer_drag *drag = es->configure_private;
- struct weston_pointer *pointer = drag->grab.pointer;
-
- assert(es->configure == pointer_drag_surface_configure);
-
- drag_surface_configure(&drag->base, pointer, NULL, es, sx, sy);
-}
-
-static int
-touch_drag_surface_get_label(struct weston_surface *surface,
- char *buf, size_t len)
-{
- return snprintf(buf, len, "touch drag icon");
-}
-
-static void
-touch_drag_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy)
-{
- struct weston_touch_drag *drag = es->configure_private;
- struct weston_touch *touch = drag->grab.touch;
-
- assert(es->configure == touch_drag_surface_configure);
-
- drag_surface_configure(&drag->base, NULL, touch, es, sx, sy);
-}
-
-static void
-destroy_drag_focus(struct wl_listener *listener, void *data)
-{
- struct weston_drag *drag =
- container_of(listener, struct weston_drag, focus_listener);
-
- drag->focus_resource = NULL;
-}
-
-static void
-weston_drag_set_focus(struct weston_drag *drag,
- struct weston_seat *seat,
- struct weston_view *view,
- wl_fixed_t sx, wl_fixed_t sy)
-{
- struct wl_resource *resource, *offer_resource = NULL;
- struct wl_display *display = seat->compositor->wl_display;
- struct weston_data_offer *offer;
- uint32_t serial;
-
- if (drag->focus && view && drag->focus->surface == view->surface) {
- drag->focus = view;
- return;
- }
-
- if (drag->focus_resource) {
- wl_data_device_send_leave(drag->focus_resource);
- wl_list_remove(&drag->focus_listener.link);
- drag->focus_resource = NULL;
- drag->focus = NULL;
- }
-
- if (!view || !view->surface->resource)
- return;
-
- if (!drag->data_source &&
- wl_resource_get_client(view->surface->resource) != drag->client)
- return;
-
- if (drag->data_source &&
- drag->data_source->offer) {
- /* Unlink the offer from the source */
- offer = drag->data_source->offer;
- offer->source = NULL;
- drag->data_source->offer = NULL;
- wl_list_remove(&offer->source_destroy_listener.link);
- }
-
- resource = wl_resource_find_for_client(&seat->drag_resource_list,
- wl_resource_get_client(view->surface->resource));
- if (!resource)
- return;
-
- serial = wl_display_next_serial(display);
-
- if (drag->data_source) {
- drag->data_source->accepted = false;
- offer = weston_data_source_send_offer(drag->data_source, resource);
- if (offer == NULL)
- return;
-
- data_offer_update_action(offer);
-
- offer_resource = offer->resource;
- if (wl_resource_get_version (offer_resource) >=
- WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) {
- wl_data_offer_send_source_actions (offer_resource,
- drag->data_source->dnd_actions);
- }
- }
-
- wl_data_device_send_enter(resource, serial, view->surface->resource,
- sx, sy, offer_resource);
-
- drag->focus = view;
- drag->focus_listener.notify = destroy_drag_focus;
- wl_resource_add_destroy_listener(resource, &drag->focus_listener);
- drag->focus_resource = resource;
-}
-
-static void
-drag_grab_focus(struct weston_pointer_grab *grab)
-{
- struct weston_pointer_drag *drag =
- container_of(grab, struct weston_pointer_drag, grab);
- struct weston_pointer *pointer = grab->pointer;
- struct weston_view *view;
- wl_fixed_t sx, sy;
-
- view = weston_compositor_pick_view(pointer->seat->compositor,
- pointer->x, pointer->y,
- &sx, &sy);
- if (drag->base.focus != view)
- weston_drag_set_focus(&drag->base, pointer->seat, view, sx, sy);
-}
-
-static void
-drag_grab_motion(struct weston_pointer_grab *grab, uint32_t time,
- struct weston_pointer_motion_event *event)
-{
- struct weston_pointer_drag *drag =
- container_of(grab, struct weston_pointer_drag, grab);
- struct weston_pointer *pointer = drag->grab.pointer;
- float fx, fy;
- wl_fixed_t sx, sy;
-
- weston_pointer_move(pointer, event);
-
- if (drag->base.icon) {
- fx = wl_fixed_to_double(pointer->x) + drag->base.dx;
- fy = wl_fixed_to_double(pointer->y) + drag->base.dy;
- weston_view_set_position(drag->base.icon, fx, fy);
- weston_view_schedule_repaint(drag->base.icon);
- }
-
- if (drag->base.focus_resource) {
- weston_view_from_global_fixed(drag->base.focus,
- pointer->x, pointer->y,
- &sx, &sy);
-
- wl_data_device_send_motion(drag->base.focus_resource, time, sx, sy);
- }
-}
-
-static void
-data_device_end_drag_grab(struct weston_drag *drag,
- struct weston_seat *seat)
-{
- if (drag->icon) {
- if (weston_view_is_mapped(drag->icon))
- weston_view_unmap(drag->icon);
-
- drag->icon->surface->configure = NULL;
- weston_surface_set_label_func(drag->icon->surface, NULL);
- pixman_region32_clear(&drag->icon->surface->pending.input);
- wl_list_remove(&drag->icon_destroy_listener.link);
- weston_view_destroy(drag->icon);
- }
-
- weston_drag_set_focus(drag, seat, NULL, 0, 0);
-}
-
-static void
-data_device_end_pointer_drag_grab(struct weston_pointer_drag *drag)
-{
- struct weston_pointer *pointer = drag->grab.pointer;
- struct weston_keyboard *keyboard = drag->base.keyboard_grab.keyboard;
-
- data_device_end_drag_grab(&drag->base, pointer->seat);
- weston_pointer_end_grab(pointer);
- weston_keyboard_end_grab(keyboard);
- free(drag);
-}
-
-static void
-drag_grab_button(struct weston_pointer_grab *grab,
- uint32_t time, uint32_t button, uint32_t state_w)
-{
- struct weston_pointer_drag *drag =
- container_of(grab, struct weston_pointer_drag, grab);
- struct weston_pointer *pointer = drag->grab.pointer;
- enum wl_pointer_button_state state = state_w;
- struct weston_data_source *data_source = drag->base.data_source;
-
- if (data_source &&
- pointer->grab_button == button &&
- state == WL_POINTER_BUTTON_STATE_RELEASED) {
- if (drag->base.focus_resource &&
- data_source->accepted &&
- data_source->current_dnd_action) {
- wl_data_device_send_drop(drag->base.focus_resource);
-
- if (wl_resource_get_version(data_source->resource) >=
- WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION)
- wl_data_source_send_dnd_drop_performed(data_source->resource);
-
- data_source->offer->in_ask =
- data_source->current_dnd_action ==
- WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
-
- data_source->seat = NULL;
- } else if (wl_resource_get_version(data_source->resource) >=
- WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) {
- wl_data_source_send_cancelled(data_source->resource);
- }
- }
-
- if (pointer->button_count == 0 &&
- state == WL_POINTER_BUTTON_STATE_RELEASED) {
- if (drag->base.data_source)
- wl_list_remove(&drag->base.data_source_listener.link);
- data_device_end_pointer_drag_grab(drag);
- }
-}
-
-static void
-drag_grab_axis(struct weston_pointer_grab *grab,
- uint32_t time, struct weston_pointer_axis_event *event)
-{
-}
-
-static void
-drag_grab_axis_source(struct weston_pointer_grab *grab, uint32_t source)
-{
-}
-
-static void
-drag_grab_frame(struct weston_pointer_grab *grab)
-{
-}
-
-static void
-drag_grab_cancel(struct weston_pointer_grab *grab)
-{
- struct weston_pointer_drag *drag =
- container_of(grab, struct weston_pointer_drag, grab);
-
- if (drag->base.data_source)
- wl_list_remove(&drag->base.data_source_listener.link);
-
- data_device_end_pointer_drag_grab(drag);
-}
-
-static const struct weston_pointer_grab_interface pointer_drag_grab_interface = {
- drag_grab_focus,
- drag_grab_motion,
- drag_grab_button,
- drag_grab_axis,
- drag_grab_axis_source,
- drag_grab_frame,
- drag_grab_cancel,
-};
-
-static void
-drag_grab_touch_down(struct weston_touch_grab *grab, uint32_t time,
- int touch_id, wl_fixed_t sx, wl_fixed_t sy)
-{
-}
-
-static void
-data_device_end_touch_drag_grab(struct weston_touch_drag *drag)
-{
- struct weston_touch *touch = drag->grab.touch;
- struct weston_keyboard *keyboard = drag->base.keyboard_grab.keyboard;
-
- data_device_end_drag_grab(&drag->base, touch->seat);
- weston_touch_end_grab(touch);
- weston_keyboard_end_grab(keyboard);
- free(drag);
-}
-
-static void
-drag_grab_touch_up(struct weston_touch_grab *grab,
- uint32_t time, int touch_id)
-{
- struct weston_touch_drag *touch_drag =
- container_of(grab, struct weston_touch_drag, grab);
- struct weston_touch *touch = grab->touch;
-
- if (touch_id != touch->grab_touch_id)
- return;
-
- if (touch_drag->base.focus_resource)
- wl_data_device_send_drop(touch_drag->base.focus_resource);
- if (touch_drag->base.data_source)
- wl_list_remove(&touch_drag->base.data_source_listener.link);
- data_device_end_touch_drag_grab(touch_drag);
-}
-
-static void
-drag_grab_touch_focus(struct weston_touch_drag *drag)
-{
- struct weston_touch *touch = drag->grab.touch;
- struct weston_view *view;
- wl_fixed_t view_x, view_y;
-
- view = weston_compositor_pick_view(touch->seat->compositor,
- touch->grab_x, touch->grab_y,
- &view_x, &view_y);
- if (drag->base.focus != view)
- weston_drag_set_focus(&drag->base, touch->seat,
- view, view_x, view_y);
-}
-
-static void
-drag_grab_touch_motion(struct weston_touch_grab *grab, uint32_t time,
- int touch_id, wl_fixed_t x, wl_fixed_t y)
-{
- struct weston_touch_drag *touch_drag =
- container_of(grab, struct weston_touch_drag, grab);
- struct weston_touch *touch = grab->touch;
- wl_fixed_t view_x, view_y;
- float fx, fy;
-
- if (touch_id != touch->grab_touch_id)
- return;
-
- drag_grab_touch_focus(touch_drag);
- if (touch_drag->base.icon) {
- fx = wl_fixed_to_double(touch->grab_x) + touch_drag->base.dx;
- fy = wl_fixed_to_double(touch->grab_y) + touch_drag->base.dy;
- weston_view_set_position(touch_drag->base.icon, fx, fy);
- weston_view_schedule_repaint(touch_drag->base.icon);
- }
-
- if (touch_drag->base.focus_resource) {
- weston_view_from_global_fixed(touch_drag->base.focus,
- touch->grab_x, touch->grab_y,
- &view_x, &view_y);
- wl_data_device_send_motion(touch_drag->base.focus_resource, time,
- view_x, view_y);
- }
-}
-
-static void
-drag_grab_touch_frame(struct weston_touch_grab *grab)
-{
-}
-
-static void
-drag_grab_touch_cancel(struct weston_touch_grab *grab)
-{
- struct weston_touch_drag *touch_drag =
- container_of(grab, struct weston_touch_drag, grab);
-
- if (touch_drag->base.data_source)
- wl_list_remove(&touch_drag->base.data_source_listener.link);
- data_device_end_touch_drag_grab(touch_drag);
-}
-
-static const struct weston_touch_grab_interface touch_drag_grab_interface = {
- drag_grab_touch_down,
- drag_grab_touch_up,
- drag_grab_touch_motion,
- drag_grab_touch_frame,
- drag_grab_touch_cancel
-};
-
-static void
-drag_grab_keyboard_key(struct weston_keyboard_grab *grab,
- uint32_t time, uint32_t key, uint32_t state)
-{
-}
-
-static void
-drag_grab_keyboard_modifiers(struct weston_keyboard_grab *grab,
- uint32_t serial, uint32_t mods_depressed,
- uint32_t mods_latched,
- uint32_t mods_locked, uint32_t group)
-{
- struct weston_keyboard *keyboard = grab->keyboard;
- struct weston_drag *drag =
- container_of(grab, struct weston_drag, keyboard_grab);
- uint32_t compositor_action;
-
- if (mods_depressed & (1 << keyboard->xkb_info->shift_mod))
- compositor_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
- else if (mods_depressed & (1 << keyboard->xkb_info->ctrl_mod))
- compositor_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
- else
- compositor_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
-
- drag->data_source->compositor_action = compositor_action;
-
- if (drag->data_source->offer)
- data_offer_update_action(drag->data_source->offer);
-}
-
-static void
-drag_grab_keyboard_cancel(struct weston_keyboard_grab *grab)
-{
- struct weston_drag *drag =
- container_of(grab, struct weston_drag, keyboard_grab);
- struct weston_pointer *pointer = grab->keyboard->seat->pointer_state;
- struct weston_touch *touch = grab->keyboard->seat->touch_state;
-
- if (pointer && pointer->grab->interface == &pointer_drag_grab_interface) {
- struct weston_touch_drag *touch_drag =
- (struct weston_touch_drag *) drag;
- drag_grab_touch_cancel(&touch_drag->grab);
- } else if (touch && touch->grab->interface == &touch_drag_grab_interface) {
- struct weston_pointer_drag *pointer_drag =
- (struct weston_pointer_drag *) drag;
- drag_grab_cancel(&pointer_drag->grab);
- }
-}
-
-static const struct weston_keyboard_grab_interface keyboard_drag_grab_interface = {
- drag_grab_keyboard_key,
- drag_grab_keyboard_modifiers,
- drag_grab_keyboard_cancel
-};
-
-static void
-destroy_pointer_data_device_source(struct wl_listener *listener, void *data)
-{
- struct weston_pointer_drag *drag = container_of(listener,
- struct weston_pointer_drag, base.data_source_listener);
-
- data_device_end_pointer_drag_grab(drag);
-}
-
-static void
-handle_drag_icon_destroy(struct wl_listener *listener, void *data)
-{
- struct weston_drag *drag = container_of(listener, struct weston_drag,
- icon_destroy_listener);
-
- drag->icon = NULL;
-}
-
-WL_EXPORT int
-weston_pointer_start_drag(struct weston_pointer *pointer,
- struct weston_data_source *source,
- struct weston_surface *icon,
- struct wl_client *client)
-{
- struct weston_pointer_drag *drag;
- struct weston_keyboard *keyboard =
- weston_seat_get_keyboard(pointer->seat);
-
- drag = zalloc(sizeof *drag);
- if (drag == NULL)
- return -1;
-
- drag->grab.interface = &pointer_drag_grab_interface;
- drag->base.keyboard_grab.interface = &keyboard_drag_grab_interface;
- drag->base.client = client;
- drag->base.data_source = source;
-
- if (icon) {
- drag->base.icon = weston_view_create(icon);
- if (drag->base.icon == NULL) {
- free(drag);
- return -1;
- }
-
- drag->base.icon_destroy_listener.notify = handle_drag_icon_destroy;
- wl_signal_add(&icon->destroy_signal,
- &drag->base.icon_destroy_listener);
-
- icon->configure = pointer_drag_surface_configure;
- icon->configure_private = drag;
- weston_surface_set_label_func(icon,
- pointer_drag_surface_get_label);
- } else {
- drag->base.icon = NULL;
- }
-
- if (source) {
- drag->base.data_source_listener.notify = destroy_pointer_data_device_source;
- wl_signal_add(&source->destroy_signal,
- &drag->base.data_source_listener);
- }
-
- weston_pointer_clear_focus(pointer);
- weston_keyboard_set_focus(keyboard, NULL);
-
- weston_pointer_start_grab(pointer, &drag->grab);
- weston_keyboard_start_grab(keyboard, &drag->base.keyboard_grab);
-
- return 0;
-}
-
-static void
-destroy_touch_data_device_source(struct wl_listener *listener, void *data)
-{
- struct weston_touch_drag *drag = container_of(listener,
- struct weston_touch_drag, base.data_source_listener);
-
- data_device_end_touch_drag_grab(drag);
-}
-
-WL_EXPORT int
-weston_touch_start_drag(struct weston_touch *touch,
- struct weston_data_source *source,
- struct weston_surface *icon,
- struct wl_client *client)
-{
- struct weston_touch_drag *drag;
- struct weston_keyboard *keyboard =
- weston_seat_get_keyboard(touch->seat);
-
- drag = zalloc(sizeof *drag);
- if (drag == NULL)
- return -1;
-
- drag->grab.interface = &touch_drag_grab_interface;
- drag->base.client = client;
- drag->base.data_source = source;
-
- if (icon) {
- drag->base.icon = weston_view_create(icon);
- if (drag->base.icon == NULL) {
- free(drag);
- return -1;
- }
-
- drag->base.icon_destroy_listener.notify = handle_drag_icon_destroy;
- wl_signal_add(&icon->destroy_signal,
- &drag->base.icon_destroy_listener);
-
- icon->configure = touch_drag_surface_configure;
- icon->configure_private = drag;
- weston_surface_set_label_func(icon,
- touch_drag_surface_get_label);
- } else {
- drag->base.icon = NULL;
- }
-
- if (source) {
- drag->base.data_source_listener.notify = destroy_touch_data_device_source;
- wl_signal_add(&source->destroy_signal,
- &drag->base.data_source_listener);
- }
-
- weston_keyboard_set_focus(keyboard, NULL);
-
- weston_touch_start_grab(touch, &drag->grab);
- weston_keyboard_start_grab(keyboard, &drag->base.keyboard_grab);
-
- drag_grab_touch_focus(drag);
-
- return 0;
-}
-
-static void
-data_device_start_drag(struct wl_client *client, struct wl_resource *resource,
- struct wl_resource *source_resource,
- struct wl_resource *origin_resource,
- struct wl_resource *icon_resource, uint32_t serial)
-{
- struct weston_seat *seat = wl_resource_get_user_data(resource);
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
- struct weston_touch *touch = weston_seat_get_touch(seat);
- struct weston_surface *origin = wl_resource_get_user_data(origin_resource);
- struct weston_data_source *source = NULL;
- struct weston_surface *icon = NULL;
- int is_pointer_grab, is_touch_grab;
- int32_t ret = 0;
-
- is_pointer_grab = pointer &&
- pointer->button_count == 1 &&
- pointer->grab_serial == serial &&
- pointer->focus &&
- pointer->focus->surface == origin;
-
- is_touch_grab = touch &&
- touch->num_tp == 1 &&
- touch->grab_serial == serial &&
- touch->focus &&
- touch->focus->surface == origin;
-
- if (!is_pointer_grab && !is_touch_grab)
- return;
-
- /* FIXME: Check that the data source type array isn't empty. */
-
- if (source_resource)
- source = wl_resource_get_user_data(source_resource);
- if (icon_resource)
- icon = wl_resource_get_user_data(icon_resource);
-
- if (icon) {
- if (weston_surface_set_role(icon, "wl_data_device-icon",
- resource,
- WL_DATA_DEVICE_ERROR_ROLE) < 0)
- return;
- }
-
- if (is_pointer_grab)
- ret = weston_pointer_start_drag(pointer, source, icon, client);
- else if (is_touch_grab)
- ret = weston_touch_start_drag(touch, source, icon, client);
-
- if (ret < 0)
- wl_resource_post_no_memory(resource);
- else
- source->seat = seat;
-}
-
-static void
-destroy_selection_data_source(struct wl_listener *listener, void *data)
-{
- struct weston_seat *seat = container_of(listener, struct weston_seat,
- selection_data_source_listener);
- struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
- struct wl_resource *data_device;
- struct weston_surface *focus = NULL;
-
- seat->selection_data_source = NULL;
-
- if (keyboard)
- focus = keyboard->focus;
- if (focus && focus->resource) {
- data_device = wl_resource_find_for_client(&seat->drag_resource_list,
- wl_resource_get_client(focus->resource));
- if (data_device)
- wl_data_device_send_selection(data_device, NULL);
- }
-
- wl_signal_emit(&seat->selection_signal, seat);
-}
-
-/** \brief Send the selection to the specified client
- *
- * This function creates a new wl_data_offer if there is a wl_data_source
- * currently set as the selection and sends it to the specified client,
- * followed by the wl_data_device.selection() event.
- * If there is no current selection the wl_data_device.selection() event
- * will carry a NULL wl_data_offer.
- *
- * If the client does not have a wl_data_device for the specified seat
- * nothing will be done.
- *
- * \param seat The seat owning the wl_data_device used to send the events.
- * \param client The client to which to send the selection.
- */
-WL_EXPORT void
-weston_seat_send_selection(struct weston_seat *seat, struct wl_client *client)
-{
- struct weston_data_offer *offer;
- struct wl_resource *data_device;
-
- wl_resource_for_each(data_device, &seat->drag_resource_list) {
- if (wl_resource_get_client(data_device) != client)
- continue;
-
- if (seat->selection_data_source) {
- offer = weston_data_source_send_offer(seat->selection_data_source,
- data_device);
- wl_data_device_send_selection(data_device, offer->resource);
- } else {
- wl_data_device_send_selection(data_device, NULL);
- }
- }
-}
-
-WL_EXPORT void
-weston_seat_set_selection(struct weston_seat *seat,
- struct weston_data_source *source, uint32_t serial)
-{
- struct weston_surface *focus = NULL;
- struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
-
- if (seat->selection_data_source &&
- seat->selection_serial - serial < UINT32_MAX / 2)
- return;
-
- if (seat->selection_data_source) {
- seat->selection_data_source->cancel(seat->selection_data_source);
- wl_list_remove(&seat->selection_data_source_listener.link);
- seat->selection_data_source = NULL;
- }
-
- seat->selection_data_source = source;
- seat->selection_serial = serial;
-
- if (keyboard)
- focus = keyboard->focus;
- if (focus && focus->resource) {
- weston_seat_send_selection(seat, wl_resource_get_client(focus->resource));
- }
-
- wl_signal_emit(&seat->selection_signal, seat);
-
- if (source) {
- seat->selection_data_source_listener.notify =
- destroy_selection_data_source;
- wl_signal_add(&source->destroy_signal,
- &seat->selection_data_source_listener);
- }
-}
-
-static void
-data_device_set_selection(struct wl_client *client,
- struct wl_resource *resource,
- struct wl_resource *source_resource, uint32_t serial)
-{
- struct weston_data_source *source;
-
- if (!source_resource)
- return;
-
- source = wl_resource_get_user_data(source_resource);
-
- if (source->actions_set) {
- wl_resource_post_error(source_resource,
- WL_DATA_SOURCE_ERROR_INVALID_SOURCE,
- "cannot set drag-and-drop source as selection");
- return;
- }
-
- /* FIXME: Store serial and check against incoming serial here. */
- weston_seat_set_selection(wl_resource_get_user_data(resource),
- source, serial);
-}
-static void
-data_device_release(struct wl_client *client, struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static const struct wl_data_device_interface data_device_interface = {
- data_device_start_drag,
- data_device_set_selection,
- data_device_release
-};
-
-static void
-destroy_data_source(struct wl_resource *resource)
-{
- struct weston_data_source *source =
- wl_resource_get_user_data(resource);
- char **p;
-
- wl_signal_emit(&source->destroy_signal, source);
-
- wl_array_for_each(p, &source->mime_types)
- free(*p);
-
- wl_array_release(&source->mime_types);
-
- free(source);
-}
-
-static void
-client_source_accept(struct weston_data_source *source,
- uint32_t time, const char *mime_type)
-{
- wl_data_source_send_target(source->resource, mime_type);
-}
-
-static void
-client_source_send(struct weston_data_source *source,
- const char *mime_type, int32_t fd)
-{
- wl_data_source_send_send(source->resource, mime_type, fd);
- close(fd);
-}
-
-static void
-client_source_cancel(struct weston_data_source *source)
-{
- wl_data_source_send_cancelled(source->resource);
-}
-
-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 =
- wl_resource_create(client, &wl_data_source_interface,
- wl_resource_get_version(resource), id);
- if (source->resource == NULL) {
- free(source);
- wl_resource_post_no_memory(resource);
- return;
- }
-
- wl_signal_init(&source->destroy_signal);
- source->accept = client_source_accept;
- source->send = client_source_send;
- source->cancel = client_source_cancel;
- source->offer = NULL;
- source->accepted = false;
- source->seat = NULL;
- source->actions_set = false;
- source->dnd_actions = 0;
- source->current_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
- source->compositor_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
-
- wl_array_init(&source->mime_types);
-
- wl_resource_set_implementation(source->resource, &data_source_interface,
- source, destroy_data_source);
-}
-
-static void unbind_data_device(struct wl_resource *resource)
-{
- wl_list_remove(wl_resource_get_link(resource));
-}
-
-static void
-get_data_device(struct wl_client *client,
- struct wl_resource *manager_resource,
- uint32_t id, struct wl_resource *seat_resource)
-{
- struct weston_seat *seat = wl_resource_get_user_data(seat_resource);
- struct wl_resource *resource;
-
- resource = wl_resource_create(client,
- &wl_data_device_interface,
- wl_resource_get_version(manager_resource),
- id);
- if (resource == NULL) {
- wl_resource_post_no_memory(manager_resource);
- return;
- }
-
- wl_list_insert(&seat->drag_resource_list,
- wl_resource_get_link(resource));
- wl_resource_set_implementation(resource, &data_device_interface,
- seat, 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)
-{
- struct wl_resource *resource;
-
- resource = wl_resource_create(client,
- &wl_data_device_manager_interface,
- version, id);
- if (resource == NULL) {
- wl_client_post_no_memory(client);
- return;
- }
-
- wl_resource_set_implementation(resource, &manager_interface,
- NULL, NULL);
-}
-
-WL_EXPORT void
-wl_data_device_set_keyboard_focus(struct weston_seat *seat)
-{
- struct weston_surface *focus;
- struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
-
- if (!keyboard)
- return;
-
- focus = keyboard->focus;
- if (!focus || !focus->resource)
- return;
-
- weston_seat_send_selection(seat, wl_resource_get_client(focus->resource));
-}
-
-WL_EXPORT int
-wl_data_device_manager_init(struct wl_display *display)
-{
- if (wl_global_create(display,
- &wl_data_device_manager_interface, 3,
- NULL, bind_manager) == NULL)
- return -1;
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright © 2013 David Herrmann <dh.herrmann@gmail.com>
- *
- * 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.
- */
-
-/*
- * DBus Helpers
- * This file contains the dbus mainloop integration and several helpers to
- * make lowlevel libdbus access easier.
- */
-
-#include "config.h"
-
-#include <dbus/dbus.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <sys/eventfd.h>
-#include <sys/timerfd.h>
-#include <unistd.h>
-#include <wayland-server.h>
-
-#include "compositor.h"
-#include "dbus.h"
-
-/*
- * DBus Mainloop Integration
- * weston_dbus_bind() and weston_dbus_unbind() allow to bind an existing
- * DBusConnection to an existing wl_event_loop object. All dbus dispatching
- * is then nicely integrated into the wayland event loop.
- * Note that this only provides basic watch and timeout dispatching. No
- * remote thread wakeup, signal handling or other dbus insanity is supported.
- * This is fine as long as you don't use any of the deprecated libdbus
- * interfaces (like waking up remote threads..). There is really no rational
- * reason to support these.
- */
-
-static int weston_dbus_dispatch_watch(int fd, uint32_t mask, void *data)
-{
- DBusWatch *watch = data;
- uint32_t flags = 0;
-
- if (dbus_watch_get_enabled(watch)) {
- if (mask & WL_EVENT_READABLE)
- flags |= DBUS_WATCH_READABLE;
- if (mask & WL_EVENT_WRITABLE)
- flags |= DBUS_WATCH_WRITABLE;
- if (mask & WL_EVENT_HANGUP)
- flags |= DBUS_WATCH_HANGUP;
- if (mask & WL_EVENT_ERROR)
- flags |= DBUS_WATCH_ERROR;
-
- dbus_watch_handle(watch, flags);
- }
-
- return 0;
-}
-
-static dbus_bool_t weston_dbus_add_watch(DBusWatch *watch, void *data)
-{
- struct wl_event_loop *loop = data;
- struct wl_event_source *s;
- int fd;
- uint32_t mask = 0, flags;
-
- if (dbus_watch_get_enabled(watch)) {
- flags = dbus_watch_get_flags(watch);
- if (flags & DBUS_WATCH_READABLE)
- mask |= WL_EVENT_READABLE;
- if (flags & DBUS_WATCH_WRITABLE)
- mask |= WL_EVENT_WRITABLE;
- }
-
- fd = dbus_watch_get_unix_fd(watch);
- s = wl_event_loop_add_fd(loop, fd, mask, weston_dbus_dispatch_watch,
- watch);
- if (!s)
- return FALSE;
-
- dbus_watch_set_data(watch, s, NULL);
- return TRUE;
-}
-
-static void weston_dbus_remove_watch(DBusWatch *watch, void *data)
-{
- struct wl_event_source *s;
-
- s = dbus_watch_get_data(watch);
- if (!s)
- return;
-
- wl_event_source_remove(s);
-}
-
-static void weston_dbus_toggle_watch(DBusWatch *watch, void *data)
-{
- struct wl_event_source *s;
- uint32_t mask = 0, flags;
-
- s = dbus_watch_get_data(watch);
- if (!s)
- return;
-
- if (dbus_watch_get_enabled(watch)) {
- flags = dbus_watch_get_flags(watch);
- if (flags & DBUS_WATCH_READABLE)
- mask |= WL_EVENT_READABLE;
- if (flags & DBUS_WATCH_WRITABLE)
- mask |= WL_EVENT_WRITABLE;
- }
-
- wl_event_source_fd_update(s, mask);
-}
-
-static int weston_dbus_dispatch_timeout(void *data)
-{
- DBusTimeout *timeout = data;
-
- if (dbus_timeout_get_enabled(timeout))
- dbus_timeout_handle(timeout);
-
- return 0;
-}
-
-static int weston_dbus_adjust_timeout(DBusTimeout *timeout,
- struct wl_event_source *s)
-{
- int64_t t = 0;
-
- if (dbus_timeout_get_enabled(timeout))
- t = dbus_timeout_get_interval(timeout);
-
- return wl_event_source_timer_update(s, t);
-}
-
-static dbus_bool_t weston_dbus_add_timeout(DBusTimeout *timeout, void *data)
-{
- struct wl_event_loop *loop = data;
- struct wl_event_source *s;
- int r;
-
- s = wl_event_loop_add_timer(loop, weston_dbus_dispatch_timeout,
- timeout);
- if (!s)
- return FALSE;
-
- r = weston_dbus_adjust_timeout(timeout, s);
- if (r < 0) {
- wl_event_source_remove(s);
- return FALSE;
- }
-
- dbus_timeout_set_data(timeout, s, NULL);
- return TRUE;
-}
-
-static void weston_dbus_remove_timeout(DBusTimeout *timeout, void *data)
-{
- struct wl_event_source *s;
-
- s = dbus_timeout_get_data(timeout);
- if (!s)
- return;
-
- wl_event_source_remove(s);
-}
-
-static void weston_dbus_toggle_timeout(DBusTimeout *timeout, void *data)
-{
- struct wl_event_source *s;
-
- s = dbus_timeout_get_data(timeout);
- if (!s)
- return;
-
- weston_dbus_adjust_timeout(timeout, s);
-}
-
-static int weston_dbus_dispatch(int fd, uint32_t mask, void *data)
-{
- DBusConnection *c = data;
- int r;
-
- do {
- r = dbus_connection_dispatch(c);
- if (r == DBUS_DISPATCH_COMPLETE)
- r = 0;
- else if (r == DBUS_DISPATCH_DATA_REMAINS)
- r = -EAGAIN;
- else if (r == DBUS_DISPATCH_NEED_MEMORY)
- r = -ENOMEM;
- else
- r = -EIO;
- } while (r == -EAGAIN);
-
- if (r)
- weston_log("cannot dispatch dbus events: %d\n", r);
-
- return 0;
-}
-
-static int weston_dbus_bind(struct wl_event_loop *loop, DBusConnection *c,
- struct wl_event_source **ctx_out)
-{
- bool b;
- int r, fd;
-
- /* Idle events cannot reschedule themselves, therefore we use a dummy
- * event-fd and mark it for post-dispatch. Hence, the dbus
- * dispatcher is called after every dispatch-round.
- * This is required as dbus doesn't allow dispatching events from
- * within its own event sources. */
- fd = eventfd(0, EFD_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- *ctx_out = wl_event_loop_add_fd(loop, fd, 0, weston_dbus_dispatch, c);
- close(fd);
-
- if (!*ctx_out)
- return -ENOMEM;
-
- wl_event_source_check(*ctx_out);
-
- b = dbus_connection_set_watch_functions(c,
- weston_dbus_add_watch,
- weston_dbus_remove_watch,
- weston_dbus_toggle_watch,
- loop,
- NULL);
- if (!b) {
- r = -ENOMEM;
- goto error;
- }
-
- b = dbus_connection_set_timeout_functions(c,
- weston_dbus_add_timeout,
- weston_dbus_remove_timeout,
- weston_dbus_toggle_timeout,
- loop,
- NULL);
- if (!b) {
- r = -ENOMEM;
- goto error;
- }
-
- dbus_connection_ref(c);
- return 0;
-
-error:
- dbus_connection_set_timeout_functions(c, NULL, NULL, NULL,
- NULL, NULL);
- dbus_connection_set_watch_functions(c, NULL, NULL, NULL,
- NULL, NULL);
- wl_event_source_remove(*ctx_out);
- *ctx_out = NULL;
- return r;
-}
-
-static void weston_dbus_unbind(DBusConnection *c, struct wl_event_source *ctx)
-{
- dbus_connection_set_timeout_functions(c, NULL, NULL, NULL,
- NULL, NULL);
- dbus_connection_set_watch_functions(c, NULL, NULL, NULL,
- NULL, NULL);
- dbus_connection_unref(c);
- wl_event_source_remove(ctx);
-}
-
-/*
- * Convenience Helpers
- * Several convenience helpers are provided to make using dbus in weston
- * easier. We don't use any of the gdbus or qdbus helpers as they pull in
- * huge dependencies and actually are quite awful to use. Instead, we only
- * use the basic low-level libdbus library.
- */
-
-int weston_dbus_open(struct wl_event_loop *loop, DBusBusType bus,
- DBusConnection **out, struct wl_event_source **ctx_out)
-{
- DBusConnection *c;
- int r;
-
- /* Ihhh, global state.. stupid dbus. */
- dbus_connection_set_change_sigpipe(FALSE);
-
- /* This is actually synchronous. It blocks for some authentication and
- * setup. We just trust the dbus-server here and accept this blocking
- * call. There is no real reason to complicate things further and make
- * this asynchronous/non-blocking. A context should be created during
- * thead/process/app setup, so blocking calls should be fine. */
- c = dbus_bus_get_private(bus, NULL);
- if (!c)
- return -EIO;
-
- dbus_connection_set_exit_on_disconnect(c, FALSE);
-
- r = weston_dbus_bind(loop, c, ctx_out);
- if (r < 0)
- goto error;
-
- *out = c;
- return r;
-
-error:
- dbus_connection_close(c);
- dbus_connection_unref(c);
- return r;
-}
-
-void weston_dbus_close(DBusConnection *c, struct wl_event_source *ctx)
-{
- weston_dbus_unbind(c, ctx);
- dbus_connection_close(c);
- dbus_connection_unref(c);
-}
-
-int weston_dbus_add_match(DBusConnection *c, const char *format, ...)
-{
- DBusError err;
- int r;
- va_list list;
- char *str;
-
- va_start(list, format);
- r = vasprintf(&str, format, list);
- va_end(list);
-
- if (r < 0)
- return -ENOMEM;
-
- dbus_error_init(&err);
- dbus_bus_add_match(c, str, &err);
- free(str);
- if (dbus_error_is_set(&err)) {
- dbus_error_free(&err);
- return -EIO;
- }
-
- return 0;
-}
-
-int weston_dbus_add_match_signal(DBusConnection *c, const char *sender,
- const char *iface, const char *member,
- const char *path)
-{
- return weston_dbus_add_match(c,
- "type='signal',"
- "sender='%s',"
- "interface='%s',"
- "member='%s',"
- "path='%s'",
- sender, iface, member, path);
-}
-
-void weston_dbus_remove_match(DBusConnection *c, const char *format, ...)
-{
- int r;
- va_list list;
- char *str;
-
- va_start(list, format);
- r = vasprintf(&str, format, list);
- va_end(list);
-
- if (r < 0)
- return;
-
- dbus_bus_remove_match(c, str, NULL);
- free(str);
-}
-
-void weston_dbus_remove_match_signal(DBusConnection *c, const char *sender,
- const char *iface, const char *member,
- const char *path)
-{
- return weston_dbus_remove_match(c,
- "type='signal',"
- "sender='%s',"
- "interface='%s',"
- "member='%s',"
- "path='%s'",
- sender, iface, member, path);
-}
+++ /dev/null
-/*
- * Copyright © 2013 David Herrmann <dh.herrmann@gmail.com>
- *
- * 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.
- */
-
-#ifndef _WESTON_DBUS_H_
-#define _WESTON_DBUS_H_
-
-#include "config.h"
-
-#include <errno.h>
-#include <wayland-server.h>
-
-#include "compositor.h"
-
-#ifdef HAVE_DBUS
-
-#include <dbus/dbus.h>
-
-/*
- * weston_dbus_open() - Open new dbus connection
- *
- * Opens a new dbus connection to the bus given as @bus. It automatically
- * integrates the new connection into the main-loop @loop. The connection
- * itself is returned in @out.
- * This also returns a context source used for dbus dispatching. It is
- * returned on success in @ctx_out and must be passed to weston_dbus_close()
- * unchanged. You must not access it from outside of a dbus helper!
- *
- * Returns 0 on success, negative error code on failure.
- */
-int weston_dbus_open(struct wl_event_loop *loop, DBusBusType bus,
- DBusConnection **out, struct wl_event_source **ctx_out);
-
-/*
- * weston_dbus_close() - Close dbus connection
- *
- * Closes a dbus connection that was previously opened via weston_dbus_open().
- * It unbinds the connection from the main-loop it was previously bound to,
- * closes the dbus connection and frees all resources. If you want to access
- * @c after this call returns, you must hold a dbus-reference to it. But
- * notice that the connection is closed after this returns so it cannot be
- * used to spawn new dbus requests.
- * You must pass the context source returns by weston_dbus_open() as @ctx.
- */
-void weston_dbus_close(DBusConnection *c, struct wl_event_source *ctx);
-
-/*
- * weston_dbus_add_match() - Add dbus match
- *
- * Configure a dbus-match on the given dbus-connection. This match is saved
- * on the dbus-server as long as the connection is open. See dbus-manual
- * for information. Compared to the dbus_bus_add_match() this allows a
- * var-arg formatted match-string.
- */
-int weston_dbus_add_match(DBusConnection *c, const char *format, ...);
-
-/*
- * weston_dbus_add_match_signal() - Add dbus signal match
- *
- * Same as weston_dbus_add_match() but does the dbus-match formatting for
- * signals internally.
- */
-int weston_dbus_add_match_signal(DBusConnection *c, const char *sender,
- const char *iface, const char *member,
- const char *path);
-
-/*
- * weston_dbus_remove_match() - Remove dbus match
- *
- * Remove a previously configured dbus-match from the dbus server. There is
- * no need to remove dbus-matches if you close the connection, anyway.
- * Compared to dbus_bus_remove_match() this allows a var-arg formatted
- * match string.
- */
-void weston_dbus_remove_match(DBusConnection *c, const char *format, ...);
-
-/*
- * weston_dbus_remove_match_signal() - Remove dbus signal match
- *
- * Same as weston_dbus_remove_match() but does the dbus-match formatting for
- * signals internally.
- */
-void weston_dbus_remove_match_signal(DBusConnection *c, const char *sender,
- const char *iface, const char *member,
- const char *path);
-
-#endif /* HAVE_DBUS */
-
-#endif // _WESTON_DBUS_H_
+++ /dev/null
-/*
- * Copyright © 2012 Intel Corporation
- * Copyright © 2015 Collabora, Ltd.
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <float.h>
-#include <assert.h>
-#include <linux/input.h>
-#include <drm_fourcc.h>
-
-#include "gl-renderer.h"
-#include "vertex-clipping.h"
-#include "linux-dmabuf.h"
-#include "linux-dmabuf-unstable-v1-server-protocol.h"
-
-#include "shared/helpers.h"
-#include "weston-egl-ext.h"
-
-struct gl_shader {
- GLuint program;
- GLuint vertex_shader, fragment_shader;
- GLint proj_uniform;
- GLint tex_uniforms[3];
- GLint alpha_uniform;
- GLint color_uniform;
- const char *vertex_source, *fragment_source;
-};
-
-#define BUFFER_DAMAGE_COUNT 2
-
-enum gl_border_status {
- BORDER_STATUS_CLEAN = 0,
- BORDER_TOP_DIRTY = 1 << GL_RENDERER_BORDER_TOP,
- BORDER_LEFT_DIRTY = 1 << GL_RENDERER_BORDER_LEFT,
- BORDER_RIGHT_DIRTY = 1 << GL_RENDERER_BORDER_RIGHT,
- BORDER_BOTTOM_DIRTY = 1 << GL_RENDERER_BORDER_BOTTOM,
- BORDER_ALL_DIRTY = 0xf,
- BORDER_SIZE_CHANGED = 0x10
-};
-
-struct gl_border_image {
- GLuint tex;
- int32_t width, height;
- int32_t tex_width;
- void *data;
-};
-
-struct gl_output_state {
- EGLSurface egl_surface;
- pixman_region32_t buffer_damage[BUFFER_DAMAGE_COUNT];
- int buffer_damage_index;
- enum gl_border_status border_damage[BUFFER_DAMAGE_COUNT];
- struct gl_border_image borders[4];
- enum gl_border_status border_status;
-
- struct weston_matrix output_matrix;
-};
-
-enum buffer_type {
- BUFFER_TYPE_NULL,
- BUFFER_TYPE_SOLID, /* internal solid color surfaces without a buffer */
- BUFFER_TYPE_SHM,
- BUFFER_TYPE_EGL
-};
-
-struct gl_renderer;
-
-struct egl_image {
- struct gl_renderer *renderer;
- EGLImageKHR image;
- int refcount;
-};
-
-enum import_type {
- IMPORT_TYPE_INVALID,
- IMPORT_TYPE_DIRECT,
- IMPORT_TYPE_GL_CONVERSION
-};
-
-struct dmabuf_image {
- struct linux_dmabuf_buffer *dmabuf;
- int num_images;
- struct egl_image *images[3];
- struct wl_list link;
-
- enum import_type import_type;
- GLenum target;
- struct gl_shader *shader;
-};
-
-struct yuv_plane_descriptor {
- int width_divisor;
- int height_divisor;
- uint32_t format;
- int plane_index;
-};
-
-struct yuv_format_descriptor {
- uint32_t format;
- int input_planes;
- int output_planes;
- int texture_type;
- struct yuv_plane_descriptor plane[4];
-};
-
-struct gl_surface_state {
- GLfloat color[4];
- struct gl_shader *shader;
-
- GLuint textures[3];
- int num_textures;
- bool needs_full_upload;
- pixman_region32_t texture_damage;
-
- /* These are only used by SHM surfaces to detect when we need
- * to do a full upload to specify a new internal texture
- * format */
- GLenum gl_format;
- GLenum gl_pixel_type;
-
- struct egl_image* images[3];
- GLenum target;
- int num_images;
-
- struct weston_buffer_reference buffer_ref;
- enum buffer_type buffer_type;
- int pitch; /* in pixels */
- int height; /* in pixels */
- int y_inverted;
-
- struct weston_surface *surface;
-
- struct wl_listener surface_destroy_listener;
- struct wl_listener renderer_destroy_listener;
-};
-
-struct gl_renderer {
- struct weston_renderer base;
- int fragment_shader_debug;
- int fan_debug;
- struct weston_binding *fragment_binding;
- struct weston_binding *fan_binding;
-
- EGLDisplay egl_display;
- EGLContext egl_context;
- EGLConfig egl_config;
-
- struct wl_array vertices;
- struct wl_array vtxcnt;
-
- PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
- PFNEGLCREATEIMAGEKHRPROC create_image;
- PFNEGLDESTROYIMAGEKHRPROC destroy_image;
-
-#ifdef EGL_EXT_swap_buffers_with_damage
- PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage;
-#endif
-
- PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC create_platform_window;
-
- int has_unpack_subimage;
-
- PFNEGLBINDWAYLANDDISPLAYWL bind_display;
- PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
- PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
- int has_bind_display;
-
- int has_egl_image_external;
-
- int has_egl_buffer_age;
-
- int has_configless_context;
-
- int has_dmabuf_import;
- struct wl_list dmabuf_images;
-
- struct gl_shader texture_shader_rgba;
- struct gl_shader texture_shader_rgbx;
- struct gl_shader texture_shader_egl_external;
- struct gl_shader texture_shader_y_uv;
- struct gl_shader texture_shader_y_u_v;
- struct gl_shader texture_shader_y_xuxv;
- struct gl_shader invert_color_shader;
- struct gl_shader solid_shader;
- struct gl_shader *current_shader;
-
- struct wl_signal destroy_signal;
-};
-
-static PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = NULL;
-
-static inline const char *
-dump_format(uint32_t format, char out[4])
-{
-#if BYTE_ORDER == BIG_ENDIAN
- format = __builtin_bswap32(format);
-#endif
- memcpy(out, &format, 4);
- return out;
-}
-
-static inline struct gl_output_state *
-get_output_state(struct weston_output *output)
-{
- return (struct gl_output_state *)output->renderer_state;
-}
-
-static int
-gl_renderer_create_surface(struct weston_surface *surface);
-
-static inline struct gl_surface_state *
-get_surface_state(struct weston_surface *surface)
-{
- if (!surface->renderer_state)
- gl_renderer_create_surface(surface);
-
- return (struct gl_surface_state *)surface->renderer_state;
-}
-
-static inline struct gl_renderer *
-get_renderer(struct weston_compositor *ec)
-{
- return (struct gl_renderer *)ec->renderer;
-}
-
-static struct egl_image*
-egl_image_create(struct gl_renderer *gr, EGLenum target,
- EGLClientBuffer buffer, const EGLint *attribs)
-{
- struct egl_image *img;
-
- img = zalloc(sizeof *img);
- img->renderer = gr;
- img->refcount = 1;
- img->image = gr->create_image(gr->egl_display, EGL_NO_CONTEXT,
- target, buffer, attribs);
-
- if (img->image == EGL_NO_IMAGE_KHR) {
- free(img);
- return NULL;
- }
-
- return img;
-}
-
-static struct egl_image*
-egl_image_ref(struct egl_image *image)
-{
- image->refcount++;
-
- return image;
-}
-
-static int
-egl_image_unref(struct egl_image *image)
-{
- struct gl_renderer *gr = image->renderer;
-
- assert(image->refcount > 0);
-
- image->refcount--;
- if (image->refcount > 0)
- return image->refcount;
-
- gr->destroy_image(gr->egl_display, image->image);
- free(image);
-
- return 0;
-}
-
-static struct dmabuf_image*
-dmabuf_image_create(void)
-{
- struct dmabuf_image *img;
-
- img = zalloc(sizeof *img);
- wl_list_init(&img->link);
-
- return img;
-}
-
-static void
-dmabuf_image_destroy(struct dmabuf_image *image)
-{
- int i;
-
- for (i = 0; i < image->num_images; ++i)
- egl_image_unref(image->images[i]);
-
- if (image->dmabuf)
- linux_dmabuf_buffer_set_user_data(image->dmabuf, NULL, NULL);
-
- wl_list_remove(&image->link);
-}
-
-static const char *
-egl_error_string(EGLint code)
-{
-#define MYERRCODE(x) case x: return #x;
- switch (code) {
- MYERRCODE(EGL_SUCCESS)
- MYERRCODE(EGL_NOT_INITIALIZED)
- MYERRCODE(EGL_BAD_ACCESS)
- MYERRCODE(EGL_BAD_ALLOC)
- MYERRCODE(EGL_BAD_ATTRIBUTE)
- MYERRCODE(EGL_BAD_CONTEXT)
- MYERRCODE(EGL_BAD_CONFIG)
- MYERRCODE(EGL_BAD_CURRENT_SURFACE)
- MYERRCODE(EGL_BAD_DISPLAY)
- MYERRCODE(EGL_BAD_SURFACE)
- MYERRCODE(EGL_BAD_MATCH)
- MYERRCODE(EGL_BAD_PARAMETER)
- MYERRCODE(EGL_BAD_NATIVE_PIXMAP)
- MYERRCODE(EGL_BAD_NATIVE_WINDOW)
- MYERRCODE(EGL_CONTEXT_LOST)
- default:
- return "unknown";
- }
-#undef MYERRCODE
-}
-
-static void
-gl_renderer_print_egl_error_state(void)
-{
- EGLint code;
-
- code = eglGetError();
- weston_log("EGL error state: %s (0x%04lx)\n",
- egl_error_string(code), (long)code);
-}
-
-#define max(a, b) (((a) > (b)) ? (a) : (b))
-#define min(a, b) (((a) > (b)) ? (b) : (a))
-
-/*
- * Compute the boundary vertices of the intersection of the global coordinate
- * aligned rectangle 'rect', and an arbitrary quadrilateral produced from
- * 'surf_rect' when transformed from surface coordinates into global coordinates.
- * The vertices are written to 'ex' and 'ey', and the return value is the
- * number of vertices. Vertices are produced in clockwise winding order.
- * Guarantees to produce either zero vertices, or 3-8 vertices with non-zero
- * polygon area.
- */
-static int
-calculate_edges(struct weston_view *ev, pixman_box32_t *rect,
- pixman_box32_t *surf_rect, GLfloat *ex, GLfloat *ey)
-{
-
- struct clip_context ctx;
- int i, n;
- GLfloat min_x, max_x, min_y, max_y;
- struct polygon8 surf = {
- { surf_rect->x1, surf_rect->x2, surf_rect->x2, surf_rect->x1 },
- { surf_rect->y1, surf_rect->y1, surf_rect->y2, surf_rect->y2 },
- 4
- };
-
- ctx.clip.x1 = rect->x1;
- ctx.clip.y1 = rect->y1;
- ctx.clip.x2 = rect->x2;
- ctx.clip.y2 = rect->y2;
-
- /* transform surface to screen space: */
- for (i = 0; i < surf.n; i++)
- weston_view_to_global_float(ev, surf.x[i], surf.y[i],
- &surf.x[i], &surf.y[i]);
-
- /* find bounding box: */
- min_x = max_x = surf.x[0];
- min_y = max_y = surf.y[0];
-
- for (i = 1; i < surf.n; i++) {
- min_x = min(min_x, surf.x[i]);
- max_x = max(max_x, surf.x[i]);
- min_y = min(min_y, surf.y[i]);
- max_y = max(max_y, surf.y[i]);
- }
-
- /* First, simple bounding box check to discard early transformed
- * surface rects that do not intersect with the clip region:
- */
- if ((min_x >= ctx.clip.x2) || (max_x <= ctx.clip.x1) ||
- (min_y >= ctx.clip.y2) || (max_y <= ctx.clip.y1))
- return 0;
-
- /* Simple case, bounding box edges are parallel to surface edges,
- * there will be only four edges. We just need to clip the surface
- * vertices to the clip rect bounds:
- */
- if (!ev->transform.enabled)
- return clip_simple(&ctx, &surf, ex, ey);
-
- /* Transformed case: use a general polygon clipping algorithm to
- * clip the surface rectangle with each side of 'rect'.
- * The algorithm is Sutherland-Hodgman, as explained in
- * http://www.codeguru.com/cpp/misc/misc/graphics/article.php/c8965/Polygon-Clipping.htm
- * but without looking at any of that code.
- */
- n = clip_transformed(&ctx, &surf, ex, ey);
-
- if (n < 3)
- return 0;
-
- return n;
-}
-
-static bool
-merge_down(pixman_box32_t *a, pixman_box32_t *b, pixman_box32_t *merge)
-{
- if (a->x1 == b->x1 && a->x2 == b->x2 && a->y1 == b->y2) {
- merge->x1 = a->x1;
- merge->x2 = a->x2;
- merge->y1 = b->y1;
- merge->y2 = a->y2;
- return true;
- }
- return false;
-}
-
-static int
-compress_bands(pixman_box32_t *inrects, int nrects,
- pixman_box32_t **outrects)
-{
- bool merged;
- pixman_box32_t *out, merge_rect;
- int i, j, nout;
-
- if (!nrects) {
- *outrects = NULL;
- return 0;
- }
-
- /* nrects is an upper bound - we're not too worried about
- * allocating a little extra
- */
- out = malloc(sizeof(pixman_box32_t) * nrects);
- out[0] = inrects[0];
- nout = 1;
- for (i = 1; i < nrects; i++) {
- for (j = 0; j < nout; j++) {
- merged = merge_down(&inrects[i], &out[j], &merge_rect);
- if (merged) {
- out[j] = merge_rect;
- break;
- }
- }
- if (!merged) {
- out[nout] = inrects[i];
- nout++;
- }
- }
- *outrects = out;
- return nout;
-}
-
-static int
-texture_region(struct weston_view *ev, pixman_region32_t *region,
- pixman_region32_t *surf_region)
-{
- struct gl_surface_state *gs = get_surface_state(ev->surface);
- struct weston_compositor *ec = ev->surface->compositor;
- struct gl_renderer *gr = get_renderer(ec);
- GLfloat *v, inv_width, inv_height;
- unsigned int *vtxcnt, nvtx = 0;
- pixman_box32_t *rects, *surf_rects;
- pixman_box32_t *raw_rects;
- int i, j, k, nrects, nsurf, raw_nrects;
- bool used_band_compression;
- raw_rects = pixman_region32_rectangles(region, &raw_nrects);
- surf_rects = pixman_region32_rectangles(surf_region, &nsurf);
-
- if (raw_nrects < 4) {
- used_band_compression = false;
- nrects = raw_nrects;
- rects = raw_rects;
- } else {
- nrects = compress_bands(raw_rects, raw_nrects, &rects);
- used_band_compression = true;
- }
- /* worst case we can have 8 vertices per rect (ie. clipped into
- * an octagon):
- */
- v = wl_array_add(&gr->vertices, nrects * nsurf * 8 * 4 * sizeof *v);
- vtxcnt = wl_array_add(&gr->vtxcnt, nrects * nsurf * sizeof *vtxcnt);
-
- inv_width = 1.0 / gs->pitch;
- inv_height = 1.0 / gs->height;
-
- for (i = 0; i < nrects; i++) {
- pixman_box32_t *rect = &rects[i];
- for (j = 0; j < nsurf; j++) {
- pixman_box32_t *surf_rect = &surf_rects[j];
- GLfloat sx, sy, bx, by;
- GLfloat ex[8], ey[8]; /* edge points in screen space */
- int n;
-
- /* The transformed surface, after clipping to the clip region,
- * can have as many as eight sides, emitted as a triangle-fan.
- * The first vertex in the triangle fan can be chosen arbitrarily,
- * since the area is guaranteed to be convex.
- *
- * If a corner of the transformed surface falls outside of the
- * clip region, instead of emitting one vertex for the corner
- * of the surface, up to two are emitted for two corresponding
- * intersection point(s) between the surface and the clip region.
- *
- * To do this, we first calculate the (up to eight) points that
- * form the intersection of the clip rect and the transformed
- * surface.
- */
- n = calculate_edges(ev, rect, surf_rect, ex, ey);
- if (n < 3)
- continue;
-
- /* emit edge points: */
- for (k = 0; k < n; k++) {
- weston_view_from_global_float(ev, ex[k], ey[k],
- &sx, &sy);
- /* position: */
- *(v++) = ex[k];
- *(v++) = ey[k];
- /* texcoord: */
- weston_surface_to_buffer_float(ev->surface,
- sx, sy,
- &bx, &by);
- *(v++) = bx * inv_width;
- if (gs->y_inverted) {
- *(v++) = by * inv_height;
- } else {
- *(v++) = (gs->height - by) * inv_height;
- }
- }
-
- vtxcnt[nvtx++] = n;
- }
- }
-
- if (used_band_compression)
- free(rects);
- return nvtx;
-}
-
-static void
-triangle_fan_debug(struct weston_view *view, int first, int count)
-{
- struct weston_compositor *compositor = view->surface->compositor;
- struct gl_renderer *gr = get_renderer(compositor);
- int i;
- GLushort *buffer;
- GLushort *index;
- int nelems;
- static int color_idx = 0;
- static const GLfloat color[][4] = {
- { 1.0, 0.0, 0.0, 1.0 },
- { 0.0, 1.0, 0.0, 1.0 },
- { 0.0, 0.0, 1.0, 1.0 },
- { 1.0, 1.0, 1.0, 1.0 },
- };
-
- nelems = (count - 1 + count - 2) * 2;
-
- buffer = malloc(sizeof(GLushort) * nelems);
- index = buffer;
-
- for (i = 1; i < count; i++) {
- *index++ = first;
- *index++ = first + i;
- }
-
- for (i = 2; i < count; i++) {
- *index++ = first + i - 1;
- *index++ = first + i;
- }
-
- glUseProgram(gr->solid_shader.program);
- glUniform4fv(gr->solid_shader.color_uniform, 1,
- color[color_idx++ % ARRAY_LENGTH(color)]);
- glDrawElements(GL_LINES, nelems, GL_UNSIGNED_SHORT, buffer);
- glUseProgram(gr->current_shader->program);
- free(buffer);
-}
-
-static void
-repaint_region(struct weston_view *ev, pixman_region32_t *region,
- pixman_region32_t *surf_region)
-{
- struct weston_compositor *ec = ev->surface->compositor;
- struct gl_renderer *gr = get_renderer(ec);
- GLfloat *v;
- unsigned int *vtxcnt;
- int i, first, nfans;
-
- /* The final region to be painted is the intersection of
- * 'region' and 'surf_region'. However, 'region' is in the global
- * coordinates, and 'surf_region' is in the surface-local
- * coordinates. texture_region() will iterate over all pairs of
- * rectangles from both regions, compute the intersection
- * polygon for each pair, and store it as a triangle fan if
- * it has a non-zero area (at least 3 vertices, actually).
- */
- nfans = texture_region(ev, region, surf_region);
-
- v = gr->vertices.data;
- vtxcnt = gr->vtxcnt.data;
-
- /* position: */
- glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[0]);
- glEnableVertexAttribArray(0);
-
- /* texcoord: */
- glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[2]);
- glEnableVertexAttribArray(1);
-
- for (i = 0, first = 0; i < nfans; i++) {
- glDrawArrays(GL_TRIANGLE_FAN, first, vtxcnt[i]);
- if (gr->fan_debug)
- triangle_fan_debug(ev, first, vtxcnt[i]);
- first += vtxcnt[i];
- }
-
- glDisableVertexAttribArray(1);
- glDisableVertexAttribArray(0);
-
- gr->vertices.size = 0;
- gr->vtxcnt.size = 0;
-}
-
-static int
-use_output(struct weston_output *output)
-{
- static int errored;
- struct gl_output_state *go = get_output_state(output);
- struct gl_renderer *gr = get_renderer(output->compositor);
- EGLBoolean ret;
-
- ret = eglMakeCurrent(gr->egl_display, go->egl_surface,
- go->egl_surface, gr->egl_context);
-
- if (ret == EGL_FALSE) {
- if (errored)
- return -1;
- errored = 1;
- weston_log("Failed to make EGL context current.\n");
- gl_renderer_print_egl_error_state();
- return -1;
- }
-
- return 0;
-}
-
-static int
-shader_init(struct gl_shader *shader, struct gl_renderer *gr,
- const char *vertex_source, const char *fragment_source);
-
-static void
-use_shader(struct gl_renderer *gr, struct gl_shader *shader)
-{
- if (!shader->program) {
- int ret;
-
- ret = shader_init(shader, gr,
- shader->vertex_source,
- shader->fragment_source);
-
- if (ret < 0)
- weston_log("warning: failed to compile shader\n");
- }
-
- if (gr->current_shader == shader)
- return;
- glUseProgram(shader->program);
- gr->current_shader = shader;
-}
-
-static void
-shader_uniforms(struct gl_shader *shader,
- struct weston_view *view,
- struct weston_output *output)
-{
- int i;
- struct gl_surface_state *gs = get_surface_state(view->surface);
- struct gl_output_state *go = get_output_state(output);
-
- glUniformMatrix4fv(shader->proj_uniform,
- 1, GL_FALSE, go->output_matrix.d);
- glUniform4fv(shader->color_uniform, 1, gs->color);
- glUniform1f(shader->alpha_uniform, view->alpha);
-
- for (i = 0; i < gs->num_textures; i++)
- glUniform1i(shader->tex_uniforms[i], i);
-}
-
-static void
-draw_view(struct weston_view *ev, struct weston_output *output,
- pixman_region32_t *damage) /* in global coordinates */
-{
- struct weston_compositor *ec = ev->surface->compositor;
- struct gl_renderer *gr = get_renderer(ec);
- struct gl_surface_state *gs = get_surface_state(ev->surface);
- /* repaint bounding region in global coordinates: */
- pixman_region32_t repaint;
- /* opaque region in surface coordinates: */
- pixman_region32_t surface_opaque;
- /* non-opaque region in surface coordinates: */
- pixman_region32_t surface_blend;
- GLint filter;
- int i;
-
- /* In case of a runtime switch of renderers, we may not have received
- * an attach for this surface since the switch. In that case we don't
- * have a valid buffer or a proper shader set up so skip rendering. */
- if (!gs->shader)
- return;
-
- pixman_region32_init(&repaint);
- pixman_region32_intersect(&repaint,
- &ev->transform.boundingbox, damage);
- pixman_region32_subtract(&repaint, &repaint, &ev->clip);
-
- if (!pixman_region32_not_empty(&repaint))
- goto out;
-
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-
- if (gr->fan_debug) {
- use_shader(gr, &gr->solid_shader);
- shader_uniforms(&gr->solid_shader, ev, output);
- }
-
- use_shader(gr, gs->shader);
- shader_uniforms(gs->shader, ev, output);
-
- if (ev->transform.enabled || output->zoom.active ||
- output->current_scale != ev->surface->buffer_viewport.buffer.scale)
- filter = GL_LINEAR;
- else
- filter = GL_NEAREST;
-
- for (i = 0; i < gs->num_textures; i++) {
- glActiveTexture(GL_TEXTURE0 + i);
- glBindTexture(gs->target, gs->textures[i]);
- glTexParameteri(gs->target, GL_TEXTURE_MIN_FILTER, filter);
- glTexParameteri(gs->target, GL_TEXTURE_MAG_FILTER, filter);
- }
-
- /* blended region is whole surface minus opaque region: */
- pixman_region32_init_rect(&surface_blend, 0, 0,
- ev->surface->width, ev->surface->height);
- if (ev->geometry.scissor_enabled)
- pixman_region32_intersect(&surface_blend, &surface_blend,
- &ev->geometry.scissor);
- pixman_region32_subtract(&surface_blend, &surface_blend,
- &ev->surface->opaque);
-
- /* XXX: Should we be using ev->transform.opaque here? */
- pixman_region32_init(&surface_opaque);
- if (ev->geometry.scissor_enabled)
- pixman_region32_intersect(&surface_opaque,
- &ev->surface->opaque,
- &ev->geometry.scissor);
- else
- pixman_region32_copy(&surface_opaque, &ev->surface->opaque);
-
- if (pixman_region32_not_empty(&surface_opaque)) {
- if (gs->shader == &gr->texture_shader_rgba) {
- /* Special case for RGBA textures with possibly
- * bad data in alpha channel: use the shader
- * that forces texture alpha = 1.0.
- * Xwayland surfaces need this.
- */
- use_shader(gr, &gr->texture_shader_rgbx);
- shader_uniforms(&gr->texture_shader_rgbx, ev, output);
- }
-
- if (ev->alpha < 1.0)
- glEnable(GL_BLEND);
- else
- glDisable(GL_BLEND);
-
- repaint_region(ev, &repaint, &surface_opaque);
- }
-
- if (pixman_region32_not_empty(&surface_blend)) {
- use_shader(gr, gs->shader);
- glEnable(GL_BLEND);
- repaint_region(ev, &repaint, &surface_blend);
- }
-
- pixman_region32_fini(&surface_blend);
- pixman_region32_fini(&surface_opaque);
-
-out:
- pixman_region32_fini(&repaint);
-}
-
-static void
-repaint_views(struct weston_output *output, pixman_region32_t *damage)
-{
- struct weston_compositor *compositor = output->compositor;
- struct weston_view *view;
-
- wl_list_for_each_reverse(view, &compositor->view_list, link)
- if (view->plane == &compositor->primary_plane)
- draw_view(view, output, damage);
-}
-
-static void
-draw_output_border_texture(struct gl_output_state *go,
- enum gl_renderer_border_side side,
- int32_t x, int32_t y,
- int32_t width, int32_t height)
-{
- struct gl_border_image *img = &go->borders[side];
- static GLushort indices [] = { 0, 1, 3, 3, 1, 2 };
-
- if (!img->data) {
- if (img->tex) {
- glDeleteTextures(1, &img->tex);
- img->tex = 0;
- }
-
- return;
- }
-
- if (!img->tex) {
- glGenTextures(1, &img->tex);
- glBindTexture(GL_TEXTURE_2D, img->tex);
-
- glTexParameteri(GL_TEXTURE_2D,
- GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D,
- GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D,
- GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D,
- GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- } else {
- glBindTexture(GL_TEXTURE_2D, img->tex);
- }
-
- if (go->border_status & (1 << side)) {
-#ifdef GL_EXT_unpack_subimage
- glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
- glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0);
- glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0);
-#endif
- glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT,
- img->tex_width, img->height, 0,
- GL_BGRA_EXT, GL_UNSIGNED_BYTE, img->data);
- }
-
- GLfloat texcoord[] = {
- 0.0f, 0.0f,
- (GLfloat)img->width / (GLfloat)img->tex_width, 0.0f,
- (GLfloat)img->width / (GLfloat)img->tex_width, 1.0f,
- 0.0f, 1.0f,
- };
-
- GLfloat verts[] = {
- x, y,
- x + width, y,
- x + width, y + height,
- x, y + height
- };
-
- glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts);
- glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texcoord);
- glEnableVertexAttribArray(0);
- glEnableVertexAttribArray(1);
-
- glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
-
- glDisableVertexAttribArray(1);
- glDisableVertexAttribArray(0);
-}
-
-static int
-output_has_borders(struct weston_output *output)
-{
- struct gl_output_state *go = get_output_state(output);
-
- return go->borders[GL_RENDERER_BORDER_TOP].data ||
- go->borders[GL_RENDERER_BORDER_RIGHT].data ||
- go->borders[GL_RENDERER_BORDER_BOTTOM].data ||
- go->borders[GL_RENDERER_BORDER_LEFT].data;
-}
-
-static void
-draw_output_borders(struct weston_output *output,
- enum gl_border_status border_status)
-{
- struct gl_output_state *go = get_output_state(output);
- struct gl_renderer *gr = get_renderer(output->compositor);
- struct gl_shader *shader = &gr->texture_shader_rgba;
- struct gl_border_image *top, *bottom, *left, *right;
- struct weston_matrix matrix;
- int full_width, full_height;
-
- if (border_status == BORDER_STATUS_CLEAN)
- return; /* Clean. Nothing to do. */
-
- top = &go->borders[GL_RENDERER_BORDER_TOP];
- bottom = &go->borders[GL_RENDERER_BORDER_BOTTOM];
- left = &go->borders[GL_RENDERER_BORDER_LEFT];
- right = &go->borders[GL_RENDERER_BORDER_RIGHT];
-
- full_width = output->current_mode->width + left->width + right->width;
- full_height = output->current_mode->height + top->height + bottom->height;
-
- glDisable(GL_BLEND);
- use_shader(gr, shader);
-
- glViewport(0, 0, full_width, full_height);
-
- weston_matrix_init(&matrix);
- weston_matrix_translate(&matrix, -full_width/2.0, -full_height/2.0, 0);
- weston_matrix_scale(&matrix, 2.0/full_width, -2.0/full_height, 1);
- glUniformMatrix4fv(shader->proj_uniform, 1, GL_FALSE, matrix.d);
-
- glUniform1i(shader->tex_uniforms[0], 0);
- glUniform1f(shader->alpha_uniform, 1);
- glActiveTexture(GL_TEXTURE0);
-
- if (border_status & BORDER_TOP_DIRTY)
- draw_output_border_texture(go, GL_RENDERER_BORDER_TOP,
- 0, 0,
- full_width, top->height);
- if (border_status & BORDER_LEFT_DIRTY)
- draw_output_border_texture(go, GL_RENDERER_BORDER_LEFT,
- 0, top->height,
- left->width, output->current_mode->height);
- if (border_status & BORDER_RIGHT_DIRTY)
- draw_output_border_texture(go, GL_RENDERER_BORDER_RIGHT,
- full_width - right->width, top->height,
- right->width, output->current_mode->height);
- if (border_status & BORDER_BOTTOM_DIRTY)
- draw_output_border_texture(go, GL_RENDERER_BORDER_BOTTOM,
- 0, full_height - bottom->height,
- full_width, bottom->height);
-}
-
-static void
-output_get_border_damage(struct weston_output *output,
- enum gl_border_status border_status,
- pixman_region32_t *damage)
-{
- struct gl_output_state *go = get_output_state(output);
- struct gl_border_image *top, *bottom, *left, *right;
- int full_width, full_height;
-
- if (border_status == BORDER_STATUS_CLEAN)
- return; /* Clean. Nothing to do. */
-
- top = &go->borders[GL_RENDERER_BORDER_TOP];
- bottom = &go->borders[GL_RENDERER_BORDER_BOTTOM];
- left = &go->borders[GL_RENDERER_BORDER_LEFT];
- right = &go->borders[GL_RENDERER_BORDER_RIGHT];
-
- full_width = output->current_mode->width + left->width + right->width;
- full_height = output->current_mode->height + top->height + bottom->height;
- if (border_status & BORDER_TOP_DIRTY)
- pixman_region32_union_rect(damage, damage,
- 0, 0,
- full_width, top->height);
- if (border_status & BORDER_LEFT_DIRTY)
- pixman_region32_union_rect(damage, damage,
- 0, top->height,
- left->width, output->current_mode->height);
- if (border_status & BORDER_RIGHT_DIRTY)
- pixman_region32_union_rect(damage, damage,
- full_width - right->width, top->height,
- right->width, output->current_mode->height);
- if (border_status & BORDER_BOTTOM_DIRTY)
- pixman_region32_union_rect(damage, damage,
- 0, full_height - bottom->height,
- full_width, bottom->height);
-}
-
-static void
-output_get_damage(struct weston_output *output,
- pixman_region32_t *buffer_damage, uint32_t *border_damage)
-{
- struct gl_output_state *go = get_output_state(output);
- struct gl_renderer *gr = get_renderer(output->compositor);
- EGLint buffer_age = 0;
- EGLBoolean ret;
- int i;
-
- if (gr->has_egl_buffer_age) {
- ret = eglQuerySurface(gr->egl_display, go->egl_surface,
- EGL_BUFFER_AGE_EXT, &buffer_age);
- if (ret == EGL_FALSE) {
- weston_log("buffer age query failed.\n");
- gl_renderer_print_egl_error_state();
- }
- }
-
- if (buffer_age == 0 || buffer_age - 1 > BUFFER_DAMAGE_COUNT) {
- pixman_region32_copy(buffer_damage, &output->region);
- *border_damage = BORDER_ALL_DIRTY;
- } else {
- for (i = 0; i < buffer_age - 1; i++)
- *border_damage |= go->border_damage[(go->buffer_damage_index + i) % BUFFER_DAMAGE_COUNT];
-
- if (*border_damage & BORDER_SIZE_CHANGED) {
- /* If we've had a resize, we have to do a full
- * repaint. */
- *border_damage |= BORDER_ALL_DIRTY;
- pixman_region32_copy(buffer_damage, &output->region);
- } else {
- for (i = 0; i < buffer_age - 1; i++)
- pixman_region32_union(buffer_damage,
- buffer_damage,
- &go->buffer_damage[(go->buffer_damage_index + i) % BUFFER_DAMAGE_COUNT]);
- }
- }
-}
-
-static void
-output_rotate_damage(struct weston_output *output,
- pixman_region32_t *output_damage,
- enum gl_border_status border_status)
-{
- struct gl_output_state *go = get_output_state(output);
- struct gl_renderer *gr = get_renderer(output->compositor);
-
- if (!gr->has_egl_buffer_age)
- return;
-
- go->buffer_damage_index += BUFFER_DAMAGE_COUNT - 1;
- go->buffer_damage_index %= BUFFER_DAMAGE_COUNT;
-
- pixman_region32_copy(&go->buffer_damage[go->buffer_damage_index], output_damage);
- go->border_damage[go->buffer_damage_index] = border_status;
-}
-
-/* NOTE: We now allow falling back to ARGB gl visuals when XRGB is
- * unavailable, so we're assuming the background has no transparency
- * and that everything with a blend, like drop shadows, will have something
- * opaque (like the background) drawn underneath it.
- *
- * Depending on the underlying hardware, violating that assumption could
- * result in seeing through to another display plane.
- */
-static void
-gl_renderer_repaint_output(struct weston_output *output,
- pixman_region32_t *output_damage)
-{
- struct gl_output_state *go = get_output_state(output);
- struct weston_compositor *compositor = output->compositor;
- struct gl_renderer *gr = get_renderer(compositor);
- EGLBoolean ret;
- static int errored;
-#ifdef EGL_EXT_swap_buffers_with_damage
- int i, nrects, buffer_height;
- EGLint *egl_damage, *d;
- pixman_box32_t *rects;
-#endif
- pixman_region32_t buffer_damage, total_damage;
- enum gl_border_status border_damage = BORDER_STATUS_CLEAN;
-
- if (use_output(output) < 0)
- return;
-
- /* Calculate the viewport */
- glViewport(go->borders[GL_RENDERER_BORDER_LEFT].width,
- go->borders[GL_RENDERER_BORDER_BOTTOM].height,
- output->current_mode->width,
- output->current_mode->height);
-
- /* Calculate the global GL matrix */
- go->output_matrix = output->matrix;
- weston_matrix_translate(&go->output_matrix,
- -(output->current_mode->width / 2.0),
- -(output->current_mode->height / 2.0), 0);
- weston_matrix_scale(&go->output_matrix,
- 2.0 / output->current_mode->width,
- -2.0 / output->current_mode->height, 1);
-
- /* if debugging, redraw everything outside the damage to clean up
- * debug lines from the previous draw on this buffer:
- */
- if (gr->fan_debug) {
- pixman_region32_t undamaged;
- pixman_region32_init(&undamaged);
- pixman_region32_subtract(&undamaged, &output->region,
- output_damage);
- gr->fan_debug = 0;
- repaint_views(output, &undamaged);
- gr->fan_debug = 1;
- pixman_region32_fini(&undamaged);
- }
-
- pixman_region32_init(&total_damage);
- pixman_region32_init(&buffer_damage);
-
- output_get_damage(output, &buffer_damage, &border_damage);
- output_rotate_damage(output, output_damage, go->border_status);
-
- pixman_region32_union(&total_damage, &buffer_damage, output_damage);
- border_damage |= go->border_status;
-
- repaint_views(output, &total_damage);
-
- pixman_region32_fini(&total_damage);
- pixman_region32_fini(&buffer_damage);
-
- draw_output_borders(output, border_damage);
-
- pixman_region32_copy(&output->previous_damage, output_damage);
- wl_signal_emit(&output->frame_signal, output);
-
-#ifdef EGL_EXT_swap_buffers_with_damage
- if (gr->swap_buffers_with_damage) {
- pixman_region32_init(&buffer_damage);
- weston_transformed_region(output->width, output->height,
- output->transform,
- output->current_scale,
- output_damage, &buffer_damage);
-
- if (output_has_borders(output)) {
- pixman_region32_translate(&buffer_damage,
- go->borders[GL_RENDERER_BORDER_LEFT].width,
- go->borders[GL_RENDERER_BORDER_TOP].height);
- output_get_border_damage(output, go->border_status,
- &buffer_damage);
- }
-
- rects = pixman_region32_rectangles(&buffer_damage, &nrects);
- egl_damage = malloc(nrects * 4 * sizeof(EGLint));
-
- buffer_height = go->borders[GL_RENDERER_BORDER_TOP].height +
- output->current_mode->height +
- go->borders[GL_RENDERER_BORDER_BOTTOM].height;
-
- d = egl_damage;
- for (i = 0; i < nrects; ++i) {
- *d++ = rects[i].x1;
- *d++ = buffer_height - rects[i].y2;
- *d++ = rects[i].x2 - rects[i].x1;
- *d++ = rects[i].y2 - rects[i].y1;
- }
- ret = gr->swap_buffers_with_damage(gr->egl_display,
- go->egl_surface,
- egl_damage, nrects);
- free(egl_damage);
- pixman_region32_fini(&buffer_damage);
- } else {
- ret = eglSwapBuffers(gr->egl_display, go->egl_surface);
- }
-#else /* ! defined EGL_EXT_swap_buffers_with_damage */
- ret = eglSwapBuffers(gr->egl_display, go->egl_surface);
-#endif
-
- if (ret == EGL_FALSE && !errored) {
- errored = 1;
- weston_log("Failed in eglSwapBuffers.\n");
- gl_renderer_print_egl_error_state();
- }
-
- go->border_status = BORDER_STATUS_CLEAN;
-}
-
-static int
-gl_renderer_read_pixels(struct weston_output *output,
- pixman_format_code_t format, void *pixels,
- uint32_t x, uint32_t y,
- uint32_t width, uint32_t height)
-{
- GLenum gl_format;
- struct gl_output_state *go = get_output_state(output);
-
- x += go->borders[GL_RENDERER_BORDER_LEFT].width;
- y += go->borders[GL_RENDERER_BORDER_BOTTOM].height;
-
- switch (format) {
- case PIXMAN_a8r8g8b8:
- gl_format = GL_BGRA_EXT;
- break;
- case PIXMAN_a8b8g8r8:
- gl_format = GL_RGBA;
- break;
- default:
- return -1;
- }
-
- if (use_output(output) < 0)
- return -1;
-
- glPixelStorei(GL_PACK_ALIGNMENT, 1);
- glReadPixels(x, y, width, height, gl_format,
- GL_UNSIGNED_BYTE, pixels);
-
- return 0;
-}
-
-static void
-gl_renderer_flush_damage(struct weston_surface *surface)
-{
- struct gl_renderer *gr = get_renderer(surface->compositor);
- struct gl_surface_state *gs = get_surface_state(surface);
- struct weston_buffer *buffer = gs->buffer_ref.buffer;
- struct weston_view *view;
- bool texture_used;
-
-#ifdef GL_EXT_unpack_subimage
- pixman_box32_t *rectangles;
- void *data;
- int i, n;
-#endif
-
- pixman_region32_union(&gs->texture_damage,
- &gs->texture_damage, &surface->damage);
-
- if (!buffer)
- return;
-
- /* Avoid upload, if the texture won't be used this time.
- * We still accumulate the damage in texture_damage, and
- * hold the reference to the buffer, in case the surface
- * migrates back to the primary plane.
- */
- texture_used = false;
- wl_list_for_each(view, &surface->views, surface_link) {
- if (view->plane == &surface->compositor->primary_plane) {
- texture_used = true;
- break;
- }
- }
- if (!texture_used)
- return;
-
- if (!pixman_region32_not_empty(&gs->texture_damage) &&
- !gs->needs_full_upload)
- goto done;
-
- glBindTexture(GL_TEXTURE_2D, gs->textures[0]);
-
- if (!gr->has_unpack_subimage) {
- wl_shm_buffer_begin_access(buffer->shm_buffer);
- glTexImage2D(GL_TEXTURE_2D, 0, gs->gl_format,
- gs->pitch, buffer->height, 0,
- gs->gl_format, gs->gl_pixel_type,
- wl_shm_buffer_get_data(buffer->shm_buffer));
- wl_shm_buffer_end_access(buffer->shm_buffer);
-
- goto done;
- }
-
-#ifdef GL_EXT_unpack_subimage
- glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, gs->pitch);
- data = wl_shm_buffer_get_data(buffer->shm_buffer);
-
- if (gs->needs_full_upload) {
- glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0);
- glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0);
- wl_shm_buffer_begin_access(buffer->shm_buffer);
- glTexImage2D(GL_TEXTURE_2D, 0, gs->gl_format,
- gs->pitch, buffer->height, 0,
- gs->gl_format, gs->gl_pixel_type, data);
- wl_shm_buffer_end_access(buffer->shm_buffer);
- goto done;
- }
-
- rectangles = pixman_region32_rectangles(&gs->texture_damage, &n);
- wl_shm_buffer_begin_access(buffer->shm_buffer);
- for (i = 0; i < n; i++) {
- pixman_box32_t r;
-
- r = weston_surface_to_buffer_rect(surface, rectangles[i]);
-
- glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, r.x1);
- glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, r.y1);
- glTexSubImage2D(GL_TEXTURE_2D, 0, r.x1, r.y1,
- r.x2 - r.x1, r.y2 - r.y1,
- gs->gl_format, gs->gl_pixel_type, data);
- }
- wl_shm_buffer_end_access(buffer->shm_buffer);
-#endif
-
-done:
- pixman_region32_fini(&gs->texture_damage);
- pixman_region32_init(&gs->texture_damage);
- gs->needs_full_upload = false;
-
- weston_buffer_reference(&gs->buffer_ref, NULL);
-}
-
-static void
-ensure_textures(struct gl_surface_state *gs, int num_textures)
-{
- int i;
-
- if (num_textures <= gs->num_textures)
- return;
-
- for (i = gs->num_textures; i < num_textures; i++) {
- glGenTextures(1, &gs->textures[i]);
- glBindTexture(gs->target, gs->textures[i]);
- glTexParameteri(gs->target,
- GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(gs->target,
- GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- }
- gs->num_textures = num_textures;
- glBindTexture(gs->target, 0);
-}
-
-static void
-gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer,
- struct wl_shm_buffer *shm_buffer)
-{
- struct weston_compositor *ec = es->compositor;
- struct gl_renderer *gr = get_renderer(ec);
- struct gl_surface_state *gs = get_surface_state(es);
- GLenum gl_format, gl_pixel_type;
- int pitch;
-
- buffer->shm_buffer = shm_buffer;
- buffer->width = wl_shm_buffer_get_width(shm_buffer);
- buffer->height = wl_shm_buffer_get_height(shm_buffer);
-
- switch (wl_shm_buffer_get_format(shm_buffer)) {
- case WL_SHM_FORMAT_XRGB8888:
- gs->shader = &gr->texture_shader_rgbx;
- pitch = wl_shm_buffer_get_stride(shm_buffer) / 4;
- gl_format = GL_BGRA_EXT;
- gl_pixel_type = GL_UNSIGNED_BYTE;
- break;
- case WL_SHM_FORMAT_ARGB8888:
- gs->shader = &gr->texture_shader_rgba;
- pitch = wl_shm_buffer_get_stride(shm_buffer) / 4;
- gl_format = GL_BGRA_EXT;
- gl_pixel_type = GL_UNSIGNED_BYTE;
- break;
- case WL_SHM_FORMAT_RGB565:
- gs->shader = &gr->texture_shader_rgbx;
- pitch = wl_shm_buffer_get_stride(shm_buffer) / 2;
- gl_format = GL_RGB;
- gl_pixel_type = GL_UNSIGNED_SHORT_5_6_5;
- break;
- default:
- weston_log("warning: unknown shm buffer format: %08x\n",
- wl_shm_buffer_get_format(shm_buffer));
- return;
- }
-
- /* Only allocate a texture if it doesn't match existing one.
- * If a switch from DRM allocated buffer to a SHM buffer is
- * happening, we need to allocate a new texture buffer. */
- if (pitch != gs->pitch ||
- buffer->height != gs->height ||
- gl_format != gs->gl_format ||
- gl_pixel_type != gs->gl_pixel_type ||
- gs->buffer_type != BUFFER_TYPE_SHM) {
- gs->pitch = pitch;
- gs->height = buffer->height;
- gs->target = GL_TEXTURE_2D;
- gs->gl_format = gl_format;
- gs->gl_pixel_type = gl_pixel_type;
- gs->buffer_type = BUFFER_TYPE_SHM;
- gs->needs_full_upload = true;
- gs->y_inverted = 1;
-
- gs->surface = es;
-
- ensure_textures(gs, 1);
- }
-}
-
-static void
-gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer,
- uint32_t format)
-{
- struct weston_compositor *ec = es->compositor;
- struct gl_renderer *gr = get_renderer(ec);
- struct gl_surface_state *gs = get_surface_state(es);
- EGLint attribs[3];
- int i, num_planes;
-
- buffer->legacy_buffer = (struct wl_buffer *)buffer->resource;
- gr->query_buffer(gr->egl_display, buffer->legacy_buffer,
- EGL_WIDTH, &buffer->width);
- gr->query_buffer(gr->egl_display, buffer->legacy_buffer,
- EGL_HEIGHT, &buffer->height);
- gr->query_buffer(gr->egl_display, buffer->legacy_buffer,
- EGL_WAYLAND_Y_INVERTED_WL, &buffer->y_inverted);
-
- for (i = 0; i < gs->num_images; i++) {
- egl_image_unref(gs->images[i]);
- gs->images[i] = NULL;
- }
- gs->num_images = 0;
- gs->target = GL_TEXTURE_2D;
- switch (format) {
- case EGL_TEXTURE_RGB:
- case EGL_TEXTURE_RGBA:
- default:
- num_planes = 1;
- gs->shader = &gr->texture_shader_rgba;
- break;
- case EGL_TEXTURE_EXTERNAL_WL:
- num_planes = 1;
- gs->target = GL_TEXTURE_EXTERNAL_OES;
- gs->shader = &gr->texture_shader_egl_external;
- break;
- case EGL_TEXTURE_Y_UV_WL:
- num_planes = 2;
- gs->shader = &gr->texture_shader_y_uv;
- break;
- case EGL_TEXTURE_Y_U_V_WL:
- num_planes = 3;
- gs->shader = &gr->texture_shader_y_u_v;
- break;
- case EGL_TEXTURE_Y_XUXV_WL:
- num_planes = 2;
- gs->shader = &gr->texture_shader_y_xuxv;
- break;
- }
-
- ensure_textures(gs, num_planes);
- for (i = 0; i < num_planes; i++) {
- attribs[0] = EGL_WAYLAND_PLANE_WL;
- attribs[1] = i;
- attribs[2] = EGL_NONE;
- gs->images[i] = egl_image_create(gr,
- EGL_WAYLAND_BUFFER_WL,
- buffer->legacy_buffer,
- attribs);
- if (!gs->images[i]) {
- weston_log("failed to create img for plane %d\n", i);
- continue;
- }
- gs->num_images++;
-
- glActiveTexture(GL_TEXTURE0 + i);
- glBindTexture(gs->target, gs->textures[i]);
- gr->image_target_texture_2d(gs->target,
- gs->images[i]->image);
- }
-
- gs->pitch = buffer->width;
- gs->height = buffer->height;
- gs->buffer_type = BUFFER_TYPE_EGL;
- gs->y_inverted = buffer->y_inverted;
-}
-
-static void
-gl_renderer_destroy_dmabuf(struct linux_dmabuf_buffer *dmabuf)
-{
- struct dmabuf_image *image = dmabuf->user_data;
-
- dmabuf_image_destroy(image);
-}
-
-static struct egl_image *
-import_simple_dmabuf(struct gl_renderer *gr,
- struct dmabuf_attributes *attributes)
-{
- struct egl_image *image;
- EGLint attribs[30];
- int atti = 0;
-
- /* This requires the Mesa commit in
- * Mesa 10.3 (08264e5dad4df448e7718e782ad9077902089a07) or
- * Mesa 10.2.7 (55d28925e6109a4afd61f109e845a8a51bd17652).
- * Otherwise Mesa closes the fd behind our back and re-importing
- * will fail.
- * https://bugs.freedesktop.org/show_bug.cgi?id=76188
- */
-
- attribs[atti++] = EGL_WIDTH;
- attribs[atti++] = attributes->width;
- attribs[atti++] = EGL_HEIGHT;
- attribs[atti++] = attributes->height;
- attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
- attribs[atti++] = attributes->format;
- /* XXX: Add modifier here when supported */
-
- if (attributes->n_planes > 0) {
- attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT;
- attribs[atti++] = attributes->fd[0];
- attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
- attribs[atti++] = attributes->offset[0];
- attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
- attribs[atti++] = attributes->stride[0];
- }
-
- if (attributes->n_planes > 1) {
- attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT;
- attribs[atti++] = attributes->fd[1];
- attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
- attribs[atti++] = attributes->offset[1];
- attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
- attribs[atti++] = attributes->stride[1];
- }
-
- if (attributes->n_planes > 2) {
- attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT;
- attribs[atti++] = attributes->fd[2];
- attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
- attribs[atti++] = attributes->offset[2];
- attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
- attribs[atti++] = attributes->stride[2];
- }
-
- attribs[atti++] = EGL_NONE;
-
- image = egl_image_create(gr, EGL_LINUX_DMA_BUF_EXT, NULL,
- attribs);
-
- return image;
-}
-
-/* The kernel header drm_fourcc.h defines the DRM formats below. We duplicate
- * some of the definitions here so that building Weston won't require
- * bleeding-edge kernel headers.
- */
-#ifndef DRM_FORMAT_R8
-#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */
-#endif
-
-#ifndef DRM_FORMAT_GR88
-#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
-#endif
-
-struct yuv_format_descriptor yuv_formats[] = {
- {
- .format = DRM_FORMAT_YUYV,
- .input_planes = 1,
- .output_planes = 2,
- .texture_type = EGL_TEXTURE_Y_XUXV_WL,
- {{
- .width_divisor = 1,
- .height_divisor = 1,
- .format = DRM_FORMAT_GR88,
- .plane_index = 0
- }, {
- .width_divisor = 2,
- .height_divisor = 1,
- .format = DRM_FORMAT_ARGB8888,
- .plane_index = 0
- }}
- }, {
- .format = DRM_FORMAT_NV12,
- .input_planes = 2,
- .output_planes = 2,
- .texture_type = EGL_TEXTURE_Y_UV_WL,
- {{
- .width_divisor = 1,
- .height_divisor = 1,
- .format = DRM_FORMAT_R8,
- .plane_index = 0
- }, {
- .width_divisor = 2,
- .height_divisor = 2,
- .format = DRM_FORMAT_GR88,
- .plane_index = 1
- }}
- }, {
- .format = DRM_FORMAT_YUV420,
- .input_planes = 3,
- .output_planes = 3,
- .texture_type = EGL_TEXTURE_Y_U_V_WL,
- {{
- .width_divisor = 1,
- .height_divisor = 1,
- .format = DRM_FORMAT_R8,
- .plane_index = 0
- }, {
- .width_divisor = 2,
- .height_divisor = 2,
- .format = DRM_FORMAT_R8,
- .plane_index = 1
- }, {
- .width_divisor = 2,
- .height_divisor = 2,
- .format = DRM_FORMAT_R8,
- .plane_index = 2
- }}
- }
-};
-
-static struct egl_image *
-import_dmabuf_single_plane(struct gl_renderer *gr,
- const struct dmabuf_attributes *attributes,
- struct yuv_plane_descriptor *descriptor)
-{
- struct dmabuf_attributes plane;
- struct egl_image *image;
- char fmt[4];
-
- plane.width = attributes->width / descriptor->width_divisor;
- plane.height = attributes->height / descriptor->height_divisor;
- plane.format = descriptor->format;
- plane.n_planes = 1;
- plane.fd[0] = attributes->fd[descriptor->plane_index];
- plane.offset[0] = attributes->offset[descriptor->plane_index];
- plane.stride[0] = attributes->stride[descriptor->plane_index];
- plane.modifier[0] = attributes->modifier[descriptor->plane_index];
-
- image = import_simple_dmabuf(gr, &plane);
- if (!image) {
- weston_log("Failed to import plane %d as %.4s\n",
- descriptor->plane_index,
- dump_format(descriptor->format, fmt));
- return NULL;
- }
-
- return image;
-}
-
-static bool
-import_yuv_dmabuf(struct gl_renderer *gr,
- struct dmabuf_image *image)
-{
- unsigned i;
- int j;
- int ret;
- struct yuv_format_descriptor *format = NULL;
- struct dmabuf_attributes *attributes = &image->dmabuf->attributes;
- char fmt[4];
-
- for (i = 0; i < ARRAY_LENGTH(yuv_formats); ++i) {
- if (yuv_formats[i].format == attributes->format) {
- format = &yuv_formats[i];
- break;
- }
- }
-
- if (!format) {
- weston_log("Error during import, and no known conversion for format "
- "%.4s in the renderer",
- dump_format(attributes->format, fmt));
- return false;
- }
-
- if (attributes->n_planes != format->input_planes) {
- weston_log("%.4s dmabuf must contain %d plane%s (%d provided)",
- dump_format(format->format, fmt),
- format->input_planes,
- (format->input_planes > 1) ? "s" : "",
- attributes->n_planes);
- return false;
- }
-
- for (j = 0; j < format->output_planes; ++j) {
- image->images[j] = import_dmabuf_single_plane(gr, attributes,
- &format->plane[j]);
- if (!image->images[j]) {
- while (j) {
- ret = egl_image_unref(image->images[--j]);
- assert(ret == 0);
- }
- return false;
- }
- }
-
- image->num_images = format->output_planes;
-
- switch (format->texture_type) {
- case EGL_TEXTURE_Y_XUXV_WL:
- image->shader = &gr->texture_shader_y_xuxv;
- break;
- case EGL_TEXTURE_Y_UV_WL:
- image->shader = &gr->texture_shader_y_uv;
- break;
- case EGL_TEXTURE_Y_U_V_WL:
- image->shader = &gr->texture_shader_y_u_v;
- break;
- default:
- assert(false);
- }
-
- return true;
-}
-
-static GLenum
-choose_texture_target(struct dmabuf_attributes *attributes)
-{
- if (attributes->n_planes > 1)
- return GL_TEXTURE_EXTERNAL_OES;
-
- switch (attributes->format & ~DRM_FORMAT_BIG_ENDIAN) {
- case DRM_FORMAT_YUYV:
- case DRM_FORMAT_YVYU:
- case DRM_FORMAT_UYVY:
- case DRM_FORMAT_VYUY:
- case DRM_FORMAT_AYUV:
- return GL_TEXTURE_EXTERNAL_OES;
- default:
- return GL_TEXTURE_2D;
- }
-}
-
-static struct dmabuf_image *
-import_dmabuf(struct gl_renderer *gr,
- struct linux_dmabuf_buffer *dmabuf)
-{
- struct egl_image *egl_image;
- struct dmabuf_image *image;
-
- image = dmabuf_image_create();
- image->dmabuf = dmabuf;
-
- egl_image = import_simple_dmabuf(gr, &dmabuf->attributes);
- if (egl_image) {
- image->num_images = 1;
- image->images[0] = egl_image;
- image->import_type = IMPORT_TYPE_DIRECT;
- image->target = choose_texture_target(&dmabuf->attributes);
-
- switch (image->target) {
- case GL_TEXTURE_2D:
- image->shader = &gr->texture_shader_rgba;
- break;
- default:
- image->shader = &gr->texture_shader_egl_external;
- }
- } else {
- if (!import_yuv_dmabuf(gr, image)) {
- dmabuf_image_destroy(image);
- return NULL;
- }
- image->import_type = IMPORT_TYPE_GL_CONVERSION;
- image->target = GL_TEXTURE_2D;
- }
-
- return image;
-}
-
-static bool
-gl_renderer_import_dmabuf(struct weston_compositor *ec,
- struct linux_dmabuf_buffer *dmabuf)
-{
- struct gl_renderer *gr = get_renderer(ec);
- struct dmabuf_image *image;
- int i;
-
- assert(gr->has_dmabuf_import);
-
- for (i = 0; i < dmabuf->attributes.n_planes; i++) {
- /* EGL import does not have modifiers */
- if (dmabuf->attributes.modifier[i] != 0)
- return false;
- }
-
- /* reject all flags we do not recognize or handle */
- if (dmabuf->attributes.flags & ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT)
- return false;
-
- image = import_dmabuf(gr, dmabuf);
- if (!image)
- return false;
-
- wl_list_insert(&gr->dmabuf_images, &image->link);
- linux_dmabuf_buffer_set_user_data(dmabuf, image,
- gl_renderer_destroy_dmabuf);
-
- return true;
-}
-
-static bool
-import_known_dmabuf(struct gl_renderer *gr,
- struct dmabuf_image *image)
-{
- switch (image->import_type) {
- case IMPORT_TYPE_DIRECT:
- image->images[0] = import_simple_dmabuf(gr, &image->dmabuf->attributes);
- if (!image->images[0])
- return false;
- break;
-
- case IMPORT_TYPE_GL_CONVERSION:
- if (!import_yuv_dmabuf(gr, image))
- return false;
- break;
-
- default:
- weston_log("Invalid import type for dmabuf\n");
- return false;
- }
-
- return true;
-}
-
-static void
-gl_renderer_attach_dmabuf(struct weston_surface *surface,
- struct weston_buffer *buffer,
- struct linux_dmabuf_buffer *dmabuf)
-{
- struct gl_renderer *gr = get_renderer(surface->compositor);
- struct gl_surface_state *gs = get_surface_state(surface);
- struct dmabuf_image *image;
- int i;
- int ret;
-
- if (!gr->has_dmabuf_import) {
- linux_dmabuf_buffer_send_server_error(dmabuf,
- "EGL dmabuf import not supported");
- return;
- }
-
- buffer->width = dmabuf->attributes.width;
- buffer->height = dmabuf->attributes.height;
- buffer->y_inverted =
- !!(dmabuf->attributes.flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT);
-
- for (i = 0; i < gs->num_images; i++)
- egl_image_unref(gs->images[i]);
- gs->num_images = 0;
-
- /*
- * We try to always hold an imported EGLImage from the dmabuf
- * to prevent the client from preventing re-imports. But, we also
- * need to re-import every time the contents may change because
- * GL driver's caching may need flushing.
- *
- * Here we release the cache reference which has to be final.
- */
- image = linux_dmabuf_buffer_get_user_data(dmabuf);
-
- /* The dmabuf_image should have been created during the import */
- assert(image != NULL);
-
- for (i = 0; i < image->num_images; ++i) {
- ret = egl_image_unref(image->images[i]);
- assert(ret == 0);
- }
-
- if (!import_known_dmabuf(gr, image)) {
- linux_dmabuf_buffer_send_server_error(dmabuf, "EGL dmabuf import failed");
- return;
- }
-
- gs->num_images = image->num_images;
- for (i = 0; i < gs->num_images; ++i)
- gs->images[i] = egl_image_ref(image->images[i]);
-
- gs->target = image->target;
- ensure_textures(gs, gs->num_images);
- for (i = 0; i < gs->num_images; ++i) {
- glActiveTexture(GL_TEXTURE0 + i);
- glBindTexture(gs->target, gs->textures[i]);
- gr->image_target_texture_2d(gs->target, gs->images[i]->image);
- }
-
- gs->shader = image->shader;
- gs->pitch = buffer->width;
- gs->height = buffer->height;
- gs->buffer_type = BUFFER_TYPE_EGL;
- gs->y_inverted = buffer->y_inverted;
-}
-
-static void
-gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
-{
- struct weston_compositor *ec = es->compositor;
- struct gl_renderer *gr = get_renderer(ec);
- struct gl_surface_state *gs = get_surface_state(es);
- struct wl_shm_buffer *shm_buffer;
- struct linux_dmabuf_buffer *dmabuf;
- EGLint format;
- int i;
-
- weston_buffer_reference(&gs->buffer_ref, buffer);
-
- if (!buffer) {
- for (i = 0; i < gs->num_images; i++) {
- egl_image_unref(gs->images[i]);
- gs->images[i] = NULL;
- }
- gs->num_images = 0;
- glDeleteTextures(gs->num_textures, gs->textures);
- gs->num_textures = 0;
- gs->buffer_type = BUFFER_TYPE_NULL;
- gs->y_inverted = 1;
- return;
- }
-
- shm_buffer = wl_shm_buffer_get(buffer->resource);
-
- if (shm_buffer)
- gl_renderer_attach_shm(es, buffer, shm_buffer);
- else if (gr->query_buffer(gr->egl_display, (void *) buffer->resource,
- EGL_TEXTURE_FORMAT, &format))
- gl_renderer_attach_egl(es, buffer, format);
- else if ((dmabuf = linux_dmabuf_buffer_get(buffer->resource)))
- gl_renderer_attach_dmabuf(es, buffer, dmabuf);
- else {
- weston_log("unhandled buffer type!\n");
- weston_buffer_reference(&gs->buffer_ref, NULL);
- gs->buffer_type = BUFFER_TYPE_NULL;
- gs->y_inverted = 1;
- }
-}
-
-static void
-gl_renderer_surface_set_color(struct weston_surface *surface,
- float red, float green, float blue, float alpha)
-{
- struct gl_surface_state *gs = get_surface_state(surface);
- struct gl_renderer *gr = get_renderer(surface->compositor);
-
- gs->color[0] = red;
- gs->color[1] = green;
- gs->color[2] = blue;
- gs->color[3] = alpha;
- gs->buffer_type = BUFFER_TYPE_SOLID;
- gs->pitch = 1;
- gs->height = 1;
-
- gs->shader = &gr->solid_shader;
-}
-
-static void
-gl_renderer_surface_get_content_size(struct weston_surface *surface,
- int *width, int *height)
-{
- struct gl_surface_state *gs = get_surface_state(surface);
-
- if (gs->buffer_type == BUFFER_TYPE_NULL) {
- *width = 0;
- *height = 0;
- } else {
- *width = gs->pitch;
- *height = gs->height;
- }
-}
-
-static uint32_t
-pack_color(pixman_format_code_t format, float *c)
-{
- uint8_t r = round(c[0] * 255.0f);
- uint8_t g = round(c[1] * 255.0f);
- uint8_t b = round(c[2] * 255.0f);
- uint8_t a = round(c[3] * 255.0f);
-
- switch (format) {
- case PIXMAN_a8b8g8r8:
- return (a << 24) | (b << 16) | (g << 8) | r;
- default:
- assert(0);
- return 0;
- }
-}
-
-static int
-gl_renderer_surface_copy_content(struct weston_surface *surface,
- void *target, size_t size,
- int src_x, int src_y,
- int width, int height)
-{
- static const GLfloat verts[4 * 2] = {
- 0.0f, 0.0f,
- 1.0f, 0.0f,
- 1.0f, 1.0f,
- 0.0f, 1.0f
- };
- static const GLfloat projmat_normal[16] = { /* transpose */
- 2.0f, 0.0f, 0.0f, 0.0f,
- 0.0f, 2.0f, 0.0f, 0.0f,
- 0.0f, 0.0f, 1.0f, 0.0f,
- -1.0f, -1.0f, 0.0f, 1.0f
- };
- static const GLfloat projmat_yinvert[16] = { /* transpose */
- 2.0f, 0.0f, 0.0f, 0.0f,
- 0.0f, -2.0f, 0.0f, 0.0f,
- 0.0f, 0.0f, 1.0f, 0.0f,
- -1.0f, 1.0f, 0.0f, 1.0f
- };
- const pixman_format_code_t format = PIXMAN_a8b8g8r8;
- const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */
- const GLenum gl_format = GL_RGBA; /* PIXMAN_a8b8g8r8 little-endian */
- struct gl_renderer *gr = get_renderer(surface->compositor);
- struct gl_surface_state *gs = get_surface_state(surface);
- int cw, ch;
- GLuint fbo;
- GLuint tex;
- GLenum status;
- const GLfloat *proj;
- int i;
-
- gl_renderer_surface_get_content_size(surface, &cw, &ch);
-
- switch (gs->buffer_type) {
- case BUFFER_TYPE_NULL:
- return -1;
- case BUFFER_TYPE_SOLID:
- *(uint32_t *)target = pack_color(format, gs->color);
- return 0;
- case BUFFER_TYPE_SHM:
- gl_renderer_flush_damage(surface);
- /* fall through */
- case BUFFER_TYPE_EGL:
- break;
- }
-
- glGenTextures(1, &tex);
- glBindTexture(GL_TEXTURE_2D, tex);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cw, ch,
- 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- glGenFramebuffers(1, &fbo);
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, tex, 0);
-
- status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- weston_log("%s: fbo error: %#x\n", __func__, status);
- glDeleteFramebuffers(1, &fbo);
- glDeleteTextures(1, &tex);
- return -1;
- }
-
- glViewport(0, 0, cw, ch);
- glDisable(GL_BLEND);
- use_shader(gr, gs->shader);
- if (gs->y_inverted)
- proj = projmat_normal;
- else
- proj = projmat_yinvert;
-
- glUniformMatrix4fv(gs->shader->proj_uniform, 1, GL_FALSE, proj);
- glUniform1f(gs->shader->alpha_uniform, 1.0f);
-
- for (i = 0; i < gs->num_textures; i++) {
- glUniform1i(gs->shader->tex_uniforms[i], i);
-
- glActiveTexture(GL_TEXTURE0 + i);
- glBindTexture(gs->target, gs->textures[i]);
- glTexParameteri(gs->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(gs->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- }
-
- /* position: */
- glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts);
- glEnableVertexAttribArray(0);
-
- /* texcoord: */
- glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, verts);
- glEnableVertexAttribArray(1);
-
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-
- glDisableVertexAttribArray(1);
- glDisableVertexAttribArray(0);
-
- glPixelStorei(GL_PACK_ALIGNMENT, bytespp);
- glReadPixels(src_x, src_y, width, height, gl_format,
- GL_UNSIGNED_BYTE, target);
-
- glDeleteFramebuffers(1, &fbo);
- glDeleteTextures(1, &tex);
-
- return 0;
-}
-
-static void
-surface_state_destroy(struct gl_surface_state *gs, struct gl_renderer *gr)
-{
- int i;
-
- wl_list_remove(&gs->surface_destroy_listener.link);
- wl_list_remove(&gs->renderer_destroy_listener.link);
-
- gs->surface->renderer_state = NULL;
-
- glDeleteTextures(gs->num_textures, gs->textures);
-
- for (i = 0; i < gs->num_images; i++)
- egl_image_unref(gs->images[i]);
-
- weston_buffer_reference(&gs->buffer_ref, NULL);
- pixman_region32_fini(&gs->texture_damage);
- free(gs);
-}
-
-static void
-surface_state_handle_surface_destroy(struct wl_listener *listener, void *data)
-{
- struct gl_surface_state *gs;
- struct gl_renderer *gr;
-
- gs = container_of(listener, struct gl_surface_state,
- surface_destroy_listener);
-
- gr = get_renderer(gs->surface->compositor);
-
- surface_state_destroy(gs, gr);
-}
-
-static void
-surface_state_handle_renderer_destroy(struct wl_listener *listener, void *data)
-{
- struct gl_surface_state *gs;
- struct gl_renderer *gr;
-
- gr = data;
-
- gs = container_of(listener, struct gl_surface_state,
- renderer_destroy_listener);
-
- surface_state_destroy(gs, gr);
-}
-
-static int
-gl_renderer_create_surface(struct weston_surface *surface)
-{
- struct gl_surface_state *gs;
- struct gl_renderer *gr = get_renderer(surface->compositor);
-
- gs = zalloc(sizeof *gs);
- if (gs == NULL)
- return -1;
-
- /* A buffer is never attached to solid color surfaces, yet
- * they still go through texcoord computations. Do not divide
- * by zero there.
- */
- gs->pitch = 1;
- gs->y_inverted = 1;
-
- gs->surface = surface;
-
- pixman_region32_init(&gs->texture_damage);
- surface->renderer_state = gs;
-
- gs->surface_destroy_listener.notify =
- surface_state_handle_surface_destroy;
- wl_signal_add(&surface->destroy_signal,
- &gs->surface_destroy_listener);
-
- gs->renderer_destroy_listener.notify =
- surface_state_handle_renderer_destroy;
- wl_signal_add(&gr->destroy_signal,
- &gs->renderer_destroy_listener);
-
- if (surface->buffer_ref.buffer) {
- gl_renderer_attach(surface, surface->buffer_ref.buffer);
- gl_renderer_flush_damage(surface);
- }
-
- return 0;
-}
-
-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";
-
-/* Declare common fragment shader uniforms */
-#define FRAGMENT_CONVERT_YUV \
- " y *= alpha;\n" \
- " u *= alpha;\n" \
- " v *= alpha;\n" \
- " gl_FragColor.r = y + 1.59602678 * v;\n" \
- " gl_FragColor.g = y - 0.39176229 * u - 0.81296764 * v;\n" \
- " gl_FragColor.b = y + 2.01723214 * u;\n" \
- " gl_FragColor.a = alpha;\n"
-
-static const char fragment_debug[] =
- " gl_FragColor = vec4(0.0, 0.3, 0.0, 0.2) + gl_FragColor * 0.8;\n";
-
-static const char fragment_brace[] =
- "}\n";
-
-static const char texture_fragment_shader_rgba[] =
- "precision mediump float;\n"
- "varying vec2 v_texcoord;\n"
- "uniform sampler2D tex;\n"
- "uniform float alpha;\n"
- "void main()\n"
- "{\n"
- " gl_FragColor = alpha * texture2D(tex, v_texcoord)\n;"
- ;
-
-static const char texture_fragment_shader_rgbx[] =
- "precision mediump float;\n"
- "varying vec2 v_texcoord;\n"
- "uniform sampler2D tex;\n"
- "uniform float alpha;\n"
- "void main()\n"
- "{\n"
- " gl_FragColor.rgb = alpha * texture2D(tex, v_texcoord).rgb\n;"
- " gl_FragColor.a = alpha;\n"
- ;
-
-static const char texture_fragment_shader_egl_external[] =
- "#extension GL_OES_EGL_image_external : require\n"
- "precision mediump float;\n"
- "varying vec2 v_texcoord;\n"
- "uniform samplerExternalOES tex;\n"
- "uniform float alpha;\n"
- "void main()\n"
- "{\n"
- " gl_FragColor = alpha * texture2D(tex, v_texcoord)\n;"
- ;
-
-static const char texture_fragment_shader_y_uv[] =
- "precision mediump float;\n"
- "uniform sampler2D tex;\n"
- "uniform sampler2D tex1;\n"
- "varying vec2 v_texcoord;\n"
- "uniform float alpha;\n"
- "void main() {\n"
- " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n"
- " float u = texture2D(tex1, v_texcoord).r - 0.5;\n"
- " float v = texture2D(tex1, v_texcoord).g - 0.5;\n"
- FRAGMENT_CONVERT_YUV
- ;
-
-static const char texture_fragment_shader_y_u_v[] =
- "precision mediump float;\n"
- "uniform sampler2D tex;\n"
- "uniform sampler2D tex1;\n"
- "uniform sampler2D tex2;\n"
- "varying vec2 v_texcoord;\n"
- "uniform float alpha;\n"
- "void main() {\n"
- " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n"
- " float u = texture2D(tex1, v_texcoord).x - 0.5;\n"
- " float v = texture2D(tex2, v_texcoord).x - 0.5;\n"
- FRAGMENT_CONVERT_YUV
- ;
-
-static const char texture_fragment_shader_y_xuxv[] =
- "precision mediump float;\n"
- "uniform sampler2D tex;\n"
- "uniform sampler2D tex1;\n"
- "varying vec2 v_texcoord;\n"
- "uniform float alpha;\n"
- "void main() {\n"
- " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n"
- " float u = texture2D(tex1, v_texcoord).g - 0.5;\n"
- " float v = texture2D(tex1, v_texcoord).a - 0.5;\n"
- FRAGMENT_CONVERT_YUV
- ;
-
-static const char solid_fragment_shader[] =
- "precision mediump float;\n"
- "uniform vec4 color;\n"
- "uniform float alpha;\n"
- "void main()\n"
- "{\n"
- " gl_FragColor = alpha * color\n;"
- ;
-
-static int
-compile_shader(GLenum type, int count, const char **sources)
-{
- GLuint s;
- char msg[512];
- GLint status;
-
- s = glCreateShader(type);
- glShaderSource(s, count, sources, NULL);
- glCompileShader(s);
- glGetShaderiv(s, GL_COMPILE_STATUS, &status);
- if (!status) {
- glGetShaderInfoLog(s, sizeof msg, NULL, msg);
- weston_log("shader info: %s\n", msg);
- return GL_NONE;
- }
-
- return s;
-}
-
-static int
-shader_init(struct gl_shader *shader, struct gl_renderer *renderer,
- const char *vertex_source, const char *fragment_source)
-{
- char msg[512];
- GLint status;
- int count;
- const char *sources[3];
-
- shader->vertex_shader =
- compile_shader(GL_VERTEX_SHADER, 1, &vertex_source);
-
- if (renderer->fragment_shader_debug) {
- sources[0] = fragment_source;
- sources[1] = fragment_debug;
- sources[2] = fragment_brace;
- count = 3;
- } else {
- sources[0] = fragment_source;
- sources[1] = fragment_brace;
- count = 2;
- }
-
- shader->fragment_shader =
- compile_shader(GL_FRAGMENT_SHADER, count, sources);
-
- 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);
- weston_log("link info: %s\n", msg);
- return -1;
- }
-
- shader->proj_uniform = glGetUniformLocation(shader->program, "proj");
- shader->tex_uniforms[0] = glGetUniformLocation(shader->program, "tex");
- shader->tex_uniforms[1] = glGetUniformLocation(shader->program, "tex1");
- shader->tex_uniforms[2] = glGetUniformLocation(shader->program, "tex2");
- shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha");
- shader->color_uniform = glGetUniformLocation(shader->program, "color");
-
- return 0;
-}
-
-static void
-shader_release(struct gl_shader *shader)
-{
- glDeleteShader(shader->vertex_shader);
- glDeleteShader(shader->fragment_shader);
- glDeleteProgram(shader->program);
-
- shader->vertex_shader = 0;
- shader->fragment_shader = 0;
- shader->program = 0;
-}
-
-static void
-log_extensions(const char *name, const char *extensions)
-{
- const char *p, *end;
- int l;
- int len;
-
- l = weston_log("%s:", name);
- p = extensions;
- while (*p) {
- end = strchrnul(p, ' ');
- len = end - p;
- if (l + len > 78)
- l = weston_log_continue("\n" STAMP_SPACE "%.*s",
- len, p);
- else
- l += weston_log_continue(" %.*s", len, p);
- for (p = end; isspace(*p); p++)
- ;
- }
- weston_log_continue("\n");
-}
-
-static void
-log_egl_gl_info(EGLDisplay egldpy)
-{
- const char *str;
-
- str = eglQueryString(egldpy, EGL_VERSION);
- weston_log("EGL version: %s\n", str ? str : "(null)");
-
- str = eglQueryString(egldpy, EGL_VENDOR);
- weston_log("EGL vendor: %s\n", str ? str : "(null)");
-
- str = eglQueryString(egldpy, EGL_CLIENT_APIS);
- weston_log("EGL client APIs: %s\n", str ? str : "(null)");
-
- str = eglQueryString(egldpy, EGL_EXTENSIONS);
- log_extensions("EGL extensions", str ? str : "(null)");
-
- str = (char *)glGetString(GL_VERSION);
- weston_log("GL version: %s\n", str ? str : "(null)");
-
- str = (char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
- weston_log("GLSL version: %s\n", str ? str : "(null)");
-
- str = (char *)glGetString(GL_VENDOR);
- weston_log("GL vendor: %s\n", str ? str : "(null)");
-
- str = (char *)glGetString(GL_RENDERER);
- weston_log("GL renderer: %s\n", str ? str : "(null)");
-
- str = (char *)glGetString(GL_EXTENSIONS);
- log_extensions("GL extensions", str ? str : "(null)");
-}
-
-static void
-log_egl_config_info(EGLDisplay egldpy, EGLConfig eglconfig)
-{
- EGLint r, g, b, a;
-
- weston_log("Chosen EGL config details:\n");
-
- weston_log_continue(STAMP_SPACE "RGBA bits");
- if (eglGetConfigAttrib(egldpy, eglconfig, EGL_RED_SIZE, &r) &&
- eglGetConfigAttrib(egldpy, eglconfig, EGL_GREEN_SIZE, &g) &&
- eglGetConfigAttrib(egldpy, eglconfig, EGL_BLUE_SIZE, &b) &&
- eglGetConfigAttrib(egldpy, eglconfig, EGL_ALPHA_SIZE, &a))
- weston_log_continue(": %d %d %d %d\n", r, g, b, a);
- else
- weston_log_continue(" unknown\n");
-
- weston_log_continue(STAMP_SPACE "swap interval range");
- if (eglGetConfigAttrib(egldpy, eglconfig, EGL_MIN_SWAP_INTERVAL, &a) &&
- eglGetConfigAttrib(egldpy, eglconfig, EGL_MAX_SWAP_INTERVAL, &b))
- weston_log_continue(": %d - %d\n", a, b);
- else
- weston_log_continue(" unknown\n");
-}
-
-static int
-match_config_to_visual(EGLDisplay egl_display,
- EGLint visual_id,
- EGLConfig *configs,
- int count)
-{
- int i;
-
- for (i = 0; i < count; ++i) {
- EGLint id;
-
- if (!eglGetConfigAttrib(egl_display,
- configs[i], EGL_NATIVE_VISUAL_ID,
- &id))
- continue;
-
- if (id == visual_id)
- return i;
- }
-
- return -1;
-}
-
-static int
-egl_choose_config(struct gl_renderer *gr, const EGLint *attribs,
- const EGLint *visual_id, const int n_ids,
- EGLConfig *config_out)
-{
- EGLint count = 0;
- EGLint matched = 0;
- EGLConfig *configs;
- int i, config_index = -1;
-
- if (!eglGetConfigs(gr->egl_display, NULL, 0, &count) || count < 1) {
- weston_log("No EGL configs to choose from.\n");
- return -1;
- }
- configs = calloc(count, sizeof *configs);
- if (!configs)
- return -1;
-
- if (!eglChooseConfig(gr->egl_display, attribs, configs,
- count, &matched) || !matched) {
- weston_log("No EGL configs with appropriate attributes.\n");
- goto out;
- }
-
- if (!visual_id)
- config_index = 0;
-
- for (i = 0; config_index == -1 && i < n_ids; i++)
- config_index = match_config_to_visual(gr->egl_display,
- visual_id[i],
- configs,
- matched);
-
- if (config_index != -1)
- *config_out = configs[config_index];
-
-out:
- free(configs);
- if (config_index == -1)
- return -1;
-
- if (i > 1)
- weston_log("Unable to use first choice EGL config with id"
- " 0x%x, succeeded with alternate id 0x%x.\n",
- visual_id[0], visual_id[i - 1]);
- return 0;
-}
-
-static void
-gl_renderer_output_set_border(struct weston_output *output,
- enum gl_renderer_border_side side,
- int32_t width, int32_t height,
- int32_t tex_width, unsigned char *data)
-{
- struct gl_output_state *go = get_output_state(output);
-
- if (go->borders[side].width != width ||
- go->borders[side].height != height)
- /* In this case, we have to blow everything and do a full
- * repaint. */
- go->border_status |= BORDER_SIZE_CHANGED | BORDER_ALL_DIRTY;
-
- if (data == NULL) {
- width = 0;
- height = 0;
- }
-
- go->borders[side].width = width;
- go->borders[side].height = height;
- go->borders[side].tex_width = tex_width;
- go->borders[side].data = data;
- go->border_status |= 1 << side;
-}
-
-static int
-gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface);
-
-static int
-gl_renderer_output_create(struct weston_output *output,
- EGLNativeWindowType window_for_legacy,
- void *window_for_platform,
- const EGLint *attribs,
- const EGLint *visual_id,
- int n_ids)
-{
- struct weston_compositor *ec = output->compositor;
- struct gl_renderer *gr = get_renderer(ec);
- struct gl_output_state *go;
- EGLConfig egl_config;
- int i;
-
- if (egl_choose_config(gr, attribs, visual_id,
- n_ids, &egl_config) == -1) {
- weston_log("failed to choose EGL config for output\n");
- return -1;
- }
-
- if (egl_config != gr->egl_config &&
- !gr->has_configless_context) {
- weston_log("attempted to use a different EGL config for an "
- "output but EGL_MESA_configless_context is not "
- "supported\n");
- return -1;
- }
-
- go = zalloc(sizeof *go);
- if (go == NULL)
- return -1;
-
- if (gr->create_platform_window) {
- go->egl_surface =
- gr->create_platform_window(gr->egl_display,
- egl_config,
- window_for_platform,
- NULL);
- } else {
- go->egl_surface =
- eglCreateWindowSurface(gr->egl_display,
- egl_config,
- window_for_legacy, NULL);
- }
-
- if (go->egl_surface == EGL_NO_SURFACE) {
- weston_log("failed to create egl surface\n");
- free(go);
- return -1;
- }
-
- if (gr->egl_context == NULL)
- if (gl_renderer_setup(ec, go->egl_surface) < 0) {
- free(go);
- return -1;
- }
-
- for (i = 0; i < BUFFER_DAMAGE_COUNT; i++)
- pixman_region32_init(&go->buffer_damage[i]);
-
- output->renderer_state = go;
-
- log_egl_config_info(gr->egl_display, egl_config);
-
- return 0;
-}
-
-static void
-gl_renderer_output_destroy(struct weston_output *output)
-{
- struct gl_renderer *gr = get_renderer(output->compositor);
- struct gl_output_state *go = get_output_state(output);
- int i;
-
- for (i = 0; i < 2; i++)
- pixman_region32_fini(&go->buffer_damage[i]);
-
- eglDestroySurface(gr->egl_display, go->egl_surface);
-
- free(go);
-}
-
-static EGLSurface
-gl_renderer_output_surface(struct weston_output *output)
-{
- return get_output_state(output)->egl_surface;
-}
-
-static void
-gl_renderer_destroy(struct weston_compositor *ec)
-{
- struct gl_renderer *gr = get_renderer(ec);
- struct dmabuf_image *image, *next;
-
- wl_signal_emit(&gr->destroy_signal, gr);
-
- if (gr->has_bind_display)
- gr->unbind_display(gr->egl_display, ec->wl_display);
-
- /* Work around crash in egl_dri2.c's dri2_make_current() - when does this apply? */
- eglMakeCurrent(gr->egl_display,
- EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT);
-
-
- wl_list_for_each_safe(image, next, &gr->dmabuf_images, link)
- dmabuf_image_destroy(image);
-
- eglTerminate(gr->egl_display);
- eglReleaseThread();
-
- wl_array_release(&gr->vertices);
- wl_array_release(&gr->vtxcnt);
-
- if (gr->fragment_binding)
- weston_binding_destroy(gr->fragment_binding);
- if (gr->fan_binding)
- weston_binding_destroy(gr->fan_binding);
-
- free(gr);
-}
-
-static bool
-check_extension(const char *extensions, const char *extension)
-{
- size_t extlen = strlen(extension);
- const char *end = extensions + strlen(extensions);
-
- while (extensions < end) {
- size_t n = 0;
-
- /* Skip whitespaces, if any */
- if (*extensions == ' ') {
- extensions++;
- continue;
- }
-
- n = strcspn(extensions, " ");
-
- /* Compare strings */
- if (n == extlen && strncmp(extension, extensions, n) == 0)
- return true; /* Found */
-
- extensions += n;
- }
-
- /* Not found */
- return false;
-}
-
-static void
-renderer_setup_egl_client_extensions(struct gl_renderer *gr)
-{
- const char *extensions;
-
- extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
- if (!extensions) {
- weston_log("Retrieving EGL client extension string failed.\n");
- return;
- }
-
- if (check_extension(extensions, "EGL_EXT_platform_base"))
- gr->create_platform_window =
- (void *) eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
- else
- weston_log("warning: EGL_EXT_platform_base not supported.\n");
-}
-
-static int
-gl_renderer_setup_egl_extensions(struct weston_compositor *ec)
-{
- struct gl_renderer *gr = get_renderer(ec);
- const char *extensions;
- EGLBoolean ret;
-
- gr->create_image = (void *) eglGetProcAddress("eglCreateImageKHR");
- gr->destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR");
- gr->bind_display =
- (void *) eglGetProcAddress("eglBindWaylandDisplayWL");
- gr->unbind_display =
- (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL");
- gr->query_buffer =
- (void *) eglGetProcAddress("eglQueryWaylandBufferWL");
-
- extensions =
- (const char *) eglQueryString(gr->egl_display, EGL_EXTENSIONS);
- if (!extensions) {
- weston_log("Retrieving EGL extension string failed.\n");
- return -1;
- }
-
- if (check_extension(extensions, "EGL_WL_bind_wayland_display"))
- gr->has_bind_display = 1;
- if (gr->has_bind_display) {
- ret = gr->bind_display(gr->egl_display, ec->wl_display);
- if (!ret)
- gr->has_bind_display = 0;
- }
-
- if (check_extension(extensions, "EGL_EXT_buffer_age"))
- gr->has_egl_buffer_age = 1;
- else
- weston_log("warning: EGL_EXT_buffer_age not supported. "
- "Performance could be affected.\n");
-
-#ifdef EGL_EXT_swap_buffers_with_damage
- if (check_extension(extensions, "EGL_EXT_swap_buffers_with_damage"))
- gr->swap_buffers_with_damage =
- (void *) eglGetProcAddress("eglSwapBuffersWithDamageEXT");
- else
- weston_log("warning: EGL_EXT_swap_buffers_with_damage not "
- "supported. Performance could be affected.\n");
-#endif
-
-#ifdef EGL_MESA_configless_context
- if (check_extension(extensions, "EGL_MESA_configless_context"))
- gr->has_configless_context = 1;
-#endif
-
-#ifdef EGL_EXT_image_dma_buf_import
- if (check_extension(extensions, "EGL_EXT_image_dma_buf_import"))
- gr->has_dmabuf_import = 1;
-#endif
-
- renderer_setup_egl_client_extensions(gr);
-
- return 0;
-}
-
-static const EGLint gl_renderer_opaque_attribs[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_RED_SIZE, 1,
- EGL_GREEN_SIZE, 1,
- EGL_BLUE_SIZE, 1,
- EGL_ALPHA_SIZE, 0,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_NONE
-};
-
-static const EGLint gl_renderer_alpha_attribs[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_RED_SIZE, 1,
- EGL_GREEN_SIZE, 1,
- EGL_BLUE_SIZE, 1,
- EGL_ALPHA_SIZE, 1,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_NONE
-};
-
-/** Checks whether a platform EGL client extension is supported
- *
- * \param ec The weston compositor
- * \param extension_suffix The EGL client extension suffix
- * \return 1 if supported, 0 if using fallbacks, -1 unsupported
- *
- * This function checks whether a specific platform_* extension is supported
- * by EGL.
- *
- * The extension suffix should be the suffix of the platform extension (that
- * specifies a <platform> argument as defined in EGL_EXT_platform_base). For
- * example, passing "foo" will check whether either "EGL_KHR_platform_foo",
- * "EGL_EXT_platform_foo", or "EGL_MESA_platform_foo" is supported.
- *
- * The return value is 1:
- * - if the supplied EGL client extension is supported.
- * The return value is 0:
- * - if the platform_base client extension isn't supported so will
- * fallback to eglGetDisplay and friends.
- * The return value is -1:
- * - if the supplied EGL client extension is not supported.
- */
-static int
-gl_renderer_supports(struct weston_compositor *ec,
- const char *extension_suffix)
-{
- static const char *extensions = NULL;
- char s[64];
-
- if (!extensions) {
- extensions = (const char *) eglQueryString(
- EGL_NO_DISPLAY, EGL_EXTENSIONS);
-
- if (!extensions)
- return 0;
-
- log_extensions("EGL client extensions",
- extensions);
- }
-
- if (!check_extension(extensions, "EGL_EXT_platform_base"))
- return 0;
-
- snprintf(s, sizeof s, "EGL_KHR_platform_%s", extension_suffix);
- if (check_extension(extensions, s))
- return 1;
-
- snprintf(s, sizeof s, "EGL_EXT_platform_%s", extension_suffix);
- if (check_extension(extensions, s))
- return 1;
-
- snprintf(s, sizeof s, "EGL_MESA_platform_%s", extension_suffix);
- if (check_extension(extensions, s))
- return 1;
-
- /* at this point we definitely have some platform extensions but
- * haven't found the supplied platform, so chances are it's
- * not supported. */
-
- return -1;
-}
-
-static const char *
-platform_to_extension(EGLenum platform)
-{
- switch (platform) {
- case EGL_PLATFORM_GBM_KHR:
- return "gbm";
- case EGL_PLATFORM_WAYLAND_KHR:
- return "wayland";
- case EGL_PLATFORM_X11_KHR:
- return "x11";
- default:
- assert(0 && "bad EGL platform enum");
- }
-}
-
-static int
-gl_renderer_create(struct weston_compositor *ec, EGLenum platform,
- void *native_window, const EGLint *attribs,
- const EGLint *visual_id, int n_ids)
-{
- struct gl_renderer *gr;
- EGLint major, minor;
- int supports = 0;
-
- if (platform) {
- supports = gl_renderer_supports(
- ec, platform_to_extension(platform));
- if (supports < 0)
- return -1;
- }
-
- gr = zalloc(sizeof *gr);
- if (gr == NULL)
- return -1;
-
- gr->base.read_pixels = gl_renderer_read_pixels;
- gr->base.repaint_output = gl_renderer_repaint_output;
- gr->base.flush_damage = gl_renderer_flush_damage;
- gr->base.attach = gl_renderer_attach;
- gr->base.surface_set_color = gl_renderer_surface_set_color;
- gr->base.destroy = gl_renderer_destroy;
- gr->base.surface_get_content_size =
- gl_renderer_surface_get_content_size;
- gr->base.surface_copy_content = gl_renderer_surface_copy_content;
- gr->egl_display = NULL;
-
- /* extension_suffix is supported */
- if (supports) {
- if (!get_platform_display) {
- get_platform_display = (void *) eglGetProcAddress(
- "eglGetPlatformDisplayEXT");
- }
-
- /* also wrap this in the supports check because
- * eglGetProcAddress can return non-NULL and still not
- * support the feature at runtime, so ensure the
- * appropriate extension checks have been done. */
- if (get_platform_display && platform) {
- gr->egl_display = get_platform_display(platform,
- native_window,
- NULL);
- }
- }
-
- if (!gr->egl_display) {
- weston_log("warning: either no EGL_EXT_platform_base "
- "support or specific platform support; "
- "falling back to eglGetDisplay.\n");
- gr->egl_display = eglGetDisplay(native_window);
- }
-
- if (gr->egl_display == EGL_NO_DISPLAY) {
- weston_log("failed to create display\n");
- goto fail;
- }
-
- if (!eglInitialize(gr->egl_display, &major, &minor)) {
- weston_log("failed to initialize display\n");
- goto fail_with_error;
- }
-
- if (egl_choose_config(gr, attribs, visual_id,
- n_ids, &gr->egl_config) < 0) {
- weston_log("failed to choose EGL config\n");
- goto fail_terminate;
- }
-
- ec->renderer = &gr->base;
- ec->capabilities |= WESTON_CAP_ROTATION_ANY;
- ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP;
- ec->capabilities |= WESTON_CAP_VIEW_CLIP_MASK;
-
- if (gl_renderer_setup_egl_extensions(ec) < 0)
- goto fail_with_error;
-
- wl_list_init(&gr->dmabuf_images);
- if (gr->has_dmabuf_import)
- gr->base.import_dmabuf = gl_renderer_import_dmabuf;
-
- wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565);
-
- wl_signal_init(&gr->destroy_signal);
-
- return 0;
-
-fail_with_error:
- gl_renderer_print_egl_error_state();
-fail_terminate:
- eglTerminate(gr->egl_display);
-fail:
- free(gr);
- return -1;
-}
-
-static EGLDisplay
-gl_renderer_display(struct weston_compositor *ec)
-{
- return get_renderer(ec)->egl_display;
-}
-
-static int
-compile_shaders(struct weston_compositor *ec)
-{
- struct gl_renderer *gr = get_renderer(ec);
-
- gr->texture_shader_rgba.vertex_source = vertex_shader;
- gr->texture_shader_rgba.fragment_source = texture_fragment_shader_rgba;
-
- gr->texture_shader_rgbx.vertex_source = vertex_shader;
- gr->texture_shader_rgbx.fragment_source = texture_fragment_shader_rgbx;
-
- gr->texture_shader_egl_external.vertex_source = vertex_shader;
- gr->texture_shader_egl_external.fragment_source =
- texture_fragment_shader_egl_external;
-
- gr->texture_shader_y_uv.vertex_source = vertex_shader;
- gr->texture_shader_y_uv.fragment_source = texture_fragment_shader_y_uv;
-
- gr->texture_shader_y_u_v.vertex_source = vertex_shader;
- gr->texture_shader_y_u_v.fragment_source =
- texture_fragment_shader_y_u_v;
-
- gr->texture_shader_y_xuxv.vertex_source = vertex_shader;
- gr->texture_shader_y_xuxv.fragment_source =
- texture_fragment_shader_y_xuxv;
-
- gr->solid_shader.vertex_source = vertex_shader;
- gr->solid_shader.fragment_source = solid_fragment_shader;
-
- return 0;
-}
-
-static void
-fragment_debug_binding(struct weston_keyboard *keyboard, uint32_t time,
- uint32_t key, void *data)
-{
- struct weston_compositor *ec = data;
- struct gl_renderer *gr = get_renderer(ec);
- struct weston_output *output;
-
- gr->fragment_shader_debug ^= 1;
-
- shader_release(&gr->texture_shader_rgba);
- shader_release(&gr->texture_shader_rgbx);
- shader_release(&gr->texture_shader_egl_external);
- shader_release(&gr->texture_shader_y_uv);
- shader_release(&gr->texture_shader_y_u_v);
- shader_release(&gr->texture_shader_y_xuxv);
- shader_release(&gr->solid_shader);
-
- /* Force use_shader() to call glUseProgram(), since we need to use
- * the recompiled version of the shader. */
- gr->current_shader = NULL;
-
- wl_list_for_each(output, &ec->output_list, link)
- weston_output_damage(output);
-}
-
-static void
-fan_debug_repaint_binding(struct weston_keyboard *keyboard, uint32_t time,
- uint32_t key, void *data)
-{
- struct weston_compositor *compositor = data;
- struct gl_renderer *gr = get_renderer(compositor);
-
- gr->fan_debug = !gr->fan_debug;
- weston_compositor_damage_all(compositor);
-}
-
-static int
-gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface)
-{
- struct gl_renderer *gr = get_renderer(ec);
- const char *extensions;
- EGLConfig context_config;
- EGLBoolean ret;
-
- static const EGLint context_attribs[] = {
- EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_NONE
- };
-
- if (!eglBindAPI(EGL_OPENGL_ES_API)) {
- weston_log("failed to bind EGL_OPENGL_ES_API\n");
- gl_renderer_print_egl_error_state();
- return -1;
- }
-
- context_config = gr->egl_config;
-
-#ifdef EGL_MESA_configless_context
- if (gr->has_configless_context)
- context_config = EGL_NO_CONFIG_MESA;
-#endif
-
- gr->egl_context = eglCreateContext(gr->egl_display, context_config,
- EGL_NO_CONTEXT, context_attribs);
- if (gr->egl_context == NULL) {
- weston_log("failed to create context\n");
- gl_renderer_print_egl_error_state();
- return -1;
- }
-
- ret = eglMakeCurrent(gr->egl_display, egl_surface,
- egl_surface, gr->egl_context);
- if (ret == EGL_FALSE) {
- weston_log("Failed to make EGL context current.\n");
- gl_renderer_print_egl_error_state();
- return -1;
- }
-
- log_egl_gl_info(gr->egl_display);
-
- gr->image_target_texture_2d =
- (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES");
-
- extensions = (const char *) glGetString(GL_EXTENSIONS);
- if (!extensions) {
- weston_log("Retrieving GL extension string failed.\n");
- return -1;
- }
-
- if (!check_extension(extensions, "GL_EXT_texture_format_BGRA8888")) {
- weston_log("GL_EXT_texture_format_BGRA8888 not available\n");
- return -1;
- }
-
- if (check_extension(extensions, "GL_EXT_read_format_bgra"))
- ec->read_format = PIXMAN_a8r8g8b8;
- else
- ec->read_format = PIXMAN_a8b8g8r8;
-
-#ifdef GL_EXT_unpack_subimage
- if (check_extension(extensions, "GL_EXT_unpack_subimage"))
- gr->has_unpack_subimage = 1;
-#endif
-
- if (check_extension(extensions, "GL_OES_EGL_image_external"))
- gr->has_egl_image_external = 1;
-
- glActiveTexture(GL_TEXTURE0);
-
- if (compile_shaders(ec))
- return -1;
-
- gr->fragment_binding =
- weston_compositor_add_debug_binding(ec, KEY_S,
- fragment_debug_binding,
- ec);
- gr->fan_binding =
- weston_compositor_add_debug_binding(ec, KEY_F,
- fan_debug_repaint_binding,
- ec);
-
- weston_log("GL ES 2 renderer features:\n");
- weston_log_continue(STAMP_SPACE "read-back format: %s\n",
- ec->read_format == PIXMAN_a8r8g8b8 ? "BGRA" : "RGBA");
- weston_log_continue(STAMP_SPACE "wl_shm sub-image to texture: %s\n",
- gr->has_unpack_subimage ? "yes" : "no");
- weston_log_continue(STAMP_SPACE "EGL Wayland extension: %s\n",
- gr->has_bind_display ? "yes" : "no");
-
-
- return 0;
-}
-
-WL_EXPORT struct gl_renderer_interface gl_renderer_interface = {
- .opaque_attribs = gl_renderer_opaque_attribs,
- .alpha_attribs = gl_renderer_alpha_attribs,
-
- .create = gl_renderer_create,
- .display = gl_renderer_display,
- .output_create = gl_renderer_output_create,
- .output_destroy = gl_renderer_output_destroy,
- .output_surface = gl_renderer_output_surface,
- .output_set_border = gl_renderer_output_set_border,
- .print_egl_error_state = gl_renderer_print_egl_error_state
-};
+++ /dev/null
-/*
- * Copyright © 2012 John Kåre Alsaker
- *
- * 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.
- */
-
-#include "config.h"
-
-#include "compositor.h"
-
-#ifdef ENABLE_EGL
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#else
-
-typedef int EGLint;
-typedef int EGLenum;
-typedef void *EGLDisplay;
-typedef void *EGLSurface;
-typedef void *EGLConfig;
-typedef intptr_t EGLNativeDisplayType;
-typedef intptr_t EGLNativeWindowType;
-#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0)
-
-#endif /* ENABLE_EGL */
-
-#ifndef EGL_EXT_platform_base
-typedef EGLDisplay (*PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
-typedef EGLSurface (*PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC) (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLint *attrib_list);
-#endif
-
-#ifndef EGL_PLATFORM_GBM_KHR
-#define EGL_PLATFORM_GBM_KHR 0x31D7
-#endif
-
-#ifndef EGL_PLATFORM_WAYLAND_KHR
-#define EGL_PLATFORM_WAYLAND_KHR 0x31D8
-#endif
-
-#ifndef EGL_PLATFORM_X11_KHR
-#define EGL_PLATFORM_X11_KHR 0x31D5
-#endif
-
-#define NO_EGL_PLATFORM 0
-
-enum gl_renderer_border_side {
- GL_RENDERER_BORDER_TOP = 0,
- GL_RENDERER_BORDER_LEFT = 1,
- GL_RENDERER_BORDER_RIGHT = 2,
- GL_RENDERER_BORDER_BOTTOM = 3,
-};
-
-struct gl_renderer_interface {
- const EGLint *opaque_attribs;
- const EGLint *alpha_attribs;
-
- int (*create)(struct weston_compositor *ec,
- EGLenum platform,
- void *native_window,
- const EGLint *attribs,
- const EGLint *visual_id,
- const int n_ids);
-
- EGLDisplay (*display)(struct weston_compositor *ec);
-
- int (*output_create)(struct weston_output *output,
- EGLNativeWindowType window_for_legacy,
- void *window_for_platform,
- const EGLint *attribs,
- const EGLint *visual_id,
- const int n_ids);
-
- void (*output_destroy)(struct weston_output *output);
-
- EGLSurface (*output_surface)(struct weston_output *output);
-
- /* Sets the output border.
- *
- * The side specifies the side for which we are setting the border.
- * The width and height are the width and height of the border.
- * The tex_width patemeter specifies the width of the actual
- * texture; this may be larger than width if the data is not
- * tightly packed.
- *
- * The top and bottom textures will extend over the sides to the
- * full width of the bordered window. The right and left edges,
- * however, will extend only to the top and bottom of the
- * compositor surface. This is demonstrated by the picture below:
- *
- * +-----------------------+
- * | TOP |
- * +-+-------------------+-+
- * | | | |
- * |L| |R|
- * |E| |I|
- * |F| |G|
- * |T| |H|
- * | | |T|
- * | | | |
- * +-+-------------------+-+
- * | BOTTOM |
- * +-----------------------+
- */
- void (*output_set_border)(struct weston_output *output,
- enum gl_renderer_border_side side,
- int32_t width, int32_t height,
- int32_t tex_width, unsigned char *data);
-
- void (*print_egl_error_state)(void);
-};
-
+++ /dev/null
-/*
- * Copyright © 2013 Intel Corporation
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <assert.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <limits.h>
-
-#include "shared/helpers.h"
-#include "shared/os-compatibility.h"
-#include "compositor.h"
-
-static void
-empty_region(pixman_region32_t *region)
-{
- pixman_region32_fini(region);
- pixman_region32_init(region);
-}
-
-static struct weston_pointer_client *
-weston_pointer_client_create(struct wl_client *client)
-{
- struct weston_pointer_client *pointer_client;
-
- pointer_client = zalloc(sizeof *pointer_client);
- if (!pointer_client)
- return NULL;
-
- pointer_client->client = client;
- wl_list_init(&pointer_client->pointer_resources);
-
- return pointer_client;
-}
-
-static void
-weston_pointer_client_destroy(struct weston_pointer_client *pointer_client)
-{
- free(pointer_client);
-}
-
-static bool
-weston_pointer_client_is_empty(struct weston_pointer_client *pointer_client)
-{
- return wl_list_empty(&pointer_client->pointer_resources);
-}
-
-static struct weston_pointer_client *
-weston_pointer_get_pointer_client(struct weston_pointer *pointer,
- struct wl_client *client)
-{
- struct weston_pointer_client *pointer_client;
-
- wl_list_for_each(pointer_client, &pointer->pointer_clients, link) {
- if (pointer_client->client == client)
- return pointer_client;
- }
-
- return NULL;
-}
-
-static struct weston_pointer_client *
-weston_pointer_ensure_pointer_client(struct weston_pointer *pointer,
- struct wl_client *client)
-{
- struct weston_pointer_client *pointer_client;
-
- pointer_client = weston_pointer_get_pointer_client(pointer, client);
- if (pointer_client)
- return pointer_client;
-
- pointer_client = weston_pointer_client_create(client);
- wl_list_insert(&pointer->pointer_clients, &pointer_client->link);
-
- if (pointer->focus &&
- pointer->focus->surface->resource &&
- wl_resource_get_client(pointer->focus->surface->resource) == client) {
- pointer->focus_client = pointer_client;
- }
-
- return pointer_client;
-}
-
-static void
-weston_pointer_cleanup_pointer_client(struct weston_pointer *pointer,
- struct weston_pointer_client *pointer_client)
-{
- if (weston_pointer_client_is_empty(pointer_client)) {
- if (pointer->focus_client == pointer_client)
- pointer->focus_client = NULL;
- wl_list_remove(&pointer_client->link);
- weston_pointer_client_destroy(pointer_client);
- }
-}
-
-static void
-unbind_pointer_client_resource(struct wl_resource *resource)
-{
- struct weston_pointer *pointer = wl_resource_get_user_data(resource);
- struct wl_client *client = wl_resource_get_client(resource);
- struct weston_pointer_client *pointer_client;
-
- pointer_client = weston_pointer_get_pointer_client(pointer, client);
- assert(pointer_client);
-
- wl_list_remove(wl_resource_get_link(resource));
- weston_pointer_cleanup_pointer_client(pointer, pointer_client);
-}
-
-static void unbind_resource(struct wl_resource *resource)
-{
- wl_list_remove(wl_resource_get_link(resource));
-}
-
-WL_EXPORT void
-weston_seat_repick(struct weston_seat *seat)
-{
- const struct weston_pointer *pointer = weston_seat_get_pointer(seat);
-
- if (!pointer)
- return;
-
- pointer->grab->interface->focus(pointer->grab);
-}
-
-static void
-weston_compositor_idle_inhibit(struct weston_compositor *compositor)
-{
- weston_compositor_wake(compositor);
- compositor->idle_inhibit++;
-}
-
-static void
-weston_compositor_idle_release(struct weston_compositor *compositor)
-{
- compositor->idle_inhibit--;
- weston_compositor_wake(compositor);
-}
-
-static void
-pointer_focus_view_destroyed(struct wl_listener *listener, void *data)
-{
- struct weston_pointer *pointer =
- container_of(listener, struct weston_pointer,
- focus_view_listener);
-
- weston_pointer_clear_focus(pointer);
-}
-
-static void
-pointer_focus_resource_destroyed(struct wl_listener *listener, void *data)
-{
- struct weston_pointer *pointer =
- container_of(listener, struct weston_pointer,
- focus_resource_listener);
-
- weston_pointer_clear_focus(pointer);
-}
-
-static void
-keyboard_focus_resource_destroyed(struct wl_listener *listener, void *data)
-{
- struct weston_keyboard *keyboard =
- container_of(listener, struct weston_keyboard,
- focus_resource_listener);
-
- weston_keyboard_set_focus(keyboard, NULL);
-}
-
-static void
-touch_focus_view_destroyed(struct wl_listener *listener, void *data)
-{
- struct weston_touch *touch =
- container_of(listener, struct weston_touch,
- focus_view_listener);
-
- weston_touch_set_focus(touch, NULL);
-}
-
-static void
-touch_focus_resource_destroyed(struct wl_listener *listener, void *data)
-{
- struct weston_touch *touch =
- container_of(listener, struct weston_touch,
- focus_resource_listener);
-
- weston_touch_set_focus(touch, NULL);
-}
-
-static void
-move_resources(struct wl_list *destination, struct wl_list *source)
-{
- wl_list_insert_list(destination, source);
- wl_list_init(source);
-}
-
-static void
-move_resources_for_client(struct wl_list *destination,
- struct wl_list *source,
- struct wl_client *client)
-{
- struct wl_resource *resource, *tmp;
- wl_resource_for_each_safe(resource, tmp, source) {
- if (wl_resource_get_client(resource) == client) {
- wl_list_remove(wl_resource_get_link(resource));
- wl_list_insert(destination,
- wl_resource_get_link(resource));
- }
- }
-}
-
-static void
-default_grab_pointer_focus(struct weston_pointer_grab *grab)
-{
- struct weston_pointer *pointer = grab->pointer;
- struct weston_view *view;
- wl_fixed_t sx, sy;
-
- if (pointer->button_count > 0)
- return;
-
- view = weston_compositor_pick_view(pointer->seat->compositor,
- pointer->x, pointer->y,
- &sx, &sy);
-
- if (pointer->focus != view || pointer->sx != sx || pointer->sy != sy)
- weston_pointer_set_focus(pointer, view, sx, sy);
-}
-
-static void
-default_grab_pointer_motion(struct weston_pointer_grab *grab, uint32_t time,
- struct weston_pointer_motion_event *event)
-{
- struct weston_pointer *pointer = grab->pointer;
- struct wl_list *resource_list;
- struct wl_resource *resource;
- wl_fixed_t x, y;
- wl_fixed_t old_sx = pointer->sx;
- wl_fixed_t old_sy = pointer->sy;
-
- if (pointer->focus) {
- weston_pointer_motion_to_abs(pointer, event, &x, &y);
- weston_view_from_global_fixed(pointer->focus, x, y,
- &pointer->sx, &pointer->sy);
- }
-
- weston_pointer_move(pointer, event);
-
- if (pointer->focus_client &&
- (old_sx != pointer->sx || old_sy != pointer->sy)) {
- resource_list = &pointer->focus_client->pointer_resources;
- wl_resource_for_each(resource, resource_list) {
- wl_pointer_send_motion(resource, time,
- pointer->sx, pointer->sy);
- }
- }
-}
-
-static void
-default_grab_pointer_button(struct weston_pointer_grab *grab,
- uint32_t time, uint32_t button, uint32_t state_w)
-{
- struct weston_pointer *pointer = grab->pointer;
- struct weston_compositor *compositor = pointer->seat->compositor;
- struct weston_view *view;
- struct wl_resource *resource;
- uint32_t serial;
- enum wl_pointer_button_state state = state_w;
- struct wl_display *display = compositor->wl_display;
- wl_fixed_t sx, sy;
- struct wl_list *resource_list = NULL;
-
- if (pointer->focus_client)
- resource_list = &pointer->focus_client->pointer_resources;
- if (resource_list && !wl_list_empty(resource_list)) {
- resource_list = &pointer->focus_client->pointer_resources;
- serial = wl_display_next_serial(display);
- wl_resource_for_each(resource, resource_list)
- wl_pointer_send_button(resource,
- serial,
- time,
- button,
- state_w);
- }
-
- if (pointer->button_count == 0 &&
- state == WL_POINTER_BUTTON_STATE_RELEASED) {
- view = weston_compositor_pick_view(compositor,
- pointer->x, pointer->y,
- &sx, &sy);
-
- weston_pointer_set_focus(pointer, view, sx, sy);
- }
-}
-
-/** Send wl_pointer.axis events to focused resources.
- *
- * \param pointer The pointer where the axis events originates from.
- * \param time The timestamp of the event
- * \param axis The axis enum value of the event
- * \param value The axis value of the event
- *
- * For every resource that is currently in focus, send a wl_pointer.axis event
- * with the passed parameters. The focused resources are the wl_pointer
- * resources of the client which currently has the surface with pointer focus.
- */
-WL_EXPORT void
-weston_pointer_send_axis(struct weston_pointer *pointer,
- uint32_t time,
- struct weston_pointer_axis_event *event)
-{
- struct wl_resource *resource;
- struct wl_list *resource_list;
-
- if (!pointer->focus_client)
- return;
-
- resource_list = &pointer->focus_client->pointer_resources;
- wl_resource_for_each(resource, resource_list) {
- if (event->has_discrete &&
- wl_resource_get_version(resource) >=
- WL_POINTER_AXIS_DISCRETE_SINCE_VERSION)
- wl_pointer_send_axis_discrete(resource, event->axis,
- event->discrete);
-
- if (event->value)
- wl_pointer_send_axis(resource, time,
- event->axis,
- wl_fixed_from_double(event->value));
- else if (wl_resource_get_version(resource) >=
- WL_POINTER_AXIS_STOP_SINCE_VERSION)
- wl_pointer_send_axis_stop(resource, time,
- event->axis);
- }
-}
-
-WL_EXPORT void
-weston_pointer_send_axis_source(struct weston_pointer *pointer, uint32_t source)
-{
- struct wl_resource *resource;
- struct wl_list *resource_list;
-
- if (!pointer->focus_client)
- return;
-
- resource_list = &pointer->focus_client->pointer_resources;
- wl_resource_for_each(resource, resource_list) {
- if (wl_resource_get_version(resource) >=
- WL_POINTER_AXIS_SOURCE_SINCE_VERSION) {
- wl_pointer_send_axis_source(resource, source);
- }
- }
-}
-
-static void
-pointer_send_frame(struct wl_resource *resource)
-{
- if (wl_resource_get_version(resource) >=
- WL_POINTER_FRAME_SINCE_VERSION) {
- wl_pointer_send_frame(resource);
- }
-}
-
-WL_EXPORT void
-weston_pointer_send_frame(struct weston_pointer *pointer)
-{
- struct wl_resource *resource;
- struct wl_list *resource_list;
-
- if (!pointer->focus_client)
- return;
-
- resource_list = &pointer->focus_client->pointer_resources;
- wl_resource_for_each(resource, resource_list)
- pointer_send_frame(resource);
-}
-
-static void
-default_grab_pointer_axis(struct weston_pointer_grab *grab,
- uint32_t time,
- struct weston_pointer_axis_event *event)
-{
- weston_pointer_send_axis(grab->pointer, time, event);
-}
-
-static void
-default_grab_pointer_axis_source(struct weston_pointer_grab *grab,
- uint32_t source)
-{
- weston_pointer_send_axis_source(grab->pointer, source);
-}
-
-static void
-default_grab_pointer_frame(struct weston_pointer_grab *grab)
-{
- weston_pointer_send_frame(grab->pointer);
-}
-
-static void
-default_grab_pointer_cancel(struct weston_pointer_grab *grab)
-{
-}
-
-static const struct weston_pointer_grab_interface
- default_pointer_grab_interface = {
- default_grab_pointer_focus,
- default_grab_pointer_motion,
- default_grab_pointer_button,
- default_grab_pointer_axis,
- default_grab_pointer_axis_source,
- default_grab_pointer_frame,
- default_grab_pointer_cancel,
-};
-
-static void
-default_grab_touch_down(struct weston_touch_grab *grab, uint32_t time,
- int touch_id, wl_fixed_t x, wl_fixed_t y)
-{
- struct weston_touch *touch = grab->touch;
- struct wl_display *display = touch->seat->compositor->wl_display;
- uint32_t serial;
- struct wl_resource *resource;
- struct wl_list *resource_list;
- wl_fixed_t sx, sy;
-
- if (!touch->focus)
- return;
-
- weston_view_from_global_fixed(touch->focus, x, y, &sx, &sy);
-
- resource_list = &touch->focus_resource_list;
-
- if (!wl_list_empty(resource_list)) {
- serial = wl_display_next_serial(display);
- wl_resource_for_each(resource, resource_list)
- wl_touch_send_down(resource, serial, time,
- touch->focus->surface->resource,
- touch_id, sx, sy);
- }
-}
-
-static void
-default_grab_touch_up(struct weston_touch_grab *grab,
- uint32_t time, int touch_id)
-{
- struct weston_touch *touch = grab->touch;
- struct wl_display *display = touch->seat->compositor->wl_display;
- uint32_t serial;
- struct wl_resource *resource;
- struct wl_list *resource_list;
-
- resource_list = &touch->focus_resource_list;
-
- if (!wl_list_empty(resource_list)) {
- serial = wl_display_next_serial(display);
- wl_resource_for_each(resource, resource_list)
- wl_touch_send_up(resource, serial, time, touch_id);
- }
-}
-
-static void
-default_grab_touch_motion(struct weston_touch_grab *grab, uint32_t time,
- int touch_id, wl_fixed_t x, wl_fixed_t y)
-{
- struct weston_touch *touch = grab->touch;
- struct wl_resource *resource;
- struct wl_list *resource_list;
- wl_fixed_t sx, sy;
-
- weston_view_from_global_fixed(touch->focus, x, y, &sx, &sy);
-
- resource_list = &touch->focus_resource_list;
-
- wl_resource_for_each(resource, resource_list) {
- wl_touch_send_motion(resource, time,
- touch_id, sx, sy);
- }
-}
-
-static void
-default_grab_touch_frame(struct weston_touch_grab *grab)
-{
- struct wl_resource *resource;
-
- wl_resource_for_each(resource, &grab->touch->focus_resource_list)
- wl_touch_send_frame(resource);
-}
-
-static void
-default_grab_touch_cancel(struct weston_touch_grab *grab)
-{
-}
-
-static const struct weston_touch_grab_interface default_touch_grab_interface = {
- default_grab_touch_down,
- default_grab_touch_up,
- default_grab_touch_motion,
- default_grab_touch_frame,
- default_grab_touch_cancel,
-};
-
-static void
-default_grab_keyboard_key(struct weston_keyboard_grab *grab,
- uint32_t time, uint32_t key, uint32_t state)
-{
- struct weston_keyboard *keyboard = grab->keyboard;
- struct wl_resource *resource;
- struct wl_display *display = keyboard->seat->compositor->wl_display;
- uint32_t serial;
- struct wl_list *resource_list;
-
- resource_list = &keyboard->focus_resource_list;
- if (!wl_list_empty(resource_list)) {
- serial = wl_display_next_serial(display);
- wl_resource_for_each(resource, resource_list)
- wl_keyboard_send_key(resource,
- serial,
- time,
- key,
- state);
- }
-}
-
-static void
-send_modifiers_to_resource(struct weston_keyboard *keyboard,
- struct wl_resource *resource,
- uint32_t serial)
-{
- wl_keyboard_send_modifiers(resource,
- serial,
- keyboard->modifiers.mods_depressed,
- keyboard->modifiers.mods_latched,
- keyboard->modifiers.mods_locked,
- keyboard->modifiers.group);
-}
-
-static void
-send_modifiers_to_client_in_list(struct wl_client *client,
- struct wl_list *list,
- uint32_t serial,
- struct weston_keyboard *keyboard)
-{
- struct wl_resource *resource;
-
- wl_resource_for_each(resource, list) {
- if (wl_resource_get_client(resource) == client)
- send_modifiers_to_resource(keyboard,
- resource,
- serial);
- }
-}
-
-static struct weston_pointer_client *
-find_pointer_client_for_surface(struct weston_pointer *pointer,
- struct weston_surface *surface)
-{
- struct wl_client *client;
-
- if (!surface)
- return NULL;
-
- if (!surface->resource)
- return NULL;
-
- client = wl_resource_get_client(surface->resource);
- return weston_pointer_get_pointer_client(pointer, client);
-}
-
-static struct weston_pointer_client *
-find_pointer_client_for_view(struct weston_pointer *pointer, struct weston_view *view)
-{
- if (!view)
- return NULL;
-
- return find_pointer_client_for_surface(pointer, view->surface);
-}
-
-static struct wl_resource *
-find_resource_for_surface(struct wl_list *list, struct weston_surface *surface)
-{
- if (!surface)
- return NULL;
-
- if (!surface->resource)
- return NULL;
-
- return wl_resource_find_for_client(list, wl_resource_get_client(surface->resource));
-}
-
-static void
-default_grab_keyboard_modifiers(struct weston_keyboard_grab *grab,
- uint32_t serial, uint32_t mods_depressed,
- uint32_t mods_latched,
- uint32_t mods_locked, uint32_t group)
-{
- struct weston_keyboard *keyboard = grab->keyboard;
- struct weston_pointer *pointer =
- weston_seat_get_pointer(grab->keyboard->seat);
- struct wl_resource *resource;
- struct wl_list *resource_list;
-
- resource_list = &keyboard->focus_resource_list;
-
- wl_resource_for_each(resource, resource_list) {
- wl_keyboard_send_modifiers(resource, serial, mods_depressed,
- mods_latched, mods_locked, group);
- }
- if (pointer && pointer->focus && pointer->focus->surface->resource &&
- pointer->focus->surface != keyboard->focus) {
- struct wl_client *pointer_client =
- wl_resource_get_client(pointer->focus->surface->resource);
- send_modifiers_to_client_in_list(pointer_client,
- &keyboard->resource_list,
- serial,
- keyboard);
- }
-}
-
-static void
-default_grab_keyboard_cancel(struct weston_keyboard_grab *grab)
-{
-}
-
-static const struct weston_keyboard_grab_interface
- default_keyboard_grab_interface = {
- default_grab_keyboard_key,
- default_grab_keyboard_modifiers,
- default_grab_keyboard_cancel,
-};
-
-static void
-pointer_unmap_sprite(struct weston_pointer *pointer)
-{
- struct weston_surface *surface = pointer->sprite->surface;
-
- if (weston_surface_is_mapped(surface))
- weston_surface_unmap(surface);
-
- wl_list_remove(&pointer->sprite_destroy_listener.link);
- surface->configure = NULL;
- surface->configure_private = NULL;
- weston_surface_set_label_func(surface, NULL);
- weston_view_destroy(pointer->sprite);
- pointer->sprite = NULL;
-}
-
-static void
-pointer_handle_sprite_destroy(struct wl_listener *listener, void *data)
-{
- struct weston_pointer *pointer =
- container_of(listener, struct weston_pointer,
- sprite_destroy_listener);
-
- pointer->sprite = NULL;
-}
-
-static void
-weston_pointer_reset_state(struct weston_pointer *pointer)
-{
- pointer->button_count = 0;
-}
-
-static void
-weston_pointer_handle_output_destroy(struct wl_listener *listener, void *data);
-
-WL_EXPORT struct weston_pointer *
-weston_pointer_create(struct weston_seat *seat)
-{
- struct weston_pointer *pointer;
-
- pointer = zalloc(sizeof *pointer);
- if (pointer == NULL)
- return NULL;
-
- wl_list_init(&pointer->pointer_clients);
- weston_pointer_set_default_grab(pointer,
- seat->compositor->default_pointer_grab);
- wl_list_init(&pointer->focus_resource_listener.link);
- pointer->focus_resource_listener.notify = pointer_focus_resource_destroyed;
- pointer->default_grab.pointer = pointer;
- pointer->grab = &pointer->default_grab;
- wl_signal_init(&pointer->motion_signal);
- wl_signal_init(&pointer->focus_signal);
- wl_list_init(&pointer->focus_view_listener.link);
-
- pointer->sprite_destroy_listener.notify = pointer_handle_sprite_destroy;
-
- /* FIXME: Pick better co-ords. */
- pointer->x = wl_fixed_from_int(100);
- pointer->y = wl_fixed_from_int(100);
-
- pointer->output_destroy_listener.notify =
- weston_pointer_handle_output_destroy;
- wl_signal_add(&seat->compositor->output_destroyed_signal,
- &pointer->output_destroy_listener);
-
- pointer->sx = wl_fixed_from_int(-1000000);
- pointer->sy = wl_fixed_from_int(-1000000);
-
- return pointer;
-}
-
-WL_EXPORT void
-weston_pointer_destroy(struct weston_pointer *pointer)
-{
- if (pointer->sprite)
- pointer_unmap_sprite(pointer);
-
- /* XXX: What about pointer->resource_list? */
-
- wl_list_remove(&pointer->focus_resource_listener.link);
- wl_list_remove(&pointer->focus_view_listener.link);
- wl_list_remove(&pointer->output_destroy_listener.link);
- free(pointer);
-}
-
-void
-weston_pointer_set_default_grab(struct weston_pointer *pointer,
- const struct weston_pointer_grab_interface *interface)
-{
- if (interface)
- pointer->default_grab.interface = interface;
- else
- pointer->default_grab.interface =
- &default_pointer_grab_interface;
-}
-
-WL_EXPORT struct weston_keyboard *
-weston_keyboard_create(void)
-{
- struct weston_keyboard *keyboard;
-
- keyboard = zalloc(sizeof *keyboard);
- if (keyboard == NULL)
- return NULL;
-
- wl_list_init(&keyboard->resource_list);
- wl_list_init(&keyboard->focus_resource_list);
- wl_list_init(&keyboard->focus_resource_listener.link);
- keyboard->focus_resource_listener.notify = keyboard_focus_resource_destroyed;
- wl_array_init(&keyboard->keys);
- keyboard->default_grab.interface = &default_keyboard_grab_interface;
- keyboard->default_grab.keyboard = keyboard;
- keyboard->grab = &keyboard->default_grab;
- wl_signal_init(&keyboard->focus_signal);
-
- return keyboard;
-}
-
-static void
-weston_xkb_info_destroy(struct weston_xkb_info *xkb_info);
-
-WL_EXPORT void
-weston_keyboard_destroy(struct weston_keyboard *keyboard)
-{
- /* XXX: What about keyboard->resource_list? */
-
-#ifdef ENABLE_XKBCOMMON
- if (keyboard->seat->compositor->use_xkbcommon) {
- xkb_state_unref(keyboard->xkb_state.state);
- if (keyboard->xkb_info)
- weston_xkb_info_destroy(keyboard->xkb_info);
- xkb_keymap_unref(keyboard->pending_keymap);
- }
-#endif
-
- wl_array_release(&keyboard->keys);
- wl_list_remove(&keyboard->focus_resource_listener.link);
- free(keyboard);
-}
-
-static void
-weston_touch_reset_state(struct weston_touch *touch)
-{
- touch->num_tp = 0;
-}
-
-WL_EXPORT struct weston_touch *
-weston_touch_create(void)
-{
- struct weston_touch *touch;
-
- touch = zalloc(sizeof *touch);
- if (touch == NULL)
- return NULL;
-
- wl_list_init(&touch->resource_list);
- wl_list_init(&touch->focus_resource_list);
- wl_list_init(&touch->focus_view_listener.link);
- touch->focus_view_listener.notify = touch_focus_view_destroyed;
- wl_list_init(&touch->focus_resource_listener.link);
- touch->focus_resource_listener.notify = touch_focus_resource_destroyed;
- touch->default_grab.interface = &default_touch_grab_interface;
- touch->default_grab.touch = touch;
- touch->grab = &touch->default_grab;
- wl_signal_init(&touch->focus_signal);
-
- return touch;
-}
-
-WL_EXPORT void
-weston_touch_destroy(struct weston_touch *touch)
-{
- /* XXX: What about touch->resource_list? */
-
- wl_list_remove(&touch->focus_view_listener.link);
- wl_list_remove(&touch->focus_resource_listener.link);
- free(touch);
-}
-
-static void
-seat_send_updated_caps(struct weston_seat *seat)
-{
- enum wl_seat_capability caps = 0;
- struct wl_resource *resource;
-
- if (seat->pointer_device_count > 0)
- caps |= WL_SEAT_CAPABILITY_POINTER;
- if (seat->keyboard_device_count > 0)
- caps |= WL_SEAT_CAPABILITY_KEYBOARD;
- if (seat->touch_device_count > 0)
- caps |= WL_SEAT_CAPABILITY_TOUCH;
-
- wl_resource_for_each(resource, &seat->base_resource_list) {
- wl_seat_send_capabilities(resource, caps);
- }
- wl_signal_emit(&seat->updated_caps_signal, seat);
-}
-
-
-/** Clear the pointer focus
- *
- * \param pointer the pointer to clear focus for.
- *
- * This can be used to unset pointer focus and set the co-ordinates to the
- * arbitrary values we use for the no focus case.
- *
- * There's no requirement to use this function. For example, passing the
- * results of a weston_compositor_pick_view() directly to
- * weston_pointer_set_focus() will do the right thing when no view is found.
- */
-WL_EXPORT void
-weston_pointer_clear_focus(struct weston_pointer *pointer)
-{
- weston_pointer_set_focus(pointer, NULL,
- wl_fixed_from_int(-1000000),
- wl_fixed_from_int(-1000000));
-}
-
-WL_EXPORT void
-weston_pointer_set_focus(struct weston_pointer *pointer,
- struct weston_view *view,
- wl_fixed_t sx, wl_fixed_t sy)
-{
- struct weston_pointer_client *pointer_client;
- struct weston_keyboard *kbd = weston_seat_get_keyboard(pointer->seat);
- struct wl_resource *resource;
- struct wl_resource *surface_resource;
- struct wl_display *display = pointer->seat->compositor->wl_display;
- uint32_t serial;
- struct wl_list *focus_resource_list;
- int refocus = 0;
-
- if ((!pointer->focus && view) ||
- (pointer->focus && !view) ||
- (pointer->focus && pointer->focus->surface != view->surface) ||
- pointer->sx != sx || pointer->sy != sy)
- refocus = 1;
-
- if (pointer->focus_client && refocus) {
- focus_resource_list = &pointer->focus_client->pointer_resources;
- if (!wl_list_empty(focus_resource_list)) {
- serial = wl_display_next_serial(display);
- surface_resource = pointer->focus->surface->resource;
- wl_resource_for_each(resource, focus_resource_list) {
- wl_pointer_send_leave(resource, serial,
- surface_resource);
- pointer_send_frame(resource);
- }
- }
-
- pointer->focus_client = NULL;
- }
-
- pointer_client = find_pointer_client_for_view(pointer, view);
- if (pointer_client && refocus) {
- struct wl_client *surface_client = pointer_client->client;
-
- serial = wl_display_next_serial(display);
-
- if (kbd && kbd->focus != view->surface)
- send_modifiers_to_client_in_list(surface_client,
- &kbd->resource_list,
- serial,
- kbd);
-
- pointer->focus_client = pointer_client;
-
- focus_resource_list = &pointer->focus_client->pointer_resources;
- wl_resource_for_each(resource, focus_resource_list) {
- wl_pointer_send_enter(resource,
- serial,
- view->surface->resource,
- sx, sy);
- pointer_send_frame(resource);
- }
-
- pointer->focus_serial = serial;
- }
-
- wl_list_remove(&pointer->focus_view_listener.link);
- wl_list_init(&pointer->focus_view_listener.link);
- wl_list_remove(&pointer->focus_resource_listener.link);
- wl_list_init(&pointer->focus_resource_listener.link);
- if (view)
- wl_signal_add(&view->destroy_signal, &pointer->focus_view_listener);
- if (view && view->surface->resource)
- wl_resource_add_destroy_listener(view->surface->resource,
- &pointer->focus_resource_listener);
-
- pointer->focus = view;
- pointer->focus_view_listener.notify = pointer_focus_view_destroyed;
- pointer->sx = sx;
- pointer->sy = sy;
-
- assert(view || sx == wl_fixed_from_int(-1000000));
- assert(view || sy == wl_fixed_from_int(-1000000));
-
- wl_signal_emit(&pointer->focus_signal, pointer);
-}
-
-static void
-send_enter_to_resource_list(struct wl_list *list,
- struct weston_keyboard *keyboard,
- struct weston_surface *surface,
- uint32_t serial)
-{
- struct wl_resource *resource;
-
- wl_resource_for_each(resource, list) {
- send_modifiers_to_resource(keyboard, resource, serial);
- wl_keyboard_send_enter(resource, serial,
- surface->resource,
- &keyboard->keys);
- }
-}
-
-WL_EXPORT void
-weston_keyboard_set_focus(struct weston_keyboard *keyboard,
- struct weston_surface *surface)
-{
- struct wl_resource *resource;
- struct wl_display *display = keyboard->seat->compositor->wl_display;
- uint32_t serial;
- struct wl_list *focus_resource_list;
-
- focus_resource_list = &keyboard->focus_resource_list;
-
- if (!wl_list_empty(focus_resource_list) && keyboard->focus != surface) {
- serial = wl_display_next_serial(display);
- wl_resource_for_each(resource, focus_resource_list) {
- wl_keyboard_send_leave(resource, serial,
- keyboard->focus->resource);
- }
- move_resources(&keyboard->resource_list, focus_resource_list);
- }
-
- if (find_resource_for_surface(&keyboard->resource_list, surface) &&
- keyboard->focus != surface) {
- struct wl_client *surface_client =
- wl_resource_get_client(surface->resource);
-
- serial = wl_display_next_serial(display);
-
- move_resources_for_client(focus_resource_list,
- &keyboard->resource_list,
- surface_client);
- send_enter_to_resource_list(focus_resource_list,
- keyboard,
- surface,
- serial);
- keyboard->focus_serial = serial;
- }
-
- wl_list_remove(&keyboard->focus_resource_listener.link);
- wl_list_init(&keyboard->focus_resource_listener.link);
- if (surface && surface->resource)
- wl_resource_add_destroy_listener(surface->resource,
- &keyboard->focus_resource_listener);
-
- keyboard->focus = surface;
- wl_signal_emit(&keyboard->focus_signal, keyboard);
-}
-
-/* Users of this function must manually manage the keyboard focus */
-WL_EXPORT void
-weston_keyboard_start_grab(struct weston_keyboard *keyboard,
- struct weston_keyboard_grab *grab)
-{
- keyboard->grab = grab;
- grab->keyboard = keyboard;
-}
-
-WL_EXPORT void
-weston_keyboard_end_grab(struct weston_keyboard *keyboard)
-{
- keyboard->grab = &keyboard->default_grab;
-}
-
-static void
-weston_keyboard_cancel_grab(struct weston_keyboard *keyboard)
-{
- keyboard->grab->interface->cancel(keyboard->grab);
-}
-
-WL_EXPORT void
-weston_pointer_start_grab(struct weston_pointer *pointer,
- struct weston_pointer_grab *grab)
-{
- pointer->grab = grab;
- grab->pointer = pointer;
- pointer->grab->interface->focus(pointer->grab);
-}
-
-WL_EXPORT void
-weston_pointer_end_grab(struct weston_pointer *pointer)
-{
- pointer->grab = &pointer->default_grab;
- pointer->grab->interface->focus(pointer->grab);
-}
-
-static void
-weston_pointer_cancel_grab(struct weston_pointer *pointer)
-{
- pointer->grab->interface->cancel(pointer->grab);
-}
-
-WL_EXPORT void
-weston_touch_start_grab(struct weston_touch *touch, struct weston_touch_grab *grab)
-{
- touch->grab = grab;
- grab->touch = touch;
-}
-
-WL_EXPORT void
-weston_touch_end_grab(struct weston_touch *touch)
-{
- touch->grab = &touch->default_grab;
-}
-
-static void
-weston_touch_cancel_grab(struct weston_touch *touch)
-{
- touch->grab->interface->cancel(touch->grab);
-}
-
-static void
-weston_pointer_clamp_for_output(struct weston_pointer *pointer,
- struct weston_output *output,
- wl_fixed_t *fx, wl_fixed_t *fy)
-{
- int x, y;
-
- x = wl_fixed_to_int(*fx);
- y = wl_fixed_to_int(*fy);
-
- if (x < output->x)
- *fx = wl_fixed_from_int(output->x);
- else if (x >= output->x + output->width)
- *fx = wl_fixed_from_int(output->x +
- output->width - 1);
- if (y < output->y)
- *fy = wl_fixed_from_int(output->y);
- else if (y >= output->y + output->height)
- *fy = wl_fixed_from_int(output->y +
- output->height - 1);
-}
-
-WL_EXPORT void
-weston_pointer_clamp(struct weston_pointer *pointer, wl_fixed_t *fx, wl_fixed_t *fy)
-{
- struct weston_compositor *ec = pointer->seat->compositor;
- struct weston_output *output, *prev = NULL;
- int x, y, old_x, old_y, valid = 0;
-
- x = wl_fixed_to_int(*fx);
- y = wl_fixed_to_int(*fy);
- old_x = wl_fixed_to_int(pointer->x);
- old_y = wl_fixed_to_int(pointer->y);
-
- wl_list_for_each(output, &ec->output_list, link) {
- if (pointer->seat->output && pointer->seat->output != output)
- continue;
- if (pixman_region32_contains_point(&output->region,
- x, y, NULL))
- valid = 1;
- if (pixman_region32_contains_point(&output->region,
- old_x, old_y, NULL))
- prev = output;
- }
-
- if (!prev)
- prev = pointer->seat->output;
-
- if (prev && !valid)
- weston_pointer_clamp_for_output(pointer, prev, fx, fy);
-}
-
-static void
-weston_pointer_move_to(struct weston_pointer *pointer,
- wl_fixed_t x, wl_fixed_t y)
-{
- int32_t ix, iy;
-
- weston_pointer_clamp (pointer, &x, &y);
-
- pointer->x = x;
- pointer->y = y;
-
- ix = wl_fixed_to_int(x);
- iy = wl_fixed_to_int(y);
-
- if (pointer->sprite) {
- weston_view_set_position(pointer->sprite,
- ix - pointer->hotspot_x,
- iy - pointer->hotspot_y);
- weston_view_schedule_repaint(pointer->sprite);
- }
-
- pointer->grab->interface->focus(pointer->grab);
- wl_signal_emit(&pointer->motion_signal, pointer);
-}
-
-WL_EXPORT void
-weston_pointer_motion_to_abs(struct weston_pointer *pointer,
- struct weston_pointer_motion_event *event,
- wl_fixed_t *x, wl_fixed_t *y)
-{
- if (event->mask & WESTON_POINTER_MOTION_ABS) {
- *x = wl_fixed_from_double(event->x);
- *y = wl_fixed_from_double(event->y);
- } else if (event->mask & WESTON_POINTER_MOTION_REL) {
- *x = pointer->x + wl_fixed_from_double(event->dx);
- *y = pointer->y + wl_fixed_from_double(event->dy);
- } else {
- assert(!"invalid motion event");
- *x = *y = 0;
- }
-}
-
-WL_EXPORT void
-weston_pointer_move(struct weston_pointer *pointer,
- struct weston_pointer_motion_event *event)
-{
- wl_fixed_t x, y;
-
- weston_pointer_motion_to_abs(pointer, event, &x, &y);
- weston_pointer_move_to(pointer, x, y);
-}
-
-/** Verify if the pointer is in a valid position and move it if it isn't.
- */
-static void
-weston_pointer_handle_output_destroy(struct wl_listener *listener, void *data)
-{
- struct weston_pointer *pointer;
- struct weston_compositor *ec;
- struct weston_output *output, *closest = NULL;
- int x, y, distance, min = INT_MAX;
- wl_fixed_t fx, fy;
-
- pointer = container_of(listener, struct weston_pointer,
- output_destroy_listener);
- ec = pointer->seat->compositor;
-
- x = wl_fixed_to_int(pointer->x);
- y = wl_fixed_to_int(pointer->y);
-
- wl_list_for_each(output, &ec->output_list, link) {
- if (pixman_region32_contains_point(&output->region,
- x, y, NULL))
- return;
-
- /* Aproximante the distance from the pointer to the center of
- * the output. */
- distance = abs(output->x + output->width / 2 - x) +
- abs(output->y + output->height / 2 - y);
- if (distance < min) {
- min = distance;
- closest = output;
- }
- }
-
- /* Nothing to do if there's no output left. */
- if (!closest)
- return;
-
- fx = pointer->x;
- fy = pointer->y;
-
- weston_pointer_clamp_for_output(pointer, closest, &fx, &fy);
- weston_pointer_move_to(pointer, fx, fy);
-}
-
-WL_EXPORT void
-notify_motion(struct weston_seat *seat,
- uint32_t time,
- struct weston_pointer_motion_event *event)
-{
- struct weston_compositor *ec = seat->compositor;
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
-
- weston_compositor_wake(ec);
- pointer->grab->interface->motion(pointer->grab, time, event);
-}
-
-static void
-run_modifier_bindings(struct weston_seat *seat, uint32_t old, uint32_t new)
-{
- struct weston_compositor *compositor = seat->compositor;
- struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
- uint32_t diff;
- unsigned int i;
- struct {
- uint32_t xkb;
- enum weston_keyboard_modifier weston;
- } mods[] = {
- { keyboard->xkb_info->ctrl_mod, MODIFIER_CTRL },
- { keyboard->xkb_info->alt_mod, MODIFIER_ALT },
- { keyboard->xkb_info->super_mod, MODIFIER_SUPER },
- { keyboard->xkb_info->shift_mod, MODIFIER_SHIFT },
- };
-
- diff = new & ~old;
- for (i = 0; i < ARRAY_LENGTH(mods); i++) {
- if (diff & (1 << mods[i].xkb))
- weston_compositor_run_modifier_binding(compositor,
- keyboard,
- mods[i].weston,
- WL_KEYBOARD_KEY_STATE_PRESSED);
- }
-
- diff = old & ~new;
- for (i = 0; i < ARRAY_LENGTH(mods); i++) {
- if (diff & (1 << mods[i].xkb))
- weston_compositor_run_modifier_binding(compositor,
- keyboard,
- mods[i].weston,
- WL_KEYBOARD_KEY_STATE_RELEASED);
- }
-}
-
-WL_EXPORT void
-notify_motion_absolute(struct weston_seat *seat,
- uint32_t time, double x, double y)
-{
- struct weston_compositor *ec = seat->compositor;
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
- struct weston_pointer_motion_event event = { 0 };
-
- weston_compositor_wake(ec);
-
- event = (struct weston_pointer_motion_event) {
- .mask = WESTON_POINTER_MOTION_ABS,
- .x = x,
- .y = y,
- };
-
- pointer->grab->interface->motion(pointer->grab, time, &event);
-}
-
-WL_EXPORT void
-weston_surface_activate(struct weston_surface *surface,
- struct weston_seat *seat)
-{
- struct weston_compositor *compositor = seat->compositor;
- struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
-
- if (keyboard) {
- weston_keyboard_set_focus(keyboard, surface);
- wl_data_device_set_keyboard_focus(seat);
- }
-
- wl_signal_emit(&compositor->activate_signal, surface);
-}
-
-WL_EXPORT void
-notify_button(struct weston_seat *seat, uint32_t time, int32_t button,
- enum wl_pointer_button_state state)
-{
- struct weston_compositor *compositor = seat->compositor;
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
-
- if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
- weston_compositor_idle_inhibit(compositor);
- if (pointer->button_count == 0) {
- pointer->grab_button = button;
- pointer->grab_time = time;
- pointer->grab_x = pointer->x;
- pointer->grab_y = pointer->y;
- }
- pointer->button_count++;
- } else {
- weston_compositor_idle_release(compositor);
- pointer->button_count--;
- }
-
- weston_compositor_run_button_binding(compositor, pointer, time, button,
- state);
-
- pointer->grab->interface->button(pointer->grab, time, button, state);
-
- if (pointer->button_count == 1)
- pointer->grab_serial =
- wl_display_get_serial(compositor->wl_display);
-}
-
-WL_EXPORT void
-notify_axis(struct weston_seat *seat, uint32_t time,
- struct weston_pointer_axis_event *event)
-{
- struct weston_compositor *compositor = seat->compositor;
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
-
- weston_compositor_wake(compositor);
-
- if (weston_compositor_run_axis_binding(compositor, pointer,
- time, event))
- return;
-
- pointer->grab->interface->axis(pointer->grab, time, event);
-}
-
-WL_EXPORT void
-notify_axis_source(struct weston_seat *seat, uint32_t source)
-{
- struct weston_compositor *compositor = seat->compositor;
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
-
- weston_compositor_wake(compositor);
-
- pointer->grab->interface->axis_source(pointer->grab, source);
-}
-
-WL_EXPORT void
-notify_pointer_frame(struct weston_seat *seat)
-{
- struct weston_compositor *compositor = seat->compositor;
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
-
- weston_compositor_wake(compositor);
-
- pointer->grab->interface->frame(pointer->grab);
-}
-
-WL_EXPORT int
-weston_keyboard_set_locks(struct weston_keyboard *keyboard,
- uint32_t mask, uint32_t value)
-{
-#ifdef ENABLE_XKBCOMMON
- uint32_t serial;
- xkb_mod_mask_t mods_depressed, mods_latched, mods_locked, group;
- xkb_mod_mask_t num, caps;
-
- /* We don't want the leds to go out of sync with the actual state
- * so if the backend has no way to change the leds don't try to
- * change the state */
- if (!keyboard->seat->led_update)
- return -1;
-
- mods_depressed = xkb_state_serialize_mods(keyboard->xkb_state.state,
- XKB_STATE_DEPRESSED);
- mods_latched = xkb_state_serialize_mods(keyboard->xkb_state.state,
- XKB_STATE_LATCHED);
- mods_locked = xkb_state_serialize_mods(keyboard->xkb_state.state,
- XKB_STATE_LOCKED);
- group = xkb_state_serialize_group(keyboard->xkb_state.state,
- XKB_STATE_EFFECTIVE);
-
- num = (1 << keyboard->xkb_info->mod2_mod);
- caps = (1 << keyboard->xkb_info->caps_mod);
- if (mask & WESTON_NUM_LOCK) {
- if (value & WESTON_NUM_LOCK)
- mods_locked |= num;
- else
- mods_locked &= ~num;
- }
- if (mask & WESTON_CAPS_LOCK) {
- if (value & WESTON_CAPS_LOCK)
- mods_locked |= caps;
- else
- mods_locked &= ~caps;
- }
-
- xkb_state_update_mask(keyboard->xkb_state.state, mods_depressed,
- mods_latched, mods_locked, 0, 0, group);
-
- serial = wl_display_next_serial(
- keyboard->seat->compositor->wl_display);
- notify_modifiers(keyboard->seat, serial);
-
- return 0;
-#else
- return -1;
-#endif
-}
-
-#ifdef ENABLE_XKBCOMMON
-WL_EXPORT void
-notify_modifiers(struct weston_seat *seat, uint32_t serial)
-{
- struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
- struct weston_keyboard_grab *grab = keyboard->grab;
- uint32_t mods_depressed, mods_latched, mods_locked, group;
- uint32_t mods_lookup;
- enum weston_led leds = 0;
- int changed = 0;
-
- /* Serialize and update our internal state, checking to see if it's
- * different to the previous state. */
- mods_depressed = xkb_state_serialize_mods(keyboard->xkb_state.state,
- XKB_STATE_MODS_DEPRESSED);
- mods_latched = xkb_state_serialize_mods(keyboard->xkb_state.state,
- XKB_STATE_MODS_LATCHED);
- mods_locked = xkb_state_serialize_mods(keyboard->xkb_state.state,
- XKB_STATE_MODS_LOCKED);
- group = xkb_state_serialize_layout(keyboard->xkb_state.state,
- XKB_STATE_LAYOUT_EFFECTIVE);
-
- if (mods_depressed != keyboard->modifiers.mods_depressed ||
- mods_latched != keyboard->modifiers.mods_latched ||
- mods_locked != keyboard->modifiers.mods_locked ||
- group != keyboard->modifiers.group)
- changed = 1;
-
- run_modifier_bindings(seat, keyboard->modifiers.mods_depressed,
- mods_depressed);
-
- keyboard->modifiers.mods_depressed = mods_depressed;
- keyboard->modifiers.mods_latched = mods_latched;
- keyboard->modifiers.mods_locked = mods_locked;
- keyboard->modifiers.group = group;
-
- /* And update the modifier_state for bindings. */
- mods_lookup = mods_depressed | mods_latched;
- seat->modifier_state = 0;
- if (mods_lookup & (1 << keyboard->xkb_info->ctrl_mod))
- seat->modifier_state |= MODIFIER_CTRL;
- if (mods_lookup & (1 << keyboard->xkb_info->alt_mod))
- seat->modifier_state |= MODIFIER_ALT;
- if (mods_lookup & (1 << keyboard->xkb_info->super_mod))
- seat->modifier_state |= MODIFIER_SUPER;
- if (mods_lookup & (1 << keyboard->xkb_info->shift_mod))
- seat->modifier_state |= MODIFIER_SHIFT;
-
- /* Finally, notify the compositor that LEDs have changed. */
- if (xkb_state_led_index_is_active(keyboard->xkb_state.state,
- keyboard->xkb_info->num_led))
- leds |= LED_NUM_LOCK;
- if (xkb_state_led_index_is_active(keyboard->xkb_state.state,
- keyboard->xkb_info->caps_led))
- leds |= LED_CAPS_LOCK;
- if (xkb_state_led_index_is_active(keyboard->xkb_state.state,
- keyboard->xkb_info->scroll_led))
- leds |= LED_SCROLL_LOCK;
- if (leds != keyboard->xkb_state.leds && seat->led_update)
- seat->led_update(seat, leds);
- keyboard->xkb_state.leds = leds;
-
- if (changed) {
- grab->interface->modifiers(grab,
- serial,
- keyboard->modifiers.mods_depressed,
- keyboard->modifiers.mods_latched,
- keyboard->modifiers.mods_locked,
- keyboard->modifiers.group);
- }
-}
-
-static void
-update_modifier_state(struct weston_seat *seat, uint32_t serial, uint32_t key,
- enum wl_keyboard_key_state state)
-{
- struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
- enum xkb_key_direction direction;
-
- /* Keyboard modifiers don't exist in raw keyboard mode */
- if (!seat->compositor->use_xkbcommon)
- return;
-
- if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
- direction = XKB_KEY_DOWN;
- else
- direction = XKB_KEY_UP;
-
- /* Offset the keycode by 8, as the evdev XKB rules reflect X's
- * broken keycode system, which starts at 8. */
- xkb_state_update_key(keyboard->xkb_state.state, key + 8, direction);
-
- notify_modifiers(seat, serial);
-}
-
-static void
-send_keymap(struct wl_resource *resource, struct weston_xkb_info *xkb_info)
-{
- wl_keyboard_send_keymap(resource,
- WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
- xkb_info->keymap_fd,
- xkb_info->keymap_size);
-}
-
-static void
-send_modifiers(struct wl_resource *resource, uint32_t serial, struct weston_keyboard *keyboard)
-{
- wl_keyboard_send_modifiers(resource, serial,
- keyboard->modifiers.mods_depressed,
- keyboard->modifiers.mods_latched,
- keyboard->modifiers.mods_locked,
- keyboard->modifiers.group);
-}
-
-static struct weston_xkb_info *
-weston_xkb_info_create(struct xkb_keymap *keymap);
-
-static void
-update_keymap(struct weston_seat *seat)
-{
- struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
- struct wl_resource *resource;
- struct weston_xkb_info *xkb_info;
- struct xkb_state *state;
- xkb_mod_mask_t latched_mods;
- xkb_mod_mask_t locked_mods;
-
- xkb_info = weston_xkb_info_create(keyboard->pending_keymap);
-
- xkb_keymap_unref(keyboard->pending_keymap);
- keyboard->pending_keymap = NULL;
-
- if (!xkb_info) {
- weston_log("failed to create XKB info\n");
- return;
- }
-
- state = xkb_state_new(xkb_info->keymap);
- if (!state) {
- weston_log("failed to initialise XKB state\n");
- weston_xkb_info_destroy(xkb_info);
- return;
- }
-
- latched_mods = xkb_state_serialize_mods(keyboard->xkb_state.state,
- XKB_STATE_MODS_LATCHED);
- locked_mods = xkb_state_serialize_mods(keyboard->xkb_state.state,
- XKB_STATE_MODS_LOCKED);
- xkb_state_update_mask(state,
- 0, /* depressed */
- latched_mods,
- locked_mods,
- 0, 0, 0);
-
- weston_xkb_info_destroy(keyboard->xkb_info);
- keyboard->xkb_info = xkb_info;
-
- xkb_state_unref(keyboard->xkb_state.state);
- keyboard->xkb_state.state = state;
-
- wl_resource_for_each(resource, &keyboard->resource_list)
- send_keymap(resource, xkb_info);
- wl_resource_for_each(resource, &keyboard->focus_resource_list)
- send_keymap(resource, xkb_info);
-
- notify_modifiers(seat, wl_display_next_serial(seat->compositor->wl_display));
-
- if (!latched_mods && !locked_mods)
- return;
-
- wl_resource_for_each(resource, &keyboard->resource_list)
- send_modifiers(resource, wl_display_get_serial(seat->compositor->wl_display), keyboard);
- wl_resource_for_each(resource, &keyboard->focus_resource_list)
- send_modifiers(resource, wl_display_get_serial(seat->compositor->wl_display), keyboard);
-}
-#else
-WL_EXPORT void
-notify_modifiers(struct weston_seat *seat, uint32_t serial)
-{
-}
-
-static void
-update_modifier_state(struct weston_seat *seat, uint32_t serial, uint32_t key,
- enum wl_keyboard_key_state state)
-{
-}
-
-static void
-update_keymap(struct weston_seat *seat)
-{
-}
-#endif
-
-WL_EXPORT void
-notify_key(struct weston_seat *seat, uint32_t time, uint32_t key,
- enum wl_keyboard_key_state state,
- enum weston_key_state_update update_state)
-{
- struct weston_compositor *compositor = seat->compositor;
- struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
- struct weston_keyboard_grab *grab = keyboard->grab;
- uint32_t *k, *end;
-
- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
- weston_compositor_idle_inhibit(compositor);
- } else {
- weston_compositor_idle_release(compositor);
- }
-
- end = keyboard->keys.data + keyboard->keys.size;
- for (k = keyboard->keys.data; k < end; k++) {
- if (*k == key) {
- /* Ignore server-generated repeats. */
- if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
- return;
- *k = *--end;
- }
- }
- keyboard->keys.size = (void *) end - keyboard->keys.data;
- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
- k = wl_array_add(&keyboard->keys, sizeof *k);
- *k = key;
- }
-
- if (grab == &keyboard->default_grab ||
- grab == &keyboard->input_method_grab) {
- weston_compositor_run_key_binding(compositor, keyboard, time,
- key, state);
- grab = keyboard->grab;
- }
-
- grab->interface->key(grab, time, key, state);
-
- if (keyboard->pending_keymap &&
- keyboard->keys.size == 0)
- update_keymap(seat);
-
- if (update_state == STATE_UPDATE_AUTOMATIC) {
- update_modifier_state(seat,
- wl_display_get_serial(compositor->wl_display),
- key,
- state);
- }
-
- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
- keyboard->grab_serial =
- wl_display_get_serial(compositor->wl_display);
- keyboard->grab_time = time;
- keyboard->grab_key = key;
- }
-}
-
-WL_EXPORT void
-notify_pointer_focus(struct weston_seat *seat, struct weston_output *output,
- double x, double y)
-{
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
-
- if (output) {
- weston_pointer_move_to(pointer,
- wl_fixed_from_double(x),
- wl_fixed_from_double(y));
- } else {
- /* FIXME: We should call weston_pointer_set_focus(seat,
- * NULL) here, but somehow that breaks re-entry... */
- }
-}
-
-static void
-destroy_device_saved_kbd_focus(struct wl_listener *listener, void *data)
-{
- struct weston_seat *ws;
-
- ws = container_of(listener, struct weston_seat,
- saved_kbd_focus_listener);
-
- ws->saved_kbd_focus = NULL;
-}
-
-WL_EXPORT void
-notify_keyboard_focus_in(struct weston_seat *seat, struct wl_array *keys,
- enum weston_key_state_update update_state)
-{
- struct weston_compositor *compositor = seat->compositor;
- struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
- struct weston_surface *surface;
- uint32_t *k, serial;
-
- serial = wl_display_next_serial(compositor->wl_display);
- wl_array_copy(&keyboard->keys, keys);
- wl_array_for_each(k, &keyboard->keys) {
- weston_compositor_idle_inhibit(compositor);
- if (update_state == STATE_UPDATE_AUTOMATIC)
- update_modifier_state(seat, serial, *k,
- WL_KEYBOARD_KEY_STATE_PRESSED);
- }
-
- surface = seat->saved_kbd_focus;
-
- if (surface) {
- wl_list_remove(&seat->saved_kbd_focus_listener.link);
- weston_keyboard_set_focus(keyboard, surface);
- seat->saved_kbd_focus = NULL;
- }
-}
-
-WL_EXPORT void
-notify_keyboard_focus_out(struct weston_seat *seat)
-{
- struct weston_compositor *compositor = seat->compositor;
- struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
- uint32_t *k, serial;
-
- serial = wl_display_next_serial(compositor->wl_display);
- wl_array_for_each(k, &keyboard->keys) {
- weston_compositor_idle_release(compositor);
- update_modifier_state(seat, serial, *k,
- WL_KEYBOARD_KEY_STATE_RELEASED);
- }
-
- seat->modifier_state = 0;
-
- if (keyboard->focus) {
- seat->saved_kbd_focus = keyboard->focus;
- seat->saved_kbd_focus_listener.notify =
- destroy_device_saved_kbd_focus;
- wl_signal_add(&keyboard->focus->destroy_signal,
- &seat->saved_kbd_focus_listener);
- }
-
- weston_keyboard_set_focus(keyboard, NULL);
- weston_keyboard_cancel_grab(keyboard);
- if (pointer)
- weston_pointer_cancel_grab(pointer);
-}
-
-WL_EXPORT void
-weston_touch_set_focus(struct weston_touch *touch, struct weston_view *view)
-{
- struct wl_list *focus_resource_list;
-
- focus_resource_list = &touch->focus_resource_list;
-
- if (view && touch->focus &&
- touch->focus->surface == view->surface) {
- touch->focus = view;
- return;
- }
-
- wl_list_remove(&touch->focus_resource_listener.link);
- wl_list_init(&touch->focus_resource_listener.link);
- wl_list_remove(&touch->focus_view_listener.link);
- wl_list_init(&touch->focus_view_listener.link);
-
- if (!wl_list_empty(focus_resource_list)) {
- move_resources(&touch->resource_list,
- focus_resource_list);
- }
-
- if (view) {
- struct wl_client *surface_client;
-
- if (!view->surface->resource) {
- touch->focus = NULL;
- return;
- }
-
- surface_client = wl_resource_get_client(view->surface->resource);
- move_resources_for_client(focus_resource_list,
- &touch->resource_list,
- surface_client);
- wl_resource_add_destroy_listener(view->surface->resource,
- &touch->focus_resource_listener);
- wl_signal_add(&view->destroy_signal, &touch->focus_view_listener);
- }
- touch->focus = view;
-}
-
-/**
- * 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 weston_seat *seat, uint32_t time, int touch_id,
- double double_x, double double_y, int touch_type)
-{
- struct weston_compositor *ec = seat->compositor;
- struct weston_touch *touch = weston_seat_get_touch(seat);
- struct weston_touch_grab *grab = touch->grab;
- struct weston_view *ev;
- wl_fixed_t sx, sy;
- wl_fixed_t x = wl_fixed_from_double(double_x);
- wl_fixed_t y = wl_fixed_from_double(double_y);
-
- /* Update grab's global coordinates. */
- if (touch_id == touch->grab_touch_id && touch_type != WL_TOUCH_UP) {
- touch->grab_x = x;
- touch->grab_y = y;
- }
-
- switch (touch_type) {
- case WL_TOUCH_DOWN:
- weston_compositor_idle_inhibit(ec);
-
- touch->num_tp++;
-
- /* the first finger down picks the view, and all further go
- * to that view for the remainder of the touch session i.e.
- * until all touch points are up again. */
- if (touch->num_tp == 1) {
- ev = weston_compositor_pick_view(ec, x, y, &sx, &sy);
- weston_touch_set_focus(touch, ev);
- } else if (!touch->focus) {
- /* Unexpected condition: We have non-initial touch but
- * there is no focused surface.
- */
- weston_log("touch event received with %d points down "
- "but no surface focused\n", touch->num_tp);
- return;
- }
-
- weston_compositor_run_touch_binding(ec, touch,
- time, touch_type);
-
- grab->interface->down(grab, time, touch_id, x, y);
- if (touch->num_tp == 1) {
- touch->grab_serial =
- wl_display_get_serial(ec->wl_display);
- touch->grab_touch_id = touch_id;
- touch->grab_time = time;
- touch->grab_x = x;
- touch->grab_y = y;
- }
-
- break;
- case WL_TOUCH_MOTION:
- ev = touch->focus;
- if (!ev)
- break;
-
- grab->interface->motion(grab, time, touch_id, x, y);
- break;
- case WL_TOUCH_UP:
- if (touch->num_tp == 0) {
- /* This can happen if we start out with one or
- * more fingers on the touch screen, in which
- * case we didn't get the corresponding down
- * event. */
- weston_log("unmatched touch up event\n");
- break;
- }
- weston_compositor_idle_release(ec);
- touch->num_tp--;
-
- grab->interface->up(grab, time, touch_id);
- if (touch->num_tp == 0)
- weston_touch_set_focus(touch, NULL);
- break;
- }
-}
-
-WL_EXPORT void
-notify_touch_frame(struct weston_seat *seat)
-{
- struct weston_touch *touch = weston_seat_get_touch(seat);
- struct weston_touch_grab *grab = touch->grab;
-
- grab->interface->frame(grab);
-}
-
-WL_EXPORT void
-notify_touch_cancel(struct weston_seat *seat)
-{
- struct weston_touch *touch = weston_seat_get_touch(seat);
- struct weston_touch_grab *grab = touch->grab;
-
- grab->interface->cancel(grab);
-}
-
-static int
-pointer_cursor_surface_get_label(struct weston_surface *surface,
- char *buf, size_t len)
-{
- return snprintf(buf, len, "cursor");
-}
-
-static void
-pointer_cursor_surface_configure(struct weston_surface *es,
- int32_t dx, int32_t dy)
-{
- struct weston_pointer *pointer = es->configure_private;
- int x, y;
-
- if (es->width == 0)
- return;
-
- assert(es == pointer->sprite->surface);
-
- pointer->hotspot_x -= dx;
- pointer->hotspot_y -= dy;
-
- x = wl_fixed_to_int(pointer->x) - pointer->hotspot_x;
- y = wl_fixed_to_int(pointer->y) - pointer->hotspot_y;
-
- weston_view_set_position(pointer->sprite, x, y);
-
- empty_region(&es->pending.input);
- empty_region(&es->input);
-
- if (!weston_surface_is_mapped(es)) {
- weston_layer_entry_insert(&es->compositor->cursor_layer.view_list,
- &pointer->sprite->layer_link);
- weston_view_update_transform(pointer->sprite);
- }
-}
-
-static void
-pointer_set_cursor(struct wl_client *client, struct wl_resource *resource,
- uint32_t serial, struct wl_resource *surface_resource,
- int32_t x, int32_t y)
-{
- struct weston_pointer *pointer = wl_resource_get_user_data(resource);
- struct weston_surface *surface = NULL;
-
- if (surface_resource)
- surface = wl_resource_get_user_data(surface_resource);
-
- if (pointer->focus == NULL)
- return;
- /* pointer->focus->surface->resource can be NULL. Surfaces like the
- black_surface used in shell.c for fullscreen don't have
- a resource, but can still have focus */
- if (pointer->focus->surface->resource == NULL)
- return;
- if (wl_resource_get_client(pointer->focus->surface->resource) != client)
- return;
- if (pointer->focus_serial - serial > UINT32_MAX / 2)
- return;
-
- if (!surface) {
- if (pointer->sprite)
- pointer_unmap_sprite(pointer);
- return;
- }
-
- if (pointer->sprite && pointer->sprite->surface == surface &&
- pointer->hotspot_x == x && pointer->hotspot_y == y)
- return;
-
- if (!pointer->sprite || pointer->sprite->surface != surface) {
- if (weston_surface_set_role(surface, "wl_pointer-cursor",
- resource,
- WL_POINTER_ERROR_ROLE) < 0)
- return;
-
- if (pointer->sprite)
- pointer_unmap_sprite(pointer);
-
- wl_signal_add(&surface->destroy_signal,
- &pointer->sprite_destroy_listener);
-
- surface->configure = pointer_cursor_surface_configure;
- surface->configure_private = pointer;
- weston_surface_set_label_func(surface,
- pointer_cursor_surface_get_label);
- pointer->sprite = weston_view_create(surface);
- }
-
- pointer->hotspot_x = x;
- pointer->hotspot_y = y;
-
- if (surface->buffer_ref.buffer) {
- pointer_cursor_surface_configure(surface, 0, 0);
- weston_view_schedule_repaint(pointer->sprite);
- }
-}
-
-static void
-pointer_release(struct wl_client *client, struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static const struct wl_pointer_interface pointer_interface = {
- pointer_set_cursor,
- pointer_release
-};
-
-static void
-seat_get_pointer(struct wl_client *client, struct wl_resource *resource,
- uint32_t id)
-{
- struct weston_seat *seat = wl_resource_get_user_data(resource);
- /* We use the pointer_state directly, which means we'll
- * give a wl_pointer if the seat has ever had one - even though
- * the spec explicitly states that this request only takes effect
- * if the seat has the pointer capability.
- *
- * This prevents a race between the compositor sending new
- * capabilities and the client trying to use the old ones.
- */
- struct weston_pointer *pointer = seat->pointer_state;
- struct wl_resource *cr;
- struct weston_pointer_client *pointer_client;
-
- if (!pointer)
- return;
-
- cr = wl_resource_create(client, &wl_pointer_interface,
- wl_resource_get_version(resource), id);
- if (cr == NULL) {
- wl_client_post_no_memory(client);
- return;
- }
-
- pointer_client = weston_pointer_ensure_pointer_client(pointer, client);
- if (!pointer_client) {
- wl_client_post_no_memory(client);
- return;
- }
-
- wl_list_insert(&pointer_client->pointer_resources,
- wl_resource_get_link(cr));
- wl_resource_set_implementation(cr, &pointer_interface, pointer,
- unbind_pointer_client_resource);
-
- if (pointer->focus && pointer->focus->surface->resource &&
- wl_resource_get_client(pointer->focus->surface->resource) == client) {
- wl_fixed_t sx, sy;
-
- weston_view_from_global_fixed(pointer->focus,
- pointer->x,
- pointer->y,
- &sx, &sy);
-
- wl_pointer_send_enter(cr,
- pointer->focus_serial,
- pointer->focus->surface->resource,
- sx, sy);
- pointer_send_frame(cr);
- }
-}
-
-static void
-keyboard_release(struct wl_client *client, struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static const struct wl_keyboard_interface keyboard_interface = {
- keyboard_release
-};
-
-static bool
-should_send_modifiers_to_client(struct weston_seat *seat,
- struct wl_client *client)
-{
- struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
-
- if (keyboard &&
- keyboard->focus &&
- keyboard->focus->resource &&
- wl_resource_get_client(keyboard->focus->resource) == client)
- return true;
-
- if (pointer &&
- pointer->focus &&
- pointer->focus->surface->resource &&
- wl_resource_get_client(pointer->focus->surface->resource) == client)
- return true;
-
- return false;
-}
-
-static void
-seat_get_keyboard(struct wl_client *client, struct wl_resource *resource,
- uint32_t id)
-{
- struct weston_seat *seat = wl_resource_get_user_data(resource);
- /* We use the keyboard_state directly, which means we'll
- * give a wl_keyboard if the seat has ever had one - even though
- * the spec explicitly states that this request only takes effect
- * if the seat has the keyboard capability.
- *
- * This prevents a race between the compositor sending new
- * capabilities and the client trying to use the old ones.
- */
- struct weston_keyboard *keyboard = seat->keyboard_state;
- struct wl_resource *cr;
-
- if (!keyboard)
- return;
-
- cr = wl_resource_create(client, &wl_keyboard_interface,
- wl_resource_get_version(resource), id);
- if (cr == NULL) {
- wl_client_post_no_memory(client);
- return;
- }
-
- /* May be moved to focused list later by either
- * weston_keyboard_set_focus or directly if this client is already
- * focused */
- wl_list_insert(&keyboard->resource_list, wl_resource_get_link(cr));
- wl_resource_set_implementation(cr, &keyboard_interface,
- seat, unbind_resource);
-
- if (wl_resource_get_version(cr) >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) {
- wl_keyboard_send_repeat_info(cr,
- seat->compositor->kb_repeat_rate,
- seat->compositor->kb_repeat_delay);
- }
-
- if (seat->compositor->use_xkbcommon) {
- wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
- keyboard->xkb_info->keymap_fd,
- keyboard->xkb_info->keymap_size);
- } else {
- int null_fd = open("/dev/null", O_RDONLY);
- wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP,
- null_fd,
- 0);
- close(null_fd);
- }
-
- if (should_send_modifiers_to_client(seat, client)) {
- send_modifiers_to_resource(keyboard,
- cr,
- keyboard->focus_serial);
- }
-
- if (keyboard->focus && keyboard->focus->resource &&
- wl_resource_get_client(keyboard->focus->resource) == client) {
- struct weston_surface *surface =
- (struct weston_surface *)keyboard->focus;
-
- wl_list_remove(wl_resource_get_link(cr));
- wl_list_insert(&keyboard->focus_resource_list,
- wl_resource_get_link(cr));
- wl_keyboard_send_enter(cr,
- keyboard->focus_serial,
- surface->resource,
- &keyboard->keys);
-
- /* If this is the first keyboard resource for this
- * client... */
- if (keyboard->focus_resource_list.prev ==
- wl_resource_get_link(cr))
- wl_data_device_set_keyboard_focus(seat);
- }
-}
-
-static void
-touch_release(struct wl_client *client, struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static const struct wl_touch_interface touch_interface = {
- touch_release
-};
-
-static void
-seat_get_touch(struct wl_client *client, struct wl_resource *resource,
- uint32_t id)
-{
- struct weston_seat *seat = wl_resource_get_user_data(resource);
- /* We use the touch_state directly, which means we'll
- * give a wl_touch if the seat has ever had one - even though
- * the spec explicitly states that this request only takes effect
- * if the seat has the touch capability.
- *
- * This prevents a race between the compositor sending new
- * capabilities and the client trying to use the old ones.
- */
- struct weston_touch *touch = seat->touch_state;
- struct wl_resource *cr;
-
- if (!touch)
- return;
-
- cr = wl_resource_create(client, &wl_touch_interface,
- wl_resource_get_version(resource), id);
- if (cr == NULL) {
- wl_client_post_no_memory(client);
- return;
- }
-
- if (touch->focus &&
- wl_resource_get_client(touch->focus->surface->resource) == client) {
- wl_list_insert(&touch->focus_resource_list,
- wl_resource_get_link(cr));
- } else {
- wl_list_insert(&touch->resource_list,
- wl_resource_get_link(cr));
- }
- wl_resource_set_implementation(cr, &touch_interface,
- seat, unbind_resource);
-}
-
-static void
-seat_release(struct wl_client *client, struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static const struct wl_seat_interface seat_interface = {
- seat_get_pointer,
- seat_get_keyboard,
- seat_get_touch,
- seat_release,
-};
-
-static void
-bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id)
-{
- struct weston_seat *seat = data;
- struct wl_resource *resource;
- enum wl_seat_capability caps = 0;
-
- resource = wl_resource_create(client,
- &wl_seat_interface, version, id);
- wl_list_insert(&seat->base_resource_list, wl_resource_get_link(resource));
- wl_resource_set_implementation(resource, &seat_interface, data,
- unbind_resource);
-
- if (weston_seat_get_pointer(seat))
- caps |= WL_SEAT_CAPABILITY_POINTER;
- if (weston_seat_get_keyboard(seat))
- caps |= WL_SEAT_CAPABILITY_KEYBOARD;
- if (weston_seat_get_touch(seat))
- caps |= WL_SEAT_CAPABILITY_TOUCH;
-
- wl_seat_send_capabilities(resource, caps);
- if (version >= WL_SEAT_NAME_SINCE_VERSION)
- wl_seat_send_name(resource, seat->seat_name);
-}
-
-#ifdef ENABLE_XKBCOMMON
-WL_EXPORT int
-weston_compositor_set_xkb_rule_names(struct weston_compositor *ec,
- struct xkb_rule_names *names)
-{
- ec->use_xkbcommon = 1;
-
- if (ec->xkb_context == NULL) {
- ec->xkb_context = xkb_context_new(0);
- if (ec->xkb_context == NULL) {
- weston_log("failed to create XKB context\n");
- return -1;
- }
- }
-
- if (names)
- ec->xkb_names = *names;
- if (!ec->xkb_names.rules)
- ec->xkb_names.rules = strdup("evdev");
- if (!ec->xkb_names.model)
- ec->xkb_names.model = strdup("pc105");
- if (!ec->xkb_names.layout)
- ec->xkb_names.layout = strdup("us");
-
- return 0;
-}
-
-static void
-weston_xkb_info_destroy(struct weston_xkb_info *xkb_info)
-{
- if (--xkb_info->ref_count > 0)
- return;
-
- xkb_keymap_unref(xkb_info->keymap);
-
- if (xkb_info->keymap_area)
- munmap(xkb_info->keymap_area, xkb_info->keymap_size);
- if (xkb_info->keymap_fd >= 0)
- close(xkb_info->keymap_fd);
- free(xkb_info);
-}
-
-void
-weston_compositor_xkb_destroy(struct weston_compositor *ec)
-{
- /*
- * If we're operating in raw keyboard mode, we never initialized
- * libxkbcommon so there's no cleanup to do either.
- */
- if (!ec->use_xkbcommon)
- return;
-
- free((char *) ec->xkb_names.rules);
- free((char *) ec->xkb_names.model);
- free((char *) ec->xkb_names.layout);
- free((char *) ec->xkb_names.variant);
- free((char *) ec->xkb_names.options);
-
- if (ec->xkb_info)
- weston_xkb_info_destroy(ec->xkb_info);
- xkb_context_unref(ec->xkb_context);
-}
-
-static struct weston_xkb_info *
-weston_xkb_info_create(struct xkb_keymap *keymap)
-{
- struct weston_xkb_info *xkb_info = zalloc(sizeof *xkb_info);
- if (xkb_info == NULL)
- return NULL;
-
- xkb_info->keymap = xkb_keymap_ref(keymap);
- xkb_info->ref_count = 1;
-
- char *keymap_str;
-
- xkb_info->shift_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
- XKB_MOD_NAME_SHIFT);
- xkb_info->caps_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
- XKB_MOD_NAME_CAPS);
- xkb_info->ctrl_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
- XKB_MOD_NAME_CTRL);
- xkb_info->alt_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
- XKB_MOD_NAME_ALT);
- xkb_info->mod2_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
- "Mod2");
- xkb_info->mod3_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
- "Mod3");
- xkb_info->super_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
- XKB_MOD_NAME_LOGO);
- xkb_info->mod5_mod = xkb_keymap_mod_get_index(xkb_info->keymap,
- "Mod5");
-
- xkb_info->num_led = xkb_keymap_led_get_index(xkb_info->keymap,
- XKB_LED_NAME_NUM);
- xkb_info->caps_led = xkb_keymap_led_get_index(xkb_info->keymap,
- XKB_LED_NAME_CAPS);
- xkb_info->scroll_led = xkb_keymap_led_get_index(xkb_info->keymap,
- XKB_LED_NAME_SCROLL);
-
- keymap_str = xkb_keymap_get_as_string(xkb_info->keymap,
- XKB_KEYMAP_FORMAT_TEXT_V1);
- if (keymap_str == NULL) {
- weston_log("failed to get string version of keymap\n");
- goto err_keymap;
- }
- xkb_info->keymap_size = strlen(keymap_str) + 1;
-
- xkb_info->keymap_fd = os_create_anonymous_file(xkb_info->keymap_size);
- if (xkb_info->keymap_fd < 0) {
- weston_log("creating a keymap file for %lu bytes failed: %m\n",
- (unsigned long) xkb_info->keymap_size);
- goto err_keymap_str;
- }
-
- xkb_info->keymap_area = mmap(NULL, xkb_info->keymap_size,
- PROT_READ | PROT_WRITE,
- MAP_SHARED, xkb_info->keymap_fd, 0);
- if (xkb_info->keymap_area == MAP_FAILED) {
- weston_log("failed to mmap() %lu bytes\n",
- (unsigned long) xkb_info->keymap_size);
- goto err_dev_zero;
- }
- strcpy(xkb_info->keymap_area, keymap_str);
- free(keymap_str);
-
- return xkb_info;
-
-err_dev_zero:
- close(xkb_info->keymap_fd);
-err_keymap_str:
- free(keymap_str);
-err_keymap:
- xkb_keymap_unref(xkb_info->keymap);
- free(xkb_info);
- return NULL;
-}
-
-static int
-weston_compositor_build_global_keymap(struct weston_compositor *ec)
-{
- struct xkb_keymap *keymap;
-
- if (ec->xkb_info != NULL)
- return 0;
-
- keymap = xkb_keymap_new_from_names(ec->xkb_context,
- &ec->xkb_names,
- 0);
- if (keymap == NULL) {
- weston_log("failed to compile global XKB keymap\n");
- weston_log(" tried rules %s, model %s, layout %s, variant %s, "
- "options %s\n",
- ec->xkb_names.rules, ec->xkb_names.model,
- ec->xkb_names.layout, ec->xkb_names.variant,
- ec->xkb_names.options);
- return -1;
- }
-
- ec->xkb_info = weston_xkb_info_create(keymap);
- xkb_keymap_unref(keymap);
- if (ec->xkb_info == NULL)
- return -1;
-
- return 0;
-}
-#else
-WL_EXPORT int
-weston_compositor_set_xkb_rule_names(struct weston_compositor *ec,
- struct xkb_rule_names *names)
-{
- return 0;
-}
-
-void
-weston_compositor_xkb_destroy(struct weston_compositor *ec)
-{
-}
-#endif
-
-WL_EXPORT void
-weston_seat_update_keymap(struct weston_seat *seat, struct xkb_keymap *keymap)
-{
- struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
-
- if (!keyboard || !keymap)
- return;
-
-#ifdef ENABLE_XKBCOMMON
- if (!seat->compositor->use_xkbcommon)
- return;
-
- xkb_keymap_unref(keyboard->pending_keymap);
- keyboard->pending_keymap = xkb_keymap_ref(keymap);
-
- if (keyboard->keys.size == 0)
- update_keymap(seat);
-#endif
-}
-
-WL_EXPORT int
-weston_seat_init_keyboard(struct weston_seat *seat, struct xkb_keymap *keymap)
-{
- struct weston_keyboard *keyboard;
-
- if (seat->keyboard_state) {
- seat->keyboard_device_count += 1;
- if (seat->keyboard_device_count == 1)
- seat_send_updated_caps(seat);
- return 0;
- }
-
- keyboard = weston_keyboard_create();
- if (keyboard == NULL) {
- weston_log("failed to allocate weston keyboard struct\n");
- return -1;
- }
-
-#ifdef ENABLE_XKBCOMMON
- if (seat->compositor->use_xkbcommon) {
- if (keymap != NULL) {
- keyboard->xkb_info = weston_xkb_info_create(keymap);
- if (keyboard->xkb_info == NULL)
- goto err;
- } else {
- if (weston_compositor_build_global_keymap(seat->compositor) < 0)
- goto err;
- keyboard->xkb_info = seat->compositor->xkb_info;
- keyboard->xkb_info->ref_count++;
- }
-
- keyboard->xkb_state.state = xkb_state_new(keyboard->xkb_info->keymap);
- if (keyboard->xkb_state.state == NULL) {
- weston_log("failed to initialise XKB state\n");
- goto err;
- }
-
- keyboard->xkb_state.leds = 0;
- }
-#endif
-
- seat->keyboard_state = keyboard;
- seat->keyboard_device_count = 1;
- keyboard->seat = seat;
-
- seat_send_updated_caps(seat);
-
- return 0;
-
-err:
- if (keyboard->xkb_info)
- weston_xkb_info_destroy(keyboard->xkb_info);
- free(keyboard);
-
- return -1;
-}
-
-static void
-weston_keyboard_reset_state(struct weston_keyboard *keyboard)
-{
- struct weston_seat *seat = keyboard->seat;
- struct xkb_state *state;
-
-#ifdef ENABLE_XKBCOMMON
- if (seat->compositor->use_xkbcommon) {
- state = xkb_state_new(keyboard->xkb_info->keymap);
- if (!state) {
- weston_log("failed to reset XKB state\n");
- return;
- }
- xkb_state_unref(keyboard->xkb_state.state);
- keyboard->xkb_state.state = state;
-
- keyboard->xkb_state.leds = 0;
- }
-#endif
-
- seat->modifier_state = 0;
-}
-
-WL_EXPORT void
-weston_seat_release_keyboard(struct weston_seat *seat)
-{
- seat->keyboard_device_count--;
- assert(seat->keyboard_device_count >= 0);
- if (seat->keyboard_device_count == 0) {
- weston_keyboard_set_focus(seat->keyboard_state, NULL);
- weston_keyboard_cancel_grab(seat->keyboard_state);
- weston_keyboard_reset_state(seat->keyboard_state);
- seat_send_updated_caps(seat);
- }
-}
-
-WL_EXPORT void
-weston_seat_init_pointer(struct weston_seat *seat)
-{
- struct weston_pointer *pointer;
-
- if (seat->pointer_state) {
- seat->pointer_device_count += 1;
- if (seat->pointer_device_count == 1)
- seat_send_updated_caps(seat);
- return;
- }
-
- pointer = weston_pointer_create(seat);
- if (pointer == NULL)
- return;
-
- seat->pointer_state = pointer;
- seat->pointer_device_count = 1;
- pointer->seat = seat;
-
- seat_send_updated_caps(seat);
-}
-
-WL_EXPORT void
-weston_seat_release_pointer(struct weston_seat *seat)
-{
- struct weston_pointer *pointer = seat->pointer_state;
-
- seat->pointer_device_count--;
- if (seat->pointer_device_count == 0) {
- weston_pointer_clear_focus(pointer);
- weston_pointer_cancel_grab(pointer);
-
- if (pointer->sprite)
- pointer_unmap_sprite(pointer);
-
- weston_pointer_reset_state(pointer);
- seat_send_updated_caps(seat);
-
- /* seat->pointer is intentionally not destroyed so that
- * a newly attached pointer on this seat will retain
- * the previous cursor co-ordinates.
- */
- }
-}
-
-WL_EXPORT void
-weston_seat_init_touch(struct weston_seat *seat)
-{
- struct weston_touch *touch;
-
- if (seat->touch_state) {
- seat->touch_device_count += 1;
- if (seat->touch_device_count == 1)
- seat_send_updated_caps(seat);
- return;
- }
-
- touch = weston_touch_create();
- if (touch == NULL)
- return;
-
- seat->touch_state = touch;
- seat->touch_device_count = 1;
- touch->seat = seat;
-
- seat_send_updated_caps(seat);
-}
-
-WL_EXPORT void
-weston_seat_release_touch(struct weston_seat *seat)
-{
- seat->touch_device_count--;
- if (seat->touch_device_count == 0) {
- weston_touch_set_focus(seat->touch_state, NULL);
- weston_touch_cancel_grab(seat->touch_state);
- weston_touch_reset_state(seat->touch_state);
- seat_send_updated_caps(seat);
- }
-}
-
-WL_EXPORT void
-weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec,
- const char *seat_name)
-{
- memset(seat, 0, sizeof *seat);
-
- seat->selection_data_source = NULL;
- wl_list_init(&seat->base_resource_list);
- wl_signal_init(&seat->selection_signal);
- wl_list_init(&seat->drag_resource_list);
- wl_signal_init(&seat->destroy_signal);
- wl_signal_init(&seat->updated_caps_signal);
-
- seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, 5,
- seat, bind_seat);
-
- seat->compositor = ec;
- seat->modifier_state = 0;
- seat->seat_name = strdup(seat_name);
-
- wl_list_insert(ec->seat_list.prev, &seat->link);
-
- clipboard_create(seat);
-
- wl_signal_emit(&ec->seat_created_signal, seat);
-}
-
-WL_EXPORT void
-weston_seat_release(struct weston_seat *seat)
-{
- wl_list_remove(&seat->link);
-
- if (seat->saved_kbd_focus)
- wl_list_remove(&seat->saved_kbd_focus_listener.link);
-
- if (seat->pointer_state)
- weston_pointer_destroy(seat->pointer_state);
- if (seat->keyboard_state)
- weston_keyboard_destroy(seat->keyboard_state);
- if (seat->touch_state)
- weston_touch_destroy(seat->touch_state);
-
- free (seat->seat_name);
-
- wl_global_destroy(seat->global);
-
- wl_signal_emit(&seat->destroy_signal, seat);
-}
-
-/** Get a seat's keyboard pointer
- *
- * \param seat The seat to query
- * \return The seat's keyboard pointer, or NULL if no keyboard is present
- *
- * The keyboard pointer for a seat isn't freed when all keyboards are removed,
- * so it should only be used when the seat's keyboard_device_count is greater
- * than zero. This function does that test and only returns a pointer
- * when a keyboard is present.
- */
-WL_EXPORT struct weston_keyboard *
-weston_seat_get_keyboard(struct weston_seat *seat)
-{
- if (!seat)
- return NULL;
-
- if (seat->keyboard_device_count)
- return seat->keyboard_state;
-
- return NULL;
-}
-
-/** Get a seat's pointer pointer
- *
- * \param seat The seat to query
- * \return The seat's pointer pointer, or NULL if no pointer device is present
- *
- * The pointer pointer for a seat isn't freed when all mice are removed,
- * so it should only be used when the seat's pointer_device_count is greater
- * than zero. This function does that test and only returns a pointer
- * when a pointing device is present.
- */
-WL_EXPORT struct weston_pointer *
-weston_seat_get_pointer(struct weston_seat *seat)
-{
- if (!seat)
- return NULL;
-
- if (seat->pointer_device_count)
- return seat->pointer_state;
-
- return NULL;
-}
-
-/** Get a seat's touch pointer
- *
- * \param seat The seat to query
- * \return The seat's touch pointer, or NULL if no touch device is present
- *
- * The touch pointer for a seat isn't freed when all touch devices are removed,
- * so it should only be used when the seat's touch_device_count is greater
- * than zero. This function does that test and only returns a pointer
- * when a touch device is present.
- */
-WL_EXPORT struct weston_touch *
-weston_seat_get_touch(struct weston_seat *seat)
-{
- if (!seat)
- return NULL;
-
- if (seat->touch_device_count)
- return seat->touch_state;
-
- return NULL;
-}
+++ /dev/null
-/*
- * Copyright © 2012 Benjamin Franzke
- * Copyright © 2013 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 "config.h"
-
-#include "compositor.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <signal.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <linux/vt.h>
-#include <linux/kd.h>
-#include <linux/major.h>
-
-#include "launcher-impl.h"
-
-#define DRM_MAJOR 226
-
-#ifndef KDSKBMUTE
-#define KDSKBMUTE 0x4B51
-#endif
-
-#ifdef HAVE_LIBDRM
-
-#include <xf86drm.h>
-
-static inline int
-is_drm_master(int drm_fd)
-{
- drm_magic_t magic;
-
- return drmGetMagic(drm_fd, &magic) == 0 &&
- drmAuthMagic(drm_fd, magic) == 0;
-}
-
-#else
-
-static inline int
-drmDropMaster(int drm_fd)
-{
- return 0;
-}
-
-static inline int
-drmSetMaster(int drm_fd)
-{
- return 0;
-}
-
-static inline int
-is_drm_master(int drm_fd)
-{
- return 0;
-}
-
-#endif
-
-struct launcher_direct {
- struct weston_launcher base;
- struct weston_compositor *compositor;
- int kb_mode, tty, drm_fd;
- struct wl_event_source *vt_source;
-};
-
-static int
-vt_handler(int signal_number, void *data)
-{
- struct launcher_direct *launcher = data;
- struct weston_compositor *compositor = launcher->compositor;
-
- if (compositor->session_active) {
- compositor->session_active = 0;
- wl_signal_emit(&compositor->session_signal, compositor);
- drmDropMaster(launcher->drm_fd);
- ioctl(launcher->tty, VT_RELDISP, 1);
- } else {
- ioctl(launcher->tty, VT_RELDISP, VT_ACKACQ);
- drmSetMaster(launcher->drm_fd);
- compositor->session_active = 1;
- wl_signal_emit(&compositor->session_signal, compositor);
- }
-
- return 1;
-}
-
-static int
-setup_tty(struct launcher_direct *launcher, int tty)
-{
- struct wl_event_loop *loop;
- struct vt_mode mode = { 0 };
- struct stat buf;
- char tty_device[32] ="<stdin>";
- int ret, kd_mode;
-
- if (tty == 0) {
- launcher->tty = dup(tty);
- if (launcher->tty == -1) {
- weston_log("couldn't dup stdin: %m\n");
- return -1;
- }
- } else {
- snprintf(tty_device, sizeof tty_device, "/dev/tty%d", tty);
- launcher->tty = open(tty_device, O_RDWR | O_CLOEXEC);
- if (launcher->tty == -1) {
- weston_log("couldn't open tty %s: %m\n", tty_device);
- return -1;
- }
- }
-
- if (fstat(launcher->tty, &buf) == -1 ||
- major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0) {
- weston_log("%s not a vt\n", tty_device);
- weston_log("if running weston from ssh, "
- "use --tty to specify a tty\n");
- goto err_close;
- }
-
- ret = ioctl(launcher->tty, KDGETMODE, &kd_mode);
- if (ret) {
- weston_log("failed to get VT mode: %m\n");
- return -1;
- }
- if (kd_mode != KD_TEXT) {
- weston_log("%s is already in graphics mode, "
- "is another display server running?\n", tty_device);
- goto err_close;
- }
-
- ioctl(launcher->tty, VT_ACTIVATE, minor(buf.st_rdev));
- ioctl(launcher->tty, VT_WAITACTIVE, minor(buf.st_rdev));
-
- if (ioctl(launcher->tty, KDGKBMODE, &launcher->kb_mode)) {
- weston_log("failed to read keyboard mode: %m\n");
- goto err_close;
- }
-
- if (ioctl(launcher->tty, KDSKBMUTE, 1) &&
- ioctl(launcher->tty, KDSKBMODE, K_OFF)) {
- weston_log("failed to set K_OFF keyboard mode: %m\n");
- goto err_close;
- }
-
- ret = ioctl(launcher->tty, KDSETMODE, KD_GRAPHICS);
- if (ret) {
- weston_log("failed to set KD_GRAPHICS mode on tty: %m\n");
- goto err_close;
- }
-
- /*
- * SIGRTMIN is used as global VT-acquire+release signal. Note that
- * SIGRT* must be tested on runtime, as their exact values are not
- * known at compile-time. POSIX requires 32 of them to be available.
- */
- if (SIGRTMIN > SIGRTMAX) {
- weston_log("not enough RT signals available: %u-%u\n",
- SIGRTMIN, SIGRTMAX);
- ret = -EINVAL;
- goto err_close;
- }
-
- mode.mode = VT_PROCESS;
- mode.relsig = SIGRTMIN;
- mode.acqsig = SIGRTMIN;
- if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0) {
- weston_log("failed to take control of vt handling\n");
- goto err_close;
- }
-
- loop = wl_display_get_event_loop(launcher->compositor->wl_display);
- launcher->vt_source =
- wl_event_loop_add_signal(loop, SIGRTMIN, vt_handler, launcher);
- if (!launcher->vt_source)
- goto err_close;
-
- return 0;
-
- err_close:
- close(launcher->tty);
- return -1;
-}
-
-static int
-launcher_direct_open(struct weston_launcher *launcher_base, const char *path, int flags)
-{
- struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base);
- struct stat s;
- int fd;
-
- fd = open(path, flags | O_CLOEXEC);
- if (fd == -1)
- return -1;
-
- if (fstat(fd, &s) == -1) {
- close(fd);
- return -1;
- }
-
- if (major(s.st_rdev) == DRM_MAJOR) {
- launcher->drm_fd = fd;
- if (!is_drm_master(fd)) {
- weston_log("drm fd not master\n");
- close(fd);
- return -1;
- }
- }
-
- return fd;
-}
-
-static void
-launcher_direct_close(struct weston_launcher *launcher_base, int fd)
-{
- close(fd);
-}
-
-static void
-launcher_direct_restore(struct weston_launcher *launcher_base)
-{
- struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base);
- struct vt_mode mode = { 0 };
-
- if (ioctl(launcher->tty, KDSKBMUTE, 0) &&
- ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode))
- weston_log("failed to restore kb mode: %m\n");
-
- if (ioctl(launcher->tty, KDSETMODE, KD_TEXT))
- weston_log("failed to set KD_TEXT mode on tty: %m\n");
-
- /* We have to drop master before we switch the VT back in
- * VT_AUTO, so we don't risk switching to a VT with another
- * display server, that will then fail to set drm master. */
- drmDropMaster(launcher->drm_fd);
-
- mode.mode = VT_AUTO;
- if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0)
- weston_log("could not reset vt handling\n");
-}
-
-static int
-launcher_direct_activate_vt(struct weston_launcher *launcher_base, int vt)
-{
- struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base);
- return ioctl(launcher->tty, VT_ACTIVATE, vt);
-}
-
-static int
-launcher_direct_connect(struct weston_launcher **out, struct weston_compositor *compositor,
- int tty, const char *seat_id, bool sync_drm)
-{
- struct launcher_direct *launcher;
-
- if (geteuid() != 0)
- return -EINVAL;
-
- launcher = zalloc(sizeof(*launcher));
- if (launcher == NULL)
- return -ENOMEM;
-
- launcher->base.iface = &launcher_direct_iface;
- launcher->compositor = compositor;
-
- if (setup_tty(launcher, tty) == -1) {
- free(launcher);
- return -1;
- }
-
- * (struct launcher_direct **) out = launcher;
- return 0;
-}
-
-static void
-launcher_direct_destroy(struct weston_launcher *launcher_base)
-{
- struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base);
-
- launcher_direct_restore(&launcher->base);
- wl_event_source_remove(launcher->vt_source);
-
- if (launcher->tty >= 0)
- close(launcher->tty);
-
- free(launcher);
-}
-
-struct launcher_interface launcher_direct_iface = {
- launcher_direct_connect,
- launcher_direct_destroy,
- launcher_direct_open,
- launcher_direct_close,
- launcher_direct_activate_vt,
- launcher_direct_restore,
-};
+++ /dev/null
-/*
- * Copyright © 2015 Jasper St. Pierre
- *
- * 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 "config.h"
-
-#include "compositor.h"
-
-struct weston_launcher;
-
-struct launcher_interface {
- int (* connect) (struct weston_launcher **launcher_out, struct weston_compositor *compositor,
- int tty, const char *seat_id, bool sync_drm);
- void (* destroy) (struct weston_launcher *launcher);
- int (* open) (struct weston_launcher *launcher, const char *path, int flags);
- void (* close) (struct weston_launcher *launcher, int fd);
- int (* activate_vt) (struct weston_launcher *launcher, int vt);
- void (* restore) (struct weston_launcher *launcher);
-};
-
-struct weston_launcher {
- struct launcher_interface *iface;
-};
-
-extern struct launcher_interface launcher_logind_iface;
-extern struct launcher_interface launcher_weston_launch_iface;
-extern struct launcher_interface launcher_direct_iface;
+++ /dev/null
-/*
- * Copyright © 2013 David Herrmann
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <systemd/sd-login.h>
-#include <unistd.h>
-
-#include "compositor.h"
-#include "dbus.h"
-#include "launcher-impl.h"
-
-#define DRM_MAJOR 226
-
-struct launcher_logind {
- struct weston_launcher base;
- struct weston_compositor *compositor;
- bool sync_drm;
- char *seat;
- char *sid;
- unsigned int vtnr;
- int vt;
- int kb_mode;
-
- DBusConnection *dbus;
- struct wl_event_source *dbus_ctx;
- char *spath;
- DBusPendingCall *pending_active;
-};
-
-static int
-launcher_logind_take_device(struct launcher_logind *wl, uint32_t major,
- uint32_t minor, bool *paused_out)
-{
- DBusMessage *m, *reply;
- bool b;
- int r, fd;
- dbus_bool_t paused;
-
- m = dbus_message_new_method_call("org.freedesktop.login1",
- wl->spath,
- "org.freedesktop.login1.Session",
- "TakeDevice");
- if (!m)
- return -ENOMEM;
-
- b = dbus_message_append_args(m,
- DBUS_TYPE_UINT32, &major,
- DBUS_TYPE_UINT32, &minor,
- DBUS_TYPE_INVALID);
- if (!b) {
- r = -ENOMEM;
- goto err_unref;
- }
-
- reply = dbus_connection_send_with_reply_and_block(wl->dbus, m,
- -1, NULL);
- if (!reply) {
- r = -ENODEV;
- goto err_unref;
- }
-
- b = dbus_message_get_args(reply, NULL,
- DBUS_TYPE_UNIX_FD, &fd,
- DBUS_TYPE_BOOLEAN, &paused,
- DBUS_TYPE_INVALID);
- if (!b) {
- r = -ENODEV;
- goto err_reply;
- }
-
- r = fd;
- if (paused_out)
- *paused_out = paused;
-
-err_reply:
- dbus_message_unref(reply);
-err_unref:
- dbus_message_unref(m);
- return r;
-}
-
-static void
-launcher_logind_release_device(struct launcher_logind *wl, uint32_t major,
- uint32_t minor)
-{
- DBusMessage *m;
- bool b;
-
- m = dbus_message_new_method_call("org.freedesktop.login1",
- wl->spath,
- "org.freedesktop.login1.Session",
- "ReleaseDevice");
- if (m) {
- b = dbus_message_append_args(m,
- DBUS_TYPE_UINT32, &major,
- DBUS_TYPE_UINT32, &minor,
- DBUS_TYPE_INVALID);
- if (b)
- dbus_connection_send(wl->dbus, m, NULL);
- dbus_message_unref(m);
- }
-}
-
-static void
-launcher_logind_pause_device_complete(struct launcher_logind *wl, uint32_t major,
- uint32_t minor)
-{
- DBusMessage *m;
- bool b;
-
- m = dbus_message_new_method_call("org.freedesktop.login1",
- wl->spath,
- "org.freedesktop.login1.Session",
- "PauseDeviceComplete");
- if (m) {
- b = dbus_message_append_args(m,
- DBUS_TYPE_UINT32, &major,
- DBUS_TYPE_UINT32, &minor,
- DBUS_TYPE_INVALID);
- if (b)
- dbus_connection_send(wl->dbus, m, NULL);
- dbus_message_unref(m);
- }
-}
-
-static int
-launcher_logind_open(struct weston_launcher *launcher, const char *path, int flags)
-{
- struct launcher_logind *wl = wl_container_of(launcher, wl, base);
- struct stat st;
- int fl, r, fd;
-
- r = stat(path, &st);
- if (r < 0)
- return -1;
- if (!S_ISCHR(st.st_mode)) {
- errno = ENODEV;
- return -1;
- }
-
- fd = launcher_logind_take_device(wl, major(st.st_rdev),
- minor(st.st_rdev), NULL);
- if (fd < 0)
- return fd;
-
- /* Compared to weston_launcher_open() we cannot specify the open-mode
- * directly. Instead, logind passes us an fd with sane default modes.
- * For DRM and evdev this means O_RDWR | O_CLOEXEC. If we want
- * something else, we need to change it afterwards. We currently
- * only support setting O_NONBLOCK. Changing access-modes is not
- * possible so accept whatever logind passes us. */
-
- fl = fcntl(fd, F_GETFL);
- if (fl < 0) {
- r = -errno;
- goto err_close;
- }
-
- if (flags & O_NONBLOCK)
- fl |= O_NONBLOCK;
-
- r = fcntl(fd, F_SETFL, fl);
- if (r < 0) {
- r = -errno;
- goto err_close;
- }
- return fd;
-
-err_close:
- close(fd);
- launcher_logind_release_device(wl, major(st.st_rdev),
- minor(st.st_rdev));
- errno = -r;
- return -1;
-}
-
-static void
-launcher_logind_close(struct weston_launcher *launcher, int fd)
-{
- struct launcher_logind *wl = wl_container_of(launcher, wl, base);
- struct stat st;
- int r;
-
- r = fstat(fd, &st);
- if (r < 0) {
- weston_log("logind: cannot fstat fd: %m\n");
- return;
- }
-
- if (!S_ISCHR(st.st_mode)) {
- weston_log("logind: invalid device passed\n");
- return;
- }
-
- launcher_logind_release_device(wl, major(st.st_rdev),
- minor(st.st_rdev));
-}
-
-static void
-launcher_logind_restore(struct weston_launcher *launcher)
-{
-}
-
-static int
-launcher_logind_activate_vt(struct weston_launcher *launcher, int vt)
-{
- struct launcher_logind *wl = wl_container_of(launcher, wl, base);
- DBusMessage *m;
- bool b;
- int r;
-
- m = dbus_message_new_method_call("org.freedesktop.login1",
- "/org/freedesktop/login1/seat/self",
- "org.freedesktop.login1.Seat",
- "SwitchTo");
- if (!m)
- return -ENOMEM;
-
- b = dbus_message_append_args(m,
- DBUS_TYPE_UINT32, &vt,
- DBUS_TYPE_INVALID);
- if (!b) {
- r = -ENOMEM;
- goto err_unref;
- }
-
- dbus_connection_send(wl->dbus, m, NULL);
- r = 0;
-
- err_unref:
- dbus_message_unref(m);
- return r;
-}
-
-static void
-launcher_logind_set_active(struct launcher_logind *wl, bool active)
-{
- if (!wl->compositor->session_active == !active)
- return;
-
- wl->compositor->session_active = active;
-
- wl_signal_emit(&wl->compositor->session_signal,
- wl->compositor);
-}
-
-static void
-parse_active(struct launcher_logind *wl, DBusMessage *m, DBusMessageIter *iter)
-{
- DBusMessageIter sub;
- dbus_bool_t b;
-
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
- return;
-
- dbus_message_iter_recurse(iter, &sub);
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
- return;
-
- dbus_message_iter_get_basic(&sub, &b);
-
- /* If the backend requested DRM master-device synchronization, we only
- * wake-up the compositor once the master-device is up and running. For
- * other backends, we immediately forward the Active-change event. */
- if (!wl->sync_drm || !b)
- launcher_logind_set_active(wl, b);
-}
-
-static void
-get_active_cb(DBusPendingCall *pending, void *data)
-{
- struct launcher_logind *wl = data;
- DBusMessageIter iter;
- DBusMessage *m;
- int type;
-
- dbus_pending_call_unref(wl->pending_active);
- wl->pending_active = NULL;
-
- m = dbus_pending_call_steal_reply(pending);
- if (!m)
- return;
-
- type = dbus_message_get_type(m);
- if (type == DBUS_MESSAGE_TYPE_METHOD_RETURN &&
- dbus_message_iter_init(m, &iter))
- parse_active(wl, m, &iter);
-
- dbus_message_unref(m);
-}
-
-static void
-launcher_logind_get_active(struct launcher_logind *wl)
-{
- DBusPendingCall *pending;
- DBusMessage *m;
- bool b;
- const char *iface, *name;
-
- m = dbus_message_new_method_call("org.freedesktop.login1",
- wl->spath,
- "org.freedesktop.DBus.Properties",
- "Get");
- if (!m)
- return;
-
- iface = "org.freedesktop.login1.Session";
- name = "Active";
- b = dbus_message_append_args(m,
- DBUS_TYPE_STRING, &iface,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID);
- if (!b)
- goto err_unref;
-
- b = dbus_connection_send_with_reply(wl->dbus, m, &pending, -1);
- if (!b)
- goto err_unref;
-
- b = dbus_pending_call_set_notify(pending, get_active_cb, wl, NULL);
- if (!b) {
- dbus_pending_call_cancel(pending);
- dbus_pending_call_unref(pending);
- goto err_unref;
- }
-
- if (wl->pending_active) {
- dbus_pending_call_cancel(wl->pending_active);
- dbus_pending_call_unref(wl->pending_active);
- }
- wl->pending_active = pending;
- return;
-
-err_unref:
- dbus_message_unref(m);
-}
-
-static void
-disconnected_dbus(struct launcher_logind *wl)
-{
- weston_log("logind: dbus connection lost, exiting..\n");
- launcher_logind_restore(&wl->base);
- exit(-1);
-}
-
-static void
-session_removed(struct launcher_logind *wl, DBusMessage *m)
-{
- const char *name, *obj;
- bool r;
-
- r = dbus_message_get_args(m, NULL,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_OBJECT_PATH, &obj,
- DBUS_TYPE_INVALID);
- if (!r) {
- weston_log("logind: cannot parse SessionRemoved dbus signal\n");
- return;
- }
-
- if (!strcmp(name, wl->sid)) {
- weston_log("logind: our session got closed, exiting..\n");
- launcher_logind_restore(&wl->base);
- exit(-1);
- }
-}
-
-static void
-property_changed(struct launcher_logind *wl, DBusMessage *m)
-{
- DBusMessageIter iter, sub, entry;
- const char *interface, *name;
-
- if (!dbus_message_iter_init(m, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
- goto error;
-
- dbus_message_iter_get_basic(&iter, &interface);
-
- if (!dbus_message_iter_next(&iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
- goto error;
-
- dbus_message_iter_recurse(&iter, &sub);
-
- while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY) {
- dbus_message_iter_recurse(&sub, &entry);
-
- if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
- goto error;
-
- dbus_message_iter_get_basic(&entry, &name);
- if (!dbus_message_iter_next(&entry))
- goto error;
-
- if (!strcmp(name, "Active")) {
- parse_active(wl, m, &entry);
- return;
- }
-
- dbus_message_iter_next(&sub);
- }
-
- if (!dbus_message_iter_next(&iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
- goto error;
-
- dbus_message_iter_recurse(&iter, &sub);
-
- while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
- dbus_message_iter_get_basic(&sub, &name);
-
- if (!strcmp(name, "Active")) {
- launcher_logind_get_active(wl);
- return;
- }
-
- dbus_message_iter_next(&sub);
- }
-
- return;
-
-error:
- weston_log("logind: cannot parse PropertiesChanged dbus signal\n");
-}
-
-static void
-device_paused(struct launcher_logind *wl, DBusMessage *m)
-{
- bool r;
- const char *type;
- uint32_t major, minor;
-
- r = dbus_message_get_args(m, NULL,
- DBUS_TYPE_UINT32, &major,
- DBUS_TYPE_UINT32, &minor,
- DBUS_TYPE_STRING, &type,
- DBUS_TYPE_INVALID);
- if (!r) {
- weston_log("logind: cannot parse PauseDevice dbus signal\n");
- return;
- }
-
- /* "pause" means synchronous pausing. Acknowledge it unconditionally
- * as we support asynchronous device shutdowns, anyway.
- * "force" means asynchronous pausing.
- * "gone" means the device is gone. We handle it the same as "force" as
- * a following udev event will be caught, too.
- *
- * If it's our main DRM device, tell the compositor to go asleep. */
-
- if (!strcmp(type, "pause"))
- launcher_logind_pause_device_complete(wl, major, minor);
-
- if (wl->sync_drm && major == DRM_MAJOR)
- launcher_logind_set_active(wl, false);
-}
-
-static void
-device_resumed(struct launcher_logind *wl, DBusMessage *m)
-{
- bool r;
- uint32_t major;
-
- r = dbus_message_get_args(m, NULL,
- DBUS_TYPE_UINT32, &major,
- /*DBUS_TYPE_UINT32, &minor,
- DBUS_TYPE_UNIX_FD, &fd,*/
- DBUS_TYPE_INVALID);
- if (!r) {
- weston_log("logind: cannot parse ResumeDevice dbus signal\n");
- return;
- }
-
- /* DeviceResumed messages provide us a new file-descriptor for
- * resumed devices. For DRM devices it's the same as before, for evdev
- * devices it's a new open-file. As we reopen evdev devices, anyway,
- * there is no need for us to handle this event for evdev. For DRM, we
- * notify the compositor to wake up. */
-
- if (wl->sync_drm && major == DRM_MAJOR)
- launcher_logind_set_active(wl, true);
-}
-
-static DBusHandlerResult
-filter_dbus(DBusConnection *c, DBusMessage *m, void *data)
-{
- struct launcher_logind *wl = data;
-
- if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) {
- disconnected_dbus(wl);
- } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Manager",
- "SessionRemoved")) {
- session_removed(wl, m);
- } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties",
- "PropertiesChanged")) {
- property_changed(wl, m);
- } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session",
- "PauseDevice")) {
- device_paused(wl, m);
- } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session",
- "ResumeDevice")) {
- device_resumed(wl, m);
- }
-
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static int
-launcher_logind_setup_dbus(struct launcher_logind *wl)
-{
- bool b;
- int r;
-
- r = asprintf(&wl->spath, "/org/freedesktop/login1/session/%s",
- wl->sid);
- if (r < 0)
- return -ENOMEM;
-
- b = dbus_connection_add_filter(wl->dbus, filter_dbus, wl, NULL);
- if (!b) {
- weston_log("logind: cannot add dbus filter\n");
- r = -ENOMEM;
- goto err_spath;
- }
-
- r = weston_dbus_add_match_signal(wl->dbus,
- "org.freedesktop.login1",
- "org.freedesktop.login1.Manager",
- "SessionRemoved",
- "/org/freedesktop/login1");
- if (r < 0) {
- weston_log("logind: cannot add dbus match\n");
- goto err_spath;
- }
-
- r = weston_dbus_add_match_signal(wl->dbus,
- "org.freedesktop.login1",
- "org.freedesktop.login1.Session",
- "PauseDevice",
- wl->spath);
- if (r < 0) {
- weston_log("logind: cannot add dbus match\n");
- goto err_spath;
- }
-
- r = weston_dbus_add_match_signal(wl->dbus,
- "org.freedesktop.login1",
- "org.freedesktop.login1.Session",
- "ResumeDevice",
- wl->spath);
- if (r < 0) {
- weston_log("logind: cannot add dbus match\n");
- goto err_spath;
- }
-
- r = weston_dbus_add_match_signal(wl->dbus,
- "org.freedesktop.login1",
- "org.freedesktop.DBus.Properties",
- "PropertiesChanged",
- wl->spath);
- if (r < 0) {
- weston_log("logind: cannot add dbus match\n");
- goto err_spath;
- }
-
- return 0;
-
-err_spath:
- /* don't remove any dbus-match as the connection is closed, anyway */
- free(wl->spath);
- return r;
-}
-
-static void
-launcher_logind_destroy_dbus(struct launcher_logind *wl)
-{
- /* don't remove any dbus-match as the connection is closed, anyway */
- free(wl->spath);
-}
-
-static int
-launcher_logind_take_control(struct launcher_logind *wl)
-{
- DBusError err;
- DBusMessage *m, *reply;
- dbus_bool_t force;
- bool b;
- int r;
-
- dbus_error_init(&err);
-
- m = dbus_message_new_method_call("org.freedesktop.login1",
- wl->spath,
- "org.freedesktop.login1.Session",
- "TakeControl");
- if (!m)
- return -ENOMEM;
-
- force = false;
- b = dbus_message_append_args(m,
- DBUS_TYPE_BOOLEAN, &force,
- DBUS_TYPE_INVALID);
- if (!b) {
- r = -ENOMEM;
- goto err_unref;
- }
-
- reply = dbus_connection_send_with_reply_and_block(wl->dbus,
- m, -1, &err);
- if (!reply) {
- if (dbus_error_has_name(&err, DBUS_ERROR_UNKNOWN_METHOD))
- weston_log("logind: old systemd version detected\n");
- else
- weston_log("logind: cannot take control over session %s\n", wl->sid);
-
- dbus_error_free(&err);
- r = -EIO;
- goto err_unref;
- }
-
- dbus_message_unref(reply);
- dbus_message_unref(m);
- return 0;
-
-err_unref:
- dbus_message_unref(m);
- return r;
-}
-
-static void
-launcher_logind_release_control(struct launcher_logind *wl)
-{
- DBusMessage *m;
-
- m = dbus_message_new_method_call("org.freedesktop.login1",
- wl->spath,
- "org.freedesktop.login1.Session",
- "ReleaseControl");
- if (m) {
- dbus_connection_send(wl->dbus, m, NULL);
- dbus_message_unref(m);
- }
-}
-
-static int
-weston_sd_session_get_vt(const char *sid, unsigned int *out)
-{
-#ifdef HAVE_SYSTEMD_LOGIN_209
- return sd_session_get_vt(sid, out);
-#else
- int r;
- char *tty;
-
- r = sd_session_get_tty(sid, &tty);
- if (r < 0)
- return r;
-
- r = sscanf(tty, "tty%u", out);
- free(tty);
-
- if (r != 1)
- return -EINVAL;
-
- return 0;
-#endif
-}
-
-static int
-launcher_logind_activate(struct launcher_logind *wl)
-{
- DBusMessage *m;
-
- m = dbus_message_new_method_call("org.freedesktop.login1",
- wl->spath,
- "org.freedesktop.login1.Session",
- "Activate");
- if (!m)
- return -ENOMEM;
-
- dbus_connection_send(wl->dbus, m, NULL);
- return 0;
-}
-
-static int
-launcher_logind_connect(struct weston_launcher **out, struct weston_compositor *compositor,
- int tty, const char *seat_id, bool sync_drm)
-{
- struct launcher_logind *wl;
- struct wl_event_loop *loop;
- char *t;
- int r;
-
- wl = zalloc(sizeof(*wl));
- if (wl == NULL) {
- r = -ENOMEM;
- goto err_out;
- }
-
- wl->base.iface = &launcher_logind_iface;
- wl->compositor = compositor;
- wl->sync_drm = sync_drm;
-
- wl->seat = strdup(seat_id);
- if (!wl->seat) {
- r = -ENOMEM;
- goto err_wl;
- }
-
- r = sd_pid_get_session(getpid(), &wl->sid);
- if (r < 0) {
- weston_log("logind: not running in a systemd session\n");
- goto err_seat;
- }
-
- t = NULL;
- r = sd_session_get_seat(wl->sid, &t);
- if (r < 0) {
- weston_log("logind: failed to get session seat\n");
- free(t);
- goto err_session;
- } else if (strcmp(seat_id, t)) {
- weston_log("logind: weston's seat '%s' differs from session-seat '%s'\n",
- seat_id, t);
- r = -EINVAL;
- free(t);
- goto err_session;
- }
- free(t);
-
- r = weston_sd_session_get_vt(wl->sid, &wl->vtnr);
- if (r < 0) {
- weston_log("logind: session not running on a VT\n");
- goto err_session;
- } else if (tty > 0 && wl->vtnr != (unsigned int )tty) {
- weston_log("logind: requested VT --tty=%d differs from real session VT %u\n",
- tty, wl->vtnr);
- r = -EINVAL;
- goto err_session;
- }
-
- loop = wl_display_get_event_loop(compositor->wl_display);
- r = weston_dbus_open(loop, DBUS_BUS_SYSTEM, &wl->dbus, &wl->dbus_ctx);
- if (r < 0) {
- weston_log("logind: cannot connect to system dbus\n");
- goto err_session;
- }
-
- r = launcher_logind_setup_dbus(wl);
- if (r < 0)
- goto err_dbus;
-
- r = launcher_logind_take_control(wl);
- if (r < 0)
- goto err_dbus_cleanup;
-
- r = launcher_logind_activate(wl);
- if (r < 0)
- goto err_dbus_cleanup;
-
- weston_log("logind: session control granted\n");
- * (struct launcher_logind **) out = wl;
- return 0;
-
-err_dbus_cleanup:
- launcher_logind_destroy_dbus(wl);
-err_dbus:
- weston_dbus_close(wl->dbus, wl->dbus_ctx);
-err_session:
- free(wl->sid);
-err_seat:
- free(wl->seat);
-err_wl:
- free(wl);
-err_out:
- weston_log("logind: cannot setup systemd-logind helper (%d), using legacy fallback\n", r);
- errno = -r;
- return -1;
-}
-
-static void
-launcher_logind_destroy(struct weston_launcher *launcher)
-{
- struct launcher_logind *wl = wl_container_of(launcher, wl, base);
-
- if (wl->pending_active) {
- dbus_pending_call_cancel(wl->pending_active);
- dbus_pending_call_unref(wl->pending_active);
- }
-
- launcher_logind_release_control(wl);
- launcher_logind_destroy_dbus(wl);
- weston_dbus_close(wl->dbus, wl->dbus_ctx);
- free(wl->sid);
- free(wl->seat);
- free(wl);
-}
-
-struct launcher_interface launcher_logind_iface = {
- launcher_logind_connect,
- launcher_logind_destroy,
- launcher_logind_open,
- launcher_logind_close,
- launcher_logind_activate_vt,
- launcher_logind_restore,
-};
+++ /dev/null
-/*
- * Copyright © 2012 Benjamin Franzke
- * Copyright © 2013 Intel Corporation
- *
- * 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.
- */
-
-#include "config.h"
-
-#include "compositor.h"
-
-#include "launcher-util.h"
-#include "launcher-impl.h"
-
-#include <unistd.h>
-#include <linux/input.h>
-
-static struct launcher_interface *ifaces[] = {
-#ifdef HAVE_SYSTEMD_LOGIN
- &launcher_logind_iface,
-#endif
- &launcher_weston_launch_iface,
- &launcher_direct_iface,
- NULL,
-};
-
-WL_EXPORT struct weston_launcher *
-weston_launcher_connect(struct weston_compositor *compositor, int tty,
- const char *seat_id, bool sync_drm)
-{
- struct launcher_interface **it;
-
- for (it = ifaces; *it != NULL; it++) {
- struct launcher_interface *iface = *it;
- struct weston_launcher *launcher;
-
- if (iface->connect(&launcher, compositor, tty, seat_id, sync_drm) == 0)
- return launcher;
- }
-
- return NULL;
-}
-
-WL_EXPORT void
-weston_launcher_destroy(struct weston_launcher *launcher)
-{
- launcher->iface->destroy(launcher);
-}
-
-WL_EXPORT int
-weston_launcher_open(struct weston_launcher *launcher,
- const char *path, int flags)
-{
- return launcher->iface->open(launcher, path, flags);
-}
-
-WL_EXPORT void
-weston_launcher_close(struct weston_launcher *launcher, int fd)
-{
- launcher->iface->close(launcher, fd);
-}
-
-WL_EXPORT int
-weston_launcher_activate_vt(struct weston_launcher *launcher, int vt)
-{
- return launcher->iface->activate_vt(launcher, vt);
-}
-
-WL_EXPORT void
-weston_launcher_restore(struct weston_launcher *launcher)
-{
- launcher->iface->restore(launcher);
-}
-
-
-static void
-switch_vt_binding(struct weston_keyboard *keyboard,
- uint32_t time, uint32_t key, void *data)
-{
- struct weston_compositor *compositor = data;
-
- weston_launcher_activate_vt(compositor->launcher, key - KEY_F1 + 1);
-}
-
-WL_EXPORT void
-weston_setup_vt_switch_bindings(struct weston_compositor *compositor)
-{
- uint32_t key;
-
- if (compositor->vt_switching == false)
- return;
-
- for (key = KEY_F1; key < KEY_F9; key++)
- weston_compositor_add_key_binding(compositor, key,
- MODIFIER_CTRL | MODIFIER_ALT,
- switch_vt_binding,
- compositor);
-}
+++ /dev/null
-/*
- * Copyright © 2012 Benjamin Franzke
- *
- * 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.
- */
-
-#ifndef _WESTON_LAUNCHER_UTIL_H_
-#define _WESTON_LAUNCHER_UTIL_H_
-
-#include "config.h"
-
-#include "compositor.h"
-
-struct weston_launcher;
-
-struct weston_launcher *
-weston_launcher_connect(struct weston_compositor *compositor, int tty,
- const char *seat_id, bool sync_drm);
-
-void
-weston_launcher_destroy(struct weston_launcher *launcher);
-
-int
-weston_launcher_open(struct weston_launcher *launcher,
- const char *path, int flags);
-
-void
-weston_launcher_close(struct weston_launcher *launcher, int fd);
-
-int
-weston_launcher_activate_vt(struct weston_launcher *launcher, int vt);
-
-void
-weston_launcher_restore(struct weston_launcher *launcher);
-
-void
-weston_setup_vt_switch_bindings(struct weston_compositor *compositor);
-
-#endif
+++ /dev/null
-/*
- * Copyright © 2012 Benjamin Franzke
- * Copyright © 2013 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 "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <errno.h>
-#include <signal.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/uio.h>
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <linux/vt.h>
-#include <linux/kd.h>
-#include <linux/major.h>
-
-#include "compositor.h"
-#include "weston-launch.h"
-#include "launcher-impl.h"
-
-#define DRM_MAJOR 226
-
-#ifndef KDSKBMUTE
-#define KDSKBMUTE 0x4B51
-#endif
-
-#ifdef HAVE_LIBDRM
-
-#include <xf86drm.h>
-
-static inline int
-is_drm_master(int drm_fd)
-{
- drm_magic_t magic;
-
- return drmGetMagic(drm_fd, &magic) == 0 &&
- drmAuthMagic(drm_fd, magic) == 0;
-}
-
-#else
-
-static inline int
-drmDropMaster(int drm_fd)
-{
- return 0;
-}
-
-static inline int
-drmSetMaster(int drm_fd)
-{
- return 0;
-}
-
-static inline int
-is_drm_master(int drm_fd)
-{
- return 0;
-}
-
-#endif
-
-
-union cmsg_data { unsigned char b[4]; int fd; };
-
-struct launcher_weston_launch {
- struct weston_launcher base;
- struct weston_compositor *compositor;
- struct wl_event_loop *loop;
- int fd;
- struct wl_event_source *source;
-
- int kb_mode, tty, drm_fd;
-};
-
-static int
-launcher_weston_launch_open(struct weston_launcher *launcher_base,
- const char *path, int flags)
-{
- struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base);
- int n, ret;
- struct msghdr msg;
- struct cmsghdr *cmsg;
- struct iovec iov;
- union cmsg_data *data;
- char control[CMSG_SPACE(sizeof data->fd)];
- ssize_t len;
- struct weston_launcher_open *message;
-
- n = sizeof(*message) + strlen(path) + 1;
- message = malloc(n);
- if (!message)
- return -1;
-
- message->header.opcode = WESTON_LAUNCHER_OPEN;
- message->flags = flags;
- strcpy(message->path, path);
-
- do {
- len = send(launcher->fd, message, n, 0);
- } while (len < 0 && errno == EINTR);
- free(message);
-
- memset(&msg, 0, sizeof msg);
- iov.iov_base = &ret;
- iov.iov_len = sizeof ret;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = control;
- msg.msg_controllen = sizeof control;
-
- do {
- len = recvmsg(launcher->fd, &msg, MSG_CMSG_CLOEXEC);
- } while (len < 0 && errno == EINTR);
-
- if (len != sizeof ret ||
- ret < 0)
- return -1;
-
- cmsg = CMSG_FIRSTHDR(&msg);
- if (!cmsg ||
- cmsg->cmsg_level != SOL_SOCKET ||
- cmsg->cmsg_type != SCM_RIGHTS) {
- fprintf(stderr, "invalid control message\n");
- return -1;
- }
-
- data = (union cmsg_data *) CMSG_DATA(cmsg);
- if (data->fd == -1) {
- fprintf(stderr, "missing drm fd in socket request\n");
- return -1;
- }
-
- return data->fd;
-}
-
-static void
-launcher_weston_launch_close(struct weston_launcher *launcher_base, int fd)
-{
- close(fd);
-}
-
-static void
-launcher_weston_launch_restore(struct weston_launcher *launcher_base)
-{
- struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base);
- struct vt_mode mode = { 0 };
-
- if (ioctl(launcher->tty, KDSKBMUTE, 0) &&
- ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode))
- weston_log("failed to restore kb mode: %m\n");
-
- if (ioctl(launcher->tty, KDSETMODE, KD_TEXT))
- weston_log("failed to set KD_TEXT mode on tty: %m\n");
-
- /* We have to drop master before we switch the VT back in
- * VT_AUTO, so we don't risk switching to a VT with another
- * display server, that will then fail to set drm master. */
- drmDropMaster(launcher->drm_fd);
-
- mode.mode = VT_AUTO;
- if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0)
- weston_log("could not reset vt handling\n");
-}
-
-static int
-launcher_weston_launch_data(int fd, uint32_t mask, void *data)
-{
- struct launcher_weston_launch *launcher = data;
- int len, ret;
-
- if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
- weston_log("launcher socket closed, exiting\n");
- /* Normally the weston-launch will reset the tty, but
- * in this case it died or something, so do it here so
- * we don't end up with a stuck vt. */
- launcher_weston_launch_restore(&launcher->base);
- exit(-1);
- }
-
- do {
- len = recv(launcher->fd, &ret, sizeof ret, 0);
- } while (len < 0 && errno == EINTR);
-
- switch (ret) {
- case WESTON_LAUNCHER_ACTIVATE:
- launcher->compositor->session_active = 1;
- wl_signal_emit(&launcher->compositor->session_signal,
- launcher->compositor);
- break;
- case WESTON_LAUNCHER_DEACTIVATE:
- launcher->compositor->session_active = 0;
- wl_signal_emit(&launcher->compositor->session_signal,
- launcher->compositor);
- break;
- default:
- weston_log("unexpected event from weston-launch\n");
- break;
- }
-
- return 1;
-}
-
-static int
-launcher_weston_launch_activate_vt(struct weston_launcher *launcher_base, int vt)
-{
- struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base);
- return ioctl(launcher->tty, VT_ACTIVATE, vt);
-}
-
-static int
-launcher_weston_launch_connect(struct weston_launcher **out, struct weston_compositor *compositor,
- int tty, const char *seat_id, bool sync_drm)
-{
- struct launcher_weston_launch *launcher;
- struct wl_event_loop *loop;
-
- launcher = malloc(sizeof *launcher);
- if (launcher == NULL)
- return -ENOMEM;
-
- launcher->base.iface = &launcher_weston_launch_iface;
- * (struct launcher_weston_launch **) out = launcher;
- launcher->compositor = compositor;
- launcher->drm_fd = -1;
- launcher->fd = weston_environment_get_fd("WESTON_LAUNCHER_SOCK");
- if (launcher->fd != -1) {
- launcher->tty = weston_environment_get_fd("WESTON_TTY_FD");
- /* We don't get a chance to read out the original kb
- * mode for the tty, so just hard code K_UNICODE here
- * in case we have to clean if weston-launch dies. */
- launcher->kb_mode = K_UNICODE;
-
- loop = wl_display_get_event_loop(compositor->wl_display);
- launcher->source = wl_event_loop_add_fd(loop, launcher->fd,
- WL_EVENT_READABLE,
- launcher_weston_launch_data,
- launcher);
- if (launcher->source == NULL) {
- free(launcher);
- return -ENOMEM;
- }
-
- return 0;
- } else {
- return -1;
- }
-}
-
-static void
-launcher_weston_launch_destroy(struct weston_launcher *launcher_base)
-{
- struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base);
-
- if (launcher->fd != -1) {
- close(launcher->fd);
- wl_event_source_remove(launcher->source);
- } else {
- launcher_weston_launch_restore(&launcher->base);
- }
-
- if (launcher->tty >= 0)
- close(launcher->tty);
-
- free(launcher);
-}
-
-struct launcher_interface launcher_weston_launch_iface = {
- launcher_weston_launch_connect,
- launcher_weston_launch_destroy,
- launcher_weston_launch_open,
- launcher_weston_launch_close,
- launcher_weston_launch_activate_vt,
- launcher_weston_launch_restore,
-};
+++ /dev/null
-/*
- * libbacklight - userspace interface to Linux backlight control
- *
- * Copyright © 2012 Intel Corporation
- * Copyright 2010 Red Hat <mjg@redhat.com>
- *
- * 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.
- *
- * Authors:
- * Matthew Garrett <mjg@redhat.com>
- * Tiago Vignatti <vignatti@freedesktop.org>
- */
-
-#include "config.h"
-
-#include "libbacklight.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <linux/types.h>
-#include <dirent.h>
-#include <drm.h>
-#include <fcntl.h>
-#include <malloc.h>
-#include <string.h>
-#include <errno.h>
-
-static long backlight_get(struct backlight *backlight, char *node)
-{
- char buffer[100];
- char *path;
- int fd;
- long value, ret;
-
- if (asprintf(&path, "%s/%s", backlight->path, node) < 0)
- return -ENOMEM;
- fd = open(path, O_RDONLY);
- if (fd < 0) {
- ret = -1;
- goto out;
- }
-
- ret = read(fd, &buffer, sizeof(buffer));
- if (ret < 1) {
- ret = -1;
- goto out;
- }
-
- value = strtol(buffer, NULL, 10);
- ret = value;
-out:
- if (fd >= 0)
- close(fd);
- free(path);
- return ret;
-}
-
-long backlight_get_brightness(struct backlight *backlight)
-{
- return backlight_get(backlight, "brightness");
-}
-
-long backlight_get_max_brightness(struct backlight *backlight)
-{
- return backlight_get(backlight, "max_brightness");
-}
-
-long backlight_get_actual_brightness(struct backlight *backlight)
-{
- return backlight_get(backlight, "actual_brightness");
-}
-
-long backlight_set_brightness(struct backlight *backlight, long brightness)
-{
- char *path;
- char *buffer = NULL;
- int fd;
- long ret;
-
- if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0)
- return -ENOMEM;
-
- fd = open(path, O_RDWR);
- if (fd < 0) {
- ret = -1;
- goto out;
- }
-
- ret = read(fd, &buffer, sizeof(buffer));
- if (ret < 1) {
- ret = -1;
- goto out;
- }
-
- if (asprintf(&buffer, "%ld", brightness) < 0) {
- ret = -1;
- goto out;
- }
-
- ret = write(fd, buffer, strlen(buffer));
- if (ret < 0) {
- ret = -1;
- goto out;
- }
-
- ret = backlight_get_brightness(backlight);
- backlight->brightness = ret;
-out:
- free(buffer);
- free(path);
- if (fd >= 0)
- close(fd);
- return ret;
-}
-
-void backlight_destroy(struct backlight *backlight)
-{
- if (!backlight)
- return;
-
- if (backlight->path)
- free(backlight->path);
-
- free(backlight);
-}
-
-struct backlight *backlight_init(struct udev_device *drm_device,
- uint32_t connector_type)
-{
- const char *syspath = NULL;
- char *pci_name = NULL;
- char *chosen_path = NULL;
- char *path = NULL;
- DIR *backlights = NULL;
- struct dirent *entry;
- enum backlight_type type = 0;
- char buffer[100];
- struct backlight *backlight = NULL;
- int ret;
-
- if (!drm_device)
- return NULL;
-
- syspath = udev_device_get_syspath(drm_device);
- if (!syspath)
- return NULL;
-
- if (asprintf(&path, "%s/%s", syspath, "device") < 0)
- return NULL;
-
- ret = readlink(path, buffer, sizeof(buffer) - 1);
- free(path);
- if (ret < 0)
- return NULL;
-
- buffer[ret] = '\0';
- pci_name = basename(buffer);
-
- if (connector_type <= 0)
- return NULL;
-
- backlights = opendir("/sys/class/backlight");
- if (!backlights)
- return NULL;
-
- /* Find the "best" backlight for the device. Firmware
- interfaces are preferred over platform interfaces are
- preferred over raw interfaces. For raw interfaces we'll
- check if the device ID in the form of pci match, while
- for firmware interfaces we require the pci ID to
- match. It's assumed that platform interfaces always match,
- since we can't actually associate them with IDs.
-
- A further awkwardness is that, while it's theoretically
- possible for an ACPI interface to include support for
- changing the backlight of external devices, it's unlikely
- to ever be done. It's effectively impossible for a platform
- interface to do so. So if we get asked about anything that
- isn't LVDS or eDP, we pretty much have to require that the
- control be supplied via a raw interface */
-
- while ((entry = readdir(backlights))) {
- char *backlight_path;
- char *parent;
- enum backlight_type entry_type;
- int fd;
-
- if (entry->d_name[0] == '.')
- continue;
-
- if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight",
- entry->d_name) < 0)
- goto err;
-
- if (asprintf(&path, "%s/%s", backlight_path, "type") < 0) {
- free(backlight_path);
- goto err;
- }
-
- fd = open(path, O_RDONLY);
-
- if (fd < 0)
- goto out;
-
- ret = read (fd, &buffer, sizeof(buffer));
- close (fd);
-
- if (ret < 1)
- goto out;
-
- buffer[ret] = '\0';
-
- if (!strncmp(buffer, "raw\n", sizeof(buffer)))
- entry_type = BACKLIGHT_RAW;
- else if (!strncmp(buffer, "platform\n", sizeof(buffer)))
- entry_type = BACKLIGHT_PLATFORM;
- else if (!strncmp(buffer, "firmware\n", sizeof(buffer)))
- entry_type = BACKLIGHT_FIRMWARE;
- else
- goto out;
-
- if (connector_type != DRM_MODE_CONNECTOR_LVDS &&
- connector_type != DRM_MODE_CONNECTOR_eDP) {
- /* External displays are assumed to require
- gpu control at the moment */
- if (entry_type != BACKLIGHT_RAW)
- goto out;
- }
-
- free (path);
-
- if (asprintf(&path, "%s/%s", backlight_path, "device") < 0)
- goto err;
-
- ret = readlink(path, buffer, sizeof(buffer) - 1);
-
- if (ret < 0)
- goto out;
-
- buffer[ret] = '\0';
-
- parent = basename(buffer);
-
- /* Perform matching for raw and firmware backlights -
- platform backlights have to be assumed to match */
- if (entry_type == BACKLIGHT_RAW ||
- entry_type == BACKLIGHT_FIRMWARE) {
- if (!(pci_name && !strcmp(pci_name, parent)))
- goto out;
- }
-
- if (entry_type < type)
- goto out;
-
- type = entry_type;
-
- if (chosen_path)
- free(chosen_path);
- chosen_path = strdup(backlight_path);
-
- out:
- free(backlight_path);
- free(path);
- }
-
- if (!chosen_path)
- goto err;
-
- backlight = malloc(sizeof(struct backlight));
-
- if (!backlight)
- goto err;
-
- backlight->path = chosen_path;
- backlight->type = type;
-
- backlight->max_brightness = backlight_get_max_brightness(backlight);
- if (backlight->max_brightness < 0)
- goto err;
-
- backlight->brightness = backlight_get_actual_brightness(backlight);
- if (backlight->brightness < 0)
- goto err;
-
- closedir(backlights);
- return backlight;
-err:
- closedir(backlights);
- free (chosen_path);
- free (backlight);
- return NULL;
-}
+++ /dev/null
-/*
- * libbacklight - userspace interface to Linux backlight control
- *
- * Copyright © 2012 Intel Corporation
- * Copyright 2010 Red Hat <mjg@redhat.com>
- *
- * 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.
- *
- * Authors:
- * Matthew Garrett <mjg@redhat.com>
- * Tiago Vignatti <vignatti@freedesktop.org>
- */
-#ifndef LIBBACKLIGHT_H
-#define LIBBACKLIGHT_H
-#include <libudev.h>
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-enum backlight_type {
- BACKLIGHT_RAW,
- BACKLIGHT_PLATFORM,
- BACKLIGHT_FIRMWARE
-};
-
-struct backlight {
- char *path;
- int max_brightness;
- int brightness;
- enum backlight_type type;
-};
-
-/*
- * Find and set up a backlight for a valid udev connector device, i.e. one
- * matching drm subsytem and with status of connected.
- */
-struct backlight *backlight_init(struct udev_device *drm_device,
- uint32_t connector_type);
-
-/* Free backlight resources */
-void backlight_destroy(struct backlight *backlight);
-
-/* Provide the maximum backlight value */
-long backlight_get_max_brightness(struct backlight *backlight);
-
-/* Provide the cached backlight value */
-long backlight_get_brightness(struct backlight *backlight);
-
-/* Provide the hardware backlight value */
-long backlight_get_actual_brightness(struct backlight *backlight);
-
-/* Set the backlight to a value between 0 and max */
-long backlight_set_brightness(struct backlight *backlight, long brightness);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBBACKLIGHT_H */
+++ /dev/null
-/*
- * Copyright © 2010 Intel Corporation
- * Copyright © 2013 Jonas Ådahl
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <linux/input.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <mtdev.h>
-#include <assert.h>
-#include <libinput.h>
-
-#include "compositor.h"
-#include "libinput-device.h"
-#include "shared/helpers.h"
-
-void
-evdev_led_update(struct evdev_device *device, enum weston_led weston_leds)
-{
- enum libinput_led leds = 0;
-
- if (weston_leds & LED_NUM_LOCK)
- leds |= LIBINPUT_LED_NUM_LOCK;
- if (weston_leds & LED_CAPS_LOCK)
- leds |= LIBINPUT_LED_CAPS_LOCK;
- if (weston_leds & LED_SCROLL_LOCK)
- leds |= LIBINPUT_LED_SCROLL_LOCK;
-
- libinput_device_led_update(device->device, leds);
-}
-
-static void
-handle_keyboard_key(struct libinput_device *libinput_device,
- struct libinput_event_keyboard *keyboard_event)
-{
- struct evdev_device *device =
- libinput_device_get_user_data(libinput_device);
- int key_state =
- libinput_event_keyboard_get_key_state(keyboard_event);
- int seat_key_count =
- libinput_event_keyboard_get_seat_key_count(keyboard_event);
-
- /* Ignore key events that are not seat wide state changes. */
- if ((key_state == LIBINPUT_KEY_STATE_PRESSED &&
- seat_key_count != 1) ||
- (key_state == LIBINPUT_KEY_STATE_RELEASED &&
- seat_key_count != 0))
- return;
-
- notify_key(device->seat,
- libinput_event_keyboard_get_time(keyboard_event),
- libinput_event_keyboard_get_key(keyboard_event),
- key_state, STATE_UPDATE_AUTOMATIC);
-}
-
-static bool
-handle_pointer_motion(struct libinput_device *libinput_device,
- struct libinput_event_pointer *pointer_event)
-{
- struct evdev_device *device =
- libinput_device_get_user_data(libinput_device);
- struct weston_pointer_motion_event event = { 0 };
-
- event = (struct weston_pointer_motion_event) {
- .mask = WESTON_POINTER_MOTION_REL,
- .dx = libinput_event_pointer_get_dx(pointer_event),
- .dy = libinput_event_pointer_get_dy(pointer_event),
- };
-
- notify_motion(device->seat,
- libinput_event_pointer_get_time(pointer_event),
- &event);
-
- return true;
-}
-
-static bool
-handle_pointer_motion_absolute(
- struct libinput_device *libinput_device,
- struct libinput_event_pointer *pointer_event)
-{
- struct evdev_device *device =
- libinput_device_get_user_data(libinput_device);
- struct weston_output *output = device->output;
- uint32_t time;
- double x, y;
- uint32_t width, height;
-
- if (!output)
- return false;
-
- time = libinput_event_pointer_get_time(pointer_event);
- width = device->output->current_mode->width;
- height = device->output->current_mode->height;
-
- x = libinput_event_pointer_get_absolute_x_transformed(pointer_event,
- width);
- y = libinput_event_pointer_get_absolute_y_transformed(pointer_event,
- height);
-
- weston_output_transform_coordinate(device->output, x, y, &x, &y);
- notify_motion_absolute(device->seat, time, x, y);
-
- return true;
-}
-
-static bool
-handle_pointer_button(struct libinput_device *libinput_device,
- struct libinput_event_pointer *pointer_event)
-{
- struct evdev_device *device =
- libinput_device_get_user_data(libinput_device);
- int button_state =
- libinput_event_pointer_get_button_state(pointer_event);
- int seat_button_count =
- libinput_event_pointer_get_seat_button_count(pointer_event);
-
- /* Ignore button events that are not seat wide state changes. */
- if ((button_state == LIBINPUT_BUTTON_STATE_PRESSED &&
- seat_button_count != 1) ||
- (button_state == LIBINPUT_BUTTON_STATE_RELEASED &&
- seat_button_count != 0))
- return false;
-
- notify_button(device->seat,
- libinput_event_pointer_get_time(pointer_event),
- libinput_event_pointer_get_button(pointer_event),
- button_state);
-
- return true;
-}
-
-static double
-normalize_scroll(struct libinput_event_pointer *pointer_event,
- enum libinput_pointer_axis axis)
-{
- enum libinput_pointer_axis_source source;
- double value = 0.0;
-
- source = libinput_event_pointer_get_axis_source(pointer_event);
- /* libinput < 0.8 sent wheel click events with value 10. Since 0.8
- the value is the angle of the click in degrees. To keep
- backwards-compat with existing clients, we just send multiples of
- the click count.
- */
- switch (source) {
- case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
- value = 10 * libinput_event_pointer_get_axis_value_discrete(
- pointer_event,
- axis);
- break;
- case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
- case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
- value = libinput_event_pointer_get_axis_value(pointer_event,
- axis);
- break;
- }
-
- return value;
-}
-
-static int32_t
-get_axis_discrete(struct libinput_event_pointer *pointer_event,
- enum libinput_pointer_axis axis)
-{
- enum libinput_pointer_axis_source source;
-
- source = libinput_event_pointer_get_axis_source(pointer_event);
-
- if (source != LIBINPUT_POINTER_AXIS_SOURCE_WHEEL)
- return 0;
-
- return libinput_event_pointer_get_axis_value_discrete(pointer_event,
- axis);
-}
-
-static bool
-handle_pointer_axis(struct libinput_device *libinput_device,
- struct libinput_event_pointer *pointer_event)
-{
- static int warned;
- struct evdev_device *device =
- libinput_device_get_user_data(libinput_device);
- double vert, horiz;
- int32_t vert_discrete, horiz_discrete;
- enum libinput_pointer_axis axis;
- struct weston_pointer_axis_event weston_event;
- enum libinput_pointer_axis_source source;
- uint32_t wl_axis_source;
- bool has_vert, has_horiz;
-
- has_vert = libinput_event_pointer_has_axis(pointer_event,
- LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
- has_horiz = libinput_event_pointer_has_axis(pointer_event,
- LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
-
- if (!has_vert && !has_horiz)
- return false;
-
- source = libinput_event_pointer_get_axis_source(pointer_event);
- switch (source) {
- case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
- wl_axis_source = WL_POINTER_AXIS_SOURCE_WHEEL;
- break;
- case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
- wl_axis_source = WL_POINTER_AXIS_SOURCE_FINGER;
- break;
- case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
- wl_axis_source = WL_POINTER_AXIS_SOURCE_CONTINUOUS;
- break;
- default:
- if (warned < 5) {
- weston_log("Unknown scroll source %d.\n", source);
- warned++;
- }
- return false;
- }
-
- notify_axis_source(device->seat, wl_axis_source);
-
- if (has_vert) {
- axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
- vert_discrete = get_axis_discrete(pointer_event, axis);
- vert = normalize_scroll(pointer_event, axis);
-
- weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
- weston_event.value = vert;
- weston_event.discrete = vert_discrete;
- weston_event.has_discrete = (vert_discrete != 0);
-
- notify_axis(device->seat,
- libinput_event_pointer_get_time(pointer_event),
- &weston_event);
- }
-
- if (has_horiz) {
- axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
- horiz_discrete = get_axis_discrete(pointer_event, axis);
- horiz = normalize_scroll(pointer_event, axis);
-
- weston_event.axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL;
- weston_event.value = horiz;
- weston_event.discrete = horiz_discrete;
- weston_event.has_discrete = (horiz_discrete != 0);
-
- notify_axis(device->seat,
- libinput_event_pointer_get_time(pointer_event),
- &weston_event);
- }
-
- return true;
-}
-
-static void
-handle_touch_with_coords(struct libinput_device *libinput_device,
- struct libinput_event_touch *touch_event,
- int touch_type)
-{
- struct evdev_device *device =
- libinput_device_get_user_data(libinput_device);
- double x;
- double y;
- uint32_t width, height;
- uint32_t time;
- int32_t slot;
-
- if (!device->output)
- return;
-
- time = libinput_event_touch_get_time(touch_event);
- slot = libinput_event_touch_get_seat_slot(touch_event);
-
- width = device->output->current_mode->width;
- height = device->output->current_mode->height;
- x = libinput_event_touch_get_x_transformed(touch_event, width);
- y = libinput_event_touch_get_y_transformed(touch_event, height);
-
- weston_output_transform_coordinate(device->output,
- x, y, &x, &y);
-
- notify_touch(device->seat, time, slot, x, y, touch_type);
-}
-
-static void
-handle_touch_down(struct libinput_device *device,
- struct libinput_event_touch *touch_event)
-{
- handle_touch_with_coords(device, touch_event, WL_TOUCH_DOWN);
-}
-
-static void
-handle_touch_motion(struct libinput_device *device,
- struct libinput_event_touch *touch_event)
-{
- handle_touch_with_coords(device, touch_event, WL_TOUCH_MOTION);
-}
-
-static void
-handle_touch_up(struct libinput_device *libinput_device,
- struct libinput_event_touch *touch_event)
-{
- struct evdev_device *device =
- libinput_device_get_user_data(libinput_device);
- uint32_t time = libinput_event_touch_get_time(touch_event);
- int32_t slot = libinput_event_touch_get_seat_slot(touch_event);
-
- notify_touch(device->seat, time, slot, 0, 0, WL_TOUCH_UP);
-}
-
-static void
-handle_touch_frame(struct libinput_device *libinput_device,
- struct libinput_event_touch *touch_event)
-{
- struct evdev_device *device =
- libinput_device_get_user_data(libinput_device);
- struct weston_seat *seat = device->seat;
-
- notify_touch_frame(seat);
-}
-
-int
-evdev_device_process_event(struct libinput_event *event)
-{
- struct libinput_device *libinput_device =
- libinput_event_get_device(event);
- struct evdev_device *device =
- libinput_device_get_user_data(libinput_device);
- int handled = 1;
- bool need_frame = false;
-
- switch (libinput_event_get_type(event)) {
- case LIBINPUT_EVENT_KEYBOARD_KEY:
- handle_keyboard_key(libinput_device,
- libinput_event_get_keyboard_event(event));
- break;
- case LIBINPUT_EVENT_POINTER_MOTION:
- need_frame = handle_pointer_motion(libinput_device,
- libinput_event_get_pointer_event(event));
- break;
- case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
- need_frame = handle_pointer_motion_absolute(
- libinput_device,
- libinput_event_get_pointer_event(event));
- break;
- case LIBINPUT_EVENT_POINTER_BUTTON:
- need_frame = handle_pointer_button(libinput_device,
- libinput_event_get_pointer_event(event));
- break;
- case LIBINPUT_EVENT_POINTER_AXIS:
- need_frame = handle_pointer_axis(
- libinput_device,
- libinput_event_get_pointer_event(event));
- break;
- case LIBINPUT_EVENT_TOUCH_DOWN:
- handle_touch_down(libinput_device,
- libinput_event_get_touch_event(event));
- break;
- case LIBINPUT_EVENT_TOUCH_MOTION:
- handle_touch_motion(libinput_device,
- libinput_event_get_touch_event(event));
- break;
- case LIBINPUT_EVENT_TOUCH_UP:
- handle_touch_up(libinput_device,
- libinput_event_get_touch_event(event));
- break;
- case LIBINPUT_EVENT_TOUCH_FRAME:
- handle_touch_frame(libinput_device,
- libinput_event_get_touch_event(event));
- break;
- default:
- handled = 0;
- weston_log("unknown libinput event %d\n",
- libinput_event_get_type(event));
- }
-
- if (need_frame)
- notify_pointer_frame(device->seat);
-
- return handled;
-}
-
-static void
-notify_output_destroy(struct wl_listener *listener, void *data)
-{
- struct evdev_device *device =
- container_of(listener,
- struct evdev_device, output_destroy_listener);
- struct weston_compositor *c = device->seat->compositor;
- struct weston_output *output;
-
- if (!device->output_name && !wl_list_empty(&c->output_list)) {
- output = container_of(c->output_list.next,
- struct weston_output, link);
- evdev_device_set_output(device, output);
- } else {
- device->output = NULL;
- }
-}
-
-/**
- * The WL_CALIBRATION property requires a pixel-specific matrix to be
- * applied after scaling device coordinates to screen coordinates. libinput
- * can't do that, so we need to convert the calibration to the normalized
- * format libinput expects.
- */
-void
-evdev_device_set_calibration(struct evdev_device *device)
-{
- struct udev *udev;
- struct udev_device *udev_device = NULL;
- const char *sysname = libinput_device_get_sysname(device->device);
- const char *calibration_values;
- uint32_t width, height;
- float calibration[6];
- enum libinput_config_status status;
-
- if (!device->output)
- return;
-
- width = device->output->width;
- height = device->output->height;
- if (width == 0 || height == 0)
- return;
-
- /* If libinput has a pre-set calibration matrix, don't override it */
- if (!libinput_device_config_calibration_has_matrix(device->device) ||
- libinput_device_config_calibration_get_default_matrix(
- device->device,
- calibration) != 0)
- return;
-
- udev = udev_new();
- if (!udev)
- return;
-
- udev_device = udev_device_new_from_subsystem_sysname(udev,
- "input",
- sysname);
- if (!udev_device)
- goto out;
-
- calibration_values =
- udev_device_get_property_value(udev_device,
- "WL_CALIBRATION");
-
- if (!calibration_values || sscanf(calibration_values,
- "%f %f %f %f %f %f",
- &calibration[0],
- &calibration[1],
- &calibration[2],
- &calibration[3],
- &calibration[4],
- &calibration[5]) != 6)
- goto out;
-
- weston_log("Applying calibration: %f %f %f %f %f %f "
- "(normalized %f %f)\n",
- calibration[0],
- calibration[1],
- calibration[2],
- calibration[3],
- calibration[4],
- calibration[5],
- calibration[2] / width,
- calibration[5] / height);
-
- /* normalize to a format libinput can use. There is a chance of
- this being wrong if the width/height don't match the device
- width/height but I'm not sure how to fix that */
- calibration[2] /= width;
- calibration[5] /= height;
-
- status = libinput_device_config_calibration_set_matrix(device->device,
- calibration);
- if (status != LIBINPUT_CONFIG_STATUS_SUCCESS)
- weston_log("Failed to apply calibration.\n");
-
-out:
- if (udev_device)
- udev_device_unref(udev_device);
- udev_unref(udev);
-}
-
-void
-evdev_device_set_output(struct evdev_device *device,
- struct weston_output *output)
-{
- if (device->output_destroy_listener.notify) {
- wl_list_remove(&device->output_destroy_listener.link);
- device->output_destroy_listener.notify = NULL;
- }
-
- device->output = output;
- device->output_destroy_listener.notify = notify_output_destroy;
- wl_signal_add(&output->destroy_signal,
- &device->output_destroy_listener);
- evdev_device_set_calibration(device);
-}
-
-struct evdev_device *
-evdev_device_create(struct libinput_device *libinput_device,
- struct weston_seat *seat)
-{
- struct evdev_device *device;
-
- device = zalloc(sizeof *device);
- if (device == NULL)
- return NULL;
-
- device->seat = seat;
- wl_list_init(&device->link);
- device->device = libinput_device;
-
- if (libinput_device_has_capability(libinput_device,
- LIBINPUT_DEVICE_CAP_KEYBOARD)) {
- weston_seat_init_keyboard(seat, NULL);
- device->seat_caps |= EVDEV_SEAT_KEYBOARD;
- }
- if (libinput_device_has_capability(libinput_device,
- LIBINPUT_DEVICE_CAP_POINTER)) {
- weston_seat_init_pointer(seat);
- device->seat_caps |= EVDEV_SEAT_POINTER;
- }
- if (libinput_device_has_capability(libinput_device,
- LIBINPUT_DEVICE_CAP_TOUCH)) {
- weston_seat_init_touch(seat);
- device->seat_caps |= EVDEV_SEAT_TOUCH;
- }
-
- libinput_device_set_user_data(libinput_device, device);
- libinput_device_ref(libinput_device);
-
- return device;
-}
-
-void
-evdev_device_destroy(struct evdev_device *device)
-{
- if (device->seat_caps & EVDEV_SEAT_POINTER)
- weston_seat_release_pointer(device->seat);
- if (device->seat_caps & EVDEV_SEAT_KEYBOARD)
- weston_seat_release_keyboard(device->seat);
- if (device->seat_caps & EVDEV_SEAT_TOUCH)
- weston_seat_release_touch(device->seat);
-
- if (device->output)
- wl_list_remove(&device->output_destroy_listener.link);
- wl_list_remove(&device->link);
- libinput_device_unref(device->device);
- free(device->devnode);
- free(device->output_name);
- free(device);
-}
-
-void
-evdev_notify_keyboard_focus(struct weston_seat *seat,
- struct wl_list *evdev_devices)
-{
- struct wl_array keys;
-
- if (seat->keyboard_device_count == 0)
- return;
-
- wl_array_init(&keys);
- notify_keyboard_focus_in(seat, &keys, STATE_UPDATE_AUTOMATIC);
- wl_array_release(&keys);
-}
+++ /dev/null
-/*
- * Copyright © 2011, 2012 Intel Corporation
- *
- * 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.
- */
-
-#ifndef _LIBINPUT_DEVICE_H_
-#define _LIBINPUT_DEVICE_H_
-
-#include "config.h"
-
-#include <linux/input.h>
-#include <wayland-util.h>
-#include <libinput.h>
-
-#include "compositor.h"
-
-enum evdev_device_seat_capability {
- EVDEV_SEAT_POINTER = (1 << 0),
- EVDEV_SEAT_KEYBOARD = (1 << 1),
- EVDEV_SEAT_TOUCH = (1 << 2)
-};
-
-struct evdev_device {
- struct weston_seat *seat;
- enum evdev_device_seat_capability seat_caps;
- struct libinput_device *device;
- struct wl_list link;
- struct weston_output *output;
- struct wl_listener output_destroy_listener;
- char *devnode;
- char *output_name;
- int fd;
-};
-
-void
-evdev_led_update(struct evdev_device *device, enum weston_led leds);
-
-struct evdev_device *
-evdev_device_create(struct libinput_device *libinput_device,
- struct weston_seat *seat);
-
-int
-evdev_device_process_event(struct libinput_event *event);
-
-void
-evdev_device_set_output(struct evdev_device *device,
- struct weston_output *output);
-void
-evdev_device_destroy(struct evdev_device *device);
-
-void
-evdev_notify_keyboard_focus(struct weston_seat *seat,
- struct wl_list *evdev_devices);
-void
-evdev_device_set_calibration(struct evdev_device *device);
-
-int
-dispatch_libinput(struct libinput *libinput);
-
-#endif /* _LIBINPUT_DEVICE_H_ */
+++ /dev/null
-/*
- * Copyright © 2013 Intel Corporation
- * Copyright © 2013 Jonas Ådahl
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <libinput.h>
-#include <libudev.h>
-
-#include "compositor.h"
-#include "launcher-util.h"
-#include "libinput-seat.h"
-#include "libinput-device.h"
-#include "shared/helpers.h"
-
-static void
-process_events(struct udev_input *input);
-static struct udev_seat *
-udev_seat_create(struct udev_input *input, const char *seat_name);
-static void
-udev_seat_destroy(struct udev_seat *seat);
-
-static struct udev_seat *
-get_udev_seat(struct udev_input *input, struct libinput_device *device)
-{
- struct libinput_seat *libinput_seat;
- const char *seat_name;
-
- libinput_seat = libinput_device_get_seat(device);
- seat_name = libinput_seat_get_logical_name(libinput_seat);
- return udev_seat_get_named(input, seat_name);
-}
-
-static void
-device_added(struct udev_input *input, struct libinput_device *libinput_device)
-{
- struct weston_compositor *c;
- struct evdev_device *device;
- struct weston_output *output;
- const char *output_name;
- struct weston_seat *seat;
- struct udev_seat *udev_seat;
- struct weston_pointer *pointer;
-
- c = input->compositor;
-
- udev_seat = get_udev_seat(input, libinput_device);
- if (!udev_seat)
- return;
-
- seat = &udev_seat->base;
- device = evdev_device_create(libinput_device, seat);
- if (device == NULL)
- return;
-
- if (input->configure_device != NULL)
- input->configure_device(c, device->device);
- evdev_device_set_calibration(device);
- udev_seat = (struct udev_seat *) seat;
- wl_list_insert(udev_seat->devices_list.prev, &device->link);
-
- pointer = weston_seat_get_pointer(seat);
- if (seat->output && pointer)
- weston_pointer_clamp(pointer,
- &pointer->x,
- &pointer->y);
-
- output_name = libinput_device_get_output_name(libinput_device);
- if (output_name) {
- device->output_name = strdup(output_name);
- wl_list_for_each(output, &c->output_list, link)
- if (output->name &&
- strcmp(output->name, device->output_name) == 0)
- evdev_device_set_output(device, output);
- } else if (device->output == NULL && !wl_list_empty(&c->output_list)) {
- output = container_of(c->output_list.next,
- struct weston_output, link);
- evdev_device_set_output(device, output);
- }
-
- if (!input->suspended)
- weston_seat_repick(seat);
-}
-
-static void
-device_removed(struct udev_input *input, struct libinput_device *libinput_device)
-{
- struct evdev_device *device;
-
- device = libinput_device_get_user_data(libinput_device);
- evdev_device_destroy(device);
-}
-
-static void
-udev_seat_remove_devices(struct udev_seat *seat)
-{
- struct evdev_device *device, *next;
-
- wl_list_for_each_safe(device, next, &seat->devices_list, link) {
- evdev_device_destroy(device);
- }
-}
-
-void
-udev_input_disable(struct udev_input *input)
-{
- if (input->suspended)
- return;
-
- libinput_suspend(input->libinput);
- process_events(input);
- input->suspended = 1;
-}
-
-static int
-udev_input_process_event(struct libinput_event *event)
-{
- struct libinput *libinput = libinput_event_get_context(event);
- struct libinput_device *libinput_device =
- libinput_event_get_device(event);
- struct udev_input *input = libinput_get_user_data(libinput);
- int handled = 1;
-
- switch (libinput_event_get_type(event)) {
- case LIBINPUT_EVENT_DEVICE_ADDED:
- device_added(input, libinput_device);
- break;
- case LIBINPUT_EVENT_DEVICE_REMOVED:
- device_removed(input, libinput_device);
- break;
- default:
- handled = 0;
- }
-
- return handled;
-}
-
-static void
-process_event(struct libinput_event *event)
-{
- if (udev_input_process_event(event))
- return;
- if (evdev_device_process_event(event))
- return;
-}
-
-static void
-process_events(struct udev_input *input)
-{
- struct libinput_event *event;
-
- while ((event = libinput_get_event(input->libinput))) {
- process_event(event);
- libinput_event_destroy(event);
- }
-}
-
-static int
-udev_input_dispatch(struct udev_input *input)
-{
- if (libinput_dispatch(input->libinput) != 0)
- weston_log("libinput: Failed to dispatch libinput\n");
-
- process_events(input);
-
- return 0;
-}
-
-static int
-libinput_source_dispatch(int fd, uint32_t mask, void *data)
-{
- struct udev_input *input = data;
-
- return udev_input_dispatch(input) != 0;
-}
-
-static int
-open_restricted(const char *path, int flags, void *user_data)
-{
- struct udev_input *input = user_data;
- struct weston_launcher *launcher = input->compositor->launcher;
-
- return weston_launcher_open(launcher, path, flags);
-}
-
-static void
-close_restricted(int fd, void *user_data)
-{
- struct udev_input *input = user_data;
- struct weston_launcher *launcher = input->compositor->launcher;
-
- weston_launcher_close(launcher, fd);
-}
-
-const struct libinput_interface libinput_interface = {
- open_restricted,
- close_restricted,
-};
-
-int
-udev_input_enable(struct udev_input *input)
-{
- struct wl_event_loop *loop;
- struct weston_compositor *c = input->compositor;
- int fd;
- struct udev_seat *seat;
- int devices_found = 0;
-
- loop = wl_display_get_event_loop(c->wl_display);
- fd = libinput_get_fd(input->libinput);
- input->libinput_source =
- wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
- libinput_source_dispatch, input);
- if (!input->libinput_source) {
- return -1;
- }
-
- if (input->suspended) {
- if (libinput_resume(input->libinput) != 0) {
- wl_event_source_remove(input->libinput_source);
- input->libinput_source = NULL;
- return -1;
- }
- input->suspended = 0;
- process_events(input);
- }
-
- wl_list_for_each(seat, &input->compositor->seat_list, base.link) {
- evdev_notify_keyboard_focus(&seat->base, &seat->devices_list);
-
- if (!wl_list_empty(&seat->devices_list))
- devices_found = 1;
- }
-
- if (devices_found == 0) {
- weston_log(
- "warning: no input devices on entering Weston. "
- "Possible causes:\n"
- "\t- no permissions to read /dev/input/event*\n"
- "\t- seats misconfigured "
- "(Weston backend option 'seat', "
- "udev device property ID_SEAT)\n");
- return -1;
- }
-
- return 0;
-}
-
-static void
-libinput_log_func(struct libinput *libinput,
- enum libinput_log_priority priority,
- const char *format, va_list args)
-{
- weston_vlog(format, args);
-}
-
-int
-udev_input_init(struct udev_input *input, struct weston_compositor *c,
- struct udev *udev, const char *seat_id,
- udev_configure_device_t configure_device)
-{
- enum libinput_log_priority priority = LIBINPUT_LOG_PRIORITY_INFO;
- const char *log_priority = NULL;
-
- memset(input, 0, sizeof *input);
-
- input->compositor = c;
- input->configure_device = configure_device;
-
- log_priority = getenv("WESTON_LIBINPUT_LOG_PRIORITY");
-
- input->libinput = libinput_udev_create_context(&libinput_interface,
- input, udev);
- if (!input->libinput) {
- return -1;
- }
-
- libinput_log_set_handler(input->libinput, &libinput_log_func);
-
- if (log_priority) {
- if (strcmp(log_priority, "debug") == 0) {
- priority = LIBINPUT_LOG_PRIORITY_DEBUG;
- } else if (strcmp(log_priority, "info") == 0) {
- priority = LIBINPUT_LOG_PRIORITY_INFO;
- } else if (strcmp(log_priority, "error") == 0) {
- priority = LIBINPUT_LOG_PRIORITY_ERROR;
- }
- }
-
- libinput_log_set_priority(input->libinput, priority);
-
- if (libinput_udev_assign_seat(input->libinput, seat_id) != 0) {
- libinput_unref(input->libinput);
- return -1;
- }
-
- process_events(input);
-
- return udev_input_enable(input);
-}
-
-void
-udev_input_destroy(struct udev_input *input)
-{
- struct udev_seat *seat, *next;
-
- wl_event_source_remove(input->libinput_source);
- wl_list_for_each_safe(seat, next, &input->compositor->seat_list, base.link)
- udev_seat_destroy(seat);
- libinput_unref(input->libinput);
-}
-
-static void
-udev_seat_led_update(struct weston_seat *seat_base, enum weston_led leds)
-{
- struct udev_seat *seat = (struct udev_seat *) seat_base;
- struct evdev_device *device;
-
- wl_list_for_each(device, &seat->devices_list, link)
- evdev_led_update(device, leds);
-}
-
-static void
-notify_output_create(struct wl_listener *listener, void *data)
-{
- struct udev_seat *seat = container_of(listener, struct udev_seat,
- output_create_listener);
- struct evdev_device *device;
- struct weston_output *output = data;
-
- wl_list_for_each(device, &seat->devices_list, link) {
- if (device->output_name &&
- strcmp(output->name, device->output_name) == 0) {
- evdev_device_set_output(device, output);
- }
-
- if (device->output_name == NULL && device->output == NULL)
- evdev_device_set_output(device, output);
- }
-}
-
-static struct udev_seat *
-udev_seat_create(struct udev_input *input, const char *seat_name)
-{
- struct weston_compositor *c = input->compositor;
- struct udev_seat *seat;
-
- seat = zalloc(sizeof *seat);
- if (!seat)
- return NULL;
-
- weston_seat_init(&seat->base, c, seat_name);
- seat->base.led_update = udev_seat_led_update;
-
- seat->output_create_listener.notify = notify_output_create;
- wl_signal_add(&c->output_created_signal,
- &seat->output_create_listener);
-
- wl_list_init(&seat->devices_list);
-
- return seat;
-}
-
-static void
-udev_seat_destroy(struct udev_seat *seat)
-{
- struct weston_keyboard *keyboard =
- weston_seat_get_keyboard(&seat->base);
-
- if (keyboard)
- notify_keyboard_focus_out(&seat->base);
-
- udev_seat_remove_devices(seat);
- weston_seat_release(&seat->base);
- wl_list_remove(&seat->output_create_listener.link);
- free(seat);
-}
-
-struct udev_seat *
-udev_seat_get_named(struct udev_input *input, const char *seat_name)
-{
- struct udev_seat *seat;
-
- wl_list_for_each(seat, &input->compositor->seat_list, base.link) {
- if (strcmp(seat->base.seat_name, seat_name) == 0)
- return seat;
- }
-
- return udev_seat_create(input, seat_name);
-}
+++ /dev/null
-/*
- * Copyright © 2013 Intel Corporation
- * Copyright © 2013 Jonas Ådahl
- *
- * 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.
- */
-
-#ifndef _LIBINPUT_SEAT_H_
-#define _LIBINPUT_SEAT_H_
-
-#include "config.h"
-
-#include <libudev.h>
-
-#include "compositor.h"
-
-struct libinput_device;
-
-struct udev_seat {
- struct weston_seat base;
- struct wl_list devices_list;
- struct wl_listener output_create_listener;
-};
-
-typedef void (*udev_configure_device_t)(struct weston_compositor *compositor,
- struct libinput_device *device);
-
-struct udev_input {
- struct libinput *libinput;
- struct wl_event_source *libinput_source;
- struct weston_compositor *compositor;
- int suspended;
- udev_configure_device_t configure_device;
-};
-
-int
-udev_input_enable(struct udev_input *input);
-void
-udev_input_disable(struct udev_input *input);
-int
-udev_input_init(struct udev_input *input,
- struct weston_compositor *c,
- struct udev *udev,
- const char *seat_id,
- udev_configure_device_t configure_device);
-void
-udev_input_destroy(struct udev_input *input);
-
-struct udev_seat *
-udev_seat_get_named(struct udev_input *u,
- const char *seat_name);
-
-#endif
+++ /dev/null
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-pkgincludedir=${includedir}/libweston-@LIBWESTON_ABI_VERSION@
-
-Name: libweston API
-Description: Header files for libweston compositors development
-Version: @WESTON_VERSION@
-Requires.private: wayland-server pixman-1 xkbcommon
-Cflags: -I${pkgincludedir}
-Libs: -L${libdir} -lweston-@LIBWESTON_ABI_VERSION@
+++ /dev/null
-/*
- * Copyright © 2014, 2015 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 "config.h"
-
-#include <assert.h>
-#include <unistd.h>
-#include <sys/types.h>
-
-#include "compositor.h"
-#include "linux-dmabuf.h"
-#include "linux-dmabuf-unstable-v1-server-protocol.h"
-
-static void
-linux_dmabuf_buffer_destroy(struct linux_dmabuf_buffer *buffer)
-{
- int i;
-
- for (i = 0; i < buffer->attributes.n_planes; i++) {
- close(buffer->attributes.fd[i]);
- buffer->attributes.fd[i] = -1;
- }
-
- buffer->attributes.n_planes = 0;
- free(buffer);
-}
-
-static void
-destroy_params(struct wl_resource *params_resource)
-{
- struct linux_dmabuf_buffer *buffer;
-
- buffer = wl_resource_get_user_data(params_resource);
-
- if (!buffer)
- return;
-
- linux_dmabuf_buffer_destroy(buffer);
-}
-
-static void
-params_destroy(struct wl_client *client, struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static void
-params_add(struct wl_client *client,
- struct wl_resource *params_resource,
- int32_t name_fd,
- uint32_t plane_idx,
- uint32_t offset,
- uint32_t stride,
- uint32_t modifier_hi,
- uint32_t modifier_lo)
-{
- struct linux_dmabuf_buffer *buffer;
-
- buffer = wl_resource_get_user_data(params_resource);
- if (!buffer) {
- wl_resource_post_error(params_resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
- "params was already used to create a wl_buffer");
- close(name_fd);
- return;
- }
-
- assert(buffer->params_resource == params_resource);
- assert(!buffer->buffer_resource);
-
- if (plane_idx >= MAX_DMABUF_PLANES) {
- wl_resource_post_error(params_resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
- "plane index %u is too high", plane_idx);
- close(name_fd);
- return;
- }
-
- if (buffer->attributes.fd[plane_idx] != -1) {
- wl_resource_post_error(params_resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
- "a dmabuf has already been added for plane %u",
- plane_idx);
- close(name_fd);
- return;
- }
-
- buffer->attributes.fd[plane_idx] = name_fd;
- buffer->attributes.offset[plane_idx] = offset;
- buffer->attributes.stride[plane_idx] = stride;
- buffer->attributes.modifier[plane_idx] = ((uint64_t)modifier_hi << 32) |
- modifier_lo;
- buffer->attributes.n_planes++;
-}
-
-static void
-linux_dmabuf_wl_buffer_destroy(struct wl_client *client,
- struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static const struct wl_buffer_interface linux_dmabuf_buffer_implementation = {
- linux_dmabuf_wl_buffer_destroy
-};
-
-static void
-destroy_linux_dmabuf_wl_buffer(struct wl_resource *resource)
-{
- struct linux_dmabuf_buffer *buffer;
-
- buffer = wl_resource_get_user_data(resource);
- assert(buffer->buffer_resource == resource);
- assert(!buffer->params_resource);
-
- if (buffer->user_data_destroy_func)
- buffer->user_data_destroy_func(buffer);
-
- linux_dmabuf_buffer_destroy(buffer);
-}
-
-static void
-params_create(struct wl_client *client,
- struct wl_resource *params_resource,
- int32_t width,
- int32_t height,
- uint32_t format,
- uint32_t flags)
-{
- struct linux_dmabuf_buffer *buffer;
- int i;
-
- buffer = wl_resource_get_user_data(params_resource);
-
- if (!buffer) {
- wl_resource_post_error(params_resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
- "params was already used to create a wl_buffer");
- return;
- }
-
- assert(buffer->params_resource == params_resource);
- assert(!buffer->buffer_resource);
-
- /* Switch the linux_dmabuf_buffer object from params resource to
- * eventually wl_buffer resource.
- */
- wl_resource_set_user_data(buffer->params_resource, NULL);
- buffer->params_resource = NULL;
-
- if (!buffer->attributes.n_planes) {
- wl_resource_post_error(params_resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
- "no dmabuf has been added to the params");
- goto err_out;
- }
-
- /* Check for holes in the dmabufs set (e.g. [0, 1, 3]) */
- for (i = 0; i < buffer->attributes.n_planes; i++) {
- if (buffer->attributes.fd[i] == -1) {
- wl_resource_post_error(params_resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
- "no dmabuf has been added for plane %i", i);
- goto err_out;
- }
- }
-
- buffer->attributes.width = width;
- buffer->attributes.height = height;
- buffer->attributes.format = format;
- buffer->attributes.flags = flags;
-
- if (width < 1 || height < 1) {
- wl_resource_post_error(params_resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
- "invalid width %d or height %d", width, height);
- goto err_out;
- }
-
- for (i = 0; i < buffer->attributes.n_planes; i++) {
- off_t size;
-
- if ((uint64_t) buffer->attributes.offset[i] + buffer->attributes.stride[i] > UINT32_MAX) {
- wl_resource_post_error(params_resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
- "size overflow for plane %i", i);
- goto err_out;
- }
-
- if (i == 0 &&
- (uint64_t) buffer->attributes.offset[i] +
- (uint64_t) buffer->attributes.stride[i] * height > UINT32_MAX) {
- wl_resource_post_error(params_resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
- "size overflow for plane %i", i);
- goto err_out;
- }
-
- /* Don't report an error as it might be caused
- * by the kernel not supporting seeking on dmabuf */
- size = lseek(buffer->attributes.fd[i], 0, SEEK_END);
- if (size == -1)
- continue;
-
- if (buffer->attributes.offset[i] >= size) {
- wl_resource_post_error(params_resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
- "invalid offset %i for plane %i",
- buffer->attributes.offset[i], i);
- goto err_out;
- }
-
- if (buffer->attributes.offset[i] + buffer->attributes.stride[i] > size) {
- wl_resource_post_error(params_resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
- "invalid stride %i for plane %i",
- buffer->attributes.stride[i], i);
- goto err_out;
- }
-
- /* Only valid for first plane as other planes might be
- * sub-sampled according to fourcc format */
- if (i == 0 &&
- buffer->attributes.offset[i] + buffer->attributes.stride[i] * height > size) {
- wl_resource_post_error(params_resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
- "invalid buffer stride or height for plane %i", i);
- goto err_out;
- }
- }
-
- /* XXX: Some additional sanity checks could be done with respect
- * to the fourcc format. A centralized collection (kernel or
- * libdrm) would be useful to avoid code duplication for these
- * checks (e.g. drm_format_num_planes).
- */
-
- if (!weston_compositor_import_dmabuf(buffer->compositor, buffer))
- goto err_failed;
-
- buffer->buffer_resource = wl_resource_create(client,
- &wl_buffer_interface,
- 1, 0);
- if (!buffer->buffer_resource) {
- wl_resource_post_no_memory(params_resource);
- goto err_buffer;
- }
-
- wl_resource_set_implementation(buffer->buffer_resource,
- &linux_dmabuf_buffer_implementation,
- buffer, destroy_linux_dmabuf_wl_buffer);
-
- zwp_linux_buffer_params_v1_send_created(params_resource,
- buffer->buffer_resource);
-
- return;
-
-err_buffer:
- if (buffer->user_data_destroy_func)
- buffer->user_data_destroy_func(buffer);
-
-err_failed:
- zwp_linux_buffer_params_v1_send_failed(params_resource);
-
-err_out:
- linux_dmabuf_buffer_destroy(buffer);
-}
-
-static const struct zwp_linux_buffer_params_v1_interface
-zwp_linux_buffer_params_implementation = {
- params_destroy,
- params_add,
- params_create
-};
-
-static void
-linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource)
-{
- wl_resource_destroy(resource);
-}
-
-static void
-linux_dmabuf_create_params(struct wl_client *client,
- struct wl_resource *linux_dmabuf_resource,
- uint32_t params_id)
-{
- struct weston_compositor *compositor;
- struct linux_dmabuf_buffer *buffer;
- uint32_t version;
- int i;
-
- version = wl_resource_get_version(linux_dmabuf_resource);
- compositor = wl_resource_get_user_data(linux_dmabuf_resource);
-
- buffer = zalloc(sizeof *buffer);
- if (!buffer)
- goto err_out;
-
- for (i = 0; i < MAX_DMABUF_PLANES; i++)
- buffer->attributes.fd[i] = -1;
-
- buffer->compositor = compositor;
- buffer->params_resource =
- wl_resource_create(client,
- &zwp_linux_buffer_params_v1_interface,
- version, params_id);
- if (!buffer->params_resource)
- goto err_dealloc;
-
- wl_resource_set_implementation(buffer->params_resource,
- &zwp_linux_buffer_params_implementation,
- buffer, destroy_params);
-
- return;
-
-err_dealloc:
- free(buffer);
-
-err_out:
- wl_resource_post_no_memory(linux_dmabuf_resource);
-}
-
-/** Get the linux_dmabuf_buffer from a wl_buffer resource
- *
- * If the given wl_buffer resource was created through the linux_dmabuf
- * protocol interface, returns the linux_dmabuf_buffer object. This can
- * be used as a type check for a wl_buffer.
- *
- * \param resource A wl_buffer resource.
- * \return The linux_dmabuf_buffer if it exists, or NULL otherwise.
- */
-WL_EXPORT struct linux_dmabuf_buffer *
-linux_dmabuf_buffer_get(struct wl_resource *resource)
-{
- struct linux_dmabuf_buffer *buffer;
-
- if (!resource)
- return NULL;
-
- if (!wl_resource_instance_of(resource, &wl_buffer_interface,
- &linux_dmabuf_buffer_implementation))
- return NULL;
-
- buffer = wl_resource_get_user_data(resource);
- assert(buffer);
- assert(!buffer->params_resource);
- assert(buffer->buffer_resource == resource);
-
- return buffer;
-}
-
-/** Set renderer-private data
- *
- * Set the user data for the linux_dmabuf_buffer. It is invalid to overwrite
- * a non-NULL user data with a new non-NULL pointer. This is meant to
- * protect against renderers fighting over linux_dmabuf_buffer user data
- * ownership.
- *
- * The renderer-private data is usually set from the
- * weston_renderer::import_dmabuf hook.
- *
- * \param buffer The linux_dmabuf_buffer object to set for.
- * \param data The new renderer-private data pointer.
- * \param func Destructor function to be called for the renderer-private
- * data when the linux_dmabuf_buffer gets destroyed.
- *
- * \sa weston_compositor_import_dmabuf
- */
-WL_EXPORT void
-linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer,
- void *data,
- dmabuf_user_data_destroy_func func)
-{
- assert(data == NULL || buffer->user_data == NULL);
-
- buffer->user_data = data;
- buffer->user_data_destroy_func = func;
-}
-
-/** Get renderer-private data
- *
- * Get the user data from the linux_dmabuf_buffer.
- *
- * \param buffer The linux_dmabuf_buffer to query.
- * \return Renderer-private data pointer.
- *
- * \sa linux_dmabuf_buffer_set_user_data
- */
-WL_EXPORT void *
-linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer)
-{
- return buffer->user_data;
-}
-
-static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_implementation = {
- linux_dmabuf_destroy,
- linux_dmabuf_create_params
-};
-
-static void
-bind_linux_dmabuf(struct wl_client *client,
- void *data, uint32_t version, uint32_t id)
-{
- struct weston_compositor *compositor = data;
- struct wl_resource *resource;
-
- resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface,
- version, id);
- if (resource == NULL) {
- wl_client_post_no_memory(client);
- return;
- }
-
- wl_resource_set_implementation(resource, &linux_dmabuf_implementation,
- compositor, NULL);
-
- /* EGL_EXT_image_dma_buf_import does not provide a way to query the
- * supported pixel formats. */
- /* XXX: send formats */
-}
-
-/** Advertise linux_dmabuf support
- *
- * Calling this initializes the zwp_linux_dmabuf protocol support, so that
- * the interface will be advertised to clients. Essentially it creates a
- * global. Do not call this function multiple times in the compositor's
- * lifetime. There is no way to deinit explicitly, globals will be reaped
- * when the wl_display gets destroyed.
- *
- * \param compositor The compositor to init for.
- * \return Zero on success, -1 on failure.
- */
-WL_EXPORT int
-linux_dmabuf_setup(struct weston_compositor *compositor)
-{
- if (!wl_global_create(compositor->wl_display,
- &zwp_linux_dmabuf_v1_interface, 1,
- compositor, bind_linux_dmabuf))
- return -1;
-
- return 0;
-}
-
-/** Resolve an internal compositor error by disconnecting the client.
- *
- * This function is used in cases when the dmabuf-based wl_buffer
- * turns out unusable and there is no fallback path. This is used by
- * renderers which are the fallback path in the first place.
- *
- * It is possible the fault is caused by a compositor bug, the underlying
- * graphics stack bug or normal behaviour, or perhaps a client mistake.
- * In any case, the options are to either composite garbage or nothing,
- * or disconnect the client. This is a helper function for the latter.
- *
- * The error is sent as a INVALID_OBJECT error on the client's wl_display.
- *
- * \param buffer The linux_dmabuf_buffer that is unusable.
- * \param msg A custom error message attached to the protocol error.
- */
-WL_EXPORT void
-linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer,
- const char *msg)
-{
- struct wl_client *client;
- struct wl_resource *display_resource;
- uint32_t id;
-
- assert(buffer->buffer_resource);
- id = wl_resource_get_id(buffer->buffer_resource);
- client = wl_resource_get_client(buffer->buffer_resource);
- display_resource = wl_client_get_object(client, 1);
-
- assert(display_resource);
- wl_resource_post_error(display_resource,
- WL_DISPLAY_ERROR_INVALID_OBJECT,
- "linux_dmabuf server error with "
- "wl_buffer@%u: %s", id, msg);
-}
+++ /dev/null
-/*
- * Copyright © 2014, 2015 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.
- */
-
-#ifndef WESTON_LINUX_DMABUF_H
-#define WESTON_LINUX_DMABUF_H
-
-#include <stdint.h>
-
-#define MAX_DMABUF_PLANES 4
-
-struct linux_dmabuf_buffer;
-typedef void (*dmabuf_user_data_destroy_func)(
- struct linux_dmabuf_buffer *buffer);
-
-struct dmabuf_attributes {
- int32_t width;
- int32_t height;
- uint32_t format;
- uint32_t flags; /* enum zlinux_buffer_params_flags */
- int n_planes;
- int fd[MAX_DMABUF_PLANES];
- uint32_t offset[MAX_DMABUF_PLANES];
- uint32_t stride[MAX_DMABUF_PLANES];
- uint64_t modifier[MAX_DMABUF_PLANES];
-};
-
-struct linux_dmabuf_buffer {
- struct wl_resource *buffer_resource;
- struct wl_resource *params_resource;
- struct weston_compositor *compositor;
- struct dmabuf_attributes attributes;
-
- void *user_data;
- dmabuf_user_data_destroy_func user_data_destroy_func;
-
- /* XXX:
- *
- * Add backend private data. This would be for the backend
- * to do all additional imports it might ever use in advance.
- * The basic principle, even if not implemented in drivers today,
- * is that dmabufs are first attached, but the actual allocation
- * is deferred to first use. This would allow the exporter and all
- * attachers to agree on how to allocate.
- *
- * The DRM backend would use this to create drmFBs for each
- * dmabuf_buffer, just in case at some point it would become
- * feasible to scan it out directly. This would improve the
- * possibilities to successfully scan out, avoiding compositing.
- */
-};
-
-int
-linux_dmabuf_setup(struct weston_compositor *compositor);
-
-struct linux_dmabuf_buffer *
-linux_dmabuf_buffer_get(struct wl_resource *resource);
-
-void
-linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer,
- void *data,
- dmabuf_user_data_destroy_func func);
-void *
-linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer);
-
-void
-linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer,
- const char *msg);
-
-#endif /* WESTON_LINUX_DMABUF_H */
+++ /dev/null
-/*
- * Copyright © 2012 Martin Minarik
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-#include <time.h>
-
-#include <wayland-util.h>
-
-#include "compositor.h"
-
-static log_func_t log_handler = 0;
-static log_func_t log_continue_handler = 0;
-
-/** Install the log handler
- *
- * The given functions will be called to output text as passed to the
- * \a weston_log and \a weston_log_continue functions.
- *
- * \param log The log function. This function will be called when
- * \a weston_log is called, and should begin a new line,
- * with user defined line headers, if any.
- * \param cont The continue log function. This function will be called
- * when \a weston_log_continue is called, and should append
- * its output to the current line, without any header or
- * other content in between.
- */
-WL_EXPORT void
-weston_log_set_handler(log_func_t log, log_func_t cont)
-{
- log_handler = log;
- log_continue_handler = cont;
-}
-
-WL_EXPORT int
-weston_vlog(const char *fmt, va_list ap)
-{
- return log_handler(fmt, ap);
-}
-
-WL_EXPORT int
-weston_log(const char *fmt, ...)
-{
- int l;
- va_list argp;
-
- va_start(argp, fmt);
- l = weston_vlog(fmt, argp);
- va_end(argp);
-
- return l;
-}
-
-WL_EXPORT int
-weston_vlog_continue(const char *fmt, va_list argp)
-{
- return log_continue_handler(fmt, argp);
-}
-
-WL_EXPORT int
-weston_log_continue(const char *fmt, ...)
-{
- int l;
- va_list argp;
-
- va_start(argp, fmt);
- l = weston_vlog_continue(fmt, argp);
- va_end(argp);
-
- return l;
-}
+++ /dev/null
-/*
- * Copyright © 2012 Intel Corporation
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-
-#include "compositor.h"
-
-static int
-noop_renderer_read_pixels(struct weston_output *output,
- pixman_format_code_t format, void *pixels,
- uint32_t x, uint32_t y,
- uint32_t width, uint32_t height)
-{
- return 0;
-}
-
-static void
-noop_renderer_repaint_output(struct weston_output *output,
- pixman_region32_t *output_damage)
-{
-}
-
-static void
-noop_renderer_flush_damage(struct weston_surface *surface)
-{
-}
-
-static void
-noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
-{
- struct wl_shm_buffer *shm_buffer;
- uint8_t *data;
- uint32_t size, i, width, height, stride;
- volatile unsigned char unused = 0; /* volatile so it's not optimized out */
-
- if (!buffer)
- return;
-
- shm_buffer = wl_shm_buffer_get(buffer->resource);
-
- if (!shm_buffer) {
- weston_log("No-op renderer supports only SHM buffers\n");
- return;
- }
-
- data = wl_shm_buffer_get_data(shm_buffer);
- stride = wl_shm_buffer_get_stride(shm_buffer);
- width = wl_shm_buffer_get_width(shm_buffer);
- height = wl_shm_buffer_get_height(shm_buffer);
- size = stride * height;
-
- /* Access the buffer data to make sure the buffer's client gets killed
- * if the buffer size is invalid. This makes the bad_buffer test pass.
- * This can be removed if we start reading the buffer contents
- * somewhere else, e.g. in repaint_output(). */
- wl_shm_buffer_begin_access(shm_buffer);
- for (i = 0; i < size; i++)
- unused ^= data[i];
- wl_shm_buffer_end_access(shm_buffer);
-
- buffer->shm_buffer = shm_buffer;
- buffer->width = width;
- buffer->height = height;
-}
-
-static void
-noop_renderer_surface_set_color(struct weston_surface *surface,
- float red, float green, float blue, float alpha)
-{
-}
-
-static void
-noop_renderer_destroy(struct weston_compositor *ec)
-{
- free(ec->renderer);
- ec->renderer = NULL;
-}
-
-WL_EXPORT int
-noop_renderer_init(struct weston_compositor *ec)
-{
- struct weston_renderer *renderer;
-
- renderer = malloc(sizeof *renderer);
- if (renderer == NULL)
- return -1;
-
- renderer->read_pixels = noop_renderer_read_pixels;
- renderer->repaint_output = noop_renderer_repaint_output;
- renderer->flush_damage = noop_renderer_flush_damage;
- renderer->attach = noop_renderer_attach;
- renderer->surface_set_color = noop_renderer_surface_set_color;
- renderer->destroy = noop_renderer_destroy;
- ec->renderer = renderer;
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright © 2012 Intel Corporation
- * Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
- * Copyright © 2015 Collabora, Ltd.
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <errno.h>
-#include <stdlib.h>
-#include <assert.h>
-
-#include "pixman-renderer.h"
-#include "shared/helpers.h"
-
-#include <linux/input.h>
-
-struct pixman_output_state {
- void *shadow_buffer;
- pixman_image_t *shadow_image;
- pixman_image_t *hw_buffer;
-};
-
-struct pixman_surface_state {
- struct weston_surface *surface;
-
- pixman_image_t *image;
- struct weston_buffer_reference buffer_ref;
-
- struct wl_listener buffer_destroy_listener;
- struct wl_listener surface_destroy_listener;
- struct wl_listener renderer_destroy_listener;
-};
-
-struct pixman_renderer {
- struct weston_renderer base;
-
- int repaint_debug;
- pixman_image_t *debug_color;
- struct weston_binding *debug_binding;
-
- struct wl_signal destroy_signal;
-};
-
-static inline struct pixman_output_state *
-get_output_state(struct weston_output *output)
-{
- return (struct pixman_output_state *)output->renderer_state;
-}
-
-static int
-pixman_renderer_create_surface(struct weston_surface *surface);
-
-static inline struct pixman_surface_state *
-get_surface_state(struct weston_surface *surface)
-{
- if (!surface->renderer_state)
- pixman_renderer_create_surface(surface);
-
- return (struct pixman_surface_state *)surface->renderer_state;
-}
-
-static inline struct pixman_renderer *
-get_renderer(struct weston_compositor *ec)
-{
- return (struct pixman_renderer *)ec->renderer;
-}
-
-static int
-pixman_renderer_read_pixels(struct weston_output *output,
- pixman_format_code_t format, void *pixels,
- uint32_t x, uint32_t y,
- uint32_t width, uint32_t height)
-{
- struct pixman_output_state *po = get_output_state(output);
- pixman_transform_t transform;
- pixman_image_t *out_buf;
-
- if (!po->hw_buffer) {
- errno = ENODEV;
- return -1;
- }
-
- out_buf = pixman_image_create_bits(format,
- width,
- height,
- pixels,
- (PIXMAN_FORMAT_BPP(format) / 8) * width);
-
- /* Caller expects vflipped source image */
- pixman_transform_init_translate(&transform,
- pixman_int_to_fixed (x),
- pixman_int_to_fixed (y - pixman_image_get_height (po->hw_buffer)));
- pixman_transform_scale(&transform, NULL,
- pixman_fixed_1,
- pixman_fixed_minus_1);
- pixman_image_set_transform(po->hw_buffer, &transform);
-
- pixman_image_composite32(PIXMAN_OP_SRC,
- po->hw_buffer, /* src */
- NULL /* mask */,
- out_buf, /* dest */
- 0, 0, /* src_x, src_y */
- 0, 0, /* mask_x, mask_y */
- 0, 0, /* dest_x, dest_y */
- pixman_image_get_width (po->hw_buffer), /* width */
- pixman_image_get_height (po->hw_buffer) /* height */);
- pixman_image_set_transform(po->hw_buffer, NULL);
-
- pixman_image_unref(out_buf);
-
- return 0;
-}
-
-static void
-region_global_to_output(struct weston_output *output, pixman_region32_t *region)
-{
- if (output->zoom.active) {
- weston_matrix_transform_region(region, &output->matrix, region);
- } else {
- pixman_region32_translate(region, -output->x, -output->y);
- weston_transformed_region(output->width, output->height,
- output->transform,
- output->current_scale,
- region, region);
- }
-}
-
-#define D2F(v) pixman_double_to_fixed((double)v)
-
-static void
-weston_matrix_to_pixman_transform(pixman_transform_t *pt,
- const struct weston_matrix *wm)
-{
- /* Pixman supports only 2D transform matrix, but Weston uses 3D, *
- * so we're omitting Z coordinate here. */
- pt->matrix[0][0] = pixman_double_to_fixed(wm->d[0]);
- pt->matrix[0][1] = pixman_double_to_fixed(wm->d[4]);
- pt->matrix[0][2] = pixman_double_to_fixed(wm->d[12]);
- pt->matrix[1][0] = pixman_double_to_fixed(wm->d[1]);
- pt->matrix[1][1] = pixman_double_to_fixed(wm->d[5]);
- pt->matrix[1][2] = pixman_double_to_fixed(wm->d[13]);
- pt->matrix[2][0] = pixman_double_to_fixed(wm->d[3]);
- pt->matrix[2][1] = pixman_double_to_fixed(wm->d[7]);
- pt->matrix[2][2] = pixman_double_to_fixed(wm->d[15]);
-}
-
-static void
-pixman_renderer_compute_transform(pixman_transform_t *transform_out,
- struct weston_view *ev,
- struct weston_output *output)
-{
- struct weston_matrix matrix;
-
- /* Set up the source transformation based on the surface
- position, the output position/transform/scale and the client
- specified buffer transform/scale */
- matrix = output->inverse_matrix;
-
- if (ev->transform.enabled) {
- weston_matrix_multiply(&matrix, &ev->transform.inverse);
- } else {
- weston_matrix_translate(&matrix,
- -ev->geometry.x, -ev->geometry.y, 0);
- }
-
- weston_matrix_multiply(&matrix, &ev->surface->surface_to_buffer_matrix);
-
- weston_matrix_to_pixman_transform(transform_out, &matrix);
-}
-
-static bool
-view_transformation_is_translation(struct weston_view *view)
-{
- if (!view->transform.enabled)
- return true;
-
- if (view->transform.matrix.type <= WESTON_MATRIX_TRANSFORM_TRANSLATE)
- return true;
-
- return false;
-}
-
-static void
-region_intersect_only_translation(pixman_region32_t *result_global,
- pixman_region32_t *global,
- pixman_region32_t *surf,
- struct weston_view *view)
-{
- float view_x, view_y;
-
- assert(view_transformation_is_translation(view));
-
- /* Convert from surface to global coordinates */
- pixman_region32_copy(result_global, surf);
- weston_view_to_global_float(view, 0, 0, &view_x, &view_y);
- pixman_region32_translate(result_global, (int)view_x, (int)view_y);
-
- pixman_region32_intersect(result_global, result_global, global);
-}
-
-static void
-composite_whole(pixman_op_t op,
- pixman_image_t *src,
- pixman_image_t *mask,
- pixman_image_t *dest,
- const pixman_transform_t *transform,
- pixman_filter_t filter)
-{
- int32_t dest_width;
- int32_t dest_height;
-
- dest_width = pixman_image_get_width(dest);
- dest_height = pixman_image_get_height(dest);
-
- pixman_image_set_transform(src, transform);
- pixman_image_set_filter(src, filter, NULL, 0);
-
- pixman_image_composite32(op, src, mask, dest,
- 0, 0, /* src_x, src_y */
- 0, 0, /* mask_x, mask_y */
- 0, 0, /* dest_x, dest_y */
- dest_width, dest_height);
-}
-
-static void
-composite_clipped(pixman_image_t *src,
- pixman_image_t *mask,
- pixman_image_t *dest,
- const pixman_transform_t *transform,
- pixman_filter_t filter,
- pixman_region32_t *src_clip)
-{
- int n_box;
- pixman_box32_t *boxes;
- int32_t dest_width;
- int32_t dest_height;
- int src_stride;
- int bitspp;
- pixman_format_code_t src_format;
- void *src_data;
- int i;
-
- /* Hardcoded to use PIXMAN_OP_OVER, because sampling outside of
- * a Pixman image produces (0,0,0,0) instead of discarding the
- * fragment.
- */
-
- dest_width = pixman_image_get_width(dest);
- dest_height = pixman_image_get_height(dest);
- src_format = pixman_image_get_format(src);
- src_stride = pixman_image_get_stride(src);
- bitspp = PIXMAN_FORMAT_BPP(src_format);
- src_data = pixman_image_get_data(src);
-
- assert(src_format);
-
- /* This would be massive overdraw, except when n_box is 1. */
- boxes = pixman_region32_rectangles(src_clip, &n_box);
- for (i = 0; i < n_box; i++) {
- uint8_t *ptr = src_data;
- pixman_image_t *boximg;
- pixman_transform_t adj = *transform;
-
- ptr += boxes[i].y1 * src_stride;
- ptr += boxes[i].x1 * bitspp / 8;
- boximg = pixman_image_create_bits_no_clear(src_format,
- boxes[i].x2 - boxes[i].x1,
- boxes[i].y2 - boxes[i].y1,
- (uint32_t *)ptr, src_stride);
-
- pixman_transform_translate(&adj, NULL,
- pixman_int_to_fixed(-boxes[i].x1),
- pixman_int_to_fixed(-boxes[i].y1));
- pixman_image_set_transform(boximg, &adj);
-
- pixman_image_set_filter(boximg, filter, NULL, 0);
- pixman_image_composite32(PIXMAN_OP_OVER, boximg, mask, dest,
- 0, 0, /* src_x, src_y */
- 0, 0, /* mask_x, mask_y */
- 0, 0, /* dest_x, dest_y */
- dest_width, dest_height);
-
- pixman_image_unref(boximg);
- }
-
- if (n_box > 1) {
- static bool warned = false;
-
- if (!warned)
- weston_log("Pixman-renderer warning: %dx overdraw\n",
- n_box);
- warned = true;
- }
-}
-
-/** Paint an intersected region
- *
- * \param ev The view to be painted.
- * \param output The output being painted.
- * \param repaint_output The region to be painted in output coordinates.
- * \param source_clip The region of the source image to use, in source image
- * coordinates. If NULL, use the whole source image.
- * \param pixman_op Compositing operator, either SRC or OVER.
- */
-static void
-repaint_region(struct weston_view *ev, struct weston_output *output,
- pixman_region32_t *repaint_output,
- pixman_region32_t *source_clip,
- pixman_op_t pixman_op)
-{
- struct pixman_renderer *pr =
- (struct pixman_renderer *) output->compositor->renderer;
- struct pixman_surface_state *ps = get_surface_state(ev->surface);
- struct pixman_output_state *po = get_output_state(output);
- struct weston_buffer_viewport *vp = &ev->surface->buffer_viewport;
- pixman_transform_t transform;
- pixman_filter_t filter;
- pixman_image_t *mask_image;
- pixman_color_t mask = { 0, };
-
- /* Clip rendering to the damaged output region */
- pixman_image_set_clip_region32(po->shadow_image, repaint_output);
-
- pixman_renderer_compute_transform(&transform, ev, output);
-
- if (ev->transform.enabled || output->current_scale != vp->buffer.scale)
- filter = PIXMAN_FILTER_BILINEAR;
- else
- filter = PIXMAN_FILTER_NEAREST;
-
- if (ps->buffer_ref.buffer)
- wl_shm_buffer_begin_access(ps->buffer_ref.buffer->shm_buffer);
-
- if (ev->alpha < 1.0) {
- mask.alpha = 0xffff * ev->alpha;
- mask_image = pixman_image_create_solid_fill(&mask);
- } else {
- mask_image = NULL;
- }
-
- if (source_clip)
- composite_clipped(ps->image, mask_image, po->shadow_image,
- &transform, filter, source_clip);
- else
- composite_whole(pixman_op, ps->image, mask_image,
- po->shadow_image, &transform, filter);
-
- if (mask_image)
- pixman_image_unref(mask_image);
-
- if (ps->buffer_ref.buffer)
- wl_shm_buffer_end_access(ps->buffer_ref.buffer->shm_buffer);
-
- if (pr->repaint_debug)
- pixman_image_composite32(PIXMAN_OP_OVER,
- pr->debug_color, /* src */
- NULL /* mask */,
- po->shadow_image, /* dest */
- 0, 0, /* src_x, src_y */
- 0, 0, /* mask_x, mask_y */
- 0, 0, /* dest_x, dest_y */
- pixman_image_get_width (po->shadow_image), /* width */
- pixman_image_get_height (po->shadow_image) /* height */);
-
- pixman_image_set_clip_region32 (po->shadow_image, NULL);
-}
-
-static void
-draw_view_translated(struct weston_view *view, struct weston_output *output,
- pixman_region32_t *repaint_global)
-{
- struct weston_surface *surface = view->surface;
- /* non-opaque region in surface coordinates: */
- pixman_region32_t surface_blend;
- /* region to be painted in output coordinates: */
- pixman_region32_t repaint_output;
-
- pixman_region32_init(&repaint_output);
-
- /* Blended region is whole surface minus opaque region,
- * unless surface alpha forces us to blend all.
- */
- pixman_region32_init_rect(&surface_blend, 0, 0,
- surface->width, surface->height);
-
- if (!(view->alpha < 1.0)) {
- pixman_region32_subtract(&surface_blend, &surface_blend,
- &surface->opaque);
-
- if (pixman_region32_not_empty(&surface->opaque)) {
- region_intersect_only_translation(&repaint_output,
- repaint_global,
- &surface->opaque,
- view);
- region_global_to_output(output, &repaint_output);
-
- repaint_region(view, output, &repaint_output, NULL,
- PIXMAN_OP_SRC);
- }
- }
-
- if (pixman_region32_not_empty(&surface_blend)) {
- region_intersect_only_translation(&repaint_output,
- repaint_global,
- &surface_blend, view);
- region_global_to_output(output, &repaint_output);
-
- repaint_region(view, output, &repaint_output, NULL,
- PIXMAN_OP_OVER);
- }
-
- pixman_region32_fini(&surface_blend);
- pixman_region32_fini(&repaint_output);
-}
-
-static void
-draw_view_source_clipped(struct weston_view *view,
- struct weston_output *output,
- pixman_region32_t *repaint_global)
-{
- struct weston_surface *surface = view->surface;
- pixman_region32_t surf_region;
- pixman_region32_t buffer_region;
- pixman_region32_t repaint_output;
-
- /* Do not bother separating the opaque region from non-opaque.
- * Source clipping requires PIXMAN_OP_OVER in all cases, so painting
- * opaque separately has no benefit.
- */
-
- pixman_region32_init_rect(&surf_region, 0, 0,
- surface->width, surface->height);
- if (view->geometry.scissor_enabled)
- pixman_region32_intersect(&surf_region, &surf_region,
- &view->geometry.scissor);
-
- pixman_region32_init(&buffer_region);
- weston_surface_to_buffer_region(surface, &surf_region, &buffer_region);
-
- pixman_region32_init(&repaint_output);
- pixman_region32_copy(&repaint_output, repaint_global);
- region_global_to_output(output, &repaint_output);
-
- repaint_region(view, output, &repaint_output, &buffer_region,
- PIXMAN_OP_OVER);
-
- pixman_region32_fini(&repaint_output);
- pixman_region32_fini(&buffer_region);
- pixman_region32_fini(&surf_region);
-}
-
-static void
-draw_view(struct weston_view *ev, struct weston_output *output,
- pixman_region32_t *damage) /* in global coordinates */
-{
- struct pixman_surface_state *ps = get_surface_state(ev->surface);
- /* repaint bounding region in global coordinates: */
- pixman_region32_t repaint;
-
- /* No buffer attached */
- if (!ps->image)
- return;
-
- pixman_region32_init(&repaint);
- pixman_region32_intersect(&repaint,
- &ev->transform.boundingbox, damage);
- pixman_region32_subtract(&repaint, &repaint, &ev->clip);
-
- if (!pixman_region32_not_empty(&repaint))
- goto out;
-
- if (view_transformation_is_translation(ev)) {
- /* The simple case: The surface regions opaque, non-opaque,
- * etc. are convertible to global coordinate space.
- * There is no need to use a source clip region.
- * It is possible to paint opaque region as PIXMAN_OP_SRC.
- * Also the boundingbox is accurate rather than an
- * approximation.
- */
- draw_view_translated(ev, output, &repaint);
- } else {
- /* The complex case: the view transformation does not allow
- * converting opaque etc. regions into global coordinate space.
- * Therefore we need source clipping to avoid sampling from
- * unwanted source image areas, unless the source image is
- * to be used whole. Source clipping does not work with
- * PIXMAN_OP_SRC.
- */
- draw_view_source_clipped(ev, output, &repaint);
- }
-
-out:
- pixman_region32_fini(&repaint);
-}
-static void
-repaint_surfaces(struct weston_output *output, pixman_region32_t *damage)
-{
- struct weston_compositor *compositor = output->compositor;
- struct weston_view *view;
-
- wl_list_for_each_reverse(view, &compositor->view_list, link)
- if (view->plane == &compositor->primary_plane)
- draw_view(view, output, damage);
-}
-
-static void
-copy_to_hw_buffer(struct weston_output *output, pixman_region32_t *region)
-{
- struct pixman_output_state *po = get_output_state(output);
- pixman_region32_t output_region;
-
- pixman_region32_init(&output_region);
- pixman_region32_copy(&output_region, region);
-
- region_global_to_output(output, &output_region);
-
- pixman_image_set_clip_region32 (po->hw_buffer, &output_region);
- pixman_region32_fini(&output_region);
-
- pixman_image_composite32(PIXMAN_OP_SRC,
- po->shadow_image, /* src */
- NULL /* mask */,
- po->hw_buffer, /* dest */
- 0, 0, /* src_x, src_y */
- 0, 0, /* mask_x, mask_y */
- 0, 0, /* dest_x, dest_y */
- pixman_image_get_width (po->hw_buffer), /* width */
- pixman_image_get_height (po->hw_buffer) /* height */);
-
- pixman_image_set_clip_region32 (po->hw_buffer, NULL);
-}
-
-static void
-pixman_renderer_repaint_output(struct weston_output *output,
- pixman_region32_t *output_damage)
-{
- struct pixman_output_state *po = get_output_state(output);
-
- if (!po->hw_buffer)
- return;
-
- repaint_surfaces(output, output_damage);
- copy_to_hw_buffer(output, output_damage);
-
- pixman_region32_copy(&output->previous_damage, output_damage);
- wl_signal_emit(&output->frame_signal, output);
-
- /* Actual flip should be done by caller */
-}
-
-static void
-pixman_renderer_flush_damage(struct weston_surface *surface)
-{
- /* No-op for pixman renderer */
-}
-
-static void
-buffer_state_handle_buffer_destroy(struct wl_listener *listener, void *data)
-{
- struct pixman_surface_state *ps;
-
- ps = container_of(listener, struct pixman_surface_state,
- buffer_destroy_listener);
-
- if (ps->image) {
- pixman_image_unref(ps->image);
- ps->image = NULL;
- }
-
- ps->buffer_destroy_listener.notify = NULL;
-}
-
-static void
-pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer)
-{
- struct pixman_surface_state *ps = get_surface_state(es);
- struct wl_shm_buffer *shm_buffer;
- pixman_format_code_t pixman_format;
-
- weston_buffer_reference(&ps->buffer_ref, buffer);
-
- if (ps->buffer_destroy_listener.notify) {
- wl_list_remove(&ps->buffer_destroy_listener.link);
- ps->buffer_destroy_listener.notify = NULL;
- }
-
- if (ps->image) {
- pixman_image_unref(ps->image);
- ps->image = NULL;
- }
-
- if (!buffer)
- return;
-
- shm_buffer = wl_shm_buffer_get(buffer->resource);
-
- if (! shm_buffer) {
- weston_log("Pixman renderer supports only SHM buffers\n");
- weston_buffer_reference(&ps->buffer_ref, NULL);
- return;
- }
-
- switch (wl_shm_buffer_get_format(shm_buffer)) {
- case WL_SHM_FORMAT_XRGB8888:
- pixman_format = PIXMAN_x8r8g8b8;
- break;
- case WL_SHM_FORMAT_ARGB8888:
- pixman_format = PIXMAN_a8r8g8b8;
- break;
- case WL_SHM_FORMAT_RGB565:
- pixman_format = PIXMAN_r5g6b5;
- break;
- default:
- weston_log("Unsupported SHM buffer format\n");
- weston_buffer_reference(&ps->buffer_ref, NULL);
- return;
- break;
- }
-
- buffer->shm_buffer = shm_buffer;
- buffer->width = wl_shm_buffer_get_width(shm_buffer);
- buffer->height = wl_shm_buffer_get_height(shm_buffer);
-
- ps->image = pixman_image_create_bits(pixman_format,
- buffer->width, buffer->height,
- wl_shm_buffer_get_data(shm_buffer),
- wl_shm_buffer_get_stride(shm_buffer));
-
- ps->buffer_destroy_listener.notify =
- buffer_state_handle_buffer_destroy;
- wl_signal_add(&buffer->destroy_signal,
- &ps->buffer_destroy_listener);
-}
-
-static void
-pixman_renderer_surface_state_destroy(struct pixman_surface_state *ps)
-{
- wl_list_remove(&ps->surface_destroy_listener.link);
- wl_list_remove(&ps->renderer_destroy_listener.link);
- if (ps->buffer_destroy_listener.notify) {
- wl_list_remove(&ps->buffer_destroy_listener.link);
- ps->buffer_destroy_listener.notify = NULL;
- }
-
- ps->surface->renderer_state = NULL;
-
- if (ps->image) {
- pixman_image_unref(ps->image);
- ps->image = NULL;
- }
- weston_buffer_reference(&ps->buffer_ref, NULL);
- free(ps);
-}
-
-static void
-surface_state_handle_surface_destroy(struct wl_listener *listener, void *data)
-{
- struct pixman_surface_state *ps;
-
- ps = container_of(listener, struct pixman_surface_state,
- surface_destroy_listener);
-
- pixman_renderer_surface_state_destroy(ps);
-}
-
-static void
-surface_state_handle_renderer_destroy(struct wl_listener *listener, void *data)
-{
- struct pixman_surface_state *ps;
-
- ps = container_of(listener, struct pixman_surface_state,
- renderer_destroy_listener);
-
- pixman_renderer_surface_state_destroy(ps);
-}
-
-static int
-pixman_renderer_create_surface(struct weston_surface *surface)
-{
- struct pixman_surface_state *ps;
- struct pixman_renderer *pr = get_renderer(surface->compositor);
-
- ps = zalloc(sizeof *ps);
- if (ps == NULL)
- return -1;
-
- surface->renderer_state = ps;
-
- ps->surface = surface;
-
- ps->surface_destroy_listener.notify =
- surface_state_handle_surface_destroy;
- wl_signal_add(&surface->destroy_signal,
- &ps->surface_destroy_listener);
-
- ps->renderer_destroy_listener.notify =
- surface_state_handle_renderer_destroy;
- wl_signal_add(&pr->destroy_signal,
- &ps->renderer_destroy_listener);
-
- return 0;
-}
-
-static void
-pixman_renderer_surface_set_color(struct weston_surface *es,
- float red, float green, float blue, float alpha)
-{
- struct pixman_surface_state *ps = get_surface_state(es);
- pixman_color_t color;
-
- color.red = red * 0xffff;
- color.green = green * 0xffff;
- color.blue = blue * 0xffff;
- color.alpha = alpha * 0xffff;
-
- if (ps->image) {
- pixman_image_unref(ps->image);
- ps->image = NULL;
- }
-
- ps->image = pixman_image_create_solid_fill(&color);
-}
-
-static void
-pixman_renderer_destroy(struct weston_compositor *ec)
-{
- struct pixman_renderer *pr = get_renderer(ec);
-
- wl_signal_emit(&pr->destroy_signal, pr);
- weston_binding_destroy(pr->debug_binding);
- free(pr);
-
- ec->renderer = NULL;
-}
-
-static void
-pixman_renderer_surface_get_content_size(struct weston_surface *surface,
- int *width, int *height)
-{
- struct pixman_surface_state *ps = get_surface_state(surface);
-
- if (ps->image) {
- *width = pixman_image_get_width(ps->image);
- *height = pixman_image_get_height(ps->image);
- } else {
- *width = 0;
- *height = 0;
- }
-}
-
-static int
-pixman_renderer_surface_copy_content(struct weston_surface *surface,
- void *target, size_t size,
- int src_x, int src_y,
- int width, int height)
-{
- const pixman_format_code_t format = PIXMAN_a8b8g8r8;
- const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */
- struct pixman_surface_state *ps = get_surface_state(surface);
- pixman_image_t *out_buf;
-
- if (!ps->image)
- return -1;
-
- out_buf = pixman_image_create_bits(format, width, height,
- target, width * bytespp);
-
- pixman_image_set_transform(ps->image, NULL);
- pixman_image_composite32(PIXMAN_OP_SRC,
- ps->image, /* src */
- NULL, /* mask */
- out_buf, /* dest */
- src_x, src_y, /* src_x, src_y */
- 0, 0, /* mask_x, mask_y */
- 0, 0, /* dest_x, dest_y */
- width, height);
-
- pixman_image_unref(out_buf);
-
- return 0;
-}
-
-static void
-debug_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key,
- void *data)
-{
- struct weston_compositor *ec = data;
- struct pixman_renderer *pr = (struct pixman_renderer *) ec->renderer;
-
- pr->repaint_debug ^= 1;
-
- if (pr->repaint_debug) {
- pixman_color_t red = {
- 0x3fff, 0x0000, 0x0000, 0x3fff
- };
-
- pr->debug_color = pixman_image_create_solid_fill(&red);
- } else {
- pixman_image_unref(pr->debug_color);
- weston_compositor_damage_all(ec);
- }
-}
-
-WL_EXPORT int
-pixman_renderer_init(struct weston_compositor *ec)
-{
- struct pixman_renderer *renderer;
-
- renderer = zalloc(sizeof *renderer);
- if (renderer == NULL)
- return -1;
-
- renderer->repaint_debug = 0;
- renderer->debug_color = NULL;
- renderer->base.read_pixels = pixman_renderer_read_pixels;
- renderer->base.repaint_output = pixman_renderer_repaint_output;
- renderer->base.flush_damage = pixman_renderer_flush_damage;
- renderer->base.attach = pixman_renderer_attach;
- renderer->base.surface_set_color = pixman_renderer_surface_set_color;
- renderer->base.destroy = pixman_renderer_destroy;
- renderer->base.surface_get_content_size =
- pixman_renderer_surface_get_content_size;
- renderer->base.surface_copy_content =
- pixman_renderer_surface_copy_content;
- ec->renderer = &renderer->base;
- ec->capabilities |= WESTON_CAP_ROTATION_ANY;
- ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP;
- ec->capabilities |= WESTON_CAP_VIEW_CLIP_MASK;
-
- renderer->debug_binding =
- weston_compositor_add_debug_binding(ec, KEY_R,
- debug_binding, ec);
-
- wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565);
-
- wl_signal_init(&renderer->destroy_signal);
-
- return 0;
-}
-
-WL_EXPORT void
-pixman_renderer_output_set_buffer(struct weston_output *output, pixman_image_t *buffer)
-{
- struct pixman_output_state *po = get_output_state(output);
-
- if (po->hw_buffer)
- pixman_image_unref(po->hw_buffer);
- po->hw_buffer = buffer;
-
- if (po->hw_buffer) {
- output->compositor->read_format = pixman_image_get_format(po->hw_buffer);
- pixman_image_ref(po->hw_buffer);
- }
-}
-
-WL_EXPORT int
-pixman_renderer_output_create(struct weston_output *output)
-{
- struct pixman_output_state *po;
- int w, h;
-
- po = zalloc(sizeof *po);
- if (po == NULL)
- return -1;
-
- /* set shadow image transformation */
- w = output->current_mode->width;
- h = output->current_mode->height;
-
- po->shadow_buffer = malloc(w * h * 4);
-
- if (!po->shadow_buffer) {
- free(po);
- return -1;
- }
-
- po->shadow_image =
- pixman_image_create_bits(PIXMAN_x8r8g8b8, w, h,
- po->shadow_buffer, w * 4);
-
- if (!po->shadow_image) {
- free(po->shadow_buffer);
- free(po);
- return -1;
- }
-
- output->renderer_state = po;
-
- return 0;
-}
-
-WL_EXPORT void
-pixman_renderer_output_destroy(struct weston_output *output)
-{
- struct pixman_output_state *po = get_output_state(output);
-
- pixman_image_unref(po->shadow_image);
-
- if (po->hw_buffer)
- pixman_image_unref(po->hw_buffer);
-
- free(po->shadow_buffer);
-
- po->shadow_buffer = NULL;
- po->shadow_image = NULL;
- po->hw_buffer = NULL;
-
- free(po);
-}
+++ /dev/null
-/*
- * Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
- *
- * 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.
- */
-
-#include "config.h"
-
-#include "compositor.h"
-
-int
-pixman_renderer_init(struct weston_compositor *ec);
-
-int
-pixman_renderer_output_create(struct weston_output *output);
-
-void
-pixman_renderer_output_set_buffer(struct weston_output *output, pixman_image_t *buffer);
-
-void
-pixman_renderer_output_destroy(struct weston_output *output);
+++ /dev/null
-/*
- * Copyright © 2008-2011 Kristian Høgsberg
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <linux/input.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/uio.h>
-
-#include "compositor.h"
-#include "shared/helpers.h"
-
-#include "wcap/wcap-decode.h"
-
-struct screenshooter_frame_listener {
- struct wl_listener listener;
- struct weston_buffer *buffer;
- weston_screenshooter_done_func_t done;
- void *data;
-};
-
-static void
-copy_bgra_yflip(uint8_t *dst, uint8_t *src, int height, int stride)
-{
- uint8_t *end;
-
- end = dst + height * stride;
- while (dst < end) {
- memcpy(dst, src, stride);
- dst += stride;
- src -= stride;
- }
-}
-
-static void
-copy_bgra(uint8_t *dst, uint8_t *src, int height, int stride)
-{
- /* TODO: optimize this out */
- memcpy(dst, src, height * stride);
-}
-
-static void
-copy_row_swap_RB(void *vdst, void *vsrc, int bytes)
-{
- uint32_t *dst = vdst;
- uint32_t *src = vsrc;
- uint32_t *end = dst + bytes / 4;
-
- while (dst < end) {
- uint32_t v = *src++;
- /* A R G B */
- uint32_t tmp = v & 0xff00ff00;
- tmp |= (v >> 16) & 0x000000ff;
- tmp |= (v << 16) & 0x00ff0000;
- *dst++ = tmp;
- }
-}
-
-static void
-copy_rgba_yflip(uint8_t *dst, uint8_t *src, int height, int stride)
-{
- uint8_t *end;
-
- end = dst + height * stride;
- while (dst < end) {
- copy_row_swap_RB(dst, src, stride);
- dst += stride;
- src -= stride;
- }
-}
-
-static void
-copy_rgba(uint8_t *dst, uint8_t *src, int height, int stride)
-{
- uint8_t *end;
-
- end = dst + height * stride;
- while (dst < end) {
- copy_row_swap_RB(dst, src, stride);
- dst += stride;
- src += stride;
- }
-}
-
-static void
-screenshooter_frame_notify(struct wl_listener *listener, void *data)
-{
- struct screenshooter_frame_listener *l =
- container_of(listener,
- struct screenshooter_frame_listener, listener);
- struct weston_output *output = data;
- struct weston_compositor *compositor = output->compositor;
- int32_t stride;
- uint8_t *pixels, *d, *s;
-
- output->disable_planes--;
- wl_list_remove(&listener->link);
- stride = l->buffer->width * (PIXMAN_FORMAT_BPP(compositor->read_format) / 8);
- pixels = malloc(stride * l->buffer->height);
-
- if (pixels == NULL) {
- l->done(l->data, WESTON_SCREENSHOOTER_NO_MEMORY);
- free(l);
- return;
- }
-
- compositor->renderer->read_pixels(output,
- compositor->read_format, pixels,
- 0, 0, output->current_mode->width,
- output->current_mode->height);
-
- stride = wl_shm_buffer_get_stride(l->buffer->shm_buffer);
-
- d = wl_shm_buffer_get_data(l->buffer->shm_buffer);
- s = pixels + stride * (l->buffer->height - 1);
-
- wl_shm_buffer_begin_access(l->buffer->shm_buffer);
-
- switch (compositor->read_format) {
- case PIXMAN_a8r8g8b8:
- case PIXMAN_x8r8g8b8:
- if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP)
- copy_bgra_yflip(d, s, output->current_mode->height, stride);
- else
- copy_bgra(d, pixels, output->current_mode->height, stride);
- break;
- case PIXMAN_x8b8g8r8:
- case PIXMAN_a8b8g8r8:
- if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP)
- copy_rgba_yflip(d, s, output->current_mode->height, stride);
- else
- copy_rgba(d, pixels, output->current_mode->height, stride);
- break;
- default:
- break;
- }
-
- wl_shm_buffer_end_access(l->buffer->shm_buffer);
-
- l->done(l->data, WESTON_SCREENSHOOTER_SUCCESS);
- free(pixels);
- free(l);
-}
-
-WL_EXPORT int
-weston_screenshooter_shoot(struct weston_output *output,
- struct weston_buffer *buffer,
- weston_screenshooter_done_func_t done, void *data)
-{
- struct screenshooter_frame_listener *l;
-
- if (!wl_shm_buffer_get(buffer->resource)) {
- done(data, WESTON_SCREENSHOOTER_BAD_BUFFER);
- return -1;
- }
-
- buffer->shm_buffer = wl_shm_buffer_get(buffer->resource);
- buffer->width = wl_shm_buffer_get_width(buffer->shm_buffer);
- buffer->height = wl_shm_buffer_get_height(buffer->shm_buffer);
-
- if (buffer->width < output->current_mode->width ||
- buffer->height < output->current_mode->height) {
- done(data, WESTON_SCREENSHOOTER_BAD_BUFFER);
- return -1;
- }
-
- l = malloc(sizeof *l);
- if (l == NULL) {
- done(data, WESTON_SCREENSHOOTER_NO_MEMORY);
- return -1;
- }
-
- l->buffer = buffer;
- l->done = done;
- l->data = data;
- l->listener.notify = screenshooter_frame_notify;
- wl_signal_add(&output->frame_signal, &l->listener);
- output->disable_planes++;
- weston_output_schedule_repaint(output);
-
- return 0;
-}
-
-struct weston_recorder {
- struct weston_output *output;
- uint32_t *frame, *rect;
- uint32_t *tmpbuf;
- uint32_t total;
- int fd;
- struct wl_listener frame_listener;
- int count, destroying;
-};
-
-static uint32_t *
-output_run(uint32_t *p, uint32_t delta, int run)
-{
- int i;
-
- while (run > 0) {
- if (run <= 0xe0) {
- *p++ = delta | ((run - 1) << 24);
- break;
- }
-
- i = 24 - __builtin_clz(run);
- *p++ = delta | ((i + 0xe0) << 24);
- run -= 1 << (7 + i);
- }
-
- return p;
-}
-
-static uint32_t
-component_delta(uint32_t next, uint32_t prev)
-{
- unsigned char dr, dg, db;
-
- dr = (next >> 16) - (prev >> 16);
- dg = (next >> 8) - (prev >> 8);
- db = (next >> 0) - (prev >> 0);
-
- return (dr << 16) | (dg << 8) | (db << 0);
-}
-
-static void
-weston_recorder_destroy(struct weston_recorder *recorder);
-
-static void
-weston_recorder_frame_notify(struct wl_listener *listener, void *data)
-{
- struct weston_recorder *recorder =
- container_of(listener, struct weston_recorder, frame_listener);
- struct weston_output *output = data;
- struct weston_compositor *compositor = output->compositor;
- uint32_t msecs = output->frame_time;
- pixman_box32_t *r;
- pixman_region32_t damage, transformed_damage;
- int i, j, k, n, width, height, run, stride;
- uint32_t delta, prev, *d, *s, *p, next;
- struct {
- uint32_t msecs;
- uint32_t nrects;
- } header;
- struct iovec v[2];
- int do_yflip;
- int y_orig;
- uint32_t *outbuf;
-
- do_yflip = !!(compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP);
- if (do_yflip)
- outbuf = recorder->rect;
- else
- outbuf = recorder->tmpbuf;
-
- pixman_region32_init(&damage);
- pixman_region32_init(&transformed_damage);
- pixman_region32_intersect(&damage, &output->region,
- &output->previous_damage);
- pixman_region32_translate(&damage, -output->x, -output->y);
- weston_transformed_region(output->width, output->height,
- output->transform, output->current_scale,
- &damage, &transformed_damage);
- pixman_region32_fini(&damage);
-
- r = pixman_region32_rectangles(&transformed_damage, &n);
- if (n == 0) {
- pixman_region32_fini(&transformed_damage);
- return;
- }
-
- header.msecs = msecs;
- header.nrects = n;
- v[0].iov_base = &header;
- v[0].iov_len = sizeof header;
- v[1].iov_base = r;
- v[1].iov_len = n * sizeof *r;
- recorder->total += writev(recorder->fd, v, 2);
- stride = output->current_mode->width;
-
- for (i = 0; i < n; i++) {
- width = r[i].x2 - r[i].x1;
- height = r[i].y2 - r[i].y1;
-
- if (do_yflip)
- y_orig = output->current_mode->height - r[i].y2;
- else
- y_orig = r[i].y1;
-
- compositor->renderer->read_pixels(output,
- compositor->read_format, recorder->rect,
- r[i].x1, y_orig, width, height);
-
- p = outbuf;
- run = prev = 0; /* quiet gcc */
- for (j = 0; j < height; j++) {
- if (do_yflip)
- s = recorder->rect + width * j;
- else
- s = recorder->rect + width * (height - j - 1);
- y_orig = r[i].y2 - j - 1;
- d = recorder->frame + stride * y_orig + r[i].x1;
-
- for (k = 0; k < width; k++) {
- next = *s++;
- delta = component_delta(next, *d);
- *d++ = next;
- if (run == 0 || delta == prev) {
- run++;
- } else {
- p = output_run(p, prev, run);
- run = 1;
- }
- prev = delta;
- }
- }
-
- p = output_run(p, prev, run);
-
- recorder->total += write(recorder->fd,
- outbuf, (p - outbuf) * 4);
-
-#if 0
- fprintf(stderr,
- "%dx%d at %d,%d rle from %d to %d bytes (%f) total %dM\n",
- width, height, r[i].x1, r[i].y1,
- width * height * 4, (int) (p - outbuf) * 4,
- (float) (p - outbuf) / (width * height),
- recorder->total / 1024 / 1024);
-#endif
- }
-
- pixman_region32_fini(&transformed_damage);
- recorder->count++;
-
- if (recorder->destroying)
- weston_recorder_destroy(recorder);
-}
-
-static void
-weston_recorder_free(struct weston_recorder *recorder)
-{
- if (recorder == NULL)
- return;
-
- free(recorder->tmpbuf);
- free(recorder->rect);
- free(recorder->frame);
- free(recorder);
-}
-
-static struct weston_recorder *
-weston_recorder_create(struct weston_output *output, const char *filename)
-{
- struct weston_compositor *compositor = output->compositor;
- struct weston_recorder *recorder;
- int stride, size;
- struct { uint32_t magic, format, width, height; } header;
- int do_yflip;
-
- do_yflip = !!(compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP);
-
- recorder = zalloc(sizeof *recorder);
- if (recorder == NULL) {
- weston_log("%s: out of memory\n", __func__);
- return NULL;
- }
-
- stride = output->current_mode->width;
- size = stride * 4 * output->current_mode->height;
- recorder->frame = zalloc(size);
- recorder->rect = malloc(size);
- recorder->output = output;
-
- if ((recorder->frame == NULL) || (recorder->rect == NULL)) {
- weston_log("%s: out of memory\n", __func__);
- goto err_recorder;
- }
-
- if (!do_yflip) {
- recorder->tmpbuf = malloc(size);
- if (recorder->tmpbuf == NULL) {
- weston_log("%s: out of memory\n", __func__);
- goto err_recorder;
- }
- }
-
- header.magic = WCAP_HEADER_MAGIC;
-
- switch (compositor->read_format) {
- case PIXMAN_x8r8g8b8:
- case PIXMAN_a8r8g8b8:
- header.format = WCAP_FORMAT_XRGB8888;
- break;
- case PIXMAN_a8b8g8r8:
- header.format = WCAP_FORMAT_XBGR8888;
- break;
- default:
- weston_log("unknown recorder format\n");
- goto err_recorder;
- }
-
- recorder->fd = open(filename,
- O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
-
- if (recorder->fd < 0) {
- weston_log("problem opening output file %s: %m\n", filename);
- goto err_recorder;
- }
-
- header.width = output->current_mode->width;
- header.height = output->current_mode->height;
- recorder->total += write(recorder->fd, &header, sizeof header);
-
- recorder->frame_listener.notify = weston_recorder_frame_notify;
- wl_signal_add(&output->frame_signal, &recorder->frame_listener);
- output->disable_planes++;
- weston_output_damage(output);
-
- return recorder;
-
-err_recorder:
- weston_recorder_free(recorder);
- return NULL;
-}
-
-static void
-weston_recorder_destroy(struct weston_recorder *recorder)
-{
- wl_list_remove(&recorder->frame_listener.link);
- close(recorder->fd);
- recorder->output->disable_planes--;
- weston_recorder_free(recorder);
-}
-
-WL_EXPORT struct weston_recorder *
-weston_recorder_start(struct weston_output *output, const char *filename)
-{
- struct wl_listener *listener;
-
- listener = wl_signal_get(&output->frame_signal,
- weston_recorder_frame_notify);
- if (listener) {
- weston_log("a recorder on output %s is already running\n",
- output->name);
- return NULL;
- }
-
- weston_log("starting recorder for output %s, file %s\n",
- output->name, filename);
- return weston_recorder_create(output, filename);
-}
-
-WL_EXPORT void
-weston_recorder_stop(struct weston_recorder *recorder)
-{
- weston_log("stopping recorder, total file size %dM, %d frames\n",
- recorder->total / (1024 * 1024), recorder->count);
-
- recorder->destroying = 1;
- weston_output_schedule_repaint(recorder->output);
-}
+++ /dev/null
-/*
- * Copyright © 2011 Intel Corporation
- *
- * 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.
- */
-
-#include "config.h"
-
-#include "compositor.h"
-
-WL_EXPORT void
-weston_view_geometry_dirty(struct weston_view *view)
-{
-}
-
-WL_EXPORT int
-weston_log(const char *fmt, ...)
-{
- return 0;
-}
-
-WL_EXPORT void
-weston_view_schedule_repaint(struct weston_view *view)
-{
-}
-
-WL_EXPORT void
-weston_compositor_schedule_repaint(struct weston_compositor *compositor)
-{
-}
-
-int
-main(int argc, char *argv[])
-{
- const double k = 300.0;
- const double current = 0.5;
- const double target = 1.0;
- const double friction = 1400;
-
- struct weston_spring spring;
- uint32_t time = 0;
-
- weston_spring_init(&spring, k, current, target);
- spring.friction = friction;
- spring.previous = 0.48;
- spring.timestamp = 0;
-
- while (!weston_spring_done(&spring)) {
- printf("\t%d\t%f\n", time, spring.current);
- weston_spring_update(&spring, time);
- time += 16;
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright © 2014 Pekka Paalanen <pq@iki.fi>
- * Copyright © 2014 Collabora, Ltd.
- *
- * 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.
- */
-
-#ifndef WESTON_TIMELINE_OBJECT_H
-#define WESTON_TIMELINE_OBJECT_H
-
-/*
- * This struct can be embedded in objects related to timeline output.
- * It must be initialized to all-zero. Afterwards, the timeline code
- * will handle it alone. No clean-up is necessary.
- */
-struct weston_timeline_object {
- /*
- * Timeline series gets bumped every time a new log is opened.
- * This triggers id allocation and object info emission.
- * 0 is an invalid series value.
- */
- unsigned series;
-
- /* Object id in the timeline JSON output. 0 is invalid. */
- unsigned id;
-
- /*
- * If non-zero, forces a re-emission of object description.
- * Should be set to non-zero, when changing long-lived
- * object state that is not emitted on normal timeline
- * events.
- */
- unsigned force_refresh;
-};
-
-#endif /* WESTON_TIMELINE_OBJECT_H */
+++ /dev/null
-/*
- * Copyright © 2014 Pekka Paalanen <pq@iki.fi>
- * Copyright © 2014 Collabora, Ltd.
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <time.h>
-#include <assert.h>
-
-#include "timeline.h"
-#include "compositor.h"
-#include "file-util.h"
-
-struct timeline_log {
- clock_t clk_id;
- FILE *file;
- unsigned series;
- struct wl_listener compositor_destroy_listener;
-};
-
-WL_EXPORT int weston_timeline_enabled_;
-static struct timeline_log timeline_ = { CLOCK_MONOTONIC, NULL, 0 };
-
-static int
-weston_timeline_do_open(void)
-{
- const char *prefix = "weston-timeline-";
- const char *suffix = ".log";
- char fname[1000];
-
- timeline_.file = file_create_dated(prefix, suffix,
- fname, sizeof(fname));
- if (!timeline_.file) {
- const char *msg;
-
- switch (errno) {
- case ETIME:
- msg = "failure in datetime formatting";
- break;
- default:
- msg = strerror(errno);
- }
-
- weston_log("Cannot open '%s*%s' for writing: %s\n",
- prefix, suffix, msg);
- return -1;
- }
-
- weston_log("Opened timeline file '%s'\n", fname);
-
- return 0;
-}
-
-static void
-timeline_notify_destroy(struct wl_listener *listener, void *data)
-{
- weston_timeline_close();
-}
-
-void
-weston_timeline_open(struct weston_compositor *compositor)
-{
- if (weston_timeline_enabled_)
- return;
-
- if (weston_timeline_do_open() < 0)
- return;
-
- timeline_.compositor_destroy_listener.notify = timeline_notify_destroy;
- wl_signal_add(&compositor->destroy_signal,
- &timeline_.compositor_destroy_listener);
-
- if (++timeline_.series == 0)
- ++timeline_.series;
-
- weston_timeline_enabled_ = 1;
-}
-
-void
-weston_timeline_close(void)
-{
- if (!weston_timeline_enabled_)
- return;
-
- weston_timeline_enabled_ = 0;
-
- wl_list_remove(&timeline_.compositor_destroy_listener.link);
-
- fclose(timeline_.file);
- timeline_.file = NULL;
- weston_log("Timeline log file closed.\n");
-}
-
-struct timeline_emit_context {
- FILE *cur;
- FILE *out;
- unsigned series;
-};
-
-static unsigned
-timeline_new_id(void)
-{
- static unsigned idc;
-
- if (++idc == 0)
- ++idc;
-
- return idc;
-}
-
-static int
-check_series(struct timeline_emit_context *ctx,
- struct weston_timeline_object *to)
-{
- if (to->series == 0 || to->series != ctx->series) {
- to->series = ctx->series;
- to->id = timeline_new_id();
- return 1;
- }
-
- if (to->force_refresh) {
- to->force_refresh = 0;
- return 1;
- }
-
- return 0;
-}
-
-static void
-fprint_quoted_string(FILE *fp, const char *str)
-{
- if (!str) {
- fprintf(fp, "null");
- return;
- }
-
- fprintf(fp, "\"%s\"", str);
-}
-
-static int
-emit_weston_output(struct timeline_emit_context *ctx, void *obj)
-{
- struct weston_output *o = obj;
-
- if (check_series(ctx, &o->timeline)) {
- fprintf(ctx->out, "{ \"id\":%u, "
- "\"type\":\"weston_output\", \"name\":",
- o->timeline.id);
- fprint_quoted_string(ctx->out, o->name);
- fprintf(ctx->out, " }\n");
- }
-
- fprintf(ctx->cur, "\"wo\":%u", o->timeline.id);
-
- return 1;
-}
-
-static void
-check_weston_surface_description(struct timeline_emit_context *ctx,
- struct weston_surface *s)
-{
- struct weston_surface *mains;
- char d[512];
- char mainstr[32];
-
- if (!check_series(ctx, &s->timeline))
- return;
-
- mains = weston_surface_get_main_surface(s);
- if (mains != s) {
- check_weston_surface_description(ctx, mains);
- if (snprintf(mainstr, sizeof(mainstr),
- ", \"main_surface\":%u", mains->timeline.id) < 0)
- mainstr[0] = '\0';
- } else {
- mainstr[0] = '\0';
- }
-
- if (!s->get_label || s->get_label(s, d, sizeof(d)) < 0)
- d[0] = '\0';
-
- fprintf(ctx->out, "{ \"id\":%u, "
- "\"type\":\"weston_surface\", \"desc\":", s->timeline.id);
- fprint_quoted_string(ctx->out, d[0] ? d : NULL);
- fprintf(ctx->out, "%s }\n", mainstr);
-}
-
-static int
-emit_weston_surface(struct timeline_emit_context *ctx, void *obj)
-{
- struct weston_surface *s = obj;
-
- check_weston_surface_description(ctx, s);
- fprintf(ctx->cur, "\"ws\":%u", s->timeline.id);
-
- return 1;
-}
-
-static int
-emit_vblank_timestamp(struct timeline_emit_context *ctx, void *obj)
-{
- struct timespec *ts = obj;
-
- fprintf(ctx->cur, "\"vblank\":[%" PRId64 ", %ld]",
- (int64_t)ts->tv_sec, ts->tv_nsec);
-
- return 1;
-}
-
-typedef int (*type_func)(struct timeline_emit_context *ctx, void *obj);
-
-static const type_func type_dispatch[] = {
- [TLT_OUTPUT] = emit_weston_output,
- [TLT_SURFACE] = emit_weston_surface,
- [TLT_VBLANK] = emit_vblank_timestamp,
-};
-
-WL_EXPORT void
-weston_timeline_point(const char *name, ...)
-{
- va_list argp;
- struct timespec ts;
- enum timeline_type otype;
- void *obj;
- char buf[512];
- struct timeline_emit_context ctx;
-
- clock_gettime(timeline_.clk_id, &ts);
-
- ctx.out = timeline_.file;
- ctx.cur = fmemopen(buf, sizeof(buf), "w");
- ctx.series = timeline_.series;
-
- if (!ctx.cur) {
- weston_log("Timeline error in fmemopen, closing.\n");
- weston_timeline_close();
- return;
- }
-
- fprintf(ctx.cur, "{ \"T\":[%" PRId64 ", %ld], \"N\":\"%s\"",
- (int64_t)ts.tv_sec, ts.tv_nsec, name);
-
- va_start(argp, name);
- while (1) {
- otype = va_arg(argp, enum timeline_type);
- if (otype == TLT_END)
- break;
-
- obj = va_arg(argp, void *);
- if (type_dispatch[otype]) {
- fprintf(ctx.cur, ", ");
- type_dispatch[otype](&ctx, obj);
- }
- }
- va_end(argp);
-
- fprintf(ctx.cur, " }\n");
- fflush(ctx.cur);
- if (ferror(ctx.cur)) {
- weston_log("Timeline error in constructing entry, closing.\n");
- weston_timeline_close();
- } else {
- fprintf(ctx.out, "%s", buf);
- }
-
- fclose(ctx.cur);
-}
+++ /dev/null
-/*
- * Copyright © 2014 Pekka Paalanen <pq@iki.fi>
- * Copyright © 2014 Collabora, Ltd.
- *
- * 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.
- */
-
-#ifndef WESTON_TIMELINE_H
-#define WESTON_TIMELINE_H
-
-extern int weston_timeline_enabled_;
-
-struct weston_compositor;
-
-void
-weston_timeline_open(struct weston_compositor *compositor);
-
-void
-weston_timeline_close(void);
-
-enum timeline_type {
- TLT_END = 0,
- TLT_OUTPUT,
- TLT_SURFACE,
- TLT_VBLANK,
-};
-
-#define TYPEVERIFY(type, arg) ({ \
- typeof(arg) tmp___ = (arg); \
- (void)((type)0 == tmp___); \
- tmp___; })
-
-#define TLP_END TLT_END, NULL
-#define TLP_OUTPUT(o) TLT_OUTPUT, TYPEVERIFY(struct weston_output *, (o))
-#define TLP_SURFACE(s) TLT_SURFACE, TYPEVERIFY(struct weston_surface *, (s))
-#define TLP_VBLANK(t) TLT_VBLANK, TYPEVERIFY(const struct timespec *, (t))
-
-#define TL_POINT(...) do { \
- if (weston_timeline_enabled_) \
- weston_timeline_point(__VA_ARGS__); \
-} while (0)
-
-void
-weston_timeline_point(const char *name, ...);
-
-#endif /* WESTON_TIMELINE_H */
+++ /dev/null
-/*
- * Copyright (c) 2012 Intel Corporation. All Rights Reserved.
- * Copyright © 2013 Intel Corporation
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <unistd.h>
-#include <assert.h>
-#include <errno.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include <pthread.h>
-
-#include <va/va.h>
-#include <va/va_drm.h>
-#include <va/va_drmcommon.h>
-#include <va/va_enc_h264.h>
-#include <va/va_vpp.h>
-
-#include "compositor.h"
-#include "vaapi-recorder.h"
-
-#define NAL_REF_IDC_NONE 0
-#define NAL_REF_IDC_LOW 1
-#define NAL_REF_IDC_MEDIUM 2
-#define NAL_REF_IDC_HIGH 3
-
-#define NAL_NON_IDR 1
-#define NAL_IDR 5
-#define NAL_SPS 7
-#define NAL_PPS 8
-#define NAL_SEI 6
-
-#define SLICE_TYPE_P 0
-#define SLICE_TYPE_B 1
-#define SLICE_TYPE_I 2
-
-#define ENTROPY_MODE_CAVLC 0
-#define ENTROPY_MODE_CABAC 1
-
-#define PROFILE_IDC_BASELINE 66
-#define PROFILE_IDC_MAIN 77
-#define PROFILE_IDC_HIGH 100
-
-struct vaapi_recorder {
- int drm_fd, output_fd;
- int width, height;
- int frame_count;
-
- int error;
- int destroying;
- pthread_t worker_thread;
- pthread_mutex_t mutex;
- pthread_cond_t input_cond;
-
- struct {
- int valid;
- int prime_fd, stride;
- } input;
-
- VADisplay va_dpy;
-
- /* video post processing is used for colorspace conversion */
- struct {
- VAConfigID cfg;
- VAContextID ctx;
- VABufferID pipeline_buf;
- VASurfaceID output;
- } vpp;
-
- struct {
- VAConfigID cfg;
- VAContextID ctx;
- VASurfaceID reference_picture[3];
-
- int intra_period;
- int output_size;
- int constraint_set_flag;
-
- struct {
- VAEncSequenceParameterBufferH264 seq;
- VAEncPictureParameterBufferH264 pic;
- VAEncSliceParameterBufferH264 slice;
- } param;
- } encoder;
-};
-
-static void *
-worker_thread_function(void *);
-
-/* bistream code used for writing the packed headers */
-
-#define BITSTREAM_ALLOCATE_STEPPING 4096
-
-struct bitstream {
- unsigned int *buffer;
- int bit_offset;
- int max_size_in_dword;
-};
-
-static unsigned int
-va_swap32(unsigned int val)
-{
- unsigned char *pval = (unsigned char *)&val;
-
- return ((pval[0] << 24) |
- (pval[1] << 16) |
- (pval[2] << 8) |
- (pval[3] << 0));
-}
-
-static void
-bitstream_start(struct bitstream *bs)
-{
- bs->max_size_in_dword = BITSTREAM_ALLOCATE_STEPPING;
- bs->buffer = calloc(bs->max_size_in_dword * sizeof(unsigned int), 1);
- bs->bit_offset = 0;
-}
-
-static void
-bitstream_end(struct bitstream *bs)
-{
- int pos = (bs->bit_offset >> 5);
- int bit_offset = (bs->bit_offset & 0x1f);
- int bit_left = 32 - bit_offset;
-
- if (bit_offset) {
- bs->buffer[pos] = va_swap32((bs->buffer[pos] << bit_left));
- }
-}
-
-static void
-bitstream_put_ui(struct bitstream *bs, unsigned int val, int size_in_bits)
-{
- int pos = (bs->bit_offset >> 5);
- int bit_offset = (bs->bit_offset & 0x1f);
- int bit_left = 32 - bit_offset;
-
- if (!size_in_bits)
- return;
-
- bs->bit_offset += size_in_bits;
-
- if (bit_left > size_in_bits) {
- bs->buffer[pos] = (bs->buffer[pos] << size_in_bits | val);
- return;
- }
-
- size_in_bits -= bit_left;
- bs->buffer[pos] =
- (bs->buffer[pos] << bit_left) | (val >> size_in_bits);
- bs->buffer[pos] = va_swap32(bs->buffer[pos]);
-
- if (pos + 1 == bs->max_size_in_dword) {
- bs->max_size_in_dword += BITSTREAM_ALLOCATE_STEPPING;
- bs->buffer =
- realloc(bs->buffer,
- bs->max_size_in_dword * sizeof(unsigned int));
- }
-
- bs->buffer[pos + 1] = val;
-}
-
-static void
-bitstream_put_ue(struct bitstream *bs, unsigned int val)
-{
- int size_in_bits = 0;
- int tmp_val = ++val;
-
- while (tmp_val) {
- tmp_val >>= 1;
- size_in_bits++;
- }
-
- bitstream_put_ui(bs, 0, size_in_bits - 1); /* leading zero */
- bitstream_put_ui(bs, val, size_in_bits);
-}
-
-static void
-bitstream_put_se(struct bitstream *bs, int val)
-{
- unsigned int new_val;
-
- if (val <= 0)
- new_val = -2 * val;
- else
- new_val = 2 * val - 1;
-
- bitstream_put_ue(bs, new_val);
-}
-
-static void
-bitstream_byte_aligning(struct bitstream *bs, int bit)
-{
- int bit_offset = (bs->bit_offset & 0x7);
- int bit_left = 8 - bit_offset;
- int new_val;
-
- if (!bit_offset)
- return;
-
- if (bit)
- new_val = (1 << bit_left) - 1;
- else
- new_val = 0;
-
- bitstream_put_ui(bs, new_val, bit_left);
-}
-
-static VAStatus
-encoder_create_config(struct vaapi_recorder *r)
-{
- VAConfigAttrib attrib[2];
- VAStatus status;
-
- /* FIXME: should check if VAEntrypointEncSlice is supported */
-
- /* FIXME: should check if specified attributes are supported */
-
- attrib[0].type = VAConfigAttribRTFormat;
- attrib[0].value = VA_RT_FORMAT_YUV420;
-
- attrib[1].type = VAConfigAttribRateControl;
- attrib[1].value = VA_RC_CQP;
-
- status = vaCreateConfig(r->va_dpy, VAProfileH264Main,
- VAEntrypointEncSlice, attrib, 2,
- &r->encoder.cfg);
- if (status != VA_STATUS_SUCCESS)
- return status;
-
- status = vaCreateContext(r->va_dpy, r->encoder.cfg,
- r->width, r->height, VA_PROGRESSIVE, 0, 0,
- &r->encoder.ctx);
- if (status != VA_STATUS_SUCCESS) {
- vaDestroyConfig(r->va_dpy, r->encoder.cfg);
- return status;
- }
-
- return VA_STATUS_SUCCESS;
-}
-
-static void
-encoder_destroy_config(struct vaapi_recorder *r)
-{
- vaDestroyContext(r->va_dpy, r->encoder.ctx);
- vaDestroyConfig(r->va_dpy, r->encoder.cfg);
-}
-
-static void
-encoder_init_seq_parameters(struct vaapi_recorder *r)
-{
- int width_in_mbs, height_in_mbs;
- int frame_cropping_flag = 0;
- int frame_crop_bottom_offset = 0;
-
- width_in_mbs = (r->width + 15) / 16;
- height_in_mbs = (r->height + 15) / 16;
-
- r->encoder.param.seq.level_idc = 41;
- r->encoder.param.seq.intra_period = r->encoder.intra_period;
- r->encoder.param.seq.max_num_ref_frames = 4;
- r->encoder.param.seq.picture_width_in_mbs = width_in_mbs;
- r->encoder.param.seq.picture_height_in_mbs = height_in_mbs;
- r->encoder.param.seq.seq_fields.bits.frame_mbs_only_flag = 1;
-
- /* Tc = num_units_in_tick / time_scale */
- r->encoder.param.seq.time_scale = 1800;
- r->encoder.param.seq.num_units_in_tick = 15;
-
- if (height_in_mbs * 16 - r->height > 0) {
- frame_cropping_flag = 1;
- frame_crop_bottom_offset = (height_in_mbs * 16 - r->height) / 2;
- }
-
- r->encoder.param.seq.frame_cropping_flag = frame_cropping_flag;
- r->encoder.param.seq.frame_crop_bottom_offset = frame_crop_bottom_offset;
-
- r->encoder.param.seq.seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = 2;
-}
-
-static VABufferID
-encoder_update_seq_parameters(struct vaapi_recorder *r)
-{
- VABufferID seq_buf;
- VAStatus status;
-
- status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
- VAEncSequenceParameterBufferType,
- sizeof(r->encoder.param.seq),
- 1, &r->encoder.param.seq,
- &seq_buf);
-
- if (status == VA_STATUS_SUCCESS)
- return seq_buf;
- else
- return VA_INVALID_ID;
-}
-
-static void
-encoder_init_pic_parameters(struct vaapi_recorder *r)
-{
- VAEncPictureParameterBufferH264 *pic = &r->encoder.param.pic;
-
- pic->pic_init_qp = 0;
-
- /* ENTROPY_MODE_CABAC */
- pic->pic_fields.bits.entropy_coding_mode_flag = 1;
-
- pic->pic_fields.bits.deblocking_filter_control_present_flag = 1;
-}
-
-static VABufferID
-encoder_update_pic_parameters(struct vaapi_recorder *r,
- VABufferID output_buf)
-{
- VAEncPictureParameterBufferH264 *pic = &r->encoder.param.pic;
- VAStatus status;
- VABufferID pic_param_buf;
- VASurfaceID curr_pic, pic0;
-
- curr_pic = r->encoder.reference_picture[r->frame_count % 2];
- pic0 = r->encoder.reference_picture[(r->frame_count + 1) % 2];
-
- pic->CurrPic.picture_id = curr_pic;
- pic->CurrPic.TopFieldOrderCnt = r->frame_count * 2;
- pic->ReferenceFrames[0].picture_id = pic0;
- pic->ReferenceFrames[1].picture_id = r->encoder.reference_picture[2];
- pic->ReferenceFrames[2].picture_id = VA_INVALID_ID;
-
- pic->coded_buf = output_buf;
- pic->frame_num = r->frame_count;
-
- pic->pic_fields.bits.idr_pic_flag = (r->frame_count == 0);
- pic->pic_fields.bits.reference_pic_flag = 1;
-
- status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
- VAEncPictureParameterBufferType,
- sizeof(VAEncPictureParameterBufferH264), 1,
- pic, &pic_param_buf);
-
- if (status == VA_STATUS_SUCCESS)
- return pic_param_buf;
- else
- return VA_INVALID_ID;
-}
-
-static VABufferID
-encoder_update_slice_parameter(struct vaapi_recorder *r, int slice_type)
-{
- VABufferID slice_param_buf;
- VAStatus status;
-
- int width_in_mbs = (r->width + 15) / 16;
- int height_in_mbs = (r->height + 15) / 16;
-
- memset(&r->encoder.param.slice, 0, sizeof r->encoder.param.slice);
-
- r->encoder.param.slice.num_macroblocks = width_in_mbs * height_in_mbs;
- r->encoder.param.slice.slice_type = slice_type;
-
- r->encoder.param.slice.slice_alpha_c0_offset_div2 = 2;
- r->encoder.param.slice.slice_beta_offset_div2 = 2;
-
- status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
- VAEncSliceParameterBufferType,
- sizeof(r->encoder.param.slice), 1,
- &r->encoder.param.slice,
- &slice_param_buf);
-
- if (status == VA_STATUS_SUCCESS)
- return slice_param_buf;
- else
- return VA_INVALID_ID;
-}
-
-static VABufferID
-encoder_update_misc_hdr_parameter(struct vaapi_recorder *r)
-{
- VAEncMiscParameterBuffer *misc_param;
- VAEncMiscParameterHRD *hrd;
- VABufferID buffer;
- VAStatus status;
-
- int total_size =
- sizeof(VAEncMiscParameterBuffer) +
- sizeof(VAEncMiscParameterRateControl);
-
- status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
- VAEncMiscParameterBufferType, total_size,
- 1, NULL, &buffer);
- if (status != VA_STATUS_SUCCESS)
- return VA_INVALID_ID;
-
- status = vaMapBuffer(r->va_dpy, buffer, (void **) &misc_param);
- if (status != VA_STATUS_SUCCESS) {
- vaDestroyBuffer(r->va_dpy, buffer);
- return VA_INVALID_ID;
- }
-
- misc_param->type = VAEncMiscParameterTypeHRD;
- hrd = (VAEncMiscParameterHRD *) misc_param->data;
-
- hrd->initial_buffer_fullness = 0;
- hrd->buffer_size = 0;
-
- vaUnmapBuffer(r->va_dpy, buffer);
-
- return buffer;
-}
-
-static int
-setup_encoder(struct vaapi_recorder *r)
-{
- VAStatus status;
-
- status = encoder_create_config(r);
- if (status != VA_STATUS_SUCCESS) {
- return -1;
- }
-
- status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_YUV420,
- r->width, r->height,
- r->encoder.reference_picture, 3,
- NULL, 0);
- if (status != VA_STATUS_SUCCESS) {
- encoder_destroy_config(r);
- return -1;
- }
-
- /* VAProfileH264Main */
- r->encoder.constraint_set_flag |= (1 << 1); /* Annex A.2.2 */
-
- r->encoder.output_size = r->width * r->height;
-
- r->encoder.intra_period = 30;
-
- encoder_init_seq_parameters(r);
- encoder_init_pic_parameters(r);
-
- return 0;
-}
-
-static void
-encoder_destroy(struct vaapi_recorder *r)
-{
- vaDestroySurfaces(r->va_dpy, r->encoder.reference_picture, 3);
-
- encoder_destroy_config(r);
-}
-
-static void
-nal_start_code_prefix(struct bitstream *bs)
-{
- bitstream_put_ui(bs, 0x00000001, 32);
-}
-
-static void
-nal_header(struct bitstream *bs, int nal_ref_idc, int nal_unit_type)
-{
- /* forbidden_zero_bit: 0 */
- bitstream_put_ui(bs, 0, 1);
-
- bitstream_put_ui(bs, nal_ref_idc, 2);
- bitstream_put_ui(bs, nal_unit_type, 5);
-}
-
-static void
-rbsp_trailing_bits(struct bitstream *bs)
-{
- bitstream_put_ui(bs, 1, 1);
- bitstream_byte_aligning(bs, 0);
-}
-
-static void sps_rbsp(struct bitstream *bs,
- VAEncSequenceParameterBufferH264 *seq,
- int constraint_set_flag)
-{
- int i;
-
- bitstream_put_ui(bs, PROFILE_IDC_MAIN, 8);
-
- /* constraint_set[0-3] flag */
- for (i = 0; i < 4; i++) {
- int set = (constraint_set_flag & (1 << i)) ? 1 : 0;
- bitstream_put_ui(bs, set, 1);
- }
-
- /* reserved_zero_4bits */
- bitstream_put_ui(bs, 0, 4);
- bitstream_put_ui(bs, seq->level_idc, 8);
- bitstream_put_ue(bs, seq->seq_parameter_set_id);
-
- bitstream_put_ue(bs, seq->seq_fields.bits.log2_max_frame_num_minus4);
- bitstream_put_ue(bs, seq->seq_fields.bits.pic_order_cnt_type);
- bitstream_put_ue(bs,
- seq->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4);
-
- bitstream_put_ue(bs, seq->max_num_ref_frames);
-
- /* gaps_in_frame_num_value_allowed_flag */
- bitstream_put_ui(bs, 0, 1);
-
- /* pic_width_in_mbs_minus1, pic_height_in_map_units_minus1 */
- bitstream_put_ue(bs, seq->picture_width_in_mbs - 1);
- bitstream_put_ue(bs, seq->picture_height_in_mbs - 1);
-
- bitstream_put_ui(bs, seq->seq_fields.bits.frame_mbs_only_flag, 1);
- bitstream_put_ui(bs, seq->seq_fields.bits.direct_8x8_inference_flag, 1);
-
- bitstream_put_ui(bs, seq->frame_cropping_flag, 1);
-
- if (seq->frame_cropping_flag) {
- bitstream_put_ue(bs, seq->frame_crop_left_offset);
- bitstream_put_ue(bs, seq->frame_crop_right_offset);
- bitstream_put_ue(bs, seq->frame_crop_top_offset);
- bitstream_put_ue(bs, seq->frame_crop_bottom_offset);
- }
-
- /* vui_parameters_present_flag */
- bitstream_put_ui(bs, 1, 1);
-
- /* aspect_ratio_info_present_flag */
- bitstream_put_ui(bs, 0, 1);
- /* overscan_info_present_flag */
- bitstream_put_ui(bs, 0, 1);
-
- /* video_signal_type_present_flag */
- bitstream_put_ui(bs, 0, 1);
- /* chroma_loc_info_present_flag */
- bitstream_put_ui(bs, 0, 1);
-
- /* timing_info_present_flag */
- bitstream_put_ui(bs, 1, 1);
- bitstream_put_ui(bs, seq->num_units_in_tick, 32);
- bitstream_put_ui(bs, seq->time_scale, 32);
- /* fixed_frame_rate_flag */
- bitstream_put_ui(bs, 1, 1);
-
- /* nal_hrd_parameters_present_flag */
- bitstream_put_ui(bs, 0, 1);
-
- /* vcl_hrd_parameters_present_flag */
- bitstream_put_ui(bs, 0, 1);
-
- /* low_delay_hrd_flag */
- bitstream_put_ui(bs, 0, 1);
-
- /* pic_struct_present_flag */
- bitstream_put_ui(bs, 0, 1);
- /* bitstream_restriction_flag */
- bitstream_put_ui(bs, 0, 1);
-
- rbsp_trailing_bits(bs);
-}
-
-static void pps_rbsp(struct bitstream *bs,
- VAEncPictureParameterBufferH264 *pic)
-{
- /* pic_parameter_set_id, seq_parameter_set_id */
- bitstream_put_ue(bs, pic->pic_parameter_set_id);
- bitstream_put_ue(bs, pic->seq_parameter_set_id);
-
- bitstream_put_ui(bs, pic->pic_fields.bits.entropy_coding_mode_flag, 1);
-
- /* pic_order_present_flag: 0 */
- bitstream_put_ui(bs, 0, 1);
-
- /* num_slice_groups_minus1 */
- bitstream_put_ue(bs, 0);
-
- bitstream_put_ue(bs, pic->num_ref_idx_l0_active_minus1);
- bitstream_put_ue(bs, pic->num_ref_idx_l1_active_minus1);
-
- bitstream_put_ui(bs, pic->pic_fields.bits.weighted_pred_flag, 1);
- bitstream_put_ui(bs, pic->pic_fields.bits.weighted_bipred_idc, 2);
-
- /* pic_init_qp_minus26, pic_init_qs_minus26, chroma_qp_index_offset */
- bitstream_put_se(bs, pic->pic_init_qp - 26);
- bitstream_put_se(bs, 0);
- bitstream_put_se(bs, 0);
-
- bitstream_put_ui(bs, pic->pic_fields.bits.deblocking_filter_control_present_flag, 1);
-
- /* constrained_intra_pred_flag, redundant_pic_cnt_present_flag */
- bitstream_put_ui(bs, 0, 1);
- bitstream_put_ui(bs, 0, 1);
-
- bitstream_put_ui(bs, pic->pic_fields.bits.transform_8x8_mode_flag, 1);
-
- /* pic_scaling_matrix_present_flag */
- bitstream_put_ui(bs, 0, 1);
- bitstream_put_se(bs, pic->second_chroma_qp_index_offset );
-
- rbsp_trailing_bits(bs);
-}
-
-static int
-build_packed_pic_buffer(struct vaapi_recorder *r,
- void **header_buffer)
-{
- struct bitstream bs;
-
- bitstream_start(&bs);
- nal_start_code_prefix(&bs);
- nal_header(&bs, NAL_REF_IDC_HIGH, NAL_PPS);
- pps_rbsp(&bs, &r->encoder.param.pic);
- bitstream_end(&bs);
-
- *header_buffer = bs.buffer;
- return bs.bit_offset;
-}
-
-static int
-build_packed_seq_buffer(struct vaapi_recorder *r,
- void **header_buffer)
-{
- struct bitstream bs;
-
- bitstream_start(&bs);
- nal_start_code_prefix(&bs);
- nal_header(&bs, NAL_REF_IDC_HIGH, NAL_SPS);
- sps_rbsp(&bs, &r->encoder.param.seq, r->encoder.constraint_set_flag);
- bitstream_end(&bs);
-
- *header_buffer = bs.buffer;
- return bs.bit_offset;
-}
-
-static int
-create_packed_header_buffers(struct vaapi_recorder *r, VABufferID *buffers,
- VAEncPackedHeaderType type,
- void *data, int bit_length)
-{
- VAEncPackedHeaderParameterBuffer packed_header;
- VAStatus status;
-
- packed_header.type = type;
- packed_header.bit_length = bit_length;
- packed_header.has_emulation_bytes = 0;
-
- status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
- VAEncPackedHeaderParameterBufferType,
- sizeof packed_header, 1, &packed_header,
- &buffers[0]);
- if (status != VA_STATUS_SUCCESS)
- return 0;
-
- status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
- VAEncPackedHeaderDataBufferType,
- (bit_length + 7) / 8, 1, data, &buffers[1]);
- if (status != VA_STATUS_SUCCESS) {
- vaDestroyBuffer(r->va_dpy, buffers[0]);
- return 0;
- }
-
- return 2;
-}
-
-static int
-encoder_prepare_headers(struct vaapi_recorder *r, VABufferID *buffers)
-{
- VABufferID *p;
-
- int bit_length;
- void *data;
-
- p = buffers;
-
- bit_length = build_packed_seq_buffer(r, &data);
- p += create_packed_header_buffers(r, p, VAEncPackedHeaderSequence,
- data, bit_length);
- free(data);
-
- bit_length = build_packed_pic_buffer(r, &data);
- p += create_packed_header_buffers(r, p, VAEncPackedHeaderPicture,
- data, bit_length);
- free(data);
-
- return p - buffers;
-}
-
-static VAStatus
-encoder_render_picture(struct vaapi_recorder *r, VASurfaceID input,
- VABufferID *buffers, int count)
-{
- VAStatus status;
-
- status = vaBeginPicture(r->va_dpy, r->encoder.ctx, input);
- if (status != VA_STATUS_SUCCESS)
- return status;
-
- status = vaRenderPicture(r->va_dpy, r->encoder.ctx, buffers, count);
- if (status != VA_STATUS_SUCCESS)
- return status;
-
- status = vaEndPicture(r->va_dpy, r->encoder.ctx);
- if (status != VA_STATUS_SUCCESS)
- return status;
-
- return vaSyncSurface(r->va_dpy, input);
-}
-
-static VABufferID
-encoder_create_output_buffer(struct vaapi_recorder *r)
-{
- VABufferID output_buf;
- VAStatus status;
-
- status = vaCreateBuffer(r->va_dpy, r->encoder.ctx,
- VAEncCodedBufferType, r->encoder.output_size,
- 1, NULL, &output_buf);
- if (status == VA_STATUS_SUCCESS)
- return output_buf;
- else
- return VA_INVALID_ID;
-}
-
-enum output_write_status {
- OUTPUT_WRITE_SUCCESS,
- OUTPUT_WRITE_OVERFLOW,
- OUTPUT_WRITE_FATAL
-};
-
-static enum output_write_status
-encoder_write_output(struct vaapi_recorder *r, VABufferID output_buf)
-{
- VACodedBufferSegment *segment;
- VAStatus status;
- int count;
-
- status = vaMapBuffer(r->va_dpy, output_buf, (void **) &segment);
- if (status != VA_STATUS_SUCCESS)
- return OUTPUT_WRITE_FATAL;
-
- if (segment->status & VA_CODED_BUF_STATUS_SLICE_OVERFLOW_MASK) {
- r->encoder.output_size *= 2;
- vaUnmapBuffer(r->va_dpy, output_buf);
- return OUTPUT_WRITE_OVERFLOW;
- }
-
- count = write(r->output_fd, segment->buf, segment->size);
-
- vaUnmapBuffer(r->va_dpy, output_buf);
-
- if (count < 0)
- return OUTPUT_WRITE_FATAL;
-
- return OUTPUT_WRITE_SUCCESS;
-}
-
-static void
-encoder_encode(struct vaapi_recorder *r, VASurfaceID input)
-{
- VABufferID output_buf = VA_INVALID_ID;
-
- VABufferID buffers[8];
- int count = 0;
- int i, slice_type;
- enum output_write_status ret;
-
- if ((r->frame_count % r->encoder.intra_period) == 0)
- slice_type = SLICE_TYPE_I;
- else
- slice_type = SLICE_TYPE_P;
-
- buffers[count++] = encoder_update_seq_parameters(r);
- buffers[count++] = encoder_update_misc_hdr_parameter(r);
- buffers[count++] = encoder_update_slice_parameter(r, slice_type);
-
- for (i = 0; i < count; i++)
- if (buffers[i] == VA_INVALID_ID)
- goto bail;
-
- if (r->frame_count == 0)
- count += encoder_prepare_headers(r, buffers + count);
-
- do {
- output_buf = encoder_create_output_buffer(r);
- if (output_buf == VA_INVALID_ID)
- goto bail;
-
- buffers[count++] =
- encoder_update_pic_parameters(r, output_buf);
- if (buffers[count - 1] == VA_INVALID_ID)
- goto bail;
-
- encoder_render_picture(r, input, buffers, count);
- ret = encoder_write_output(r, output_buf);
-
- vaDestroyBuffer(r->va_dpy, output_buf);
- output_buf = VA_INVALID_ID;
-
- vaDestroyBuffer(r->va_dpy, buffers[--count]);
- } while (ret == OUTPUT_WRITE_OVERFLOW);
-
- if (ret == OUTPUT_WRITE_FATAL)
- r->error = errno;
-
- for (i = 0; i < count; i++)
- vaDestroyBuffer(r->va_dpy, buffers[i]);
-
- r->frame_count++;
- return;
-
-bail:
- for (i = 0; i < count; i++)
- vaDestroyBuffer(r->va_dpy, buffers[i]);
- if (output_buf != VA_INVALID_ID)
- vaDestroyBuffer(r->va_dpy, output_buf);
-}
-
-
-static int
-setup_vpp(struct vaapi_recorder *r)
-{
- VAStatus status;
-
- status = vaCreateConfig(r->va_dpy, VAProfileNone,
- VAEntrypointVideoProc, NULL, 0,
- &r->vpp.cfg);
- if (status != VA_STATUS_SUCCESS) {
- weston_log("vaapi: failed to create VPP config\n");
- return -1;
- }
-
- status = vaCreateContext(r->va_dpy, r->vpp.cfg, r->width, r->height,
- 0, NULL, 0, &r->vpp.ctx);
- if (status != VA_STATUS_SUCCESS) {
- weston_log("vaapi: failed to create VPP context\n");
- goto err_cfg;
- }
-
- status = vaCreateBuffer(r->va_dpy, r->vpp.ctx,
- VAProcPipelineParameterBufferType,
- sizeof(VAProcPipelineParameterBuffer),
- 1, NULL, &r->vpp.pipeline_buf);
- if (status != VA_STATUS_SUCCESS) {
- weston_log("vaapi: failed to create VPP pipeline buffer\n");
- goto err_ctx;
- }
-
- status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_YUV420,
- r->width, r->height, &r->vpp.output, 1,
- NULL, 0);
- if (status != VA_STATUS_SUCCESS) {
- weston_log("vaapi: failed to create YUV surface\n");
- goto err_buf;
- }
-
- return 0;
-
-err_buf:
- vaDestroyBuffer(r->va_dpy, r->vpp.pipeline_buf);
-err_ctx:
- vaDestroyConfig(r->va_dpy, r->vpp.ctx);
-err_cfg:
- vaDestroyConfig(r->va_dpy, r->vpp.cfg);
-
- return -1;
-}
-
-static void
-vpp_destroy(struct vaapi_recorder *r)
-{
- vaDestroySurfaces(r->va_dpy, &r->vpp.output, 1);
- vaDestroyBuffer(r->va_dpy, r->vpp.pipeline_buf);
- vaDestroyConfig(r->va_dpy, r->vpp.ctx);
- vaDestroyConfig(r->va_dpy, r->vpp.cfg);
-}
-
-static int
-setup_worker_thread(struct vaapi_recorder *r)
-{
- pthread_mutex_init(&r->mutex, NULL);
- pthread_cond_init(&r->input_cond, NULL);
- pthread_create(&r->worker_thread, NULL, worker_thread_function, r);
-
- return 1;
-}
-
-static void
-destroy_worker_thread(struct vaapi_recorder *r)
-{
- pthread_mutex_lock(&r->mutex);
-
- /* Make sure the worker thread finishes */
- r->destroying = 1;
- pthread_cond_signal(&r->input_cond);
-
- pthread_mutex_unlock(&r->mutex);
-
- pthread_join(r->worker_thread, NULL);
-
- pthread_mutex_destroy(&r->mutex);
- pthread_cond_destroy(&r->input_cond);
-}
-
-struct vaapi_recorder *
-vaapi_recorder_create(int drm_fd, int width, int height, const char *filename)
-{
- struct vaapi_recorder *r;
- VAStatus status;
- int major, minor;
- int flags;
-
- r = zalloc(sizeof *r);
- if (r == NULL)
- return NULL;
-
- r->width = width;
- r->height = height;
- r->drm_fd = drm_fd;
-
- if (setup_worker_thread(r) < 0)
- goto err_free;
-
- flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
- r->output_fd = open(filename, flags, 0644);
- if (r->output_fd < 0)
- goto err_thread;
-
- r->va_dpy = vaGetDisplayDRM(drm_fd);
- if (!r->va_dpy) {
- weston_log("failed to create VA display\n");
- goto err_fd;
- }
-
- status = vaInitialize(r->va_dpy, &major, &minor);
- if (status != VA_STATUS_SUCCESS) {
- weston_log("vaapi: failed to initialize display\n");
- goto err_fd;
- }
-
- if (setup_vpp(r) < 0) {
- weston_log("vaapi: failed to initialize VPP pipeline\n");
- goto err_va_dpy;
- }
-
- if (setup_encoder(r) < 0) {
- goto err_vpp;
- }
-
- return r;
-
-err_vpp:
- vpp_destroy(r);
-err_va_dpy:
- vaTerminate(r->va_dpy);
-err_fd:
- close(r->output_fd);
-err_thread:
- destroy_worker_thread(r);
-err_free:
- free(r);
-
- return NULL;
-}
-
-void
-vaapi_recorder_destroy(struct vaapi_recorder *r)
-{
- destroy_worker_thread(r);
-
- encoder_destroy(r);
- vpp_destroy(r);
-
- vaTerminate(r->va_dpy);
-
- close(r->output_fd);
- close(r->drm_fd);
-
- free(r);
-}
-
-static VAStatus
-create_surface_from_fd(struct vaapi_recorder *r, int prime_fd,
- int stride, VASurfaceID *surface)
-{
- VASurfaceAttrib va_attribs[2];
- VASurfaceAttribExternalBuffers va_attrib_extbuf;
- VAStatus status;
-
- unsigned long buffer_fd = prime_fd;
-
- va_attrib_extbuf.pixel_format = VA_FOURCC_BGRX;
- va_attrib_extbuf.width = r->width;
- va_attrib_extbuf.height = r->height;
- va_attrib_extbuf.data_size = r->height * stride;
- va_attrib_extbuf.num_planes = 1;
- va_attrib_extbuf.pitches[0] = stride;
- va_attrib_extbuf.offsets[0] = 0;
- va_attrib_extbuf.buffers = &buffer_fd;
- va_attrib_extbuf.num_buffers = 1;
- va_attrib_extbuf.flags = 0;
- va_attrib_extbuf.private_data = NULL;
-
- va_attribs[0].type = VASurfaceAttribMemoryType;
- va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE;
- va_attribs[0].value.type = VAGenericValueTypeInteger;
- va_attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
-
- va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor;
- va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE;
- va_attribs[1].value.type = VAGenericValueTypePointer;
- va_attribs[1].value.value.p = &va_attrib_extbuf;
-
- status = vaCreateSurfaces(r->va_dpy, VA_RT_FORMAT_RGB32,
- r->width, r->height, surface, 1,
- va_attribs, 2);
-
- return status;
-}
-
-static VAStatus
-convert_rgb_to_yuv(struct vaapi_recorder *r, VASurfaceID rgb_surface)
-{
- VAProcPipelineParameterBuffer *pipeline_param;
- VAStatus status;
-
- status = vaMapBuffer(r->va_dpy, r->vpp.pipeline_buf,
- (void **) &pipeline_param);
- if (status != VA_STATUS_SUCCESS)
- return status;
-
- memset(pipeline_param, 0, sizeof *pipeline_param);
-
- pipeline_param->surface = rgb_surface;
- pipeline_param->surface_color_standard = VAProcColorStandardNone;
-
- pipeline_param->output_background_color = 0xff000000;
- pipeline_param->output_color_standard = VAProcColorStandardNone;
-
- status = vaUnmapBuffer(r->va_dpy, r->vpp.pipeline_buf);
- if (status != VA_STATUS_SUCCESS)
- return status;
-
- status = vaBeginPicture(r->va_dpy, r->vpp.ctx, r->vpp.output);
- if (status != VA_STATUS_SUCCESS)
- return status;
-
- status = vaRenderPicture(r->va_dpy, r->vpp.ctx,
- &r->vpp.pipeline_buf, 1);
- if (status != VA_STATUS_SUCCESS)
- return status;
-
- status = vaEndPicture(r->va_dpy, r->vpp.ctx);
- if (status != VA_STATUS_SUCCESS)
- return status;
-
- return status;
-}
-
-static void
-recorder_frame(struct vaapi_recorder *r)
-{
- VASurfaceID rgb_surface;
- VAStatus status;
-
- status = create_surface_from_fd(r, r->input.prime_fd,
- r->input.stride, &rgb_surface);
- if (status != VA_STATUS_SUCCESS) {
- weston_log("[libva recorder] "
- "failed to create surface from bo\n");
- return;
- }
-
- close(r->input.prime_fd);
-
- status = convert_rgb_to_yuv(r, rgb_surface);
- if (status != VA_STATUS_SUCCESS) {
- weston_log("[libva recorder] "
- "color space conversion failed\n");
- return;
- }
-
- encoder_encode(r, r->vpp.output);
-
- vaDestroySurfaces(r->va_dpy, &rgb_surface, 1);
-}
-
-static void *
-worker_thread_function(void *data)
-{
- struct vaapi_recorder *r = data;
-
- pthread_mutex_lock(&r->mutex);
-
- while (!r->destroying) {
- if (!r->input.valid)
- pthread_cond_wait(&r->input_cond, &r->mutex);
-
- /* If the thread is awaken by destroy_worker_thread(),
- * there might not be valid input */
- if (!r->input.valid)
- continue;
-
- recorder_frame(r);
- r->input.valid = 0;
- }
-
- pthread_mutex_unlock(&r->mutex);
-
- return NULL;
-}
-
-int
-vaapi_recorder_frame(struct vaapi_recorder *r, int prime_fd, int stride)
-{
- int ret = 0;
-
- pthread_mutex_lock(&r->mutex);
-
- if (r->error) {
- errno = r->error;
- ret = -1;
- goto unlock;
- }
-
- /* The mutex is never released while encoding, so this point should
- * never be reached if input.valid is true. */
- assert(!r->input.valid);
-
- r->input.prime_fd = prime_fd;
- r->input.stride = stride;
- r->input.valid = 1;
- pthread_cond_signal(&r->input_cond);
-
-unlock:
- pthread_mutex_unlock(&r->mutex);
-
- return ret;
-}
+++ /dev/null
-/*
- * Copyright © 2013 Intel Corporation
- *
- * 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.
- */
-
-#ifndef _VAAPI_RECORDER_H_
-#define _VAAPI_RECORDER_H_
-
-struct vaapi_recorder;
-
-struct vaapi_recorder *
-vaapi_recorder_create(int drm_fd, int width, int height, const char *filename);
-void
-vaapi_recorder_destroy(struct vaapi_recorder *r);
-int
-vaapi_recorder_frame(struct vaapi_recorder *r, int fd, int stride);
-
-#endif /* _VAAPI_RECORDER_H_ */
+++ /dev/null
-/*
- * Copyright © 2013 Intel Corporation
- *
- * 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.
- */
-
-#ifndef WESTON_VERSION_H
-#define WESTON_VERSION_H
-
-#define WESTON_VERSION_MAJOR @WESTON_VERSION_MAJOR@
-#define WESTON_VERSION_MINOR @WESTON_VERSION_MINOR@
-#define WESTON_VERSION_MICRO @WESTON_VERSION_MICRO@
-#define WESTON_VERSION "@WESTON_VERSION@"
-
-/* This macro may not do what you expect. Weston doesn't guarantee
- * a stable API between 1.X and 1.Y, and thus this macro will return
- * FALSE on any WESTON_VERSION_AT_LEAST(1,X,0) if the actual version
- * is 1.Y.0 and X != Y). In particular, it fails if X < Y, that is,
- * 1.3.0 is considered to not be "at least" 1.4.0.
- *
- * If you want to test for the version number being 1.3.0 or above or
- * maybe in a range (eg 1.2.0 to 1.4.0), just use the WESTON_VERSION_*
- * defines above directly.
- */
-
-#define WESTON_VERSION_AT_LEAST(major, minor, micro) \
- (WESTON_VERSION_MAJOR == (major) && \
- WESTON_VERSION_MINOR == (minor) && \
- WESTON_VERSION_MICRO >= (micro))
-
-#endif
+++ /dev/null
-/*
- * Copyright © 2012 Intel Corporation
- *
- * 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.
- */
-#include <assert.h>
-#include <float.h>
-#include <math.h>
-
-#include "vertex-clipping.h"
-
-float
-float_difference(float a, float b)
-{
- /* http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ */
- static const float max_diff = 4.0f * FLT_MIN;
- static const float max_rel_diff = 4.0e-5;
- float diff = a - b;
- float adiff = fabsf(diff);
-
- if (adiff <= max_diff)
- return 0.0f;
-
- a = fabsf(a);
- b = fabsf(b);
- if (adiff <= (a > b ? a : b) * max_rel_diff)
- return 0.0f;
-
- return diff;
-}
-
-/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line x = x_arg.
- * Compute the y coordinate of the intersection.
- */
-static float
-clip_intersect_y(float p1x, float p1y, float p2x, float p2y,
- float x_arg)
-{
- float a;
- float diff = float_difference(p1x, p2x);
-
- /* Practically vertical line segment, yet the end points have already
- * been determined to be on different sides of the line. Therefore
- * the line segment is part of the line and intersects everywhere.
- * Return the end point, so we use the whole line segment.
- */
- if (diff == 0.0f)
- return p2y;
-
- a = (x_arg - p2x) / diff;
- return p2y + (p1y - p2y) * a;
-}
-
-/* A line segment (p1x, p1y)-(p2x, p2y) intersects the line y = y_arg.
- * Compute the x coordinate of the intersection.
- */
-static float
-clip_intersect_x(float p1x, float p1y, float p2x, float p2y,
- float y_arg)
-{
- float a;
- float diff = float_difference(p1y, p2y);
-
- /* Practically horizontal line segment, yet the end points have already
- * been determined to be on different sides of the line. Therefore
- * the line segment is part of the line and intersects everywhere.
- * Return the end point, so we use the whole line segment.
- */
- if (diff == 0.0f)
- return p2x;
-
- a = (y_arg - p2y) / diff;
- return p2x + (p1x - p2x) * a;
-}
-
-enum path_transition {
- PATH_TRANSITION_OUT_TO_OUT = 0,
- PATH_TRANSITION_OUT_TO_IN = 1,
- PATH_TRANSITION_IN_TO_OUT = 2,
- PATH_TRANSITION_IN_TO_IN = 3,
-};
-
-static void
-clip_append_vertex(struct clip_context *ctx, float x, float y)
-{
- *ctx->vertices.x++ = x;
- *ctx->vertices.y++ = y;
-}
-
-static enum path_transition
-path_transition_left_edge(struct clip_context *ctx, float x, float y)
-{
- return ((ctx->prev.x >= ctx->clip.x1) << 1) | (x >= ctx->clip.x1);
-}
-
-static enum path_transition
-path_transition_right_edge(struct clip_context *ctx, float x, float y)
-{
- return ((ctx->prev.x < ctx->clip.x2) << 1) | (x < ctx->clip.x2);
-}
-
-static enum path_transition
-path_transition_top_edge(struct clip_context *ctx, float x, float y)
-{
- return ((ctx->prev.y >= ctx->clip.y1) << 1) | (y >= ctx->clip.y1);
-}
-
-static enum path_transition
-path_transition_bottom_edge(struct clip_context *ctx, float x, float y)
-{
- return ((ctx->prev.y < ctx->clip.y2) << 1) | (y < ctx->clip.y2);
-}
-
-static void
-clip_polygon_leftright(struct clip_context *ctx,
- enum path_transition transition,
- float x, float y, float clip_x)
-{
- float yi;
-
- switch (transition) {
- case PATH_TRANSITION_IN_TO_IN:
- clip_append_vertex(ctx, x, y);
- break;
- case PATH_TRANSITION_IN_TO_OUT:
- yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x);
- clip_append_vertex(ctx, clip_x, yi);
- break;
- case PATH_TRANSITION_OUT_TO_IN:
- yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x);
- clip_append_vertex(ctx, clip_x, yi);
- clip_append_vertex(ctx, x, y);
- break;
- case PATH_TRANSITION_OUT_TO_OUT:
- /* nothing */
- break;
- default:
- assert(0 && "bad enum path_transition");
- }
-
- ctx->prev.x = x;
- ctx->prev.y = y;
-}
-
-static void
-clip_polygon_topbottom(struct clip_context *ctx,
- enum path_transition transition,
- float x, float y, float clip_y)
-{
- float xi;
-
- switch (transition) {
- case PATH_TRANSITION_IN_TO_IN:
- clip_append_vertex(ctx, x, y);
- break;
- case PATH_TRANSITION_IN_TO_OUT:
- xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y);
- clip_append_vertex(ctx, xi, clip_y);
- break;
- case PATH_TRANSITION_OUT_TO_IN:
- xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y);
- clip_append_vertex(ctx, xi, clip_y);
- clip_append_vertex(ctx, x, y);
- break;
- case PATH_TRANSITION_OUT_TO_OUT:
- /* nothing */
- break;
- default:
- assert(0 && "bad enum path_transition");
- }
-
- ctx->prev.x = x;
- ctx->prev.y = y;
-}
-
-static void
-clip_context_prepare(struct clip_context *ctx, const struct polygon8 *src,
- float *dst_x, float *dst_y)
-{
- ctx->prev.x = src->x[src->n - 1];
- ctx->prev.y = src->y[src->n - 1];
- ctx->vertices.x = dst_x;
- ctx->vertices.y = dst_y;
-}
-
-static int
-clip_polygon_left(struct clip_context *ctx, const struct polygon8 *src,
- float *dst_x, float *dst_y)
-{
- enum path_transition trans;
- int i;
-
- if (src->n < 2)
- return 0;
-
- clip_context_prepare(ctx, src, dst_x, dst_y);
- for (i = 0; i < src->n; i++) {
- trans = path_transition_left_edge(ctx, src->x[i], src->y[i]);
- clip_polygon_leftright(ctx, trans, src->x[i], src->y[i],
- ctx->clip.x1);
- }
- return ctx->vertices.x - dst_x;
-}
-
-static int
-clip_polygon_right(struct clip_context *ctx, const struct polygon8 *src,
- float *dst_x, float *dst_y)
-{
- enum path_transition trans;
- int i;
-
- if (src->n < 2)
- return 0;
-
- clip_context_prepare(ctx, src, dst_x, dst_y);
- for (i = 0; i < src->n; i++) {
- trans = path_transition_right_edge(ctx, src->x[i], src->y[i]);
- clip_polygon_leftright(ctx, trans, src->x[i], src->y[i],
- ctx->clip.x2);
- }
- return ctx->vertices.x - dst_x;
-}
-
-static int
-clip_polygon_top(struct clip_context *ctx, const struct polygon8 *src,
- float *dst_x, float *dst_y)
-{
- enum path_transition trans;
- int i;
-
- if (src->n < 2)
- return 0;
-
- clip_context_prepare(ctx, src, dst_x, dst_y);
- for (i = 0; i < src->n; i++) {
- trans = path_transition_top_edge(ctx, src->x[i], src->y[i]);
- clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i],
- ctx->clip.y1);
- }
- return ctx->vertices.x - dst_x;
-}
-
-static int
-clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src,
- float *dst_x, float *dst_y)
-{
- enum path_transition trans;
- int i;
-
- if (src->n < 2)
- return 0;
-
- clip_context_prepare(ctx, src, dst_x, dst_y);
- for (i = 0; i < src->n; i++) {
- trans = path_transition_bottom_edge(ctx, src->x[i], src->y[i]);
- clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i],
- ctx->clip.y2);
- }
- return ctx->vertices.x - dst_x;
-}
-
-#define max(a, b) (((a) > (b)) ? (a) : (b))
-#define min(a, b) (((a) > (b)) ? (b) : (a))
-#define clip(x, a, b) min(max(x, a), b)
-
-int
-clip_simple(struct clip_context *ctx,
- struct polygon8 *surf,
- float *ex,
- float *ey)
-{
- int i;
- for (i = 0; i < surf->n; i++) {
- ex[i] = clip(surf->x[i], ctx->clip.x1, ctx->clip.x2);
- ey[i] = clip(surf->y[i], ctx->clip.y1, ctx->clip.y2);
- }
- return surf->n;
-}
-
-int
-clip_transformed(struct clip_context *ctx,
- struct polygon8 *surf,
- float *ex,
- float *ey)
-{
- struct polygon8 polygon;
- int i, n;
-
- polygon.n = clip_polygon_left(ctx, surf, polygon.x, polygon.y);
- surf->n = clip_polygon_right(ctx, &polygon, surf->x, surf->y);
- polygon.n = clip_polygon_top(ctx, surf, polygon.x, polygon.y);
- surf->n = clip_polygon_bottom(ctx, &polygon, surf->x, surf->y);
-
- /* Get rid of duplicate vertices */
- ex[0] = surf->x[0];
- ey[0] = surf->y[0];
- n = 1;
- for (i = 1; i < surf->n; i++) {
- if (float_difference(ex[n - 1], surf->x[i]) == 0.0f &&
- float_difference(ey[n - 1], surf->y[i]) == 0.0f)
- continue;
- ex[n] = surf->x[i];
- ey[n] = surf->y[i];
- n++;
- }
- if (float_difference(ex[n - 1], surf->x[0]) == 0.0f &&
- float_difference(ey[n - 1], surf->y[0]) == 0.0f)
- n--;
-
- return n;
-}
+++ /dev/null
-/*
- * Copyright © 2012 Intel Corporation
- *
- * 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.
- */
-#ifndef _WESTON_VERTEX_CLIPPING_H
-#define _WESTON_VERTEX_CLIPPING_H
-
-struct polygon8 {
- float x[8];
- float y[8];
- int n;
-};
-
-struct clip_context {
- struct {
- float x;
- float y;
- } prev;
-
- struct {
- float x1, y1;
- float x2, y2;
- } clip;
-
- struct {
- float *x;
- float *y;
- } vertices;
-};
-
-float
-float_difference(float a, float b);
-
-int
-clip_simple(struct clip_context *ctx,
- struct polygon8 *surf,
- float *ex,
- float *ey);
-
-int
-clip_transformed(struct clip_context *ctx,
- struct polygon8 *surf,
- float *ex,
- float *ey);\
-
-#endif
+++ /dev/null
-/*
- * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
- * All Rights Reserved.
- *
- * 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.
- */
-/* Extensions used by Weston, copied from Mesa's eglmesaext.h, */
-
-#ifndef WESTON_EGL_EXT_H
-#define WESTON_EGL_EXT_H
-
-#ifndef EGL_WL_bind_wayland_display
-#define EGL_WL_bind_wayland_display 1
-
-struct wl_display;
-
-#ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI EGLBoolean EGLAPIENTRY eglBindWaylandDisplayWL(EGLDisplay dpy, struct wl_display *display);
-EGLAPI EGLBoolean EGLAPIENTRY eglUnbindWaylandDisplayWL(EGLDisplay dpy, struct wl_display *display);
-#endif
-typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
-typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWL) (EGLDisplay dpy, struct wl_display *display);
-#endif
-
-/*
- * This is a little different to the tests shipped with EGL implementations,
- * which wrap the entire thing in #ifndef EGL_WL_bind_wayland_display, then go
- * on to define both BindWaylandDisplay and QueryWaylandBuffer.
- *
- * Unfortunately, some implementations (particularly the version of Mesa shipped
- * in Ubuntu 12.04) define EGL_WL_bind_wayland_display, but then only provide
- * prototypes for (Un)BindWaylandDisplay, completely omitting
- * QueryWaylandBuffer.
- *
- * Detect this, and provide our own definitions if necessary.
- */
-#ifndef EGL_WAYLAND_BUFFER_WL
-#define EGL_WAYLAND_BUFFER_WL 0x31D5 /* eglCreateImageKHR target */
-#define EGL_WAYLAND_PLANE_WL 0x31D6 /* eglCreateImageKHR target */
-
-#define EGL_TEXTURE_Y_U_V_WL 0x31D7
-#define EGL_TEXTURE_Y_UV_WL 0x31D8
-#define EGL_TEXTURE_Y_XUXV_WL 0x31D9
-#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
-
-struct wl_resource;
-#ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI EGLBoolean EGLAPIENTRY eglQueryWaylandBufferWL(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
-#endif
-typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
-#endif
-
-#ifndef EGL_WL_create_wayland_buffer_from_image
-#define EGL_WL_create_wayland_buffer_from_image 1
-
-#ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI struct wl_buffer * EGLAPIENTRY eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, EGLImageKHR image);
-#endif
-typedef struct wl_buffer * (EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL) (EGLDisplay dpy, EGLImageKHR image);
-#endif
-
-#ifndef EGL_TEXTURE_EXTERNAL_WL
-#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
-#endif
-
-#ifndef EGL_BUFFER_AGE_EXT
-#define EGL_BUFFER_AGE_EXT 0x313D
-#endif
-
-#ifndef EGL_WAYLAND_Y_INVERTED_WL
-#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB /* eglQueryWaylandBufferWL attribute */
-#endif
-
-/* Mesas gl2ext.h and probably Khronos upstream defined
- * GL_EXT_unpack_subimage with non _EXT suffixed GL_UNPACK_* tokens.
- * In case we're using that mess, manually define the _EXT versions
- * of the tokens here.*/
-#if defined(GL_EXT_unpack_subimage) && !defined(GL_UNPACK_ROW_LENGTH_EXT)
-#define GL_UNPACK_ROW_LENGTH_EXT 0x0CF2
-#define GL_UNPACK_SKIP_ROWS_EXT 0x0CF3
-#define GL_UNPACK_SKIP_PIXELS_EXT 0x0CF4
-#endif
-
-/* Define needed tokens from EGL_EXT_image_dma_buf_import extension
- * here to avoid having to add ifdefs everywhere.*/
-#ifndef EGL_EXT_image_dma_buf_import
-#define EGL_LINUX_DMA_BUF_EXT 0x3270
-#define EGL_LINUX_DRM_FOURCC_EXT 0x3271
-#define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272
-#define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273
-#define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274
-#define EGL_DMA_BUF_PLANE1_FD_EXT 0x3275
-#define EGL_DMA_BUF_PLANE1_OFFSET_EXT 0x3276
-#define EGL_DMA_BUF_PLANE1_PITCH_EXT 0x3277
-#define EGL_DMA_BUF_PLANE2_FD_EXT 0x3278
-#define EGL_DMA_BUF_PLANE2_OFFSET_EXT 0x3279
-#define EGL_DMA_BUF_PLANE2_PITCH_EXT 0x327A
-#endif
-
-
-#endif
+++ /dev/null
-/*
- * Copyright © 2012 Benjamin Franzke
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <poll.h>
-#include <errno.h>
-
-#include <error.h>
-#include <getopt.h>
-
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/signalfd.h>
-#include <signal.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include <linux/vt.h>
-#include <linux/major.h>
-#include <linux/kd.h>
-
-#include <pwd.h>
-#include <grp.h>
-#include <security/pam_appl.h>
-
-#ifdef HAVE_SYSTEMD_LOGIN
-#include <systemd/sd-login.h>
-#endif
-
-#include "weston-launch.h"
-
-#define DRM_MAJOR 226
-
-#ifndef KDSKBMUTE
-#define KDSKBMUTE 0x4B51
-#endif
-
-#ifndef EVIOCREVOKE
-#define EVIOCREVOKE _IOW('E', 0x91, int)
-#endif
-
-#define MAX_ARGV_SIZE 256
-
-#ifdef HAVE_LIBDRM
-
-#include <xf86drm.h>
-
-#else
-
-static inline int
-drmDropMaster(int drm_fd)
-{
- return 0;
-}
-
-static inline int
-drmSetMaster(int drm_fd)
-{
- return 0;
-}
-
-#endif
-
-struct weston_launch {
- struct pam_conv pc;
- pam_handle_t *ph;
- int tty;
- int ttynr;
- int sock[2];
- int drm_fd;
- int last_input_fd;
- int kb_mode;
- struct passwd *pw;
-
- int signalfd;
-
- pid_t child;
- int verbose;
- char *new_user;
-};
-
-union cmsg_data { unsigned char b[4]; int fd; };
-
-static gid_t *
-read_groups(void)
-{
- int n;
- gid_t *groups;
-
- n = getgroups(0, NULL);
-
- if (n < 0) {
- fprintf(stderr, "Unable to retrieve groups: %m\n");
- return NULL;
- }
-
- groups = malloc(n * sizeof(gid_t));
- if (!groups)
- return NULL;
-
- if (getgroups(n, groups) < 0) {
- fprintf(stderr, "Unable to retrieve groups: %m\n");
- free(groups);
- return NULL;
- }
- return groups;
-}
-
-static bool
-weston_launch_allowed(struct weston_launch *wl)
-{
- struct group *gr;
- gid_t *groups;
- int i;
-#ifdef HAVE_SYSTEMD_LOGIN
- char *session, *seat;
- int err;
-#endif
-
- if (getuid() == 0)
- return true;
-
- gr = getgrnam("weston-launch");
- if (gr) {
- groups = read_groups();
- if (groups) {
- for (i = 0; groups[i]; ++i) {
- if (groups[i] == gr->gr_gid) {
- free(groups);
- return true;
- }
- }
- free(groups);
- }
- }
-
-#ifdef HAVE_SYSTEMD_LOGIN
- err = sd_pid_get_session(getpid(), &session);
- if (err == 0 && session) {
- if (sd_session_is_active(session) &&
- sd_session_get_seat(session, &seat) == 0) {
- free(seat);
- free(session);
- return true;
- }
- free(session);
- }
-#endif
-
- return false;
-}
-
-static int
-pam_conversation_fn(int msg_count,
- const struct pam_message **messages,
- struct pam_response **responses,
- void *user_data)
-{
- return PAM_SUCCESS;
-}
-
-static int
-setup_pam(struct weston_launch *wl)
-{
- int err;
-
- wl->pc.conv = pam_conversation_fn;
- wl->pc.appdata_ptr = wl;
-
- err = pam_start("login", wl->pw->pw_name, &wl->pc, &wl->ph);
- if (err != PAM_SUCCESS) {
- fprintf(stderr, "failed to start pam transaction: %d: %s\n",
- err, pam_strerror(wl->ph, err));
- return -1;
- }
-
- err = pam_set_item(wl->ph, PAM_TTY, ttyname(wl->tty));
- if (err != PAM_SUCCESS) {
- fprintf(stderr, "failed to set PAM_TTY item: %d: %s\n",
- err, pam_strerror(wl->ph, err));
- return -1;
- }
-
- err = pam_open_session(wl->ph, 0);
- if (err != PAM_SUCCESS) {
- fprintf(stderr, "failed to open pam session: %d: %s\n",
- err, pam_strerror(wl->ph, err));
- return -1;
- }
-
- return 0;
-}
-
-static int
-setup_launcher_socket(struct weston_launch *wl)
-{
- if (socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, wl->sock) < 0)
- error(1, errno, "socketpair failed");
-
- if (fcntl(wl->sock[0], F_SETFD, FD_CLOEXEC) < 0)
- error(1, errno, "fcntl failed");
-
- return 0;
-}
-
-static int
-setup_signals(struct weston_launch *wl)
-{
- int ret;
- sigset_t mask;
- struct sigaction sa;
-
- memset(&sa, 0, sizeof sa);
- sa.sa_handler = SIG_DFL;
- sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
- ret = sigaction(SIGCHLD, &sa, NULL);
- assert(ret == 0);
-
- sa.sa_handler = SIG_IGN;
- sa.sa_flags = 0;
- sigaction(SIGHUP, &sa, NULL);
-
- ret = sigemptyset(&mask);
- assert(ret == 0);
- sigaddset(&mask, SIGCHLD);
- sigaddset(&mask, SIGINT);
- sigaddset(&mask, SIGTERM);
- sigaddset(&mask, SIGUSR1);
- sigaddset(&mask, SIGUSR2);
- ret = sigprocmask(SIG_BLOCK, &mask, NULL);
- assert(ret == 0);
-
- wl->signalfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
- if (wl->signalfd < 0)
- return -errno;
-
- return 0;
-}
-
-static void
-setenv_fd(const char *env, int fd)
-{
- char buf[32];
-
- snprintf(buf, sizeof buf, "%d", fd);
- setenv(env, buf, 1);
-}
-
-static int
-send_reply(struct weston_launch *wl, int reply)
-{
- int len;
-
- do {
- len = send(wl->sock[0], &reply, sizeof reply, 0);
- } while (len < 0 && errno == EINTR);
-
- return len;
-}
-
-static int
-handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
-{
- int fd = -1, ret = -1;
- char control[CMSG_SPACE(sizeof(fd))];
- struct cmsghdr *cmsg;
- struct stat s;
- struct msghdr nmsg;
- struct iovec iov;
- struct weston_launcher_open *message;
- union cmsg_data *data;
-
- message = msg->msg_iov->iov_base;
- if ((size_t)len < sizeof(*message))
- goto err0;
-
- /* Ensure path is null-terminated */
- ((char *) message)[len-1] = '\0';
-
- fd = open(message->path, message->flags);
- if (fd < 0) {
- fprintf(stderr, "Error opening device %s: %m\n",
- message->path);
- goto err0;
- }
-
- if (fstat(fd, &s) < 0) {
- close(fd);
- fd = -1;
- fprintf(stderr, "Failed to stat %s\n", message->path);
- goto err0;
- }
-
- if (major(s.st_rdev) != INPUT_MAJOR &&
- major(s.st_rdev) != DRM_MAJOR) {
- close(fd);
- fd = -1;
- fprintf(stderr, "Device %s is not an input or drm device\n",
- message->path);
- goto err0;
- }
-
-err0:
- memset(&nmsg, 0, sizeof nmsg);
- nmsg.msg_iov = &iov;
- nmsg.msg_iovlen = 1;
- if (fd != -1) {
- nmsg.msg_control = control;
- nmsg.msg_controllen = sizeof control;
- cmsg = CMSG_FIRSTHDR(&nmsg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
- data = (union cmsg_data *) CMSG_DATA(cmsg);
- data->fd = fd;
- nmsg.msg_controllen = cmsg->cmsg_len;
- ret = 0;
- }
- iov.iov_base = &ret;
- iov.iov_len = sizeof ret;
-
- if (wl->verbose)
- fprintf(stderr, "weston-launch: opened %s: ret: %d, fd: %d\n",
- message->path, ret, fd);
- do {
- len = sendmsg(wl->sock[0], &nmsg, 0);
- } while (len < 0 && errno == EINTR);
-
- if (len < 0)
- return -1;
-
- if (fd != -1 && major(s.st_rdev) == DRM_MAJOR)
- wl->drm_fd = fd;
- if (fd != -1 && major(s.st_rdev) == INPUT_MAJOR &&
- wl->last_input_fd < fd)
- wl->last_input_fd = fd;
-
- return 0;
-}
-
-static int
-handle_socket_msg(struct weston_launch *wl)
-{
- char control[CMSG_SPACE(sizeof(int))];
- char buf[BUFSIZ];
- struct msghdr msg;
- struct iovec iov;
- int ret = -1;
- ssize_t len;
- struct weston_launcher_message *message;
-
- memset(&msg, 0, sizeof(msg));
- iov.iov_base = buf;
- iov.iov_len = sizeof buf;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = control;
- msg.msg_controllen = sizeof control;
-
- do {
- len = recvmsg(wl->sock[0], &msg, 0);
- } while (len < 0 && errno == EINTR);
-
- if (len < 1)
- return -1;
-
- message = (void *) buf;
- switch (message->opcode) {
- case WESTON_LAUNCHER_OPEN:
- ret = handle_open(wl, &msg, len);
- break;
- }
-
- return ret;
-}
-
-static void
-quit(struct weston_launch *wl, int status)
-{
- struct vt_mode mode = { 0 };
- int err;
-
- close(wl->signalfd);
- close(wl->sock[0]);
-
- if (wl->new_user) {
- err = pam_close_session(wl->ph, 0);
- if (err)
- fprintf(stderr, "pam_close_session failed: %d: %s\n",
- err, pam_strerror(wl->ph, err));
- pam_end(wl->ph, err);
- }
-
- if (ioctl(wl->tty, KDSKBMUTE, 0) &&
- ioctl(wl->tty, KDSKBMODE, wl->kb_mode))
- fprintf(stderr, "failed to restore keyboard mode: %m\n");
-
- if (ioctl(wl->tty, KDSETMODE, KD_TEXT))
- fprintf(stderr, "failed to set KD_TEXT mode on tty: %m\n");
-
- /* We have to drop master before we switch the VT back in
- * VT_AUTO, so we don't risk switching to a VT with another
- * display server, that will then fail to set drm master. */
- drmDropMaster(wl->drm_fd);
-
- mode.mode = VT_AUTO;
- if (ioctl(wl->tty, VT_SETMODE, &mode) < 0)
- fprintf(stderr, "could not reset vt handling\n");
-
- exit(status);
-}
-
-static void
-close_input_fds(struct weston_launch *wl)
-{
- struct stat s;
- int fd;
-
- for (fd = 3; fd <= wl->last_input_fd; fd++) {
- if (fstat(fd, &s) == 0 && major(s.st_rdev) == INPUT_MAJOR) {
- /* EVIOCREVOKE may fail if the kernel doesn't
- * support it, but all we can do is ignore it. */
- ioctl(fd, EVIOCREVOKE, 0);
- close(fd);
- }
- }
-}
-
-static int
-handle_signal(struct weston_launch *wl)
-{
- struct signalfd_siginfo sig;
- int pid, status, ret;
-
- if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) {
- error(0, errno, "reading signalfd failed");
- return -1;
- }
-
- switch (sig.ssi_signo) {
- case SIGCHLD:
- pid = waitpid(-1, &status, 0);
- if (pid == wl->child) {
- wl->child = 0;
- if (WIFEXITED(status))
- ret = WEXITSTATUS(status);
- else if (WIFSIGNALED(status))
- /*
- * If weston dies because of signal N, we
- * return 10+N. This is distinct from
- * weston-launch dying because of a signal
- * (128+N).
- */
- ret = 10 + WTERMSIG(status);
- else
- ret = 0;
- quit(wl, ret);
- }
- break;
- case SIGTERM:
- case SIGINT:
- if (wl->child)
- kill(wl->child, sig.ssi_signo);
- break;
- case SIGUSR1:
- send_reply(wl, WESTON_LAUNCHER_DEACTIVATE);
- close_input_fds(wl);
- drmDropMaster(wl->drm_fd);
- ioctl(wl->tty, VT_RELDISP, 1);
- break;
- case SIGUSR2:
- ioctl(wl->tty, VT_RELDISP, VT_ACKACQ);
- drmSetMaster(wl->drm_fd);
- send_reply(wl, WESTON_LAUNCHER_ACTIVATE);
- break;
- default:
- return -1;
- }
-
- return 0;
-}
-
-static int
-setup_tty(struct weston_launch *wl, const char *tty)
-{
- struct stat buf;
- struct vt_mode mode = { 0 };
- char *t;
-
- if (!wl->new_user) {
- wl->tty = STDIN_FILENO;
- } else if (tty) {
- t = ttyname(STDIN_FILENO);
- if (t && strcmp(t, tty) == 0)
- wl->tty = STDIN_FILENO;
- else
- wl->tty = open(tty, O_RDWR | O_NOCTTY);
- } else {
- int tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
- char filename[16];
-
- if (tty0 < 0)
- error(1, errno, "could not open tty0");
-
- if (ioctl(tty0, VT_OPENQRY, &wl->ttynr) < 0 || wl->ttynr == -1)
- error(1, errno, "failed to find non-opened console");
-
- snprintf(filename, sizeof filename, "/dev/tty%d", wl->ttynr);
- wl->tty = open(filename, O_RDWR | O_NOCTTY);
- close(tty0);
- }
-
- if (wl->tty < 0)
- error(1, errno, "failed to open tty");
-
- if (fstat(wl->tty, &buf) == -1 ||
- major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0)
- error(1, 0, "weston-launch must be run from a virtual terminal");
-
- if (tty) {
- if (fstat(wl->tty, &buf) < 0)
- error(1, errno, "stat %s failed", tty);
-
- if (major(buf.st_rdev) != TTY_MAJOR)
- error(1, 0, "invalid tty device: %s", tty);
-
- wl->ttynr = minor(buf.st_rdev);
- }
-
- if (ioctl(wl->tty, KDGKBMODE, &wl->kb_mode))
- error(1, errno, "failed to get current keyboard mode: %m\n");
-
- if (ioctl(wl->tty, KDSKBMUTE, 1) &&
- ioctl(wl->tty, KDSKBMODE, K_OFF))
- error(1, errno, "failed to set K_OFF keyboard mode: %m\n");
-
- if (ioctl(wl->tty, KDSETMODE, KD_GRAPHICS))
- error(1, errno, "failed to set KD_GRAPHICS mode on tty: %m\n");
-
- mode.mode = VT_PROCESS;
- mode.relsig = SIGUSR1;
- mode.acqsig = SIGUSR2;
- if (ioctl(wl->tty, VT_SETMODE, &mode) < 0)
- error(1, errno, "failed to take control of vt handling\n");
-
- return 0;
-}
-
-static void
-setup_session(struct weston_launch *wl)
-{
- char **env;
- char *term;
- int i;
-
- if (wl->tty != STDIN_FILENO) {
- if (setsid() < 0)
- error(1, errno, "setsid failed");
- if (ioctl(wl->tty, TIOCSCTTY, 0) < 0)
- error(1, errno, "TIOCSCTTY failed - tty is in use");
- }
-
- term = getenv("TERM");
- clearenv();
- if (term)
- setenv("TERM", term, 1);
- setenv("USER", wl->pw->pw_name, 1);
- setenv("LOGNAME", wl->pw->pw_name, 1);
- setenv("HOME", wl->pw->pw_dir, 1);
- setenv("SHELL", wl->pw->pw_shell, 1);
-
- env = pam_getenvlist(wl->ph);
- if (env) {
- for (i = 0; env[i]; ++i) {
- if (putenv(env[i]) != 0)
- error(0, 0, "putenv %s failed", env[i]);
- }
- free(env);
- }
-}
-
-static void
-drop_privileges(struct weston_launch *wl)
-{
- if (setgid(wl->pw->pw_gid) < 0 ||
-#ifdef HAVE_INITGROUPS
- initgroups(wl->pw->pw_name, wl->pw->pw_gid) < 0 ||
-#endif
- setuid(wl->pw->pw_uid) < 0)
- error(1, errno, "dropping privileges failed");
-}
-
-static void
-launch_compositor(struct weston_launch *wl, int argc, char *argv[])
-{
- char *child_argv[MAX_ARGV_SIZE];
- sigset_t mask;
- int i;
-
- if (wl->verbose)
- printf("weston-launch: spawned weston with pid: %d\n", getpid());
- if (wl->new_user)
- setup_session(wl);
-
- if (geteuid() == 0)
- drop_privileges(wl);
-
- setenv_fd("WESTON_TTY_FD", wl->tty);
- setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]);
-
- unsetenv("DISPLAY");
-
- /* Do not give our signal mask to the new process. */
- sigemptyset(&mask);
- sigaddset(&mask, SIGTERM);
- sigaddset(&mask, SIGCHLD);
- sigaddset(&mask, SIGINT);
- sigprocmask(SIG_UNBLOCK, &mask, NULL);
-
- child_argv[0] = "/bin/sh";
- child_argv[1] = "-l";
- child_argv[2] = "-c";
- child_argv[3] = BINDIR "/weston \"$@\"";
- child_argv[4] = "weston";
- for (i = 0; i < argc; ++i)
- child_argv[5 + i] = argv[i];
- child_argv[5 + i] = NULL;
-
- execv(child_argv[0], child_argv);
- error(1, errno, "exec failed");
-}
-
-static void
-help(const char *name)
-{
- fprintf(stderr, "Usage: %s [args...] [-- [weston args..]]\n", name);
- fprintf(stderr, " -u, --user Start session as specified username\n");
- fprintf(stderr, " -t, --tty Start session on alternative tty\n");
- fprintf(stderr, " -v, --verbose Be verbose\n");
- fprintf(stderr, " -h, --help Display this help message\n");
-}
-
-int
-main(int argc, char *argv[])
-{
- struct weston_launch wl;
- int i, c;
- char *tty = NULL;
- struct option opts[] = {
- { "user", required_argument, NULL, 'u' },
- { "tty", required_argument, NULL, 't' },
- { "verbose", no_argument, NULL, 'v' },
- { "help", no_argument, NULL, 'h' },
- { 0, 0, NULL, 0 }
- };
-
- memset(&wl, 0, sizeof wl);
-
- while ((c = getopt_long(argc, argv, "u:t::vh", opts, &i)) != -1) {
- switch (c) {
- case 'u':
- wl.new_user = optarg;
- if (getuid() != 0)
- error(1, 0, "Permission denied. -u allowed for root only");
- break;
- case 't':
- tty = optarg;
- break;
- case 'v':
- wl.verbose = 1;
- break;
- case 'h':
- help("weston-launch");
- exit(EXIT_FAILURE);
- default:
- exit(EXIT_FAILURE);
- }
- }
-
- if ((argc - optind) > (MAX_ARGV_SIZE - 6))
- error(1, E2BIG, "Too many arguments to pass to weston");
-
- if (wl.new_user)
- wl.pw = getpwnam(wl.new_user);
- else
- wl.pw = getpwuid(getuid());
- if (wl.pw == NULL)
- error(1, errno, "failed to get username");
-
- if (!weston_launch_allowed(&wl))
- error(1, 0, "Permission denied. You should either:\n"
-#ifdef HAVE_SYSTEMD_LOGIN
- " - run from an active and local (systemd) session.\n"
-#else
- " - enable systemd session support for weston-launch.\n"
-#endif
- " - or add yourself to the 'weston-launch' group.");
-
- if (setup_tty(&wl, tty) < 0)
- exit(EXIT_FAILURE);
-
- if (wl.new_user && setup_pam(&wl) < 0)
- exit(EXIT_FAILURE);
-
- if (setup_launcher_socket(&wl) < 0)
- exit(EXIT_FAILURE);
-
- if (setup_signals(&wl) < 0)
- exit(EXIT_FAILURE);
-
- wl.child = fork();
- if (wl.child == -1)
- error(EXIT_FAILURE, errno, "fork failed");
-
- if (wl.child == 0)
- launch_compositor(&wl, argc - optind, argv + optind);
-
- close(wl.sock[1]);
- if (wl.tty != STDIN_FILENO)
- close(wl.tty);
-
- while (1) {
- struct pollfd fds[2];
- int n;
-
- fds[0].fd = wl.sock[0];
- fds[0].events = POLLIN;
- fds[1].fd = wl.signalfd;
- fds[1].events = POLLIN;
-
- n = poll(fds, 2, -1);
- if (n < 0)
- error(0, errno, "poll failed");
- if (fds[0].revents & POLLIN)
- handle_socket_msg(&wl);
- if (fds[1].revents)
- handle_signal(&wl);
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright © 2012 Benjamin Franzke
- *
- * 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.
- */
-
-#ifndef _WESTON_LAUNCH_H_
-#define _WESTON_LAUNCH_H_
-
-enum weston_launcher_opcode {
- WESTON_LAUNCHER_OPEN,
-};
-
-enum weston_launcher_event {
- WESTON_LAUNCHER_SUCCESS,
- WESTON_LAUNCHER_ACTIVATE,
- WESTON_LAUNCHER_DEACTIVATE
-};
-
-struct weston_launcher_message {
- int opcode;
-};
-
-struct weston_launcher_open {
- struct weston_launcher_message header;
- int flags;
- char path[0];
-};
-
-#endif
+++ /dev/null
-/*
- * Copyright © 2012 Scott Moreau
- *
- * 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.
- */
-
-#include "config.h"
-
-#include <assert.h>
-#include <stdlib.h>
-#include <stdbool.h>
-
-#include "compositor.h"
-#include "text-cursor-position-server-protocol.h"
-#include "shared/helpers.h"
-
-static void
-weston_zoom_frame_z(struct weston_animation *animation,
- struct weston_output *output, uint32_t msecs)
-{
- if (animation->frame_counter <= 1)
- output->zoom.spring_z.timestamp = msecs;
-
- weston_spring_update(&output->zoom.spring_z, msecs);
-
- if (output->zoom.spring_z.current > output->zoom.max_level)
- output->zoom.spring_z.current = output->zoom.max_level;
- else if (output->zoom.spring_z.current < 0.0)
- output->zoom.spring_z.current = 0.0;
-
- if (weston_spring_done(&output->zoom.spring_z)) {
- if (output->zoom.active && output->zoom.level <= 0.0) {
- output->zoom.active = false;
- output->zoom.seat = NULL;
- output->disable_planes--;
- wl_list_remove(&output->zoom.motion_listener.link);
- }
- output->zoom.spring_z.current = output->zoom.level;
- wl_list_remove(&animation->link);
- wl_list_init(&animation->link);
- }
-
- output->dirty = 1;
- weston_output_damage(output);
-}
-
-static void
-zoom_area_center_from_point(struct weston_output *output,
- double *x, double *y)
-{
- float level = output->zoom.spring_z.current;
-
- *x = (*x - output->x) * level + output->width / 2.;
- *y = (*y - output->y) * level + output->height / 2.;
-}
-
-static void
-weston_output_update_zoom_transform(struct weston_output *output)
-{
- double x = output->zoom.current.x; /* global pointer coords */
- double y = output->zoom.current.y;
- float level;
-
- level = output->zoom.spring_z.current;
-
- if (!output->zoom.active || level > output->zoom.max_level ||
- level == 0.0f)
- return;
-
- zoom_area_center_from_point(output, &x, &y);
-
- output->zoom.trans_x = x - output->width / 2;
- output->zoom.trans_y = y - output->height / 2;
-
- if (output->zoom.trans_x < 0)
- output->zoom.trans_x = 0;
- if (output->zoom.trans_y < 0)
- output->zoom.trans_y = 0;
- if (output->zoom.trans_x > level * output->width)
- output->zoom.trans_x = level * output->width;
- if (output->zoom.trans_y > level * output->height)
- output->zoom.trans_y = level * output->height;
-}
-
-static void
-weston_zoom_transition(struct weston_output *output)
-{
- if (output->zoom.level != output->zoom.spring_z.current) {
- output->zoom.spring_z.target = output->zoom.level;
- if (wl_list_empty(&output->zoom.animation_z.link)) {
- output->zoom.animation_z.frame_counter = 0;
- wl_list_insert(output->animation_list.prev,
- &output->zoom.animation_z.link);
- }
- }
-
- output->dirty = 1;
- weston_output_damage(output);
-}
-
-WL_EXPORT void
-weston_output_update_zoom(struct weston_output *output)
-{
- struct weston_seat *seat = output->zoom.seat;
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
-
- assert(output->zoom.active);
-
- output->zoom.current.x = wl_fixed_to_double(pointer->x);
- output->zoom.current.y = wl_fixed_to_double(pointer->y);
-
- weston_zoom_transition(output);
- weston_output_update_zoom_transform(output);
-}
-
-static void
-motion(struct wl_listener *listener, void *data)
-{
- struct weston_output_zoom *zoom =
- container_of(listener, struct weston_output_zoom, motion_listener);
- struct weston_output *output =
- container_of(zoom, struct weston_output, zoom);
-
- weston_output_update_zoom(output);
-}
-
-WL_EXPORT void
-weston_output_activate_zoom(struct weston_output *output,
- struct weston_seat *seat)
-{
- struct weston_pointer *pointer = weston_seat_get_pointer(seat);
-
- if (output->zoom.active)
- return;
-
- output->zoom.active = true;
- output->zoom.seat = seat;
- output->disable_planes++;
- wl_signal_add(&pointer->motion_signal,
- &output->zoom.motion_listener);
-}
-
-WL_EXPORT void
-weston_output_init_zoom(struct weston_output *output)
-{
- output->zoom.active = false;
- output->zoom.seat = NULL;
- output->zoom.increment = 0.07;
- output->zoom.max_level = 0.95;
- output->zoom.level = 0.0;
- output->zoom.trans_x = 0.0;
- output->zoom.trans_y = 0.0;
- weston_spring_init(&output->zoom.spring_z, 250.0, 0.0, 0.0);
- output->zoom.spring_z.friction = 1000;
- output->zoom.animation_z.frame = weston_zoom_frame_z;
- wl_list_init(&output->zoom.animation_z.link);
- output->zoom.motion_listener.notify = motion;
-}
#include <string.h>
#include <stdbool.h>
-#include "src/compositor.h"
+#include "compositor.h"
#include "ivi-shell/ivi-layout-export.h"
#include "ivi-shell/ivi-layout-private.h"
#include "ivi-test.h"
#include <string.h>
#include <assert.h>
-#include "src/compositor.h"
+#include "compositor.h"
#include "compositor/weston.h"
#include "weston-test-server-protocol.h"
#include "ivi-test.h"
#include <assert.h>
-#include "src/compositor.h"
+#include "compositor.h"
static void
surface_to_from_global(void *data)
#include <stdio.h>
#include <assert.h>
-#include "src/compositor.h"
+#include "compositor.h"
static void
surface_transform(void *data)
#include "weston-test-runner.h"
#include "shared/helpers.h"
-#include "src/vertex-clipping.h"
+#include "vertex-clipping.h"
#define BOUNDING_BOX_TOP_Y 100.0f
#define BOUNDING_BOX_LEFT_X 50.0f
#include <unistd.h>
#include <string.h>
-#include "src/compositor.h"
+#include "compositor.h"
#include "compositor/weston.h"
#include "weston-test-server-protocol.h"
#ifdef ENABLE_EGL
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include "src/weston-egl-ext.h"
+#include "weston-egl-ext.h"
#endif /* ENABLE_EGL */
#include "shared/helpers.h"