From b5e3ea218b183b91e17b843b375fadd37563a1c0 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 3 Jun 2016 17:12:10 +0300 Subject: Rename src/ to libweston/ This clarifies what is supposed to be the libweston code. v2: screen-share.c is already in compositor/ instead. Signed-off-by: Pekka Paalanen Reviewed-by: Yong Bakos Acked-by: Daniel Stone Reviewed-by: Quentin Glidic Tested-by: Quentin Glidic Tested-by: Benoit Gschwind Acked-by: Benoit Gschwind [Pekka: rebased] --- Makefile.am | 182 +- clients/cliptest.c | 2 +- configure.ac | 4 +- libweston/animation.c | 485 ++++ libweston/bindings.c | 579 +++++ libweston/clipboard.c | 306 +++ libweston/compositor-drm.c | 3285 +++++++++++++++++++++++ libweston/compositor-drm.h | 138 + libweston/compositor-fbdev.c | 783 ++++++ libweston/compositor-fbdev.h | 61 + libweston/compositor-headless.c | 258 ++ libweston/compositor-headless.h | 53 + libweston/compositor-rdp.c | 1331 ++++++++++ libweston/compositor-rdp.h | 54 + libweston/compositor-wayland.c | 2348 +++++++++++++++++ libweston/compositor-wayland.h | 61 + libweston/compositor-x11.c | 1720 +++++++++++++ libweston/compositor-x11.h | 62 + libweston/compositor.c | 5015 ++++++++++++++++++++++++++++++++++++ libweston/compositor.h | 1724 +++++++++++++ libweston/data-device.c | 1340 ++++++++++ libweston/dbus.c | 407 +++ libweston/dbus.h | 110 + libweston/gl-renderer.c | 3157 +++++++++++++++++++++++ libweston/gl-renderer.h | 132 + libweston/input.c | 2765 ++++++++++++++++++++ libweston/launcher-direct.c | 315 +++ libweston/launcher-impl.h | 45 + libweston/launcher-logind.c | 839 ++++++ libweston/launcher-util.c | 117 + libweston/launcher-util.h | 58 + libweston/launcher-weston-launch.c | 300 +++ libweston/libbacklight.c | 310 +++ libweston/libbacklight.h | 79 + libweston/libinput-device.c | 593 +++++ libweston/libinput-device.h | 80 + libweston/libinput-seat.c | 416 +++ libweston/libinput-seat.h | 72 + libweston/libweston.pc.in | 12 + libweston/linux-dmabuf.c | 497 ++++ libweston/linux-dmabuf.h | 88 + libweston/log.c | 98 + libweston/noop-renderer.c | 121 + libweston/pixman-renderer.c | 931 +++++++ libweston/pixman-renderer.h | 40 + libweston/screenshooter.c | 487 ++++ libweston/spring-tool.c | 74 + libweston/timeline-object.h | 55 + libweston/timeline.c | 292 +++ libweston/timeline.h | 65 + libweston/vaapi-recorder.c | 1161 +++++++++ libweston/vaapi-recorder.h | 38 + libweston/version.h.in | 50 + libweston/vertex-clipping.c | 330 +++ libweston/vertex-clipping.h | 66 + libweston/weston-egl-ext.h | 120 + libweston/weston-launch.c | 772 ++++++ libweston/weston-launch.h | 49 + libweston/zoom.c | 177 ++ src/animation.c | 485 ---- src/bindings.c | 579 ----- src/clipboard.c | 306 --- src/compositor-drm.c | 3285 ----------------------- src/compositor-drm.h | 138 - src/compositor-fbdev.c | 783 ------ src/compositor-fbdev.h | 61 - src/compositor-headless.c | 258 -- src/compositor-headless.h | 53 - src/compositor-rdp.c | 1331 ---------- src/compositor-rdp.h | 54 - src/compositor-wayland.c | 2348 ----------------- src/compositor-wayland.h | 61 - src/compositor-x11.c | 1720 ------------- src/compositor-x11.h | 62 - src/compositor.c | 5015 ------------------------------------ src/compositor.h | 1724 ------------- src/data-device.c | 1340 ---------- src/dbus.c | 407 --- src/dbus.h | 110 - src/gl-renderer.c | 3157 ----------------------- src/gl-renderer.h | 132 - src/input.c | 2765 -------------------- src/launcher-direct.c | 315 --- src/launcher-impl.h | 45 - src/launcher-logind.c | 839 ------ src/launcher-util.c | 117 - src/launcher-util.h | 58 - src/launcher-weston-launch.c | 300 --- src/libbacklight.c | 310 --- src/libbacklight.h | 79 - src/libinput-device.c | 593 ----- src/libinput-device.h | 80 - src/libinput-seat.c | 416 --- src/libinput-seat.h | 72 - src/libweston.pc.in | 12 - src/linux-dmabuf.c | 497 ---- src/linux-dmabuf.h | 88 - src/log.c | 98 - src/noop-renderer.c | 121 - src/pixman-renderer.c | 931 ------- src/pixman-renderer.h | 40 - src/screenshooter.c | 487 ---- src/spring-tool.c | 74 - src/timeline-object.h | 55 - src/timeline.c | 292 --- src/timeline.h | 65 - src/vaapi-recorder.c | 1161 --------- src/vaapi-recorder.h | 38 - src/version.h.in | 50 - src/vertex-clipping.c | 330 --- src/vertex-clipping.h | 66 - src/weston-egl-ext.h | 120 - src/weston-launch.c | 772 ------ src/weston-launch.h | 49 - src/zoom.c | 177 -- tests/ivi_layout-internal-test.c | 2 +- tests/ivi_layout-test-plugin.c | 2 +- tests/surface-global-test.c | 2 +- tests/surface-test.c | 2 +- tests/vertex-clip-test.c | 2 +- tests/weston-test.c | 4 +- 121 files changed, 35022 insertions(+), 35022 deletions(-) create mode 100644 libweston/animation.c create mode 100644 libweston/bindings.c create mode 100644 libweston/clipboard.c create mode 100644 libweston/compositor-drm.c create mode 100644 libweston/compositor-drm.h create mode 100644 libweston/compositor-fbdev.c create mode 100644 libweston/compositor-fbdev.h create mode 100644 libweston/compositor-headless.c create mode 100644 libweston/compositor-headless.h create mode 100644 libweston/compositor-rdp.c create mode 100644 libweston/compositor-rdp.h create mode 100644 libweston/compositor-wayland.c create mode 100644 libweston/compositor-wayland.h create mode 100644 libweston/compositor-x11.c create mode 100644 libweston/compositor-x11.h create mode 100644 libweston/compositor.c create mode 100644 libweston/compositor.h create mode 100644 libweston/data-device.c create mode 100644 libweston/dbus.c create mode 100644 libweston/dbus.h create mode 100644 libweston/gl-renderer.c create mode 100644 libweston/gl-renderer.h create mode 100644 libweston/input.c create mode 100644 libweston/launcher-direct.c create mode 100644 libweston/launcher-impl.h create mode 100644 libweston/launcher-logind.c create mode 100644 libweston/launcher-util.c create mode 100644 libweston/launcher-util.h create mode 100644 libweston/launcher-weston-launch.c create mode 100644 libweston/libbacklight.c create mode 100644 libweston/libbacklight.h create mode 100644 libweston/libinput-device.c create mode 100644 libweston/libinput-device.h create mode 100644 libweston/libinput-seat.c create mode 100644 libweston/libinput-seat.h create mode 100644 libweston/libweston.pc.in create mode 100644 libweston/linux-dmabuf.c create mode 100644 libweston/linux-dmabuf.h create mode 100644 libweston/log.c create mode 100644 libweston/noop-renderer.c create mode 100644 libweston/pixman-renderer.c create mode 100644 libweston/pixman-renderer.h create mode 100644 libweston/screenshooter.c create mode 100644 libweston/spring-tool.c create mode 100644 libweston/timeline-object.h create mode 100644 libweston/timeline.c create mode 100644 libweston/timeline.h create mode 100644 libweston/vaapi-recorder.c create mode 100644 libweston/vaapi-recorder.h create mode 100644 libweston/version.h.in create mode 100644 libweston/vertex-clipping.c create mode 100644 libweston/vertex-clipping.h create mode 100644 libweston/weston-egl-ext.h create mode 100644 libweston/weston-launch.c create mode 100644 libweston/weston-launch.h create mode 100644 libweston/zoom.c delete mode 100644 src/animation.c delete mode 100644 src/bindings.c delete mode 100644 src/clipboard.c delete mode 100644 src/compositor-drm.c delete mode 100644 src/compositor-drm.h delete mode 100644 src/compositor-fbdev.c delete mode 100644 src/compositor-fbdev.h delete mode 100644 src/compositor-headless.c delete mode 100644 src/compositor-headless.h delete mode 100644 src/compositor-rdp.c delete mode 100644 src/compositor-rdp.h delete mode 100644 src/compositor-wayland.c delete mode 100644 src/compositor-wayland.h delete mode 100644 src/compositor-x11.c delete mode 100644 src/compositor-x11.h delete mode 100644 src/compositor.c delete mode 100644 src/compositor.h delete mode 100644 src/data-device.c delete mode 100644 src/dbus.c delete mode 100644 src/dbus.h delete mode 100644 src/gl-renderer.c delete mode 100644 src/gl-renderer.h delete mode 100644 src/input.c delete mode 100644 src/launcher-direct.c delete mode 100644 src/launcher-impl.h delete mode 100644 src/launcher-logind.c delete mode 100644 src/launcher-util.c delete mode 100644 src/launcher-util.h delete mode 100644 src/launcher-weston-launch.c delete mode 100644 src/libbacklight.c delete mode 100644 src/libbacklight.h delete mode 100644 src/libinput-device.c delete mode 100644 src/libinput-device.h delete mode 100644 src/libinput-seat.c delete mode 100644 src/libinput-seat.h delete mode 100644 src/libweston.pc.in delete mode 100644 src/linux-dmabuf.c delete mode 100644 src/linux-dmabuf.h delete mode 100644 src/log.c delete mode 100644 src/noop-renderer.c delete mode 100644 src/pixman-renderer.c delete mode 100644 src/pixman-renderer.h delete mode 100644 src/screenshooter.c delete mode 100644 src/spring-tool.c delete mode 100644 src/timeline-object.h delete mode 100644 src/timeline.c delete mode 100644 src/timeline.h delete mode 100644 src/vaapi-recorder.c delete mode 100644 src/vaapi-recorder.h delete mode 100644 src/version.h.in delete mode 100644 src/vertex-clipping.c delete mode 100644 src/vertex-clipping.h delete mode 100644 src/weston-egl-ext.h delete mode 100644 src/weston-launch.c delete mode 100644 src/weston-launch.h delete mode 100644 src/zoom.c diff --git a/Makefile.am b/Makefile.am index 0d0334a7..67ac3516 100644 --- a/Makefile.am +++ b/Makefile.am @@ -44,8 +44,8 @@ all-local : weston.ini ivi-shell/weston.ini 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 \ @@ -70,38 +70,38 @@ libweston_la_LIBADD = $(COMPOSITOR_LIBS) $(LIBUNWIND_LIBS) \ 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 @@ -116,7 +116,7 @@ systemd_notify_la_SOURCES = \ compositor/systemd-notify.c \ shared/helpers.h \ shared/zalloc.h \ - src/compositor.h + libweston/compositor.h endif nodist_libweston_la_SOURCES = \ @@ -156,37 +156,37 @@ weston_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 @@ -195,7 +195,7 @@ 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) \ @@ -220,22 +220,22 @@ endif 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 \ @@ -259,10 +259,10 @@ gl_renderer_la_CFLAGS = \ $(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 @@ -279,17 +279,17 @@ x11_backend_la_CFLAGS = \ $(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 @@ -308,16 +308,16 @@ drm_backend_la_CFLAGS = \ $(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 @@ -338,8 +338,8 @@ wayland_backend_la_CFLAGS = \ $(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 \ @@ -352,8 +352,8 @@ headless_backend_la_LDFLAGS = -module -avoid-version 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 @@ -373,8 +373,8 @@ fbdev_backend_la_CFLAGS = \ $(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 @@ -390,8 +390,8 @@ rdp_backend_la_CFLAGS = \ $(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 @@ -422,11 +422,11 @@ noinst_PROGRAMS += spring-tool 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 @@ -607,8 +607,8 @@ weston_image_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) 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 @@ -837,8 +837,8 @@ module_LTLIBRARIES += desktop-shell.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)"' \ @@ -870,8 +870,8 @@ module_LTLIBRARIES += fullscreen-shell.la 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 @@ -960,8 +960,8 @@ module_LTLIBRARIES += xwayland.la 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)"' \ @@ -1184,8 +1184,8 @@ config_parser_test_CFLAGS = \ 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 = \ @@ -1334,8 +1334,8 @@ if BUILD_SETBACKLIGHT 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 diff --git a/clients/cliptest.c b/clients/cliptest.c index d0f4e3ea..57aefdab 100644 --- a/clients/cliptest.c +++ b/clients/cliptest.c @@ -49,7 +49,7 @@ #include #include -#include "src/vertex-clipping.h" +#include "libweston/vertex-clipping.h" #include "shared/xalloc.h" #include "window.h" diff --git a/configure.ac b/configure.ac index 41191f69..9a7211d2 100644 --- a/configure.ac +++ b/configure.ac @@ -633,11 +633,11 @@ if test "x$enable_systemd_notify" = "xyes"; then 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]) diff --git a/libweston/animation.c b/libweston/animation.c new file mode 100644 index 00000000..2c7943f6 --- /dev/null +++ b/libweston/animation.c @@ -0,0 +1,485 @@ +/* + * 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 +#include +#include +#include + +#include +#include + +#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; +} diff --git a/libweston/bindings.c b/libweston/bindings.c new file mode 100644 index 00000000..cc68cfe1 --- /dev/null +++ b/libweston/bindings.c @@ -0,0 +1,579 @@ +/* + * 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 +#include + +#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); +} diff --git a/libweston/clipboard.c b/libweston/clipboard.c new file mode 100644 index 00000000..54a578f0 --- /dev/null +++ b/libweston/clipboard.c @@ -0,0 +1,306 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#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; +} diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c new file mode 100644 index 00000000..f903a3b4 --- /dev/null +++ b/libweston/compositor-drm.c @@ -0,0 +1,3285 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#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; +} diff --git a/libweston/compositor-drm.h b/libweston/compositor-drm.h new file mode 100644 index 00000000..1266031f --- /dev/null +++ b/libweston/compositor-drm.h @@ -0,0 +1,138 @@ +/* + * 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 */ diff --git a/libweston/compositor-fbdev.c b/libweston/compositor-fbdev.c new file mode 100644 index 00000000..e21ceca5 --- /dev/null +++ b/libweston/compositor-fbdev.c @@ -0,0 +1,783 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; +} diff --git a/libweston/compositor-fbdev.h b/libweston/compositor-fbdev.h new file mode 100644 index 00000000..9b5bf8e6 --- /dev/null +++ b/libweston/compositor-fbdev.h @@ -0,0 +1,61 @@ +/* + * 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 */ diff --git a/libweston/compositor-headless.c b/libweston/compositor-headless.c new file mode 100644 index 00000000..b78c3210 --- /dev/null +++ b/libweston/compositor-headless.c @@ -0,0 +1,258 @@ +/* + * 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 +#include +#include +#include + +#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; +} diff --git a/libweston/compositor-headless.h b/libweston/compositor-headless.h new file mode 100644 index 00000000..79f39c89 --- /dev/null +++ b/libweston/compositor-headless.h @@ -0,0 +1,53 @@ +/* + * 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 */ diff --git a/libweston/compositor-rdp.c b/libweston/compositor-rdp.c new file mode 100644 index 00000000..d74dd5e5 --- /dev/null +++ b/libweston/compositor-rdp.c @@ -0,0 +1,1331 @@ +/* + * Copyright © 2013 Hardening + * + * 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 +#include +#include +#include + +#if HAVE_FREERDP_VERSION_H +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/libweston/compositor-rdp.h b/libweston/compositor-rdp.h new file mode 100644 index 00000000..dfa1759a --- /dev/null +++ b/libweston/compositor-rdp.h @@ -0,0 +1,54 @@ +/* + * 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 */ diff --git a/libweston/compositor-wayland.c b/libweston/compositor-wayland.c new file mode 100644 index 00000000..1343e21f --- /dev/null +++ b/libweston/compositor-wayland.c @@ -0,0 +1,2348 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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; +} diff --git a/libweston/compositor-wayland.h b/libweston/compositor-wayland.h new file mode 100644 index 00000000..de69b988 --- /dev/null +++ b/libweston/compositor-wayland.h @@ -0,0 +1,61 @@ +/* + * 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 */ diff --git a/libweston/compositor-x11.c b/libweston/compositor-x11.c new file mode 100644 index 00000000..5e46e68d --- /dev/null +++ b/libweston/compositor-x11.c @@ -0,0 +1,1720 @@ +/* + * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2010-2011 Intel Corporation + * Copyright © 2013 Vasily Khoruzhick + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef HAVE_XCB_XKB +#include +#endif + +#include +#include + +#include + +#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; +} diff --git a/libweston/compositor-x11.h b/libweston/compositor-x11.h new file mode 100644 index 00000000..8363a760 --- /dev/null +++ b/libweston/compositor-x11.h @@ -0,0 +1,62 @@ +/* + * 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_ */ diff --git a/libweston/compositor.c b/libweston/compositor.c new file mode 100644 index 00000000..37d94ec9 --- /dev/null +++ b/libweston/compositor.c @@ -0,0 +1,5015 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/libweston/compositor.h b/libweston/compositor.h new file mode 100644 index 00000000..9e5155c6 --- /dev/null +++ b/libweston/compositor.h @@ -0,0 +1,1724 @@ +/* + * 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 +#include +#include +#include + +#define WL_HIDE_DEPRECATED +#include + +#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 diff --git a/libweston/data-device.c b/libweston/data-device.c new file mode 100644 index 00000000..f04f0308 --- /dev/null +++ b/libweston/data-device.c @@ -0,0 +1,1340 @@ +/* + * 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 +#include +#include +#include +#include + +#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; +} diff --git a/libweston/dbus.c b/libweston/dbus.c new file mode 100644 index 00000000..cadedd9d --- /dev/null +++ b/libweston/dbus.c @@ -0,0 +1,407 @@ +/* + * 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. + */ + +/* + * DBus Helpers + * This file contains the dbus mainloop integration and several helpers to + * make lowlevel libdbus access easier. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/libweston/dbus.h b/libweston/dbus.h new file mode 100644 index 00000000..9bbfa380 --- /dev/null +++ b/libweston/dbus.h @@ -0,0 +1,110 @@ +/* + * 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. + */ + +#ifndef _WESTON_DBUS_H_ +#define _WESTON_DBUS_H_ + +#include "config.h" + +#include +#include + +#include "compositor.h" + +#ifdef HAVE_DBUS + +#include + +/* + * 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_ diff --git a/libweston/gl-renderer.c b/libweston/gl-renderer.c new file mode 100644 index 00000000..23c0cd72 --- /dev/null +++ b/libweston/gl-renderer.c @@ -0,0 +1,3157 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 +}; diff --git a/libweston/gl-renderer.h b/libweston/gl-renderer.h new file mode 100644 index 00000000..71f6b46e --- /dev/null +++ b/libweston/gl-renderer.h @@ -0,0 +1,132 @@ +/* + * 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 +#include + +#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); +}; + diff --git a/libweston/input.c b/libweston/input.c new file mode 100644 index 00000000..08378d1e --- /dev/null +++ b/libweston/input.c @@ -0,0 +1,2765 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/libweston/launcher-direct.c b/libweston/launcher-direct.c new file mode 100644 index 00000000..29d9c28b --- /dev/null +++ b/libweston/launcher-direct.c @@ -0,0 +1,315 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "launcher-impl.h" + +#define DRM_MAJOR 226 + +#ifndef KDSKBMUTE +#define KDSKBMUTE 0x4B51 +#endif + +#ifdef HAVE_LIBDRM + +#include + +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] =""; + 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, +}; diff --git a/libweston/launcher-impl.h b/libweston/launcher-impl.h new file mode 100644 index 00000000..742721bf --- /dev/null +++ b/libweston/launcher-impl.h @@ -0,0 +1,45 @@ +/* + * 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; diff --git a/libweston/launcher-logind.c b/libweston/launcher-logind.c new file mode 100644 index 00000000..f755ec33 --- /dev/null +++ b/libweston/launcher-logind.c @@ -0,0 +1,839 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, +}; diff --git a/libweston/launcher-util.c b/libweston/launcher-util.c new file mode 100644 index 00000000..03b9d632 --- /dev/null +++ b/libweston/launcher-util.c @@ -0,0 +1,117 @@ +/* + * 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 +#include + +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); +} diff --git a/libweston/launcher-util.h b/libweston/launcher-util.h new file mode 100644 index 00000000..93321ab7 --- /dev/null +++ b/libweston/launcher-util.h @@ -0,0 +1,58 @@ +/* + * 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 diff --git a/libweston/launcher-weston-launch.c b/libweston/launcher-weston-launch.c new file mode 100644 index 00000000..ad919f18 --- /dev/null +++ b/libweston/launcher-weston-launch.c @@ -0,0 +1,300 @@ +/* + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +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, +}; diff --git a/libweston/libbacklight.c b/libweston/libbacklight.c new file mode 100644 index 00000000..722d66f0 --- /dev/null +++ b/libweston/libbacklight.c @@ -0,0 +1,310 @@ +/* + * libbacklight - userspace interface to Linux backlight control + * + * Copyright © 2012 Intel Corporation + * Copyright 2010 Red Hat + * + * 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 + * Tiago Vignatti + */ + +#include "config.h" + +#include "libbacklight.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/libweston/libbacklight.h b/libweston/libbacklight.h new file mode 100644 index 00000000..8717ab10 --- /dev/null +++ b/libweston/libbacklight.h @@ -0,0 +1,79 @@ +/* + * libbacklight - userspace interface to Linux backlight control + * + * Copyright © 2012 Intel Corporation + * Copyright 2010 Red Hat + * + * 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 + * Tiago Vignatti + */ +#ifndef LIBBACKLIGHT_H +#define LIBBACKLIGHT_H +#include +#include + +#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 */ diff --git a/libweston/libinput-device.c b/libweston/libinput-device.c new file mode 100644 index 00000000..62350f2e --- /dev/null +++ b/libweston/libinput-device.c @@ -0,0 +1,593 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/libweston/libinput-device.h b/libweston/libinput-device.h new file mode 100644 index 00000000..5041a4aa --- /dev/null +++ b/libweston/libinput-device.h @@ -0,0 +1,80 @@ +/* + * 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 +#include +#include + +#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_ */ diff --git a/libweston/libinput-seat.c b/libweston/libinput-seat.c new file mode 100644 index 00000000..94e19f5e --- /dev/null +++ b/libweston/libinput-seat.c @@ -0,0 +1,416 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#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); +} diff --git a/libweston/libinput-seat.h b/libweston/libinput-seat.h new file mode 100644 index 00000000..65c9b64b --- /dev/null +++ b/libweston/libinput-seat.h @@ -0,0 +1,72 @@ +/* + * 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 + +#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 diff --git a/libweston/libweston.pc.in b/libweston/libweston.pc.in new file mode 100644 index 00000000..24fe8133 --- /dev/null +++ b/libweston/libweston.pc.in @@ -0,0 +1,12 @@ +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@ diff --git a/libweston/linux-dmabuf.c b/libweston/linux-dmabuf.c new file mode 100644 index 00000000..78e77a24 --- /dev/null +++ b/libweston/linux-dmabuf.c @@ -0,0 +1,497 @@ +/* + * 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 +#include +#include + +#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); +} diff --git a/libweston/linux-dmabuf.h b/libweston/linux-dmabuf.h new file mode 100644 index 00000000..cd30f91c --- /dev/null +++ b/libweston/linux-dmabuf.h @@ -0,0 +1,88 @@ +/* + * 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 + +#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 */ diff --git a/libweston/log.c b/libweston/log.c new file mode 100644 index 00000000..7d99a95d --- /dev/null +++ b/libweston/log.c @@ -0,0 +1,98 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include + +#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; +} diff --git a/libweston/noop-renderer.c b/libweston/noop-renderer.c new file mode 100644 index 00000000..b6499b82 --- /dev/null +++ b/libweston/noop-renderer.c @@ -0,0 +1,121 @@ +/* + * 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 + +#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; +} diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c new file mode 100644 index 00000000..f66a11ed --- /dev/null +++ b/libweston/pixman-renderer.c @@ -0,0 +1,931 @@ +/* + * Copyright © 2012 Intel Corporation + * Copyright © 2013 Vasily Khoruzhick + * 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 +#include +#include + +#include "pixman-renderer.h" +#include "shared/helpers.h" + +#include + +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); +} diff --git a/libweston/pixman-renderer.h b/libweston/pixman-renderer.h new file mode 100644 index 00000000..1b42f14f --- /dev/null +++ b/libweston/pixman-renderer.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2013 Vasily Khoruzhick + * + * 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); diff --git a/libweston/screenshooter.c b/libweston/screenshooter.c new file mode 100644 index 00000000..fc14ad30 --- /dev/null +++ b/libweston/screenshooter.c @@ -0,0 +1,487 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/libweston/spring-tool.c b/libweston/spring-tool.c new file mode 100644 index 00000000..1848b3f7 --- /dev/null +++ b/libweston/spring-tool.c @@ -0,0 +1,74 @@ +/* + * 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; +} diff --git a/libweston/timeline-object.h b/libweston/timeline-object.h new file mode 100644 index 00000000..943f979c --- /dev/null +++ b/libweston/timeline-object.h @@ -0,0 +1,55 @@ +/* + * Copyright © 2014 Pekka Paalanen + * 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 */ diff --git a/libweston/timeline.c b/libweston/timeline.c new file mode 100644 index 00000000..cf82428e --- /dev/null +++ b/libweston/timeline.c @@ -0,0 +1,292 @@ +/* + * Copyright © 2014 Pekka Paalanen + * 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 +#include +#include +#include +#include + +#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); +} diff --git a/libweston/timeline.h b/libweston/timeline.h new file mode 100644 index 00000000..b10a8157 --- /dev/null +++ b/libweston/timeline.h @@ -0,0 +1,65 @@ +/* + * Copyright © 2014 Pekka Paalanen + * 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 */ diff --git a/libweston/vaapi-recorder.c b/libweston/vaapi-recorder.c new file mode 100644 index 00000000..1228f7d1 --- /dev/null +++ b/libweston/vaapi-recorder.c @@ -0,0 +1,1161 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#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; +} diff --git a/libweston/vaapi-recorder.h b/libweston/vaapi-recorder.h new file mode 100644 index 00000000..6b194aa8 --- /dev/null +++ b/libweston/vaapi-recorder.h @@ -0,0 +1,38 @@ +/* + * 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_ */ diff --git a/libweston/version.h.in b/libweston/version.h.in new file mode 100644 index 00000000..b2379d09 --- /dev/null +++ b/libweston/version.h.in @@ -0,0 +1,50 @@ +/* + * 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 diff --git a/libweston/vertex-clipping.c b/libweston/vertex-clipping.c new file mode 100644 index 00000000..a71e7336 --- /dev/null +++ b/libweston/vertex-clipping.c @@ -0,0 +1,330 @@ +/* + * 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 +#include +#include + +#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; +} diff --git a/libweston/vertex-clipping.h b/libweston/vertex-clipping.h new file mode 100644 index 00000000..0c699021 --- /dev/null +++ b/libweston/vertex-clipping.h @@ -0,0 +1,66 @@ +/* + * 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 diff --git a/libweston/weston-egl-ext.h b/libweston/weston-egl-ext.h new file mode 100644 index 00000000..32f6108f --- /dev/null +++ b/libweston/weston-egl-ext.h @@ -0,0 +1,120 @@ +/* + * 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 diff --git a/libweston/weston-launch.c b/libweston/weston-launch.c new file mode 100644 index 00000000..9987d8ee --- /dev/null +++ b/libweston/weston-launch.c @@ -0,0 +1,772 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef HAVE_SYSTEMD_LOGIN +#include +#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 + +#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; +} diff --git a/libweston/weston-launch.h b/libweston/weston-launch.h new file mode 100644 index 00000000..bd974de8 --- /dev/null +++ b/libweston/weston-launch.h @@ -0,0 +1,49 @@ +/* + * 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 diff --git a/libweston/zoom.c b/libweston/zoom.c new file mode 100644 index 00000000..08c06931 --- /dev/null +++ b/libweston/zoom.c @@ -0,0 +1,177 @@ +/* + * 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 +#include +#include + +#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; +} diff --git a/src/animation.c b/src/animation.c deleted file mode 100644 index 2c7943f6..00000000 --- a/src/animation.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include -#include - -#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; -} diff --git a/src/bindings.c b/src/bindings.c deleted file mode 100644 index cc68cfe1..00000000 --- a/src/bindings.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - * 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 -#include - -#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); -} diff --git a/src/clipboard.c b/src/clipboard.c deleted file mode 100644 index 54a578f0..00000000 --- a/src/clipboard.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#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; -} diff --git a/src/compositor-drm.c b/src/compositor-drm.c deleted file mode 100644 index f903a3b4..00000000 --- a/src/compositor-drm.c +++ /dev/null @@ -1,3285 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#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; -} diff --git a/src/compositor-drm.h b/src/compositor-drm.h deleted file mode 100644 index 1266031f..00000000 --- a/src/compositor-drm.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * 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 */ diff --git a/src/compositor-fbdev.c b/src/compositor-fbdev.c deleted file mode 100644 index e21ceca5..00000000 --- a/src/compositor-fbdev.c +++ /dev/null @@ -1,783 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#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; -} diff --git a/src/compositor-fbdev.h b/src/compositor-fbdev.h deleted file mode 100644 index 9b5bf8e6..00000000 --- a/src/compositor-fbdev.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 */ diff --git a/src/compositor-headless.c b/src/compositor-headless.c deleted file mode 100644 index b78c3210..00000000 --- a/src/compositor-headless.c +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 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 -#include -#include -#include - -#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; -} diff --git a/src/compositor-headless.h b/src/compositor-headless.h deleted file mode 100644 index 79f39c89..00000000 --- a/src/compositor-headless.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 */ diff --git a/src/compositor-rdp.c b/src/compositor-rdp.c deleted file mode 100644 index d74dd5e5..00000000 --- a/src/compositor-rdp.c +++ /dev/null @@ -1,1331 +0,0 @@ -/* - * Copyright © 2013 Hardening - * - * 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 -#include -#include -#include - -#if HAVE_FREERDP_VERSION_H -#include -#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 -#include -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/src/compositor-rdp.h b/src/compositor-rdp.h deleted file mode 100644 index dfa1759a..00000000 --- a/src/compositor-rdp.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 */ diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c deleted file mode 100644 index 1343e21f..00000000 --- a/src/compositor-wayland.c +++ /dev/null @@ -1,2348 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#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; -} diff --git a/src/compositor-wayland.h b/src/compositor-wayland.h deleted file mode 100644 index de69b988..00000000 --- a/src/compositor-wayland.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 */ diff --git a/src/compositor-x11.c b/src/compositor-x11.c deleted file mode 100644 index 5e46e68d..00000000 --- a/src/compositor-x11.c +++ /dev/null @@ -1,1720 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2010-2011 Intel Corporation - * Copyright © 2013 Vasily Khoruzhick - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#ifdef HAVE_XCB_XKB -#include -#endif - -#include -#include - -#include - -#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; -} diff --git a/src/compositor-x11.h b/src/compositor-x11.h deleted file mode 100644 index 8363a760..00000000 --- a/src/compositor-x11.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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_ */ diff --git a/src/compositor.c b/src/compositor.c deleted file mode 100644 index 37d94ec9..00000000 --- a/src/compositor.c +++ /dev/null @@ -1,5015 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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); -} diff --git a/src/compositor.h b/src/compositor.h deleted file mode 100644 index 9e5155c6..00000000 --- a/src/compositor.h +++ /dev/null @@ -1,1724 +0,0 @@ -/* - * 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 -#include -#include -#include - -#define WL_HIDE_DEPRECATED -#include - -#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 diff --git a/src/data-device.c b/src/data-device.c deleted file mode 100644 index f04f0308..00000000 --- a/src/data-device.c +++ /dev/null @@ -1,1340 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -#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; -} diff --git a/src/dbus.c b/src/dbus.c deleted file mode 100644 index cadedd9d..00000000 --- a/src/dbus.c +++ /dev/null @@ -1,407 +0,0 @@ -/* - * 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. - */ - -/* - * DBus Helpers - * This file contains the dbus mainloop integration and several helpers to - * make lowlevel libdbus access easier. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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); -} diff --git a/src/dbus.h b/src/dbus.h deleted file mode 100644 index 9bbfa380..00000000 --- a/src/dbus.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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. - */ - -#ifndef _WESTON_DBUS_H_ -#define _WESTON_DBUS_H_ - -#include "config.h" - -#include -#include - -#include "compositor.h" - -#ifdef HAVE_DBUS - -#include - -/* - * 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_ diff --git a/src/gl-renderer.c b/src/gl-renderer.c deleted file mode 100644 index 23c0cd72..00000000 --- a/src/gl-renderer.c +++ /dev/null @@ -1,3157 +0,0 @@ -/* - * 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 -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#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 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 -}; diff --git a/src/gl-renderer.h b/src/gl-renderer.h deleted file mode 100644 index 71f6b46e..00000000 --- a/src/gl-renderer.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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 -#include - -#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); -}; - diff --git a/src/input.c b/src/input.c deleted file mode 100644 index 08378d1e..00000000 --- a/src/input.c +++ /dev/null @@ -1,2765 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/src/launcher-direct.c b/src/launcher-direct.c deleted file mode 100644 index 29d9c28b..00000000 --- a/src/launcher-direct.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include "launcher-impl.h" - -#define DRM_MAJOR 226 - -#ifndef KDSKBMUTE -#define KDSKBMUTE 0x4B51 -#endif - -#ifdef HAVE_LIBDRM - -#include - -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] =""; - 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, -}; diff --git a/src/launcher-impl.h b/src/launcher-impl.h deleted file mode 100644 index 742721bf..00000000 --- a/src/launcher-impl.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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; diff --git a/src/launcher-logind.c b/src/launcher-logind.c deleted file mode 100644 index f755ec33..00000000 --- a/src/launcher-logind.c +++ /dev/null @@ -1,839 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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, -}; diff --git a/src/launcher-util.c b/src/launcher-util.c deleted file mode 100644 index 03b9d632..00000000 --- a/src/launcher-util.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 -#include - -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); -} diff --git a/src/launcher-util.h b/src/launcher-util.h deleted file mode 100644 index 93321ab7..00000000 --- a/src/launcher-util.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 diff --git a/src/launcher-weston-launch.c b/src/launcher-weston-launch.c deleted file mode 100644 index ad919f18..00000000 --- a/src/launcher-weston-launch.c +++ /dev/null @@ -1,300 +0,0 @@ -/* - * 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 -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 - -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, -}; diff --git a/src/libbacklight.c b/src/libbacklight.c deleted file mode 100644 index 722d66f0..00000000 --- a/src/libbacklight.c +++ /dev/null @@ -1,310 +0,0 @@ -/* - * libbacklight - userspace interface to Linux backlight control - * - * Copyright © 2012 Intel Corporation - * Copyright 2010 Red Hat - * - * 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 - * Tiago Vignatti - */ - -#include "config.h" - -#include "libbacklight.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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; -} diff --git a/src/libbacklight.h b/src/libbacklight.h deleted file mode 100644 index 8717ab10..00000000 --- a/src/libbacklight.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * libbacklight - userspace interface to Linux backlight control - * - * Copyright © 2012 Intel Corporation - * Copyright 2010 Red Hat - * - * 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 - * Tiago Vignatti - */ -#ifndef LIBBACKLIGHT_H -#define LIBBACKLIGHT_H -#include -#include - -#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 */ diff --git a/src/libinput-device.c b/src/libinput-device.c deleted file mode 100644 index 62350f2e..00000000 --- a/src/libinput-device.c +++ /dev/null @@ -1,593 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#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); -} diff --git a/src/libinput-device.h b/src/libinput-device.h deleted file mode 100644 index 5041a4aa..00000000 --- a/src/libinput-device.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 -#include -#include - -#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_ */ diff --git a/src/libinput-seat.c b/src/libinput-seat.c deleted file mode 100644 index 94e19f5e..00000000 --- a/src/libinput-seat.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#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); -} diff --git a/src/libinput-seat.h b/src/libinput-seat.h deleted file mode 100644 index 65c9b64b..00000000 --- a/src/libinput-seat.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 - -#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 diff --git a/src/libweston.pc.in b/src/libweston.pc.in deleted file mode 100644 index 24fe8133..00000000 --- a/src/libweston.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -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@ diff --git a/src/linux-dmabuf.c b/src/linux-dmabuf.c deleted file mode 100644 index 78e77a24..00000000 --- a/src/linux-dmabuf.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * 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 -#include -#include - -#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); -} diff --git a/src/linux-dmabuf.h b/src/linux-dmabuf.h deleted file mode 100644 index cd30f91c..00000000 --- a/src/linux-dmabuf.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 - -#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 */ diff --git a/src/log.c b/src/log.c deleted file mode 100644 index 7d99a95d..00000000 --- a/src/log.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#include - -#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; -} diff --git a/src/noop-renderer.c b/src/noop-renderer.c deleted file mode 100644 index b6499b82..00000000 --- a/src/noop-renderer.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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 - -#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; -} diff --git a/src/pixman-renderer.c b/src/pixman-renderer.c deleted file mode 100644 index f66a11ed..00000000 --- a/src/pixman-renderer.c +++ /dev/null @@ -1,931 +0,0 @@ -/* - * Copyright © 2012 Intel Corporation - * Copyright © 2013 Vasily Khoruzhick - * 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 -#include -#include - -#include "pixman-renderer.h" -#include "shared/helpers.h" - -#include - -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); -} diff --git a/src/pixman-renderer.h b/src/pixman-renderer.h deleted file mode 100644 index 1b42f14f..00000000 --- a/src/pixman-renderer.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2013 Vasily Khoruzhick - * - * 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); diff --git a/src/screenshooter.c b/src/screenshooter.c deleted file mode 100644 index fc14ad30..00000000 --- a/src/screenshooter.c +++ /dev/null @@ -1,487 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include - -#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); -} diff --git a/src/spring-tool.c b/src/spring-tool.c deleted file mode 100644 index 1848b3f7..00000000 --- a/src/spring-tool.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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; -} diff --git a/src/timeline-object.h b/src/timeline-object.h deleted file mode 100644 index 943f979c..00000000 --- a/src/timeline-object.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright © 2014 Pekka Paalanen - * 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 */ diff --git a/src/timeline.c b/src/timeline.c deleted file mode 100644 index cf82428e..00000000 --- a/src/timeline.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright © 2014 Pekka Paalanen - * 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 -#include -#include -#include -#include - -#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); -} diff --git a/src/timeline.h b/src/timeline.h deleted file mode 100644 index b10a8157..00000000 --- a/src/timeline.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright © 2014 Pekka Paalanen - * 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 */ diff --git a/src/vaapi-recorder.c b/src/vaapi-recorder.c deleted file mode 100644 index 1228f7d1..00000000 --- a/src/vaapi-recorder.c +++ /dev/null @@ -1,1161 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#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; -} diff --git a/src/vaapi-recorder.h b/src/vaapi-recorder.h deleted file mode 100644 index 6b194aa8..00000000 --- a/src/vaapi-recorder.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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_ */ diff --git a/src/version.h.in b/src/version.h.in deleted file mode 100644 index b2379d09..00000000 --- a/src/version.h.in +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 diff --git a/src/vertex-clipping.c b/src/vertex-clipping.c deleted file mode 100644 index a71e7336..00000000 --- a/src/vertex-clipping.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * 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 -#include -#include - -#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; -} diff --git a/src/vertex-clipping.h b/src/vertex-clipping.h deleted file mode 100644 index 0c699021..00000000 --- a/src/vertex-clipping.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 diff --git a/src/weston-egl-ext.h b/src/weston-egl-ext.h deleted file mode 100644 index 32f6108f..00000000 --- a/src/weston-egl-ext.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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 diff --git a/src/weston-launch.c b/src/weston-launch.c deleted file mode 100644 index 9987d8ee..00000000 --- a/src/weston-launch.c +++ /dev/null @@ -1,772 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#ifdef HAVE_SYSTEMD_LOGIN -#include -#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 - -#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; -} diff --git a/src/weston-launch.h b/src/weston-launch.h deleted file mode 100644 index bd974de8..00000000 --- a/src/weston-launch.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 diff --git a/src/zoom.c b/src/zoom.c deleted file mode 100644 index 08c06931..00000000 --- a/src/zoom.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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 -#include -#include - -#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; -} diff --git a/tests/ivi_layout-internal-test.c b/tests/ivi_layout-internal-test.c index f5e296cb..bc36ad13 100644 --- a/tests/ivi_layout-internal-test.c +++ b/tests/ivi_layout-internal-test.c @@ -31,7 +31,7 @@ #include #include -#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" diff --git a/tests/ivi_layout-test-plugin.c b/tests/ivi_layout-test-plugin.c index 4cae3c59..1a28c6ee 100644 --- a/tests/ivi_layout-test-plugin.c +++ b/tests/ivi_layout-test-plugin.c @@ -32,7 +32,7 @@ #include #include -#include "src/compositor.h" +#include "compositor.h" #include "compositor/weston.h" #include "weston-test-server-protocol.h" #include "ivi-test.h" diff --git a/tests/surface-global-test.c b/tests/surface-global-test.c index 11b2455d..b0a1d1cb 100644 --- a/tests/surface-global-test.c +++ b/tests/surface-global-test.c @@ -27,7 +27,7 @@ #include -#include "src/compositor.h" +#include "compositor.h" static void surface_to_from_global(void *data) diff --git a/tests/surface-test.c b/tests/surface-test.c index 55c324be..243f8dc3 100644 --- a/tests/surface-test.c +++ b/tests/surface-test.c @@ -28,7 +28,7 @@ #include #include -#include "src/compositor.h" +#include "compositor.h" static void surface_transform(void *data) diff --git a/tests/vertex-clip-test.c b/tests/vertex-clip-test.c index 8a4e5a6b..ce876cec 100644 --- a/tests/vertex-clip-test.c +++ b/tests/vertex-clip-test.c @@ -31,7 +31,7 @@ #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 diff --git a/tests/weston-test.c b/tests/weston-test.c index 26b2ae09..1793744c 100644 --- a/tests/weston-test.c +++ b/tests/weston-test.c @@ -31,14 +31,14 @@ #include #include -#include "src/compositor.h" +#include "compositor.h" #include "compositor/weston.h" #include "weston-test-server-protocol.h" #ifdef ENABLE_EGL #include #include -#include "src/weston-egl-ext.h" +#include "weston-egl-ext.h" #endif /* ENABLE_EGL */ #include "shared/helpers.h" -- cgit v1.2.3