Compare commits

..

No commits in common. "main" and "2.0.0" have entirely different histories.
main ... 2.0.0

735 changed files with 53893 additions and 173833 deletions

View file

@ -1,15 +0,0 @@
root = true
[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = tab
indent_size = 8
max_line_length = 80
[*.xml]
indent_style = space
indent_size = 2
tab_width = 8

101
.gitignore vendored
View file

@ -1,13 +1,112 @@
*.announce
*.deps
*.jpg
*.la
*.lo
*.log
*.o
*.pc
*.sig
*.so
*.swp
.*.sw?
.sw?
*.sublime-project
*.sublime-workspace
*.tar.xz
*.trs
*~
ctags
cscope.out
.libs
.dirstamp
/aclocal.m4
/autom4te.cache
/build-aux/
/config.guess
/config.h
/config.h.in
/config.log
/config.mk
/config.status
/config.sub
/configure
/depcomp
/doc/doxygen/*.doxygen
/docs/developer
/docs/tools
/install-sh
/libtool
/ltmain.sh
/logs
/missing
/stamp-h1
/test-driver
/weston.ini
Makefile
Makefile.in
TAGS
protocol/.*.valid
protocol/*.[ch]
00*.patch
build/
weston-calibrator
weston-clickdot
weston-cliptest
weston-confine
weston-dnd
weston-editor
weston-eventdemo
weston-flower
weston-fullscreen
weston-gears
weston-image
weston-nested
weston-nested-client
weston-presentation-shm
weston-resizor
weston-scaler
weston-simple-dmabuf-intel
weston-simple-dmabuf-v4l
weston-simple-egl
weston-simple-shm
weston-simple-touch
weston-simple-damage
weston-smoke
weston-stacking
weston-subsurfaces
weston-transformed
weston-view
weston-keyboard
libtoytoolkit.a
weston-desktop-shell
weston-ivi-shell-user-interface
weston-info
weston-screenshooter
weston-tablet-shell
weston-terminal
weston-multi-resource
weston-simple-im
weston
weston-launch
spring-tool
*.weston
*.test
*.ivi
wcap-decode
matrix-test
setbacklight
weston.1
weston-drm.7
weston.ini.5
/libweston/git-version.h
/libweston/version.h
/tests/weston-ivi.ini
internal-screenshot-00.png
/zuctest

View file

@ -1,656 +0,0 @@
# vim: set expandtab shiftwidth=2 tabstop=8 textwidth=0:
.templates_sha: &template_sha 32afe5644697e503af18a736587c8619fa036a72 # see https://docs.gitlab.com/ee/ci/yaml/#includefile
# This file uses the freedesktop ci-templates to build Weston and run our
# tests in CI.
#
# ci-templates uses a multi-stage build process. First, the base container
# image is built which contains the core distribution, the toolchain, and
# all our build dependencies. This container is aggressively cached; if a
# container image matching $FDO_DISTRIBUTION_TAG is found in either the
# upstream repo (wayland/weston) or the user's downstream repo, it is
# reused for the build. This gives us predictability of build and far
# quicker runtimes, however it means that any changes to the base container
# must also change $FDO_DISTRIBUTION_TAG. When changing this, please use
# the current date as well as a unique build identifier.
#
# After the container is either rebuilt (tag mismatch) or reused (tag
# previously used), the build stage executes within this container.
#
# The final stage is used to expose documentation and coverage information,
# including publishing documentation to the public site when built on the
# main branch.
#
# Apart from the 'variables', 'include', and 'stages' top-level anchors,
# everything not beginning with a dot ('.') is the name of a job which will
# be executed as part of CI, unless the rules specify that it should not be
# run.
#
# Variables prefixed with CI_ are generally provided by GitLab itself;
# variables prefixed with FDO_ and templates prefixed by .fdo are provided
# by the ci-templates.
#
# For more information on GitLab CI, including the YAML syntax, see:
# https://docs.gitlab.com/ee/ci/yaml/README.html
#
# Note that freedesktop.org uses the 'Community Edition' of GitLab, so features
# marked as 'premium' or 'ultimate' are not available to us.
#
# For more information on ci-templates, see:
# - documentation at https://freedesktop.pages.freedesktop.org/ci-templates/
# - repo at https://gitlab.freedesktop.org/freedesktop/ci-templates/
variables:
FDO_UPSTREAM_REPO: wayland/weston
FDO_REPO_SUFFIX: "$BUILD_OS-$FDO_DISTRIBUTION_VERSION/$BUILD_ARCH"
FDO_DISTRIBUTION_TAG: '2026-04-01-mesa-26.0.4'
include:
# Here we use a fixed ref in order to isolate ourselves from ci-templates
# API changes. If you need new features from ci-templates you must bump
# this to the current SHA you require from the ci-templates repo, however
# be aware that you may need to account for API changes when doing so.
- project: 'freedesktop/ci-templates'
ref: *template_sha
file: '/templates/debian.yml'
- project: 'freedesktop/ci-templates'
ref: *template_sha
file: '/templates/ci-fairy.yml'
.default-rules:
rules:
# do not duplicate pipelines on merge pipelines
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
when: never
# we need a default case though, so all the rest still run
- when: on_success
default:
retry:
max: 2
when: runner_system_failure
.merge-rules:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: always
- when: never
# Define the build stages. These are used for UI grouping as well as
# dependencies.
stages:
- "Merge request checks"
- "Pre Base container"
- "Base container"
- "Full build and test"
- "No-GL/Vulkan build and test"
- "Other builds"
- pages
# Base variables used for anything using a Debian environment
.os-debian-lts:
variables:
BUILD_OS: debian
LLVM_VERSION: 15
USE_DEBIAN_BACKPORTS: y
PACKAGES_SPECIFIC: vulkan-validationlayers-dev
FREERDP_VERSION: 2
FDO_DISTRIBUTION_VERSION: bookworm
FDO_DISTRIBUTION_EXEC: 'env FDO_CI_CONCURRENT=${FDO_CI_CONCURRENT} BUILD_ARCH=${BUILD_ARCH} KERNEL_IMAGE=${KERNEL_IMAGE} KERNEL_DEFCONFIG=${KERNEL_DEFCONFIG} LLVM_VERSION=${LLVM_VERSION} FDO_DISTRIBUTION_VERSION=${FDO_DISTRIBUTION_VERSION} PACKAGES_SPECIFIC="${PACKAGES_SPECIFIC}" bash .gitlab-ci/debian-install.sh'
.os-debian:
variables:
BUILD_OS: debian
LLVM_VERSION: 19
FREERDP_VERSION: 3
USE_DEBIAN_BACKPORTS: y
PACKAGES_SPECIFIC: vulkan-utility-libraries-dev libpolly-19-dev libclang-rt-19-dev python3-standard-imghdr
# If you upgrade from trixie, see the use_tls=0 notes in tests/meson.build.
FDO_DISTRIBUTION_VERSION: trixie
FDO_DISTRIBUTION_EXEC: 'env FDO_CI_CONCURRENT=${FDO_CI_CONCURRENT} BUILD_ARCH=${BUILD_ARCH} KERNEL_IMAGE=${KERNEL_IMAGE} KERNEL_DEFCONFIG=${KERNEL_DEFCONFIG} LLVM_VERSION=${LLVM_VERSION} FDO_DISTRIBUTION_VERSION=${FDO_DISTRIBUTION_VERSION} PACKAGES_SPECIFIC="${PACKAGES_SPECIFIC}" bash .gitlab-ci/debian-install.sh'
# Does not inherit .default-rules as we only want it to run in MR context.
check-commit:
extends:
- .fdo.ci-fairy
- .merge-rules
stage: "Merge request checks"
script:
- ci-fairy check-commits --signed-off-by --junit-xml=results.xml
variables:
GIT_DEPTH: 100
artifacts:
reports:
junit: results.xml
.debian-lts-x86_64:
extends:
- .os-debian-lts
variables:
BUILD_ARCH: "x86-64"
KERNEL_IMAGE: "bzImage"
KERNEL_DEFCONFIG: "x86_64_defconfig"
.debian-x86_64:
extends:
- .os-debian
variables:
BUILD_ARCH: "x86-64"
KERNEL_IMAGE: "bzImage"
KERNEL_DEFCONFIG: "x86_64_defconfig"
.debian-lts-armv7:
extends:
- .os-debian-lts
variables:
BUILD_ARCH: "armv7"
FDO_DISTRIBUTION_PLATFORM: "linux/arm/v7"
.debian-armv7:
extends:
- .os-debian
variables:
BUILD_ARCH: "armv7"
FDO_DISTRIBUTION_PLATFORM: "linux/arm/v7"
# Armv7 doesn't have freerdp3 in bookworm-backports so we don't build FreeRDP
MESON_DIST_OPTIONS: "-Dbackend-rdp=false"
# Inhibit installation of freerdp-dev
FREERDP_VERSION: 0
.debian-lts-aarch64:
extends:
- .os-debian-lts
variables:
BUILD_ARCH: "aarch64"
KERNEL_IMAGE: "Image"
KERNEL_DEFCONFIG: "defconfig"
QEMU_SMP: 8 # built-in QEmu limit
.debian-aarch64:
extends:
- .os-debian
variables:
BUILD_ARCH: "aarch64"
KERNEL_IMAGE: "Image"
KERNEL_DEFCONFIG: "defconfig"
QEMU_SMP: 8 # built-in QEmu limit
base-container-pre:
rules:
# this run always in merge request
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: always
# run always on main to allow docs to publish
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_PATH == "wayland/weston" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
when: always
# allow to run manually in a branch
- when: manual
stage: "Pre Base container"
script: echo "exit 0"
# Build our base container image, which contains the core distribution, the
# toolchain, and all our build dependencies. This will be reused in the build
# stage.
x86_64-debian-lts-container_prep:
extends:
- .default-rules
- .debian-lts-x86_64
- .fdo.container-build@debian
timeout: 30m
needs:
- job: base-container-pre
stage: "Base container"
x86_64-debian-container_prep:
extends:
- .default-rules
- .debian-x86_64
- .fdo.container-build@debian
needs:
- job: base-container-pre
timeout: 30m
stage: "Base container"
armv7-debian-lts-container_prep:
extends:
- .default-rules
- .debian-lts-armv7
- .fdo.container-build@debian
tags:
- aarch64
needs:
- job: base-container-pre
timeout: 30m
stage: "Base container"
armv7-debian-container_prep:
extends:
- .default-rules
- .debian-armv7
- .fdo.container-build@debian
tags:
- aarch64
needs:
- job: base-container-pre
timeout: 30m
stage: "Base container"
aarch64-debian-lts-container_prep:
extends:
- .default-rules
- .debian-lts-aarch64
- .fdo.container-build@debian
tags:
- aarch64
needs:
- job: base-container-pre
timeout: 30m
stage: "Base container"
aarch64-debian-container_prep:
extends:
- .default-rules
- .debian-aarch64
- .fdo.container-build@debian
tags:
- aarch64
needs:
- job: base-container-pre
timeout: 30m
stage: "Base container"
# Core templates for all of our build steps. These are reused by all build jobs
# through the `extends` keyword.
.build-env:
timeout: 15m
variables:
BUILDDIR: $CI_PROJECT_DIR/build-weston-$CI_JOB_NAME
BUILDDIR_WESTINY: $CI_PROJECT_DIR/build-westiny-$CI_JOB_NAME
PREFIX: $CI_PROJECT_DIR/prefix-weston-$CI_JOB_NAME
PREFIX_WESTINY: $CI_PROJECT_DIR/prefix-westiny-$CI_JOB_NAME
before_script:
- export PATH=~/.local/bin:$PATH
- export XDG_RUNTIME_DIR="$(mktemp -p $(pwd) -d xdg-runtime-XXXXXX)"
- export TESTS_RES_PATH="$BUILDDIR/tests-res.txt"
- export VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation
- mkdir "$BUILDDIR" "$PREFIX"
- mkdir "$BUILDDIR_WESTINY" "$PREFIX_WESTINY"
.build-with-clang:
variables:
CC: clang-$LLVM_VERSION
CC_LD: lld-$LLVM_VERSION
CXX: clang++-$LLVM_VERSION
CXX_LD: lld-$LLVM_VERSION
MESON_TOOLCHAIN_OPTIONS: "$MESON_OPTIONS -Db_lundef=false" # clang+ASan+undef=boom
# Extends the core build templates to also provide for running our testing. We
# run this inside a virtme (qemu wrapper) VM environment so we can test the DRM
# backend using the 'vkms' virtual driver under Linux.
.build-and-test:
extends:
- .default-rules
variables:
SANITIZE: "-Db_sanitize=address"
script:
- "${CI_PROJECT_DIR}/.gitlab-ci/build.sh"
- "${CI_PROJECT_DIR}/.gitlab-ci/test.sh"
artifacts:
name: weston-$CI_COMMIT_SHA
when: always
paths:
- $BUILDDIR/*.png
- $BUILDDIR/meson-logs
- $BUILDDIR/dmesg.log
- $BUILDDIR/weston-virtme
- $PREFIX
reports:
junit: $BUILDDIR/meson-logs/testlog.junit.xml
# Same as above, but without running any tests.
.build-no-test:
extends:
- .default-rules
script:
- "${CI_PROJECT_DIR}/.gitlab-ci/build.sh"
artifacts:
name: weston-$CI_COMMIT_SHA
when: always
paths:
- $BUILDDIR/meson-logs
- $PREFIX
# OS/architecture-specific variants
.build-env-debian-lts-x86_64:
extends:
- .debian-lts-x86_64
- .fdo.suffixed-image@debian
- .build-env
needs:
- job: x86_64-debian-lts-container_prep
artifacts: false
.build-env-debian-x86_64:
extends:
- .debian-x86_64
- .fdo.suffixed-image@debian
- .build-env
needs:
- job: x86_64-debian-container_prep
artifacts: false
.build-env-debian-lts-armv7:
tags:
- aarch64
extends:
- .debian-lts-armv7
- .fdo.suffixed-image@debian
- .build-env
needs:
- job: armv7-debian-lts-container_prep
artifacts: false
.build-env-debian-armv7:
tags:
- aarch64
extends:
- .debian-armv7
- .fdo.suffixed-image@debian
- .build-env
needs:
- job: armv7-debian-container_prep
artifacts: false
.build-env-debian-lts-aarch64:
tags:
- aarch64
extends:
- .debian-lts-aarch64
- .fdo.suffixed-image@debian
- .build-env
needs:
- job: aarch64-debian-lts-container_prep
artifacts: false
.build-env-debian-aarch64:
tags:
- aarch64
extends:
- .debian-aarch64
- .fdo.suffixed-image@debian
- .build-env
needs:
- job: aarch64-debian-container_prep
artifacts: false
.test-env-debian-lts-x86_64:
tags:
- kvm
extends:
- .build-env-debian-lts-x86_64
- .build-and-test
needs:
- job: x86_64-debian-lts-container_prep
artifacts: false
.test-env-debian-x86_64:
tags:
- kvm
extends:
- .build-env-debian-x86_64
- .build-and-test
needs:
- job: x86_64-debian-container_prep
artifacts: false
.test-env-debian-lts-aarch64:
tags:
- kvm-aarch64
extends:
- .build-env-debian-lts-aarch64
- .build-and-test
needs:
- job: aarch64-debian-lts-container_prep
artifacts: false
.test-env-debian-aarch64:
tags:
- kvm-aarch64
extends:
- .build-env-debian-aarch64
- .build-and-test
needs:
- job: aarch64-debian-container_prep
artifacts: false
# Full build (gcov + perfetto) used for testing under KVM.
.build-options-full:
stage: "Full build and test"
variables:
MESON_OPTIONS: >
-Doptimization=0
-Db_coverage=true
--force-fallback-for=perfetto
-Dperfetto=true
-Dperfetto:werror=false
-Dwerror=true
-Dtest-skip-is-failure=true
-Ddeprecated-remoting=true
-Ddeprecated-pipewire=true
after_script:
- ninja -C "$BUILDDIR" coverage-html > "$BUILDDIR/meson-logs/ninja-coverage-html.txt"
- ninja -C "$BUILDDIR" coverage-xml
# Full build, (without gcov and perfetto)
.build-options-full-v2:
stage: "Full build and test"
variables:
MESON_OPTIONS: >
-Doptimization=0
-Dwerror=true
-Dtest-skip-is-failure=true
x86_64-debian-lts-full-build:
extends:
- .test-env-debian-lts-x86_64
- .build-options-full
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: $BUILDDIR/meson-logs/coverage.xml
x86_64-debian-full-build:
extends:
- .test-env-debian-x86_64
- .build-options-full-v2
aarch64-debian-lts-full-build:
extends:
- .test-env-debian-lts-aarch64
- .build-options-full-v2
aarch64-debian-full-build:
extends:
- .test-env-debian-aarch64
- .build-options-full-v2
x86_64-clang-debian-lts-full-build:
extends:
- .test-env-debian-lts-x86_64
- .build-with-clang
- .build-options-full-v2
x86_64-clang-debian-full-build:
extends:
- .test-env-debian-x86_64
- .build-with-clang
- .build-options-full-v2
aarch64-clang-debian-lts-full-build:
extends:
- .test-env-debian-lts-aarch64
- .build-with-clang
- .build-options-full-v2
aarch64-clang-debian-full-build:
extends:
- .test-env-debian-aarch64
- .build-with-clang
- .build-options-full-v2
# Docs should be invariant on all architectures, so we only do it on Debian
# x86-64.
docs-build:
stage: "Other builds"
variables:
MESON_OPTIONS: >
-Dwerror=true
-Ddoc=true
extends:
- .build-env-debian-x86_64
- .build-no-test
# Building without gl-renderer and/or vulkan-renderer, to make sure this keeps working.
.build-options-no-gl-no-vulkan:
stage: "No-GL/Vulkan build and test"
variables:
MESON_OPTIONS: >
-Dsimple-clients=damage,im,shm,touch,dmabuf-v4l
-Drenderer-gl=false
-Drenderer-vulkan=false
-Dwerror=true
.build-options-no-gl:
stage: "No-GL/Vulkan build and test"
variables:
MESON_OPTIONS: >
-Dsimple-clients=damage,im,shm,touch,dmabuf-v4l
-Drenderer-gl=false
-Dwerror=true
.build-options-no-vulkan:
stage: "No-GL/Vulkan build and test"
variables:
MESON_OPTIONS: >
-Dsimple-clients=damage,im,shm,touch,dmabuf-v4l
-Drenderer-vulkan=false
-Dwerror=true
x86_64-debian-lts-no-gl-no-vulkan-build:
extends:
- .test-env-debian-lts-x86_64
- .build-options-no-gl-no-vulkan
x86_64-debian-no-gl-no-vulkan-build:
extends:
- .test-env-debian-x86_64
- .build-options-no-gl-no-vulkan
armv7-debian-lts-no-gl-no-vulkan-build:
extends:
- .build-env-debian-lts-armv7
- .build-no-test
- .build-options-no-gl-no-vulkan
armv7-debian-no-gl-no-vulkan-build:
extends:
- .build-env-debian-armv7
- .build-no-test
- .build-options-no-gl-no-vulkan
armv7-clang-debian-lts-no-gl-no-vulkan-build:
extends:
- .build-env-debian-lts-armv7
- .build-with-clang
- .build-no-test
- .build-options-no-gl-no-vulkan
armv7-clang-debian-no-gl-no-vulkan-build:
extends:
- .build-env-debian-armv7
- .build-with-clang
- .build-no-test
- .build-options-no-gl-no-vulkan
aarch64-debian-lts-no-gl-no-vulkan-build:
extends:
- .test-env-debian-lts-aarch64
- .build-options-no-gl-no-vulkan
aarch64-debian-no-gl-no-vulkan-build:
extends:
- .test-env-debian-aarch64
- .build-options-no-gl-no-vulkan
x86_64-debian-no-gl-build:
extends:
- .test-env-debian-x86_64
- .build-options-no-gl
x86_64-debian-no-vulkan-build:
extends:
- .test-env-debian-x86_64
- .build-options-no-vulkan
# Expose docs and coverage reports, so we can show users any changes to these
# inside their merge requests, letting us check them before merge.
#
# This does not build the docs or coverage information itself, but just reuses
# the docs and coverage information from the x86-64 Debian builds as the
# canonical sources of coverage information; the docs themselves should be
# invariant across any architecture or OS.
docs-and-coverage:
extends:
- .default-rules
- .debian-lts-x86_64
- .fdo.suffixed-image@debian
stage: pages
needs:
- job: docs-build
artifacts: true
- job: x86_64-debian-lts-full-build
artifacts: true
timeout: 5m
script:
- mv prefix-weston-docs-build/share/doc/weston Documentation
- mv build-weston-x86_64-debian-lts-full-build/meson-logs/coveragereport Test_Coverage
- rm Test_Coverage/gcov.css
- cp doc/style/lcov-style.css Test_Coverage/gcov.css
- cp doc/style/*.png Test_Coverage/
- rm -rf build-* prefix-*
artifacts:
expose_as: 'Documentation preview and test coverage report'
paths:
- Documentation/
- Test_Coverage/
# Generate the documentation for https://wayland.pages.freedesktop.org/weston/
# Anything under public/ is published to this URL.
#
# Does not inherit .default-rules as it should only run in our default branch for
# the upstream repo.
pages:
extends:
- .debian-x86_64
- .fdo.suffixed-image@debian
stage: pages
timeout: 5m
needs:
- job: docs-build
artifacts: true
script:
- export PREFIX=$(pwd)/prefix-weston-docs-build
- mkdir public
- cp -R $PREFIX/share/doc/weston/* public/
artifacts:
paths:
- public
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_PATH == "wayland/weston" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
when: on_success
- when: never

View file

@ -1,244 +0,0 @@
#!/bin/bash
#
# Builds the dependencies required for any OS/architecture combination. See
# .gitlab-ci.yml for more information. This script is called from an
# OS-specific build scripts like debian-install.sh.
source "${FDO_CI_BASH_HELPERS}"
set -o xtrace -o errexit
# Set concurrency to an appropriate level for our shared runners, falling back
# to the conservative default form before we had this variable.
export MAKEFLAGS="-j${FDO_CI_CONCURRENT:-4}"
export NINJAFLAGS="-j${FDO_CI_CONCURRENT:-4}"
# When calling pip in newer versions, we're required to pass
# --break-system-packages so it knows that we did really want to call pip and
# aren't just doing it by accident.
PIP_ARGS="--user"
case "$FDO_DISTRIBUTION_VERSION" in
bullseye)
;;
*)
PIP_ARGS="$PIP_ARGS --break-system-packages"
;;
esac
# Build and install Meson. Generally we want to keep this in sync with what
# we require inside meson.build.
fdo_log_section_start_collapsed install_meson "install_meson"
pip3 install $PIP_ARGS git+https://github.com/mesonbuild/meson.git@1.4.2
export PATH=$HOME/.local/bin:$PATH
# Our docs are built using Sphinx (top-level organisation and final HTML/CSS
# generation), Doxygen (parse structures/functions/comments from source code),
# Breathe (a bridge between Doxygen and Sphinx), and we use the Read the Docs
# theme for the final presentation.
pip3 install $PIP_ARGS sphinx==4.2.0
pip3 install $PIP_ARGS sphinxcontrib-applehelp==1.0.4
pip3 install $PIP_ARGS sphinxcontrib-devhelp==1.0.2
pip3 install $PIP_ARGS sphinxcontrib-htmlhelp==2.0.0
pip3 install $PIP_ARGS sphinxcontrib-jsmath==1.0.1
pip3 install $PIP_ARGS sphinxcontrib-qthelp==1.0.3
pip3 install $PIP_ARGS sphinxcontrib-serializinghtml==1.1.5
pip3 install $PIP_ARGS breathe==4.31.0
pip3 install $PIP_ARGS sphinx_rtd_theme==1.0.0
fdo_log_section_end install_meson
# Build a Linux kernel for use in testing. We enable the VKMS module so we can
# predictably test the DRM backend in the absence of real hardware. We lock the
# version here so we see predictable results.
#
# To run this we use virtme-ng, a QEMU wrapper. It is a fork from virtme, whose
# development stalled.
#
# virtme-ng makes our lives easier by abstracting handling of the console,
# filesystem, etc, so we can pretend that the VM we execute in is actually
# just a regular container.
fdo_log_section_start_collapsed install_kernel "install_kernel"
if [[ -n "$KERNEL_DEFCONFIG" ]]; then
git clone --depth=1 --branch drm-misc-next-2026-03-20 https://gitlab.freedesktop.org/drm/misc/kernel.git linux
cd linux
if [[ "${BUILD_ARCH}" = "x86-64" ]]; then
LINUX_ARCH=x86
elif [[ "$BUILD_ARCH" = "aarch64" ]]; then
LINUX_ARCH=arm64
else
echo "Invalid or missing \$BUILD_ARCH"
exit 1
fi
if [[ -z "${KERNEL_DEFCONFIG}" ]]; then
echo "Invalid or missing \$KERNEL_DEFCONFIG"
exit
fi
if [[ -z "${KERNEL_IMAGE}" ]]; then
echo "Invalid or missing \$KERNEL_IMAGE"
exit
fi
make ARCH=${LINUX_ARCH} ${KERNEL_DEFCONFIG}
make ARCH=${LINUX_ARCH} kvm_guest.config
./scripts/config \
--enable CONFIG_DRM \
--enable CONFIG_DRM_KMS_HELPER \
--enable CONFIG_DRM_VKMS \
--enable CONFIG_UDMABUF
make ARCH=${LINUX_ARCH} oldconfig
make ARCH=${LINUX_ARCH}
cd ..
mkdir /weston-virtme
mv linux/arch/${LINUX_ARCH}/boot/${KERNEL_IMAGE} /weston-virtme/
mv linux/.config /weston-virtme/.config
rm -rf linux
git clone --depth=1 --branch=v1.25 --recurse-submodules https://github.com/arighi/virtme-ng.git virtme
cd virtme
./setup.py install
cd ..
fi
fdo_log_section_end install_kernel
# Build and install Wayland; keep this version in sync with our dependency
# in meson.build.
fdo_log_section_start_collapsed install_wayland "install_wayland"
git clone --branch 1.22.0 --depth=1 https://gitlab.freedesktop.org/wayland/wayland
cd wayland
git show -s HEAD
meson setup build --wrap-mode=nofallback -Ddocumentation=false
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf wayland
# Keep this version in sync with our dependency in meson.build. If you wish to
# raise a MR against custom protocol, please change this reference to clone
# your relevant tree, and make sure you bump $FDO_DISTRIBUTION_TAG.
git clone --branch 1.46 --depth=1 https://gitlab.freedesktop.org/wayland/wayland-protocols
cd wayland-protocols
git show -s HEAD
meson setup build --wrap-mode=nofallback -Dtests=false
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf wayland-protocols
fdo_log_section_end install_wayland
# Build and install our own version of libdrm. Debian 11 (bullseye) provides
# libdrm 2.4.104 which doesn't have the IN_FORMATS iterator api, and Mesa
# depends on 2.4.109 as well.
# Bump to 2.4.118 to include DRM_FORMAT_NV{15,20,30}
fdo_log_section_start_collapsed install_libdrm "install_libdrm"
git clone --branch libdrm-2.4.118 --depth=1 https://gitlab.freedesktop.org/mesa/drm.git
cd drm
meson setup build --wrap-mode=nofallback -Dauto_features=disabled \
-Dvc4=disabled -Dfreedreno=disabled -Detnaviv=disabled
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf drm
fdo_log_section_end install_libdrm
# Build and install Vulkan-Headers with a defined version, mostly because
# the version in Debian 11 (bullseye) is too old to build vulkan-renderer.
fdo_log_section_start_collapsed install_vulkan_headers "install_vulkan_headers"
git clone --branch sdk-1.3.239.0 --depth=1 https://github.com/KhronosGroup/Vulkan-Headers
cd Vulkan-Headers
cmake -G Ninja -B build
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf Vulkan-Headers
fdo_log_section_end install_vulkan_headers
# Build and install our own version of Mesa. Debian provides a perfectly usable
# Mesa, however llvmpipe's rendering behaviour can change subtly over time.
# This doesn't work for our tests which expect pixel-precise reproduction, so
# we lock it to a set version for more predictability. If you need newer
# features from Mesa then bump this version and $FDO_DISTRIBUTION_TAG, however
# please be prepared for some of the tests to change output, which will need to
# be manually inspected for correctness.
fdo_log_section_start_collapsed install_mesa "install_mesa"
# Needed for Mesa >= 25.3
git clone --branch 12.2.0 --depth=1 https://github.com/KhronosGroup/glslang
cd glslang
cmake -G Ninja -B build
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf glslang
git clone --branch mesa-26.0.4 --depth=1 https://gitlab.freedesktop.org/mesa/mesa.git
cd mesa
meson setup build --wrap-mode=nofallback -Dauto_features=disabled \
-Dgallium-drivers=llvmpipe -Dvulkan-drivers=swrast -Dvideo-codecs= \
-Degl=enabled -Dgbm=enabled -Dgles2=enabled -Dllvm=enabled \
-Dshared-glapi=enabled -Dglx=disabled
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf mesa
fdo_log_section_end install_mesa
# PipeWire is used for remoting support. Unlike our other dependencies its
# behaviour will be stable, however as a pre-1.0 project its API is not yet
# stable, so again we lock it to a fixed version.
#
# ... the version chosen is 0.3.32 with a small Clang-specific build fix.
fdo_log_section_start_collapsed install_pipewire "install_pipewire"
git clone --single-branch --branch master https://gitlab.freedesktop.org/pipewire/pipewire.git pipewire-src
cd pipewire-src
git checkout -b snapshot bf112940d0bf8f526dd6229a619c1283835b49c2
meson setup build --wrap-mode=nofallback
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf pipewire-src
fdo_log_section_end install_pipewire
# seatd lets us avoid the pain of open-coding TTY assignment within Weston.
# We use this for our tests using the DRM backend.
fdo_log_section_start_collapsed install_seatd "install_seatd"
git clone --depth=1 --branch 0.6.1 https://git.sr.ht/~kennylevinsen/seatd
cd seatd
meson setup build --wrap-mode=nofallback -Dauto_features=disabled \
-Dlibseat-seatd=enabled -Dlibseat-logind=systemd -Dserver=enabled
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf seatd
fdo_log_section_end install_seatd
# Build and install aml and neatvnc, which are required for the VNC backend
fdo_log_section_start_collapsed install_aml_neatvnc "install_aml_neatvnc"
git clone --branch v0.3.0 --depth=1 https://github.com/any1/aml.git
cd aml
meson setup build --wrap-mode=nofallback
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf aml
git clone --branch v0.7.0 --depth=1 https://github.com/any1/neatvnc.git
cd neatvnc
meson setup build --wrap-mode=nofallback -Dauto_features=disabled
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf neatvnc
fdo_log_section_end install_aml_neatvnc
# Build and install libdisplay-info, used by drm-backend
fdo_log_section_start_collapsed install_libdisplay-info "install_libdisplay-info"
git clone --branch 0.2.0 --depth=1 https://gitlab.freedesktop.org/emersion/libdisplay-info.git
cd libdisplay-info
meson setup build --wrap-mode=nofallback
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf libdisplay-info
fdo_log_section_end install_libdisplay-info
# Build and install lcms2, which we use to support color-management.
fdo_log_section_start_collapsed install_lcms2 "install_lcms2"
git clone --branch master https://github.com/mm2/Little-CMS.git lcms2
cd lcms2
git checkout -b snapshot lcms2.16
meson setup build --wrap-mode=nofallback
ninja ${NINJAFLAGS} -C build install
cd ..
rm -rf lcms2
fdo_log_section_end install_lcms2

View file

@ -1,22 +0,0 @@
#!/bin/bash
set -xe
source "${FDO_CI_BASH_HELPERS}"
fdo_log_section_start_collapsed build_weston "build_weston"
cd "$BUILDDIR"
meson --prefix="$PREFIX" --wrap-mode=nofallback $SANITIZE ${MESON_OPTIONS} ${MESON_TOOLCHAIN_OPTIONS} ${MESON_DIST_OPTIONS} ..
ninja -k0 -j${FDO_CI_CONCURRENT:-4}
ninja install
if [ "$CI_JOB_NAME" == "x86_64-debian-full-build" ]; then
cd "$BUILDDIR_WESTINY"
export NPREFIX=$CI_PROJECT_DIR/prefix-weston-$CI_JOB_NAME
export PKG_CONFIG_PATH=$NPREFIX/lib/pkgconfig/:$NPREFIX/share/pkgconfig/:$NPREFIX/lib/x86_64-linux-gnu/pkgconfig:$PKG_CONFIG_PATH
meson setup --prefix="$PREFIX_WESTINY" --wrap-mode=nofallback ../westinyplus/
ninja -k0 -j${FDO_CI_CONCURRENT:-4}
ninja install
ninja clean
cd -
fi
fdo_log_section_end build_weston

View file

@ -1,150 +0,0 @@
#!/bin/bash
#
# Constructs the base container image used to build Weston within CI. Per the
# comment at the top of .gitlab-ci.yml, any changes in this file must bump the
# $FDO_DISTRIBUTION_TAG variable so we know the container has to be rebuilt.
set -o xtrace -o errexit
# These get temporary installed for building Linux and then force-removed.
LINUX_DEV_PKGS="
bc
bison
flex
"
# These get temporary installed for building Mesa and then force-removed.
MESA_DEV_PKGS="
bison
flex
gettext
libwayland-egl-backend-dev
libxrandr-dev
libxshmfence-dev
libxrandr-dev
llvm-${LLVM_VERSION}-dev
python3-mako
"
# These get temporarily installed for other build dependencies and then
# force-removed.
# cmake is used by Vulkan-Headers
BUILD_DEV_PKGS="
cmake
"
# Needed for running the custom-built mesa
MESA_RUNTIME_PKGS="
libllvm${LLVM_VERSION}
"
if [ x"$USE_DEBIAN_BACKPORTS" = "xy" ] ; then
echo 'deb http://deb.debian.org/debian '${FDO_DISTRIBUTION_VERSION}'-backports main' >> /etc/apt/sources.list
fi
apt-get update
apt-get -y --no-install-recommends install \
autoconf \
automake \
build-essential \
clang-${LLVM_VERSION} \
curl \
doxygen \
graphviz \
gcovr \
git \
glslang-tools \
hwdata \
lcov \
libasound2-dev \
libbluetooth-dev \
libcairo2-dev \
libcolord-dev \
libdbus-1-dev \
libdrm-dev \
libegl1-mesa-dev \
libelf-dev \
libevdev-dev \
libexpat1-dev \
libffi-dev \
libgbm-dev \
libgdk-pixbuf-xlib-2.0-dev \
libgles2-mesa-dev \
libglu1-mesa-dev \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev \
libinput-dev \
libjack-jackd2-dev \
libjpeg-dev \
libjpeg-dev \
liblua5.4-dev \
libmtdev-dev \
libpam0g-dev \
libpango1.0-dev \
libpciaccess-dev \
libpixman-1-dev \
libpng-dev \
libpulse-dev \
libsbc-dev \
libsystemd-dev \
libtool \
libudev-dev \
libva-dev \
libvpx-dev \
libvulkan-dev \
libwebp-dev \
libx11-dev \
libx11-xcb-dev \
libxcb1-dev \
libxcb-composite0-dev \
libxcb-dri2-0-dev \
libxcb-dri3-dev \
libxcb-glx0-dev \
libxcb-present-dev \
libxcb-randr0-dev \
libxcb-shm0-dev \
libxcb-sync-dev \
libxcb-xfixes0-dev \
libxcb-xkb-dev \
libxcursor-dev \
libxcb-cursor-dev \
libxdamage-dev \
libxext-dev \
libxfixes-dev \
libxkbcommon-dev \
libxml2-dev \
libxxf86vm-dev \
lld-${LLVM_VERSION} \
llvm-${LLVM_VERSION} \
llvm-${LLVM_VERSION}-dev \
mesa-common-dev \
ninja-build \
pkg-config \
python3-pip \
python3-pygments \
python3-setuptools \
qemu-system \
sysvinit-core \
x11proto-dev \
xwayland \
python3-argcomplete \
flake8 pylint \
cargo rustc \
iproute2 udev \
$MESA_DEV_PKGS \
$BUILD_DEV_PKGS \
$MESA_RUNTIME_PKGS \
$PACKAGES_SPECIFIC \
$LINUX_DEV_PKGS \
if [ "$FREERDP_VERSION" -ne 0 ] ; then
apt-get -y --no-install-recommends install freerdp${FREERDP_VERSION}-dev
fi
# Actually build our dependencies ...
./.gitlab-ci/build-deps.sh
# And remove packages which are only required for our build dependencies,
# which we don't need bloating the image whilst we build and run Weston.
apt-get -y --autoremove purge $LINUX_DEV_PKGS $MESA_DEV_PKGS $BUILD_DEV_PKGS

View file

@ -1,26 +0,0 @@
# AddressSanitizer memory leak suppressions
# This leaks in Debian's fontconfig/Xwayland setup. We add the entire
# fontconfig library because turning off fast unwind -- required to catch other
# originating leaks from fontconfig; would stall our tests timing them out.
leak:libfontconfig
# Workarounds for the LeakSanitizer use_tls=0 workaround,
# see tests/meson.build
leak:wl_shm_buffer_begin_access
leak:g_malloc0
leak:sysprof_collector_get
leak:/ld-*.so*
# Add all of perfetto, since it's not easy to clean up after it
leak:perfetto
# lavapipe inexplicably leaks when Vulkan physical devices are enumerated,
# despite us a) not using that device and b) freeing the instance. This is
# apparently a known issue. (Also when ASan creates threads ...)
leak:vkEnumeratePhysicalDevices
leak:asan_thread_start
# glib/pangoft for Trixie update
leak:libpangoft2
leak:libglib

View file

@ -1,13 +0,0 @@
#!/bin/bash
set -xe
source "${FDO_CI_BASH_HELPERS}"
cd "$BUILDDIR"
test -n "${QEMU_SMP}" || QEMU_SMP=${FDO_CI_CONCURRENT:-4}
virtme-run --rw --pwd --kimg /weston-virtme/${KERNEL_IMAGE} --kopt quiet --kopt log_buf_len=2M --script-sh ../.gitlab-ci/virtme-scripts/run-weston-tests.sh --qemu-opts -m 4096 -smp ${QEMU_SMP}
TEST_RES=$(cat $TESTS_RES_PATH)
rm $TESTS_RES_PATH
cp -R /weston-virtme ./
rm weston-virtme/${KERNEL_IMAGE}
exit $TEST_RES

View file

@ -1,46 +0,0 @@
#!/bin/bash
# folders that are necessary to run Weston tests
mkdir -p /tmp/tests
mkdir -p /tmp/.X11-unix
chmod -R 0700 /tmp
# set environment variables to run Weston tests
export XDG_RUNTIME_DIR=/tmp/tests
export LIBSEAT_BACKEND=seatd
# In our test suite, we use VKMS to run DRM-backend tests. The order in which
# devices are loaded is not predictable, so the DRM node that VKMS takes can
# change across each boot. That's why we have this one-liner shell script to get
# the appropriate node for VKMS.
export WESTON_TEST_SUITE_DRM_DEVICE=$(basename /sys/bus/faux/devices/vkms/drm/card*)
# ninja test depends on meson, and meson itself looks for its modules on folder
# $HOME/.local/lib/pythonX.Y/site-packages (the Python version may differ).
# build-deps.sh installs dependencies to /usr/local.
# virtme starts with HOME=/tmp/roothome, but as we installed meson on user root,
# meson can not find its modules. So we change the HOME env var to fix that.
export HOME=/root
export PATH=$HOME/.local/bin:$PATH
export PATH=/usr/local/bin:$PATH
# Terrible hack, per comment in weston-test-runner.c's main(): find Mesa's
# llvmpipe/lavapipe driver module location
export WESTON_CI_LEAK_DL_HANDLES=$(find /usr/local -name swrast_dri.so -print 2>/dev/null || true):
export WESTON_CI_LEAK_DL_HANDLES=$WESTON_CI_LEAK_DL_HANDLES:$(find /usr/local -name libvulkan_lvp.so -print 2>/dev/null || true)
export WESTON_CI_LEAK_DL_HANDLES=$WESTON_CI_LEAK_DL_HANDLES:$(find /usr/local -name libgallium\*.so -print 2>/dev/null || true)
# run the tests and save the exit status
# we give ourselves a very generous timeout multiplier due to ASan overhead
echo 0x1f > /sys/module/drm/parameters/debug
seatd-launch -- meson test --no-rebuild --timeout-multiplier 4
# note that we need to store the return value from the tests in order to
# determine if the test suite ran successfully or not.
TEST_RES=$?
dmesg &> dmesg.log
echo 0x00 > /sys/module/drm/parameters/debug
# create a file to keep the result of this script:
# - 0 means the script succeeded
# - 1 means the tests failed, so the job itself should fail
TESTS_RES_PATH=$(pwd)/tests-res.txt
echo $TEST_RES > $TESTS_RES_PATH

View file

@ -1,3 +0,0 @@
Faith Ekstrand <faith@gfxstrand.net> <jason@jlekstrand.net>
Faith Ekstrand <faith@gfxstrand.net> <jason.ekstrand@intel.com>
Faith Ekstrand <faith@gfxstrand.net> <jason.ekstrand@collabora.com>

View file

@ -1,386 +0,0 @@
Contributing to Weston
=======================
Sending patches
---------------
Patches should be sent via
[GitLab merge requests](https://docs.gitlab.com/ce/gitlab-basics/add-merge-request.html).
Weston is
[hosted on freedesktop.org's GitLab](https://gitlab.freedesktop.org/wayland/weston/):
in order to submit code, you should create an account on this GitLab instance,
fork the core Weston repository, push your changes to a branch in your new
repository, and then submit these patches for review through a merge request.
### Forking & Permissions for new users
Due to huge amounts of spam, freedesktop.org has disabled forking of existing
projects for new users. Please head to
[How can I contribute](https://gitlab.freedesktop.org/freedesktop/freedesktop/-/wikis/home#how-can-i-contribute-to-an-existing-project-or-create-a-new-one)
and verify whether you need to perform additional steps.
### Do not send patches over email
Weston formerly accepted patches via `git-send-email`, sent to
**wayland-devel\@lists.freedesktop.org**; these were
[tracked using Patchwork](https://patchwork.freedesktop.org/project/wayland/).
New email patches are no longer accepted.
Finding something to work on
----------------------------
Weston's development is [tracked on GitLab](https://gitlab.freedesktop.org/wayland/weston).
In addition to reviewing code submissions (see below), we use the issue tracker
to discuss both bugfixes and development of new features.
The '[good for new contributors](https://gitlab.freedesktop.org/wayland/weston/issues?label_name%5B%5D=Good+for+new+contributors)'
label is used for issues the development team thinks are a good place to begin
working on Weston. These issues cover features or bugfixes which are small,
self-contained, don't require much specific background knowledge, and aren't
blocked by more complex work.
If you have picked an issue you would like to work on, you may want to mention
in the issue tracker that you would like to pick it up. You can also discuss
it with the developers in the issue tracker, or on the
[mailing list](https://lists.freedesktop.org/mailman/listinfo/wayland-devel).
Many developers also use IRC through [OFTC](https://www.oftc.net/)'s
`#wayland` channel; however you may need to wait some time for a response on
IRC, which requires keeping your client connected. If you cannot stay for a
long time (potentially some hours due to timezone differences), then you
may want to send your question to the list or issue tracker instead.
Formatting and separating commits
---------------------------------
Unlike many projects using GitHub and GitLab, Weston has a
[linear, 'recipe' style history](http://www.bitsnbites.eu/git-history-work-log-vs-recipe/).
This means that every commit should be small, digestible, stand-alone, and
functional. Rather than a purely chronological commit history like this:
doc: final docs for view transforms
fix tests when disabled, redo broken doc formatting
better transformed-view iteration (thanks Hannah!)
try to catch more cases in tests
tests: add new spline test
fix compilation on splines
doc: notes on reticulating splines
compositor: add spline reticulation for view transforms
we aim to have a clean history which only reflects the final state, broken up
into functional groupings:
compositor: add spline reticulation for view transforms
compositor: new iterator for view transforms
tests: add view-transform correctness tests
doc: fix Doxygen formatting for view transforms
This ensures that the final patch series only contains the final state,
without the changes and missteps taken along the development process.
The first line of a commit message should contain a prefix indicating
what part is affected by the patch followed by one sentence that
describes the change. For examples:
compositor-drm: Support modifiers for drm_fb
and
input: do not forward unmatched touch-ups
If in doubt what prefix to use, look at other commits that change the
same file(s) as the patch being sent.
The body of the commit message should describe what the patch changes
and why, and also note any particular side effects. This shouldn't be
empty on most of the cases. It shouldn't take a lot of effort to write
a commit message for an obvious change, so an empty commit message
body is only acceptable if the questions "What?" and "Why?" are already
answered on the one-line summary.
The lines of the commit message should have at most 76 characters, to
cope with the way git log presents them.
See [notes on commit messages] for a recommended reading on writing commit
messages.
Your patches must also include a Signed-off-by line with your name
(or pseudonym) and email address which indicates that you agree to the
[Developer's Certificate of Origin 1.1](DCO-1.1.txt).
If you're not the patch's original author, you should
also gather S-o-b's from them (and/or whomever gave the patch to you) in
addition to your own S-o-b. The
significance of this is that it certifies that you created the patch,
that it was created under an appropriate open source license, or
provided to you under those terms. This lets us indicate a chain of
responsibility for the copyright status of the code.
**Agreeing to DCO 1.1 is mandatory.** Patches without a Signed-off-by cannot
be accepted, but using a pseudonym is fine as long as the email address is
yours personally.
When you re-send patches, revised or not, it would be very good to document the
changes compared to the previous revision in the commit message and/or the
merge request. If you have already received Reviewed-by or Acked-by tags, you
should evaluate whether they still apply and include them in the respective
commit messages. Otherwise the tags may be lost, reviewers miss the credit they
deserve, and the patches may cause redundant review effort.
Tracking patches and following up
---------------------------------
Once submitted to GitLab, your patches will be reviewed by the Weston
development team on GitLab. Review may be entirely positive and result in your
code landing instantly, in which case, great! You're done. However, we may ask
you to make some revisions: fixing some bugs we've noticed, working to a
slightly different design, or adding documentation and tests.
If you do get asked to revise the patches, please bear in mind the notes above.
You should use `git rebase -i` to make revisions, so that your patches follow
the clear linear split documented above. Following that split makes it easier
for reviewers to understand your work, and to verify that the code you're
submitting is correct.
A common request is to split single large patch into multiple patches. This can
happen, for example, if when adding a new feature you notice a bug in Weston's
core which you need to fix to progress. Separating these changes into separate
commits will allow us to verify and land the bugfix quickly, pushing part of
your work for the good of everyone, whilst revision and discussion continues on
the larger feature part. It also allows us to direct you towards reviewers who
best understand the different areas you are working on.
When you have made any requested changes, please rebase the commits, verify
that they still individually look good, then force-push your new branch to
GitLab. This will update the merge request and notify everyone subscribed to
your merge request, so they can review it again.
There are also
[many GitLab CLI clients](https://about.gitlab.com/applications/#cli-clients),
if you prefer to avoid the web interface. It may be difficult to follow review
comments without using the web interface though, so we do recommend using this
to go through the review process, even if you use other clients to track the
list of available patches.
Coding style
------------
You should follow the style of the file you're editing. In general, we
try to follow the rules below.
**Note: this file uses spaces due to markdown rendering issues for tabs.
Code must be indented using tabs.**
- indent with tabs, and a tab is always 8 characters wide
- opening braces are on the same line as the if statement;
- no braces in an if-body with just one statement;
- if one of the branches of an if-else condition has braces, then the
other branch should also have braces;
- there is always an empty line between variable declarations and the
code;
```c
static int
my_function(void)
{
int a = 0;
if (a)
b();
else
c();
if (a) {
b();
c();
} else {
d();
}
}
```
- lines should be less than 80 characters wide;
- when breaking lines with functions calls, the parameters are aligned
with the opening parentheses;
- when assigning a variable with the result of a function call, if the
line would be longer we break it around the equal '=' sign if it makes
sense;
```c
long_variable_name =
function_with_a_really_long_name(parameter1, parameter2,
parameter3, parameter4);
x = function_with_a_really_long_name(parameter1, parameter2,
parameter3, parameter4);
```
- do not write fallback paths for failed simple memory allocations, use the
`x*alloc()` wrappers from `shared/xalloc.h` instead or use
`abort_oom_if_null()`
Conduct
=======
As a freedesktop.org project, Wayland follows the Contributor Covenant,
found at:
https://www.freedesktop.org/wiki/CodeOfConduct
Please conduct yourself in a respectful and civilised manner when
interacting with community members on mailing lists, IRC, or bug
trackers. The community represents the project as a whole, and abusive
or bullying behaviour is not tolerated by the project.
Licensing
=========
Weston is licensed with the intention to be usable anywhere X.org is.
Originally, X.org was covered under the MIT X11 license, but changed to
the MIT Expat license. Similarly, Weston was covered initially as MIT
X11 licensed, but changed to the MIT Expat license, following in X.org's
footsteps. Other than wording, the two licenses are substantially the
same, with the exception of a no-advertising clause in X11 not included
in Expat.
New source code files should specify the MIT Expat license in their
boilerplate, as part of the copyright statement.
Review
======
All patches, even trivial ones, require at least one positive review
(Reviewed-by). Additionally, if no Reviewed-by's have been given by
people with commit access, there needs to be at least one Acked-by from
someone with commit access. A person with commit access is expected to be
able to evaluate the patch with respect to the project scope and architecture.
The below review guidelines are intended to be interpreted in spirit, not by
the letter. There may be circumstances where some guidelines are better
ignored. We rely very much on the judgement of reviewers and commit rights
holders.
During review, the following matters should be checked:
- The commit message explains why the change is being made.
- The code fits the project's scope.
- The code license is the same MIT licence the project generally uses.
- Stable ABI or API is not broken.
- Stable ABI or API additions must be justified by actual use cases, not only
by speculation. They must also be documented, and it is strongly recommended to
include tests exercising the additions in the test suite.
- The code fits the existing software architecture, e.g. no layering
violations.
- The code is correct and does not introduce new failures for existing users,
does not add new corner-case bugs, and does not introduce new compiler
warnings.
- The patch does what it says in the commit message and changes nothing else.
- The patch is a single logical change. If the commit message addresses
multiple points, it is a hint that the commit might need splitting up.
- A bug fix should target the underlying root cause instead of hiding symptoms.
If a complete fix is not practical, partial fixes are acceptable if they come
with code comments and filed Gitlab issues for the remaining bugs.
- The bug root cause rule applies to external software components as well, e.g.
do not work around kernel driver issues in userspace.
- The test suite passes.
- The code does not depend on API or ABI which has no working free open source
implementation.
- The code is not dead or untestable. E.g. if there are no free open source
software users for it then it is effectively dead code.
- The code is written to be easy to understand, or if code cannot be clear
enough on its own there are code comments to explain it.
- The code is minimal, i.e. prefer refactor and re-use when possible unless
clarity suffers.
- The code adheres to the style guidelines.
- In a patch series, every intermediate step adheres to the above guidelines.
Commit rights
=============
Commit rights will be granted to anyone who requests them and fulfills the
below criteria:
- Submitted some (10 as a rule of thumb) non-trivial (not just simple
spelling fixes and whitespace adjustment) patches that have been merged
already.
- Are actively participating in public discussions about their work (on the
mailing list or IRC). This should not be interpreted as a requirement to
review other peoples patches but just make sure that patch submission isn't
one-way communication. Cross-review is still highly encouraged.
- Will be regularly contributing further patches. This includes regular
contributors to other parts of the open source graphics stack who only
do the occasional development in this project.
- Agrees to use their commit rights in accordance with the documented merge
criteria, tools, and processes.
To apply for commit rights, create a new issue in gitlab for the respective
project and give it the "accounts" label.
Committers are encouraged to request their commit rights get removed when they
no longer contribute to the project. Commit rights will be reinstated when they
come back to the project.
Maintainers and committers should encourage contributors to request commit
rights, especially junior contributors tend to underestimate their skills.
Stabilising for releases
========================
A release cycle ends with a stable release which also starts a new cycle and
lifts any code freezes. Gradual code freezing towards a stable release starts
with an alpha release. The release stages of a cycle are:
- **Alpha release**:
Signified by version number #.#.91.
Major features must have landed before this. Major features include
invasive code motion and refactoring, high risk changes, and new stable
library ABI.
- **Beta release**:
Signified by version number #.#.92.
Minor features must have landed before this. Minor features include all
new features that are not major, low risk changes, clean-ups, and
documentation. Stable ABI that was new in the alpha release can be removed
before a beta release if necessary.
- **Release candidates (RC)**:
Signified by version number #.#.93 and up to #.#.99.
Bug fixes that are not release critical must have landed before this.
Release critical bug fixes can still be landed after this, but they may
call for another RC.
- **Stable release**:
Signified by version number #.#.0.
Ideally no changes since the last RC.
Mind that version #.#.90 is never released. It is used during development when
no code freeze is in effect. Stable branches and point releases are not covered
by the above.
[git documentation]: http://git-scm.com/documentation
[notes on commit messages]: http://who-t.blogspot.de/2009/12/on-commit-messages.html

View file

@ -1,37 +0,0 @@
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.

1603
Makefile.am Normal file

File diff suppressed because it is too large Load diff

239
README Normal file
View file

@ -0,0 +1,239 @@
Weston
======
Weston is the reference implementation of a Wayland compositor, and a
useful compositor in its own right. Weston has various backends that
lets it run on Linux kernel modesetting and evdev input as well as
under X11. Weston ships with a few example clients, from simple
clients that demonstrate certain aspects of the protocol to more
complete clients and a simplistic toolkit. There is also a quite
capable terminal emulator (weston-terminal) and an toy/example desktop
shell. Finally, weston also provides integration with the Xorg server
and can pull X clients into the Wayland desktop and act as an X window
manager.
Refer to http://wayland.freedesktop.org/building.html for building
weston and its dependencies.
The test suite can be invoked via `make check`; see
http://wayland.freedesktop.org/testing.html for additional details.
Developer documentation can be built via `make doc`. Output will be in
the build root under
docs/developer/html/index.html
docs/tools/html/index.html
Libweston
=========
Libweston is an effort to separate the re-usable parts of Weston into
a library. Libweston provides most of the boring and tedious bits of
correctly implementing core Wayland protocols and interfacing with
input and output systems, so that people who just want to write a new
"Wayland window manager" (WM) or a small desktop environment (DE) can
focus on the WM part.
Libweston was first introduced in Weston 1.12, and is expected to
continue evolving through many Weston releases before it achieves a
stable API and feature completeness.
API/ABI (in)stability and parallel installability
-------------------------------------------------
As libweston's API surface is huge, it is impossible to get it right
in one go. Therefore developers reserve the right to break the API/ABI and bump
the major version to signify that. For git snapshots of the master branch, the
API/ABI can break any time without warning.
Libweston major can be bumped only once during a development cycle. This should
happen on the first patch that breaks the API or ABI. Further breaks before the
next Weston major.0.0 release do not cause a bump. This means that libweston
API and ABI are allowed to break also after an alpha release, up to the final
release. However, breaks after alpha should be judged by the usual practices
for allowing minor features, fixes only, or critical fixes only.
To make things tolerable for libweston users despite API/ABI breakages,
different libweston major versions are designed to be perfectly
parallel-installable. This way external projects can easily depend on a
particular API/ABI-version. Thus they do not have to fight over which
ABI-version is installed in a user's system. This allows a user to install many
different compositors each requiring a different libweston ABI-version without
tricks or conflicts.
Note, that versions of Weston itself will not be parallel-installable,
only libweston is.
For more information about parallel installability, see
http://ometer.com/parallel.html
Versioning scheme
-----------------
In order to provide consistent, easy to use versioning, libweston
follows the rules in the Apache Portable Runtime Project
http://apr.apache.org/versioning.html.
The document provides the full details, with the gist summed below:
- Major - backward incompatible changes.
- Minor - new backward compatible features.
- Patch - internal (implementation specific) fixes.
Weston and libweston have separate version numbers in configure.ac. All
releases are made by the Weston version number. Libweston version number
matches the Weston version number in all releases except maybe pre-releases.
Pre-releases have the Weston micro version 91 or greater.
A pre-release is allowed to install a libweston version greater than the Weston
version in case libweston major was bumped. In that case, the libweston version
must be Weston major + 1 and with minor and patch versions zero.
Pkg-config files are named after libweston major, but carry the Weston version
number. This means that Weston pre-release 2.1.91 may install libweston-3.pc
for the future libweston 3.0.0, but the .pc file says the version is still
2.1.91. When a libweston user wants to depend on the fully stable API and ABI
of a libweston major, he should use (e.g. for major 3):
PKG_CHECK_MODULES(LIBWESTON, [libweston-3 >= 3.0.0])
Depending only on libweston-3 without a specific version number still allows
pre-releases which might have different API or ABI.
Forward compatibility
---------------------
Inspired by ATK, Qt and KDE programs/libraries, libjpeg-turbo, GDK,
NetworkManager, js17, lz4 and many others, libweston uses a macro to restrict
the API visible to the developer - REQUIRE_LIBWESTON_API_VERSION.
Note that different projects focus on different aspects - upper and/or lower
version check, default to visible/hidden old/new symbols and so on.
libweston aims to guard all newly introduced API, in order to prevent subtle
breaks that a simple recompile (against a newer version) might cause.
The macro is of the format 0x$MAJOR$MINOR and does not include PATCH version.
As mentioned in the Versioning scheme section, the latter does not reflect any
user visible API changes, thus should be not considered part of the API version.
All new symbols should be guarded by the macro like the example given below:
#if REQUIRE_LIBWESTON_API_VERSION >= 0x0101
bool
weston_ham_sandwich(void);
#endif
In order to use the said symbol, the one will have a similar code in their
configure.ac:
PKG_CHECK_MODULES(LIBWESTON, [libweston-1 >= 1.1])
AC_DEFINE(REQUIRE_LIBWESTON_API_VERSION, [0x0101])
If the user is _not_ interested in forward compatibility, they can use 0xffff
or similar high value. Yet doing so is not recommended.
Libweston design goals
----------------------
The high-level goal of libweston is to decouple the compositor from
the shell implementation (what used to be shell plugins).
Thus, instead of launching 'weston' with various arguments to choose the
shell, one would launch the shell itself, e.g. 'weston-desktop',
'weston-ivi', 'orbital', etc. The main executable (the hosting program)
will implement the shell, while libweston will be used for a fundamental
compositor implementation.
Libweston is also intended for use by other project developers who want
to create new "Wayland WMs".
Details:
- All configuration and user interfaces will be outside of libweston.
This includes command line parsing, configuration files, and runtime
(graphical) UI.
- The hosting program (main executable) will be in full control of all
libweston options. Libweston should not have user settable options
that would work behind the hosting program's back, except perhaps
debugging features and such.
- Signal handling will be outside of libweston.
- Child process execution and management will be outside of libweston.
- The different backends (drm, fbdev, x11, etc) will be an internal
detail of libweston. Libweston will not support third party
backends. However, hosting programs need to handle
backend-specific configuration due to differences in behaviour and
available features.
- Renderers will be libweston internal details too, though again the
hosting program may affect the choice of renderer if the backend
allows, and maybe set renderer-specific options.
- plugin design ???
- xwayland ???
- weston-launch is still with libweston even though it can only launch
Weston and nothing else. We would like to allow it to launch any compositor,
but since it gives by design root access to input devices and DRM, how can
we restrict it to intended programs?
There are still many more details to be decided.
For packagers
-------------
Always build Weston with --with-cairo=image.
The Weston project is (will be) intended to be split into several
binary packages, each with its own dependencies. The maximal split
would be roughly like this:
- libweston (minimal dependencies):
+ headless backend
+ wayland backend
- gl-renderer (depends on GL libs etc.)
- drm-backend (depends on libdrm, libgbm, libudev, libinput, ...)
- x11-backend (depends of X11/xcb libs)
- xwayland (depends on X11/xcb libs)
- fbdev-backend (depends on libudev...)
- rdp-backend (depends on freerdp)
- weston (the executable, not parallel-installable):
+ desktop shell
+ ivi-shell
+ fullscreen shell
+ weston-info, weston-terminal, etc. we install by default
+ screen-share
- weston demos (not parallel-installable)
+ weston-simple-* programs
+ possibly all the programs we build but do not install by
default
- and possibly more...
Everything should be parallel-installable across libweston major
ABI-versions (libweston-1.so, libweston-2.so, etc.), except those
explicitly mentioned.
Weston's build may not sanely allow this yet, but this is the
intention.

159
README.md
View file

@ -1,159 +0,0 @@
Weston
======
![screenshot of skeletal Weston desktop](doc/wayland-screenshot.jpg)
Weston is a Wayland compositor designed for correctness, reliability,
predictability, and performance.
Out of the box, Weston provides a very basic desktop, or a full-featured
environment for non-desktop uses such as automotive, embedded, in-flight,
industrial, kiosks, set-top boxes and TVs.
It also provides a library called [libweston](#libweston) which allows
users to build their own custom full-featured environments on top of
Weston's core.
Building Weston
===============
Weston is built using [Meson](https://mesonbuild.com/). Weston often depends
on the current release versions of
[Wayland](https://gitlab.freedesktop.org/wayland/wayland) and
[wayland-protocols](https://gitlab.freedesktop.org/wayland/wayland-protocols).
If necessary, the latest Meson can be installed as a user with:
$ pip3 install --user meson
Weston's Meson build does not do autodetection and it defaults to all
features enabled, which means you likely hit missing dependencies on the first
try. If a dependency is avoidable through a build option, the error message
should tell you what option can be used to avoid it. You may need to disable
several features if you want to avoid certain dependencies.
$ git clone https://gitlab.freedesktop.org/wayland/weston.git
$ cd weston
$ meson build/ --prefix=...
$ ninja -C build/ install
$ cd ..
The `meson` command populates the build directory. This step can
fail due to missing dependencies. Any build options you want can be added on
that line, e.g. `meson build/ --prefix=... -Ddemo-clients=false`. All the build
options can be found in the file [meson_options.txt](meson_options.txt).
Once the build directory has been successfully populated, you can inspect the
configuration with `meson configure build/`. If you need to change an
option, you can do e.g. `meson configure build/ -Ddemo-clients=false`.
Every push to the Weston master repository and its forks is built using GitLab
CI. [Reading the configuration](.gitlab-ci.yml) may provide a useful example of
how to build and install Weston.
More [detailed documentation on building Weston](https://wayland.freedesktop.org/building.html)
is available on the Wayland site. There are also more details on
[how to run and write tests](https://wayland.freedesktop.org/testing.html).
For building the documentation see [documentation](#documentation).
Running Weston
==============
Once Weston is installed, most users can simply run it by typing `weston`. This
will launch Weston inside whatever environment you launch it from: when launched
from a text console, it will take over that console. When launched from inside
an existing Wayland or X11 session, it will start a 'nested' instance of Weston
inside a window in that session.
By default, Weston will start with a skeletal desktop-like environment called
`desktop-shell`. Other shells are available; for example, to load the `kiosk`
shell designed for single-application environments, you can start with:
$ weston --shell=kiosk
Help is available by running `weston --help`, or `man weston`, which will list
the available configuration options and display backends. It can also be
configured through a file on disk; more information on this can be found through
`man weston.ini`.
A small suite of example or demo clients are also provided: though they can be
useful in themselves, their main purpose is to be an example or test case for
others building compositors or clients.
Using libweston
===============
libweston is designed to allow users to use Weston's core - its client support,
backends and renderers - whilst implementing their own user interface, policy,
configuration, and lifecycle. If you would like to implement your own window
manager or desktop environment, we recommend building your project using the
libweston API.
Building and installing Weston will also install libweston's shared library
and development headers. libweston is both API-compatible and ABI-compatible
within a single stable release. It is parallel-installable, so multiple stable
releases can be installed and used side by side.
Documentation for libweston's API can be found within the source (see the
[documentation](#documentation) section), and also on
[Weston's online documentation](https://wayland.pages.freedesktop.org/weston/)
for the current stable release.
Reporting issues and contributing
=================================
Weston's development is
[hosted on freedesktop.org GitLab](https://gitlab.freedesktop.org/wayland/weston/).
Please also see [the contributing document](CONTRIBUTING.md), which details how
to make code or non-technical contributions to Weston.
Weston and libweston are not suitable for severely memory-constrained environments
where the compositor is expected to continue running even in the face of
trivial memory allocations failing. If standard functions like `malloc()`
fail for small allocations,
[you can expect libweston to abort](https://gitlab.freedesktop.org/wayland/weston/-/issues/631).
This is only likely to occur if you have disabled your OS's 'overcommit'
functionality, and not in common cases.
Documentation
=============
To read the Weston documentation online, head over to
[the Weston website](https://wayland.pages.freedesktop.org/weston/).
For documenting weston we use [sphinx](http://www.sphinx-doc.org/en/master/)
together with [breathe](https://breathe.readthedocs.io/en/latest/) to process
and augment code documentation from Doxygen. You should be able to install
both sphinx and the breathe extension using pip3 command, or your package
manager. Doxygen should be available using your distribution package manager.
Once those are set up, run `meson` with `-Ddoc=true` option in order to enable
building the documentation. Installation will place the documentation in the
prefix's path under datadir (i.e., `share/doc`).
Adding and improving documentation
----------------------------------
For re-generating the documentation a special `docs` target has been added.
Although first time you build (and subsequently install) weston, you'll see the
documentation being built, updates to the spinx documentation files or to the
source files will only be updated when using `docs` target!
Example:
~~~~
$ ninja install # generates and installs the documentation
# time passes, hack hack, add doc in sources or rST files
$ ninja install # not sufficient, docs will not be updated
$ ninja docs && ninja install # run 'docs' then install
~~~~
Improving/adding documentation can be done by modifying rST files under
`doc/sphinx/` directory or by modifying the source code using doxygen
directives.

View file

@ -1,76 +0,0 @@
# Releasing
To make a release of Weston, follow these steps.
0. Verify the test suites and codebase checks pass. All of the tests should
either pass or skip.
ninja -C build/ test
1. Verify that the wayland and wayland-protocols version dependencies are
correct, and that wayland-protocols has had a release with any needed
protocol updates.
2. Update the first stanza of `meson.build` to the intended version.
If the ABI has been broken, make sure `libweston_major` has been bumped since
the last release.
Then commit your changes:
RELEASE_NUMBER="x.y.z"
RELEASE_NAME="[alpha|beta|RC1|RC2|official|point]"
git status
git commit meson.build -m "build: bump to version $RELEASE_NUMBER for the $RELEASE_NAME release"
git push
3. Ensure that you have glab client installed locally and are authenticated to
freedesktop.org Gitlab instance. You can do that using the following:
glab auth login
Use gitlab web interface to generate a token either on Weston project or
on your profile (preferably) and use the token to authenticate. gitlab cli
client can be downloaded directly from Gitlab.com or it might be provided
directly by the distribution you're currently using to do the release.
4. Run the `release.sh` script to generate the tarballs, sign and upload them,
and generate a release announcement template. This script can be obtained
from the Wayland repository:
https://gitlab.freedesktop.org/wayland/wayland/-/blob/main/release.sh
5. Compose the release announcements. The script will generate a
weston-x.y.z.announce file with a list of changes and tags. Prepend these
with a human-readable listing of the most notable changes. For x.y.0
releases, indicate the schedule for the x.y+1.0 release.
6. PGP sign the release announcement and send it to
<wayland-devel@lists.freedesktop.org>.
7. Update `releases.html` in wayland.freedesktop.org with links to tarballs and
the release email URL.
Once satisfied:
git add releases.html releases/weston-${RELEASE_NUMBER}.tar.xz*
git commit -m "releases: add weston ${RELEASE_NUMBER} release"
git push
For x.y.0 releases, also create the release series x.y branch. The x.y branch
is for bug fixes and conservative changes to the x.y.0 release, and is where we
create x.y.z releases from. Creating the x.y branch opens up master for new
development and lets new development move on. We've done this both after the
x.y.0 release (to focus development on bug fixing for the x.y.1 release for a
little longer) or before the x.y.0 release (like we did with the 1.5.0 release,
to unblock master development early).
git branch x.y [sha]
git push origin x.y
The master branch's `meson.build` version should always be (at least) x.y.90,
with x.y being the most recent stable branch. The stable branch's `meson.build`
version is just whatever was most recently released from that branch.
For stable branches, we commit fixes to master first, then `git cherry-pick -x`
them back to the stable branch.

9
autogen.sh Executable file
View file

@ -0,0 +1,9 @@
#! /bin/sh
test -n "$srcdir" || srcdir=`dirname "$0"`
test -n "$srcdir" || srcdir=.
(
cd "$srcdir" &&
autoreconf --force -v --install
) || exit
test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"

View file

@ -24,22 +24,19 @@
#include "config.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cairo.h>
#include <math.h>
#include <assert.h>
#include <getopt.h>
#include <errno.h>
#include <linux/input.h>
#include <wayland-client.h>
#include "window.h"
#include "shared/helpers.h"
#include <libweston/matrix.h>
#include "shared/matrix.h"
/* Our points for the calibration must be not be on a line */
static const struct {
@ -117,11 +114,11 @@ finish_calibration (struct calibrator *calibrator)
*/
memset(&m, 0, sizeof(m));
for (i = 0; i < (int)ARRAY_LENGTH(test_ratios); i++) {
m.M.col[0].el[i] = calibrator->tests[i].clicked_x;
m.M.col[1].el[i] = calibrator->tests[i].clicked_y;
m.M.col[2].el[i] = 1;
m.d[i] = calibrator->tests[i].clicked_x;
m.d[i + 4] = calibrator->tests[i].clicked_y;
m.d[i + 8] = 1;
}
m.M.col[3].el[3] = 1;
m.d[15] = 1;
weston_matrix_invert(&inverse, &m);
@ -129,8 +126,8 @@ finish_calibration (struct calibrator *calibrator)
memset(&y_calib, 0, sizeof(y_calib));
for (i = 0; i < (int)ARRAY_LENGTH(test_ratios); i++) {
x_calib.v.el[i] = calibrator->tests[i].drawn_x;
y_calib.v.el[i] = calibrator->tests[i].drawn_y;
x_calib.f[i] = calibrator->tests[i].drawn_x;
y_calib.f[i] = calibrator->tests[i].drawn_y;
}
/* Multiples into the vector */
@ -138,8 +135,8 @@ finish_calibration (struct calibrator *calibrator)
weston_matrix_transform(&inverse, &y_calib);
printf ("Calibration values: %f %f %f %f %f %f\n",
x_calib.v.el[0], x_calib.v.el[1], x_calib.v.el[2],
y_calib.v.el[0], y_calib.v.el[1], y_calib.v.el[2]);
x_calib.f[0], x_calib.f[1], x_calib.f[2],
y_calib.f[0], y_calib.f[1], y_calib.f[2]);
exit(0);
}
@ -221,7 +218,7 @@ redraw_handler(struct widget *widget, void *data)
}
static struct calibrator *
calibrator_create(struct display *display, bool enable_button)
calibrator_create(struct display *display)
{
struct calibrator *calibrator;
@ -232,14 +229,11 @@ calibrator_create(struct display *display, bool enable_button)
calibrator->window = window_create(display);
calibrator->widget = window_add_widget(calibrator->window, calibrator);
window_set_title(calibrator->window, "Wayland calibrator");
window_set_appid(calibrator->window,
"org.freedesktop.weston.wayland-calibrator");
calibrator->display = display;
calibrator->current_test = ARRAY_LENGTH(test_ratios) - 1;
if (enable_button)
widget_set_button_handler(calibrator->widget, button_handler);
widget_set_button_handler(calibrator->widget, button_handler);
widget_set_touch_down_handler(calibrator->widget, touch_handler);
widget_set_redraw_handler(calibrator->widget, redraw_handler);
@ -256,49 +250,21 @@ calibrator_destroy(struct calibrator *calibrator)
free(calibrator);
}
static void
help(const char *name)
{
fprintf(stderr, "Usage: %s [args...]\n", name);
fprintf(stderr, " -m, --enable-mouse Enable mouse for testing the touchscreen\n");
fprintf(stderr, " -h, --help Display this help message\n");
}
int
main(int argc, char *argv[])
{
struct display *display;
struct calibrator *calibrator;
int c;
bool enable_mouse = 0;
struct option opts[] = {
{ "enable-mouse", no_argument, NULL, 'm' },
{ "help", no_argument, NULL, 'h' },
{ 0, 0, NULL, 0 }
};
while ((c = getopt_long(argc, argv, "mh", opts, NULL)) != -1) {
switch (c) {
case 'm':
enable_mouse = 1;
break;
case 'h':
help(argv[0]);
exit(EXIT_FAILURE);
default:
break;
}
}
display = display_create(&argc, argv);
if (display == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
calibrator = calibrator_create(display, enable_mouse);
calibrator = calibrator_create(display);
if (!calibrator)
return -1;

View file

@ -32,9 +32,9 @@
#include <cairo.h>
#include <math.h>
#include <assert.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <linux/input.h>
#include <wayland-client.h>
@ -62,7 +62,8 @@ struct clickdot {
int reset;
struct input *cursor_timeout_input;
struct toytimer cursor_timeout;
int cursor_timeout_fd;
struct task cursor_timeout_task;
};
static void
@ -223,7 +224,14 @@ button_handler(struct widget *widget,
static void
cursor_timeout_reset(struct clickdot *clickdot)
{
toytimer_arm_once_usec(&clickdot->cursor_timeout, 500 * 1000);
const long cursor_timeout = 500;
struct itimerspec its;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
its.it_value.tv_sec = cursor_timeout / 1000;
its.it_value.tv_nsec = (cursor_timeout % 1000) * 1000 * 1000;
timerfd_settime(clickdot->cursor_timeout_fd, 0, &its, NULL);
}
static int
@ -263,10 +271,15 @@ leave_handler(struct widget *widget,
}
static void
cursor_timeout_func(struct toytimer *tt)
cursor_timeout_func(struct task *task, uint32_t events)
{
struct clickdot *clickdot =
container_of(tt, struct clickdot, cursor_timeout);
container_of(task, struct clickdot, cursor_timeout_task);
uint64_t exp;
if (read(clickdot->cursor_timeout_fd, &exp, sizeof (uint64_t)) !=
sizeof(uint64_t))
abort();
input_set_pointer_image(clickdot->cursor_timeout_input,
CURSOR_LEFT_PTR);
@ -281,8 +294,6 @@ clickdot_create(struct display *display)
clickdot->window = window_create(display);
clickdot->widget = window_frame_create(clickdot->window, clickdot);
window_set_title(clickdot->window, "Wayland ClickDot");
window_set_appid(clickdot->window,
"org.freedesktop.weston.wayland-clickdot");
clickdot->display = display;
clickdot->buffer = NULL;
@ -306,8 +317,12 @@ clickdot_create(struct display *display)
clickdot->line.old_y = -1;
clickdot->reset = 0;
toytimer_init(&clickdot->cursor_timeout, CLOCK_MONOTONIC,
display, cursor_timeout_func);
clickdot->cursor_timeout_fd =
timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
clickdot->cursor_timeout_task.run = cursor_timeout_func;
display_watch_fd(window_get_display(clickdot->window),
clickdot->cursor_timeout_fd,
EPOLLIN, &clickdot->cursor_timeout_task);
return clickdot;
}
@ -315,7 +330,9 @@ clickdot_create(struct display *display)
static void
clickdot_destroy(struct clickdot *clickdot)
{
toytimer_fini(&clickdot->cursor_timeout);
display_unwatch_fd(window_get_display(clickdot->window),
clickdot->cursor_timeout_fd);
close(clickdot->cursor_timeout_fd);
if (clickdot->buffer)
cairo_surface_destroy(clickdot->buffer);
widget_destroy(clickdot->widget);
@ -331,8 +348,7 @@ main(int argc, char *argv[])
display = display_create(&argc, argv);
if (display == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}

View file

@ -22,16 +22,12 @@
* DEALINGS IN THE SOFTWARE.
*/
/* cliptest:
* For debugging the quad clipper. An arbitrary quad (red) is transformed
* from global coordinate space to surface coordinate space and clipped to
* an axis-aligned rect (blue).
*
/* cliptest: for debugging calculate_edges() function.
* controls:
* surface rect position: mouse left drag, keys: w a s d
* surface rect size: mouse right drag, keys: i j k l
* quad orientation: mouse wheel, keys: n m
* quad transform disable: key: r
* clip box position: mouse left drag, keys: w a s d
* clip box size: mouse right drag, keys: i j k l
* surface orientation: mouse wheel, keys: n m
* surface transform disable key: r
*/
#include "config.h"
@ -49,108 +45,145 @@
#include <cairo.h>
#include <float.h>
#include <assert.h>
#include <errno.h>
#include <linux/input.h>
#include <wayland-client.h>
#include "libweston/matrix.h"
#include "libweston/vertex-clipping.h"
#include "shared/helpers.h"
#include "shared/xalloc.h"
#include "window.h"
typedef float GLfloat;
struct geometry {
pixman_box32_t surf;
pixman_box32_t clip;
pixman_box32_t quad;
pixman_box32_t surf;
float s; /* sin phi */
float c; /* cos phi */
float phi;
bool axis_aligned;
};
struct weston_surface {
};
struct weston_view {
struct weston_surface *surface;
struct {
int enabled;
} transform;
struct geometry *geometry;
};
static void
weston_view_from_global_float(struct weston_view *view,
float x, float y, float *sx, float *sy)
weston_view_to_global_float(struct weston_view *view,
float sx, float sy, float *x, float *y)
{
struct geometry *g = view->geometry;
/* pure rotation around origin by sine and cosine */
*sx = g->c * x + g->s * y;
*sy = -g->s * x + g->c * y;
}
static struct weston_coord_surface
weston_coord_global_to_surface(struct weston_view *view, struct weston_coord_global g_pos)
{
float sx, sy;
struct weston_coord_surface pos;
weston_view_from_global_float(view, g_pos.c.x, g_pos.c.y, &sx, &sy);
pos.c = weston_coord(sx, sy);
return pos;
*x = g->c * sx + g->s * sy;
*y = -g->s * sx + g->c * sy;
}
/* ---------------------- copied begins -----------------------*/
/* Keep this in sync with what is in gl-renderer.c! */
static void
global_to_surface(pixman_box32_t *rect, struct weston_view *ev,
struct clipper_vertex polygon[4])
{
struct weston_coord_global rect_g[4] = {
{ .c = weston_coord(rect->x1, rect->y1) },
{ .c = weston_coord(rect->x2, rect->y1) },
{ .c = weston_coord(rect->x2, rect->y2) },
{ .c = weston_coord(rect->x1, rect->y2) },
};
struct weston_coord rect_s;
int i;
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) > (b)) ? (b) : (a))
for (i = 0; i < 4; i++) {
rect_s = weston_coord_global_to_surface(ev, rect_g[i]).c;
polygon[i].x = (float)rect_s.x;
polygon[i].y = (float)rect_s.y;
/*
* 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;
}
/* ---------------------- copied ends -----------------------*/
static void
geometry_set_phi(struct geometry *g, float phi)
{
float integer;
g->phi = phi;
g->s = sin(phi);
g->c = cos(phi);
g->axis_aligned = fabs(modff(g->c, &integer)) < 0.0001f;
}
static void
geometry_init(struct geometry *g)
{
g->surf.x1 = -50;
g->surf.y1 = -50;
g->surf.x2 = -10;
g->surf.y2 = -10;
g->clip.x1 = -50;
g->clip.y1 = -50;
g->clip.x2 = -10;
g->clip.y2 = -10;
g->quad.x1 = -20;
g->quad.y1 = -20;
g->quad.x2 = 20;
g->quad.y2 = 20;
g->surf.x1 = -20;
g->surf.y1 = -20;
g->surf.x2 = 20;
g->surf.y2 = 20;
geometry_set_phi(g, 0.0);
}
@ -172,36 +205,35 @@ struct cliptest {
struct ui_state ui;
struct geometry geometry;
struct weston_surface surface;
struct weston_view view;
};
static void
draw_polygon_closed(cairo_t *cr, struct clipper_vertex *pos, int n)
draw_polygon_closed(cairo_t *cr, GLfloat *x, GLfloat *y, int n)
{
int i;
cairo_move_to(cr, pos[0].x, pos[0].y);
cairo_move_to(cr, x[0], y[0]);
for (i = 1; i < n; i++)
cairo_line_to(cr, pos[i].x, pos[i].y);
cairo_line_to(cr, pos[0].x, pos[0].y);
cairo_line_to(cr, x[i], y[i]);
cairo_line_to(cr, x[0], y[0]);
}
static void
draw_polygon_labels(cairo_t *cr, struct clipper_vertex *pos, int n)
draw_polygon_labels(cairo_t *cr, GLfloat *x, GLfloat *y, int n)
{
char str[16];
int i;
for (i = 0; i < n; i++) {
snprintf(str, 16, "%d", i);
cairo_move_to(cr, pos[i].x, pos[i].y);
cairo_move_to(cr, x[i], y[i]);
cairo_show_text(cr, str);
}
}
static void
draw_coordinates(cairo_t *cr, double ox, double oy, struct clipper_vertex *pos, int n)
draw_coordinates(cairo_t *cr, double ox, double oy, GLfloat *x, GLfloat *y, int n)
{
char str[64];
int i;
@ -209,7 +241,7 @@ draw_coordinates(cairo_t *cr, double ox, double oy, struct clipper_vertex *pos,
cairo_font_extents(cr, &ext);
for (i = 0; i < n; i++) {
snprintf(str, 64, "%d: %14.9f, %14.9f", i, pos[i].x, pos[i].y);
snprintf(str, 64, "%d: %14.9f, %14.9f", i, x[i], y[i]);
cairo_move_to(cr, ox, oy + ext.height * (i + 1));
cairo_show_text(cr, str);
}
@ -218,50 +250,50 @@ draw_coordinates(cairo_t *cr, double ox, double oy, struct clipper_vertex *pos,
static void
draw_box(cairo_t *cr, pixman_box32_t *box, struct weston_view *view)
{
struct clipper_vertex pos[4];
GLfloat x[4], y[4];
if (view) {
weston_view_from_global_float(view, box->x1, box->y1, &pos[0].x, &pos[0].y);
weston_view_from_global_float(view, box->x2, box->y1, &pos[1].x, &pos[1].y);
weston_view_from_global_float(view, box->x2, box->y2, &pos[2].x, &pos[2].y);
weston_view_from_global_float(view, box->x1, box->y2, &pos[3].x, &pos[3].y);
weston_view_to_global_float(view, box->x1, box->y1, &x[0], &y[0]);
weston_view_to_global_float(view, box->x2, box->y1, &x[1], &y[1]);
weston_view_to_global_float(view, box->x2, box->y2, &x[2], &y[2]);
weston_view_to_global_float(view, box->x1, box->y2, &x[3], &y[3]);
} else {
pos[0].x = box->x1; pos[0].y = box->y1;
pos[1].x = box->x2; pos[1].y = box->y1;
pos[2].x = box->x2; pos[2].y = box->y2;
pos[3].x = box->x1; pos[3].y = box->y2;
x[0] = box->x1; y[0] = box->y1;
x[1] = box->x2; y[1] = box->y1;
x[2] = box->x2; y[2] = box->y2;
x[3] = box->x1; y[3] = box->y2;
}
draw_polygon_closed(cr, pos, 4);
draw_polygon_closed(cr, x, y, 4);
}
static void
draw_geometry(cairo_t *cr, struct weston_view *view,
struct clipper_vertex *v, int n, struct clipper_quad *quad)
GLfloat *ex, GLfloat *ey, int n)
{
struct geometry *g = view->geometry;
float cx, cy;
draw_box(cr, &g->quad, view);
draw_box(cr, &g->surf, view);
cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.4);
cairo_fill(cr);
weston_view_from_global_float(view, g->quad.x1 - 4, g->quad.y1 - 4, &cx, &cy);
weston_view_to_global_float(view, g->surf.x1 - 4, g->surf.y1 - 4, &cx, &cy);
cairo_arc(cr, cx, cy, 1.5, 0.0, 2.0 * M_PI);
if (!quad->axis_aligned)
if (view->transform.enabled == 0)
cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.8);
cairo_fill(cr);
draw_box(cr, &g->surf, NULL);
draw_box(cr, &g->clip, NULL);
cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 0.4);
cairo_fill(cr);
if (n) {
draw_polygon_closed(cr, v, n);
draw_polygon_closed(cr, ex, ey, n);
cairo_set_source_rgb(cr, 0.0, 1.0, 0.0);
cairo_stroke(cr);
cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 0.5);
draw_polygon_labels(cr, v, n);
draw_polygon_labels(cr, ex, ey, n);
}
}
@ -273,13 +305,11 @@ redraw_handler(struct widget *widget, void *data)
struct rectangle allocation;
cairo_t *cr;
cairo_surface_t *surface;
struct clipper_quad quad;
struct clipper_vertex transformed_v[4], v[8];
GLfloat ex[8];
GLfloat ey[8];
int n;
global_to_surface(&g->quad, &cliptest->view, transformed_v);
clipper_quad_init(&quad, transformed_v, g->axis_aligned);
n = clipper_quad_clip_box32(&quad, &g->surf, v);
n = calculate_edges(&cliptest->view, &g->clip, &g->surf, ex, ey);
widget_get_allocation(cliptest->widget, &allocation);
@ -310,10 +340,10 @@ redraw_handler(struct widget *widget, void *data)
cairo_scale(cr, 4.0, 4.0);
cairo_set_line_width(cr, 0.5);
cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL);
cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL,
cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size(cr, 5.0);
draw_geometry(cr, &cliptest->view, v, n, &quad);
draw_geometry(cr, &cliptest->view, ex, ey, n);
cairo_pop_group_to_source(cr);
cairo_paint(cr);
@ -321,7 +351,7 @@ redraw_handler(struct widget *widget, void *data)
cairo_select_font_face(cr, "monospace", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, 12.0);
draw_coordinates(cr, 10.0, 10.0, v, n);
draw_coordinates(cr, 10.0, 10.0, ex, ey, n);
cairo_destroy(cr);
@ -346,12 +376,12 @@ motion_handler(struct widget *widget, struct input *input,
switch (ui->button) {
case BTN_LEFT:
geom->surf.x1 = ref->surf.x1 + dx;
geom->surf.y1 = ref->surf.y1 + dy;
geom->clip.x1 = ref->clip.x1 + dx;
geom->clip.y1 = ref->clip.y1 + dy;
/* fall through */
case BTN_RIGHT:
geom->surf.x2 = ref->surf.x2 + dx;
geom->surf.y2 = ref->surf.y2 + dy;
geom->clip.x2 = ref->clip.x2 + dx;
geom->clip.y2 = ref->clip.y2 + dy;
break;
default:
return CURSOR_LEFT_PTR;
@ -392,6 +422,7 @@ axis_handler(struct widget *widget, struct input *input, uint32_t time,
geometry_set_phi(geom, geom->phi +
(M_PI / 12.0) * wl_fixed_to_double(value));
cliptest->view.transform.enabled = 1;
widget_schedule_redraw(cliptest->widget);
}
@ -412,41 +443,44 @@ key_handler(struct window *window, struct input *input, uint32_t time,
display_exit(cliptest->display);
return;
case XKB_KEY_w:
g->surf.y1 -= 1;
g->surf.y2 -= 1;
g->clip.y1 -= 1;
g->clip.y2 -= 1;
break;
case XKB_KEY_a:
g->surf.x1 -= 1;
g->surf.x2 -= 1;
g->clip.x1 -= 1;
g->clip.x2 -= 1;
break;
case XKB_KEY_s:
g->surf.y1 += 1;
g->surf.y2 += 1;
g->clip.y1 += 1;
g->clip.y2 += 1;
break;
case XKB_KEY_d:
g->surf.x1 += 1;
g->surf.x2 += 1;
g->clip.x1 += 1;
g->clip.x2 += 1;
break;
case XKB_KEY_i:
g->surf.y2 -= 1;
g->clip.y2 -= 1;
break;
case XKB_KEY_j:
g->surf.x2 -= 1;
g->clip.x2 -= 1;
break;
case XKB_KEY_k:
g->surf.y2 += 1;
g->clip.y2 += 1;
break;
case XKB_KEY_l:
g->surf.x2 += 1;
g->clip.x2 += 1;
break;
case XKB_KEY_n:
geometry_set_phi(g, g->phi + (M_PI / 24.0));
cliptest->view.transform.enabled = 1;
break;
case XKB_KEY_m:
geometry_set_phi(g, g->phi - (M_PI / 24.0));
cliptest->view.transform.enabled = 1;
break;
case XKB_KEY_r:
geometry_set_phi(g, 0.0);
cliptest->view.transform.enabled = 0;
break;
default:
return;
@ -479,15 +513,14 @@ cliptest_create(struct display *display)
struct cliptest *cliptest;
cliptest = xzalloc(sizeof *cliptest);
cliptest->view.surface = &cliptest->surface;
cliptest->view.geometry = &cliptest->geometry;
cliptest->view.transform.enabled = 0;
geometry_init(&cliptest->geometry);
geometry_init(&cliptest->ui.geometry);
cliptest->window = window_create(display);
cliptest->widget = window_frame_create(cliptest->window, cliptest);
window_set_title(cliptest->window, "cliptest");
window_set_appid(cliptest->window, "org.freedesktop.weston.cliptest");
cliptest->display = display;
window_set_user_data(cliptest->window, cliptest);
@ -531,36 +564,32 @@ read_timer(void)
static int
benchmark(void)
{
struct weston_surface surface;
struct weston_view view;
struct geometry geom;
struct clipper_quad quad;
struct clipper_vertex transformed_v[4], v[8];
GLfloat ex[8], ey[8];
int i;
double t;
const int N = 1000000;
geom.surf.x1 = -19;
geom.surf.y1 = -19;
geom.surf.x2 = 19;
geom.surf.y2 = 19;
geom.clip.x1 = -19;
geom.clip.y1 = -19;
geom.clip.x2 = 19;
geom.clip.y2 = 19;
geom.quad.x1 = -20;
geom.quad.y1 = -20;
geom.quad.x2 = 20;
geom.quad.y2 = 20;
geom.surf.x1 = -20;
geom.surf.y1 = -20;
geom.surf.x2 = 20;
geom.surf.y2 = 20;
geometry_set_phi(&geom, 0.0);
view.surface = &surface;
view.transform.enabled = 1;
view.geometry = &geom;
reset_timer();
for (i = 0; i < N; i++) {
geometry_set_phi(&geom, (float)i / 360.0f);
global_to_surface(&geom.quad, &view, transformed_v);
clipper_quad_init(&quad, transformed_v, geom.axis_aligned);
clipper_quad_clip_box32(&quad, &geom.surf, v);
calculate_edges(&view, &geom.clip, &geom.surf, ex, ey);
}
t = read_timer();
@ -592,8 +621,7 @@ main(int argc, char *argv[])
d = display_create(&argc, argv);
if (d == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}

View file

@ -1,610 +0,0 @@
/*
* Copyright (C) 2024 SUSE Software Solutions Germany GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include "color-management-v1-client-protocol.h"
#include "shared/helpers.h"
#include "shared/xalloc.h"
#include "single-pixel-buffer-v1-client-protocol.h"
#include "viewporter-client-protocol.h"
#include "window.h"
enum image_description_status {
IMAGE_DESCRIPTION_NOT_CREATED = 0,
IMAGE_DESCRIPTION_READY,
IMAGE_DESCRIPTION_FAILED,
};
struct pixel_color {
uint32_t r;
uint32_t g;
uint32_t b;
uint32_t a;
};
struct color {
struct display *display;
struct window *window;
struct widget *parent_widget;
struct widget *widget;
struct wp_color_manager_v1 *color_manager;
struct wp_color_management_surface_v1 *color_surface;
struct wp_single_pixel_buffer_manager_v1 *single_pixel_manager;
struct wp_viewporter *viewporter;
struct wp_viewport *viewport;
struct pixel_color pixel_color;
bool unmanaged;
enum wp_color_manager_v1_primaries primaries;
enum wp_color_manager_v1_transfer_function transfer_function;
float min_lum;
float max_lum;
float ref_lum;
uint32_t supported_color_features;
uint32_t supported_rendering_intents;
uint32_t supported_primaries_named;
uint32_t supported_tf_named;
};
struct valid_enum {
const char *name;
uint32_t value;
};
static bool opt_help = false;
static uint32_t opt_width = 250;
static uint32_t opt_height = 250;
static const char *opt_r = NULL;
static const char *opt_g = NULL;
static const char *opt_b = NULL;
static const char *opt_a = NULL;
static bool opt_unmanaged = false;
static const char *opt_primaries = NULL;
static const char *opt_transfer_function = NULL;
static const char *opt_min_lum = NULL;
static const char *opt_max_lum = NULL;
static const char *opt_ref_lum = NULL;
static const struct weston_option cli_options[] = {
{ WESTON_OPTION_BOOLEAN, "help", 0, &opt_help },
{ WESTON_OPTION_UNSIGNED_INTEGER, "width", 'w', &opt_width },
{ WESTON_OPTION_UNSIGNED_INTEGER, "height", 'h', &opt_height },
{ WESTON_OPTION_STRING, 0, 'R', &opt_r },
{ WESTON_OPTION_STRING, 0, 'G', &opt_g },
{ WESTON_OPTION_STRING, 0, 'B', &opt_b },
{ WESTON_OPTION_STRING, 0, 'A', &opt_a },
{ WESTON_OPTION_BOOLEAN, "unmanaged", 'u', &opt_unmanaged },
{ WESTON_OPTION_STRING, "primaries", 'p', &opt_primaries },
{ WESTON_OPTION_STRING, "transfer-function", 't', &opt_transfer_function },
{ WESTON_OPTION_STRING, "min-lum", 'm', &opt_min_lum },
{ WESTON_OPTION_STRING, "max-lum", 'M', &opt_max_lum },
{ WESTON_OPTION_STRING, "ref-lum", 'r', &opt_ref_lum },
};
static const struct valid_enum valid_primaries[] = {
{ "srgb", WP_COLOR_MANAGER_V1_PRIMARIES_SRGB },
{ "bt2020", WP_COLOR_MANAGER_V1_PRIMARIES_BT2020 },
};
static const struct valid_enum valid_transfer_functions[] = {
{ "srgb", WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB },
{ "pq", WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ },
{ "linear", WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR },
};
static bool
validate_color(const char *c, uint32_t *dest, uint32_t fallback)
{
char *end;
double value;
if (!c) {
*dest = fallback;
return true;
}
value = strtod(c, &end);
if (value < 0.0 || value > 1.0 || *end != '\0') {
fprintf(stderr, "Validating color failed, it should be between 0.0 and 1.0\n");
return false;
}
*dest = value * UINT32_MAX;
return true;
}
static bool
validate_option(const struct color *color,
const char *option, uint32_t *dest,
const struct valid_enum *valid_options,
int count, uint32_t fallback)
{
int i;
if (color->unmanaged && option) {
fprintf(stderr, "Option '%s' not valid in unmanaged mode\n", option);
return false;
}
if (!option) {
*dest = fallback;
return true;
}
for (i = 0; i < count; i++) {
if (strcmp(valid_options[i].name, option) == 0) {
*dest = valid_options[i].value;
return true;
}
}
fprintf(stderr, "Validating option '%s' failed, valid options:\n", option);
for (i = 0; i < count; i++)
fprintf(stderr, "'%s' ", valid_options[i].name);
fprintf(stderr, "\n");
return false;
}
static bool
validate_luminance(const struct color *color,
const char *c, float *dest, float fallback)
{
char *end;
float value;
if (color->unmanaged && c) {
fprintf(stderr, "Luminance not valid in unmanaged mode.\n");
return false;
}
if (!c) {
*dest = fallback;
return true;
}
value = strtof(c, &end);
if (value < 0.f || value > 10000.f || *end != '\0') {
fprintf(stderr, "Validating luminance failed, it should be between 0 and 10,000\n");
return false;
}
*dest = value;
return true;
}
static bool
validate_options(struct color *color)
{
color->unmanaged = opt_unmanaged;
return validate_color(opt_r, &color->pixel_color.r, 0) &&
validate_color(opt_g, &color->pixel_color.g, 0) &&
validate_color(opt_b, &color->pixel_color.b, 0) &&
validate_color(opt_a, &color->pixel_color.a, UINT32_MAX) &&
validate_option(color, opt_primaries, &color->primaries,
valid_primaries,
ARRAY_LENGTH(valid_primaries),
WP_COLOR_MANAGER_V1_PRIMARIES_SRGB) &&
validate_option(color, opt_transfer_function, &color->transfer_function,
valid_transfer_functions,
ARRAY_LENGTH(valid_transfer_functions),
WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB) &&
validate_luminance(color, opt_min_lum, &color->min_lum, -1.f) &&
validate_luminance(color, opt_max_lum, &color->max_lum, -1.f) &&
validate_luminance(color, opt_ref_lum, &color->ref_lum, -1.f);
}
static void
usage(const char *program_name, int exit_code)
{
unsigned int i;
fprintf(stderr, "Usage: %s [OPTIONS]\n", program_name);
fprintf(stderr, " --help\n");
fprintf(stderr, " --width or -w\n");
fprintf(stderr, " --height or -h\n");
fprintf(stderr, " -R (0.0 to 1.0)\n");
fprintf(stderr, " -G (0.0 to 1.0)\n");
fprintf(stderr, " -B (0.0 to 1.0)\n");
fprintf(stderr, " -A (0.0 to 1.0)\n");
fprintf(stderr, " Mode of operation may be:\n");
fprintf(stderr, " --unmanaged or -u: do not use color-management\n");
fprintf(stderr, " Or use the following:\n");
fprintf(stderr, " --primaries or -p:");
fprintf(stderr, "\n ");
for (i = 0; i < ARRAY_LENGTH(valid_primaries); i++)
fprintf(stderr, " '%s'", valid_primaries[i].name);
fprintf(stderr, "\n");
fprintf(stderr, " --transfer-function or -t:");
fprintf(stderr, "\n ");
for (i = 0; i < ARRAY_LENGTH(valid_transfer_functions); i++)
fprintf(stderr, " '%s'", valid_transfer_functions[i].name);
fprintf(stderr, "\n");
fprintf(stderr, " --min-lum or -m (0.0 to 10000.0)\n");
fprintf(stderr, " --max-lum or -M (0.0 to 10000.0)\n");
fprintf(stderr, " --ref-lum or -r (0.0 to 10000.0)\n");
exit(exit_code);
}
static void
supported_intent(void *data, struct wp_color_manager_v1 *wp_color_manager_v1,
uint32_t render_intent)
{
struct color *color = data;
color->supported_rendering_intents |= 1 << render_intent;
}
static void
supported_feature(void *data, struct wp_color_manager_v1 *wp_color_manager_v1,
uint32_t feature)
{
struct color *color = data;
color->supported_color_features |= 1 << feature;
}
static void
supported_tf_named(void *data, struct wp_color_manager_v1 *wp_color_manager_v1,
uint32_t tf)
{
struct color *color = data;
color->supported_tf_named |= 1 << tf;
}
static void
supported_primaries_named(void *data,
struct wp_color_manager_v1 *wp_color_manager_v1,
uint32_t primaries)
{
struct color *color = data;
color->supported_primaries_named |= 1 << primaries;
}
static void
done(void *data, struct wp_color_manager_v1 *wp_color_manager_v1)
{
}
static const struct wp_color_manager_v1_listener color_manager_listener = {
supported_intent,
supported_feature,
supported_tf_named,
supported_primaries_named,
done,
};
static void
global_handler(struct display *display, uint32_t name,
const char *interface, uint32_t version, void *data)
{
struct color *color = data;
struct wl_surface *surface = widget_get_wl_surface(color->widget);
if (strcmp(interface, wp_single_pixel_buffer_manager_v1_interface.name) == 0) {
color->single_pixel_manager =
display_bind(display, name,
&wp_single_pixel_buffer_manager_v1_interface, 1);
} else if (strcmp(interface, wp_viewporter_interface.name) == 0) {
color->viewporter = display_bind(display, name,
&wp_viewporter_interface, 1);
color->viewport = wp_viewporter_get_viewport(color->viewporter, surface);
}
if (color->unmanaged)
return;
if (strcmp(interface, wp_color_manager_v1_interface.name) == 0) {
color->color_manager = display_bind(display, name,
&wp_color_manager_v1_interface, 1);
color->color_surface = wp_color_manager_v1_get_surface(color->color_manager,
surface);
wp_color_manager_v1_add_listener(color->color_manager,
&color_manager_listener, color);
}
}
static bool
check_color_requirements(struct color *color)
{
if (!color->color_manager) {
fprintf(stderr, "The compositor doesn't expose %s\n",
wp_color_manager_v1_interface.name);
return false;
}
if (!(color->supported_color_features & (1 << WP_COLOR_MANAGER_V1_FEATURE_PARAMETRIC))) {
fprintf(stderr, "The color manager doesn't support the parametric creator\n");
return false;
}
if (!(color->supported_primaries_named & (1 << color->primaries))) {
fprintf(stderr, "The color manager doesn't support the primaries name\n");
return false;
}
if (!(color->supported_tf_named & (1 << color->transfer_function))) {
fprintf(stderr, "The color manager doesn't support the transfer function\n");
return false;
}
if (!(color->supported_rendering_intents & (1 << WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL))) {
fprintf(stderr, "The color manager doesn't support perceptual render intent\n");
return false;
}
if (color->min_lum != -1.f || color->max_lum != -1.f || color->ref_lum != -1.f) {
if (!(color->supported_color_features & (1 << WP_COLOR_MANAGER_V1_FEATURE_SET_LUMINANCES))) {
fprintf(stderr, "The color manager doesn't support setting luminances\n");
return false;
}
if (color->min_lum == -1.f || color->max_lum == -1.f || color->ref_lum == -1.f) {
fprintf(stderr, "To set the luminances it is required min-lum, max-lum and ref-lum\n");
return false;
}
}
return true;
}
static void
color_destroy(struct color *color)
{
if (color->color_surface)
wp_color_management_surface_v1_destroy(color->color_surface);
if (color->color_manager)
wp_color_manager_v1_destroy(color->color_manager);
if (color->single_pixel_manager)
wp_single_pixel_buffer_manager_v1_destroy(color->single_pixel_manager);
if (color->viewport)
wp_viewport_destroy(color->viewport);
if (color->viewporter)
wp_viewporter_destroy(color->viewporter);
if (color->widget)
widget_destroy(color->widget);
if (color->parent_widget)
widget_destroy(color->parent_widget);
if (color->window)
window_destroy(color->window);
if (color->display)
display_destroy(color->display);
free(color);
}
static void
resize_handler(struct widget *parent_widget, int32_t width, int32_t height, void *data)
{
struct color *color = data;
struct rectangle allocation;
struct wl_surface *surface = widget_get_wl_surface(color->widget);
struct wl_subsurface *subsurface = widget_get_wl_subsurface(color->widget);
widget_get_allocation(parent_widget, &allocation);
wl_subsurface_set_position(subsurface, allocation.x, allocation.y);
wp_viewport_set_destination(color->viewport, width, height);
wl_surface_commit(surface);
}
static void
set_empty_input_region(struct color *color, struct widget *widget)
{
struct wl_region *region;
struct wl_compositor *compositor;
struct wl_surface *surface = widget_get_wl_surface(widget);
compositor = display_get_compositor(color->display);
region = wl_compositor_create_region(compositor);
wl_surface_set_input_region(surface, region);
wl_region_destroy(region);
}
static void
buffer_release(void *data, struct wl_buffer *buffer)
{
wl_buffer_destroy(buffer);
}
static const struct wl_buffer_listener buffer_listener = {
buffer_release
};
static void
set_single_pixel(struct color *color, struct widget *widget)
{
struct wl_surface *surface = widget_get_wl_surface(widget);
struct wl_buffer *buffer =
wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer(color->single_pixel_manager,
color->pixel_color.r,
color->pixel_color.g,
color->pixel_color.b,
color->pixel_color.a);
wl_buffer_add_listener(buffer, &buffer_listener, NULL);
wl_surface_attach(surface, buffer, 0, 0);
}
static void
image_description_failed(void *data,
struct wp_image_description_v1 *wp_image_description_v1,
uint32_t cause, const char *msg)
{
enum image_description_status *image_desc_status = data;
fprintf(stderr, "Failed to create image description: %u - %s\n",
cause, msg);
*image_desc_status = IMAGE_DESCRIPTION_FAILED;
}
static void
image_description_ready(void *data, struct wp_image_description_v1 *wp_image_description_v1,
uint32_t identity)
{
enum image_description_status *image_desc_status = data;
*image_desc_status = IMAGE_DESCRIPTION_READY;
}
static const struct wp_image_description_v1_listener image_description_listener = {
image_description_failed,
image_description_ready,
};
static struct wp_image_description_v1 *
create_image_description(struct color *color, uint32_t primaries_named, uint32_t tf_named)
{
struct wp_image_description_creator_params_v1 *params_creator;
struct wp_image_description_v1 *image_description;
enum image_description_status image_desc_status = IMAGE_DESCRIPTION_NOT_CREATED;
int ret = 0;
params_creator = wp_color_manager_v1_create_parametric_creator(color->color_manager);
wp_image_description_creator_params_v1_set_primaries_named(params_creator, primaries_named);
wp_image_description_creator_params_v1_set_tf_named(params_creator, tf_named);
if (color->min_lum != -1 && color->max_lum != -1 && color->ref_lum != -1)
wp_image_description_creator_params_v1_set_luminances(params_creator,
color->min_lum * 10000,
color->max_lum,
color->ref_lum);
image_description = wp_image_description_creator_params_v1_create(params_creator);
wp_image_description_v1_add_listener(image_description,
&image_description_listener,
&image_desc_status);
while (ret != -1 && image_desc_status == IMAGE_DESCRIPTION_NOT_CREATED)
ret = wl_display_dispatch(display_get_display(color->display));
if (ret == -1) {
wp_image_description_v1_destroy(image_description);
fprintf(stderr, "Error when creating the image description: %s\n", strerror(errno));
return NULL;
}
if (image_desc_status == IMAGE_DESCRIPTION_FAILED) {
wp_image_description_v1_destroy(image_description);
return NULL;
}
assert(image_desc_status == IMAGE_DESCRIPTION_READY);
return image_description;
}
static bool
set_image_description(struct color *color, struct widget *widget)
{
struct wp_image_description_v1 *image_description;
image_description =
create_image_description(color,
color->primaries,
color->transfer_function);
if (!image_description)
return false;
wp_color_management_surface_v1_set_image_description(
color->color_surface,
image_description,
WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL);
wp_image_description_v1_destroy(image_description);
return true;
}
int
main(int argc, char *argv[])
{
struct color *color;
if (parse_options(cli_options, ARRAY_LENGTH(cli_options), &argc, argv) > 1)
usage(argv[0], EXIT_FAILURE);
if (opt_help)
usage(argv[0], EXIT_SUCCESS);
color = xzalloc(sizeof *color);
if (!validate_options(color)) {
color_destroy(color);
exit(EXIT_FAILURE);
}
color->display = display_create(&argc, argv);
if (!color->display) {
color_destroy(color);
exit(EXIT_FAILURE);
}
color->window = window_create(color->display);
color->parent_widget = window_frame_create(color->window, color);
color->widget = window_add_subsurface(color->window, color, SUBSURFACE_SYNCHRONIZED);
display_set_user_data(color->display, color);
display_set_global_handler(color->display, global_handler);
wl_display_roundtrip(display_get_display(color->display));
if (!color->unmanaged && !check_color_requirements(color)) {
color_destroy(color);
exit(EXIT_SUCCESS);
}
window_unset_shadow(color->window);
window_set_title(color->window, "Color");
window_set_appid(color->window, "org.freedesktop.weston.color");
/* The first resize call sets the min size,
* setting 0, 0 sets a default size */
window_schedule_resize(color->window, 0, 0);
window_schedule_resize(color->window, opt_width, opt_height);
widget_set_resize_handler(color->parent_widget, resize_handler);
widget_set_use_cairo(color->widget, 0);
set_empty_input_region(color, color->widget);
set_single_pixel(color, color->widget);
if (color->unmanaged || set_image_description(color, color->widget))
display_run(color->display);
color_destroy(color);
return 0;
}

531
clients/confine.c Normal file
View file

@ -0,0 +1,531 @@
/*
* Copyright © 2010 Intel Corporation
* Copyright © 2012 Collabora, Ltd.
* Copyright © 2012 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cairo.h>
#include <math.h>
#include <assert.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <linux/input.h>
#include <wayland-client.h>
#include "window.h"
#include "shared/helpers.h"
#include "shared/xalloc.h"
#define NUM_COMPLEX_REGION_RECTS 9
static int32_t option_complex_confine_region;
static int32_t option_help;
struct confine {
struct display *display;
struct window *window;
struct widget *widget;
cairo_surface_t *buffer;
struct {
int32_t x, y;
int32_t old_x, old_y;
} line;
int reset;
struct input *cursor_timeout_input;
int cursor_timeout_fd;
struct task cursor_timeout_task;
bool pointer_confined;
bool complex_confine_region_enabled;
bool complex_confine_region_dirty;
struct rectangle complex_confine_region[NUM_COMPLEX_REGION_RECTS];
};
static void
draw_line(struct confine *confine, cairo_t *cr,
struct rectangle *allocation)
{
cairo_t *bcr;
cairo_surface_t *tmp_buffer = NULL;
if (confine->reset) {
tmp_buffer = confine->buffer;
confine->buffer = NULL;
confine->line.x = -1;
confine->line.y = -1;
confine->line.old_x = -1;
confine->line.old_y = -1;
confine->reset = 0;
}
if (confine->buffer == NULL) {
confine->buffer =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
allocation->width,
allocation->height);
bcr = cairo_create(confine->buffer);
cairo_set_source_rgba(bcr, 0, 0, 0, 0);
cairo_rectangle(bcr,
0, 0,
allocation->width, allocation->height);
cairo_fill(bcr);
}
else
bcr = cairo_create(confine->buffer);
if (tmp_buffer) {
cairo_set_source_surface(bcr, tmp_buffer, 0, 0);
cairo_rectangle(bcr, 0, 0,
allocation->width, allocation->height);
cairo_clip(bcr);
cairo_paint(bcr);
cairo_surface_destroy(tmp_buffer);
}
if (confine->line.x != -1 && confine->line.y != -1) {
if (confine->line.old_x != -1 &&
confine->line.old_y != -1) {
cairo_set_line_width(bcr, 2.0);
cairo_set_source_rgb(bcr, 1, 1, 1);
cairo_translate(bcr,
-allocation->x, -allocation->y);
cairo_move_to(bcr,
confine->line.old_x,
confine->line.old_y);
cairo_line_to(bcr,
confine->line.x,
confine->line.y);
cairo_stroke(bcr);
}
confine->line.old_x = confine->line.x;
confine->line.old_y = confine->line.y;
}
cairo_destroy(bcr);
cairo_set_source_surface(cr, confine->buffer,
allocation->x, allocation->y);
cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
cairo_rectangle(cr,
allocation->x, allocation->y,
allocation->width, allocation->height);
cairo_clip(cr);
cairo_paint(cr);
}
static void
calculate_complex_confine_region(struct confine *confine)
{
struct rectangle allocation;
int32_t x, y, w, h;
struct rectangle *rs = confine->complex_confine_region;
if (!confine->complex_confine_region_dirty)
return;
widget_get_allocation(confine->widget, &allocation);
x = allocation.x;
y = allocation.y;
w = allocation.width;
h = allocation.height;
/*
* The code below constructs a region made up of rectangles that
* is then used to set up both an illustrative shaded region in the
* widget and a confine region used when confining the pointer.
*/
rs[0].x = x + (int)round(w * 0.05);
rs[0].y = y + (int)round(h * 0.15);
rs[0].width = (int)round(w * 0.35);
rs[0].height = (int)round(h * 0.7);
rs[1].x = rs[0].x + rs[0].width;
rs[1].y = y + (int)round(h * 0.45);
rs[1].width = (int)round(w * 0.09);
rs[1].height = (int)round(h * 0.1);
rs[2].x = rs[1].x + rs[1].width;
rs[2].y = y + (int)round(h * 0.48);
rs[2].width = (int)round(w * 0.02);
rs[2].height = (int)round(h * 0.04);
rs[3].x = rs[2].x + rs[2].width;
rs[3].y = y + (int)round(h * 0.45);
rs[3].width = (int)round(w * 0.09);
rs[3].height = (int)round(h * 0.1);
rs[4].x = rs[3].x + rs[3].width;
rs[4].y = y + (int)round(h * 0.15);
rs[4].width = (int)round(w * 0.35);
rs[4].height = (int)round(h * 0.7);
rs[5].x = x + (int)round(w * 0.05);
rs[5].y = y + (int)round(h * 0.05);
rs[5].width = rs[0].width + rs[1].width + rs[2].width +
rs[3].width + rs[4].width;
rs[5].height = (int)round(h * 0.10);
rs[6].x = x + (int)round(w * 0.1);
rs[6].y = rs[4].y + rs[4].height + (int)round(h * 0.02);
rs[6].width = (int)round(w * 0.8);
rs[6].height = (int)round(h * 0.03);
rs[7].x = x + (int)round(w * 0.05);
rs[7].y = rs[6].y + rs[6].height;
rs[7].width = (int)round(w * 0.9);
rs[7].height = (int)round(h * 0.03);
rs[8].x = x + (int)round(w * 0.1);
rs[8].y = rs[7].y + rs[7].height;
rs[8].width = (int)round(w * 0.8);
rs[8].height = (int)round(h * 0.03);
confine->complex_confine_region_dirty = false;
}
static void
draw_complex_confine_region_mask(struct confine *confine, cairo_t *cr)
{
int i;
calculate_complex_confine_region(confine);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
for (i = 0; i < NUM_COMPLEX_REGION_RECTS; i++) {
cairo_rectangle(cr,
confine->complex_confine_region[i].x,
confine->complex_confine_region[i].y,
confine->complex_confine_region[i].width,
confine->complex_confine_region[i].height);
cairo_set_source_rgba(cr, 0.14, 0.14, 0.14, 0.9);
cairo_fill(cr);
}
}
static void
redraw_handler(struct widget *widget, void *data)
{
struct confine *confine = data;
cairo_surface_t *surface;
cairo_t *cr;
struct rectangle allocation;
widget_get_allocation(confine->widget, &allocation);
surface = window_get_surface(confine->window);
cr = cairo_create(surface);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_rectangle(cr,
allocation.x,
allocation.y,
allocation.width,
allocation.height);
cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
cairo_fill(cr);
if (confine->complex_confine_region_enabled) {
draw_complex_confine_region_mask(confine, cr);
}
draw_line(confine, cr, &allocation);
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
static void
keyboard_focus_handler(struct window *window,
struct input *device, void *data)
{
struct confine *confine = data;
window_schedule_redraw(confine->window);
}
static void
key_handler(struct window *window, struct input *input, uint32_t time,
uint32_t key, uint32_t sym,
enum wl_keyboard_key_state state, void *data)
{
struct confine *confine = data;
if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
return;
switch (sym) {
case XKB_KEY_Escape:
display_exit(confine->display);
break;
case XKB_KEY_BackSpace:
cairo_surface_destroy(confine->buffer);
confine->buffer = NULL;
window_schedule_redraw(confine->window);
break;
case XKB_KEY_m:
window_set_maximized(confine->window,
!window_is_maximized(window));
break;
}
}
static void
toggle_pointer_confine(struct confine *confine, struct input *input)
{
if (confine->pointer_confined) {
window_unconfine_pointer(confine->window);
} else if (confine->complex_confine_region_enabled) {
calculate_complex_confine_region(confine);
window_confine_pointer_to_rectangles(
confine->window,
input,
confine->complex_confine_region,
NUM_COMPLEX_REGION_RECTS);
} else {
window_confine_pointer_to_widget(confine->window,
confine->widget,
input);
}
confine->pointer_confined = !confine->pointer_confined;
}
static void
button_handler(struct widget *widget,
struct input *input, uint32_t time,
uint32_t button,
enum wl_pointer_button_state state, void *data)
{
struct confine *confine = data;
bool is_pressed = state == WL_POINTER_BUTTON_STATE_PRESSED;
if (is_pressed && button == BTN_LEFT)
toggle_pointer_confine(confine, input);
widget_schedule_redraw(widget);
}
static void
cursor_timeout_reset(struct confine *confine)
{
const long cursor_timeout = 500;
struct itimerspec its;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
its.it_value.tv_sec = cursor_timeout / 1000;
its.it_value.tv_nsec = (cursor_timeout % 1000) * 1000 * 1000;
timerfd_settime(confine->cursor_timeout_fd, 0, &its, NULL);
}
static int
motion_handler(struct widget *widget,
struct input *input, uint32_t time,
float x, float y, void *data)
{
struct confine *confine = data;
confine->line.x = x;
confine->line.y = y;
window_schedule_redraw(confine->window);
cursor_timeout_reset(confine);
confine->cursor_timeout_input = input;
return CURSOR_BLANK;
}
static void
resize_handler(struct widget *widget,
int32_t width, int32_t height,
void *data)
{
struct confine *confine = data;
confine->reset = 1;
if (confine->complex_confine_region_enabled) {
confine->complex_confine_region_dirty = true;
if (confine->pointer_confined) {
calculate_complex_confine_region(confine);
window_update_confine_rectangles(
confine->window,
confine->complex_confine_region,
NUM_COMPLEX_REGION_RECTS);
}
}
}
static void
leave_handler(struct widget *widget,
struct input *input, void *data)
{
struct confine *confine = data;
confine->reset = 1;
}
static void
cursor_timeout_func(struct task *task, uint32_t events)
{
struct confine *confine =
container_of(task, struct confine, cursor_timeout_task);
uint64_t exp;
if (read(confine->cursor_timeout_fd, &exp, sizeof (uint64_t)) !=
sizeof(uint64_t))
abort();
input_set_pointer_image(confine->cursor_timeout_input,
CURSOR_LEFT_PTR);
}
static void
pointer_unconfined(struct window *window, struct input *input, void *data)
{
struct confine *confine = data;
confine->pointer_confined = false;
}
static struct confine *
confine_create(struct display *display)
{
struct confine *confine;
confine = xzalloc(sizeof *confine);
confine->window = window_create(display);
confine->widget = window_frame_create(confine->window, confine);
window_set_title(confine->window, "Wayland Confine");
confine->display = display;
confine->buffer = NULL;
window_set_key_handler(confine->window, key_handler);
window_set_user_data(confine->window, confine);
window_set_keyboard_focus_handler(confine->window,
keyboard_focus_handler);
window_set_pointer_confined_handler(confine->window,
NULL,
pointer_unconfined);
widget_set_redraw_handler(confine->widget, redraw_handler);
widget_set_button_handler(confine->widget, button_handler);
widget_set_motion_handler(confine->widget, motion_handler);
widget_set_resize_handler(confine->widget, resize_handler);
widget_set_leave_handler(confine->widget, leave_handler);
widget_schedule_resize(confine->widget, 500, 400);
confine->line.x = -1;
confine->line.y = -1;
confine->line.old_x = -1;
confine->line.old_y = -1;
confine->reset = 0;
confine->cursor_timeout_fd =
timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
confine->cursor_timeout_task.run = cursor_timeout_func;
display_watch_fd(window_get_display(confine->window),
confine->cursor_timeout_fd,
EPOLLIN, &confine->cursor_timeout_task);
return confine;
}
static void
confine_destroy(struct confine *confine)
{
display_unwatch_fd(window_get_display(confine->window),
confine->cursor_timeout_fd);
close(confine->cursor_timeout_fd);
if (confine->buffer)
cairo_surface_destroy(confine->buffer);
widget_destroy(confine->widget);
window_destroy(confine->window);
free(confine);
}
static const struct weston_option confine_options[] = {
{ WESTON_OPTION_BOOLEAN, "complex-confine-region", 0, &option_complex_confine_region },
{ WESTON_OPTION_BOOLEAN, "help", 0, &option_help },
};
static void
print_help(const char *argv0)
{
printf("Usage: %s [--complex-confine-region]\n", argv0);
}
int
main(int argc, char *argv[])
{
struct display *display;
struct confine *confine;
if (parse_options(confine_options,
ARRAY_LENGTH(confine_options),
&argc, argv) > 1 ||
option_help) {
print_help(argv[0]);
return 0;
}
display = display_create(&argc, argv);
if (display == NULL) {
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
confine = confine_create(display);
if (option_complex_confine_region) {
confine->complex_confine_region_dirty = true;
confine->complex_confine_region_enabled = true;
}
display_run(display);
confine_destroy(confine);
display_destroy(display);
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -1,386 +0,0 @@
/*
* Copyright © 2018 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <cairo.h>
#include <sys/time.h>
#include <linux/input.h>
#include <wayland-client.h>
#include "weston-content-protection-client-protocol.h"
#include "window.h"
#include <wayland-client-protocol.h>
#define WIDTH 500
#define HEIGHT 400
#define FRAME_H 18
#define FRAME_W 5
#define BUTTON_WIDTH 65
#define BUTTON_HEIGHT 20
enum protection_mode {
RELAXED,
ENFORCED
};
struct protected_content_player {
struct weston_content_protection *protection;
struct weston_protected_surface *psurface;
struct display *display;
struct window *window;
struct widget *widget;
struct button_t *b0, *b1, *off, *enforced, *relaxed;
int width, height, x, y;
enum weston_protected_surface_type protection_type;
enum protection_mode mode;
};
struct button_t {
struct window *window;
struct widget *widget;
struct protected_content_player *pc_player;
const char *name;
};
/**
* An event to tell the client that there is a change in protection status
*
* This event is sent whenever there is a change in content
* protection. The content protection status can be ON or OFF. ON
* in case of the desired protection type is accepted on all
* connectors, and Off in case of any of the connector
* content-protection property is changed from "enabled"
*/
static void
handle_status_changed(void *data, struct weston_protected_surface *psurface,
uint32_t status)
{
struct protected_content_player *pc_player = data;
enum weston_protected_surface_type event_status = status;
switch (event_status) {
case WESTON_PROTECTED_SURFACE_TYPE_HDCP_0:
pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_HDCP_0;
break;
case WESTON_PROTECTED_SURFACE_TYPE_HDCP_1:
pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_HDCP_1;
break;
case WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED:
default:
pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED;
}
window_schedule_redraw(pc_player->window);
}
static const struct weston_protected_surface_listener pc_player_listener = {
handle_status_changed,
};
static void
draw_content(cairo_surface_t *surface, int x, int y, int width, int height,
enum weston_protected_surface_type type, enum protection_mode mode)
{
cairo_t *cr;
cairo_text_extents_t extents;
const char *content_text;
const char *mode_text;
cr = cairo_create(surface);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_rectangle(cr, x, y, width, height);
if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_0)
cairo_set_source_rgba(cr, 0, 1.0, 0, 1.0);
else if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_1)
cairo_set_source_rgba(cr, 0, 0, 1.0, 1.0);
else
cairo_set_source_rgba(cr, 1.0, 0, 0, 1.0);
cairo_fill(cr);
cairo_set_source_rgba(cr, 0, 0, 0, 1.0);
cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, 15);
if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_0)
content_text = "Content-Type : Type-0";
else if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_1)
content_text = "Content-Type : Type-1";
else
content_text = "Content-Type : Unprotected";
cairo_text_extents(cr, content_text, &extents);
cairo_move_to(cr, width/2 - (extents.width/2),
height/2 - (extents.height/2));
cairo_show_text(cr, content_text);
if (mode == ENFORCED)
mode_text = "Mode : Enforced";
else
mode_text = "Mode : Relaxed";
cairo_text_extents(cr, mode_text, &extents);
cairo_move_to(cr, width / 2 - (extents.width / 2),
2 * height / 3 - (2 * extents.height / 3));
cairo_show_text(cr, mode_text);
cairo_fill(cr);
cairo_destroy(cr);
}
static void
redraw_handler(struct widget *widget, void *data)
{
struct protected_content_player *pc_player = data;
cairo_surface_t *surface;
struct rectangle rect;
widget_get_allocation(pc_player->widget, &rect);
surface = window_get_surface(pc_player->window);
if (surface == NULL ||
cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
fprintf(stderr, "failed to create cairo egl surface\n");
return;
}
draw_content(surface, rect.x, rect.y, rect.width, rect.height,
pc_player->protection_type, pc_player->mode);
cairo_surface_destroy(surface);
}
static void
resize_handler(struct widget *widget, int32_t width, int32_t height, void *data)
{
struct rectangle allocation;
struct protected_content_player *pc_player = data;
widget_get_allocation(pc_player->widget, &allocation);
widget_set_allocation(pc_player->b0->widget,
allocation.x + 20, allocation.y + 30,
BUTTON_WIDTH, BUTTON_HEIGHT);
widget_set_allocation(pc_player->b1->widget,
allocation.x + 20 + BUTTON_WIDTH + 5,
allocation.y + 30,
BUTTON_WIDTH, BUTTON_HEIGHT);
widget_set_allocation(pc_player->off->widget,
allocation.x + 20 + 2 * (BUTTON_WIDTH + 5),
allocation.y + 30,
BUTTON_WIDTH, BUTTON_HEIGHT);
widget_set_allocation(pc_player->enforced->widget,
allocation.x + 20 + 3 * (BUTTON_WIDTH + 5),
allocation.y + 30,
BUTTON_WIDTH, BUTTON_HEIGHT);
widget_set_allocation(pc_player->relaxed->widget,
allocation.x + 20 + 4 * (BUTTON_WIDTH + 5),
allocation.y + 30,
BUTTON_WIDTH, BUTTON_HEIGHT);
}
static void
buttons_handler(struct widget *widget, struct input *input, uint32_t time,
uint32_t button, enum wl_pointer_button_state state, void *data)
{
struct button_t *b = data;
struct protected_content_player *pc_player = b->pc_player;
struct wl_surface *surface;
if (strcmp(b->name, "ENFORCED") == 0) {
weston_protected_surface_enforce(pc_player->psurface);
pc_player->mode = ENFORCED;
window_schedule_redraw(pc_player->window);
}
else if (strcmp(b->name, "RELAXED") == 0) {
weston_protected_surface_relax(pc_player->psurface);
pc_player->mode = RELAXED;
window_schedule_redraw(pc_player->window);
}
else if (strcmp(b->name, "TYPE-0") == 0)
weston_protected_surface_set_type(pc_player->psurface,
WESTON_PROTECTED_SURFACE_TYPE_HDCP_0);
else if (strcmp(b->name, "TYPE-1") == 0)
weston_protected_surface_set_type(pc_player->psurface,
WESTON_PROTECTED_SURFACE_TYPE_HDCP_1);
else
weston_protected_surface_set_type(pc_player->psurface,
WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED);
surface = window_get_wl_surface(pc_player->window);
wl_surface_commit(surface);
}
static void
handle_global(struct display *display, uint32_t name, const char *interface,
uint32_t version, void *data)
{
struct protected_content_player *pc_player = data;
if (strcmp(interface, "weston_content_protection") == 0) {
pc_player->protection = display_bind(display, name,
&weston_content_protection_interface,
1);
}
}
static void
buttons_redraw_handler(struct widget *widget, void *data)
{
struct button_t *b = data;
cairo_surface_t *surface;
struct rectangle allocation;
cairo_t *cr;
surface = window_get_surface(b->window);
widget_get_allocation(b->widget, &allocation);
cr = cairo_create(surface);
cairo_rectangle(cr, allocation.x, allocation.y, allocation.width,
allocation.height);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_rgba(cr, 1, 1, 1, 1);
cairo_fill(cr);
cairo_set_source_rgba(cr, 0, 0, 0, 1.0);
cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, 10);
cairo_move_to(cr, allocation.x + 5, allocation.y + 15);
cairo_show_text(cr, b->name);
cairo_fill(cr);
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
static struct button_t*
create_button(struct protected_content_player *pc_player, const char *name)
{
struct button_t *b;
b = zalloc(sizeof(struct button_t));
if (b == NULL) {
fprintf(stderr, "Failed to allocate memory for button.\n");
exit(0);
}
b->widget = widget_add_widget(pc_player->widget, b);
b->window = pc_player->window;
b->pc_player = pc_player;
b->name = name;
widget_set_redraw_handler(b->widget, buttons_redraw_handler);
widget_set_button_handler(b->widget, buttons_handler);
return b;
}
static void
destroy_button(struct button_t *b)
{
if (!b)
return;
widget_destroy(b->widget);
free(b);
}
static void free_pc_player(struct protected_content_player *pc_player)
{
if (!pc_player)
return;
destroy_button(pc_player->b0);
destroy_button(pc_player->b1);
destroy_button(pc_player->off);
destroy_button(pc_player->enforced);
destroy_button(pc_player->relaxed);
widget_destroy(pc_player->widget);
window_destroy(pc_player->window);
free(pc_player);
}
int main(int argc, char *argv[])
{
struct protected_content_player *pc_player;
struct display *d;
static const char str_type_0[] = "TYPE-0";
static const char str_type_1[] = "TYPE-1";
static const char str_type_off[] = "OFF";
static const char str_type_enforced[] = "ENFORCED";
static const char str_type_relaxed[] = "RELAXED";
struct wl_surface *surface;
pc_player = zalloc(sizeof(struct protected_content_player));
if (pc_player == NULL) {
fprintf(stderr, "failed to allocate memory: %m\n");
return -1;
}
d = display_create(&argc, argv);
if (d == NULL) {
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED;
pc_player->mode = RELAXED;
pc_player->width = WIDTH * 2.0/4.0;
pc_player->height = HEIGHT * 2.0/4.0;
pc_player->x = WIDTH * 1.0/4.0;
pc_player->y = HEIGHT * 1.0/4.0;
pc_player->window = window_create(d);
pc_player->widget = window_frame_create(pc_player->window, pc_player);
pc_player->display = d;
display_set_user_data(d, pc_player);
display_set_global_handler(d, handle_global);
surface = window_get_wl_surface(pc_player->window);
if (pc_player->protection == NULL) {
printf("The content-protection object is NULL\n");
return -1;
}
pc_player->psurface = weston_content_protection_get_protection(pc_player->protection,
surface);
weston_protected_surface_add_listener(pc_player->psurface,
&pc_player_listener,
pc_player);
pc_player->b0 = create_button(pc_player, str_type_0);
pc_player->b1 = create_button(pc_player, str_type_1);
pc_player->off = create_button(pc_player, str_type_off);
pc_player->enforced = create_button(pc_player, str_type_enforced);
pc_player->relaxed = create_button(pc_player, str_type_relaxed);
window_set_title(pc_player->window, "Weston Content Protection");
window_set_appid(pc_player->window,
"org.freedesktop.weston.weston-content-protection");
widget_set_redraw_handler(pc_player->widget, redraw_handler);
widget_set_resize_handler(pc_player->widget, resize_handler);
window_schedule_resize(pc_player->window, WIDTH, HEIGHT);
widget_schedule_redraw(pc_player->b0->widget);
widget_schedule_redraw(pc_player->b1->widget);
widget_schedule_redraw(pc_player->off->widget);
display_run(d);
weston_protected_surface_destroy(pc_player->psurface);
weston_content_protection_destroy(pc_player->protection);
free_pc_player(pc_player);
display_destroy(d);
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -35,7 +35,6 @@
#include <cairo.h>
#include <sys/epoll.h>
#include <stdbool.h>
#include <errno.h>
#include <wayland-client.h>
#include <wayland-cursor.h>
@ -416,9 +415,9 @@ data_source_dnd_finished(void *data, struct wl_data_source *source)
delete_item =
dnd_drag->dnd_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
/* The operation is already finished, we can destroy all
* related data.
*/
/* The operation is already finished, we can destroy all
* related data.
*/
dnd_drag_destroy(dnd_drag, delete_item);
window_schedule_redraw(dnd->window);
}
@ -515,7 +514,7 @@ create_drag_source(struct dnd *dnd,
dnd_drag->drag_surface =
wl_compositor_create_surface(compositor);
if (dnd->self_only || display_get_data_device_manager_version(display) <
if (display_get_data_device_manager_version(display) <
WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) {
/* Data sources version < 3 will not get action
* nor dnd_finished events, as we can't honor
@ -535,10 +534,6 @@ create_drag_source(struct dnd *dnd,
} else {
dnd_drag->data_source =
display_create_data_source(dnd->display);
if (!dnd_drag->data_source) {
fprintf(stderr, "No data device manager\n");
abort();
}
wl_data_source_add_listener(dnd_drag->data_source,
&data_source_listener,
dnd_drag);
@ -546,11 +541,11 @@ create_drag_source(struct dnd *dnd,
flower_mime_type);
wl_data_source_offer(dnd_drag->data_source,
text_mime_type);
}
if (display_get_data_device_manager_version(display) >=
WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) {
wl_data_source_set_actions(dnd_drag->data_source, actions);
}
if (display_get_data_device_manager_version(display) >=
WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) {
wl_data_source_set_actions(dnd_drag->data_source, actions);
}
wl_data_device_start_drag(input_get_data_device(input),
@ -789,8 +784,6 @@ dnd_create(struct display *display)
dnd->window = window_create(display);
dnd->widget = window_frame_create(dnd->window, dnd);
window_set_title(dnd->window, "Wayland Drag and Drop Demo");
window_set_appid(dnd->window,
"org.freedesktop.weston.wayland-drag-and-drop-demo");
dnd->display = display;
dnd->key = 100;
@ -851,8 +844,7 @@ main(int argc, char *argv[])
d = display_create(&argc, argv);
if (d == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}

View file

@ -38,7 +38,7 @@
#include <pango/pangocairo.h>
#include <libweston/config-parser.h>
#include "shared/config-parser.h"
#include "shared/helpers.h"
#include "shared/xalloc.h"
#include "window.h"
@ -49,7 +49,6 @@ struct text_entry {
struct window *window;
char *text;
int active;
bool panel_visible;
uint32_t cursor;
uint32_t anchor;
struct {
@ -77,7 +76,7 @@ struct text_entry {
uint32_t serial;
uint32_t reset_serial;
uint32_t content_purpose;
bool click_to_show;
uint32_t click_to_show;
char *preferred_language;
bool button_pressed;
};
@ -500,10 +499,8 @@ text_input_leave(void *data,
text_entry_commit_and_reset(entry);
entry->active--;
if (!entry->active) {
if (!entry->active)
zwp_text_input_v1_hide_input_panel(text_input);
entry->panel_visible = false;
}
widget_schedule_redraw(entry->widget);
}
@ -580,7 +577,7 @@ data_source_send(void *data,
struct editor *editor = data;
if (write(fd, editor->selected_text, strlen(editor->selected_text) + 1) < 0)
fprintf(stderr, "write failed: %s\n", strerror(errno));
fprintf(stderr, "write failed: %m\n");
close(fd);
}
@ -639,9 +636,6 @@ editor_copy_cut(struct editor *editor, struct input *input, bool cut)
editor->selection =
display_create_data_source(editor->display);
if (!editor->selection)
return;
wl_data_source_offer(editor->selection,
"text/plain;charset=utf-8");
wl_data_source_add_listener(editor->selection,
@ -705,7 +699,6 @@ text_entry_create(struct editor *editor, const char *text)
entry->window = editor->window;
entry->text = strdup(text);
entry->active = 0;
entry->panel_visible = false;
entry->cursor = strlen(text);
entry->anchor = entry->cursor;
entry->text_input =
@ -794,12 +787,7 @@ text_entry_activate(struct text_entry *entry,
struct wl_surface *surface = window_get_wl_surface(entry->window);
if (entry->click_to_show && entry->active) {
entry->panel_visible = !entry->panel_visible;
if (entry->panel_visible)
zwp_text_input_v1_show_input_panel(entry->text_input);
else
zwp_text_input_v1_hide_input_panel(entry->text_input);
zwp_text_input_v1_show_input_panel(entry->text_input);
return;
}
@ -1504,10 +1492,10 @@ global_handler(struct display *display, uint32_t name,
}
/** Display help for command line options, and exit */
static bool opt_help = false;
static uint32_t opt_help = 0;
/** Require a distinct click to show the input panel (virtual keyboard) */
static bool opt_click_to_show = false;
static uint32_t opt_click_to_show = 0;
/** Set a specific (RFC-3066) language. Used for the virtual keyboard, etc. */
static const char *opt_preferred_language = NULL;
@ -1609,8 +1597,7 @@ main(int argc, char *argv[])
text_buffer = read_file(argv[1]);
if (text_buffer == NULL) {
fprintf(stderr, "could not read file '%s': %s\n",
argv[1], strerror(errno));
fprintf(stderr, "could not read file '%s': %m\n", argv[1]);
return -1;
}
}
@ -1619,9 +1606,7 @@ main(int argc, char *argv[])
editor.display = display_create(&argc, argv);
if (editor.display == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
free(text_buffer);
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
@ -1630,8 +1615,6 @@ main(int argc, char *argv[])
if (editor.text_input_manager == NULL) {
fprintf(stderr, "No text input manager global\n");
display_destroy(editor.display);
free(text_buffer);
return -1;
}
@ -1652,7 +1635,6 @@ main(int argc, char *argv[])
editor.selected_text = NULL;
window_set_title(editor.window, "Text Editor");
window_set_appid(editor.window, "org.freedesktop.weston.text-editor");
window_set_key_handler(editor.window, key_handler);
window_set_keyboard_focus_handler(editor.window,
keyboard_focus_handler);

View file

@ -37,8 +37,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <cairo.h>
@ -47,7 +45,6 @@
/** window title */
static char *title = "EventDemo";
static char *appid = "org.freedesktop.weston.eventdemo";
/** window width */
static int width = 500;
@ -56,7 +53,7 @@ static int width = 500;
static int height = 400;
/** set if window has no borders */
static bool noborder = false;
static int noborder = 0;
/** if non-zero, maximum window width */
static int width_max = 0;
@ -65,25 +62,25 @@ static int width_max = 0;
static int height_max = 0;
/** set to log redrawing */
static bool log_redraw = false;
static int log_redraw = 0;
/** set to log resizing */
static bool log_resize = false;
static int log_resize = 0;
/** set to log keyboard focus */
static bool log_focus = false;
static int log_focus = 0;
/** set to log key events */
static bool log_key = false;
static int log_key = 0;
/** set to log button events */
static bool log_button = false;
static int log_button = 0;
/** set to log axis events */
static bool log_axis = false;
static int log_axis = 0;
/** set to log motion events */
static bool log_motion = false;
static int log_motion = 0;
/**
* \struct eventdemo
@ -194,11 +191,10 @@ keyboard_focus_handler(struct window *window,
/**
* \brief CALLBACK function, Wayland informs about key event
* \param window window
* \param input input
* \param time time
* \param key keycode
* \param unicode associated character
* \param state pressed or released
* \param modifiers modifiers: ctrl, alt, meta etc.
* \param data user data associated to the window
*/
static void
@ -351,8 +347,10 @@ axis_discrete_handler(struct widget *widget, struct input *input,
* \param widget widget
* \param input input device that caused the motion event
* \param time time the event happened
* \param x x position relative to the window
* \param y y position relative to the window
* \param x absolute x position
* \param y absolute y position
* \param sx x position relative to the window
* \param sy y position relative to the window
* \param data user data associated to the window
*
* Demonstrates the use of different cursors
@ -399,7 +397,6 @@ eventdemo_create(struct display *d)
} else {
e->widget = window_frame_create(e->window, e);
window_set_title(e->window, title);
window_set_appid(e->window, appid);
}
e->display = d;
@ -513,21 +510,19 @@ main(int argc, char *argv[])
if (!log_redraw && !log_resize && !log_focus && !log_key &&
!log_button && !log_axis && !log_motion)
log_redraw = log_resize = log_focus = log_key =
log_button = log_axis = log_motion = true;
log_button = log_axis = log_motion = 1;
/* Connect to the display and have the arguments parsed */
d = display_create(&argc, argv);
if (d == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
/* Create new eventdemo window */
e = eventdemo_create(d);
if (e == NULL) {
fprintf(stderr, "failed to create eventdemo: %s\n",
strerror(errno));
fprintf(stderr, "failed to create eventdemo: %m\n");
return -1;
}

View file

@ -30,7 +30,6 @@
#include <time.h>
#include <math.h>
#include <cairo.h>
#include <errno.h>
#include <sys/time.h>
#include <linux/input.h>
@ -173,8 +172,7 @@ int main(int argc, char *argv[])
d = display_create(&argc, argv);
if (d == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
@ -187,7 +185,6 @@ int main(int argc, char *argv[])
flower.window = window_create(d);
flower.widget = window_add_widget(flower.window, &flower);
window_set_title(flower.window, "Flower");
window_set_appid(flower.window, "org.freedesktop.weston.flower");
widget_set_resize_handler(flower.widget, resize_handler);
widget_set_redraw_handler(flower.widget, redraw_handler);

View file

@ -30,13 +30,13 @@
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <cairo.h>
#include <linux/input.h>
#include <wayland-client.h>
#include "window.h"
#include <libweston/zalloc.h>
#include "fullscreen-shell-unstable-v1-client-protocol.h"
#include "shared/zalloc.h"
struct fs_output {
struct wl_list link;
@ -47,6 +47,8 @@ struct fullscreen {
struct display *display;
struct window *window;
struct widget *widget;
struct zwp_fullscreen_shell_v1 *fshell;
enum zwp_fullscreen_shell_v1_present_method present_method;
int width, height;
int fullscreen;
float pointer_x, pointer_y;
@ -77,7 +79,7 @@ draw_string(cairo_t *cr,
cairo_save(cr);
cairo_select_font_face(cr, "sans-serif",
cairo_select_font_face(cr, "sans",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, 14);
@ -119,6 +121,7 @@ redraw_handler(struct widget *widget, void *data)
cairo_t *cr;
int i;
double x, y, border;
const char *method_name[] = { "default", "center", "zoom", "zoom_crop", "stretch"};
surface = window_get_surface(fullscreen->window);
if (surface == NULL ||
@ -144,17 +147,33 @@ redraw_handler(struct widget *widget, void *data)
allocation.y + 25);
cairo_set_source_rgb(cr, 1, 1, 1);
draw_string(cr,
"Surface size: %d, %d\n"
"Scale: %d, transform: %d\n"
"Pointer: %f,%f\n"
"Fullscreen: %d\n"
"Keys: (s)cale, (t)ransform, si(z)e, (f)ullscreen, (q)uit\n",
fullscreen->width, fullscreen->height,
window_get_buffer_scale (fullscreen->window),
window_get_buffer_transform (fullscreen->window),
fullscreen->pointer_x, fullscreen->pointer_y,
fullscreen->fullscreen);
if (fullscreen->fshell) {
draw_string(cr,
"Surface size: %d, %d\n"
"Scale: %d, transform: %d\n"
"Pointer: %f,%f\n"
"Output: %s, present method: %s\n"
"Keys: (s)cale, (t)ransform, si(z)e, (m)ethod,\n"
" (o)utput, modes(w)itch, (q)uit\n",
fullscreen->width, fullscreen->height,
window_get_buffer_scale (fullscreen->window),
window_get_buffer_transform (fullscreen->window),
fullscreen->pointer_x, fullscreen->pointer_y,
method_name[fullscreen->present_method],
fullscreen->current_output ? output_get_model(fullscreen->current_output->output): "null");
} else {
draw_string(cr,
"Surface size: %d, %d\n"
"Scale: %d, transform: %d\n"
"Pointer: %f,%f\n"
"Fullscreen: %d\n"
"Keys: (s)cale, (t)ransform, si(z)e, (f)ullscreen, (q)uit\n",
fullscreen->width, fullscreen->height,
window_get_buffer_scale (fullscreen->window),
window_get_buffer_transform (fullscreen->window),
fullscreen->pointer_x, fullscreen->pointer_y,
fullscreen->fullscreen);
}
y = 100;
i = 0;
@ -233,6 +252,8 @@ key_handler(struct window *window, struct input *input, uint32_t time,
struct fullscreen *fullscreen = data;
int transform, scale;
static int current_size = 0;
struct fs_output *fsout;
struct wl_output *wl_output;
int widths[] = { 640, 320, 800, 400 };
int heights[] = { 480, 240, 600, 300 };
@ -258,9 +279,6 @@ key_handler(struct window *window, struct input *input, uint32_t time,
break;
case XKB_KEY_z:
if (fullscreen->fullscreen)
break;
current_size = (current_size + 1) % 4;
fullscreen->width = widths[current_size];
fullscreen->height = heights[current_size];
@ -268,7 +286,69 @@ key_handler(struct window *window, struct input *input, uint32_t time,
fullscreen->width, fullscreen->height);
break;
case XKB_KEY_m:
if (!fullscreen->fshell)
break;
wl_output = NULL;
if (fullscreen->current_output)
wl_output = output_get_wl_output(fullscreen->current_output->output);
fullscreen->present_method = (fullscreen->present_method + 1) % 5;
zwp_fullscreen_shell_v1_present_surface(fullscreen->fshell,
window_get_wl_surface(fullscreen->window),
fullscreen->present_method,
wl_output);
window_schedule_redraw(window);
break;
case XKB_KEY_o:
if (!fullscreen->fshell)
break;
fsout = fullscreen->current_output;
wl_output = fsout ? output_get_wl_output(fsout->output) : NULL;
/* Clear the current presentation */
zwp_fullscreen_shell_v1_present_surface(fullscreen->fshell, NULL,
0, wl_output);
if (fullscreen->current_output) {
if (fullscreen->current_output->link.next == &fullscreen->output_list)
fsout = NULL;
else
fsout = wl_container_of(fullscreen->current_output->link.next,
fsout, link);
} else {
fsout = wl_container_of(fullscreen->output_list.next,
fsout, link);
}
fullscreen->current_output = fsout;
wl_output = fsout ? output_get_wl_output(fsout->output) : NULL;
zwp_fullscreen_shell_v1_present_surface(fullscreen->fshell,
window_get_wl_surface(fullscreen->window),
fullscreen->present_method,
wl_output);
window_schedule_redraw(window);
break;
case XKB_KEY_w:
if (!fullscreen->fshell || !fullscreen->current_output)
break;
wl_output = NULL;
if (fullscreen->current_output)
wl_output = output_get_wl_output(fullscreen->current_output->output);
zwp_fullscreen_shell_mode_feedback_v1_destroy(
zwp_fullscreen_shell_v1_present_surface_for_mode(fullscreen->fshell,
window_get_wl_surface(fullscreen->window),
wl_output, 0));
window_schedule_redraw(window);
break;
case XKB_KEY_f:
if (fullscreen->fshell)
break;
fullscreen->fullscreen ^= 1;
window_set_fullscreen(window, fullscreen->fullscreen);
break;
@ -340,13 +420,31 @@ touch_handler(struct widget *widget, struct input *input,
window_move(fullscreen->window, input, display_get_serial(fullscreen->display));
}
static void
fshell_capability_handler(void *data, struct zwp_fullscreen_shell_v1 *fshell,
uint32_t capability)
{
struct fullscreen *fullscreen = data;
switch (capability) {
case ZWP_FULLSCREEN_SHELL_V1_CAPABILITY_CURSOR_PLANE:
fullscreen->draw_cursor = 0;
break;
default:
break;
}
}
struct zwp_fullscreen_shell_v1_listener fullscreen_shell_listener = {
fshell_capability_handler
};
static void
usage(int error_code)
{
fprintf(stderr, "Usage: fullscreen [OPTIONS]\n\n"
" -w <width>\tSet window width to <width>\n"
" -h <height>\tSet window height to <height>\n"
" -f\t\tMap window as fullscreen\n"
" --help\tShow this help text\n\n");
exit(error_code);
@ -372,6 +470,22 @@ output_handler(struct output *output, void *data)
wl_list_insert(&fullscreen->output_list, &fsout->link);
}
static void
global_handler(struct display *display, uint32_t id, const char *interface,
uint32_t version, void *data)
{
struct fullscreen *fullscreen = data;
if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
fullscreen->fshell = display_bind(display, id,
&zwp_fullscreen_shell_v1_interface,
1);
zwp_fullscreen_shell_v1_add_listener(fullscreen->fshell,
&fullscreen_shell_listener,
fullscreen);
}
}
int main(int argc, char *argv[])
{
struct fullscreen fullscreen;
@ -381,6 +495,7 @@ int main(int argc, char *argv[])
fullscreen.width = 640;
fullscreen.height = 480;
fullscreen.fullscreen = 0;
fullscreen.present_method = ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT;
wl_list_init(&fullscreen.output_list);
fullscreen.current_output = NULL;
@ -395,8 +510,6 @@ int main(int argc, char *argv[])
usage(EXIT_FAILURE);
fullscreen.height = atol(argv[i]);
} else if (strcmp(argv[i], "-f") == 0) {
fullscreen.fullscreen = 1;
} else if (strcmp(argv[i], "--help") == 0)
usage(EXIT_SUCCESS);
else
@ -405,23 +518,33 @@ int main(int argc, char *argv[])
d = display_create(&argc, argv);
if (d == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
fullscreen.display = d;
fullscreen.fshell = NULL;
display_set_user_data(fullscreen.display, &fullscreen);
display_set_global_handler(fullscreen.display, global_handler);
display_set_output_configure_handler(fullscreen.display, output_handler);
fullscreen.window = window_create(d);
fullscreen.draw_cursor = 0;
if (fullscreen.fshell) {
fullscreen.window = window_create_custom(d);
zwp_fullscreen_shell_v1_present_surface(fullscreen.fshell,
window_get_wl_surface(fullscreen.window),
fullscreen.present_method,
NULL);
/* If we get the CURSOR_PLANE capability, we'll change this */
fullscreen.draw_cursor = 1;
} else {
fullscreen.window = window_create(d);
fullscreen.draw_cursor = 0;
}
fullscreen.widget =
window_add_widget(fullscreen.window, &fullscreen);
window_set_title(fullscreen.window, "Fullscreen");
window_set_appid(fullscreen.window, "org.freedesktop.weston.fullscreen");
widget_set_transparent(fullscreen.widget, 0);
@ -437,8 +560,6 @@ int main(int argc, char *argv[])
window_set_fullscreen_handler(fullscreen.window, fullscreen_handler);
window_set_user_data(fullscreen.window, &fullscreen);
if (fullscreen.fullscreen)
window_set_fullscreen(fullscreen.window, fullscreen.fullscreen);
/* Hack to set minimum allocation so we can shrink later */
window_schedule_resize(fullscreen.window,
1, 1);

501
clients/gears.c Normal file
View file

@ -0,0 +1,501 @@
/*
* Copyright © 2008 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 <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <GL/gl.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <linux/input.h>
#include <wayland-client.h>
#include "window.h"
struct gears {
struct window *window;
struct widget *widget;
struct display *d;
EGLDisplay display;
EGLDisplay config;
EGLContext context;
GLfloat angle;
struct {
GLfloat rotx;
GLfloat roty;
} view;
int button_down;
int last_x, last_y;
GLint gear_list[3];
int fullscreen;
int frames;
uint32_t last_fps;
};
struct gear_template {
GLfloat material[4];
GLfloat inner_radius;
GLfloat outer_radius;
GLfloat width;
GLint teeth;
GLfloat tooth_depth;
};
static const struct gear_template gear_templates[] = {
{ { 0.8, 0.1, 0.0, 1.0 }, 1.0, 4.0, 1.0, 20, 0.7 },
{ { 0.0, 0.8, 0.2, 1.0 }, 0.5, 2.0, 2.0, 10, 0.7 },
{ { 0.2, 0.2, 1.0, 1.0 }, 1.3, 2.0, 0.5, 10, 0.7 },
};
static GLfloat light_pos[4] = {5.0, 5.0, 10.0, 0.0};
static void die(const char *msg)
{
fprintf(stderr, "%s", msg);
exit(EXIT_FAILURE);
}
static void
make_gear(const struct gear_template *t)
{
GLint i;
GLfloat r0, r1, r2;
GLfloat angle, da;
GLfloat u, v, len;
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, t->material);
r0 = t->inner_radius;
r1 = t->outer_radius - t->tooth_depth / 2.0;
r2 = t->outer_radius + t->tooth_depth / 2.0;
da = 2.0 * M_PI / t->teeth / 4.0;
glShadeModel(GL_FLAT);
glNormal3f(0.0, 0.0, 1.0);
/* draw front face */
glBegin(GL_QUAD_STRIP);
for (i = 0; i <= t->teeth; i++) {
angle = i * 2.0 * M_PI / t->teeth;
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
if (i < t->teeth) {
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
}
}
glEnd();
/* draw front sides of teeth */
glBegin(GL_QUADS);
da = 2.0 * M_PI / t->teeth / 4.0;
for (i = 0; i < t->teeth; i++) {
angle = i * 2.0 * M_PI / t->teeth;
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5);
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5);
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
}
glEnd();
glNormal3f(0.0, 0.0, -1.0);
/* draw back face */
glBegin(GL_QUAD_STRIP);
for (i = 0; i <= t->teeth; i++) {
angle = i * 2.0 * M_PI / t->teeth;
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
if (i < t->teeth) {
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
}
}
glEnd();
/* draw back sides of teeth */
glBegin(GL_QUADS);
da = 2.0 * M_PI / t->teeth / 4.0;
for (i = 0; i < t->teeth; i++) {
angle = i * 2.0 * M_PI / t->teeth;
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5);
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5);
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
}
glEnd();
/* draw outward faces of teeth */
glBegin(GL_QUAD_STRIP);
for (i = 0; i < t->teeth; i++) {
angle = i * 2.0 * M_PI / t->teeth;
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
u = r2 * cos(angle + da) - r1 * cos(angle);
v = r2 * sin(angle + da) - r1 * sin(angle);
len = sqrt(u * u + v * v);
u /= len;
v /= len;
glNormal3f(v, -u, 0.0);
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5);
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5);
glNormal3f(cos(angle), sin(angle), 0.0);
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5);
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5);
u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
glNormal3f(v, -u, 0.0);
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
glNormal3f(cos(angle), sin(angle), 0.0);
}
glVertex3f(r1 * cos(0), r1 * sin(0), t->width * 0.5);
glVertex3f(r1 * cos(0), r1 * sin(0), -t->width * 0.5);
glEnd();
glShadeModel(GL_SMOOTH);
/* draw inside radius cylinder */
glBegin(GL_QUAD_STRIP);
for (i = 0; i <= t->teeth; i++) {
angle = i * 2.0 * M_PI / t->teeth;
glNormal3f(-cos(angle), -sin(angle), 0.0);
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
}
glEnd();
}
static void
update_fps(struct gears *gears, uint32_t time)
{
long diff_ms;
static bool first_call = true;
if (first_call) {
gears->last_fps = time;
first_call = false;
} else
gears->frames++;
diff_ms = time - gears->last_fps;
if (diff_ms > 5000) {
float seconds = diff_ms / 1000.0;
float fps = gears->frames / seconds;
printf("%d frames in %6.3f seconds = %6.3f FPS\n", gears->frames, seconds, fps);
fflush(stdout);
gears->frames = 0;
gears->last_fps = time;
}
}
static void
frame_callback(void *data, struct wl_callback *callback, uint32_t time)
{
struct gears *gears = data;
update_fps(gears, time);
gears->angle = (GLfloat) (time % 8192) * 360 / 8192.0;
window_schedule_redraw(gears->window);
if (callback)
wl_callback_destroy(callback);
}
static const struct wl_callback_listener listener = {
frame_callback
};
static int
motion_handler(struct widget *widget, struct input *input,
uint32_t time, float x, float y, void *data)
{
struct gears *gears = data;
int offset_x, offset_y;
float step = 0.5;
if (gears->button_down) {
offset_x = x - gears->last_x;
offset_y = y - gears->last_y;
gears->last_x = x;
gears->last_y = y;
gears->view.roty += offset_x * step;
gears->view.rotx += offset_y * step;
if (gears->view.roty >= 360)
gears->view.roty = gears->view.roty - 360;
if (gears->view.roty <= 0)
gears->view.roty = gears->view.roty + 360;
if (gears->view.rotx >= 360)
gears->view.rotx = gears->view.rotx - 360;
if (gears->view.rotx <= 0)
gears->view.rotx = gears->view.rotx + 360;
}
return CURSOR_LEFT_PTR;
}
static void
button_handler(struct widget *widget, struct input *input,
uint32_t time, uint32_t button,
enum wl_pointer_button_state state, void *data)
{
struct gears *gears = data;
if (button == BTN_LEFT) {
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
gears->button_down = 1;
input_get_position(input,
&gears->last_x, &gears->last_y);
} else {
gears->button_down = 0;
}
}
}
static void
redraw_handler(struct widget *widget, void *data)
{
struct rectangle window_allocation;
struct rectangle allocation;
struct wl_callback *callback;
struct gears *gears = data;
widget_get_allocation(gears->widget, &allocation);
window_get_allocation(gears->window, &window_allocation);
if (display_acquire_window_surface(gears->d,
gears->window,
gears->context) < 0) {
die("Unable to acquire window surface, "
"compiled without cairo-egl?\n");
}
glViewport(allocation.x,
window_allocation.height - allocation.height - allocation.y,
allocation.width, allocation.height);
glScissor(allocation.x,
window_allocation.height - allocation.height - allocation.y,
allocation.width, allocation.height);
glEnable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glTranslatef(0.0, 0.0, -50);
glRotatef(gears->view.rotx, 1.0, 0.0, 0.0);
glRotatef(gears->view.roty, 0.0, 1.0, 0.0);
glPushMatrix();
glTranslatef(-3.0, -2.0, 0.0);
glRotatef(gears->angle, 0.0, 0.0, 1.0);
glCallList(gears->gear_list[0]);
glPopMatrix();
glPushMatrix();
glTranslatef(3.1, -2.0, 0.0);
glRotatef(-2.0 * gears->angle - 9.0, 0.0, 0.0, 1.0);
glCallList(gears->gear_list[1]);
glPopMatrix();
glPushMatrix();
glTranslatef(-3.1, 4.2, 0.0);
glRotatef(-2.0 * gears->angle - 25.0, 0.0, 0.0, 1.0);
glCallList(gears->gear_list[2]);
glPopMatrix();
glPopMatrix();
glFlush();
display_release_window_surface(gears->d, gears->window);
callback = wl_surface_frame(window_get_wl_surface(gears->window));
wl_callback_add_listener(callback, &listener, gears);
}
static void
resize_handler(struct widget *widget,
int32_t width, int32_t height, void *data)
{
struct gears *gears = data;
int32_t size, big, small;
/* Constrain child size to be square and at least 300x300 */
if (width < height) {
small = width;
big = height;
} else {
small = height;
big = width;
}
if (gears->fullscreen)
size = small;
else
size = big;
widget_set_size(gears->widget, size, size);
}
static void
keyboard_focus_handler(struct window *window,
struct input *device, void *data)
{
window_schedule_redraw(window);
}
static void
fullscreen_handler(struct window *window, void *data)
{
struct gears *gears = data;
gears->fullscreen ^= 1;
window_set_fullscreen(window, gears->fullscreen);
}
static struct gears *
gears_create(struct display *display)
{
const int width = 450, height = 500;
struct gears *gears;
int i;
gears = zalloc(sizeof *gears);
gears->d = display;
gears->window = window_create(display);
gears->widget = window_frame_create(gears->window, gears);
window_set_title(gears->window, "Wayland Gears");
gears->display = display_get_egl_display(gears->d);
if (gears->display == NULL)
die("failed to create egl display\n");
eglBindAPI(EGL_OPENGL_API);
gears->config = display_get_argb_egl_config(gears->d);
gears->context = eglCreateContext(gears->display, gears->config,
EGL_NO_CONTEXT, NULL);
if (gears->context == NULL)
die("failed to create context\n");
if (!eglMakeCurrent(gears->display, NULL, NULL, gears->context))
die("failed to make context current\n");
for (i = 0; i < 3; i++) {
gears->gear_list[i] = glGenLists(1);
glNewList(gears->gear_list[i], GL_COMPILE);
make_gear(&gear_templates[i]);
glEndList();
}
gears->button_down = 0;
gears->last_x = 0;
gears->last_y = 0;
gears->view.rotx = 20.0;
gears->view.roty = 30.0;
printf("Warning: FPS count is limited by the wayland compositor or monitor refresh rate\n");
glEnable(GL_NORMALIZE);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 200.0);
glMatrixMode(GL_MODELVIEW);
glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glClearColor(0, 0, 0, 0.92);
window_set_user_data(gears->window, gears);
widget_set_resize_handler(gears->widget, resize_handler);
widget_set_redraw_handler(gears->widget, redraw_handler);
widget_set_button_handler(gears->widget, button_handler);
widget_set_motion_handler(gears->widget, motion_handler);
window_set_keyboard_focus_handler(gears->window,
keyboard_focus_handler);
window_set_fullscreen_handler(gears->window, fullscreen_handler);
window_schedule_resize(gears->window, width, height);
return gears;
}
static void
gears_destroy(struct gears *gears)
{
widget_destroy(gears->widget);
window_destroy(gears->window);
free(gears);
}
int main(int argc, char *argv[])
{
struct display *d;
struct gears *gears;
d = display_create(&argc, argv);
if (d == NULL) {
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
gears = gears_create(d);
display_run(d);
gears_destroy(gears);
display_destroy(d);
return 0;
}

View file

@ -36,32 +36,16 @@
#include <time.h>
#include <cairo.h>
#include <assert.h>
#include <errno.h>
#include <linux/input.h>
#include <wayland-client.h>
#include "window.h"
#include "shared/cairo-util.h"
#include "shared/helpers.h"
#include "shared/image-loader.h"
bool verbose;
#define verbose_print(...) do { \
if (verbose) \
fprintf(stderr, __VA_ARGS__); \
} while (0)
struct image {
struct window *window;
/* Decorations, buttons, etc. */
struct widget *frame_widget;
/* Where we draw the image content. */
struct widget *image_widget;
struct widget *widget;
struct display *display;
char *filename;
cairo_surface_t *image;
@ -79,39 +63,6 @@ struct image {
cairo_matrix_t matrix;
};
struct cli_render_intent_option {
int render_intent;
const char *cli_option;
};
static const struct cli_render_intent_option
cli_ri_table[] = {
{
.render_intent = -1,
.cli_option = "off",
},
{
.render_intent = RENDER_INTENT_PERCEPTUAL,
.cli_option = "per",
},
{
.render_intent = RENDER_INTENT_RELATIVE,
.cli_option = "rel",
},
{
.render_intent = RENDER_INTENT_RELATIVE_BPC,
.cli_option = "rel-bpc",
},
{
.render_intent = RENDER_INTENT_SATURATION,
.cli_option = "sat",
},
{
.render_intent = RENDER_INTENT_ABSOLUTE,
.cli_option = "abs",
},
};
static double
get_scale(struct image *image)
{
@ -130,7 +81,7 @@ clamp_view(struct image *image)
sw = image->width * scale;
sh = image->height * scale;
widget_get_allocation(image->frame_widget, &allocation);
widget_get_allocation(image->widget, &allocation);
if (sw < allocation.width) {
image->matrix.x0 =
@ -154,45 +105,27 @@ clamp_view(struct image *image)
}
static void
frame_redraw_handler(struct widget *widget, void *data)
{
struct rectangle allocation;
cairo_t *cr;
widget_get_allocation(widget, &allocation);
cr = widget_cairo_create(widget);
cairo_rectangle(cr, allocation.x, allocation.y,
allocation.width, allocation.height);
cairo_set_source_rgba(cr, 0, 0, 0, 1);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_fill(cr);
cairo_destroy(cr);
}
static void
frame_resize_handler(struct widget *widget,
int32_t width, int32_t height, void *data)
{
struct image *image = data;
clamp_view(image);
}
static void
image_redraw_handler(struct widget *widget, void *data)
redraw_handler(struct widget *widget, void *data)
{
struct image *image = data;
struct rectangle allocation;
cairo_t *cr;
cairo_surface_t *surface;
double width, height, doc_aspect, window_aspect, scale;
cairo_matrix_t matrix;
cairo_matrix_t translate;
widget_get_allocation(widget, &allocation);
cr = widget_cairo_create(widget);
surface = window_get_surface(image->window);
cr = cairo_create(surface);
widget_get_allocation(image->widget, &allocation);
cairo_rectangle(cr, allocation.x, allocation.y,
allocation.width, allocation.height);
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_clip(cr);
cairo_push_group(cr);
cairo_translate(cr, allocation.x, allocation.y);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_rgba(cr, 0, 0, 0, 1);
cairo_paint(cr);
if (!image->initialized) {
@ -214,36 +147,49 @@ image_redraw_handler(struct widget *widget, void *data)
clamp_view(image);
}
cairo_set_matrix(cr, &image->matrix);
matrix = image->matrix;
cairo_matrix_init_translate(&translate, allocation.x, allocation.y);
cairo_matrix_multiply(&matrix, &matrix, &translate);
cairo_set_matrix(cr, &matrix);
cairo_set_source_surface(cr, image->image, 0, 0);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
cairo_paint(cr);
cairo_pop_group_to_source(cr);
cairo_paint(cr);
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
static void
image_resize_handler(struct widget *widget,
int32_t width, int32_t height, void *data)
resize_handler(struct widget *widget,
int32_t width, int32_t height, void *data)
{
struct image *image = data;
struct rectangle allocation;
widget_get_allocation(image->frame_widget, &allocation);
clamp_view(image);
}
widget_set_allocation(widget,
allocation.x, allocation.y,
allocation.width, allocation.height);
static void
keyboard_focus_handler(struct window *window,
struct input *device, void *data)
{
struct image *image = data;
window_schedule_redraw(image->window);
}
static int
image_enter_handler(struct widget *widget,
struct input *input,
float x, float y, void *data)
enter_handler(struct widget *widget,
struct input *input,
float x, float y, void *data)
{
struct image *image = data;
struct rectangle allocation;
widget_get_allocation(widget, &allocation);
widget_get_allocation(image->widget, &allocation);
x -= allocation.x;
y -= allocation.y;
@ -268,14 +214,14 @@ move_viewport(struct image *image, double dx, double dy)
}
static int
image_motion_handler(struct widget *widget,
struct input *input, uint32_t time,
float x, float y, void *data)
motion_handler(struct widget *widget,
struct input *input, uint32_t time,
float x, float y, void *data)
{
struct image *image = data;
struct rectangle allocation;
widget_get_allocation(widget, &allocation);
widget_get_allocation(image->widget, &allocation);
x -= allocation.x;
y -= allocation.y;
@ -290,11 +236,11 @@ image_motion_handler(struct widget *widget,
}
static void
image_button_handler(struct widget *widget,
struct input *input, uint32_t time,
uint32_t button,
enum wl_pointer_button_state state,
void *data)
button_handler(struct widget *widget,
struct input *input, uint32_t time,
uint32_t button,
enum wl_pointer_button_state state,
void *data)
{
struct image *image = data;
@ -332,35 +278,6 @@ zoom(struct image *image, double scale)
clamp_view(image);
}
static void
image_axis_handler(struct widget *widget, struct input *input, uint32_t time,
uint32_t axis, wl_fixed_t value, void *data)
{
struct image *image = data;
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL &&
input_get_modifiers(input) == MOD_CONTROL_MASK) {
/* set zoom level to 2% per 10 axis units */
zoom(image, (1.0 - wl_fixed_to_double(value) / 500.0));
window_schedule_redraw(image->window);
} else if (input_get_modifiers(input) == 0) {
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
move_viewport(image, 0, wl_fixed_to_double(value));
else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
move_viewport(image, wl_fixed_to_double(value), 0);
}
}
static void
keyboard_focus_handler(struct window *window,
struct input *device, void *data)
{
struct image *image = data;
window_schedule_redraw(image->window);
}
static void
key_handler(struct window *window, struct input *input, uint32_t time,
uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
@ -392,6 +309,26 @@ key_handler(struct window *window, struct input *input, uint32_t time,
}
}
static void
axis_handler(struct widget *widget, struct input *input, uint32_t time,
uint32_t axis, wl_fixed_t value, void *data)
{
struct image *image = data;
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL &&
input_get_modifiers(input) == MOD_CONTROL_MASK) {
/* set zoom level to 2% per 10 axis units */
zoom(image, (1.0 - wl_fixed_to_double(value) / 500.0));
window_schedule_redraw(image->window);
} else if (input_get_modifiers(input) == 0) {
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
move_viewport(image, 0, wl_fixed_to_double(value));
else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
move_viewport(image, wl_fixed_to_double(value), 0);
}
}
static void
fullscreen_handler(struct window *window, void *data)
{
@ -411,40 +348,18 @@ close_handler(void *data)
if (*image->image_counter == 0)
display_exit(image->display);
cairo_surface_destroy(image->image);
free(image->filename);
widget_destroy(image->image_widget);
widget_destroy(image->frame_widget);
widget_destroy(image->widget);
window_destroy(image->window);
free(image);
}
static void
set_empty_input_region(struct widget *widget, struct display *display)
{
struct wl_compositor *compositor;
struct wl_surface *surface;
struct wl_region *region;
compositor = display_get_compositor(display);
surface = widget_get_wl_surface(widget);
region = wl_compositor_create_region(compositor);
wl_surface_set_input_region(surface, region);
wl_region_destroy(region);
}
static struct image *
image_create(struct display *display, const char *filename,
int *image_counter, int render_intent)
int *image_counter)
{
struct image *image;
struct weston_image *wimage;
char *b, *copy, title[512];
char *err_msg;
bool ret;
image = zalloc(sizeof *image);
if (image == NULL)
@ -465,166 +380,51 @@ image_create(struct display *display, const char *filename,
}
image->window = window_create(display);
image->widget = window_frame_create(image->window, image);
window_set_title(image->window, title);
window_set_appid(image->window, "org.freedesktop.weston.wayland-image");
image->display = display;
image->image_counter = image_counter;
*image_counter += 1;
image->initialized = false;
window_set_user_data(image->window, image);
widget_set_redraw_handler(image->widget, redraw_handler);
widget_set_resize_handler(image->widget, resize_handler);
window_set_keyboard_focus_handler(image->window,
keyboard_focus_handler);
window_set_fullscreen_handler(image->window, fullscreen_handler);
window_set_close_handler(image->window, close_handler);
widget_set_enter_handler(image->widget, enter_handler);
widget_set_motion_handler(image->widget, motion_handler);
widget_set_button_handler(image->widget, button_handler);
widget_set_axis_handler(image->widget, axis_handler);
window_set_key_handler(image->window, key_handler);
image->frame_widget = window_frame_create(image->window, image);
widget_set_redraw_handler(image->frame_widget, frame_redraw_handler);
widget_set_resize_handler(image->frame_widget, frame_resize_handler);
image->image_widget = window_add_subsurface(image->window, image,
SUBSURFACE_SYNCHRONIZED);
/* We set the input region of the subsurface where the image is draw as
* NULL, as the input region of the parent surface is automatically set
* by the toytoolkit. But as the window that finds the widget in a
* certain (x, y) position looks for surfaces that are on top first, it
* will call the image_widget handlers for input related stuff. */
set_empty_input_region(image->image_widget, display);
widget_set_redraw_handler(image->image_widget, image_redraw_handler);
widget_set_resize_handler(image->image_widget, image_resize_handler);
widget_set_enter_handler(image->image_widget, image_enter_handler);
widget_set_motion_handler(image->image_widget, image_motion_handler);
widget_set_button_handler(image->image_widget, image_button_handler);
widget_set_axis_handler(image->image_widget, image_axis_handler);
wimage = load_cairo_surface_get_user_data(image->image);
assert(wimage);
if (wimage->icc_profile_data && render_intent != -1) {
verbose_print("Image contains ICC file embedded, let's try to use the Wayland\n" \
"color-management protocol to set the surface image description\n" \
"using this ICC file.\n");
ret = widget_set_image_description_icc(image->image_widget,
wimage->icc_profile_data->fd,
wimage->icc_profile_data->length,
wimage->icc_profile_data->offset,
render_intent, &err_msg);
if (ret) {
verbose_print("Successfully set surface image description " \
"using ICC file.\n");
} else {
fprintf(stderr, "Failed to set surface image description:\n%s\n",
err_msg);
free(err_msg);
}
}
/* TODO: investigate if/how to get colorimetry info from the
* PNG/JPEG/etc image. Then use that to create a parametric image
* description and set it as the widget image description. Also, if
* clients do not enforce us to avoid setting an image description (i.e.
* render_intent != -1) but no colorimetry data is present, we can
* create a sRGB image description (through parameters) and set it as
* the image description to use. For now Weston do not support creating
* image description from parameters, that's why we've added only the
* code above that depends on ICC profiles. */
widget_schedule_resize(image->frame_widget, 500, 400);
widget_schedule_resize(image->widget, 500, 400);
return image;
}
static void
print_usage(const char *program_name)
{
const struct render_intent_info *intent_info;
const char *desc;
unsigned int i;
fprintf(stderr, "Usage:\n %s [OPTIONS] [FILENAME0] [FILENAME1] ...\n\n" \
"Options:\n", program_name);
fprintf(stderr, "-v or --verbose to print verbose log information.\n\n");
fprintf(stderr, "-h or --help to open this HELP dialogue.\n\n");
fprintf(stderr, "-r or --rendering-intent to choose the color-management rendering intent.\n\n " \
"The rendering intent is used when an image file has colorimetry data embedded,\n " \
"and the compositor should present this image taking this into account. We use\n " \
"the Wayland color-management protocol extension to set the image description\n " \
"and a rendering intent, which is up to the client to decide. This is optional,\n " \
"and if nothing set we'll use 'perceptual'. Supported values:\n\n");
for (i = 0; i < ARRAY_LENGTH(cli_ri_table); i++) {
/* "off" option does not have a corresponding render_intent_info
* object from which we would be able to get the description. */
intent_info = render_intent_info_from(cli_ri_table[i].render_intent);
if (intent_info)
desc = intent_info->desc;
else
desc = "No render intent (do not set image description)";
fprintf(stderr, " %s: %s.\n", cli_ri_table[i].cli_option, desc);
}
}
static int
get_render_intent(int *render_intent, const char *opt_rendering_intent)
{
unsigned int i;
/* The default, if client does not set anything. */
if (!opt_rendering_intent) {
*render_intent = RENDER_INTENT_PERCEPTUAL;
return 0;
}
for (i = 0; i < ARRAY_LENGTH(cli_ri_table); i++) {
if (strcmp(opt_rendering_intent, cli_ri_table[i].cli_option) == 0) {
*render_intent = cli_ri_table[i].render_intent;
return 0;
}
}
fprintf(stderr, "Error: unknown rendering intent: %s.\n\n",
opt_rendering_intent);
return -1;
}
int
main(int argc, char *argv[])
{
struct display *d;
int i;
int image_counter = 0;
int render_intent;
bool opt_help = false;
char *opt_rendering_intent = NULL;
struct weston_option cli_options[] = {
{ WESTON_OPTION_BOOLEAN, "help", 'h', &opt_help },
{ WESTON_OPTION_BOOLEAN, "verbose", 'v', &verbose },
{ WESTON_OPTION_STRING, "rendering-intent", 'r', &opt_rendering_intent },
};
parse_options(cli_options, ARRAY_LENGTH(cli_options), &argc, argv);
if (argc <= 1 || opt_help ||
get_render_intent(&render_intent, opt_rendering_intent) < 0) {
free(opt_rendering_intent);
print_usage(argv[0]);
if (argc <= 1 || argv[1][0]=='-') {
printf("Usage: %s image...\n", argv[0]);
return 1;
}
free(opt_rendering_intent);
d = display_create(&argc, argv);
if (d == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
for (i = 1; i < argc; i++)
image_create(d, argv[i], &image_counter, render_intent);
image_create(d, argv[i], &image_counter);
if (image_counter > 0)
display_run(d);

View file

@ -35,16 +35,14 @@
#include <signal.h>
#include <sys/mman.h>
#include <getopt.h>
#include <errno.h>
#include <wayland-cursor.h>
#include <wayland-client-protocol.h>
#include "shared/cairo-util.h"
#include <libweston/config-parser.h>
#include "shared/config-parser.h"
#include "shared/helpers.h"
#include "shared/os-compatibility.h"
#include "shared/xalloc.h"
#include <libweston/zalloc.h>
#include "shared/file-util.h"
#include "shared/zalloc.h"
#include "ivi-application-client-protocol.h"
#include "ivi-hmi-controller-client-protocol.h"
@ -143,8 +141,6 @@ hmi_homescreen_launcher {
uint32_t workspace_id;
char *icon;
char *path;
char **argv;
struct wl_list link;
};
@ -310,10 +306,12 @@ launcher_button(uint32_t surfaceId, struct wl_list *launcher_list)
struct hmi_homescreen_launcher *launcher = NULL;
wl_list_for_each(launcher, launcher_list, link) {
char *argv[] = { NULL };
if (surfaceId != launcher->icon_surface_id)
continue;
execute_process(launcher->path, launcher->argv);
execute_process(launcher->path, argv);
return 1;
}
@ -807,8 +805,8 @@ createShmBuffer(struct wlContextStruct *p_wlCtx)
fd = os_create_anonymous_file(size);
if (fd < 0) {
fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
size, strerror(errno));
fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
size);
return ;
}
@ -816,7 +814,7 @@ createShmBuffer(struct wlContextStruct *p_wlCtx)
mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == p_wlCtx->data) {
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
fprintf(stderr, "mmap failed: %m\n");
close(fd);
return;
}
@ -829,8 +827,7 @@ createShmBuffer(struct wlContextStruct *p_wlCtx)
WL_SHM_FORMAT_ARGB8888);
if (NULL == p_wlCtx->wlBuffer) {
fprintf(stderr, "wl_shm_create_buffer failed: %s\n",
strerror(errno));
fprintf(stderr, "wl_shm_create_buffer failed: %m\n");
close(fd);
return;
}
@ -1065,32 +1062,6 @@ create_launchers(struct wlContextCommon *cmm, struct wl_list *launcher_list)
free(launchers);
}
static char **
parse_command(char *str)
{
char **argv;
char *saveptr;
char *token;
int i;
int count = 1;
for (i = 1; str[i]; i++)
if (str[i] == ' ' && str[i-1] != ' ')
count++;
argv = xcalloc(count + 1, sizeof(char*));
i = 0;
token = strtok_r(str, " ", &saveptr);
while (token != NULL) {
argv[i++] = token;
token = strtok_r(NULL, " ", &saveptr);
}
return argv;
}
/**
* Internal method to read out weston.ini to get configuration
*/
@ -1105,7 +1076,6 @@ hmi_homescreen_setting_create(void)
const char *name = NULL;
uint32_t workspace_layer_id;
uint32_t icon_surface_id = 0;
char *filename;
wl_list_init(&setting->workspace_list);
wl_list_init(&setting->launcher_list);
@ -1125,65 +1095,51 @@ hmi_homescreen_setting_create(void)
weston_config_section_get_uint(
shellSection, "workspace-layer-id", &workspace_layer_id, 3000);
filename = file_name_with_datadir("background.png");
weston_config_section_get_string(
shellSection, "background-image", &setting->background.filePath,
filename);
free(filename);
DATADIR "/weston/background.png");
weston_config_section_get_uint(
shellSection, "background-id", &setting->background.id, 1001);
filename = file_name_with_datadir("panel.png");
weston_config_section_get_string(
shellSection, "panel-image", &setting->panel.filePath,
filename);
free(filename);
DATADIR "/weston/panel.png");
weston_config_section_get_uint(
shellSection, "panel-id", &setting->panel.id, 1002);
filename = file_name_with_datadir("tiling.png");
weston_config_section_get_string(
shellSection, "tiling-image", &setting->tiling.filePath,
filename);
free(filename);
DATADIR "/weston/tiling.png");
weston_config_section_get_uint(
shellSection, "tiling-id", &setting->tiling.id, 1003);
filename = file_name_with_datadir("sidebyside.png");
weston_config_section_get_string(
shellSection, "sidebyside-image", &setting->sidebyside.filePath,
filename);
free(filename);
DATADIR "/weston/sidebyside.png");
weston_config_section_get_uint(
shellSection, "sidebyside-id", &setting->sidebyside.id, 1004);
filename = file_name_with_datadir("fullscreen.png");
weston_config_section_get_string(
shellSection, "fullscreen-image", &setting->fullscreen.filePath,
filename);
free(filename);
DATADIR "/weston/fullscreen.png");
weston_config_section_get_uint(
shellSection, "fullscreen-id", &setting->fullscreen.id, 1005);
filename = file_name_with_datadir("random.png");
weston_config_section_get_string(
shellSection, "random-image", &setting->random.filePath,
filename);
free(filename);
DATADIR "/weston/random.png");
weston_config_section_get_uint(
shellSection, "random-id", &setting->random.id, 1006);
filename = file_name_with_datadir("home.png");
weston_config_section_get_string(
shellSection, "home-image", &setting->home.filePath,
filename);
free(filename);
DATADIR "/weston/home.png");
weston_config_section_get_uint(
shellSection, "home-id", &setting->home.id, 1007);
@ -1203,7 +1159,6 @@ hmi_homescreen_setting_create(void)
while (weston_config_next_section(config, &section, &name)) {
struct hmi_homescreen_launcher *launcher;
char *command;
if (strcmp(name, "ivi-launcher") != 0)
continue;
@ -1213,18 +1168,8 @@ hmi_homescreen_setting_create(void)
weston_config_section_get_string(section, "icon",
&launcher->icon, NULL);
weston_config_section_get_string(section, "command",
&command, NULL);
if (command == NULL) {
weston_config_section_get_string(section, "path",
&launcher->path, NULL);
launcher->argv = NULL;
} else {
launcher->argv = parse_command(command);
launcher->path = launcher->argv[0];
}
weston_config_section_get_string(section, "path",
&launcher->path, NULL);
weston_config_section_get_uint(section, "workspace-id",
&launcher->workspace_id, 0);
weston_config_section_get_uint(section, "icon-id",
@ -1324,11 +1269,6 @@ int main(int argc, char **argv)
wlCtx_WorkSpaceBackGround.cmm = &wlCtxCommon;
/* create desktop widgets */
create_launchers(&wlCtxCommon, &hmi_setting->launcher_list);
create_workspace_background(&wlCtx_WorkSpaceBackGround,
&hmi_setting->workspace_background);
for (i = 0; i < hmi_setting->screen_num; i++) {
wlCtx_BackGround[i].cmm = &wlCtxCommon;
create_background(&wlCtx_BackGround[i],
@ -1354,6 +1294,11 @@ int main(int argc, char **argv)
create_button(&wlCtx_Button_4, hmi_setting->random.id,
hmi_setting->random.filePath, 3);
create_workspace_background(&wlCtx_WorkSpaceBackGround,
&hmi_setting->workspace_background);
create_launchers(&wlCtxCommon, &hmi_setting->launcher_list);
create_home_button(&wlCtx_HomeButton, hmi_setting->home.id,
hmi_setting->home.filePath);

View file

@ -29,7 +29,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <linux/input.h>
#include <cairo.h>
@ -60,8 +59,6 @@ struct virtual_keyboard {
uint32_t surrounding_cursor;
struct keyboard *keyboard;
bool toplevel;
bool overlay;
struct zwp_input_panel_surface_v1 *ips;
};
enum key_type {
@ -276,10 +273,11 @@ static void __attribute__ ((format (printf, 1, 2)))
dbg(const char *fmt, ...)
{
#ifdef DEBUG
int l;
va_list argp;
va_start(argp, fmt);
vfprintf(stderr, fmt, argp);
l = vfprintf(stderr, fmt, argp);
va_end(argp);
#endif
}
@ -377,7 +375,7 @@ redraw_handler(struct widget *widget, void *data)
cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
cairo_clip(cr);
cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_select_font_face(cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size(cr, 16);
cairo_translate(cr, allocation.x, allocation.y);
@ -506,7 +504,7 @@ delete_before_cursor(struct virtual_keyboard *keyboard)
end = keyboard->surrounding_text + keyboard->surrounding_cursor;
zwp_input_method_context_v1_delete_surrounding_text(keyboard->context,
start - keyboard->surrounding_text,
(start - keyboard->surrounding_text) - keyboard->surrounding_cursor,
end - start);
zwp_input_method_context_v1_commit_string(keyboard->context,
keyboard->serial,
@ -960,33 +958,25 @@ global_handler(struct display *display, uint32_t name,
static void
set_toplevel(struct output *output, struct virtual_keyboard *virtual_keyboard)
{
zwp_input_panel_surface_v1_set_toplevel(virtual_keyboard->ips,
struct zwp_input_panel_surface_v1 *ips;
struct keyboard *keyboard = virtual_keyboard->keyboard;
ips = zwp_input_panel_v1_get_input_panel_surface(virtual_keyboard->input_panel,
window_get_wl_surface(keyboard->window));
zwp_input_panel_surface_v1_set_toplevel(ips,
output_get_wl_output(output),
ZWP_INPUT_PANEL_SURFACE_V1_POSITION_CENTER_BOTTOM);
virtual_keyboard->toplevel = true;
virtual_keyboard->overlay = false;
}
static void
set_overlay(struct output *output, struct virtual_keyboard *virtual_keyboard)
{
zwp_input_panel_surface_v1_set_overlay_panel(virtual_keyboard->ips);
virtual_keyboard->toplevel = false;
virtual_keyboard->overlay = true;
virtual_keyboard->toplevel = true;
}
static void
display_output_handler(struct output *output, void *data) {
struct virtual_keyboard *keyboard = data;
const char *type = getenv("WESTON_KEYBOARD_SURFACE_TYPE");
if (type && strcasecmp("overlay", type) == 0) {
if (!keyboard->overlay)
set_overlay(output, keyboard);
} else {
if (!keyboard->toplevel)
set_toplevel(output, keyboard);
}
if (!keyboard->toplevel)
set_toplevel(output, keyboard);
}
static void
@ -1002,14 +992,9 @@ keyboard_create(struct virtual_keyboard *virtual_keyboard)
keyboard->window = window_create_custom(virtual_keyboard->display);
keyboard->widget = window_add_widget(keyboard->window, keyboard);
virtual_keyboard->ips =
zwp_input_panel_v1_get_input_panel_surface(virtual_keyboard->input_panel,
window_get_wl_surface(keyboard->window));
virtual_keyboard->keyboard = keyboard;
window_set_title(keyboard->window, "Virtual keyboard");
window_set_appid(keyboard->window,
"org.freedesktop.weston.virtual-keyboard");
window_set_user_data(keyboard->window, keyboard);
widget_set_redraw_handler(keyboard->widget, redraw_handler);
@ -1026,23 +1011,6 @@ keyboard_create(struct virtual_keyboard *virtual_keyboard)
display_output_handler);
}
static void
keyboard_destroy(struct virtual_keyboard *virtual_keyboard)
{
if (virtual_keyboard->ips)
zwp_input_panel_surface_v1_destroy(virtual_keyboard->ips);
if (virtual_keyboard->input_panel)
zwp_input_panel_v1_destroy(virtual_keyboard->input_panel);
if (virtual_keyboard->input_method)
zwp_input_method_v1_destroy(virtual_keyboard->input_method);
widget_destroy(virtual_keyboard->keyboard->widget);
window_destroy(virtual_keyboard->keyboard->window);
free(virtual_keyboard->keyboard);
}
int
main(int argc, char *argv[])
{
@ -1052,8 +1020,7 @@ main(int argc, char *argv[])
virtual_keyboard.display = display_create(&argc, argv);
if (virtual_keyboard.display == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
@ -1069,8 +1036,5 @@ main(int argc, char *argv[])
display_run(virtual_keyboard.display);
keyboard_destroy(&virtual_keyboard);
display_destroy(virtual_keyboard.display);
return 0;
}

View file

@ -1,533 +0,0 @@
if get_option('resize-pool')
config_h.set('USE_RESIZE_POOL', '1')
endif
srcs_toytoolkit = [
'window.c',
color_management_v1_client_protocol_h,
color_management_v1_protocol_c,
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
text_cursor_position_client_protocol_h,
text_cursor_position_protocol_c,
relative_pointer_unstable_v1_client_protocol_h,
relative_pointer_unstable_v1_protocol_c,
pointer_constraints_unstable_v1_client_protocol_h,
pointer_constraints_unstable_v1_protocol_c,
single_pixel_buffer_v1_client_protocol_h,
single_pixel_buffer_v1_protocol_c,
tablet_unstable_v2_client_protocol_h,
tablet_unstable_v2_protocol_c,
ivi_application_client_protocol_h,
ivi_application_protocol_c,
viewporter_client_protocol_h,
viewporter_protocol_c,
]
deps_toytoolkit = [
dep_wayland_client,
dep_lib_cairo_shared,
dep_matrix_c,
dep_xkbcommon,
dependency('wayland-cursor'),
cc.find_library('util'),
]
lib_toytoolkit = static_library(
'toytoolkit',
srcs_toytoolkit,
include_directories: common_inc,
dependencies: deps_toytoolkit,
install: false,
)
dep_toytoolkit = declare_dependency(
link_with: lib_toytoolkit,
dependencies: deps_toytoolkit,
)
dep_gbm = dependency('gbm', required: false, version: '>= 21.3')
simple_clients_enabled = get_option('simple-clients')
simple_build_all = simple_clients_enabled.contains('all')
simple_clients = [
{
'name': 'damage',
'sources': [
'simple-damage.c',
viewporter_client_protocol_h,
viewporter_protocol_c,
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
],
'dep_objs': [ dep_wayland_client, dep_libshared ]
},
{
'name': 'dmabuf-feedback',
'sources': [
'simple-dmabuf-feedback.c',
'../libweston/pixel-formats.c',
linux_dmabuf_unstable_v1_client_protocol_h,
linux_dmabuf_unstable_v1_protocol_c,
presentation_time_client_protocol_h,
presentation_time_protocol_c,
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
],
'dep_objs': [
dep_wayland_client,
dep_libshared,
dep_pixman,
dep_libdrm,
dependency('libudev', version: '>= 136'),
# gbm_bo_get_fd_for_plane() from 21.1.0
dependency('gbm', version: '>= 21.1.1',
required: simple_build_all or simple_clients_enabled.contains('dmabuf-feedback'),
not_found_message: 'dmabuf-feedback requires gbm which was not found. If you rather not build this, drop "dmabuf-feedback" from simple-clients option.',
disabler: true)
],
'deps': [ 'egl', 'glesv2', 'gbm' ],
'options': [ 'renderer-gl' ]
},
{
'name': 'dmabuf-egl',
'sources': [
'simple-dmabuf-egl.c',
linux_dmabuf_unstable_v1_client_protocol_h,
linux_dmabuf_unstable_v1_protocol_c,
linux_explicit_synchronization_unstable_v1_client_protocol_h,
linux_explicit_synchronization_unstable_v1_protocol_c,
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
weston_direct_display_client_protocol_h,
weston_direct_display_protocol_c,
],
'dep_objs': [
dep_wayland_client,
dep_libdrm,
dep_libm,
dep_matrix_c,
],
'deps': [ 'egl', 'glesv2', 'gbm' ],
'options': [ 'renderer-gl' ]
},
{
'name': 'dmabuf-v4l',
'sources': [
'simple-dmabuf-v4l.c',
linux_dmabuf_unstable_v1_client_protocol_h,
linux_dmabuf_unstable_v1_protocol_c,
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
weston_direct_display_client_protocol_h,
weston_direct_display_protocol_c,
viewporter_client_protocol_h,
viewporter_protocol_c,
],
'dep_objs': [ dep_wayland_client, dep_libdrm_headers ],
'deps': [ 'wayland-cursor' ]
},
{
'name': 'egl',
'sources': [
'simple-egl.c',
fractional_scale_v1_client_protocol_h,
fractional_scale_v1_protocol_c,
tearing_control_v1_client_protocol_h,
tearing_control_v1_protocol_c,
viewporter_client_protocol_h,
viewporter_protocol_c,
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
ivi_application_client_protocol_h,
ivi_application_protocol_c,
],
'dep_objs': [
dep_libm,
dep_libshared,
dep_matrix_c,
dep_wayland_client,
],
'deps': [ 'egl', 'wayland-egl', 'glesv2', 'wayland-cursor' ],
'options': [ 'renderer-gl' ]
},
# weston-simple-im is handled specially separately due to install_dir and odd window.h usage
{
'name': 'shm',
'sources': [
'simple-shm.c',
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
ivi_application_client_protocol_h,
ivi_application_protocol_c,
],
'dep_objs': [ dep_wayland_client, dep_libshared ]
},
{
'name': 'timing',
'sources': [
'simple-timing.c',
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
commit_timing_v1_client_protocol_h,
commit_timing_v1_protocol_c,
fifo_v1_client_protocol_h,
fifo_v1_protocol_c,
presentation_time_client_protocol_h,
presentation_time_protocol_c,
],
'dep_objs': [ dep_wayland_client, dep_libshared ]
},
{
'name': 'touch',
'sources': [
'simple-touch.c',
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
],
'dep_objs': [ dep_wayland_client, dep_libshared ]
},
]
if dep_vulkan.found() and prog_glslang.found()
srcs_simple_vulkan_shaders = [
'simple_vulkan_vertex_shader.vert',
'simple_vulkan_fragment_shader.frag',
]
simple_vulkan_shaders = []
foreach s : srcs_simple_vulkan_shaders
simple_vulkan_shaders += custom_target(s + '.spv.h',
command: [ prog_glslang, '@INPUT@', '--quiet', '--variable-name', '@BASENAME@', '-V', '-x', '-o', '@OUTPUT@' ],
input: s,
output: '@BASENAME@.spv.h',
)
endforeach
simple_clients += {
'name': 'vulkan',
'sources': [
'simple-vulkan.c',
simple_vulkan_shaders,
fractional_scale_v1_client_protocol_h,
fractional_scale_v1_protocol_c,
tearing_control_v1_client_protocol_h,
tearing_control_v1_protocol_c,
viewporter_client_protocol_h,
viewporter_protocol_c,
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
],
'dep_objs': [
dep_vulkan,
dep_libm,
dep_libshared,
dep_matrix_c,
dep_wayland_client,
],
'deps': [ 'vulkan', 'wayland-cursor' ],
'options': [ 'renderer-gl' ]
}
srcs_simple_dmabuf_vulkan_shaders = [
'simple_dmabuf_vulkan_vertex_shader.vert',
'simple_dmabuf_vulkan_fragment_shader.frag',
]
simple_dmabuf_vulkan_shaders = []
foreach s : srcs_simple_dmabuf_vulkan_shaders
simple_dmabuf_vulkan_shaders += custom_target(s + '.spv.h',
command: [ prog_glslang, '@INPUT@', '--quiet', '--variable-name', '@BASENAME@', '-V', '-x', '-o', '@OUTPUT@' ],
input: s,
output: '@BASENAME@.spv.h',
)
endforeach
simple_clients += {
'name': 'dmabuf-vulkan',
'sources': [
'simple-dmabuf-vulkan.c',
simple_dmabuf_vulkan_shaders,
linux_dmabuf_unstable_v1_client_protocol_h,
linux_dmabuf_unstable_v1_protocol_c,
linux_explicit_synchronization_unstable_v1_client_protocol_h,
linux_explicit_synchronization_unstable_v1_protocol_c,
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
weston_direct_display_client_protocol_h,
weston_direct_display_protocol_c,
],
'dep_objs': [
dep_vulkan,
dep_libdrm_headers,
dep_libm,
dep_libdrm,
dep_libshared,
dep_matrix_c,
dep_wayland_client,
dep_libweston_private, # for pixel-formats.h
],
'deps': [ 'vulkan', 'wayland-cursor', 'gbm' ],
'options': [ 'renderer-gl' ]
}
endif
foreach t : simple_clients
if simple_build_all or simple_clients_enabled.contains(t.get('name'))
t_name = 'weston-simple-' + t.get('name')
t_deps = t.get('dep_objs', [])
foreach depname : t.get('deps', [])
dep = dependency(depname, required: false)
if not dep.found()
error('@0@ requires @1@ which was not found. If you rather not build this, drop "@2@" from simple-clients option.'.format(t_name, depname, t.get('name')))
endif
t_deps += dep
endforeach
foreach optname : t.get('options', [])
if not get_option(optname)
error('@0@ requires option @1@ which is not enabled. If you rather not build this, drop "@2@" from simple-clients option.'.format(t_name, optname, t.get('name')))
endif
endforeach
executable(
t_name, t.get('sources'),
include_directories: common_inc,
dependencies: t_deps,
install: true
)
endif
endforeach
if simple_build_all or simple_clients_enabled.contains('im')
executable(
'weston-simple-im', [
'simple-im.c',
input_method_unstable_v1_client_protocol_h,
input_method_unstable_v1_protocol_c,
],
include_directories: common_inc,
dependencies: [
dep_libshared,
dep_wayland_client,
dep_xkbcommon,
dependency('wayland-cursor'),
dependency('cairo')
],
install: true,
install_dir: dir_libexec
)
endif
tools_enabled = get_option('tools')
tools_list = [
{
'name': 'calibrator',
'sources': [ 'calibrator.c' ],
'deps': [ dep_toytoolkit, dep_matrix_c ],
},
{
'name': 'debug',
'sources': [
'weston-debug.c',
weston_debug_client_protocol_h,
weston_debug_protocol_c,
],
'deps': [ dep_wayland_client ]
},
{
'name': 'terminal',
'sources': [ 'terminal.c' ],
'deps': [ dep_toytoolkit ],
},
{
'name': 'touch-calibrator',
'sources': [
'touch-calibrator.c',
weston_touch_calibration_client_protocol_h,
weston_touch_calibration_protocol_c,
],
'deps': [ dep_toytoolkit, dep_matrix_c ],
},
]
foreach t : tools_list
if tools_enabled.contains(t.get('name'))
executable(
'weston-@0@'.format(t.get('name')),
t.get('sources'),
include_directories: common_inc,
dependencies: t.get('deps', []),
install: true
)
endif
endforeach
demo_clients = [
{ 'basename': 'clickdot' },
{
'basename': 'cliptest',
'dep_objs': [ dep_vertex_clipping, dep_matrix_c ]
},
{
'basename': 'color',
'add_sources': [
color_management_v1_client_protocol_h,
color_management_v1_protocol_c,
single_pixel_buffer_v1_client_protocol_h,
single_pixel_buffer_v1_protocol_c,
],
},
{
'basename': 'constraints',
'add_sources': [
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
],
},
{
'basename': 'content_protection',
'add_sources': [
weston_content_protection_client_protocol_h,
weston_content_protection_protocol_c,
]
},
{ 'basename': 'dnd' },
{
'basename': 'editor',
'add_sources': [
text_input_unstable_v1_client_protocol_h,
text_input_unstable_v1_protocol_c,
],
'deps': [ 'pangocairo', 'gobject-2.0' ]
},
{ 'basename': 'eventdemo' },
{ 'basename': 'flower' },
{
'basename': 'fullscreen',
},
{ 'basename': 'image' },
{
'basename': 'multi-resource',
'add_sources': [
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
]
},
{
'basename': 'presentation-shm',
'add_sources': [
presentation_time_client_protocol_h,
presentation_time_protocol_c,
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
]
},
{ 'basename': 'resizor' },
{
'basename': 'scaler',
'add_sources': [
viewporter_client_protocol_h,
viewporter_protocol_c,
]
},
{ 'basename': 'smoke' },
{ 'basename': 'stacking' },
{
'basename': 'subsurfaces',
'deps': [ 'egl', 'glesv2', 'wayland-egl' ]
},
{
'basename': 'tablet',
'add_sources': [
tablet_unstable_v2_client_protocol_h,
tablet_unstable_v2_protocol_c,
],
},
{ 'basename': 'transformed' },
]
if get_option('demo-clients')
foreach t : demo_clients
t_name = 'weston-' + t.get('basename')
t_srcs = [ t.get('basename') + '.c' ] + t.get('add_sources', [])
t_deps = [ dep_toytoolkit, t.get('dep_objs', []) ]
foreach depname : t.get('deps', [])
dep = dependency(depname, required: false)
if not dep.found()
error('@0@ requires \'@1@\' which was not found. If you rather not build this, set \'-Ddemo-clients=false\'.'.format(t_name, depname))
endif
t_deps += dep
endforeach
executable(
t_name, t_srcs,
include_directories: common_inc,
dependencies: t_deps,
install: true
)
endforeach
endif
if get_option('shell-desktop')
exe_keyboard = executable(
'weston-keyboard',
'keyboard.c',
text_input_unstable_v1_client_protocol_h,
text_input_unstable_v1_protocol_c,
input_method_unstable_v1_client_protocol_h,
input_method_unstable_v1_protocol_c,
include_directories: common_inc,
dependencies: dep_toytoolkit,
install_dir: get_option('libexecdir'),
install: true
)
env_modmap += 'weston-keyboard=@0@;'.format(exe_keyboard.full_path())
exe_shell_desktop = executable(
'weston-desktop-shell',
'desktop-shell.c',
weston_desktop_shell_client_protocol_h,
weston_desktop_shell_protocol_c,
tablet_unstable_v2_client_protocol_h,
tablet_unstable_v2_protocol_c,
include_directories: common_inc,
dependencies: dep_toytoolkit,
install_dir: get_option('libexecdir'),
install: true
)
env_modmap += 'weston-desktop-shell=@0@;'.format(exe_shell_desktop.full_path())
endif
if get_option('shell-desktop') or get_option('shell-kiosk') or get_option('shell-ivi')
exe_shooter = executable(
'weston-screenshooter',
'screenshot.c',
weston_output_capture_client_protocol_h,
weston_output_capture_protocol_c,
include_directories: common_inc,
dependencies: [
dep_client_buffer,
dep_toytoolkit,
dep_libweston_private, # for pixel-formats.h
dep_pixman,
],
install_dir: get_option('bindir'),
install: true
)
env_modmap += 'weston-screenshooter=@0@;'.format(exe_shooter.full_path())
endif
if get_option('shell-ivi')
exe_shell_ivi_ui = executable(
'weston-ivi-shell-user-interface',
'ivi-shell-user-interface.c',
ivi_hmi_controller_client_protocol_h,
ivi_hmi_controller_protocol_c,
ivi_application_client_protocol_h,
ivi_application_protocol_c,
include_directories: common_inc,
dependencies: dep_toytoolkit,
install: true,
install_dir: get_option('libexecdir')
)
env_modmap += 'weston-ivi-shell-user-interface=@0@;'.format(exe_shell_ivi_ui.full_path())
endif

View file

@ -1,7 +1,6 @@
/*
* Copyright © 2011 Benjamin Franzke
* Copyright © 2010, 2013 Intel Corporation
* Copyright © 2021 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -36,18 +35,14 @@
#include <sys/mman.h>
#include <signal.h>
#include <time.h>
#include <poll.h>
#include <sys/poll.h>
#include <float.h>
#include <math.h>
#include <wayland-client.h>
#include "shared/os-compatibility.h"
#include "shared/xalloc.h"
#include <libweston/zalloc.h>
#include "xdg-shell-client-protocol.h"
static int running = 1;
#include "shared/zalloc.h"
struct device {
enum { KEYBOARD, POINTER } type;
@ -66,9 +61,9 @@ struct display {
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_shell *shell;
struct wl_seat *seat;
struct wl_shm *shm;
struct xdg_wm_base *wm_base;
uint32_t formats;
struct wl_list devices;
};
@ -77,9 +72,7 @@ struct window {
struct display *display;
int width, height;
struct wl_surface *surface;
struct xdg_toplevel *xdg_toplevel;
struct xdg_surface *xdg_surface;
bool wait_for_configure;
struct wl_shell_surface *shell_surface;
};
static void
@ -104,8 +97,8 @@ attach_buffer(struct window *window, int width, int height)
fd = os_create_anonymous_file(size);
if (fd < 0) {
fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
size, strerror(errno));
fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
size);
return -1;
}
@ -123,54 +116,27 @@ attach_buffer(struct window *window, int width, int height)
}
static void
handle_xdg_surface_configure(void *data, struct xdg_surface *surface,
uint32_t serial)
handle_ping(void *data, struct wl_shell_surface *shell_surface,
uint32_t serial)
{
struct window *window = data;
xdg_surface_ack_configure(surface, serial);
if (window->wait_for_configure) {
attach_buffer(window, window->width, window->height);
wl_surface_damage(window->surface, 0, 0, window->width, window->height);
wl_surface_commit(window->surface);
window->wait_for_configure = false;
}
wl_shell_surface_pong(shell_surface, serial);
}
static const struct xdg_surface_listener xdg_surface_listener = {
handle_xdg_surface_configure,
};
static void
xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
{
xdg_wm_base_pong(shell, serial);
}
static const struct xdg_wm_base_listener wm_base_listener = {
xdg_wm_base_ping,
};
static void
handle_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
int32_t width, int32_t height,
struct wl_array *state)
handle_configure(void *data, struct wl_shell_surface *shell_surface,
uint32_t edges, int32_t width, int32_t height)
{
}
static void
handle_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
{
running = 0;
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
handle_toplevel_configure,
handle_toplevel_close,
static const struct wl_shell_surface_listener shell_surface_listener = {
handle_ping,
handle_configure,
handle_popup_done
};
static struct window *
@ -183,19 +149,19 @@ create_window(struct display *display, int width, int height)
window->width = width;
window->height = height;
window->surface = wl_compositor_create_surface(display->compositor);
window->shell_surface = wl_shell_get_shell_surface(display->shell,
window->surface);
window->xdg_surface =
xdg_wm_base_get_xdg_surface(display->wm_base, window->surface);
assert(window->xdg_surface);
if (window->shell_surface)
wl_shell_surface_add_listener(window->shell_surface,
&shell_surface_listener, window);
xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window);
wl_shell_surface_set_title(window->shell_surface, "simple-shm");
window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface);
assert(window->xdg_toplevel);
xdg_toplevel_add_listener(window->xdg_toplevel,
&xdg_toplevel_listener, window);
xdg_toplevel_set_title(window->xdg_toplevel, "multi-resource");
window->wait_for_configure = true;
wl_shell_surface_set_toplevel(window->shell_surface);
wl_surface_damage(window->surface, 0, 0, width, height);
attach_buffer(window, width, height);
wl_surface_commit(window->surface);
return window;
@ -204,11 +170,7 @@ create_window(struct display *display, int width, int height)
static void
destroy_window(struct window *window)
{
if (window->xdg_surface)
xdg_surface_destroy(window->xdg_surface);
if (window->xdg_toplevel)
xdg_toplevel_destroy(window->xdg_toplevel);
wl_shell_surface_destroy(window->shell_surface);
wl_surface_destroy(window->surface);
free(window);
}
@ -235,10 +197,9 @@ registry_handle_global(void *data, struct wl_registry *registry,
d->compositor =
wl_registry_bind(registry,
id, &wl_compositor_interface, 1);
} else if (strcmp(interface, "xdg_wm_base") == 0) {
d->wm_base = wl_registry_bind(registry,
id, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(d->wm_base, &wm_base_listener, d);
} else if (strcmp(interface, "wl_shell") == 0) {
d->shell = wl_registry_bind(registry,
id, &wl_shell_interface, 1);
} else if (strcmp(interface, "wl_shm") == 0) {
d->shm = wl_registry_bind(registry,
id, &wl_shm_interface, 1);
@ -287,11 +248,6 @@ create_display(void)
exit(1);
}
if (!display->wm_base) {
fprintf(stderr, "xdg-shell required!\n");
exit(1);
}
wl_list_init(&display->devices);
return display;
@ -340,8 +296,6 @@ static void
keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
uint32_t format, int fd, uint32_t size)
{
/* Just so we dont leak the keymap fd */
close(fd);
}
static void
@ -443,8 +397,8 @@ destroy_display(struct display *display)
if (display->shm)
wl_shm_destroy(display->shm);
if (display->wm_base)
xdg_wm_base_destroy(display->wm_base);
if (display->shell)
wl_shell_destroy(display->shell);
if (display->seat)
wl_seat_destroy(display->seat);
@ -458,6 +412,7 @@ destroy_display(struct display *display)
free(display);
}
static int running = 1;
static void
signal_int(int signum)

374
clients/nested-client.c Normal file
View file

@ -0,0 +1,374 @@
/*
* 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-egl.h>
#include <wayland-cursor.h>
#include <GLES2/gl2.h>
#include <EGL/egl.h>
#include "../shared/platform.h"
struct window;
struct seat;
struct nested_client {
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
EGLDisplay egl_display;
EGLContext egl_context;
EGLConfig egl_config;
EGLSurface egl_surface;
struct program *color_program;
GLuint vert, frag, program;
GLuint rotation;
GLuint pos;
GLuint col;
struct wl_surface *surface;
struct wl_egl_window *native;
int width, height;
};
#define POS 0
#define COL 1
static GLuint
create_shader(const char *source, GLenum shader_type)
{
GLuint shader;
GLint status;
shader = glCreateShader(shader_type);
if (shader == 0)
return 0;
glShaderSource(shader, 1, (const char **) &source, NULL);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (!status) {
char log[1000];
GLsizei len;
glGetShaderInfoLog(shader, 1000, &len, log);
fprintf(stderr, "Error: compiling %s: %*s\n",
shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
len, log);
return 0;
}
return shader;
}
static void
create_program(struct nested_client *client,
const char *vert, const char *frag)
{
GLint status;
client->vert = create_shader(vert, GL_VERTEX_SHADER);
client->frag = create_shader(frag, GL_FRAGMENT_SHADER);
client->program = glCreateProgram();
glAttachShader(client->program, client->frag);
glAttachShader(client->program, client->vert);
glBindAttribLocation(client->program, POS, "pos");
glBindAttribLocation(client->program, COL, "color");
glLinkProgram(client->program);
glGetProgramiv(client->program, GL_LINK_STATUS, &status);
if (!status) {
char log[1000];
GLsizei len;
glGetProgramInfoLog(client->program, 1000, &len, log);
fprintf(stderr, "Error: linking:\n%*s\n", len, log);
exit(1);
}
client->rotation =
glGetUniformLocation(client->program, "rotation");
}
static const char vertex_shader_text[] =
"uniform mat4 rotation;\n"
"attribute vec4 pos;\n"
"attribute vec4 color;\n"
"varying vec4 v_color;\n"
"void main() {\n"
" gl_Position = rotation * pos;\n"
" v_color = color;\n"
"}\n";
static const char color_fragment_shader_text[] =
"precision mediump float;\n"
"varying vec4 v_color;\n"
"void main() {\n"
" gl_FragColor = v_color;\n"
"}\n";
static void
render_triangle(struct nested_client *client, uint32_t time)
{
static const GLfloat verts[3][2] = {
{ -0.5, -0.5 },
{ 0.5, -0.5 },
{ 0, 0.5 }
};
static const GLfloat colors[3][3] = {
{ 1, 0, 0 },
{ 0, 1, 0 },
{ 0, 0, 1 }
};
GLfloat angle;
GLfloat rotation[4][4] = {
{ 1, 0, 0, 0 },
{ 0, 1, 0, 0 },
{ 0, 0, 1, 0 },
{ 0, 0, 0, 1 }
};
static const int32_t speed_div = 5;
static uint32_t start_time = 0;
if (client->program == 0)
create_program(client, vertex_shader_text,
color_fragment_shader_text);
if (start_time == 0)
start_time = time;
angle = ((time - start_time) / speed_div) % 360 * M_PI / 180.0;
rotation[0][0] = cos(angle);
rotation[0][2] = sin(angle);
rotation[2][0] = -sin(angle);
rotation[2][2] = cos(angle);
glClearColor(0.4, 0.4, 0.4, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(client->program);
glViewport(0, 0, client->width, client->height);
glUniformMatrix4fv(client->rotation, 1, GL_FALSE,
(GLfloat *) rotation);
glVertexAttribPointer(POS, 2, GL_FLOAT, GL_FALSE, 0, verts);
glVertexAttribPointer(COL, 3, GL_FLOAT, GL_FALSE, 0, colors);
glEnableVertexAttribArray(POS);
glEnableVertexAttribArray(COL);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(POS);
glDisableVertexAttribArray(COL);
glFlush();
}
static void
frame_callback(void *data, struct wl_callback *callback, uint32_t time);
static const struct wl_callback_listener frame_listener = {
frame_callback
};
static void
frame_callback(void *data, struct wl_callback *callback, uint32_t time)
{
struct nested_client *client = data;
if (callback)
wl_callback_destroy(callback);
callback = wl_surface_frame(client->surface);
wl_callback_add_listener(callback, &frame_listener, client);
render_triangle(client, time);
eglSwapBuffers(client->egl_display, client->egl_surface);
}
static void
registry_handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version)
{
struct nested_client *client = data;
if (strcmp(interface, "wl_compositor") == 0) {
client->compositor =
wl_registry_bind(registry, name,
&wl_compositor_interface, 1);
}
}
static void
registry_handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name)
{
}
static const struct wl_registry_listener registry_listener = {
registry_handle_global,
registry_handle_global_remove
};
static struct nested_client *
nested_client_create(void)
{
static const EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
static const EGLint config_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
};
EGLint major, minor, n;
EGLBoolean ret;
struct nested_client *client;
client = malloc(sizeof *client);
if (client == NULL)
return NULL;
client->width = 250;
client->height = 250;
client->display = wl_display_connect(NULL);
client->registry = wl_display_get_registry(client->display);
wl_registry_add_listener(client->registry,
&registry_listener, client);
/* get globals */
wl_display_roundtrip(client->display);
client->egl_display =
weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR,
client->display, NULL);
if (client->egl_display == NULL)
return NULL;
ret = eglInitialize(client->egl_display, &major, &minor);
if (!ret)
return NULL;
ret = eglBindAPI(EGL_OPENGL_ES_API);
if (!ret)
return NULL;
ret = eglChooseConfig(client->egl_display, config_attribs,
&client->egl_config, 1, &n);
if (!ret || n != 1)
return NULL;
client->egl_context = eglCreateContext(client->egl_display,
client->egl_config,
EGL_NO_CONTEXT,
context_attribs);
if (!client->egl_context)
return NULL;
client->surface = wl_compositor_create_surface(client->compositor);
client->native = wl_egl_window_create(client->surface,
client->width, client->height);
client->egl_surface = weston_platform_create_egl_surface(client->egl_display,
client->egl_config,
client->native, NULL);
eglMakeCurrent(client->egl_display, client->egl_surface,
client->egl_surface, client->egl_context);
wl_egl_window_resize(client->native,
client->width, client->height, 0, 0);
frame_callback(client, NULL, 0);
return client;
}
static void
nested_client_destroy(struct nested_client *client)
{
eglMakeCurrent(client->egl_display,
EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
weston_platform_destroy_egl_surface(client->egl_display,
client->egl_surface);
wl_egl_window_destroy(client->native);
wl_surface_destroy(client->surface);
if (client->compositor)
wl_compositor_destroy(client->compositor);
wl_registry_destroy(client->registry);
wl_display_flush(client->display);
wl_display_disconnect(client->display);
eglTerminate(client->egl_display);
eglReleaseThread();
}
int
main(int argc, char **argv)
{
struct nested_client *client;
int ret = 0;
if (getenv("WAYLAND_SOCKET") == NULL) {
fprintf(stderr,
"must be run by nested, don't run standalone\n");
return EXIT_FAILURE;
}
client = nested_client_create();
if (client == NULL)
return EXIT_FAILURE;
while (ret != -1)
ret = wl_display_dispatch(client->display);
nested_client_destroy(client);
return 0;
}

1134
clients/nested.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -35,15 +35,12 @@
#include <sys/mman.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <wayland-client.h>
#include "shared/helpers.h"
#include <libweston/zalloc.h>
#include "shared/timespec-util.h"
#include "shared/zalloc.h"
#include "shared/os-compatibility.h"
#include "presentation-time-client-protocol.h"
#include "xdg-shell-client-protocol.h"
enum run_mode {
RUN_MODE_FEEDBACK,
@ -67,7 +64,7 @@ struct display {
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct xdg_wm_base *wm_base;
struct wl_shell *shell;
struct wl_shm *shm;
uint32_t formats;
@ -100,9 +97,7 @@ struct window {
int width, height;
enum run_mode mode;
struct wl_surface *surface;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
uint32_t configure_serial;
struct wl_shell_surface *shell_surface;
struct buffer *buffers;
int num_buffers;
@ -145,14 +140,14 @@ create_shm_buffers(struct display *display, struct buffer **buffers,
fd = os_create_anonymous_file(size);
if (fd < 0) {
fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
size, strerror(errno));
fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
size);
return -1;
}
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
fprintf(stderr, "mmap failed: %m\n");
close(fd);
return -1;
}
@ -184,48 +179,27 @@ create_shm_buffers(struct display *display, struct buffer **buffers,
}
static void
xdg_wm_base_handle_ping(void *data, struct xdg_wm_base *xdg_wm_base,
uint32_t serial)
handle_ping(void *data, struct wl_shell_surface *shell_surface,
uint32_t serial)
{
xdg_wm_base_pong(xdg_wm_base, serial);
}
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
.ping = xdg_wm_base_handle_ping,
};
static void
xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface,
uint32_t serial)
{
struct window *window = data;
window->configure_serial = serial;
}
static const struct xdg_surface_listener xdg_surface_listener = {
.configure = xdg_surface_handle_configure,
};
static void
xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *xdg_toplevel,
int32_t width, int32_t height,
struct wl_array *states)
{
/* noop */
wl_shell_surface_pong(shell_surface, serial);
}
static void
xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel)
handle_configure(void *data, struct wl_shell_surface *shell_surface,
uint32_t edges, int32_t width, int32_t height)
{
fprintf(stderr, "presentation-shm exiting\n");
exit(0);
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
.configure = xdg_toplevel_handle_configure,
.close = xdg_toplevel_handle_close,
static void
handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
{
}
static const struct wl_shell_surface_listener shell_surface_listener = {
handle_ping,
handle_configure,
handle_popup_done
};
static struct window *
@ -252,30 +226,16 @@ create_window(struct display *display, int width, int height,
window->width = width;
window->height = height;
window->surface = wl_compositor_create_surface(display->compositor);
window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base,
window->surface);
window->shell_surface = wl_shell_get_shell_surface(display->shell,
window->surface);
if (!window->xdg_surface)
return NULL;
if (window->shell_surface)
wl_shell_surface_add_listener(window->shell_surface,
&shell_surface_listener, window);
window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface);
wl_shell_surface_set_title(window->shell_surface, title);
if (!window->xdg_toplevel)
return NULL;
xdg_wm_base_add_listener(display->wm_base, &xdg_wm_base_listener,
NULL);
xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener,
window);
xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener,
window);
xdg_toplevel_set_title(window->xdg_toplevel, title);
xdg_toplevel_set_min_size(window->xdg_toplevel, width, height);
xdg_toplevel_set_max_size(window->xdg_toplevel, width, height);
wl_surface_commit(window->surface);
wl_display_roundtrip(window->display->display);
wl_shell_surface_set_toplevel(window->shell_surface);
window->num_buffers = 60;
window->refresh_nsec = NSEC_PER_SEC / 60; /* 60 Hz guess */
@ -315,7 +275,7 @@ destroy_window(struct window *window)
if (window->callback)
wl_callback_destroy(window->callback);
xdg_surface_destroy(window->xdg_surface);
wl_shell_surface_destroy(window->shell_surface);
wl_surface_destroy(window->surface);
for (i = 0; i < window->num_buffers; i++)
@ -423,6 +383,14 @@ timespec_to_ms(const struct timespec *ts)
return (uint32_t)ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
}
static void
timespec_from_proto(struct timespec *tm, uint32_t tv_sec_hi,
uint32_t tv_sec_lo, uint32_t tv_nsec)
{
tm->tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo;
tm->tv_nsec = tv_nsec;
}
static int
timespec_diff_to_usec(const struct timespec *a, const struct timespec *b)
{
@ -446,7 +414,7 @@ feedback_presented(void *data,
struct feedback *feedback = data;
struct window *window = feedback->window;
struct feedback *prev_feedback = window->received_feedback;
uint64_t seq = u64_from_u32s(seq_hi, seq_lo);
uint64_t seq = ((uint64_t)seq_hi << 32) + seq_lo;
const struct timespec *prevpresent;
uint32_t commit, present;
uint32_t f2c, c2p, f2p;
@ -519,7 +487,7 @@ window_emulate_rendering(struct window *window)
ret = nanosleep(&delay, NULL);
if (ret)
printf("nanosleep failed: %s\n", strerror(errno));
printf("nanosleep failed: %m\n");
}
static void
@ -560,12 +528,6 @@ window_commit_next(struct window *window)
buffer = window_next_buffer(window);
assert(buffer);
if (window->configure_serial) {
xdg_surface_ack_configure(window->xdg_surface,
window->configure_serial);
window->configure_serial = 0;
}
wl_surface_attach(window->surface, buffer->buffer, 0, 0);
wl_surface_damage(window->surface, 0, 0, window->width, window->height);
wl_surface_commit(window->surface);
@ -762,10 +724,9 @@ registry_handle_global(void *data, struct wl_registry *registry,
d->compositor =
wl_registry_bind(registry,
name, &wl_compositor_interface, 1);
} else if (strcmp(interface, "xdg_wm_base") == 0) {
d->wm_base =
wl_registry_bind(registry, name,
&xdg_wm_base_interface, 1);
} else if (strcmp(interface, "wl_shell") == 0) {
d->shell = wl_registry_bind(registry,
name, &wl_shell_interface, 1);
} else if (strcmp(interface, "wl_shm") == 0) {
d->shm = wl_registry_bind(registry,
name, &wl_shm_interface, 1);
@ -775,7 +736,7 @@ registry_handle_global(void *data, struct wl_registry *registry,
} else if (strcmp(interface, wp_presentation_interface.name) == 0) {
d->presentation =
wl_registry_bind(registry,
name, &wp_presentation_interface, 2);
name, &wp_presentation_interface, 1);
wp_presentation_add_listener(d->presentation,
&presentation_listener, d);
}
@ -851,8 +812,8 @@ destroy_display(struct display *display)
if (display->shm)
wl_shm_destroy(display->shm);
if (display->wm_base)
xdg_wm_base_destroy(display->wm_base);
if (display->shell)
wl_shell_destroy(display->shell);
if (display->compositor)
wl_compositor_destroy(display->compositor);

View file

@ -31,7 +31,6 @@
#include <cairo.h>
#include <math.h>
#include <assert.h>
#include <errno.h>
#include <linux/input.h>
#include <wayland-client.h>
@ -54,7 +53,6 @@ struct resizor {
struct spring height;
struct wl_callback *frame_callback;
bool pointer_locked;
bool locked_frame_callback_registered;
struct input *locked_input;
float pointer_x;
float pointer_y;
@ -332,15 +330,11 @@ button_handler(struct widget *widget,
handle_pointer_unlocked);
resizor->locked_input = input;
if (resizor->locked_frame_callback_registered)
return;
surface = window_get_wl_surface(resizor->window);
callback = wl_surface_frame(surface);
wl_callback_add_listener(callback,
&locked_pointer_frame_listener,
resizor);
resizor->locked_frame_callback_registered = true;
} else if (button == BTN_LEFT &&
state == WL_POINTER_BUTTON_STATE_RELEASED) {
input_set_pointer_image(input, CURSOR_LEFT_PTR);
@ -392,8 +386,6 @@ resizor_create(struct display *display)
resizor->window = window_create(display);
resizor->widget = window_frame_create(resizor->window, resizor);
window_set_title(resizor->window, "Wayland Resizor");
window_set_appid(resizor->window,
"org.freedesktop.weston.wayland-resizor");
resizor->display = display;
window_set_key_handler(resizor->window, key_handler);
@ -442,8 +434,7 @@ main(int argc, char *argv[])
display = display_create(&argc, argv);
if (display == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}

View file

@ -28,7 +28,6 @@
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <cairo.h>
#include <linux/input.h>
@ -289,8 +288,7 @@ main(int argc, char *argv[])
d = display_create(&argc, argv);
if (d == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
@ -303,7 +301,6 @@ main(int argc, char *argv[])
box.window = window_create(d);
box.widget = window_add_widget(box.window, &box);
window_set_title(box.window, "Scaler Test Box");
window_set_appid(box.window, "org.freedesktop.weston.scaler-test-box");
window_set_buffer_scale(box.window, BUFFER_SCALE);
widget_set_resize_handler(box.widget, resize_handler);
@ -317,11 +314,6 @@ main(int argc, char *argv[])
display_set_user_data(box.display, &box);
display_set_global_handler(box.display, global_handler);
if (box.mode != MODE_NO_VIEWPORT && !box.viewport) {
fprintf(stderr, "compositor doesn't support viewporter\n");
return -1;
}
display_run(d);
widget_destroy(box.widget);

View file

@ -1,6 +1,5 @@
/*
* Copyright © 2008 Kristian Høgsberg
* Copyright 2022 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -24,325 +23,121 @@
#include "config.h"
#include <assert.h>
#include <cairo.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <pixman.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <sys/param.h>
#include <sys/mman.h>
#include <cairo.h>
#include <wayland-client.h>
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "pixel-formats.h"
#include "shared/client-buffer-util.h"
#include "shared/file-util.h"
#include "weston-screenshooter-client-protocol.h"
#include "shared/os-compatibility.h"
#include "shared/string-helpers.h"
#include "shared/xalloc.h"
#include "weston-output-capture-client-protocol.h"
struct screenshooter_app {
struct wl_display *display;
struct wl_registry *registry;
struct wl_shm *shm;
struct zwp_linux_dmabuf_v1 *dmabuf;
struct weston_capture_v1 *capture_factory;
/* The screenshooter is a good example of a custom object exposed by
* the compositor and serves as a test bed for implementing client
* side marshalling outside libwayland.so */
bool verbose;
const struct pixel_format_info *requested_format;
enum weston_capture_v1_source src_type;
enum client_buffer_type buffer_type;
struct wl_list output_list; /* struct screenshooter_output::link */
bool retry;
bool failed;
int waitcount;
};
struct screenshooter_buffer {
struct client_buffer *buf;
pixman_image_t *image;
enum weston_capture_v1_source src_type;
};
static struct wl_shm *shm;
static struct weston_screenshooter *screenshooter;
static struct wl_list output_list;
int min_x, min_y, max_x, max_y;
int buffer_copy_done;
struct screenshooter_output {
struct screenshooter_app *app;
uint32_t name;
struct wl_list link; /* struct screenshooter_app::output_list */
struct wl_output *wl_output;
int offset_x, offset_y;
struct weston_capture_source_v1 *source;
int buffer_width;
int buffer_height;
struct wl_array formats;
bool formats_done;
struct screenshooter_buffer *buffer;
};
struct buffer_size {
int width, height;
int min_x, min_y;
int max_x, max_y;
};
static struct screenshooter_buffer *
screenshot_create_shm_buffer(struct screenshooter_app *app,
size_t width, size_t height,
const struct pixel_format_info *fmt)
{
struct screenshooter_buffer *buffer;
assert(width > 0);
assert(height > 0);
assert(fmt && fmt->bpp > 0);
assert(fmt->pixman_format);
buffer = xzalloc(sizeof *buffer);
buffer->buf = client_buffer_util_create_shm_buffer(app->shm,
fmt,
width,
height);
buffer->image = pixman_image_create_bits(fmt->pixman_format,
width, height,
buffer->buf->data,
buffer->buf->strides[0]);
abort_oom_if_null(buffer->image);
return buffer;
}
static struct screenshooter_buffer *
screenshot_create_udmabuf(struct screenshooter_app *app,
int width, int height,
const struct pixel_format_info *fmt)
{
struct screenshooter_buffer* buffer = NULL;
assert(width > 0);
assert(height > 0);
assert(fmt);
buffer = xzalloc(sizeof *buffer);
buffer->buf = client_buffer_util_create_dmabuf_buffer(app->display,
app->dmabuf,
fmt,
width,
height);
if (fmt->pixman_format) {
buffer->image = pixman_image_create_bits(fmt->pixman_format,
width, height,
buffer->buf->data,
buffer->buf->strides[0]);
abort_oom_if_null(buffer->image);
}
return buffer;
}
static void
screenshooter_buffer_destroy(struct screenshooter_buffer *buffer)
{
if (!buffer)
return;
if (buffer->image)
pixman_image_unref(buffer->image);
client_buffer_util_destroy_buffer(buffer->buf);
free(buffer);
}
static void
capture_source_handle_format(void *data,
struct weston_capture_source_v1 *proxy,
uint32_t drm_format)
{
struct screenshooter_output *output = data;
uint32_t *fmt;
assert(output->source == proxy);
if (output->formats_done) {
wl_array_release(&output->formats);
wl_array_init(&output->formats);
output->formats_done = false;
}
fmt = wl_array_add(&output->formats, sizeof(uint32_t));
assert(fmt);
*fmt = drm_format;
if (output->app->verbose) {
const struct pixel_format_info *fmt_info;
fmt_info = pixel_format_get_info(drm_format);
assert(fmt_info);
printf("Got format %s / 0x%x\n", fmt_info->drm_format_name,
drm_format);
}
}
static void
capture_source_handle_formats_done(void *data,
struct weston_capture_source_v1 *proxy)
{
struct screenshooter_output *output = data;
output->formats_done = true;
}
static void
capture_source_handle_size(void *data,
struct weston_capture_source_v1 *proxy,
int32_t width, int32_t height)
{
struct screenshooter_output *output = data;
assert(width > 0);
assert(height > 0);
output->buffer_width = width;
output->buffer_height = height;
if (output->app->verbose)
printf("Got size %dx%d\n", width, height);
}
static void
capture_source_handle_complete(void *data,
struct weston_capture_source_v1 *proxy)
{
struct screenshooter_output *output = data;
output->app->waitcount--;
}
static void
capture_source_handle_retry(void *data,
struct weston_capture_source_v1 *proxy)
{
struct screenshooter_output *output = data;
output->app->waitcount--;
output->app->retry = true;
}
static void
capture_source_handle_failed(void *data,
struct weston_capture_source_v1 *proxy,
const char *msg)
{
struct screenshooter_output *output = data;
output->app->waitcount--;
/* We don't set app.failed here because there could be other
* outputs we still want to capture!
*/
if (msg)
fprintf(stderr, "Output capture error: %s\n", msg);
}
static const struct weston_capture_source_v1_listener capture_source_handlers = {
.format = capture_source_handle_format,
.formats_done = capture_source_handle_formats_done,
.size = capture_source_handle_size,
.complete = capture_source_handle_complete,
.retry = capture_source_handle_retry,
.failed = capture_source_handle_failed,
struct wl_output *output;
struct wl_buffer *buffer;
int width, height, offset_x, offset_y;
void *data;
struct wl_list link;
};
static void
create_output(struct screenshooter_app *app, uint32_t output_name, uint32_t version)
display_handle_geometry(void *data,
struct wl_output *wl_output,
int x,
int y,
int physical_width,
int physical_height,
int subpixel,
const char *make,
const char *model,
int transform)
{
struct screenshooter_output *output;
version = MIN(version, 4);
output = xzalloc(sizeof *output);
output->app = app;
output->name = output_name;
output->wl_output = wl_registry_bind(app->registry, output_name,
&wl_output_interface, version);
abort_oom_if_null(output->wl_output);
output = wl_output_get_user_data(wl_output);
output->source = weston_capture_v1_create(app->capture_factory,
output->wl_output,
app->src_type);
abort_oom_if_null(output->source);
weston_capture_source_v1_add_listener(output->source,
&capture_source_handlers, output);
wl_array_init(&output->formats);
wl_list_insert(&app->output_list, &output->link);
if (wl_output == output->output) {
output->offset_x = x;
output->offset_y = y;
}
}
static void
destroy_output(struct screenshooter_output *output)
display_handle_mode(void *data,
struct wl_output *wl_output,
uint32_t flags,
int width,
int height,
int refresh)
{
weston_capture_source_v1_destroy(output->source);
struct screenshooter_output *output;
wl_array_release(&output->formats);
output = wl_output_get_user_data(wl_output);
if (wl_output_get_version(output->wl_output) >= WL_OUTPUT_RELEASE_SINCE_VERSION)
wl_output_release(output->wl_output);
else
wl_output_destroy(output->wl_output);
screenshooter_buffer_destroy(output->buffer);
wl_list_remove(&output->link);
free(output);
if (wl_output == output->output && (flags & WL_OUTPUT_MODE_CURRENT)) {
output->width = width;
output->height = height;
}
}
static const struct wl_output_listener output_listener = {
display_handle_geometry,
display_handle_mode
};
static void
screenshot_done(void *data, struct weston_screenshooter *screenshooter)
{
buffer_copy_done = 1;
}
static const struct weston_screenshooter_listener screenshooter_listener = {
screenshot_done
};
static void
handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version)
{
struct screenshooter_app *app = data;
static struct screenshooter_output *output;
if (strcmp(interface, wl_output_interface.name) == 0) {
create_output(app, name, version);
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
app->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
/*
* Not listening for format advertisements,
* weston_capture_source_v1.format event tells us what to use.
*/
} else if (strcmp(interface, weston_capture_v1_interface.name) == 0) {
app->capture_factory = wl_registry_bind(registry, name,
&weston_capture_v1_interface,
2);
} else if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) {
if (version < 3)
return;
app->dmabuf = wl_registry_bind(registry, name,
&zwp_linux_dmabuf_v1_interface,
3);
if (strcmp(interface, "wl_output") == 0) {
output = xmalloc(sizeof *output);
output->output = wl_registry_bind(registry, name,
&wl_output_interface, 1);
wl_list_insert(&output_list, &output->link);
wl_output_add_listener(output->output, &output_listener, output);
} else if (strcmp(interface, "wl_shm") == 0) {
shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
} else if (strcmp(interface, "weston_screenshooter") == 0) {
screenshooter = wl_registry_bind(registry, name,
&weston_screenshooter_interface,
1);
}
}
static void
handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
{
/* Dynamic output removals will just fail the respective shot. */
/* XXX: unimplemented */
}
static const struct wl_registry_listener registry_listener = {
@ -350,390 +145,157 @@ static const struct wl_registry_listener registry_listener = {
handle_global_remove
};
static void
screenshooter_output_capture(struct screenshooter_output *output)
static struct wl_buffer *
create_shm_buffer(int width, int height, void **data_out)
{
const struct pixel_format_info *fmt_info = NULL;
uint32_t *fmt;
struct wl_shm_pool *pool;
struct wl_buffer *buffer;
int fd, size, stride;
void *data;
screenshooter_buffer_destroy(output->buffer);
stride = width * 4;
size = stride * height;
wl_array_for_each(fmt, &output->formats) {
fmt_info = pixel_format_get_info(*fmt);
assert(fmt_info);
if (fmt_info == output->app->requested_format ||
output->app->requested_format == NULL)
break;
fmt_info = NULL;
}
if (!fmt_info) {
fprintf(stderr, "No supported format found\n");
exit(1);
fd = os_create_anonymous_file(size);
if (fd < 0) {
fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
size);
return NULL;
}
if (output->app->verbose)
printf("Creating buffer with format %s / 0x%x and size %ux%u\n",
fmt_info->drm_format_name, fmt_info->format,
output->buffer_width, output->buffer_height);
if (output->app->buffer_type == CLIENT_BUFFER_TYPE_SHM) {
output->buffer = screenshot_create_shm_buffer(output->app,
output->buffer_width,
output->buffer_height,
fmt_info);
} else if (output->app->buffer_type == CLIENT_BUFFER_TYPE_DMABUF) {
output->buffer = screenshot_create_udmabuf(output->app,
output->buffer_width,
output->buffer_height,
fmt_info);
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
fprintf(stderr, "mmap failed: %m\n");
close(fd);
return NULL;
}
abort_oom_if_null(output->buffer);
weston_capture_source_v1_capture(output->source,
output->buffer->buf->wl_buffer);
output->app->waitcount++;
pool = wl_shm_create_pool(shm, fd, size);
close(fd);
buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
WL_SHM_FORMAT_XRGB8888);
wl_shm_pool_destroy(pool);
*data_out = data;
return buffer;
}
static void
screenshot_write_png(const struct buffer_size *buff_size,
struct wl_list *output_list)
write_png(int width, int height)
{
pixman_image_t *shot;
int output_stride, buffer_stride, i;
cairo_surface_t *surface;
struct screenshooter_output *output;
FILE *fp;
char filepath[PATH_MAX];
void *data, *d, *s;
struct screenshooter_output *output, *next;
shot = pixman_image_create_bits(PIXMAN_a8r8g8b8,
buff_size->width, buff_size->height,
NULL, 0);
abort_oom_if_null(shot);
buffer_stride = width * 4;
wl_list_for_each(output, output_list, link) {
client_buffer_util_maybe_sync_dmabuf_start(output->buffer->buf);
data = xmalloc(buffer_stride * height);
if (!data)
return;
pixman_image_composite32(PIXMAN_OP_SRC,
output->buffer->image, /* src */
NULL, /* mask */
shot, /* dest */
0, 0, /* src x,y */
0, 0, /* mask x,y */
output->offset_x, output->offset_y, /* dst x,y */
output->buffer_width, output->buffer_height);
wl_list_for_each_safe(output, next, &output_list, link) {
output_stride = output->width * 4;
s = output->data;
d = data + (output->offset_y - min_y) * buffer_stride +
(output->offset_x - min_x) * 4;
client_buffer_util_maybe_sync_dmabuf_end(output->buffer->buf);
for (i = 0; i < output->height; i++) {
memcpy(d, s, output_stride);
d += buffer_stride;
s += output_stride;
}
free(output);
}
surface = cairo_image_surface_create_for_data((void *)pixman_image_get_data(shot),
surface = cairo_image_surface_create_for_data(data,
CAIRO_FORMAT_ARGB32,
pixman_image_get_width(shot),
pixman_image_get_height(shot),
pixman_image_get_stride(shot));
fp = file_create_dated(getenv("XDG_PICTURES_DIR"), "wayland-screenshot-",
".png", filepath, sizeof(filepath));
if (fp) {
fclose (fp);
cairo_surface_write_to_png(surface, filepath);
}
width, height, buffer_stride);
cairo_surface_write_to_png(surface, "wayland-screenshot.png");
cairo_surface_destroy(surface);
pixman_image_unref(shot);
}
static void
screenshot_write_yuv(const struct buffer_size *buff_size,
struct wl_list *output_list)
{
struct screenshooter_output *output;
int i = 0;
wl_list_for_each(output, output_list, link) {
struct screenshooter_buffer *buffer = output->buffer;
char filepath[PATH_MAX];
char filepath_prefix[100];
int write_offset = 0;
FILE *fp;
sprintf(filepath_prefix, "wayland-screenshot-output-%d-", i++);
fp = file_create_dated(getenv("XDG_PICTURES_DIR"),
filepath_prefix, ".yuv", filepath,
sizeof(filepath));
if (!fp) {
fprintf(stderr, "Writing yuv file for output %d failed\n", i);
return;
}
client_buffer_util_maybe_sync_dmabuf_start(buffer->buf);
for (unsigned int j = 0; j < pixel_format_get_plane_count(buffer->buf->fmt); j++) {
int plane_height =
buffer->buf->height / pixel_format_hsub(buffer->buf->fmt, j);
for (int k = 0; k < plane_height; k++) {
size_t lines_written;
lines_written = fwrite(buffer->buf->data + write_offset,
buffer->buf->bytes_per_line[j],
1, fp);
if (lines_written != 1) {
fprintf(stderr,
"Writing yuv file for output %d " \
"failed during write(): %s\n",
i, strerror(errno));
return;
}
write_offset += buffer->buf->strides[j];
}
}
fclose (fp);
client_buffer_util_maybe_sync_dmabuf_end(buffer->buf);
}
free(data);
}
static int
screenshot_set_buffer_size(struct buffer_size *buff_size,
struct wl_list *output_list)
set_buffer_size(int *width, int *height)
{
struct screenshooter_output *output;
buff_size->min_x = buff_size->min_y = INT_MAX;
buff_size->max_x = buff_size->max_y = INT_MIN;
min_x = min_y = INT_MAX;
max_x = max_y = INT_MIN;
int position = 0;
wl_list_for_each_reverse(output, output_list, link) {
wl_list_for_each_reverse(output, &output_list, link) {
output->offset_x = position;
position += output->buffer_width;
position += output->width;
}
wl_list_for_each(output, output_list, link) {
buff_size->min_x = MIN(buff_size->min_x, output->offset_x);
buff_size->min_y = MIN(buff_size->min_y, output->offset_y);
buff_size->max_x =
MAX(buff_size->max_x, output->offset_x + output->buffer_width);
buff_size->max_y =
MAX(buff_size->max_y, output->offset_y + output->buffer_height);
wl_list_for_each(output, &output_list, link) {
min_x = MIN(min_x, output->offset_x);
min_y = MIN(min_y, output->offset_y);
max_x = MAX(max_x, output->offset_x + output->width);
max_y = MAX(max_y, output->offset_y + output->height);
}
if (buff_size->max_x <= buff_size->min_x ||
buff_size->max_y <= buff_size->min_y)
if (max_x <= min_x || max_y <= min_y)
return -1;
buff_size->width = buff_size->max_x - buff_size->min_x;
buff_size->height = buff_size->max_y - buff_size->min_y;
*width = max_x - min_x;
*height = max_y - min_y;
return 0;
}
static bool
received_formats_for_all_outputs(struct screenshooter_app *app)
int main(int argc, char *argv[])
{
struct wl_display *display;
struct wl_registry *registry;
struct screenshooter_output *output;
int width, height;
wl_list_for_each(output, &app->output_list, link) {
if (!output->formats_done)
return false;
}
return true;
}
static bool
all_output_formats_are_yuv(struct wl_list *output_list)
{
struct screenshooter_output *output;
int color_model = -1;
wl_list_for_each(output, output_list, link) {
if (color_model == -1) {
color_model = output->buffer->buf->fmt->color_model;
continue;
}
if ((int)output->buffer->buf->fmt->color_model != color_model) {
fprintf(stderr, "Mixing of RGB and YUV output formats not supported\n");
exit(1);
}
}
assert(color_model == (int)COLOR_MODEL_RGB ||
color_model == (int)COLOR_MODEL_YUV);
return color_model == COLOR_MODEL_YUV;
}
static void
print_usage_and_exit(void)
{
printf("usage flags:\n"
"\t'-h,--help'"
"\n\t\tprint this help output\n"
"\t'-v,--verbose'"
"\n\t\tprint additional output\n"
"\t'-f,--format=<>'"
"\n\t\tthe DRM format name to use without the DRM_FORMAT_ prefix, e.g. RGBA8888 or NV12\n"
"\n\t\tIn case of YCbCr formats like NV12, instead of a single .png, the output will consist of raw .yuv files for each output."
"\n\t\tThese files do not contain any metadata, however that can be added by converting to .y4m with a command like:"
"\n\t\tffmpeg -s 1024x768 -r 1 -pix_fmt yuv420p -i ~/wayland-screenshot-output-0-2025-08-01_15-58-24.yuv -c:v copy screenshot.y4m\n"
"\n\t\tNote that this may not work for all YCbCr pixel formats.\n"
"\t'-s,--source-type=<>'"
"\n\t\tframebuffer to use framebuffer source (default), "
"\n\t\twriteback to use writeback source\n"
"\t'-b,--buffer-type=<>'"
"\n\t\tshm to use a SHM buffer (default), "
"\n\t\tdmabuf to use a DMA buffer\n");
exit(0);
}
static const struct weston_enum_map source_types [] = {
{ "framebuffer", WESTON_CAPTURE_V1_SOURCE_FRAMEBUFFER },
{ "writeback", WESTON_CAPTURE_V1_SOURCE_WRITEBACK },
};
static const struct weston_enum_map buffer_types [] = {
{ "shm", CLIENT_BUFFER_TYPE_SHM },
{ "dmabuf", CLIENT_BUFFER_TYPE_DMABUF },
};
int
main(int argc, char *argv[])
{
struct screenshooter_output *output;
struct screenshooter_output *tmp_output;
struct buffer_size buff_size = {};
struct screenshooter_app app = {};
int c, option_index;
app.src_type = WESTON_CAPTURE_V1_SOURCE_FRAMEBUFFER;
app.buffer_type = CLIENT_BUFFER_TYPE_SHM;
static struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"verbose", no_argument, NULL, 'v'},
{"format", required_argument, NULL, 'f'},
{"source-type", required_argument, NULL, 's'},
{"buffer-type", required_argument, NULL, 'b'},
{0, 0, 0, 0}
};
while ((c = getopt_long(argc, argv, "hvf:s:b:",
long_options, &option_index)) != -1) {
const struct weston_enum_map *entry;
switch(c) {
case 'v':
app.verbose = true;
break;
case 'f':
app.requested_format = pixel_format_get_info_by_drm_name(optarg);
if (!app.requested_format) {
fprintf(stderr, "Unknown format %s\n", optarg);
return -1;
}
break;
case 's':
entry = weston_enum_map_find_name(source_types,
optarg);
if (!entry)
print_usage_and_exit();
app.src_type = entry->value;
break;
case 'b':
entry = weston_enum_map_find_name(buffer_types,
optarg);
if (!entry)
print_usage_and_exit();
app.buffer_type = entry->value;
break;
default:
print_usage_and_exit();
}
}
wl_list_init(&app.output_list);
app.display = wl_display_connect(NULL);
if (app.display == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
if (getenv("WAYLAND_SOCKET") == NULL) {
fprintf(stderr, "%s must be launched by weston.\n"
"Use the MOD+S shortcut to take a screenshot.\n",
program_invocation_short_name);
return -1;
}
app.registry = wl_display_get_registry(app.display);
wl_registry_add_listener(app.registry, &registry_listener, &app);
/* Process wl_registry advertisements */
wl_display_roundtrip(app.display);
if (!app.capture_factory) {
fprintf(stderr, "Error: display does not support weston_capture_v1\n");
display = wl_display_connect(NULL);
if (display == NULL) {
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
if(app.buffer_type == CLIENT_BUFFER_TYPE_SHM && !app.shm) {
fprintf(stderr, "Error: display does not support wl_shm\n");
return -1;
}
if (app.buffer_type == CLIENT_BUFFER_TYPE_DMABUF && !app.dmabuf) {
fprintf(stderr, "Error: Compositor does not support zwp_linux_dmabuf_v1\n");
wl_list_init(&output_list);
registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (screenshooter == NULL) {
fprintf(stderr, "display doesn't support screenshooter\n");
return -1;
}
if (app.verbose) {
printf("Taking screenshot with %s source %s buffer\n",
(app.src_type == WESTON_CAPTURE_V1_SOURCE_FRAMEBUFFER) ? "framebuffer" : "writeback",
(app.buffer_type == CLIENT_BUFFER_TYPE_SHM) ? "shm" : "dma");
weston_screenshooter_add_listener(screenshooter,
&screenshooter_listener,
screenshooter);
if (set_buffer_size(&width, &height))
return -1;
wl_list_for_each(output, &output_list, link) {
output->buffer = create_shm_buffer(output->width, output->height, &output->data);
weston_screenshooter_shoot(screenshooter,
output->output,
output->buffer);
buffer_copy_done = 0;
while (!buffer_copy_done)
wl_display_roundtrip(display);
}
/* Process initial events for wl_output and weston_capture_source_v1 */
wl_display_roundtrip(app.display);
while (!received_formats_for_all_outputs(&app)) {
if (app.verbose)
printf("Waiting for compositor to send capture source data\n");
if (wl_display_dispatch(app.display) < 0) {
fprintf(stderr, "Error: connection terminated\n");
return -1;
}
}
do {
app.retry = false;
wl_list_for_each(output, &app.output_list, link)
screenshooter_output_capture(output);
while (app.waitcount > 0 && !app.failed) {
if (wl_display_dispatch(app.display) < 0)
app.failed = true;
assert(app.waitcount >= 0);
}
} while (app.retry && !app.failed);
if (!app.failed) {
if (screenshot_set_buffer_size(&buff_size, &app.output_list) < 0)
return -1;
if (all_output_formats_are_yuv(&app.output_list))
screenshot_write_yuv(&buff_size, &app.output_list);
else
screenshot_write_png(&buff_size, &app.output_list);
} else {
fprintf(stderr, "Error: screenshot or protocol failure\n");
}
wl_list_for_each_safe(output, tmp_output, &app.output_list, link)
destroy_output(output);
weston_capture_v1_destroy(app.capture_factory);
wl_shm_destroy(app.shm);
if (app.dmabuf)
zwp_linux_dmabuf_v1_destroy(app.dmabuf);
wl_registry_destroy(app.registry);
wl_display_disconnect(app.display);
write_png(width, height);
return 0;
}

View file

@ -35,12 +35,12 @@
#include <sys/mman.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <wayland-client.h>
#include "shared/os-compatibility.h"
#include <libweston/zalloc.h>
#include "xdg-shell-client-protocol.h"
#include "shared/zalloc.h"
#include "xdg-shell-unstable-v6-client-protocol.h"
#include "fullscreen-shell-unstable-v1-client-protocol.h"
#include "viewporter-client-protocol.h"
int print_debug = 0;
@ -51,7 +51,8 @@ struct display {
int compositor_version;
struct wl_compositor *compositor;
struct wp_viewporter *viewporter;
struct xdg_wm_base *wm_base;
struct zxdg_shell_v6 *shell;
struct zwp_fullscreen_shell_v1 *fshell;
struct wl_shm *shm;
uint32_t formats;
};
@ -73,8 +74,8 @@ struct window {
int width, height, border;
struct wl_surface *surface;
struct wp_viewport *viewport;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct zxdg_surface_v6 *xdg_surface;
struct zxdg_toplevel_v6 *xdg_toplevel;
struct wl_callback *callback;
struct buffer buffers[2];
struct buffer *prev_buffer;
@ -122,14 +123,14 @@ create_shm_buffer(struct display *display, struct buffer *buffer,
fd = os_create_anonymous_file(size);
if (fd < 0) {
fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
size, strerror(errno));
fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
size);
return -1;
}
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
fprintf(stderr, "mmap failed: %m\n");
close(fd);
return -1;
}
@ -148,12 +149,12 @@ create_shm_buffer(struct display *display, struct buffer *buffer,
}
static void
xdg_surface_handle_configure(void *data, struct xdg_surface *surface,
xdg_surface_handle_configure(void *data, struct zxdg_surface_v6 *surface,
uint32_t serial)
{
struct window *window = data;
xdg_surface_ack_configure(surface, serial);
zxdg_surface_v6_ack_configure(surface, serial);
if (window->wait_for_configure) {
redraw(window, NULL, 0);
@ -161,24 +162,24 @@ xdg_surface_handle_configure(void *data, struct xdg_surface *surface,
}
}
static const struct xdg_surface_listener xdg_surface_listener = {
static const struct zxdg_surface_v6_listener xdg_surface_listener = {
xdg_surface_handle_configure,
};
static void
xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *toplevel,
xdg_toplevel_handle_configure(void *data, struct zxdg_toplevel_v6 *toplevel,
int32_t width, int32_t height,
struct wl_array *states)
{
}
static void
xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel)
xdg_toplevel_handle_close(void *data, struct zxdg_toplevel_v6 *xdg_toplevel)
{
running = 0;
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = {
xdg_toplevel_handle_configure,
xdg_toplevel_handle_close,
};
@ -316,30 +317,33 @@ create_window(struct display *display, int width, int height,
window->viewport = wp_viewporter_get_viewport(display->viewporter,
window->surface);
if (display->wm_base) {
if (display->shell) {
window->xdg_surface =
xdg_wm_base_get_xdg_surface(display->wm_base,
window->surface);
zxdg_shell_v6_get_xdg_surface(display->shell,
window->surface);
assert(window->xdg_surface);
xdg_surface_add_listener(window->xdg_surface,
&xdg_surface_listener, window);
zxdg_surface_v6_add_listener(window->xdg_surface,
&xdg_surface_listener, window);
window->xdg_toplevel =
xdg_surface_get_toplevel(window->xdg_surface);
zxdg_surface_v6_get_toplevel(window->xdg_surface);
assert(window->xdg_toplevel);
xdg_toplevel_add_listener(window->xdg_toplevel,
&xdg_toplevel_listener, window);
zxdg_toplevel_v6_add_listener(window->xdg_toplevel,
&xdg_toplevel_listener, window);
xdg_toplevel_set_title(window->xdg_toplevel, "simple-damage");
xdg_toplevel_set_app_id(window->xdg_toplevel,
"org.freedesktop.weston.simple-damage");
zxdg_toplevel_v6_set_title(window->xdg_toplevel, "simple-damage");
window->wait_for_configure = true;
wl_surface_commit(window->surface);
} else if (display->fshell) {
zwp_fullscreen_shell_v1_present_surface(display->fshell,
window->surface,
ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT,
NULL);
} else {
assert(0);
}
@ -366,9 +370,9 @@ destroy_window(struct window *window)
wl_buffer_destroy(window->buffers[1].buffer);
if (window->xdg_toplevel)
xdg_toplevel_destroy(window->xdg_toplevel);
zxdg_toplevel_v6_destroy(window->xdg_toplevel);
if (window->xdg_surface)
xdg_surface_destroy(window->xdg_surface);
zxdg_surface_v6_destroy(window->xdg_surface);
if (window->viewport)
wp_viewport_destroy(window->viewport);
wl_surface_destroy(window->surface);
@ -456,32 +460,32 @@ window_get_transformed_ball(struct window *window, float *bx, float *by)
*by = wy;
break;
case WL_OUTPUT_TRANSFORM_90:
*bx = wy;
*by = window->width - wx;
*bx = window->height - wy;
*by = wx;
break;
case WL_OUTPUT_TRANSFORM_180:
*bx = window->width - wx;
*by = window->height - wy;
break;
case WL_OUTPUT_TRANSFORM_270:
*bx = window->height - wy;
*by = wx;
*bx = wy;
*by = window->width - wx;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED:
*bx = window->width - wx;
*by = wy;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
*bx = wy;
*by = wx;
*bx = window->height - wy;
*by = window->width - wx;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
*bx = wx;
*by = window->height - wy;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
*bx = window->height - wy;
*by = window->width - wx;
*bx = wy;
*by = wx;
break;
}
@ -497,20 +501,6 @@ window_get_transformed_ball(struct window *window, float *bx, float *by)
static const struct wl_callback_listener frame_listener;
static void
set_opaque_region(struct window *window)
{
struct wl_region *region;
region = wl_compositor_create_region(window->display->compositor);
wl_region_add(region, 0, 0, window->width, window->height);
wl_region_subtract(region, window->border, window->border,
window->width - 2 * window->border,
window->height - 2 * window->border);
wl_surface_set_opaque_region(window->surface, region);
wl_region_destroy(region);
}
static void
redraw(void *data, struct wl_callback *callback, uint32_t time)
{
@ -579,32 +569,32 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
off_x = tx;
break;
case WL_OUTPUT_TRANSFORM_90:
off_y = bheight - tx;
off_x = ty;
off_y = tx;
off_x = bwidth - ty;
break;
case WL_OUTPUT_TRANSFORM_180:
off_y = bheight - ty;
off_x = bwidth - tx;
break;
case WL_OUTPUT_TRANSFORM_270:
off_y = tx;
off_x = bwidth - ty;
off_y = bheight - tx;
off_x = ty;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED:
off_y = ty;
off_x = bwidth - tx;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
off_y = tx;
off_x = ty;
off_y = bheight - tx;
off_x = bwidth - ty;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
off_y = bheight - ty;
off_x = tx;
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
off_y = bheight - tx;
off_x = bwidth - ty;
off_y = tx;
off_x = ty;
break;
}
wp_viewport_set_source(window->viewport,
@ -698,8 +688,6 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
if (callback)
wl_callback_destroy(callback);
set_opaque_region(window);
window->callback = wl_surface_frame(window->surface);
wl_callback_add_listener(window->callback, &frame_listener, window);
wl_surface_commit(window->surface);
@ -723,13 +711,13 @@ struct wl_shm_listener shm_listener = {
};
static void
xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
xdg_shell_ping(void *data, struct zxdg_shell_v6*shell, uint32_t serial)
{
xdg_wm_base_pong(shell, serial);
zxdg_shell_v6_pong(shell, serial);
}
static const struct xdg_wm_base_listener wm_base_listener = {
xdg_wm_base_ping,
static const struct zxdg_shell_v6_listener xdg_shell_listener = {
xdg_shell_ping,
};
static void
@ -755,10 +743,13 @@ registry_handle_global(void *data, struct wl_registry *registry,
} else if (strcmp(interface, "wp_viewporter") == 0) {
d->viewporter = wl_registry_bind(registry, id,
&wp_viewporter_interface, 1);
} else if (strcmp(interface, "xdg_wm_base") == 0) {
d->wm_base = wl_registry_bind(registry,
id, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(d->wm_base, &wm_base_listener, d);
} else if (strcmp(interface, "zxdg_shell_v6") == 0) {
d->shell = wl_registry_bind(registry,
id, &zxdg_shell_v6_interface, 1);
zxdg_shell_v6_add_listener(d->shell, &xdg_shell_listener, d);
} else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
d->fshell = wl_registry_bind(registry,
id, &zwp_fullscreen_shell_v1_interface, 1);
} else if (strcmp(interface, "wl_shm") == 0) {
d->shm = wl_registry_bind(registry,
id, &wl_shm_interface, 1);
@ -782,7 +773,7 @@ create_display(int version)
{
struct display *display;
display = zalloc(sizeof *display);
display = malloc(sizeof *display);
if (display == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
@ -817,8 +808,11 @@ destroy_display(struct display *display)
if (display->shm)
wl_shm_destroy(display->shm);
if (display->wm_base)
xdg_wm_base_destroy(display->wm_base);
if (display->shell)
zxdg_shell_v6_destroy(display->shell);
if (display->fshell)
zwp_fullscreen_shell_v1_release(display->fshell);
if (display->viewporter)
wp_viewporter_destroy(display->viewporter);
@ -954,7 +948,7 @@ main(int argc, char **argv)
while (running && ret != -1)
ret = wl_display_dispatch(display->display);
fprintf(stderr, "simple-damage exiting\n");
fprintf(stderr, "simple-shm exiting\n");
destroy_window(window);
destroy_display(display);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,624 @@
/*
* Copyright © 2011 Benjamin Franzke
* Copyright © 2010 Intel Corporation
* 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <unistd.h>
#include <sys/mman.h>
#include <signal.h>
#include <fcntl.h>
#include <xf86drm.h>
#include <i915_drm.h>
#include <intel_bufmgr.h>
#include <drm_fourcc.h>
#include <wayland-client.h>
#include "shared/zalloc.h"
#include "xdg-shell-unstable-v6-client-protocol.h"
#include "fullscreen-shell-unstable-v1-client-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
struct display {
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct zxdg_shell_v6 *shell;
struct zwp_fullscreen_shell_v1 *fshell;
struct zwp_linux_dmabuf_v1 *dmabuf;
int xrgb8888_format_found;
};
struct buffer {
struct wl_buffer *buffer;
int busy;
int drm_fd;
drm_intel_bufmgr *bufmgr;
drm_intel_bo *bo;
uint32_t gem_handle;
int dmabuf_fd;
uint8_t *mmap;
int width;
int height;
int bpp;
unsigned long stride;
};
#define NUM_BUFFERS 3
struct window {
struct display *display;
int width, height;
struct wl_surface *surface;
struct zxdg_surface_v6 *xdg_surface;
struct zxdg_toplevel_v6 *xdg_toplevel;
struct buffer buffers[NUM_BUFFERS];
struct buffer *prev_buffer;
struct wl_callback *callback;
bool initialized;
bool wait_for_configure;
};
static int running = 1;
static void
redraw(void *data, struct wl_callback *callback, uint32_t time);
static void
buffer_release(void *data, struct wl_buffer *buffer)
{
struct buffer *mybuf = data;
mybuf->busy = 0;
}
static const struct wl_buffer_listener buffer_listener = {
buffer_release
};
static int
drm_connect(struct buffer *my_buf)
{
/* This won't work with card0 as we need to be authenticated; instead,
* boot with drm.rnodes=1 and use that. */
my_buf->drm_fd = open("/dev/dri/renderD128", O_RDWR);
if (my_buf->drm_fd < 0)
return 0;
my_buf->bufmgr = drm_intel_bufmgr_gem_init(my_buf->drm_fd, 32);
if (!my_buf->bufmgr)
return 0;
return 1;
}
static void
drm_shutdown(struct buffer *my_buf)
{
drm_intel_bufmgr_destroy(my_buf->bufmgr);
close(my_buf->drm_fd);
}
static int
alloc_bo(struct buffer *my_buf)
{
/* XXX: try different tiling modes for testing FB modifiers. */
uint32_t tiling = I915_TILING_NONE;
assert(my_buf->bufmgr);
my_buf->bo = drm_intel_bo_alloc_tiled(my_buf->bufmgr, "test",
my_buf->width, my_buf->height,
(my_buf->bpp / 8), &tiling,
&my_buf->stride, 0);
printf("buffer allocated w %d, h %d, stride %lu, size %lu\n",
my_buf->width, my_buf->height, my_buf->stride, my_buf->bo->size);
if (!my_buf->bo)
return 0;
if (tiling != I915_TILING_NONE)
return 0;
return 1;
}
static void
free_bo(struct buffer *my_buf)
{
drm_intel_bo_unreference(my_buf->bo);
}
static int
map_bo(struct buffer *my_buf)
{
if (drm_intel_gem_bo_map_gtt(my_buf->bo) != 0)
return 0;
my_buf->mmap = my_buf->bo->virtual;
return 1;
}
static void
fill_content(struct buffer *my_buf)
{
int x = 0, y = 0;
uint32_t *pix;
assert(my_buf->mmap);
for (y = 0; y < my_buf->height; y++) {
pix = (uint32_t *)(my_buf->mmap + y * my_buf->stride);
for (x = 0; x < my_buf->width; x++) {
*pix++ = (0xff << 24) | ((x % 256) << 16) |
((y % 256) << 8) | 0xf0;
}
}
}
static void
unmap_bo(struct buffer *my_buf)
{
drm_intel_gem_bo_unmap_gtt(my_buf->bo);
}
static void
create_succeeded(void *data,
struct zwp_linux_buffer_params_v1 *params,
struct wl_buffer *new_buffer)
{
struct buffer *buffer = data;
buffer->buffer = new_buffer;
wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer);
zwp_linux_buffer_params_v1_destroy(params);
}
static void
create_failed(void *data, struct zwp_linux_buffer_params_v1 *params)
{
struct buffer *buffer = data;
buffer->buffer = NULL;
running = 0;
zwp_linux_buffer_params_v1_destroy(params);
fprintf(stderr, "Error: zwp_linux_buffer_params.create failed.\n");
}
static const struct zwp_linux_buffer_params_v1_listener params_listener = {
create_succeeded,
create_failed
};
static int
create_dmabuf_buffer(struct display *display, struct buffer *buffer,
int width, int height)
{
struct zwp_linux_buffer_params_v1 *params;
uint64_t modifier;
uint32_t flags;
if (!drm_connect(buffer)) {
fprintf(stderr, "drm_connect failed\n");
goto error;
}
buffer->width = width;
buffer->height = height;
buffer->bpp = 32; /* hardcoded XRGB8888 format */
if (!alloc_bo(buffer)) {
fprintf(stderr, "alloc_bo failed\n");
goto error1;
}
if (!map_bo(buffer)) {
fprintf(stderr, "map_bo failed\n");
goto error2;
}
fill_content(buffer);
unmap_bo(buffer);
if (drm_intel_bo_gem_export_to_prime(buffer->bo, &buffer->dmabuf_fd) != 0) {
fprintf(stderr, "drm_intel_bo_gem_export_to_prime failed\n");
goto error2;
}
if (buffer->dmabuf_fd < 0) {
fprintf(stderr, "error: dmabuf_fd < 0\n");
goto error2;
}
/* We now have a dmabuf! It should contain 2x2 tiles (i.e. each tile
* is 256x256) of misc colours, and be mappable, either as ARGB8888, or
* XRGB8888. */
modifier = 0;
flags = 0;
params = zwp_linux_dmabuf_v1_create_params(display->dmabuf);
zwp_linux_buffer_params_v1_add(params,
buffer->dmabuf_fd,
0, /* plane_idx */
0, /* offset */
buffer->stride,
modifier >> 32,
modifier & 0xffffffff);
zwp_linux_buffer_params_v1_add_listener(params, &params_listener, buffer);
zwp_linux_buffer_params_v1_create(params,
buffer->width,
buffer->height,
DRM_FORMAT_XRGB8888,
flags);
return 0;
error2:
free_bo(buffer);
error1:
drm_shutdown(buffer);
error:
return -1;
}
static void
xdg_surface_handle_configure(void *data, struct zxdg_surface_v6 *surface,
uint32_t serial)
{
struct window *window = data;
zxdg_surface_v6_ack_configure(surface, serial);
if (window->initialized && window->wait_for_configure)
redraw(window, NULL, 0);
window->wait_for_configure = false;
}
static const struct zxdg_surface_v6_listener xdg_surface_listener = {
xdg_surface_handle_configure,
};
static void
xdg_toplevel_handle_configure(void *data, struct zxdg_toplevel_v6 *toplevel,
int32_t width, int32_t height,
struct wl_array *states)
{
}
static void
xdg_toplevel_handle_close(void *data, struct zxdg_toplevel_v6 *xdg_toplevel)
{
running = 0;
}
static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = {
xdg_toplevel_handle_configure,
xdg_toplevel_handle_close,
};
static struct window *
create_window(struct display *display, int width, int height)
{
struct window *window;
int i;
int ret;
window = zalloc(sizeof *window);
if (!window)
return NULL;
window->callback = NULL;
window->display = display;
window->width = width;
window->height = height;
window->surface = wl_compositor_create_surface(display->compositor);
if (display->shell) {
window->xdg_surface =
zxdg_shell_v6_get_xdg_surface(display->shell,
window->surface);
assert(window->xdg_surface);
zxdg_surface_v6_add_listener(window->xdg_surface,
&xdg_surface_listener, window);
window->xdg_toplevel =
zxdg_surface_v6_get_toplevel(window->xdg_surface);
assert(window->xdg_toplevel);
zxdg_toplevel_v6_add_listener(window->xdg_toplevel,
&xdg_toplevel_listener, window);
zxdg_toplevel_v6_set_title(window->xdg_toplevel, "simple-dmabuf");
window->wait_for_configure = true;
wl_surface_commit(window->surface);
} else if (display->fshell) {
zwp_fullscreen_shell_v1_present_surface(display->fshell,
window->surface,
ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT,
NULL);
} else {
assert(0);
}
for (i = 0; i < NUM_BUFFERS; ++i) {
ret = create_dmabuf_buffer(display, &window->buffers[i],
width, height);
if (ret < 0)
return NULL;
}
return window;
}
static void
destroy_window(struct window *window)
{
int i;
if (window->callback)
wl_callback_destroy(window->callback);
for (i = 0; i < NUM_BUFFERS; i++) {
if (!window->buffers[i].buffer)
continue;
wl_buffer_destroy(window->buffers[i].buffer);
free_bo(&window->buffers[i]);
close(window->buffers[i].dmabuf_fd);
drm_shutdown(&window->buffers[i]);
}
if (window->xdg_toplevel)
zxdg_toplevel_v6_destroy(window->xdg_toplevel);
if (window->xdg_surface)
zxdg_surface_v6_destroy(window->xdg_surface);
wl_surface_destroy(window->surface);
free(window);
}
static struct buffer *
window_next_buffer(struct window *window)
{
int i;
for (i = 0; i < NUM_BUFFERS; i++)
if (!window->buffers[i].busy)
return &window->buffers[i];
return NULL;
}
static const struct wl_callback_listener frame_listener;
static void
redraw(void *data, struct wl_callback *callback, uint32_t time)
{
struct window *window = data;
struct buffer *buffer;
buffer = window_next_buffer(window);
if (!buffer) {
fprintf(stderr,
!callback ? "Failed to create the first buffer.\n" :
"All buffers busy at redraw(). Server bug?\n");
abort();
}
/* XXX: would be nice to draw something that changes here... */
wl_surface_attach(window->surface, buffer->buffer, 0, 0);
wl_surface_damage(window->surface, 0, 0, window->width, window->height);
if (callback)
wl_callback_destroy(callback);
window->callback = wl_surface_frame(window->surface);
wl_callback_add_listener(window->callback, &frame_listener, window);
wl_surface_commit(window->surface);
buffer->busy = 1;
}
static const struct wl_callback_listener frame_listener = {
redraw
};
static void
dmabuf_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format)
{
struct display *d = data;
if (format == DRM_FORMAT_XRGB8888)
d->xrgb8888_format_found = 1;
}
static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
dmabuf_format
};
static void
xdg_shell_ping(void *data, struct zxdg_shell_v6 *shell, uint32_t serial)
{
zxdg_shell_v6_pong(shell, serial);
}
static const struct zxdg_shell_v6_listener xdg_shell_listener = {
xdg_shell_ping,
};
static void
registry_handle_global(void *data, struct wl_registry *registry,
uint32_t id, const char *interface, uint32_t version)
{
struct display *d = data;
if (strcmp(interface, "wl_compositor") == 0) {
d->compositor =
wl_registry_bind(registry,
id, &wl_compositor_interface, 1);
} else if (strcmp(interface, "zxdg_shell_v6") == 0) {
d->shell = wl_registry_bind(registry,
id, &zxdg_shell_v6_interface, 1);
zxdg_shell_v6_add_listener(d->shell, &xdg_shell_listener, d);
} else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
d->fshell = wl_registry_bind(registry,
id, &zwp_fullscreen_shell_v1_interface, 1);
} else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) {
d->dmabuf = wl_registry_bind(registry,
id, &zwp_linux_dmabuf_v1_interface, 1);
zwp_linux_dmabuf_v1_add_listener(d->dmabuf, &dmabuf_listener, d);
}
}
static void
registry_handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name)
{
}
static const struct wl_registry_listener registry_listener = {
registry_handle_global,
registry_handle_global_remove
};
static struct display *
create_display(void)
{
struct display *display;
display = malloc(sizeof *display);
if (display == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
display->display = wl_display_connect(NULL);
assert(display->display);
/* XXX: fake, because the compositor does not yet advertise anything */
display->xrgb8888_format_found = 1;
display->registry = wl_display_get_registry(display->display);
wl_registry_add_listener(display->registry,
&registry_listener, display);
wl_display_roundtrip(display->display);
if (display->dmabuf == NULL) {
fprintf(stderr, "No zwp_linux_dmabuf global\n");
exit(1);
}
wl_display_roundtrip(display->display);
if (!display->xrgb8888_format_found) {
fprintf(stderr, "DRM_FORMAT_XRGB8888 not available\n");
exit(1);
}
return display;
}
static void
destroy_display(struct display *display)
{
if (display->dmabuf)
zwp_linux_dmabuf_v1_destroy(display->dmabuf);
if (display->shell)
zxdg_shell_v6_destroy(display->shell);
if (display->fshell)
zwp_fullscreen_shell_v1_release(display->fshell);
if (display->compositor)
wl_compositor_destroy(display->compositor);
wl_registry_destroy(display->registry);
wl_display_flush(display->display);
wl_display_disconnect(display->display);
free(display);
}
static void
signal_int(int signum)
{
running = 0;
}
int
main(int argc, char **argv)
{
struct sigaction sigint;
struct display *display;
struct window *window;
int ret = 0;
display = create_display();
window = create_window(display, 250, 250);
if (!window)
return 1;
sigint.sa_handler = signal_int;
sigemptyset(&sigint.sa_mask);
sigint.sa_flags = SA_RESETHAND;
sigaction(SIGINT, &sigint, NULL);
/* Here we retrieve the linux-dmabuf objects, or error */
wl_display_roundtrip(display->display);
if (!running)
return 1;
window->initialized = true;
if (!window->wait_for_configure)
redraw(window, NULL, 0);
while (running && ret != -1)
ret = wl_display_dispatch(display->display);
fprintf(stderr, "simple-dmabuf exiting\n");
destroy_window(window);
destroy_display(display);
return 0;
}

View file

@ -30,13 +30,14 @@
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <getopt.h>
#include <assert.h>
#include <unistd.h>
#include <sys/mman.h>
#include <signal.h>
#include <fcntl.h>
#include <drm_fourcc.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
@ -45,23 +46,12 @@
#include <linux/input.h>
#include <wayland-client.h>
#include <wayland-cursor.h>
#include <libweston/zalloc.h>
#include "xdg-shell-client-protocol.h"
#include "shared/zalloc.h"
#include "xdg-shell-unstable-v6-client-protocol.h"
#include "fullscreen-shell-unstable-v1-client-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "weston-direct-display-client-protocol.h"
#include "viewporter-client-protocol.h"
#include "shared/helpers.h"
#include "shared/weston-drm-fourcc.h"
#define CLEAR(x) memset(&(x), 0, sizeof(x))
#define OPT_FLAG_INVERT (1 << 0)
#define OPT_FLAG_DIRECT_DISPLAY (1 << 1)
#define WIN_FLAG_FULLSCREEN (1 << 0)
#define WIN_FLAG_FULLSCREEN_CURSOR (1 << 1)
struct window;
static void
redraw(void *data, struct wl_callback *callback, uint32_t time);
@ -88,7 +78,7 @@ static inline const char *
dump_format(uint32_t format, char out[4])
{
#if BYTE_ORDER == BIG_ENDIAN
format = bswap32(format);
format = __builtin_bswap32(format);
#endif
memcpy(out, &format, 4);
return out;
@ -109,23 +99,15 @@ struct display {
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_seat *seat;
struct wl_pointer *pointer;
struct wl_keyboard *keyboard;
struct wl_shm *shm;
struct wl_cursor_theme *cursor_theme;
struct wl_cursor *default_cursor;
struct wl_surface *cursor_surface;
struct xdg_wm_base *wm_base;
struct zxdg_shell_v6 *shell;
struct zwp_fullscreen_shell_v1 *fshell;
struct zwp_linux_dmabuf_v1 *dmabuf;
struct weston_direct_display_v1 *direct_display;
struct wp_viewporter *viewporter;
bool requested_format_found;
uint32_t opts;
int v4l_fd;
struct buffer_format format;
uint32_t drm_format;
struct window *window;
};
struct buffer {
@ -138,20 +120,17 @@ struct buffer {
int data_offsets[VIDEO_MAX_PLANES];
};
#define NUM_BUFFERS 4
#define NUM_BUFFERS 3
struct window {
struct display *display;
struct wl_surface *surface;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct zxdg_surface_v6 *xdg_surface;
struct zxdg_toplevel_v6 *xdg_toplevel;
struct buffer buffers[NUM_BUFFERS];
struct wl_callback *callback;
struct wp_viewport *viewport;
bool wait_for_configure;
bool initialized;
bool fullscreen;
bool fullscreen_cursor;
};
static bool running = true;
@ -217,6 +196,7 @@ static unsigned int
set_format(struct display *display, uint32_t format)
{
struct v4l2_format fmt;
char buf[4];
CLEAR(fmt);
@ -227,33 +207,30 @@ set_format(struct display *display, uint32_t format)
return 0;
}
/* NOTE: pix and pix_mp are in a union, pixelformat member maps between them. */
const int format_matches = fmt.fmt.pix.pixelformat == format;
/* No need to set the format if it already is the one we want */
if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
format_matches)
fmt.fmt.pix.pixelformat == format)
return 1;
if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
format_matches)
fmt.fmt.pix_mp.pixelformat == format)
return fmt.fmt.pix_mp.num_planes;
fmt.fmt.pix.pixelformat = format;
if (display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
fmt.fmt.pix.pixelformat = format;
else
fmt.fmt.pix_mp.pixelformat = format;
if (xioctl(display->v4l_fd, VIDIOC_S_FMT, &fmt) == -1) {
perror("VIDIOC_S_FMT");
return 0;
}
const int format_was_set = fmt.fmt.pix.pixelformat == format;
if (!format_was_set) {
char want_name[4];
char have_name[4];
dump_format(format, want_name);
dump_format(fmt.fmt.pix.pixelformat, have_name);
fprintf(stderr, "Tried to set format: %.4s but have: %.4s\n",
want_name, have_name);
if ((display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
fmt.fmt.pix.pixelformat != format) ||
(display->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
fmt.fmt.pix_mp.pixelformat != format)) {
fprintf(stderr, "Failed to set format to %.4s\n",
dump_format(format, buf));
return 0;
}
@ -268,8 +245,6 @@ v4l_connect(struct display *display, const char *dev_name)
{
struct v4l2_capability cap;
struct v4l2_requestbuffers req;
struct v4l2_input input;
int index_input = -1;
unsigned int num_planes;
display->v4l_fd = open(dev_name, O_RDWR);
@ -287,16 +262,6 @@ v4l_connect(struct display *display, const char *dev_name)
return 0;
}
if (xioctl(display->v4l_fd, VIDIOC_G_INPUT, &index_input) == 0) {
input.index = index_input;
if (xioctl(display->v4l_fd, VIDIOC_ENUMINPUT, &input) == 0) {
if (input.status & V4L2_IN_ST_VFLIP) {
fprintf(stdout, "Found camera sensor y-flipped\n");
display->opts |= OPT_FLAG_INVERT;
}
}
}
if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
display->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
@ -395,33 +360,18 @@ create_dmabuf_buffer(struct display *display, struct buffer *buffer)
struct zwp_linux_buffer_params_v1 *params;
uint64_t modifier;
uint32_t flags;
int i;
unsigned i;
modifier = 0;
flags = 0;
if (display->opts & OPT_FLAG_INVERT)
flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT;
/* XXX: apparently some webcams may actually provide y-inverted images,
* in which case we should set
* flags = ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT
*/
params = zwp_linux_dmabuf_v1_create_params(display->dmabuf);
if ((display->opts & OPT_FLAG_DIRECT_DISPLAY) && display->direct_display) {
weston_direct_display_v1_enable(display->direct_display, params);
if (display->opts & OPT_FLAG_INVERT) {
flags &= ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT;
fprintf(stdout, "dmabuf y-inverted attribute flag was removed"
", as display-direct flag was set\n");
}
}
const int num_planes = (int) display->format.num_planes;
for (i = 0; i < num_planes; ++i) {
fprintf(stderr, "buffer %d, plane %d has dma fd %d and stride "
"%d and modifier %" PRIu64 "\n",
buffer->index, i, buffer->dmabuf_fds[i],
display->format.strides[i], modifier);
for (i = 0; i < display->format.num_planes; ++i)
zwp_linux_buffer_params_v1_add(params,
buffer->dmabuf_fds[i],
i, /* plane_idx */
@ -429,124 +379,8 @@ create_dmabuf_buffer(struct display *display, struct buffer *buffer)
display->format.strides[i],
modifier >> 32,
modifier & 0xffffffff);
}
/* Some v4l2 devices can output NV12, but will do so without the MPLANE
* api. Instead, it outputs both the luminance and chrominance planes
* in the same dma buffer. Here we account for that, and add an extra
* plane from the same buffer if necessary. If it needs an extra plane,
* set the stride of the chrominance plane. NOTE: Also handles cases
* where 3 planes are expected in 1 dma buffer (untested)
*/
enum plane_layout_t {
DISJOINT = 0,
CONTIGUOUS,
};
enum chrom_packing_t {
CHROM_SEPARATE = 0, /* Cr/Cb are in their own planes. */
CHROM_COMBINED, /* Cr/Cb are interleaved. */
};
/* This table contains some planar formats we could fix-up and support. */
const struct planar_layout_t {
/* Format identification. */
uint32_t v4l_fourcc;
/* Disjoint or contigious planes? */
enum plane_layout_t plane_layout;
/* Zero if Cb/Cr in separate planes. */
enum chrom_packing_t chrom_packing;
/* Expected plane count. */
int num_planes;
/* Horizontal sub-sampling for chroma. */
int chroma_subsample_hori;
/* Vertical sub-sampling for chroma. */
int chroma_subsample_vert;
} planar_layouts[] = {
{ V4L2_PIX_FMT_NV12M, DISJOINT, CHROM_COMBINED, 2, 2,2 },
{ V4L2_PIX_FMT_NV21M, DISJOINT, CHROM_COMBINED, 2, 2,2 },
{ V4L2_PIX_FMT_NV16M, DISJOINT, CHROM_COMBINED, 2, 2,1 },
{ V4L2_PIX_FMT_NV61M, DISJOINT, CHROM_COMBINED, 2, 2,1 },
{ V4L2_PIX_FMT_NV12, CONTIGUOUS, CHROM_COMBINED, 2, 2,2 },
{ V4L2_PIX_FMT_NV21, CONTIGUOUS, CHROM_COMBINED, 2, 2,2 },
{ V4L2_PIX_FMT_NV16, CONTIGUOUS, CHROM_COMBINED, 2, 2,1 },
{ V4L2_PIX_FMT_NV61, CONTIGUOUS, CHROM_COMBINED, 2, 2,1 },
{ V4L2_PIX_FMT_NV24, CONTIGUOUS, CHROM_COMBINED, 2, 1,1 },
{ V4L2_PIX_FMT_NV42, CONTIGUOUS, CHROM_COMBINED, 2, 1,1 },
{ V4L2_PIX_FMT_YUV420, CONTIGUOUS, CHROM_SEPARATE, 3, 2,2 },
{ V4L2_PIX_FMT_YVU420, CONTIGUOUS, CHROM_SEPARATE, 3, 2,2 },
{ V4L2_PIX_FMT_YUV420M, DISJOINT, CHROM_SEPARATE, 3, 2,2 },
{ V4L2_PIX_FMT_YVU420M, DISJOINT, CHROM_SEPARATE, 3, 2,2 },
{ 0, 0, 0, 0, 0 },
};
int layoutnr = 0;
int num_missing_planes = 0; /* Non-zero if format needs more planes in dma buf. */
int stride_extra_plane = 0;
int vrtres_extra_plane = 0;
const uint32_t stride0 = display->format.strides[0];
/* Search the table. */
while (planar_layouts[layoutnr].v4l_fourcc) {
const struct planar_layout_t *layout =
planar_layouts + layoutnr;
if (layout->v4l_fourcc == display->format.format) {
/* If disjoint planes are missing, there is nothing to
* salvage. */
if (layout->plane_layout == DISJOINT)
assert(num_planes == layout->num_planes);
/* Is this a case where we need to add 1 or 2 missing
* planes? */
num_missing_planes = layout->num_planes - num_planes;
if (num_missing_planes > 0) {
/* With this knowledge:
* - Stride for Y
* - Packing of chrominance
* - Horizontal subsampling ...we can compute
* the stride for Cr and Cb.
*/
const uint32_t num_chrom_parts =
layout->chrom_packing == CHROM_COMBINED ? 2 : 1;
stride_extra_plane =
stride0 * num_chrom_parts /
layout->chroma_subsample_hori;
vrtres_extra_plane =
display->format.height /
layout->chroma_subsample_vert;
break;
}
}
layoutnr += 1;
}
/* If we determined we need additional planes, add them. */
int offset_in_buffer = buffer->data_offsets[0] +
display->format.height * stride0;
for (i = 0; i < num_missing_planes; ++i) {
/* Add same dma buffer, but with offset for chromimance plane. */
fprintf(stderr,"Adding additional chrominance plane.\n");
zwp_linux_buffer_params_v1_add(params,
buffer->dmabuf_fds[0],
1 + i, /* plane_idx */
offset_in_buffer,
stride_extra_plane,
modifier >> 32,
modifier & 0xffffffff);
offset_in_buffer += vrtres_extra_plane * stride_extra_plane;
}
zwp_linux_buffer_params_v1_add_listener(params, &params_listener, buffer);
fprintf(stderr,"creating buffer of size %dx%d format %c%c%c%c flags %d\n",
display->format.width,
display->format.height,
(display->drm_format >> 0) & 0xff,
(display->drm_format >> 8) & 0xff,
(display->drm_format >> 16) & 0xff,
(display->drm_format >> 24) & 0xff,
flags
);
zwp_linux_buffer_params_v1_add_listener(params, &params_listener,
buffer);
zwp_linux_buffer_params_v1_create(params,
display->format.width,
display->format.height,
@ -623,6 +457,8 @@ dequeue(struct display *display)
return -1;
}
assert(buf.flags & V4L2_BUF_FLAG_DONE);
return buf.index;
}
@ -702,77 +538,42 @@ start_capture(struct display *display)
}
static void
xdg_surface_handle_configure(void *data, struct xdg_surface *surface,
xdg_surface_handle_configure(void *data, struct zxdg_surface_v6 *surface,
uint32_t serial)
{
struct window *window = data;
xdg_surface_ack_configure(surface, serial);
zxdg_surface_v6_ack_configure(surface, serial);
if (window->initialized && window->wait_for_configure)
redraw(window, NULL, 0);
window->wait_for_configure = false;
}
static const struct xdg_surface_listener xdg_surface_listener = {
static const struct zxdg_surface_v6_listener xdg_surface_listener = {
xdg_surface_handle_configure,
};
static void
xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *toplevel,
xdg_toplevel_handle_configure(void *data, struct zxdg_toplevel_v6 *toplevel,
int32_t width, int32_t height,
struct wl_array *states)
{
struct window *window = data;
uint32_t *p;
window->fullscreen = 0;
wl_array_for_each(p, states) {
uint32_t state = *p;
switch (state) {
case XDG_TOPLEVEL_STATE_FULLSCREEN:
window->fullscreen = 1;
break;
}
}
if (!window->viewport)
return;
if (window->fullscreen) {
float ratio_w = (float)width / window->display->format.width;
float ratio_h = (float)height / window->display->format.height;
int32_t viewport_w;
int32_t viewport_h;
if (ratio_w > ratio_h) {
viewport_w = width / ratio_w * ratio_h;
viewport_h = height;
} else {
viewport_w = width;
viewport_h = height / ratio_h * ratio_w;
}
wp_viewport_set_destination(window->viewport, viewport_w,
viewport_h);
} else {
wp_viewport_set_destination(window->viewport, -1, -1);
}
}
static void
xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel)
xdg_toplevel_handle_close(void *data, struct zxdg_toplevel_v6 *xdg_toplevel)
{
running = 0;
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = {
xdg_toplevel_handle_configure,
xdg_toplevel_handle_close,
};
static struct window *
create_window(struct display *display, uint32_t win_flags)
create_window(struct display *display)
{
struct window *window;
@ -784,41 +585,33 @@ create_window(struct display *display, uint32_t win_flags)
window->display = display;
window->surface = wl_compositor_create_surface(display->compositor);
if (display->wm_base) {
if (display->viewporter) {
window->viewport =
wp_viewporter_get_viewport(display->viewporter,
window->surface);
}
if (display->shell) {
window->xdg_surface =
xdg_wm_base_get_xdg_surface(display->wm_base,
window->surface);
zxdg_shell_v6_get_xdg_surface(display->shell,
window->surface);
assert(window->xdg_surface);
xdg_surface_add_listener(window->xdg_surface,
&xdg_surface_listener, window);
zxdg_surface_v6_add_listener(window->xdg_surface,
&xdg_surface_listener, window);
window->xdg_toplevel =
xdg_surface_get_toplevel(window->xdg_surface);
zxdg_surface_v6_get_toplevel(window->xdg_surface);
assert(window->xdg_toplevel);
xdg_toplevel_add_listener(window->xdg_toplevel,
&xdg_toplevel_listener, window);
zxdg_toplevel_v6_add_listener(window->xdg_toplevel,
&xdg_toplevel_listener, window);
xdg_toplevel_set_title(window->xdg_toplevel, "simple-dmabuf-v4l");
xdg_toplevel_set_app_id(window->xdg_toplevel,
"org.freedesktop.weston.simple-dmabuf-v4l");
if (win_flags & WIN_FLAG_FULLSCREEN)
xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL);
if (win_flags & WIN_FLAG_FULLSCREEN_CURSOR)
window->fullscreen_cursor = true;
zxdg_toplevel_v6_set_title(window->xdg_toplevel, "simple-dmabuf-v4l");
window->wait_for_configure = true;
wl_surface_commit(window->surface);
} else if (display->fshell) {
zwp_fullscreen_shell_v1_present_surface(display->fshell,
window->surface,
ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT,
NULL);
} else {
assert(0);
}
@ -835,13 +628,10 @@ destroy_window(struct window *window)
if (window->callback)
wl_callback_destroy(window->callback);
if (window->viewport)
wp_viewport_destroy(window->viewport);
if (window->xdg_toplevel)
xdg_toplevel_destroy(window->xdg_toplevel);
zxdg_toplevel_v6_destroy(window->xdg_toplevel);
if (window->xdg_surface)
xdg_surface_destroy(window->xdg_surface);
zxdg_surface_v6_destroy(window->xdg_surface);
wl_surface_destroy(window->surface);
for (i = 0; i < NUM_BUFFERS; i++) {
@ -890,7 +680,9 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
assert(!buffer->busy);
wl_surface_attach(window->surface, buffer->buffer, 0, 0);
wl_surface_damage(window->surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_damage(window->surface, 0, 0,
window->display->format.width,
window->display->format.height);
if (callback)
wl_callback_destroy(callback);
@ -905,97 +697,18 @@ static const struct wl_callback_listener frame_listener = {
redraw
};
static void
dmabuf_modifier(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf,
uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo)
{
struct display *d = data;
uint64_t modifier = u64_from_u32s(modifier_hi, modifier_lo);
if (format == d->drm_format && modifier == DRM_FORMAT_MOD_LINEAR)
d->requested_format_found = true;
}
static void
dmabuf_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf,
uint32_t format)
{
/* deprecated */
struct display *d = data;
if (format == d->drm_format)
d->requested_format_found = true;
}
static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
dmabuf_format,
dmabuf_modifier
};
static void
pointer_handle_enter(void *data, struct wl_pointer *pointer,
uint32_t serial, struct wl_surface *surface,
wl_fixed_t sx, wl_fixed_t sy)
{
struct display *display = data;
struct wl_buffer *buffer;
struct wl_cursor *cursor = display->default_cursor;
struct wl_cursor_image *image;
if (display->window->fullscreen && !display->window->fullscreen_cursor)
wl_pointer_set_cursor(pointer, serial, NULL, 0, 0);
else if (cursor) {
image = cursor->images[0];
buffer = wl_cursor_image_get_buffer(image);
if (!buffer)
return;
wl_pointer_set_cursor(pointer, serial,
display->cursor_surface,
image->hotspot_x,
image->hotspot_y);
wl_surface_attach(display->cursor_surface, buffer, 0, 0);
wl_surface_damage(display->cursor_surface, 0, 0,
image->width, image->height);
wl_surface_commit(display->cursor_surface);
}
}
static void
pointer_handle_leave(void *data, struct wl_pointer *pointer,
uint32_t serial, struct wl_surface *surface)
{
}
static void
pointer_handle_motion(void *data, struct wl_pointer *pointer,
uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
{
}
static void
pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, uint32_t time, uint32_t button,
uint32_t state)
{
struct display *display = data;
if (!display->window->xdg_toplevel)
return;
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED)
xdg_toplevel_move(display->window->xdg_toplevel,
display->seat, serial);
}
static void
pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis, wl_fixed_t value)
{
}
static const struct wl_pointer_listener pointer_listener = {
pointer_handle_enter,
pointer_handle_leave,
pointer_handle_motion,
pointer_handle_button,
pointer_handle_axis,
dmabuf_format
};
static void
@ -1026,15 +739,10 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
{
struct display *d = data;
if (!d->wm_base)
if (!d->shell)
return;
if (key == KEY_F11 && state) {
if (d->window->fullscreen)
xdg_toplevel_unset_fullscreen(d->window->xdg_toplevel);
else
xdg_toplevel_set_fullscreen(d->window->xdg_toplevel, NULL);
} else if (key == KEY_ESC && state)
if (key == KEY_ESC && state)
running = false;
}
@ -1060,14 +768,6 @@ seat_handle_capabilities(void *data, struct wl_seat *seat,
{
struct display *d = data;
if ((caps & WL_SEAT_CAPABILITY_POINTER) && !d->pointer) {
d->pointer = wl_seat_get_pointer(seat);
wl_pointer_add_listener(d->pointer, &pointer_listener, d);
} else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && d->pointer) {
wl_pointer_destroy(d->pointer);
d->pointer = NULL;
}
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !d->keyboard) {
d->keyboard = wl_seat_get_keyboard(seat);
wl_keyboard_add_listener(d->keyboard, &keyboard_listener, d);
@ -1082,13 +782,13 @@ static const struct wl_seat_listener seat_listener = {
};
static void
xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
xdg_shell_ping(void *data, struct zxdg_shell_v6 *shell, uint32_t serial)
{
xdg_wm_base_pong(shell, serial);
zxdg_shell_v6_pong(shell, serial);
}
static const struct xdg_wm_base_listener wm_base_listener = {
xdg_wm_base_ping,
static const struct zxdg_shell_v6_listener xdg_shell_listener = {
xdg_shell_ping,
};
static void
@ -1097,44 +797,28 @@ registry_handle_global(void *data, struct wl_registry *registry,
{
struct display *d = data;
if (strcmp(interface, wl_compositor_interface.name) == 0) {
if (strcmp(interface, "wl_compositor") == 0) {
d->compositor =
wl_registry_bind(registry,
id, &wl_compositor_interface, 1);
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
} else if (strcmp(interface, "wl_seat") == 0) {
d->seat = wl_registry_bind(registry,
id, &wl_seat_interface, 1);
wl_seat_add_listener(d->seat, &seat_listener, d);
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
d->shm = wl_registry_bind(registry, id,
&wl_shm_interface, 1);
d->cursor_theme = wl_cursor_theme_load(NULL, 32, d->shm);
if (!d->cursor_theme) {
fprintf(stderr, "unable to load default theme\n");
return;
}
d->default_cursor =
wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr");
if (!d->default_cursor) {
fprintf(stderr, "unable to load default left pointer\n");
// TODO: abort ?
}
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
d->wm_base = wl_registry_bind(registry,
id, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(d->wm_base, &wm_base_listener, d);
} else if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) {
} else if (strcmp(interface, "zxdg_shell_v6") == 0) {
d->shell = wl_registry_bind(registry,
id, &zxdg_shell_v6_interface, 1);
zxdg_shell_v6_add_listener(d->shell, &xdg_shell_listener, d);
} else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
d->fshell = wl_registry_bind(registry,
id, &zwp_fullscreen_shell_v1_interface,
1);
} else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) {
d->dmabuf = wl_registry_bind(registry,
id, &zwp_linux_dmabuf_v1_interface, 3);
id, &zwp_linux_dmabuf_v1_interface,
1);
zwp_linux_dmabuf_v1_add_listener(d->dmabuf, &dmabuf_listener,
d);
} else if (strcmp(interface, weston_direct_display_v1_interface.name) == 0) {
d->direct_display = wl_registry_bind(registry,
id, &weston_direct_display_v1_interface, 1);
} else if (strcmp(interface, wp_viewporter_interface.name) == 0) {
d->viewporter = wl_registry_bind(registry, id,
&wp_viewporter_interface,
1);
}
}
@ -1150,11 +834,11 @@ static const struct wl_registry_listener registry_listener = {
};
static struct display *
create_display(uint32_t requested_format, uint32_t opt_flags)
create_display(uint32_t requested_format)
{
struct display *display;
display = zalloc(sizeof *display);
display = malloc(sizeof *display);
if (display == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
@ -1175,36 +859,28 @@ create_display(uint32_t requested_format, uint32_t opt_flags)
wl_display_roundtrip(display->display);
if (!display->requested_format_found) {
char want_name[4];
/* XXX: fake, because the compositor does not yet advertise anything */
display->requested_format_found = true;
dump_format(requested_format, want_name);
fprintf(stderr, "Requested DRM format %4s not available\n", want_name);
if (!display->requested_format_found) {
fprintf(stderr, "DRM_FORMAT_YUYV not available\n");
exit(1);
}
if (opt_flags)
display->opts = opt_flags;
display->cursor_surface =
wl_compositor_create_surface(display->compositor);
return display;
}
static void
destroy_display(struct display *display)
{
wl_surface_destroy(display->cursor_surface);
if (display->dmabuf)
zwp_linux_dmabuf_v1_destroy(display->dmabuf);
if (display->viewporter)
wp_viewporter_destroy(display->viewporter);
if (display->shell)
zxdg_shell_v6_destroy(display->shell);
if (display->wm_base)
xdg_wm_base_destroy(display->wm_base);
if (display->fshell)
zwp_fullscreen_shell_v1_release(display->fshell);
if (display->compositor)
wl_compositor_destroy(display->compositor);
@ -1218,7 +894,7 @@ destroy_display(struct display *display)
static void
usage(const char *argv0)
{
printf("Usage: %s [-v v4l2_device] [-f v4l2_format] [-d drm_format] [-i|--y-invert] [-g|--d-display] [-s|--fullscreen]\n"
printf("Usage: %s [V4L2 device] [V4L2 format] [DRM format]\n"
"\n"
"The default V4L2 device is /dev/video0\n"
"\n"
@ -1227,16 +903,7 @@ usage(const char *argv0)
"DRM formats are defined in <libdrm/drm_fourcc.h>\n"
"The default for both formats is YUYV.\n"
"If the V4L2 and DRM formats differ, the data is simply "
"reinterpreted rather than converted.\n\n"
"Flags:\n"
"- y-invert force the image to be y-flipped;\n note will be "
"automatically added if we detect if the camera sensor is "
"y-flipped\n"
"- d-display skip importing dmabuf-based buffer into the GPU\n "
"and attempt pass the buffer straight to the display controller\n"
"- fullscreen make the window fullscreen and scale up the image\n"
"- fs-cursor show the cursor in fullscreen mode\n",
argv0);
"reinterpreted rather than converted.\n", argv0);
printf("\n"
"How to set up Vivid the virtual video driver for testing:\n"
@ -1247,11 +914,8 @@ usage(const char *argv0)
" here we assume /dev/video0\n"
"- set the pixel format:\n"
" $ v4l2-ctl -d /dev/video0 --set-fmt-video=width=640,pixelformat=XR24\n"
"- optionally could add 'allocators=0x1' to options as to create"
" the buffer in a dmabuf-contiguous way\n"
" (as some display-controllers require it)\n"
"- launch the demo:\n"
" $ %s -v /dev/video0 -f XR24 -d XR24\n"
" $ %s /dev/video0 XR24 XR24\n"
"You should see a test pattern with color bars, and some text.\n"
"\n"
"More about vivid: https://www.kernel.org/doc/Documentation/video4linux/vivid.txt\n"
@ -1272,69 +936,32 @@ main(int argc, char **argv)
struct sigaction sigint;
struct display *display;
struct window *window;
const char *v4l_device = NULL;
uint32_t v4l_format = 0x0;
uint32_t drm_format = 0x0;
uint32_t opts_flags = 0x0;
uint32_t win_flags = 0x0;
int c, opt_index, ret = 0;
const char *v4l_device;
uint32_t v4l_format, drm_format;
int ret = 0;
static struct option long_options[] = {
{ "v4l2-device", required_argument, NULL, 'v' },
{ "v4l2-format", required_argument, NULL, 'f' },
{ "drm-format", required_argument, NULL, 'd' },
{ "y-invert", no_argument, NULL, 'i' },
{ "d-display", no_argument, NULL, 'g' },
{ "fullscreen", no_argument, NULL, 's' },
{ "fs-cursor", no_argument, NULL, 'c' },
{ "help", no_argument, NULL, 'h' },
{ 0, 0, NULL, 0 }
};
while ((c = getopt_long(argc, argv, "hiv:d:f:gsc", long_options,
&opt_index)) != -1) {
switch (c) {
case 'v':
v4l_device = optarg;
break;
case 'f':
v4l_format = parse_format(optarg);
break;
case 'd':
drm_format = parse_format(optarg);
break;
case 'i':
opts_flags |= OPT_FLAG_INVERT;
break;
case 'g':
opts_flags |= OPT_FLAG_DIRECT_DISPLAY;
break;
case 's':
win_flags |= WIN_FLAG_FULLSCREEN;
break;
case 'c':
win_flags |= WIN_FLAG_FULLSCREEN_CURSOR;
break;
default:
case 'h':
usage(argv[0]);
break;
}
if (argc < 2) {
v4l_device = "/dev/video0";
} else if (!strcmp(argv[1], "--help")) {
usage(argv[0]);
} else {
v4l_device = argv[1];
}
if (!v4l_device)
v4l_device = "/dev/video0";
if (v4l_format == 0x0)
if (argc < 3)
v4l_format = parse_format("YUYV");
else
v4l_format = parse_format(argv[2]);
if (drm_format == 0x0)
if (argc < 4)
drm_format = v4l_format;
else
drm_format = parse_format(argv[3]);
display = create_display(drm_format, opts_flags);
display = create_display(drm_format);
display->format.format = v4l_format;
display->window = window = create_window(display, win_flags);
window = create_window(display);
if (!window)
return 1;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -28,9 +28,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <signal.h>
#include <linux/input.h>
@ -106,8 +104,6 @@ static const uint32_t ignore_keys_on_compose[] = {
XKB_KEY_Shift_R
};
static int running = 1;
static void
handle_surrounding_text(void *data,
struct zwp_input_method_context_v1 *context,
@ -279,8 +275,8 @@ input_method_keyboard_modifiers(void *data,
keyboard->modifiers |= MOD_SHIFT_MASK;
zwp_input_method_context_v1_modifiers(context, serial,
mods_depressed, mods_latched,
mods_locked, group);
mods_depressed, mods_depressed,
mods_latched, group);
}
static const struct wl_keyboard_listener input_method_keyboard_listener = {
@ -397,7 +393,7 @@ simple_im_key_handler(struct simple_im *keyboard,
if (keyboard->compose_state == state_compose) {
uint32_t i = 0;
const struct compose_seq *cs;
struct compose_seq *cs;
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
return;
@ -484,25 +480,17 @@ simple_im_key_handler(struct simple_im *keyboard,
text);
}
static void
signal_int(int signum)
{
running = 0;
}
int
main(int argc, char *argv[])
{
struct simple_im simple_im;
struct sigaction sigint;
int ret = 0;
memset(&simple_im, 0, sizeof(simple_im));
simple_im.display = wl_display_connect(NULL);
if (simple_im.display == NULL) {
fprintf(stderr, "Failed to connect to server: %s\n",
strerror(errno));
fprintf(stderr, "Failed to connect to server: %m\n");
return -1;
}
@ -524,33 +512,11 @@ main(int argc, char *argv[])
simple_im.context = NULL;
simple_im.key_handler = simple_im_key_handler;
sigint.sa_handler = signal_int;
sigemptyset(&sigint.sa_mask);
sigint.sa_flags = SA_RESETHAND;
sigaction(SIGINT, &sigint, NULL);
while (running && ret != -1)
while (ret != -1)
ret = wl_display_dispatch(simple_im.display);
if (simple_im.input_method)
zwp_input_method_v1_destroy(simple_im.input_method);
if (simple_im.context)
zwp_input_method_context_v1_destroy(simple_im.context);
if (simple_im.keyboard)
wl_keyboard_destroy(simple_im.keyboard);
xkb_context_unref(simple_im.xkb_context);
xkb_state_unref(simple_im.state);
xkb_keymap_unref(simple_im.keymap);
wl_registry_destroy(simple_im.registry);
wl_display_flush(simple_im.display);
wl_display_disconnect(simple_im.display);
if (ret == -1) {
fprintf(stderr, "Dispatch error: %s\n", strerror(errno));
fprintf(stderr, "Dispatch error: %m\n");
return -1;
}

View file

@ -33,130 +33,45 @@
#include <unistd.h>
#include <sys/mman.h>
#include <signal.h>
#include <errno.h>
#include <assert.h>
#include <linux/input.h>
#include <wayland-client.h>
#include "shared/os-compatibility.h"
#include <libweston/zalloc.h>
#include "xdg-shell-client-protocol.h"
#include "shared/zalloc.h"
#include "xdg-shell-unstable-v6-client-protocol.h"
#include "fullscreen-shell-unstable-v1-client-protocol.h"
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
#define FMT(fmt, bpp, r, g, b, a) { WL_SHM_FORMAT_ ## fmt, #fmt, bpp, { r, g, b, a } }
#define MAX_BUFFER_ALLOC 2
struct window;
struct format {
uint32_t code;
const char *string;
int bpp;
uint64_t color[4];
};
#include <sys/types.h>
#include "ivi-application-client-protocol.h"
#define IVI_SURFACE_ID 9000
struct display {
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct xdg_wm_base *wm_base;
struct wl_seat *seat;
struct wl_keyboard *keyboard;
struct zxdg_shell_v6 *shell;
struct zwp_fullscreen_shell_v1 *fshell;
struct wl_shm *shm;
const struct format *format;
bool paint_format;
bool has_format;
struct window *window;
bool has_xrgb;
struct ivi_application *ivi_application;
};
struct buffer {
struct window *window;
struct wl_buffer *buffer;
void *shm_data;
int busy;
int width, height;
size_t size; /* width * 4 * height */
struct wl_list buffer_link; /** window::buffer_list */
};
struct window {
struct display *display;
int width, height;
int init_width, init_height;
struct wl_surface *surface;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct wl_list buffer_list;
struct zxdg_surface_v6 *xdg_surface;
struct zxdg_toplevel_v6 *xdg_toplevel;
struct ivi_surface *ivi_surface;
struct buffer buffers[2];
struct buffer *prev_buffer;
struct wl_callback *callback;
bool wait_for_configure;
bool maximized;
bool fullscreen;
bool needs_update_buffer;
};
static const struct format shm_formats[] = {
/* 8 bpp formats */
FMT(R8, 8, 0x00, 0x55, 0xaa, 0xff ),
/* 16 bpp formats */
FMT(R16, 16, 0x0000, 0x5555, 0xaaaa, 0xffff ),
FMT(GR88, 16, 0x00ff, 0xff00, 0x0000, 0xffff ),
FMT(RG88, 16, 0xff00, 0x00ff, 0x0000, 0xffff ),
FMT(RGB565, 16, 0xf800, 0x07e0, 0x001f, 0xffff ),
FMT(BGR565, 16, 0x001f, 0x07e0, 0xf800, 0xffff ),
FMT(XRGB4444, 16, 0xff00, 0xf0f0, 0xf00f, 0x7777 ),
FMT(ARGB4444, 16, 0xff00, 0xf0f0, 0xf00f, 0x7777 ),
FMT(XBGR4444, 16, 0xf00f, 0xf0f0, 0xff00, 0x7777 ),
FMT(ABGR4444, 16, 0xf00f, 0xf0f0, 0xff00, 0x7777 ),
FMT(RGBX4444, 16, 0xf00f, 0x0f0f, 0x00ff, 0x7777 ),
FMT(RGBA4444, 16, 0xf00f, 0x0f0f, 0x00ff, 0x7777 ),
FMT(BGRX4444, 16, 0x00ff, 0x0f0f, 0xf00f, 0x7777 ),
FMT(BGRA4444, 16, 0x00ff, 0x0f0f, 0xf00f, 0x7777 ),
FMT(XRGB1555, 16, 0xfc00, 0x83e1, 0x801f, 0x0000 ),
FMT(ARGB1555, 16, 0xfc00, 0x83e1, 0x801f, 0x0000 ),
FMT(XBGR1555, 16, 0x801f, 0x83e1, 0xfc00, 0x0000 ),
FMT(ABGR1555, 16, 0x801f, 0x83e1, 0xfc00, 0x0000 ),
FMT(RGBX5551, 16, 0xf801, 0x07c1, 0x003f, 0x0000 ),
FMT(RGBA5551, 16, 0xf801, 0x07c1, 0x003f, 0x0000 ),
FMT(BGRX5551, 16, 0x003f, 0x07c1, 0xf801, 0x0000 ),
FMT(BGRA5551, 16, 0x003f, 0x07c1, 0xf801, 0x0000 ),
/* 24 bpp formats */
FMT(RGB888, 24, 0xff0000, 0x00ff00, 0x0000ff, 0xffffff ),
FMT(BGR888, 24, 0x0000ff, 0x00ff00, 0xff0000, 0xffffff ),
/* 32 bpp formats */
FMT(GR1616, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0xffffffff ),
FMT(RG1616, 32, 0xffff0000, 0x0000ffff, 0x00000000, 0xffffffff ),
FMT(XRGB8888, 32, 0xffff0000, 0xff00ff00, 0xff0000ff, 0x7f7f7f7f ),
FMT(ARGB8888, 32, 0xffff0000, 0xff00ff00, 0xff0000ff, 0x7f7f7f7f ),
FMT(XBGR8888, 32, 0xff0000ff, 0xff00ff00, 0xffff0000, 0x7f7f7f7f ),
FMT(ABGR8888, 32, 0xff0000ff, 0xff00ff00, 0xffff0000, 0x7f7f7f7f ),
FMT(RGBX8888, 32, 0xff0000ff, 0x00ff00ff, 0x0000ffff, 0x7f7f7f7f ),
FMT(RGBA8888, 32, 0xff0000ff, 0x00ff00ff, 0x0000ffff, 0x7f7f7f7f ),
FMT(BGRX8888, 32, 0x0000ffff, 0x00ff00ff, 0xff0000ff, 0x7f7f7f7f ),
FMT(BGRA8888, 32, 0x0000ffff, 0x00ff00ff, 0xff0000ff, 0x7f7f7f7f ),
FMT(XRGB2101010, 32, 0xfff00000, 0xc00ffc00, 0xc00003ff, 0x5ff7fdff ),
FMT(ARGB2101010, 32, 0xfff00000, 0xc00ffc00, 0xc00003ff, 0x5ff7fdff ),
FMT(XBGR2101010, 32, 0xc00003ff, 0xc00ffc00, 0xfff00000, 0x5ff7fdff ),
FMT(ABGR2101010, 32, 0xc00003ff, 0xc00ffc00, 0xfff00000, 0x5ff7fdff ),
FMT(RGBX1010102, 32, 0xffc00003, 0x003ff003, 0x00000fff, 0x7fdff7fd ),
FMT(RGBA1010102, 32, 0xffc00003, 0x003ff003, 0x00000fff, 0x7fdff7fd ),
FMT(BGRX1010102, 32, 0x00000fff, 0x003ff003, 0xffc00003, 0x7fdff7fd ),
FMT(BGRA1010102, 32, 0x00000fff, 0x003ff003, 0xffc00003, 0x7fdff7fd ),
/* 64 bpp formats */
FMT(XRGB16161616, 64, 0xffffffff00000000, 0xffff0000ffff0000, 0xffff00000000ffff, 0x7fff7fff7fff7fff ),
FMT(ARGB16161616, 64, 0xffffffff00000000, 0xffff0000ffff0000, 0xffff00000000ffff, 0x7fff7fff7fff7fff ),
FMT(XBGR16161616, 64, 0xffff00000000ffff, 0xffff0000ffff0000, 0xffffffff00000000, 0x7fff7fff7fff7fff ),
FMT(ABGR16161616, 64, 0xffff00000000ffff, 0xffff0000ffff0000, 0xffffffff00000000, 0x7fff7fff7fff7fff ),
FMT(XRGB16161616F, 64, 0x3c003c0000000000, 0x3c0000003c000000, 0x3c00000000003c00, 0x3800380038003800 ),
FMT(ARGB16161616F, 64, 0x3c003c0000000000, 0x3c0000003c000000, 0x3c00000000003c00, 0x3800380038003800 ),
FMT(XBGR16161616F, 64, 0x3c00000000003c00, 0x3c0000003c000000, 0x3c003c0000000000, 0x3800380038003800 ),
FMT(ABGR16161616F, 64, 0x3c00000000003c00, 0x3c0000003c000000, 0x3c003c0000000000, 0x3800380038003800 ),
};
static int running = 1;
@ -164,58 +79,6 @@ static int running = 1;
static void
redraw(void *data, struct wl_callback *callback, uint32_t time);
static struct buffer *
alloc_buffer(struct window *window, int width, int height)
{
struct buffer *buffer = calloc(1, sizeof(*buffer));
buffer->width = width;
buffer->height = height;
wl_list_insert(&window->buffer_list, &buffer->buffer_link);
return buffer;
}
static void
destroy_buffer(struct buffer *buffer)
{
if (buffer->buffer)
wl_buffer_destroy(buffer->buffer);
munmap(buffer->shm_data, buffer->size);
wl_list_remove(&buffer->buffer_link);
free(buffer);
}
static struct buffer *
pick_free_buffer(struct window *window)
{
struct buffer *b;
struct buffer *buffer = NULL;
wl_list_for_each(b, &window->buffer_list, buffer_link) {
if (!b->busy) {
buffer = b;
break;
}
}
return buffer;
}
static void
prune_old_released_buffers(struct window *window)
{
struct buffer *b, *b_next;
wl_list_for_each_safe(b, b_next,
&window->buffer_list, buffer_link) {
if (!b->busy && (b->width != window->width ||
b->height != window->height))
destroy_buffer(b);
}
}
static void
buffer_release(void *data, struct wl_buffer *buffer)
{
@ -229,31 +92,26 @@ static const struct wl_buffer_listener buffer_listener = {
};
static int
create_shm_buffer(struct window *window, struct buffer *buffer,
const struct format *format)
create_shm_buffer(struct display *display, struct buffer *buffer,
int width, int height, uint32_t format)
{
struct wl_shm_pool *pool;
int fd, size, stride;
void *data;
int width, height;
struct display *display;
width = window->width;
height = window->height;
stride = width * (format->bpp / 8);
stride = width * 4;
size = stride * height;
display = window->display;
fd = os_create_anonymous_file(size);
if (fd < 0) {
fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
size, strerror(errno));
fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
size);
return -1;
}
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
fprintf(stderr, "mmap failed: %m\n");
close(fd);
return -1;
}
@ -261,97 +119,23 @@ create_shm_buffer(struct window *window, struct buffer *buffer,
pool = wl_shm_create_pool(display->shm, fd, size);
buffer->buffer = wl_shm_pool_create_buffer(pool, 0,
width, height,
stride, format->code);
stride, format);
wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer);
wl_shm_pool_destroy(pool);
close(fd);
buffer->size = size;
buffer->shm_data = data;
return 0;
}
static void
keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
uint32_t format, int fd, uint32_t size)
{
/* Just so we dont leak the keymap fd */
close(fd);
}
static void
keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
uint32_t serial, struct wl_surface *surface,
struct wl_array *keys)
{
}
static void
keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
uint32_t serial, struct wl_surface *surface)
{
}
static void
keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t time, uint32_t key,
uint32_t state)
{
struct display *d = data;
if (key == KEY_F11 && state) {
if (d->window->fullscreen)
xdg_toplevel_unset_fullscreen(d->window->xdg_toplevel);
else
xdg_toplevel_set_fullscreen(d->window->xdg_toplevel, NULL);
} else if (key == KEY_ESC && state) {
running = 0;
}
}
static void
keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t mods_depressed,
uint32_t mods_latched, uint32_t mods_locked,
uint32_t group)
{
}
static const struct wl_keyboard_listener keyboard_listener = {
keyboard_handle_keymap,
keyboard_handle_enter,
keyboard_handle_leave,
keyboard_handle_key,
keyboard_handle_modifiers,
};
static void
seat_handle_capabilities(void *data, struct wl_seat *seat,
enum wl_seat_capability caps)
{
struct display *d = data;
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !d->keyboard) {
d->keyboard = wl_seat_get_keyboard(seat);
wl_keyboard_add_listener(d->keyboard, &keyboard_listener, d);
} else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && d->keyboard) {
wl_keyboard_destroy(d->keyboard);
d->keyboard = NULL;
}
}
static const struct wl_seat_listener seat_listener = {
seat_handle_capabilities,
};
static void
handle_xdg_surface_configure(void *data, struct xdg_surface *surface,
handle_xdg_surface_configure(void *data, struct zxdg_surface_v6 *surface,
uint32_t serial)
{
struct window *window = data;
xdg_surface_ack_configure(surface, serial);
zxdg_surface_v6_ack_configure(surface, serial);
if (window->wait_for_configure) {
redraw(window, NULL, 0);
@ -359,64 +143,43 @@ handle_xdg_surface_configure(void *data, struct xdg_surface *surface,
}
}
static const struct xdg_surface_listener xdg_surface_listener = {
static const struct zxdg_surface_v6_listener xdg_surface_listener = {
handle_xdg_surface_configure,
};
static void
handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
handle_xdg_toplevel_configure(void *data, struct zxdg_toplevel_v6 *xdg_toplevel,
int32_t width, int32_t height,
struct wl_array *states)
struct wl_array *state)
{
struct window *window = data;
uint32_t *p;
window->fullscreen = false;
window->maximized = false;
wl_array_for_each(p, states) {
uint32_t state = *p;
switch (state) {
case XDG_TOPLEVEL_STATE_FULLSCREEN:
window->fullscreen = true;
break;
case XDG_TOPLEVEL_STATE_MAXIMIZED:
window->maximized = true;
break;
}
}
if (width > 0 && height > 0) {
if (!window->fullscreen && !window->maximized) {
window->init_width = width;
window->init_height = height;
}
window->width = width;
window->height = height;
} else if (!window->fullscreen && !window->maximized) {
window->width = window->init_width;
window->height = window->init_height;
}
window->needs_update_buffer = true;
}
static void
handle_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
handle_xdg_toplevel_close(void *data, struct zxdg_toplevel_v6 *xdg_toplevel)
{
running = 0;
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = {
handle_xdg_toplevel_configure,
handle_xdg_toplevel_close,
};
static void
handle_ivi_surface_configure(void *data, struct ivi_surface *ivi_surface,
int32_t width, int32_t height)
{
/* Simple-shm is resizable */
}
static const struct ivi_surface_listener ivi_surface_listener = {
handle_ivi_surface_configure,
};
static struct window *
create_window(struct display *display, int width, int height)
{
struct window *window;
int i;
window = zalloc(sizeof *window);
if (!window)
@ -426,58 +189,65 @@ create_window(struct display *display, int width, int height)
window->display = display;
window->width = width;
window->height = height;
window->init_width = width;
window->init_height = height;
window->surface = wl_compositor_create_surface(display->compositor);
window->needs_update_buffer = false;
wl_list_init(&window->buffer_list);
if (display->wm_base) {
if (display->shell) {
window->xdg_surface =
xdg_wm_base_get_xdg_surface(display->wm_base,
window->surface);
zxdg_shell_v6_get_xdg_surface(display->shell,
window->surface);
assert(window->xdg_surface);
xdg_surface_add_listener(window->xdg_surface,
&xdg_surface_listener, window);
zxdg_surface_v6_add_listener(window->xdg_surface,
&xdg_surface_listener, window);
window->xdg_toplevel =
xdg_surface_get_toplevel(window->xdg_surface);
zxdg_surface_v6_get_toplevel(window->xdg_surface);
assert(window->xdg_toplevel);
xdg_toplevel_add_listener(window->xdg_toplevel,
&xdg_toplevel_listener, window);
xdg_toplevel_set_title(window->xdg_toplevel, "simple-shm");
xdg_toplevel_set_app_id(window->xdg_toplevel,
"org.freedesktop.weston.simple-shm");
zxdg_toplevel_v6_add_listener(window->xdg_toplevel,
&xdg_toplevel_listener, window);
zxdg_toplevel_v6_set_title(window->xdg_toplevel, "simple-shm");
wl_surface_commit(window->surface);
window->wait_for_configure = true;
} else if (display->fshell) {
zwp_fullscreen_shell_v1_present_surface(display->fshell,
window->surface,
ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT,
NULL);
} else if (display->ivi_application ) {
uint32_t id_ivisurf = IVI_SURFACE_ID + (uint32_t)getpid();
window->ivi_surface =
ivi_application_surface_create(display->ivi_application,
id_ivisurf, window->surface);
if (window->ivi_surface == NULL) {
fprintf(stderr, "Failed to create ivi_client_surface\n");
abort();
}
ivi_surface_add_listener(window->ivi_surface,
&ivi_surface_listener, window);
} else {
assert(0);
}
for (i = 0; i < MAX_BUFFER_ALLOC; i++)
alloc_buffer(window, window->width, window->height);
return window;
}
static void
destroy_window(struct window *window)
{
struct buffer *buffer, *buffer_next;
if (window->callback)
wl_callback_destroy(window->callback);
wl_list_for_each_safe(buffer, buffer_next,
&window->buffer_list, buffer_link)
destroy_buffer(buffer);
if (window->buffers[0].buffer)
wl_buffer_destroy(window->buffers[0].buffer);
if (window->buffers[1].buffer)
wl_buffer_destroy(window->buffers[1].buffer);
if (window->xdg_toplevel)
xdg_toplevel_destroy(window->xdg_toplevel);
zxdg_toplevel_v6_destroy(window->xdg_toplevel);
if (window->xdg_surface)
xdg_surface_destroy(window->xdg_surface);
zxdg_surface_v6_destroy(window->xdg_surface);
wl_surface_destroy(window->surface);
free(window);
}
@ -485,34 +255,27 @@ destroy_window(struct window *window)
static struct buffer *
window_next_buffer(struct window *window)
{
struct buffer *buffer = NULL;
struct buffer *buffer;
int ret = 0;
if (window->needs_update_buffer) {
int i;
for (i = 0; i < MAX_BUFFER_ALLOC; i++)
alloc_buffer(window, window->width, window->height);
window->needs_update_buffer = false;
}
buffer = pick_free_buffer(window);
if (!buffer)
if (!window->buffers[0].busy)
buffer = &window->buffers[0];
else if (!window->buffers[1].busy)
buffer = &window->buffers[1];
else
return NULL;
if (!buffer->buffer) {
ret = create_shm_buffer(window, buffer,
window->display->format);
ret = create_shm_buffer(window->display, buffer,
window->width, window->height,
WL_SHM_FORMAT_XRGB8888);
if (ret < 0)
return NULL;
/* paint the padding */
memset(buffer->shm_data, 0xff,
window->width * window->height *
(window->display->format->bpp / 8));
window->width * window->height * 4);
}
return buffer;
@ -564,122 +327,6 @@ paint_pixels(void *image, int padding, int width, int height, uint32_t time)
}
}
static void
paint_format(void *image, const struct format *format, int width, int height)
{
uint64_t *img64 = (uint64_t*) image;
uint32_t *img32 = (uint32_t*) image;
uint16_t *img16 = (uint16_t*) image;
uint8_t *img8 = (uint8_t*) image;
uint64_t color;
int i, j;
#define GET_COLOR(y) \
(y < (1 * (height / 4))) ? format->color[0] : \
(y < (2 * (height / 4))) ? format->color[1] : \
(y < (3 * (height / 4))) ? format->color[2] : \
format->color[3]
switch (format->code) {
case WL_SHM_FORMAT_R8:
for (i = 0; i < height; i++) {
color = GET_COLOR(i);
for (j = 0; j < width; j++)
img8[i * width + j] = color;
}
break;
case WL_SHM_FORMAT_R16:
case WL_SHM_FORMAT_GR88:
case WL_SHM_FORMAT_RG88:
case WL_SHM_FORMAT_RGB565:
case WL_SHM_FORMAT_BGR565:
case WL_SHM_FORMAT_XRGB4444:
case WL_SHM_FORMAT_ARGB4444:
case WL_SHM_FORMAT_XBGR4444:
case WL_SHM_FORMAT_ABGR4444:
case WL_SHM_FORMAT_RGBX4444:
case WL_SHM_FORMAT_RGBA4444:
case WL_SHM_FORMAT_BGRX4444:
case WL_SHM_FORMAT_BGRA4444:
case WL_SHM_FORMAT_XRGB1555:
case WL_SHM_FORMAT_ARGB1555:
case WL_SHM_FORMAT_XBGR1555:
case WL_SHM_FORMAT_ABGR1555:
case WL_SHM_FORMAT_RGBX5551:
case WL_SHM_FORMAT_RGBA5551:
case WL_SHM_FORMAT_BGRX5551:
case WL_SHM_FORMAT_BGRA5551:
for (i = 0; i < height; i++) {
color = GET_COLOR(i);
for (j = 0; j < width; j++)
img16[i * width + j] = color;
}
break;
case WL_SHM_FORMAT_RGB888:
case WL_SHM_FORMAT_BGR888:
for (i = 0; i < height; i++) {
color = GET_COLOR(i);
for (j = 0; j < width; j++) {
img8[(i * width + j) * 3 + 0] =
(color >> 0) & 0xff;
img8[(i * width + j) * 3 + 1] =
(color >> 8) & 0xff;
img8[(i * width + j) * 3 + 2] =
(color >> 16) & 0xff;
}
}
break;
case WL_SHM_FORMAT_GR1616:
case WL_SHM_FORMAT_RG1616:
case WL_SHM_FORMAT_XRGB8888:
case WL_SHM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_XBGR8888:
case WL_SHM_FORMAT_ABGR8888:
case WL_SHM_FORMAT_RGBX8888:
case WL_SHM_FORMAT_RGBA8888:
case WL_SHM_FORMAT_BGRX8888:
case WL_SHM_FORMAT_BGRA8888:
case WL_SHM_FORMAT_XRGB2101010:
case WL_SHM_FORMAT_ARGB2101010:
case WL_SHM_FORMAT_XBGR2101010:
case WL_SHM_FORMAT_ABGR2101010:
case WL_SHM_FORMAT_RGBX1010102:
case WL_SHM_FORMAT_RGBA1010102:
case WL_SHM_FORMAT_BGRX1010102:
case WL_SHM_FORMAT_BGRA1010102:
for (i = 0; i < height; i++) {
color = GET_COLOR(i);
for (j = 0; j < width; j++)
img32[i * width + j] = color;
}
break;
case WL_SHM_FORMAT_XRGB16161616:
case WL_SHM_FORMAT_ARGB16161616:
case WL_SHM_FORMAT_XBGR16161616:
case WL_SHM_FORMAT_ABGR16161616:
case WL_SHM_FORMAT_XRGB16161616F:
case WL_SHM_FORMAT_ARGB16161616F:
case WL_SHM_FORMAT_XBGR16161616F:
case WL_SHM_FORMAT_ABGR16161616F:
for (i = 0; i < height; i++) {
color = GET_COLOR(i);
for (j = 0; j < width; j++)
img64[i * width + j] = color;
}
break;
default:
assert(0);
break;
};
#undef GET_COLOR
}
static const struct wl_callback_listener frame_listener;
static void
@ -688,8 +335,6 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
struct window *window = data;
struct buffer *buffer;
prune_old_released_buffers(window);
buffer = window_next_buffer(window);
if (!buffer) {
fprintf(stderr,
@ -698,12 +343,7 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
abort();
}
if (window->display->paint_format)
paint_format(buffer->shm_data, window->display->format,
window->width, window->height);
else
paint_pixels(buffer->shm_data, 20, window->width,
window->height, time);
paint_pixels(buffer->shm_data, 20, window->width, window->height, time);
wl_surface_attach(window->surface, buffer->buffer, 0, 0);
wl_surface_damage(window->surface,
@ -727,8 +367,8 @@ shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
{
struct display *d = data;
if (format == d->format->code)
d->has_format = true;
if (format == WL_SHM_FORMAT_XRGB8888)
d->has_xrgb = true;
}
struct wl_shm_listener shm_listener = {
@ -736,13 +376,13 @@ struct wl_shm_listener shm_listener = {
};
static void
xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
xdg_shell_ping(void *data, struct zxdg_shell_v6 *shell, uint32_t serial)
{
xdg_wm_base_pong(shell, serial);
zxdg_shell_v6_pong(shell, serial);
}
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
xdg_wm_base_ping,
static const struct zxdg_shell_v6_listener xdg_shell_listener = {
xdg_shell_ping,
};
static void
@ -755,19 +395,23 @@ registry_handle_global(void *data, struct wl_registry *registry,
d->compositor =
wl_registry_bind(registry,
id, &wl_compositor_interface, 1);
} else if (strcmp(interface, "xdg_wm_base") == 0) {
d->wm_base = wl_registry_bind(registry,
id, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(d->wm_base, &xdg_wm_base_listener, d);
} else if (strcmp(interface, "wl_seat") == 0) {
d->seat = wl_registry_bind(registry, id,
&wl_seat_interface, 1);
wl_seat_add_listener(d->seat, &seat_listener, d);
} else if (strcmp(interface, "zxdg_shell_v6") == 0) {
d->shell = wl_registry_bind(registry,
id, &zxdg_shell_v6_interface, 1);
zxdg_shell_v6_add_listener(d->shell, &xdg_shell_listener, d);
} else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
d->fshell = wl_registry_bind(registry,
id, &zwp_fullscreen_shell_v1_interface, 1);
} else if (strcmp(interface, "wl_shm") == 0) {
d->shm = wl_registry_bind(registry,
id, &wl_shm_interface, 1);
wl_shm_add_listener(d->shm, &shm_listener, d);
}
else if (strcmp(interface, "ivi_application") == 0) {
d->ivi_application =
wl_registry_bind(registry, id,
&ivi_application_interface, 1);
}
}
static void
@ -782,11 +426,11 @@ static const struct wl_registry_listener registry_listener = {
};
static struct display *
create_display(const struct format *format, bool paint_format)
create_display(void)
{
struct display *display;
display = zalloc(sizeof *display);
display = malloc(sizeof *display);
if (display == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
@ -794,9 +438,7 @@ create_display(const struct format *format, bool paint_format)
display->display = wl_display_connect(NULL);
assert(display->display);
display->format = format;
display->paint_format = paint_format;
display->has_format= false;
display->has_xrgb = false;
display->registry = wl_display_get_registry(display->display);
wl_registry_add_listener(display->registry,
&registry_listener, display);
@ -848,9 +490,8 @@ create_display(const struct format *format, bool paint_format)
* technique.
*/
if (!display->has_format) {
fprintf(stderr, "Format '%s' not supported by compositor.\n",
format->string);
if (!display->has_xrgb) {
fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
exit(1);
}
@ -863,8 +504,11 @@ destroy_display(struct display *display)
if (display->shm)
wl_shm_destroy(display->shm);
if (display->wm_base)
xdg_wm_base_destroy(display->wm_base);
if (display->shell)
zxdg_shell_v6_destroy(display->shell);
if (display->fshell)
zwp_fullscreen_shell_v1_release(display->fshell);
if (display->compositor)
wl_compositor_destroy(display->compositor);
@ -881,86 +525,19 @@ signal_int(int signum)
running = 0;
}
static void
usage(const char *program)
{
fprintf(stdout,
"Usage: %s [OPTIONS]\n"
"\n"
"Draw pixels into shared memory buffers using wl_shm\n"
"\n"
"Options:\n"
" -h, --help Show this help\n"
" -F, --format <format> Test format (see list below)\n"
"\n"
"RGB formats:\n"
" - 8 bpp: r8.\n"
"\n"
" - 16 bpp: r16, gr88, rg88, rgb565, bgr565, xrgb4444, argb4444, xbgr4444,\n"
" abgr4444, rgbx4444, rgba4444, bgrx4444, bgra4444, xrgb1555,\n"
" argb1555, xbgr1555, abgr1555, rgbx5551, rgba5551, bgrx5551,\n"
" bgra5551.\n"
"\n"
" - 24 bpp: rgb888, bgr888.\n"
"\n"
" - 32 bpp: gr1616, rg1616, xrgb8888, argb8888, xbgr8888, abgr8888, rgbx8888,\n"
" rgba8888, bgrx8888, bgra8888, xrgb2101010, argb2101010, xbgr2101010,\n"
" abgr2101010, rgbx1010102, rgba1010102, bgrx1010102, bgra1010102.\n"
"\n"
" - 64 bpp: xrgb16161616, argb16161616, xbgr16161616, abgr16161616,\n"
" xrgb16161616f, argb16161616f, xbgr16161616f, abgr16161616f.\n",
program);
}
int
main(int argc, char **argv)
{
struct sigaction sigint;
struct display *display;
struct window *window;
const struct format *format = NULL;
bool paint_format = false;
const char *value;
int ret = 0, i, j;
int ret = 0;
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-h") ||
!strcmp(argv[i], "--help")) {
usage(argv[0]);
return 0;
} else if (!strcmp(argv[i], "-F") ||
!strcmp(argv[i], "--format")) {
value = ++i == argc ? "" : argv[i];
for (j = 0; j < (int) ARRAY_SIZE(shm_formats); j++) {
if (!strcasecmp(shm_formats[j].string, value)) {
format = &shm_formats[j];
paint_format = true;
break;
}
}
if (!format) {
fprintf(stderr, "Format '%s' not supported by "
"client.\n", value);
return 1;
}
} else {
fprintf(stderr, "Invalid argument: '%s'\n", argv[i - 1]);
return 1;
}
}
if (!format)
for (i = 0; i < (int) ARRAY_SIZE(shm_formats); i++)
if (shm_formats[i].code == WL_SHM_FORMAT_XRGB8888)
format = &shm_formats[i];
assert(format);
display = create_display(format, paint_format);
window = create_window(display, 256, 256);
display = create_display();
window = create_window(display, 250, 250);
if (!window)
return 1;
display->window = window;
sigint.sa_handler = signal_int;
sigemptyset(&sigint.sa_mask);
sigint.sa_flags = SA_RESETHAND;
@ -978,6 +555,11 @@ main(int argc, char **argv)
fprintf(stderr, "simple-shm exiting\n");
if (window->display->ivi_application) {
ivi_surface_destroy(window->ivi_surface);
ivi_application_destroy(window->display->ivi_application);
}
destroy_window(window);
destroy_display(display);

View file

@ -1,880 +0,0 @@
/*
* Copyright © 2011 Benjamin Franzke
* Copyright © 2010 Intel Corporation
* Copyright © 2025 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <unistd.h>
#include <sys/mman.h>
#include <signal.h>
#include <errno.h>
#include <assert.h>
#include <linux/input.h>
#include <wayland-client.h>
#include "shared/os-compatibility.h"
#include "shared/timespec-util.h"
#include "shared/xalloc.h"
#include <libweston/zalloc.h>
#include "xdg-shell-client-protocol.h"
#include "commit-timing-v1-client-protocol.h"
#include "fifo-v1-client-protocol.h"
#include "presentation-time-client-protocol.h"
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
#define MAX_BUFFER_ALLOC 1000
struct display {
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct xdg_wm_base *wm_base;
struct wl_seat *seat;
struct wl_keyboard *keyboard;
struct wl_shm *shm;
struct wp_commit_timing_manager_v1 *commit_timing_manager;
struct wp_fifo_manager_v1 *fifo_manager;
struct wp_presentation *presentation;
bool have_clock_id;
clockid_t presentation_clock_id;
int64_t first_frame_time;
int64_t refresh_nsec;
};
struct buffer {
struct window *window;
struct wl_buffer *buffer;
void *shm_data;
int busy;
int width, height;
size_t size; /* width * 4 * height */
struct wl_list buffer_link; /** window::buffer_list */
};
struct window {
struct display *display;
int width, height;
int init_width, init_height;
struct wl_surface *surface;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct wl_list buffer_list;
struct wp_fifo_v1 *fifo;
struct wp_commit_timer_v1 *commit_timer;
bool wait_for_configure;
bool maximized;
bool fullscreen;
bool needs_update_buffer;
};
struct feedback {
struct wp_presentation_feedback *fb;
struct window *window;
int64_t target_time;
bool final;
};
static int running = 1;
static void
draw_for_time(void *data, int64_t time, bool wait_fifo);
static void
finish_run(struct window *window);
static struct buffer *
alloc_buffer(struct window *window, int width, int height)
{
struct buffer *buffer = calloc(1, sizeof(*buffer));
buffer->width = width;
buffer->height = height;
wl_list_insert(&window->buffer_list, &buffer->buffer_link);
return buffer;
}
static void
destroy_buffer(struct buffer *buffer)
{
if (buffer->buffer)
wl_buffer_destroy(buffer->buffer);
munmap(buffer->shm_data, buffer->size);
wl_list_remove(&buffer->buffer_link);
free(buffer);
}
static struct buffer *
pick_free_buffer(struct window *window)
{
struct buffer *b;
struct buffer *buffer = NULL;
wl_list_for_each(b, &window->buffer_list, buffer_link) {
if (!b->busy) {
buffer = b;
break;
}
}
return buffer;
}
static void
prune_old_released_buffers(struct window *window)
{
struct buffer *b, *b_next;
wl_list_for_each_safe(b, b_next,
&window->buffer_list, buffer_link) {
if (!b->busy && (b->width != window->width ||
b->height != window->height))
destroy_buffer(b);
}
}
static void
buffer_release(void *data, struct wl_buffer *buffer)
{
struct buffer *mybuf = data;
mybuf->busy = 0;
}
static const struct wl_buffer_listener buffer_listener = {
buffer_release
};
static int
create_shm_buffer(struct window *window, struct buffer *buffer)
{
struct wl_shm_pool *pool;
int fd, size, stride;
void *data;
int width, height;
struct display *display;
width = window->width;
height = window->height;
stride = width * 4;
size = stride * height;
display = window->display;
fd = os_create_anonymous_file(size);
if (fd < 0) {
fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
size, strerror(errno));
return -1;
}
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
close(fd);
return -1;
}
pool = wl_shm_create_pool(display->shm, fd, size);
buffer->buffer = wl_shm_pool_create_buffer(pool, 0,
width, height,
stride,
WL_SHM_FORMAT_XRGB8888);
wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer);
wl_shm_pool_destroy(pool);
close(fd);
buffer->size = size;
buffer->shm_data = data;
return 0;
}
static void
keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
uint32_t format, int fd, uint32_t size)
{
/* Just so we dont leak the keymap fd */
close(fd);
}
static void
keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
uint32_t serial, struct wl_surface *surface,
struct wl_array *keys)
{
}
static void
keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
uint32_t serial, struct wl_surface *surface)
{
}
static void
keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t time, uint32_t key,
uint32_t state)
{
if (key == KEY_ESC && state)
running = 0;
}
static void
keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t mods_depressed,
uint32_t mods_latched, uint32_t mods_locked,
uint32_t group)
{
}
static const struct wl_keyboard_listener keyboard_listener = {
keyboard_handle_keymap,
keyboard_handle_enter,
keyboard_handle_leave,
keyboard_handle_key,
keyboard_handle_modifiers,
};
static void
seat_handle_capabilities(void *data, struct wl_seat *seat,
enum wl_seat_capability caps)
{
struct display *d = data;
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !d->keyboard) {
d->keyboard = wl_seat_get_keyboard(seat);
wl_keyboard_add_listener(d->keyboard, &keyboard_listener, d);
} else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && d->keyboard) {
wl_keyboard_destroy(d->keyboard);
d->keyboard = NULL;
}
}
static const struct wl_seat_listener seat_listener = {
seat_handle_capabilities,
};
static void
handle_xdg_surface_configure(void *data, struct xdg_surface *surface,
uint32_t serial)
{
struct window *window = data;
xdg_surface_ack_configure(surface, serial);
if (window->wait_for_configure) {
draw_for_time(window, 0, false);
window->wait_for_configure = false;
}
}
static const struct xdg_surface_listener xdg_surface_listener = {
handle_xdg_surface_configure,
};
static void
handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
int32_t width, int32_t height,
struct wl_array *states)
{
struct window *window = data;
uint32_t *p;
window->fullscreen = false;
window->maximized = false;
wl_array_for_each(p, states) {
uint32_t state = *p;
switch (state) {
case XDG_TOPLEVEL_STATE_FULLSCREEN:
window->fullscreen = true;
break;
case XDG_TOPLEVEL_STATE_MAXIMIZED:
window->maximized = true;
break;
}
}
if (width > 0 && height > 0) {
if (!window->fullscreen && !window->maximized) {
window->init_width = width;
window->init_height = height;
}
window->width = width;
window->height = height;
} else if (!window->fullscreen && !window->maximized) {
window->width = window->init_width;
window->height = window->init_height;
}
window->needs_update_buffer = true;
}
static void
handle_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
{
running = 0;
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
handle_xdg_toplevel_configure,
handle_xdg_toplevel_close,
};
static struct window *
create_window(struct display *display, int width, int height)
{
struct window *window;
int i;
window = zalloc(sizeof *window);
if (!window)
return NULL;
window->display = display;
window->width = width;
window->height = height;
window->init_width = width;
window->init_height = height;
window->surface = wl_compositor_create_surface(display->compositor);
window->fifo = wp_fifo_manager_v1_get_fifo(display->fifo_manager,
window->surface);
window->commit_timer = wp_commit_timing_manager_v1_get_timer(display->commit_timing_manager,
window->surface);
window->needs_update_buffer = false;
wl_list_init(&window->buffer_list);
assert(display->wm_base);
window->xdg_surface =
xdg_wm_base_get_xdg_surface(display->wm_base,
window->surface);
assert(window->xdg_surface);
xdg_surface_add_listener(window->xdg_surface,
&xdg_surface_listener, window);
window->xdg_toplevel =
xdg_surface_get_toplevel(window->xdg_surface);
assert(window->xdg_toplevel);
xdg_toplevel_add_listener(window->xdg_toplevel,
&xdg_toplevel_listener, window);
xdg_toplevel_set_title(window->xdg_toplevel, "simple-timing");
xdg_toplevel_set_app_id(window->xdg_toplevel,
"org.freedesktop.weston.simple-timing");
wl_surface_commit(window->surface);
window->wait_for_configure = true;
for (i = 0; i < MAX_BUFFER_ALLOC; i++)
alloc_buffer(window, window->width, window->height);
return window;
}
static void
destroy_window(struct window *window)
{
struct buffer *buffer, *buffer_next;
wl_list_for_each_safe(buffer, buffer_next,
&window->buffer_list, buffer_link)
destroy_buffer(buffer);
if (window->xdg_toplevel)
xdg_toplevel_destroy(window->xdg_toplevel);
if (window->xdg_surface)
xdg_surface_destroy(window->xdg_surface);
wl_surface_destroy(window->surface);
if (window->fifo)
wp_fifo_v1_destroy(window->fifo);
if (window->commit_timer)
wp_commit_timer_v1_destroy(window->commit_timer);
free(window);
}
static struct buffer *
window_next_buffer(struct window *window)
{
struct buffer *buffer = NULL;
int ret = 0;
if (window->needs_update_buffer) {
int i;
for (i = 0; i < MAX_BUFFER_ALLOC; i++)
alloc_buffer(window, window->width, window->height);
window->needs_update_buffer = false;
}
buffer = pick_free_buffer(window);
if (!buffer)
return NULL;
if (!buffer->buffer) {
ret = create_shm_buffer(window, buffer);
if (ret < 0)
return NULL;
/* paint the padding */
memset(buffer->shm_data, 0xff,
window->width * window->height * 4);
}
return buffer;
}
static void
paint_pixels(void *image, int width, int height, uint32_t time)
{
const int halfh = height / 2;
const int halfw = width / 2;
int ir, or;
uint32_t *pixel = image;
int y;
/* squared radii thresholds */
or = (halfw < halfh ? halfw : halfh) - 8;
ir = or - 32;
or *= or;
ir *= ir;
for (y = 0; y < height; y++) {
int x;
int y2 = (y - halfh) * (y - halfh);
for (x = 0; x < width; x++) {
uint32_t v;
/* squared distance from center */
int r2 = (x - halfw) * (x - halfw) + y2;
if (r2 < ir)
v = (r2 / 32 + time / 64) * 0x0080401;
else if (r2 < or)
v = (y + time / 32) * 0x0080401;
else
v = (x + time / 16) * 0x0080401;
v &= 0x00ffffff;
/* cross if compositor uses X from XRGB as alpha */
if (abs(x - y) > 6 && abs(x + y - height) > 6)
v |= 0xff000000;
*pixel++ = v;
}
}
}
static void
queue_some_frames(struct window *window)
{
struct display *display = window->display;
int64_t target_nsec;
int i;
assert(display->first_frame_time);
/* Round off error will cause us problems if we don't
* reduce this a bit, because we could end up rounding
* to either side of a refresh.
*/
target_nsec = display->first_frame_time - 100000;
for (i = 0; i < 60; i++) {
target_nsec += display->refresh_nsec * 2;
draw_for_time(window, target_nsec, false);
}
for (i = 0; i < 30; i++) {
target_nsec += display->refresh_nsec * 4;
draw_for_time(window, target_nsec, false);
}
for (i = 0; i < 10; i++) {
target_nsec += display->refresh_nsec * 10;
draw_for_time(window, target_nsec, false);
}
for (i = 0; i < 10; i++) {
target_nsec += display->refresh_nsec * 100;
draw_for_time(window, target_nsec, false);
}
finish_run(window);
}
static void
feedback_sync_output(void *data,
struct wp_presentation_feedback *presentation_feedback,
struct wl_output *output)
{
/* Just don't care */
}
static void
feedback_presented(void *data,
struct wp_presentation_feedback *presentation_feedback,
uint32_t tv_sec_hi,
uint32_t tv_sec_lo,
uint32_t tv_nsec,
uint32_t refresh_nsec,
uint32_t seq_hi,
uint32_t seq_lo,
uint32_t flags)
{
struct feedback *feedback = data;
struct window *window = feedback->window;
struct display *display = window->display;
struct timespec pres_ts = {
.tv_sec = ((int64_t)tv_sec_hi << 32) + tv_sec_lo,
.tv_nsec = tv_nsec,
};
int64_t ntime = timespec_to_nsec(&pres_ts);
double delay;
if (feedback->final) {
running = 0;
goto out;
}
if (!feedback->target_time) {
display->first_frame_time = ntime;
display->refresh_nsec = refresh_nsec;
queue_some_frames(window);
goto out;
}
delay = (ntime - feedback->target_time) / 1000000.0;
printf("%fms away from intended time\n", delay);
if (fabs(delay) > display->refresh_nsec / 1000000)
printf("Warning: we missed the intended target display cycle.\n");
out:
wp_presentation_feedback_destroy(feedback->fb);
free(feedback);
}
static void
feedback_discarded(void *data,
struct wp_presentation_feedback *presentation_feedback)
{
struct feedback *feedback = data;
printf("Warning: a frame was discarded\n");
if (feedback->final)
running = 0;
wp_presentation_feedback_destroy(feedback->fb);
free(feedback);
}
static const struct wp_presentation_feedback_listener feedback_listener = {
feedback_sync_output,
feedback_presented,
feedback_discarded,
};
static void
finish_run(struct window *window)
{
struct display *display = window->display;
struct feedback *feedback;
struct buffer *buffer;
feedback = xzalloc(sizeof *feedback);
feedback->window = window;
feedback->final = true;
feedback->target_time = 0;
buffer = window_next_buffer(window);
assert(buffer);
paint_pixels(buffer->shm_data, window->width,
window->height, 1);
wl_surface_attach(window->surface, buffer->buffer, 0, 0);
wl_surface_damage(window->surface, 0, 0, window->width, window->height);
feedback->fb = wp_presentation_feedback(display->presentation,
window->surface);
wp_presentation_feedback_add_listener(feedback->fb,
&feedback_listener, feedback);
wp_fifo_v1_wait_barrier(window->fifo);
wl_surface_commit(window->surface);
}
static void
draw_for_time(void *data, int64_t time, bool wait_fifo)
{
struct window *window = data;
struct display *display = window->display;
struct buffer *buffer;
struct feedback *feedback;
assert(display->have_clock_id);
prune_old_released_buffers(window);
buffer = window_next_buffer(window);
assert(buffer);
paint_pixels(buffer->shm_data, window->width,
window->height, time / 1000000);
wl_surface_attach(window->surface, buffer->buffer, 0, 0);
wl_surface_damage(window->surface, 0, 0, window->width, window->height);
feedback = xzalloc(sizeof *feedback);
feedback->window = window;
feedback->fb = wp_presentation_feedback(display->presentation,
window->surface);
wp_presentation_feedback_add_listener(feedback->fb,
&feedback_listener, feedback);
feedback->target_time = time;
if (time) {
struct timespec target;
timespec_from_nsec(&target, time);
wp_commit_timer_v1_set_timestamp(window->commit_timer,
(int64_t)target.tv_sec >> 32,
target.tv_sec,
target.tv_nsec);
}
wp_fifo_v1_set_barrier(window->fifo);
wl_surface_commit(window->surface);
buffer->busy = 1;
}
static void
xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
{
xdg_wm_base_pong(shell, serial);
}
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
xdg_wm_base_ping,
};
static void
presentation_handle_clock_id(void *data,
struct wp_presentation *wp_presentation,
uint32_t clock_id)
{
struct display *display = data;
display->presentation_clock_id = clock_id;
display->have_clock_id = true;
}
static const struct wp_presentation_listener presentation_listener = {
presentation_handle_clock_id,
};
static void
registry_handle_global(void *data, struct wl_registry *registry,
uint32_t id, const char *interface, uint32_t version)
{
struct display *d = data;
if (strcmp(interface, "wl_compositor") == 0) {
d->compositor =
wl_registry_bind(registry,
id, &wl_compositor_interface, 1);
} else if (strcmp(interface, "xdg_wm_base") == 0) {
d->wm_base = wl_registry_bind(registry,
id, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(d->wm_base, &xdg_wm_base_listener, d);
} else if (strcmp(interface, "wl_seat") == 0) {
d->seat = wl_registry_bind(registry, id,
&wl_seat_interface, 1);
wl_seat_add_listener(d->seat, &seat_listener, d);
} else if (strcmp(interface, "wl_shm") == 0) {
d->shm = wl_registry_bind(registry,
id, &wl_shm_interface, 1);
} else if (strcmp(interface, wp_commit_timing_manager_v1_interface.name) == 0) {
d->commit_timing_manager = wl_registry_bind(registry, id,
&wp_commit_timing_manager_v1_interface,
1);
} else if (strcmp(interface, wp_fifo_manager_v1_interface.name) == 0) {
d->fifo_manager = wl_registry_bind(registry, id,
&wp_fifo_manager_v1_interface,
1);
} else if (strcmp(interface, wp_presentation_interface.name) == 0) {
d->presentation = wl_registry_bind(registry, id,
&wp_presentation_interface,
2);
wp_presentation_add_listener(d->presentation,
&presentation_listener, d);
}
}
static void
registry_handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name)
{
}
static const struct wl_registry_listener registry_listener = {
registry_handle_global,
registry_handle_global_remove
};
static struct display *
create_display(void)
{
struct display *display;
display = xzalloc(sizeof *display);
display->display = wl_display_connect(NULL);
assert(display->display);
display->registry = wl_display_get_registry(display->display);
wl_registry_add_listener(display->registry,
&registry_listener, display);
wl_display_roundtrip(display->display);
if (display->shm == NULL) {
fprintf(stderr, "No wl_shm global\n");
exit(1);
}
wl_display_roundtrip(display->display);
return display;
}
static void
destroy_display(struct display *display)
{
if (display->shm)
wl_shm_destroy(display->shm);
if (display->wm_base)
xdg_wm_base_destroy(display->wm_base);
if (display->compositor)
wl_compositor_destroy(display->compositor);
if (display->presentation)
wp_presentation_destroy(display->presentation);
if (display->fifo_manager)
wp_fifo_manager_v1_destroy(display->fifo_manager);
if (display->commit_timing_manager)
wp_commit_timing_manager_v1_destroy(display->commit_timing_manager);
if (display->keyboard)
wl_keyboard_destroy(display->keyboard);
if (display->seat)
wl_seat_destroy(display->seat);
wl_registry_destroy(display->registry);
wl_display_flush(display->display);
wl_display_disconnect(display->display);
free(display);
}
static void
signal_int(int signum)
{
running = 0;
}
static void
usage(const char *program)
{
fprintf(stdout,
"Usage: %s [OPTIONS]\n"
"\n"
"Schedule frames in the future with commit-timing\n"
"\n"
"Options:\n"
" -h, --help Show this help\n"
"\n",
program);
}
int
main(int argc, char **argv)
{
struct sigaction sigint;
struct display *display;
struct window *window;
int ret = 0, i;
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-h") ||
!strcmp(argv[i], "--help")) {
usage(argv[0]);
return 0;
} else {
fprintf(stderr, "Invalid argument: '%s'\n", argv[i - 1]);
return 1;
}
}
display = create_display();
window = create_window(display, 256, 256);
if (!window)
return 1;
sigint.sa_handler = signal_int;
sigemptyset(&sigint.sa_mask);
sigint.sa_flags = SA_RESETHAND;
sigaction(SIGINT, &sigint, NULL);
while (running && ret != -1)
ret = wl_display_dispatch(display->display);
fprintf(stderr, "simple-timing exiting\n");
destroy_window(window);
destroy_display(display);
return 0;
}

View file

@ -1,7 +1,6 @@
/*
* Copyright © 2011 Benjamin Franzke
* Copyright © 2011 Intel Corporation
* Copyright © 2021 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -32,108 +31,66 @@
#include <stdbool.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <wayland-client.h>
#include "shared/helpers.h"
#include "shared/xalloc.h"
#include "shared/os-compatibility.h"
#include "xdg-shell-client-protocol.h"
struct seat {
struct touch *touch;
struct wl_seat *seat;
struct wl_touch *wl_touch;
};
struct buffer {
struct wl_buffer *buffer;
void *data;
};
struct touch {
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct xdg_wm_base *wm_base;
struct wl_shell *shell;
struct wl_shm *shm;
struct wl_pointer *pointer;
struct wl_keyboard *keyboard;
struct wl_surface *surface;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct buffer *buffer;
struct wl_shell_surface *shell_surface;
struct wl_buffer *buffer;
int has_argb;
int width, height;
int init_width, init_height;
bool running;
bool wait_for_configure;
bool needs_buffer_update;
bool has_argb;
bool maximized;
bool fullscreen;
void *data;
};
static struct buffer *
static void
create_shm_buffer(struct touch *touch)
{
struct wl_shm_pool *pool;
int fd, size, stride;
void *data;
struct buffer *buffer;
buffer = xzalloc(sizeof(*buffer));
stride = touch->width * 4;
size = stride * touch->height;
fd = os_create_anonymous_file(size);
if (fd < 0) {
fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
size, strerror(errno));
fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
size);
exit(1);
}
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
touch->data =
mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (touch->data == MAP_FAILED) {
fprintf(stderr, "mmap failed: %m\n");
close(fd);
return NULL;
exit(1);
}
pool = wl_shm_create_pool(touch->shm, fd, size);
buffer->buffer =
touch->buffer =
wl_shm_pool_create_buffer(pool, 0,
touch->width, touch->height, stride,
WL_SHM_FORMAT_ARGB8888);
wl_shm_pool_destroy(pool);
buffer->data = data;
close(fd);
return buffer;
}
static void
redraw(void *data)
{
struct touch *touch = data;
struct buffer *buffer = NULL;
buffer = create_shm_buffer(touch);
assert(buffer);
if (touch->buffer)
free(touch->buffer);
touch->buffer = buffer;
/* paint the "work-area" */
memset(buffer->data, 64, touch->width * touch->height * 4);
wl_surface_attach(touch->surface, buffer->buffer, 0, 0);
wl_surface_damage(touch->surface, 0, 0, touch->width, touch->height);
wl_surface_commit(touch->surface);
}
static void
@ -142,7 +99,7 @@ shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
struct touch *touch = data;
if (format == WL_SHM_FORMAT_ARGB8888)
touch->has_argb = true;
touch->has_argb = 1;
}
struct wl_shm_listener shm_listener = {
@ -172,7 +129,7 @@ touch_paint(struct touch *touch, int32_t x, int32_t y, int32_t id)
y < 2 || y >= touch->height - 2)
return;
p = ((uint32_t *) touch->buffer->data) + (x - 2) + (y - 2) * touch->width;
p = (uint32_t *) touch->data + (x - 2) + (y - 2) * touch->width;
p[2] = c;
p += touch->width;
p[1] = c;
@ -191,7 +148,7 @@ touch_paint(struct touch *touch, int32_t x, int32_t y, int32_t id)
p += touch->width;
p[2] = c;
wl_surface_attach(touch->surface, touch->buffer->buffer, 0, 0);
wl_surface_attach(touch->surface, touch->buffer, 0, 0);
wl_surface_damage(touch->surface, x - 2, y - 2, 5, 5);
/* todo: We could queue up more damage before committing, if there
* are more input events to handle.
@ -283,32 +240,27 @@ add_seat(struct touch *touch, uint32_t name, uint32_t version)
}
static void
handle_xdg_surface_configure(void *data, struct xdg_surface *surface,
uint32_t serial)
handle_ping(void *data, struct wl_shell_surface *shell_surface,
uint32_t serial)
{
struct touch *touch = data;
xdg_surface_ack_configure(surface, serial);
if (touch->wait_for_configure || touch->needs_buffer_update) {
redraw(touch);
touch->wait_for_configure = false;
touch->needs_buffer_update = false;
}
wl_shell_surface_pong(shell_surface, serial);
}
static const struct xdg_surface_listener xdg_surface_listener = {
handle_xdg_surface_configure,
};
static void
xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
handle_configure(void *data, struct wl_shell_surface *shell_surface,
uint32_t edges, int32_t width, int32_t height)
{
xdg_wm_base_pong(shell, serial);
}
static const struct xdg_wm_base_listener wm_base_listener = {
xdg_wm_base_ping,
static void
handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
{
}
static const struct wl_shell_surface_listener shell_surface_listener = {
handle_ping,
handle_configure,
handle_popup_done
};
static void
@ -321,12 +273,10 @@ handle_global(void *data, struct wl_registry *registry,
touch->compositor =
wl_registry_bind(registry, name,
&wl_compositor_interface, 1);
} else if (strcmp(interface, "xdg_wm_base") == 0) {
touch->wm_base =
} else if (strcmp(interface, "wl_shell") == 0) {
touch->shell =
wl_registry_bind(registry, name,
&xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(touch->wm_base,
&wm_base_listener, touch);
&wl_shell_interface, 1);
} else if (strcmp(interface, "wl_shm") == 0) {
touch->shm = wl_registry_bind(registry, name,
&wl_shm_interface, 1);
@ -346,56 +296,6 @@ static const struct wl_registry_listener registry_listener = {
handle_global_remove
};
static void
handle_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
int32_t width, int32_t height, struct wl_array *states)
{
struct touch *touch = data;
uint32_t *p;
touch->fullscreen = false;
touch->maximized = false;
wl_array_for_each(p, states) {
uint32_t state = *p;
switch (state) {
case XDG_TOPLEVEL_STATE_FULLSCREEN:
touch->fullscreen = true;
break;
case XDG_TOPLEVEL_STATE_MAXIMIZED:
touch->maximized = true;
break;
}
}
if (width > 0 && height > 0) {
if (!touch->fullscreen && !touch->maximized) {
touch->init_width = width;
touch->init_width = height;
}
touch->width = width;
touch->height = height;
} else if (!touch->fullscreen && !touch->maximized) {
touch->width = touch->init_width;
touch->height = touch->init_height;
}
touch->needs_buffer_update = true;
}
static void
handle_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
{
struct touch *touch = data;
touch->running = false;
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
handle_toplevel_configure,
handle_toplevel_close,
};
static struct touch *
touch_create(int width, int height)
{
@ -409,8 +309,7 @@ touch_create(int width, int height)
touch->display = wl_display_connect(NULL);
assert(touch->display);
touch->has_argb = false;
touch->buffer = NULL;
touch->has_argb = 0;
touch->registry = wl_display_get_registry(touch->display);
wl_registry_add_listener(touch->registry, &registry_listener, touch);
wl_display_dispatch(touch->display);
@ -421,59 +320,30 @@ touch_create(int width, int height)
exit(1);
}
if (!touch->wm_base) {
fprintf(stderr, "xdg-shell required!\n");
exit(1);
touch->width = width;
touch->height = height;
touch->surface = wl_compositor_create_surface(touch->compositor);
touch->shell_surface = wl_shell_get_shell_surface(touch->shell,
touch->surface);
create_shm_buffer(touch);
if (touch->shell_surface) {
wl_shell_surface_add_listener(touch->shell_surface,
&shell_surface_listener, touch);
wl_shell_surface_set_toplevel(touch->shell_surface);
}
touch->init_width = width;
touch->init_height = height;
touch->surface = wl_compositor_create_surface(touch->compositor);
wl_surface_set_user_data(touch->surface, touch);
wl_shell_surface_set_title(touch->shell_surface, "simple-touch");
touch->xdg_surface =
xdg_wm_base_get_xdg_surface(touch->wm_base, touch->surface);
assert(touch->xdg_surface);
xdg_surface_add_listener(touch->xdg_surface, &xdg_surface_listener, touch);
touch->xdg_toplevel = xdg_surface_get_toplevel(touch->xdg_surface);
assert(touch->xdg_toplevel);
xdg_toplevel_add_listener(touch->xdg_toplevel,
&xdg_toplevel_listener, touch);
xdg_toplevel_set_title(touch->xdg_toplevel, "simple-touch");
xdg_toplevel_set_app_id(touch->xdg_toplevel, "simple-touch");
touch->wait_for_configure = true;
touch->needs_buffer_update = false;
memset(touch->data, 64, width * height * 4);
wl_surface_attach(touch->surface, touch->buffer, 0, 0);
wl_surface_damage(touch->surface, 0, 0, width, height);
wl_surface_commit(touch->surface);
touch->running = true;
return touch;
}
static void
destroy_touch(struct touch *touch)
{
if (touch->buffer->buffer)
wl_buffer_destroy(touch->buffer->buffer);
if (touch->xdg_toplevel)
xdg_toplevel_destroy(touch->xdg_toplevel);
if (touch->xdg_surface)
xdg_surface_destroy(touch->xdg_surface);
if (touch->wm_base)
xdg_wm_base_destroy(touch->wm_base);
if (touch->shm)
wl_shm_destroy(touch->shm);
if (touch->compositor)
wl_compositor_destroy(touch->compositor);
wl_surface_destroy(touch->surface);
free(touch->buffer);
free(touch);
}
int
main(int argc, char **argv)
{
@ -482,9 +352,8 @@ main(int argc, char **argv)
touch = touch_create(600, 500);
while (ret != -1 && touch->running)
while (ret != -1)
ret = wl_display_dispatch(touch->display);
destroy_touch(touch);
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -1,9 +0,0 @@
#version 450 core
layout(location = 0) in vec4 v_color;
layout(location = 0) out vec4 f_color;
void main() {
f_color = v_color;
}

View file

@ -1,17 +0,0 @@
#version 450 core
layout(std140, set = 0, binding = 0) uniform block {
uniform mat4 reflection;
uniform float offset;
};
layout(location = 0) in vec4 pos;
layout(location = 1) in vec4 color;
layout(location = 0) out vec4 v_color;
void main()
{
gl_Position = reflection * (pos + vec4(offset, offset, 0.0, 0.0));
v_color = color;
}

View file

@ -1,9 +0,0 @@
#version 450 core
layout(location = 0) in vec4 vVaryingColor;
layout(location = 0) out vec4 f_color;
void main()
{
f_color = vVaryingColor;
}

View file

@ -1,16 +0,0 @@
#version 450 core
layout(std140, set = 0, binding = 0) uniform block {
uniform mat4 rotation;
};
layout(location = 0) in vec4 in_position;
layout(location = 1) in vec4 in_color;
layout(location = 0) out vec4 vVaryingColor;
void main()
{
gl_Position = rotation * in_position;
vVaryingColor = vec4(in_color.rgba);
}

View file

@ -29,7 +29,6 @@
#include <string.h>
#include <time.h>
#include <math.h>
#include <errno.h>
#include <cairo.h>
#include <wayland-client.h>
@ -274,8 +273,7 @@ int main(int argc, char *argv[])
d = display_create(&argc, argv);
if (d == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
@ -285,7 +283,6 @@ int main(int argc, char *argv[])
smoke.window = window_create(d);
smoke.widget = window_add_widget(smoke.window, &smoke);
window_set_title(smoke.window, "smoke");
window_set_appid(smoke.window, "org.freedesktop.weston.smoke");
window_set_buffer_type(smoke.window, WINDOW_BUFFER_TYPE_SHM);
clock_gettime(CLOCK_MONOTONIC, &ts);

View file

@ -29,30 +29,19 @@
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#include <linux/input.h>
#include <cairo.h>
#include <wayland-util.h>
#include "shared/xalloc.h"
#include "shared/helpers.h"
#include "window.h"
struct stacking_window {
struct stacking *stacking;
struct window *window;
struct widget *widget;
struct wl_list link;
};
struct stacking {
struct display *display;
struct wl_list windows;
struct window *root_window;
};
static void
close_handler(void *data);
static void
button_handler(struct widget *widget,
struct input *input, uint32_t time,
@ -73,54 +62,29 @@ redraw_handler(struct widget *widget, void *data);
/* Iff parent_window is set, the new window will be transient. */
static struct window *
create_window(struct stacking *stacking, struct window *parent_window)
new_window(struct stacking *stacking, struct window *parent_window)
{
struct stacking_window *new_stacking_window;
struct window *new_window;
struct widget *new_widget;
new_stacking_window = xzalloc(sizeof *new_stacking_window);
new_stacking_window->stacking = stacking;
new_window = window_create(stacking->display);
new_stacking_window->window = new_window;
window_set_parent(new_window, parent_window);
new_widget = window_frame_create(new_window, new_window);
new_stacking_window->widget = new_widget;
wl_list_insert(stacking->windows.prev, &new_stacking_window->link);
window_set_title(new_window, "Stacking Test");
window_set_appid(new_window, "org.freedesktop.weston.stacking-test");
window_set_key_handler(new_window, key_handler);
window_set_keyboard_focus_handler(new_window, keyboard_focus_handler);
window_set_fullscreen_handler(new_window, fullscreen_handler);
window_set_close_handler(new_window, close_handler);
widget_set_button_handler(new_widget, button_handler);
widget_set_redraw_handler(new_widget, redraw_handler);
window_set_user_data(new_window, new_stacking_window);
window_set_user_data(new_window, stacking);
window_schedule_resize(new_window, 300, 300);
return new_window;
}
static void
destroy_window(struct stacking_window *stacking_window)
{
struct stacking *stacking = stacking_window->stacking;
widget_destroy(stacking_window->widget);
window_destroy(stacking_window->window);
wl_list_remove(&stacking_window->link);
free(stacking_window);
if (wl_list_empty(&stacking->windows))
display_exit(stacking->display);
}
static void
show_popup_cb(void *data, struct input *input, int index)
{
@ -142,27 +106,18 @@ show_popup(struct stacking *stacking, struct input *input, uint32_t time,
show_popup_cb, entries, ARRAY_LENGTH(entries));
}
static void
close_handler(void *data)
{
struct stacking_window *stacking_window = data;
destroy_window(stacking_window);
}
static void
button_handler(struct widget *widget,
struct input *input, uint32_t time,
uint32_t button,
enum wl_pointer_button_state state, void *data)
{
struct window *window = data;
struct stacking_window *stacking_window = window_get_user_data(window);
struct stacking *stacking = data;
switch (button) {
case BTN_RIGHT:
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
show_popup(stacking_window->stacking, input, time,
show_popup(stacking, input, time,
widget_get_user_data(widget));
break;
@ -178,8 +133,7 @@ key_handler(struct window *window,
uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
void *data)
{
struct stacking_window *stacking_window = data;
struct stacking *stacking = stacking_window->stacking;
struct stacking *stacking = data;
if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
return;
@ -195,24 +149,20 @@ key_handler(struct window *window,
case XKB_KEY_n:
/* New top-level window. */
create_window(stacking, NULL);
new_window(stacking, NULL);
break;
case XKB_KEY_p:
show_popup(stacking, input, time, window);
break;
case XKB_KEY_c:
destroy_window(stacking_window);
break;
case XKB_KEY_q:
display_exit(stacking->display);
exit (0);
break;
case XKB_KEY_t:
/* New transient window. */
create_window(stacking, window);
new_window(stacking, window);
break;
default:
@ -249,7 +199,7 @@ draw_string(cairo_t *cr,
cairo_save(cr);
cairo_select_font_face(cr, "sans-serif",
cairo_select_font_face(cr, "sans",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, 14);
@ -297,11 +247,12 @@ set_window_background_colour(cairo_t *cr, struct window *window)
static void
redraw_handler(struct widget *widget, void *data)
{
struct window *window = data;
struct window *window;
struct rectangle allocation;
cairo_t *cr;
widget_get_allocation(widget, &allocation);
window = widget_get_user_data(widget);
cr = widget_cairo_create(widget);
cairo_translate(cr, allocation.x, allocation.y);
@ -323,8 +274,7 @@ redraw_handler(struct widget *widget, void *data)
"Transient? %u\n"
"Keys: (f)ullscreen, (m)aximize,\n"
" (n)ew window, (p)opup,\n"
" (c)lose, (q)uit,\n"
" (t)ransient window\n",
" (q)uit, (t)ransient window\n",
window, window_is_fullscreen(window),
window_is_maximized(window), window_get_parent(window) ? 1 : 0);
@ -335,27 +285,22 @@ int
main(int argc, char *argv[])
{
struct stacking stacking;
struct stacking_window *stacking_window, *tmp;
memset(&stacking, 0, sizeof stacking);
wl_list_init(&stacking.windows);
stacking.display = display_create(&argc, argv);
if (stacking.display == NULL) {
fprintf(stderr, "Failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "Failed to create display: %m\n");
return -1;
}
display_set_user_data(stacking.display, &stacking);
create_window(&stacking, NULL);
stacking.root_window = new_window(&stacking, NULL);
display_run(stacking.display);
wl_list_for_each_safe(stacking_window, tmp, &stacking.windows, link)
destroy_window(stacking_window);
window_destroy(stacking.root_window);
display_destroy(stacking.display);
return 0;

View file

@ -32,7 +32,6 @@
#include <cairo.h>
#include <math.h>
#include <assert.h>
#include <errno.h>
#include <linux/input.h>
#include <wayland-client.h>
@ -44,7 +43,7 @@
#include "shared/helpers.h"
#include "shared/xalloc.h"
#include <libweston/zalloc.h>
#include "shared/zalloc.h"
#include "window.h"
#if 0
@ -56,8 +55,8 @@
static int32_t option_red_mode;
static int32_t option_triangle_mode;
static bool option_no_triangle;
static bool option_help;
static int32_t option_no_triangle;
static int32_t option_help;
static const struct weston_option options[] = {
{ WESTON_OPTION_INTEGER, "red-mode", 'r', &option_red_mode },
@ -296,7 +295,7 @@ create_shader(const char *source, GLenum shader_type)
char log[1000];
GLsizei len;
glGetShaderInfoLog(shader, 1000, &len, log);
fprintf(stderr, "Error: compiling %s: %.*s\n",
fprintf(stderr, "Error: compiling %s: %*s\n",
shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
len, log);
exit(1);
@ -325,7 +324,7 @@ triangle_init_gl(struct triangle_gl_state *trigl)
char log[1000];
GLsizei len;
glGetProgramInfoLog(program, 1000, &len, log);
fprintf(stderr, "Error: linking:\n%.*s\n", len, log);
fprintf(stderr, "Error: linking:\n%*s\n", len, log);
exit(1);
}
@ -733,8 +732,6 @@ demoapp_create(struct display *display)
app->window = window_create(app->display);
app->widget = window_frame_create(app->window, app);
window_set_title(app->window, "Wayland Sub-surface Demo");
window_set_appid(app->window,
"org.freedesktop.weston.wayland-sub-surface-demo");
window_set_key_handler(app->window, key_handler);
window_set_user_data(app->window, app);
@ -791,8 +788,7 @@ main(int argc, char *argv[])
display = display_create(&argc, argv);
if (display == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}

View file

@ -1,254 +0,0 @@
/*
* Copyright © 2014 Lyude
*
* 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cairo.h>
#include <math.h>
#include <assert.h>
#include <stdbool.h>
#include <linux/input.h>
#include <wayland-client.h>
#include "window.h"
#include "tablet-unstable-v2-client-protocol.h"
struct display *display;
struct window *window;
struct widget *widget;
cairo_surface_t *draw_buffer;
int old_x, old_y;
int current_x, current_y;
enum zwp_tablet_tool_v2_type tool_type;
bool tablet_is_down;
double current_pressure;
#define WL_TABLET_AXIS_MAX 65535
static void
redraw_handler(struct widget *widget, void *data)
{
cairo_surface_t *surface;
cairo_t *window_cr, *drawing_cr;
struct rectangle allocation;
widget_get_allocation(widget, &allocation);
surface = window_get_surface(window);
/* Setup the background */
window_cr = cairo_create(surface);
cairo_set_operator(window_cr, CAIRO_OPERATOR_SOURCE);
cairo_rectangle(window_cr,
allocation.x,
allocation.y,
allocation.width,
allocation.height);
cairo_set_source_rgba(window_cr, 0, 0, 0, 0.8);
cairo_fill(window_cr);
/* Update the drawing buffer */
if (tablet_is_down) {
if (old_x != -1 && old_y != -1) {
drawing_cr = cairo_create(draw_buffer);
if (tool_type == ZWP_TABLET_TOOL_V2_TYPE_PEN) {
cairo_set_source_rgb(drawing_cr, 1, 1, 1);
cairo_set_line_width(drawing_cr,
current_pressure /
WL_TABLET_AXIS_MAX * 7 + 1);
} else if (tool_type == ZWP_TABLET_TOOL_V2_TYPE_ERASER) {
cairo_set_operator(drawing_cr, CAIRO_OPERATOR_CLEAR);
cairo_set_source_rgb(drawing_cr, 0, 0, 0);
cairo_set_line_width(drawing_cr,
current_pressure /
WL_TABLET_AXIS_MAX * 30 + 10);
}
cairo_set_line_cap(drawing_cr, CAIRO_LINE_CAP_ROUND);
cairo_translate(drawing_cr,
-allocation.x,
-allocation.y);
cairo_move_to(drawing_cr, old_x, old_y);
cairo_line_to(drawing_cr, current_x, current_y);
cairo_stroke(drawing_cr);
cairo_destroy(drawing_cr);
}
old_x = current_x;
old_y = current_y;
}
/* Squash the drawing buffer onto the window's buffer */
cairo_set_source_surface(window_cr,
draw_buffer,
allocation.x,
allocation.y);
cairo_set_operator(window_cr, CAIRO_OPERATOR_ADD);
cairo_rectangle(window_cr,
allocation.x,
allocation.y,
allocation.width,
allocation.height);
cairo_clip(window_cr);
cairo_paint(window_cr);
cairo_destroy(window_cr);
cairo_surface_destroy(surface);
}
static void
resize_handler(struct widget *widget,
int32_t width, int32_t height,
void *data)
{
cairo_surface_t *tmp_buffer;
cairo_t *cr;
tmp_buffer = draw_buffer;
draw_buffer = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
width, height);
cr = cairo_create(draw_buffer);
cairo_set_source_rgba(cr, 0, 0, 0, 0);
cairo_rectangle(cr, 0, 0, width, height);
cairo_fill(cr);
if (tmp_buffer) {
cairo_set_source_surface(cr, tmp_buffer, 0, 0);
cairo_rectangle(cr, 0, 0, width, height);
cairo_clip(cr);
cairo_paint(cr);
}
cairo_destroy(cr);
cairo_surface_destroy(tmp_buffer);
}
static void
proximity_in_handler(struct widget *widget, struct tablet_tool *tool,
struct tablet *tablet, void *data)
{
tool_type = tablet_tool_get_type(tool);
}
static void
pressure_handler(struct widget *widget, struct tablet_tool *tool,
uint32_t pressure, void *data)
{
current_pressure = pressure;
}
static int
tablet_motion_handler(struct widget *widget, struct tablet_tool *tool,
float x, float y, void *data)
{
int cursor;
current_x = x;
current_y = y;
if (tablet_is_down) {
widget_schedule_redraw(widget);
cursor = CURSOR_HAND1;
} else {
cursor = CURSOR_LEFT_PTR;
}
return cursor;
}
static void
tablet_down_handler(struct widget *widget, struct tablet_tool *tool, void *data)
{
tablet_is_down = true;
}
static void
tablet_up_handler(struct widget *widget, struct tablet_tool *tool, void *data)
{
tablet_is_down = false;
old_x = -1;
old_y = -1;
}
static void
init_globals(void)
{
window = window_create(display);
widget = window_frame_create(window, NULL);
window_set_title(window, "Wayland Tablet Demo");
old_x = -1;
old_y = -1;
widget_set_tablet_tool_axis_handlers(widget,
tablet_motion_handler,
pressure_handler,
NULL, NULL,
NULL, NULL, NULL);
widget_set_tablet_tool_down_handler(widget, tablet_down_handler);
widget_set_tablet_tool_up_handler(widget, tablet_up_handler);
widget_set_tablet_tool_proximity_handlers(widget,
proximity_in_handler,
NULL);
widget_set_redraw_handler(widget, redraw_handler);
widget_set_resize_handler(widget, resize_handler);
widget_schedule_resize(widget, 1000, 800);
}
static void
cleanup(void)
{
widget_destroy(widget);
window_destroy(window);
}
int
main(int argc, char *argv[])
{
display = display_create(&argc, argv);
if (display == NULL) {
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
init_globals();
display_run(display);
cleanup();
display_destroy(display);
return 0;
}

View file

@ -23,7 +23,6 @@
#include "config.h"
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@ -39,26 +38,23 @@
#include <sys/epoll.h>
#include <wchar.h>
#include <locale.h>
#include <errno.h>
#include <linux/input.h>
#include <wayland-client.h>
#include <libweston/config-parser.h>
#include "shared/config-parser.h"
#include "shared/helpers.h"
#include "shared/xalloc.h"
#include "window.h"
static bool option_fullscreen;
static bool option_maximize;
static int option_fullscreen;
static char *option_font;
static int option_font_size;
static char *option_term;
static char *option_shell;
static struct wl_list terminal_list;
struct sigaction oldact;
static struct terminal *
terminal_create(struct display *display);
@ -484,7 +480,6 @@ struct terminal {
int selection_start_row, selection_start_col;
int selection_end_row, selection_end_col;
struct wl_list link;
int pace_pipe;
};
/* Create default tab stops, every 8 characters */
@ -864,13 +859,9 @@ resize_handler(struct widget *widget,
struct terminal *terminal = data;
int32_t columns, rows, m;
if (terminal->pace_pipe >= 0) {
close(terminal->pace_pipe);
terminal->pace_pipe = -1;
}
m = 2 * terminal->margin;
columns = (int32_t) round((width - m) / terminal->average_width);
rows = (int32_t) round((height - m) / terminal->extents.height);
columns = (width - m) / (int32_t) terminal->average_width;
rows = (height - m) / (int32_t) terminal->extents.height;
if (!window_is_fullscreen(terminal->window) &&
!window_is_maximized(terminal->window)) {
@ -1125,9 +1116,9 @@ redraw_handler(struct widget *widget, void *data)
cairo_stroke(cr);
}
/* skip space glyph (RLE) we use as a placeholder of
the right half of a double-width character,
because RLE is not available in every font. */
/* skip space glyph (RLE) we use as a placeholder of
the right half of a double-width character,
because RLE is not available in every font. */
if (p_row[col].ch == 0x200B)
continue;
@ -1688,7 +1679,6 @@ handle_escape(struct terminal *terminal)
fprintf(stderr, "Unimplemented windowOp %d\n", args[0]);
break;
}
break;
case 'u': /* Restore cursor location */
terminal->row = terminal->saved_row;
terminal->column = terminal->saved_column;
@ -1712,7 +1702,7 @@ handle_non_csi_escape(struct terminal *terminal, char code)
break;
case 'E': /* NEL - Newline */
terminal->column = 0;
FALLTHROUGH;
// fallthrough
case 'D': /* IND - Linefeed */
terminal->row += 1;
if (terminal->row > terminal->margin_bottom) {
@ -1894,7 +1884,7 @@ handle_special_char(struct terminal *terminal, char c)
if (terminal->mode & MODE_LF_NEWLINE) {
terminal->column = 0;
}
FALLTHROUGH;
/* fallthrough */
case '\v':
case '\f':
terminal->row++;
@ -2209,29 +2199,10 @@ data_source_cancelled(void *data, struct wl_data_source *source)
wl_data_source_destroy(source);
}
static void
data_source_dnd_drop_performed(void *data, struct wl_data_source *source)
{
}
static void
data_source_dnd_finished(void *data, struct wl_data_source *source)
{
}
static void
data_source_action(void *data,
struct wl_data_source *source, uint32_t dnd_action)
{
}
static const struct wl_data_source_listener data_source_listener = {
data_source_target,
data_source_send,
data_source_cancelled,
data_source_dnd_drop_performed,
data_source_dnd_finished,
data_source_action
data_source_cancelled
};
static const char text_mime_type[] = "text/plain;charset=utf-8";
@ -2286,9 +2257,6 @@ terminal_copy(struct terminal *terminal, struct input *input)
{
terminal->selection =
display_create_data_source(terminal->display);
if (!terminal->selection)
return;
wl_data_source_offer(terminal->selection,
"text/plain;charset=utf-8");
wl_data_source_add_listener(terminal->selection,
@ -2631,7 +2599,7 @@ recompute_selection(struct terminal *terminal)
int side_margin, top_margin;
int start_x, end_x;
int cw, ch;
union utf8_char *data = NULL;
union utf8_char *data;
cw = terminal->average_width;
ch = terminal->extents.height;
@ -2950,8 +2918,6 @@ terminal_create(struct display *display)
terminal->widget = window_frame_create(terminal->window, terminal);
terminal->title = xstrdup("Wayland Terminal");
window_set_title(terminal->window, terminal->title);
window_set_appid(terminal->window,
"org.freedesktop.weston.wayland-terminal");
widget_set_transparent(terminal->widget, 0);
init_state_machine(&terminal->state_machine);
@ -3024,22 +2990,13 @@ static void
terminal_destroy(struct terminal *terminal)
{
display_unwatch_fd(terminal->display, terminal->master);
close(terminal->master);
cairo_scaled_font_destroy(terminal->font_bold);
cairo_scaled_font_destroy(terminal->font_normal);
widget_destroy(terminal->widget);
window_destroy(terminal->window);
close(terminal->master);
wl_list_remove(&terminal->link);
if (wl_list_empty(&terminal_list))
display_exit(terminal->display);
free(terminal->data);
free(terminal->data_attr);
free(terminal->tab_ruler);
free(terminal->title);
free(terminal);
}
@ -3058,12 +3015,10 @@ io_handler(struct task *task, uint32_t events)
}
len = read(terminal->master, buffer, sizeof buffer);
if (len < 0) {
if (len < 0)
terminal_destroy(terminal);
return;
}
terminal_data(terminal, buffer, len);
else
terminal_data(terminal, buffer, len);
}
static int
@ -3071,52 +3026,21 @@ terminal_run(struct terminal *terminal, const char *path)
{
int master;
pid_t pid;
int pipes[2];
/* Awkwardness: There's a sticky race condition here. If
* anything prints after the forkpty() but before the window has
* a size then we'll segfault. So we make a pipe and wait on
* it before actually exec()ing the terminal. The resize
* handler closes it in the parent process and the child continues
* on to launch a shell.
*
* The reason we don't just do terminal_run() after the window
* has a size is that we'd prefer to perform the fork() before
* the process opens a wayland connection.
*/
if (pipe(pipes) == -1) {
fprintf(stderr, "Can't create pipe for pacing.\n");
exit(EXIT_FAILURE);
}
pid = forkpty(&master, NULL, NULL, NULL);
if (pid == 0) {
int ret;
close(pipes[1]);
do {
char tmp;
ret = read(pipes[0], &tmp, 1);
} while (ret == -1 && errno == EINTR);
close(pipes[0]);
setenv("TERM", option_term, 1);
setenv("COLORTERM", option_term, 1);
sigaction(SIGPIPE, &oldact, NULL);
if (execl(path, path, NULL)) {
printf("exec failed: %s\n", strerror(errno));
printf("exec failed: %m\n");
exit(EXIT_FAILURE);
}
} else if (pid < 0) {
fprintf(stderr, "failed to fork and create pty (%s).\n",
strerror(errno));
fprintf(stderr, "failed to fork and create pty (%m).\n");
return -1;
}
close(pipes[0]);
terminal->master = master;
terminal->pace_pipe = pipes[1];
fcntl(master, F_SETFL, O_NONBLOCK);
terminal->io_task.run = io_handler;
display_watch_fd(terminal->display, terminal->master,
@ -3124,8 +3048,6 @@ terminal_run(struct terminal *terminal, const char *path)
if (option_fullscreen)
window_set_fullscreen(terminal->window, 1);
else if (option_maximize)
window_set_maximized(terminal->window, 1);
else
terminal_resize(terminal, 80, 24);
@ -3134,7 +3056,6 @@ terminal_run(struct terminal *terminal, const char *path)
static const struct weston_option terminal_options[] = {
{ WESTON_OPTION_BOOLEAN, "fullscreen", 'f', &option_fullscreen },
{ WESTON_OPTION_BOOLEAN, "maximized", 'm', &option_maximize },
{ WESTON_OPTION_STRING, "font", 0, &option_font },
{ WESTON_OPTION_INTEGER, "font-size", 0, &option_font_size },
{ WESTON_OPTION_STRING, "shell", 0, &option_shell },
@ -3143,9 +3064,8 @@ static const struct weston_option terminal_options[] = {
int main(int argc, char *argv[])
{
struct display *d;
struct terminal *terminal, *tmp;
struct terminal *terminal;
const char *config_file;
struct sigaction sigpipe;
struct weston_config *config;
struct weston_config_section *s;
@ -3155,12 +3075,12 @@ int main(int argc, char *argv[])
option_shell = getenv("SHELL");
if (!option_shell)
option_shell = "/bin/sh";
option_shell = "/bin/bash";
config_file = weston_config_get_name_from_env();
config = weston_config_parse(config_file);
s = weston_config_get_section(config, "terminal", NULL, NULL);
weston_config_section_get_string(s, "font", &option_font, "monospace");
weston_config_section_get_string(s, "font", &option_font, "mono");
weston_config_section_get_int(s, "font-size", &option_font_size, 14);
weston_config_section_get_string(s, "term", &option_term, "xterm");
weston_config_destroy(config);
@ -3169,27 +3089,15 @@ int main(int argc, char *argv[])
ARRAY_LENGTH(terminal_options), &argc, argv) > 1) {
printf("Usage: %s [OPTIONS]\n"
" --fullscreen or -f\n"
" --maximized or -m\n"
" --font=NAME\n"
" --font-size=SIZE\n"
" --shell=NAME\n", argv[0]);
return 1;
}
/* Disable SIGPIPE so that paste operations do not crash the program
* when the file descriptor provided to receive data is a pipe or
* socket whose reading end has been closed */
sigpipe.sa_handler = SIG_IGN;
sigemptyset(&sigpipe.sa_mask);
sigemptyset(&oldact.sa_mask);
sigpipe.sa_flags = 0;
sigaction(SIGPIPE, &sigpipe, &oldact);
d = display_create(&argc, argv);
if (d == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
@ -3200,9 +3108,5 @@ int main(int argc, char *argv[])
display_run(d);
wl_list_for_each_safe(terminal, tmp, &terminal_list, link)
terminal_destroy(terminal);
display_destroy(d);
return 0;
}

View file

@ -1,972 +0,0 @@
/*
* Copyright 2012 Intel Corporation
* Copyright 2017-2018 Collabora, Ltd.
* Copyright 2017-2018 General Electric Company
*
* 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 <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cairo.h>
#include <math.h>
#include <assert.h>
#include <getopt.h>
#include <errno.h>
#include <wayland-client.h>
#include "clients/window.h"
#include "shared/helpers.h"
#include <libweston/matrix.h>
#include "weston-touch-calibration-client-protocol.h"
enum exit_code {
CAL_EXIT_SUCCESS = 0,
CAL_EXIT_ERROR = 1,
CAL_EXIT_CANCELLED = 2,
};
static int debug_;
static int verbose_;
#define pr_ver(...) do { \
if (verbose_) \
printf(__VA_ARGS__); \
} while (0)
#define pr_dbg(...) do { \
if (debug_) \
fprintf(stderr, __VA_ARGS__); \
} while (0)
static void
pr_err(const char *fmt, ...) WL_PRINTF(1, 2);
/* Our points for the calibration must be not be on a line */
static const struct {
float x_ratio, y_ratio;
} test_ratios[] = {
{ 0.15, 0.10 }, /* three points for calibration */
{ 0.85, 0.13 },
{ 0.20, 0.80 },
{ 0.70, 0.75 } /* and one for verification */
};
#define NR_SAMPLES ((int)ARRAY_LENGTH(test_ratios))
struct point {
double x;
double y;
};
struct sample {
int ind;
struct point drawn; /**< drawn point, pixels */
struct weston_touch_coordinate *pending;
struct point drawn_cal; /**< drawn point, converted */
bool conv_done;
struct point touched; /**< touch point, normalized */
bool touch_done;
};
struct poly {
struct color {
double r, g, b, a;
} color;
int n_verts;
const struct point *verts;
};
/** Touch event handling state machine
*
* Only a complete down->up->frame sequence should be accepted with user
* feedback "right", and anything that deviates from that (invalid_touch,
* cancel, multiple touch-downs) needs to undo the current sample and
* possibly show user feedback "wrong".
*
* \<STATE\>
* - \<triggers\>: \<actions\>
*
* IDLE
* - touch down: sample, -> DOWN
* - touch up: no-op
* - frame: no-op
* - invalid_touch: (undo), wrong, -> WAIT
* - cancel: no-op
* DOWN (first touch down)
* - touch down: undo, wrong, -> WAIT
* - touch up: -> UP
* - frame: no-op
* - invalid_touch: undo, wrong, -> WAIT
* - cancel: undo, -> IDLE
* UP (first touch was down and up)
* - touch down: undo, wrong, -> WAIT
* - touch up: no-op
* - frame: right, touch finish, -> WAIT
* - invalid_touch: undo, wrong, -> WAIT
* - cancel: undo, -> IDLE
* WAIT (show user feedback)
* - touch down: no-op
* - touch up: no-op
* - frame, cancel, timer: if num_tp == 0 && timer_done -> IDLE
* - invalid_touch: no-op
*/
enum touch_state {
STATE_IDLE,
STATE_DOWN,
STATE_UP,
STATE_WAIT
};
struct calibrator {
struct sample samples[NR_SAMPLES];
int current_sample;
struct display *display;
struct weston_touch_calibration *calibration;
struct weston_touch_calibrator *calibrator;
struct window *window;
struct widget *widget;
int n_devices_listed;
char *match_name;
char *device_name;
int width;
int height;
bool cancelled;
const struct poly *current_poly;
bool exiting;
struct toytimer wait_timer;
bool timer_pending;
enum touch_state state;
int num_tp; /* touch points down count */
};
static struct sample *
current_sample(struct calibrator *cal)
{
return &cal->samples[cal->current_sample];
}
static void
sample_start(struct calibrator *cal, int i)
{
struct sample *s = &cal->samples[i];
assert(i >= 0 && i < NR_SAMPLES);
s->ind = i;
s->drawn.x = round(test_ratios[i].x_ratio * cal->width);
s->drawn.y = round(test_ratios[i].y_ratio * cal->height);
s->pending = NULL;
s->conv_done = false;
s->touch_done = false;
cal->current_sample = i;
}
static struct point
wire_to_point(uint32_t xu, uint32_t yu)
{
struct point p = {
.x = (double)xu / 0xffffffff,
.y = (double)yu / 0xffffffff
};
return p;
}
static void
sample_touch_down(struct calibrator *cal, uint32_t xu, uint32_t yu)
{
struct sample *s = current_sample(cal);
s->touched = wire_to_point(xu, yu);
s->touch_done = true;
pr_dbg("Down[%d] (%f, %f)\n", s->ind, s->touched.x, s->touched.y);
}
static void
coordinate_result_handler(void *data, struct weston_touch_coordinate *interface,
uint32_t xu, uint32_t yu)
{
struct sample *s = data;
weston_touch_coordinate_destroy(s->pending);
s->pending = NULL;
s->drawn_cal = wire_to_point(xu, yu);
s->conv_done = true;
pr_dbg("Conv[%d] (%f, %f)\n", s->ind, s->drawn_cal.x, s->drawn_cal.y);
}
struct weston_touch_coordinate_listener coordinate_listener = {
coordinate_result_handler
};
static void
sample_undo(struct calibrator *cal)
{
struct sample *s = current_sample(cal);
pr_dbg("Undo[%d]\n", s->ind);
s->touch_done = false;
s->conv_done = false;
if (s->pending) {
weston_touch_coordinate_destroy(s->pending);
s->pending = NULL;
}
}
static void
sample_finish(struct calibrator *cal)
{
struct sample *s = current_sample(cal);
pr_dbg("Finish[%d]\n", s->ind);
assert(!s->pending && !s->conv_done);
s->pending = weston_touch_calibrator_convert(cal->calibrator,
(int32_t)s->drawn.x,
(int32_t)s->drawn.y);
weston_touch_coordinate_add_listener(s->pending,
&coordinate_listener, s);
if (cal->current_sample + 1 < NR_SAMPLES) {
sample_start(cal, cal->current_sample + 1);
} else {
pr_dbg("got all touches\n");
cal->exiting = true;
}
}
/*
* Calibration algorithm:
*
* The equation we want to apply at event time where x' and y' are the
* calibrated co-ordinates.
*
* x' = Ax + By + C
* y' = Dx + Ey + F
*
* For example "zero calibration" would be A=1.0 B=0.0 C=0.0, D=0.0, E=1.0,
* and F=0.0.
*
* With 6 unknowns we need 6 equations to find the constants:
*
* x1' = Ax1 + By1 + C
* y1' = Dx1 + Ey1 + F
* ...
* x3' = Ax3 + By3 + C
* y3' = Dx3 + Ey3 + F
*
* In matrix form:
*
* x1' x1 y1 1 A
* x2' = x2 y2 1 x B
* x3' x3 y3 1 C
*
* So making the matrix M we can find the constants with:
*
* A x1'
* B = M^-1 x x2'
* C x3'
*
* (and similarly for D, E and F)
*
* For the calibration the desired values x, y are the same values at which
* we've drawn at.
*
*/
static int
compute_calibration(struct calibrator *cal, float *result)
{
struct weston_matrix m;
struct weston_matrix inverse;
struct weston_vector x_calib;
struct weston_vector y_calib;
int i;
assert(NR_SAMPLES >= 3);
/*
* x1 y1 1 0
* x2 y2 1 0
* x3 y3 1 0
* 0 0 0 1
*/
weston_matrix_init(&m);
for (i = 0; i < 3; i++) {
m.M.col[0].el[i] = cal->samples[i].touched.x;
m.M.col[1].el[i] = cal->samples[i].touched.y;
m.M.col[2].el[i] = 1.0f;
}
m.type = WESTON_MATRIX_TRANSFORM_OTHER;
if (weston_matrix_invert(&inverse, &m) < 0) {
pr_err("non-invertible matrix during computation\n");
return -1;
}
for (i = 0; i < 3; i++) {
x_calib.v.el[i] = cal->samples[i].drawn_cal.x;
y_calib.v.el[i] = cal->samples[i].drawn_cal.y;
}
x_calib.v.el[3] = 0.0f;
y_calib.v.el[3] = 0.0f;
/* Multiples into the vector */
weston_matrix_transform(&inverse, &x_calib);
weston_matrix_transform(&inverse, &y_calib);
for (i = 0; i < 3; i++)
result[i] = x_calib.v.el[i];
for (i = 0; i < 3; i++)
result[i + 3] = y_calib.v.el[i];
return 0;
}
static int
verify_calibration(struct calibrator *cal, const float *r)
{
double thr = 0.1; /* accepted error radius */
struct point e; /* expected value; error */
const struct sample *s = &cal->samples[3];
/* transform raw touches through the matrix */
e.x = r[0] * s->touched.x + r[1] * s->touched.y + r[2];
e.y = r[3] * s->touched.x + r[4] * s->touched.y + r[5];
/* compute error */
e.x -= s->drawn_cal.x;
e.y -= s->drawn_cal.y;
pr_dbg("calibration test error: %f, %f\n", e.x, e.y);
if (e.x * e.x + e.y * e.y < thr * thr)
return 0;
pr_err("Calibration verification failed, too large error.\n");
return -1;
}
static void
send_calibration(struct calibrator *cal, float *values)
{
struct wl_array matrix;
float *f;
int i;
wl_array_init(&matrix);
for (i = 0; i < 6; i++) {
f = wl_array_add(&matrix, sizeof *f);
*f = values[i];
}
weston_touch_calibration_save(cal->calibration,
cal->device_name, &matrix);
wl_array_release(&matrix);
}
static const struct point cross_verts[] = {
{ 0.1, 0.2 },
{ 0.2, 0.1 },
{ 0.5, 0.4 },
{ 0.8, 0.1 },
{ 0.9, 0.2 },
{ 0.6, 0.5 },
{ 0.9, 0.8 },
{ 0.8, 0.9 },
{ 0.5, 0.6 },
{ 0.2, 0.9 },
{ 0.1, 0.8 },
{ 0.4, 0.5 },
};
/* a red cross, for "wrong" */
static const struct poly cross = {
.color = { 0.7, 0.0, 0.0, 1.0 },
.n_verts = ARRAY_LENGTH(cross_verts),
.verts = cross_verts
};
static const struct point check_verts[] = {
{ 0.5, 0.7 },
{ 0.8, 0.1 },
{ 0.9, 0.1 },
{ 0.55, 0.8 },
{ 0.45, 0.8 },
{ 0.3, 0.5 },
{ 0.4, 0.5 }
};
/* a green check mark, for "right" */
static const struct poly check = {
.color = { 0.0, 0.7, 0.0, 1.0 },
.n_verts = ARRAY_LENGTH(check_verts),
.verts = check_verts
};
static void
draw_poly(cairo_t *cr, const struct poly *poly)
{
int i;
cairo_set_source_rgba(cr, poly->color.r, poly->color.g,
poly->color.b, poly->color.a);
cairo_move_to(cr, poly->verts[0].x, poly->verts[0].y);
for (i = 1; i < poly->n_verts; i++)
cairo_line_to(cr, poly->verts[i].x, poly->verts[i].y);
cairo_close_path(cr);
cairo_fill(cr);
}
static void
feedback_show(struct calibrator *cal, const struct poly *what)
{
cal->current_poly = what;
widget_schedule_redraw(cal->widget);
toytimer_arm_once_usec(&cal->wait_timer, 1000 * 1000);
cal->timer_pending = true;
}
static void
feedback_hide(struct calibrator *cal)
{
cal->current_poly = NULL;
widget_schedule_redraw(cal->widget);
}
static void
try_enter_state_idle(struct calibrator *cal)
{
if (cal->num_tp != 0)
return;
if (cal->timer_pending)
return;
cal->state = STATE_IDLE;
feedback_hide(cal);
if (cal->exiting)
display_exit(cal->display);
}
static void
enter_state_wait(struct calibrator *cal)
{
assert(cal->timer_pending);
cal->state = STATE_WAIT;
}
static void
wait_timer_done(struct toytimer *tt)
{
struct calibrator *cal = container_of(tt, struct calibrator, wait_timer);
assert(cal->state == STATE_WAIT);
cal->timer_pending = false;
try_enter_state_idle(cal);
}
static void
redraw_handler(struct widget *widget, void *data)
{
struct calibrator *cal = data;
struct sample *s = current_sample(cal);
struct rectangle allocation;
cairo_surface_t *surface;
cairo_t *cr;
widget_get_allocation(cal->widget, &allocation);
assert(allocation.width == cal->width);
assert(allocation.height == cal->height);
surface = window_get_surface(cal->window);
cr = cairo_create(surface);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
cairo_paint(cr);
if (!cal->current_poly) {
cairo_translate(cr, s->drawn.x, s->drawn.y);
cairo_set_line_width(cr, 2.0);
cairo_set_source_rgb(cr, 0.7, 0.0, 0.0);
cairo_move_to(cr, 0, -10.0);
cairo_line_to(cr, 0, 10.0);
cairo_stroke(cr);
cairo_move_to(cr, -10.0, 0);
cairo_line_to(cr, 10.0, 0.0);
cairo_stroke(cr);
} else {
cairo_scale(cr, allocation.width, allocation.height);
draw_poly(cr, cal->current_poly);
}
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
static struct calibrator *
calibrator_create(struct display *display, const char *match_name)
{
struct calibrator *cal;
cal = zalloc(sizeof *cal);
if (!cal)
abort();
cal->match_name = match_name ? strdup(match_name) : NULL;
cal->window = window_create_custom(display);
cal->widget = window_add_widget(cal->window, cal);
window_inhibit_redraw(cal->window);
window_set_title(cal->window, "Touchscreen calibrator");
window_set_appid(cal->window,
"org.freedesktop.weston.touchscreen-calibrator");
cal->display = display;
widget_set_redraw_handler(cal->widget, redraw_handler);
toytimer_init(&cal->wait_timer, CLOCK_MONOTONIC,
display, wait_timer_done);
cal->state = STATE_IDLE;
cal->num_tp = 0;
return cal;
}
static void
configure_handler(void *data, struct weston_touch_calibrator *interface,
int32_t width, int32_t height)
{
struct calibrator *cal = data;
pr_dbg("Configure calibrator window to size %ix%i\n", width, height);
cal->width = width;
cal->height = height;
window_schedule_resize(cal->window, width, height);
window_uninhibit_redraw(cal->window);
sample_start(cal, 0);
widget_schedule_redraw(cal->widget);
}
static void
cancel_calibration_handler(void *data, struct weston_touch_calibrator *interface)
{
struct calibrator *cal = data;
pr_dbg("calibration cancelled by the display server, quitting.\n");
cal->cancelled = true;
display_exit(cal->display);
}
static void
invalid_touch_handler(void *data, struct weston_touch_calibrator *interface)
{
struct calibrator *cal = data;
pr_dbg("invalid touch\n");
switch (cal->state) {
case STATE_IDLE:
case STATE_DOWN:
case STATE_UP:
sample_undo(cal);
feedback_show(cal, &cross);
enter_state_wait(cal);
break;
case STATE_WAIT:
/* no-op */
break;
}
}
static void
down_handler(void *data, struct weston_touch_calibrator *interface,
uint32_t time, int32_t id, uint32_t xu, uint32_t yu)
{
struct calibrator *cal = data;
cal->num_tp++;
switch (cal->state) {
case STATE_IDLE:
sample_touch_down(cal, xu, yu);
cal->state = STATE_DOWN;
break;
case STATE_DOWN:
case STATE_UP:
sample_undo(cal);
feedback_show(cal, &cross);
enter_state_wait(cal);
break;
case STATE_WAIT:
/* no-op */
break;
}
if (cal->current_poly)
return;
}
static void
up_handler(void *data, struct weston_touch_calibrator *interface,
uint32_t time, int32_t id)
{
struct calibrator *cal = data;
cal->num_tp--;
if (cal->num_tp < 0) {
pr_dbg("Unmatched touch up.\n");
cal->num_tp = 0;
}
switch (cal->state) {
case STATE_DOWN:
cal->state = STATE_UP;
break;
case STATE_IDLE:
case STATE_UP:
case STATE_WAIT:
/* no-op */
break;
}
}
static void
motion_handler(void *data, struct weston_touch_calibrator *interface,
uint32_t time, int32_t id, uint32_t xu, uint32_t yu)
{
/* motion is ignored */
}
static void
frame_handler(void *data, struct weston_touch_calibrator *interface)
{
struct calibrator *cal = data;
switch (cal->state) {
case STATE_IDLE:
case STATE_DOWN:
/* no-op */
break;
case STATE_UP:
feedback_show(cal, &check);
sample_finish(cal);
enter_state_wait(cal);
break;
case STATE_WAIT:
try_enter_state_idle(cal);
break;
}
}
static void
cancel_handler(void *data, struct weston_touch_calibrator *interface)
{
struct calibrator *cal = data;
cal->num_tp = 0;
switch (cal->state) {
case STATE_IDLE:
/* no-op */
break;
case STATE_DOWN:
case STATE_UP:
sample_undo(cal);
try_enter_state_idle(cal);
break;
case STATE_WAIT:
try_enter_state_idle(cal);
break;
}
}
struct weston_touch_calibrator_listener calibrator_listener = {
configure_handler,
cancel_calibration_handler,
invalid_touch_handler,
down_handler,
up_handler,
motion_handler,
frame_handler,
cancel_handler
};
static void
calibrator_show(struct calibrator *cal)
{
struct wl_surface *surface = window_get_wl_surface(cal->window);
cal->calibrator =
weston_touch_calibration_create_calibrator(cal->calibration,
surface,
cal->device_name);
weston_touch_calibrator_add_listener(cal->calibrator,
&calibrator_listener, cal);
}
static void
calibrator_destroy(struct calibrator *cal)
{
toytimer_fini(&cal->wait_timer);
if (cal->calibrator)
weston_touch_calibrator_destroy(cal->calibrator);
if (cal->calibration)
weston_touch_calibration_destroy(cal->calibration);
if (cal->widget)
widget_destroy(cal->widget);
if (cal->window)
window_destroy(cal->window);
free(cal->match_name);
free(cal->device_name);
free(cal);
}
static void
touch_device_handler(void *data, struct weston_touch_calibration *c,
const char *device, const char *head)
{
struct calibrator *cal = data;
cal->n_devices_listed++;
if (!cal->match_name) {
printf("device \"%s\" - head \"%s\"\n", device, head);
return;
}
if (cal->device_name)
return;
if (strcmp(cal->match_name, device) == 0 ||
strcmp(cal->match_name, head) == 0)
cal->device_name = strdup(device);
}
struct weston_touch_calibration_listener touch_calibration_listener = {
touch_device_handler
};
static void
global_handler(struct display *display, uint32_t name,
const char *interface, uint32_t version, void *data)
{
struct calibrator *cal = data;
if (strcmp(interface, "weston_touch_calibration") == 0) {
cal->calibration = display_bind(display, name,
&weston_touch_calibration_interface, 1);
weston_touch_calibration_add_listener(cal->calibration,
&touch_calibration_listener,
cal);
}
}
static int
calibrator_run(struct calibrator *cal)
{
struct wl_display *dpy;
struct sample *s;
bool wait;
int i;
int ret;
float result[6];
calibrator_show(cal);
display_run(cal->display);
if (cal->cancelled)
return CAL_EXIT_CANCELLED;
/* remove the window, no more input events */
widget_destroy(cal->widget);
cal->widget = NULL;
window_destroy(cal->window);
cal->window = NULL;
/* wait for all conversions to return */
dpy = display_get_display(cal->display);
do {
wait = false;
for (i = 0; i < NR_SAMPLES; i++)
if (cal->samples[i].pending)
wait = true;
if (wait) {
ret = wl_display_roundtrip(dpy);
if (ret < 0)
return CAL_EXIT_ERROR;
}
} while (wait);
for (i = 0; i < NR_SAMPLES; i++) {
s = &cal->samples[i];
if (!s->conv_done || !s->touch_done)
return CAL_EXIT_ERROR;
}
if (compute_calibration(cal, result) < 0)
return CAL_EXIT_ERROR;
if (verify_calibration(cal, result) < 0)
return CAL_EXIT_ERROR;
pr_ver("Calibration values:");
for (i = 0; i < 6; i++)
pr_ver(" %f", result[i]);
pr_ver("\n");
send_calibration(cal, result);
ret = wl_display_roundtrip(dpy);
if (ret < 0)
return CAL_EXIT_ERROR;
return CAL_EXIT_SUCCESS;
}
static void
pr_err(const char *fmt, ...)
{
va_list argp;
va_start(argp, fmt);
fprintf(stderr, "%s error: ", program_invocation_short_name);
vfprintf(stderr, fmt, argp);
va_end(argp);
}
static void
help(void)
{
fprintf(stderr, "Compute a touchscreen calibration matrix for "
"a Wayland compositor by\n"
"having the user touch points on the screen.\n\n");
fprintf(stderr, "Usage: %s [options...] name\n\n",
program_invocation_short_name);
fprintf(stderr,
"Where 'name' can be a touch device sys path or a head name.\n"
"If 'name' is not given, all devices available for "
"calibration will be listed.\n"
"If 'name' is given, it must be exactly as listed.\n"
"Options:\n"
" --debug Print messages to help debugging.\n"
" -h, --help Display this help message\n"
" -v, --verbose Print list header and calibration result.\n");
}
int
main(int argc, char *argv[])
{
struct display *display;
struct calibrator *cal;
int c;
char *match_name = NULL;
int exit_code = CAL_EXIT_SUCCESS;
static const struct option opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "debug", no_argument, &debug_, 1 },
{ "verbose", no_argument, &verbose_, 1 },
{ 0, 0, NULL, 0 }
};
while ((c = getopt_long(argc, argv, "hv", opts, NULL)) != -1) {
switch (c) {
case 'h':
help();
return CAL_EXIT_SUCCESS;
case 'v':
verbose_ = 1;
break;
case 0:
break;
default:
return CAL_EXIT_ERROR;
}
}
if (optind < argc)
match_name = argv[optind++];
if (optind < argc) {
pr_err("extra arguments given.\n\n");
help();
return CAL_EXIT_ERROR;
}
display = display_create(&argc, argv);
if (!display)
return CAL_EXIT_ERROR;
cal = calibrator_create(display, match_name);
if (!cal)
return CAL_EXIT_ERROR;
display_set_user_data(display, cal);
display_set_global_handler(display, global_handler);
if (!match_name)
pr_ver("Available touch devices:\n");
/* Roundtrip to get list of available touch devices,
* first globals, then touch_device events */
wl_display_roundtrip(display_get_display(display));
wl_display_roundtrip(display_get_display(display));
if (!cal->calibration) {
exit_code = CAL_EXIT_ERROR;
pr_err("the Wayland server does not expose the calibration interface.\n");
} else if (cal->device_name) {
exit_code = calibrator_run(cal);
} else if (match_name) {
exit_code = CAL_EXIT_ERROR;
pr_err("\"%s\" was not found.\n", match_name);
} else if (cal->n_devices_listed == 0) {
fprintf(stderr, "No devices listed.\n");
}
calibrator_destroy(cal);
display_destroy(display);
return exit_code;
}

View file

@ -29,7 +29,6 @@
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <cairo.h>
#include <linux/input.h>
@ -227,13 +226,11 @@ static void
usage(int error_code)
{
fprintf(stderr, "Usage: transformed [OPTIONS]\n\n"
" -d\t\tUse \"driver\" fullscreen method\n"
" -w <width>\tSet window width to <width>\n"
" -h <height>\tSet window height to <height>\n"
" --help\tShow this help text\n\n");
fprintf(stderr, "This version has been fixed for "
"https://gitlab.freedesktop.org/wayland/weston/issues/99 .\n");
exit(error_code);
}
@ -266,8 +263,7 @@ int main(int argc, char *argv[])
d = display_create(&argc, argv);
if (d == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
@ -277,8 +273,6 @@ int main(int argc, char *argv[])
window_add_widget(transformed.window, &transformed);
window_set_title(transformed.window, "Transformed");
window_set_appid(transformed.window,
"org.freedesktop.weston.transformed");
widget_set_transparent(transformed.widget, 0);
widget_set_default_cursor(transformed.widget, CURSOR_BLANK);

View file

@ -1,502 +0,0 @@
/*
* Copyright © 2017 Pekka Paalanen <pq@iki.fi>
* Copyright © 2018 Zodiac Inflight Innovations
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <wayland-client.h>
#include "shared/helpers.h"
#include <libweston/zalloc.h>
#include "weston-debug-client-protocol.h"
struct debug_app {
struct {
bool help;
bool list;
bool bind_all;
char *output;
char *outfd;
} opt;
int out_fd;
struct wl_display *dpy;
struct wl_registry *registry;
struct weston_debug_v1 *debug_iface;
struct wl_list stream_list;
};
struct debug_stream {
struct wl_list link;
bool should_bind;
char *name;
char *desc;
struct weston_debug_stream_v1 *obj;
};
/**
* Called either through stream_find in response to an advertisement
* event (see comment on stream_find) when we have all the information,
* or directly from option parsing to make a placeholder entry when the
* stream was explicitly named on the command line to bind to.
*/
static struct debug_stream *
stream_alloc(struct debug_app *app, const char *name, const char *desc)
{
struct debug_stream *stream;
stream = zalloc(sizeof *stream);
if (!stream)
return NULL;
stream->name = strdup(name);
if (!stream->name) {
free(stream);
return NULL;
}
if (desc) {
stream->desc = strdup(desc);
if (!stream->desc) {
free(stream->name);
free(stream);
return NULL;
}
}
stream->should_bind = app->opt.bind_all;
wl_list_insert(app->stream_list.prev, &stream->link);
return stream;
}
/**
* Called in response to a stream advertisement event. If our stream was
* manually specified on the command line, then it will already have a
* dummy entry in stream_list: we fill in its description and return.
* If there's no entry in the list, we make a new one and return that.
*/
static struct debug_stream *
stream_find(struct debug_app *app, const char *name, const char *desc)
{
struct debug_stream *stream;
wl_list_for_each(stream, &app->stream_list, link) {
if (strcmp(stream->name, name) == 0) {
assert(stream->desc == NULL);
if (desc)
stream->desc = strdup(desc);
return stream;
}
}
return stream_alloc(app, name, desc);
}
static void
stream_destroy(struct debug_stream *stream)
{
if (stream->obj)
weston_debug_stream_v1_destroy(stream->obj);
wl_list_remove(&stream->link);
free(stream->desc);
free(stream->name);
free(stream);
}
static void
destroy_streams(struct debug_app *app)
{
struct debug_stream *stream;
struct debug_stream *tmp;
wl_list_for_each_safe(stream, tmp, &app->stream_list, link)
stream_destroy(stream);
}
static void
debug_advertise(void *data, struct weston_debug_v1 *debug, const char *name,
const char *desc)
{
struct debug_app *app = data;
(void) stream_find(app, name, desc);
}
static const struct weston_debug_v1_listener debug_listener = {
debug_advertise,
};
static void
global_handler(void *data, struct wl_registry *registry, uint32_t id,
const char *interface, uint32_t version)
{
struct debug_app *app = data;
uint32_t myver;
assert(app->registry == registry);
if (!strcmp(interface, weston_debug_v1_interface.name)) {
if (app->debug_iface)
return;
myver = MIN(1, version);
app->debug_iface =
wl_registry_bind(registry, id,
&weston_debug_v1_interface, myver);
weston_debug_v1_add_listener(app->debug_iface, &debug_listener,
app);
}
}
static void
global_remove_handler(void *data, struct wl_registry *registry, uint32_t name)
{
}
static const struct wl_registry_listener registry_listener = {
global_handler,
global_remove_handler
};
static void
handle_stream_complete(void *data, struct weston_debug_stream_v1 *obj)
{
struct debug_stream *stream = data;
assert(stream->obj == obj);
stream_destroy(stream);
}
static void
handle_stream_failure(void *data, struct weston_debug_stream_v1 *obj,
const char *msg)
{
struct debug_stream *stream = data;
assert(stream->obj == obj);
fprintf(stderr, "Debug stream '%s' aborted: %s\n", stream->name, msg);
stream_destroy(stream);
}
static const struct weston_debug_stream_v1_listener stream_listener = {
handle_stream_complete,
handle_stream_failure
};
static void
start_streams(struct debug_app *app)
{
struct debug_stream *stream;
wl_list_for_each(stream, &app->stream_list, link) {
if (!stream->should_bind)
continue;
stream->obj = weston_debug_v1_subscribe(app->debug_iface,
stream->name,
app->out_fd);
weston_debug_stream_v1_add_listener(stream->obj,
&stream_listener, stream);
}
}
static void
list_streams(struct debug_app *app)
{
struct debug_stream *stream;
fprintf(stderr, "Available debug streams:\n");
wl_list_for_each(stream, &app->stream_list, link) {
if (stream->should_bind && stream->desc) {
fprintf(stderr, " %s [will bind]\n", stream->name);
fprintf(stderr, " %s\n", stream->desc);
} else if (stream->should_bind) {
fprintf(stderr, " %s [wanted but not found]\n",
stream->name);
} else {
fprintf(stderr, " %s [will not bind]\n",
stream->name);
fprintf(stderr, " %s\n", stream->desc);
}
}
}
static int
setup_out_fd(const char *output, const char *outfd)
{
int fd = -1;
int flags;
assert(!(output && outfd));
if (output) {
if (strcmp(output, "-") == 0) {
fd = STDOUT_FILENO;
} else {
fd = open(output,
O_WRONLY | O_APPEND | O_CREAT, 0644);
if (fd < 0) {
fprintf(stderr,
"Error: opening file '%s' failed: %s\n",
output, strerror(errno));
}
return fd;
}
} else if (outfd) {
fd = atoi(outfd);
} else {
fd = STDOUT_FILENO;
}
flags = fcntl(fd, F_GETFL);
if (flags == -1) {
fprintf(stderr,
"Error: cannot use file descriptor %d: %s\n", fd,
strerror(errno));
return -1;
}
if ((flags & O_ACCMODE) != O_WRONLY &&
(flags & O_ACCMODE) != O_RDWR) {
fprintf(stderr,
"Error: file descriptor %d is not writable.\n", fd);
return -1;
}
return fd;
}
static void
print_help(void)
{
fprintf(stderr,
"Usage: weston-debug [options] [names]\n"
"Where options may be:\n"
" -h, --help\n"
" This help text, and exit with success.\n"
" -l, --list\n"
" Print a list of available debug streams to stderr.\n"
" -a, --all-streams\n"
" Bind to all available streams.\n"
" -o FILE, --output FILE\n"
" Direct output to file named FILE. Use - for stdout.\n"
" Stdout is the default. Mutually exclusive with -f.\n"
" -f FD, --outfd FD\n"
" Direct output to the file descriptor FD.\n"
" Stdout (1) is the default. Mutually exclusive with -o.\n"
"Names are whatever debug stream names the compositor supports.\n"
);
}
static int
parse_cmdline(struct debug_app *app, int argc, char **argv)
{
static const struct option opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "list", no_argument, NULL, 'l' },
{ "all-streams", no_argument, NULL, 'a' },
{ "output", required_argument, NULL, 'o' },
{ "outfd", required_argument, NULL, 'f' },
{ 0 }
};
static const char optstr[] = "hlao:f:";
int c;
bool failed = false;
while (1) {
c = getopt_long(argc, argv, optstr, opts, NULL);
if (c == -1)
break;
switch (c) {
case 'h':
app->opt.help = true;
break;
case 'l':
app->opt.list = true;
break;
case 'a':
app->opt.bind_all = true;
break;
case 'o':
free(app->opt.output);
app->opt.output = strdup(optarg);
break;
case 'f':
free(app->opt.outfd);
app->opt.outfd = strdup(optarg);
break;
case '?':
failed = true;
break;
default:
fprintf(stderr, "huh? getopt => %c (%d)\n", c, c);
failed = true;
}
}
if (failed)
return -1;
while (optind < argc) {
struct debug_stream *stream =
stream_alloc(app, argv[optind++], NULL);
stream->should_bind = true;
}
return 0;
}
int
main(int argc, char **argv)
{
struct debug_app app = {};
int ret = 0;
wl_list_init(&app.stream_list);
app.out_fd = -1;
if (parse_cmdline(&app, argc, argv) < 0) {
ret = 1;
goto out_parse;
}
if (app.opt.help) {
print_help();
goto out_parse;
}
if (!app.opt.list && !app.opt.bind_all &&
wl_list_empty(&app.stream_list)) {
fprintf(stderr, "Error: no options given.\n\n");
ret = 1;
print_help();
goto out_parse;
}
if (app.opt.bind_all && !wl_list_empty(&app.stream_list)) {
fprintf(stderr, "Error: --all and specific stream names cannot be used simultaneously.\n");
ret = 1;
goto out_parse;
}
if (app.opt.output && app.opt.outfd) {
fprintf(stderr, "Error: options --output and --outfd cannot be used simultaneously.\n");
ret = 1;
goto out_parse;
}
app.out_fd = setup_out_fd(app.opt.output, app.opt.outfd);
if (app.out_fd < 0) {
ret = 1;
goto out_parse;
}
app.dpy = wl_display_connect(NULL);
if (!app.dpy) {
fprintf(stderr, "Error: Could not connect to Wayland display: %s\n",
strerror(errno));
ret = 1;
goto out_parse;
}
app.registry = wl_display_get_registry(app.dpy);
wl_registry_add_listener(app.registry, &registry_listener, &app);
wl_display_roundtrip(app.dpy);
if (!app.debug_iface) {
ret = 1;
fprintf(stderr,
"The Wayland server does not support %s interface.\n",
weston_debug_v1_interface.name);
goto out_conn;
}
wl_display_roundtrip(app.dpy); /* for weston_debug_v1::advertise */
if (app.opt.list)
list_streams(&app);
start_streams(&app);
weston_debug_v1_destroy(app.debug_iface);
while (1) {
struct debug_stream *stream;
bool empty = true;
wl_list_for_each(stream, &app.stream_list, link) {
if (stream->obj) {
empty = false;
break;
}
}
if (empty)
break;
if (wl_display_dispatch(app.dpy) < 0) {
ret = 1;
break;
}
}
out_conn:
destroy_streams(&app);
/* Wait for server to close all files */
wl_display_roundtrip(app.dpy);
wl_registry_destroy(app.registry);
wl_display_disconnect(app.dpy);
out_parse:
if (app.out_fd != -1)
close(app.out_fd);
destroy_streams(&app);
free(app.opt.output);
free(app.opt.outfd);
return ret;
}

738
clients/weston-info.c Normal file
View file

@ -0,0 +1,738 @@
/*
* Copyright © 2012 Philipp Brüschweiler
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wayland-client.h>
#include "shared/helpers.h"
#include "shared/os-compatibility.h"
#include "shared/xalloc.h"
#include "shared/zalloc.h"
#include "presentation-time-client-protocol.h"
typedef void (*print_info_t)(void *info);
typedef void (*destroy_info_t)(void *info);
struct global_info {
struct wl_list link;
uint32_t id;
uint32_t version;
char *interface;
print_info_t print;
destroy_info_t destroy;
};
struct output_mode {
struct wl_list link;
uint32_t flags;
int32_t width, height;
int32_t refresh;
};
struct output_info {
struct global_info global;
struct wl_output *output;
int32_t version;
struct {
int32_t x, y;
int32_t scale;
int32_t physical_width, physical_height;
enum wl_output_subpixel subpixel;
enum wl_output_transform output_transform;
char *make;
char *model;
} geometry;
struct wl_list modes;
};
struct shm_format {
struct wl_list link;
uint32_t format;
};
struct shm_info {
struct global_info global;
struct wl_shm *shm;
struct wl_list formats;
};
struct seat_info {
struct global_info global;
struct wl_seat *seat;
struct weston_info *info;
uint32_t capabilities;
char *name;
int32_t repeat_rate;
int32_t repeat_delay;
};
struct presentation_info {
struct global_info global;
struct wp_presentation *presentation;
clockid_t clk_id;
};
struct weston_info {
struct wl_display *display;
struct wl_registry *registry;
struct wl_list infos;
bool roundtrip_needed;
};
static void
print_global_info(void *data)
{
struct global_info *global = data;
printf("interface: '%s', version: %u, name: %u\n",
global->interface, global->version, global->id);
}
static void
init_global_info(struct weston_info *info,
struct global_info *global, uint32_t id,
const char *interface, uint32_t version)
{
global->id = id;
global->version = version;
global->interface = xstrdup(interface);
wl_list_insert(info->infos.prev, &global->link);
}
static void
print_output_info(void *data)
{
struct output_info *output = data;
struct output_mode *mode;
const char *subpixel_orientation;
const char *transform;
print_global_info(data);
switch (output->geometry.subpixel) {
case WL_OUTPUT_SUBPIXEL_UNKNOWN:
subpixel_orientation = "unknown";
break;
case WL_OUTPUT_SUBPIXEL_NONE:
subpixel_orientation = "none";
break;
case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB:
subpixel_orientation = "horizontal rgb";
break;
case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR:
subpixel_orientation = "horizontal bgr";
break;
case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB:
subpixel_orientation = "vertical rgb";
break;
case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR:
subpixel_orientation = "vertical bgr";
break;
default:
fprintf(stderr, "unknown subpixel orientation %u\n",
output->geometry.subpixel);
subpixel_orientation = "unexpected value";
break;
}
switch (output->geometry.output_transform) {
case WL_OUTPUT_TRANSFORM_NORMAL:
transform = "normal";
break;
case WL_OUTPUT_TRANSFORM_90:
transform = "90°";
break;
case WL_OUTPUT_TRANSFORM_180:
transform = "180°";
break;
case WL_OUTPUT_TRANSFORM_270:
transform = "270°";
break;
case WL_OUTPUT_TRANSFORM_FLIPPED:
transform = "flipped";
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
transform = "flipped 90°";
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
transform = "flipped 180°";
break;
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
transform = "flipped 270°";
break;
default:
fprintf(stderr, "unknown output transform %u\n",
output->geometry.output_transform);
transform = "unexpected value";
break;
}
printf("\tx: %d, y: %d,",
output->geometry.x, output->geometry.y);
if (output->version >= 2)
printf(" scale: %d,", output->geometry.scale);
printf("\n");
printf("\tphysical_width: %d mm, physical_height: %d mm,\n",
output->geometry.physical_width,
output->geometry.physical_height);
printf("\tmake: '%s', model: '%s',\n",
output->geometry.make, output->geometry.model);
printf("\tsubpixel_orientation: %s, output_transform: %s,\n",
subpixel_orientation, transform);
wl_list_for_each(mode, &output->modes, link) {
printf("\tmode:\n");
printf("\t\twidth: %d px, height: %d px, refresh: %.3f Hz,\n",
mode->width, mode->height,
(float) mode->refresh / 1000);
printf("\t\tflags:");
if (mode->flags & WL_OUTPUT_MODE_CURRENT)
printf(" current");
if (mode->flags & WL_OUTPUT_MODE_PREFERRED)
printf(" preferred");
printf("\n");
}
}
static void
print_shm_info(void *data)
{
struct shm_info *shm = data;
struct shm_format *format;
print_global_info(data);
printf("\tformats:");
wl_list_for_each(format, &shm->formats, link)
switch (format->format) {
case WL_SHM_FORMAT_ARGB8888:
printf(" ARGB8888");
break;
case WL_SHM_FORMAT_XRGB8888:
printf(" XRGB8888");
break;
case WL_SHM_FORMAT_RGB565:
printf(" RGB565");
break;
default:
printf(" unknown(%08x)", format->format);
break;
}
printf("\n");
}
static void
print_seat_info(void *data)
{
struct seat_info *seat = data;
print_global_info(data);
printf("\tname: %s\n", seat->name);
printf("\tcapabilities:");
if (seat->capabilities & WL_SEAT_CAPABILITY_POINTER)
printf(" pointer");
if (seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD)
printf(" keyboard");
if (seat->capabilities & WL_SEAT_CAPABILITY_TOUCH)
printf(" touch");
printf("\n");
if (seat->repeat_rate > 0)
printf("\tkeyboard repeat rate: %d\n", seat->repeat_rate);
if (seat->repeat_delay > 0)
printf("\tkeyboard repeat delay: %d\n", seat->repeat_delay);
}
static void
keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
uint32_t format, int fd, uint32_t size)
{
}
static void
keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
uint32_t serial, struct wl_surface *surface,
struct wl_array *keys)
{
}
static void
keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
uint32_t serial, struct wl_surface *surface)
{
}
static void
keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t time, uint32_t key,
uint32_t state)
{
}
static void
keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t mods_depressed,
uint32_t mods_latched, uint32_t mods_locked,
uint32_t group)
{
}
static void
keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard,
int32_t rate, int32_t delay)
{
struct seat_info *seat = data;
seat->repeat_rate = rate;
seat->repeat_delay = delay;
}
static const struct wl_keyboard_listener keyboard_listener = {
keyboard_handle_keymap,
keyboard_handle_enter,
keyboard_handle_leave,
keyboard_handle_key,
keyboard_handle_modifiers,
keyboard_handle_repeat_info,
};
static void
seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
enum wl_seat_capability caps)
{
struct seat_info *seat = data;
seat->capabilities = caps;
/* we want listen for repeat_info from wl_keyboard, but only
* do so if the seat info is >= 4 and if we actually have a
* keyboard */
if (seat->global.version < 4)
return;
if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
struct wl_keyboard *keyboard;
keyboard = wl_seat_get_keyboard(seat->seat);
wl_keyboard_add_listener(keyboard, &keyboard_listener,
seat);
seat->info->roundtrip_needed = true;
}
}
static void
seat_handle_name(void *data, struct wl_seat *wl_seat,
const char *name)
{
struct seat_info *seat = data;
seat->name = xstrdup(name);
}
static const struct wl_seat_listener seat_listener = {
seat_handle_capabilities,
seat_handle_name,
};
static void
destroy_seat_info(void *data)
{
struct seat_info *seat = data;
wl_seat_destroy(seat->seat);
if (seat->name != NULL)
free(seat->name);
}
static void
add_seat_info(struct weston_info *info, uint32_t id, uint32_t version)
{
struct seat_info *seat = xzalloc(sizeof *seat);
/* required to set roundtrip_needed to true in capabilities
* handler */
seat->info = info;
init_global_info(info, &seat->global, id, "wl_seat", version);
seat->global.print = print_seat_info;
seat->global.destroy = destroy_seat_info;
seat->seat = wl_registry_bind(info->registry,
id, &wl_seat_interface, MIN(version, 4));
wl_seat_add_listener(seat->seat, &seat_listener, seat);
seat->repeat_rate = seat->repeat_delay = -1;
info->roundtrip_needed = true;
}
static void
shm_handle_format(void *data, struct wl_shm *wl_shm, uint32_t format)
{
struct shm_info *shm = data;
struct shm_format *shm_format = xzalloc(sizeof *shm_format);
wl_list_insert(&shm->formats, &shm_format->link);
shm_format->format = format;
}
static const struct wl_shm_listener shm_listener = {
shm_handle_format,
};
static void
destroy_shm_info(void *data)
{
struct shm_info *shm = data;
struct shm_format *format, *tmp;
wl_list_for_each_safe(format, tmp, &shm->formats, link) {
wl_list_remove(&format->link);
free(format);
}
wl_shm_destroy(shm->shm);
}
static void
add_shm_info(struct weston_info *info, uint32_t id, uint32_t version)
{
struct shm_info *shm = xzalloc(sizeof *shm);
init_global_info(info, &shm->global, id, "wl_shm", version);
shm->global.print = print_shm_info;
shm->global.destroy = destroy_shm_info;
wl_list_init(&shm->formats);
shm->shm = wl_registry_bind(info->registry,
id, &wl_shm_interface, 1);
wl_shm_add_listener(shm->shm, &shm_listener, shm);
info->roundtrip_needed = true;
}
static void
output_handle_geometry(void *data, struct wl_output *wl_output,
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 output_transform)
{
struct output_info *output = data;
output->geometry.x = x;
output->geometry.y = y;
output->geometry.physical_width = physical_width;
output->geometry.physical_height = physical_height;
output->geometry.subpixel = subpixel;
output->geometry.make = xstrdup(make);
output->geometry.model = xstrdup(model);
output->geometry.output_transform = output_transform;
}
static void
output_handle_mode(void *data, struct wl_output *wl_output,
uint32_t flags, int32_t width, int32_t height,
int32_t refresh)
{
struct output_info *output = data;
struct output_mode *mode = xmalloc(sizeof *mode);
mode->flags = flags;
mode->width = width;
mode->height = height;
mode->refresh = refresh;
wl_list_insert(output->modes.prev, &mode->link);
}
static void
output_handle_done(void *data, struct wl_output *wl_output)
{
/* don't bother waiting for this; there's no good reason a
* compositor will wait more than one roundtrip before sending
* these initial events. */
}
static void
output_handle_scale(void *data, struct wl_output *wl_output,
int32_t scale)
{
struct output_info *output = data;
output->geometry.scale = scale;
}
static const struct wl_output_listener output_listener = {
output_handle_geometry,
output_handle_mode,
output_handle_done,
output_handle_scale,
};
static void
destroy_output_info(void *data)
{
struct output_info *output = data;
struct output_mode *mode, *tmp;
wl_output_destroy(output->output);
if (output->geometry.make != NULL)
free(output->geometry.make);
if (output->geometry.model != NULL)
free(output->geometry.model);
wl_list_for_each_safe(mode, tmp, &output->modes, link) {
wl_list_remove(&mode->link);
free(mode);
}
}
static void
add_output_info(struct weston_info *info, uint32_t id, uint32_t version)
{
struct output_info *output = xzalloc(sizeof *output);
init_global_info(info, &output->global, id, "wl_output", version);
output->global.print = print_output_info;
output->global.destroy = destroy_output_info;
output->version = MIN(version, 2);
output->geometry.scale = 1;
wl_list_init(&output->modes);
output->output = wl_registry_bind(info->registry, id,
&wl_output_interface, output->version);
wl_output_add_listener(output->output, &output_listener,
output);
info->roundtrip_needed = true;
}
static void
destroy_presentation_info(void *info)
{
struct presentation_info *prinfo = info;
wp_presentation_destroy(prinfo->presentation);
}
static const char *
clock_name(clockid_t clk_id)
{
static const char *names[] = {
[CLOCK_REALTIME] = "CLOCK_REALTIME",
[CLOCK_MONOTONIC] = "CLOCK_MONOTONIC",
[CLOCK_MONOTONIC_RAW] = "CLOCK_MONOTONIC_RAW",
[CLOCK_REALTIME_COARSE] = "CLOCK_REALTIME_COARSE",
[CLOCK_MONOTONIC_COARSE] = "CLOCK_MONOTONIC_COARSE",
#ifdef CLOCK_BOOTTIME
[CLOCK_BOOTTIME] = "CLOCK_BOOTTIME",
#endif
};
if (clk_id < 0 || (unsigned)clk_id >= ARRAY_LENGTH(names))
return "unknown";
return names[clk_id];
}
static void
print_presentation_info(void *info)
{
struct presentation_info *prinfo = info;
print_global_info(info);
printf("\tpresentation clock id: %d (%s)\n",
prinfo->clk_id, clock_name(prinfo->clk_id));
}
static void
presentation_handle_clock_id(void *data, struct wp_presentation *presentation,
uint32_t clk_id)
{
struct presentation_info *prinfo = data;
prinfo->clk_id = clk_id;
}
static const struct wp_presentation_listener presentation_listener = {
presentation_handle_clock_id
};
static void
add_presentation_info(struct weston_info *info, uint32_t id, uint32_t version)
{
struct presentation_info *prinfo = xzalloc(sizeof *prinfo);
init_global_info(info, &prinfo->global, id,
wp_presentation_interface.name, version);
prinfo->global.print = print_presentation_info;
prinfo->global.destroy = destroy_presentation_info;
prinfo->clk_id = -1;
prinfo->presentation = wl_registry_bind(info->registry, id,
&wp_presentation_interface, 1);
wp_presentation_add_listener(prinfo->presentation,
&presentation_listener, prinfo);
info->roundtrip_needed = true;
}
static void
destroy_global_info(void *data)
{
}
static void
add_global_info(struct weston_info *info, uint32_t id,
const char *interface, uint32_t version)
{
struct global_info *global = xzalloc(sizeof *global);
init_global_info(info, global, id, interface, version);
global->print = print_global_info;
global->destroy = destroy_global_info;
}
static void
global_handler(void *data, struct wl_registry *registry, uint32_t id,
const char *interface, uint32_t version)
{
struct weston_info *info = data;
if (!strcmp(interface, "wl_seat"))
add_seat_info(info, id, version);
else if (!strcmp(interface, "wl_shm"))
add_shm_info(info, id, version);
else if (!strcmp(interface, "wl_output"))
add_output_info(info, id, version);
else if (!strcmp(interface, wp_presentation_interface.name))
add_presentation_info(info, id, version);
else
add_global_info(info, id, interface, version);
}
static void
global_remove_handler(void *data, struct wl_registry *registry, uint32_t name)
{
}
static const struct wl_registry_listener registry_listener = {
global_handler,
global_remove_handler
};
static void
print_infos(struct wl_list *infos)
{
struct global_info *info;
wl_list_for_each(info, infos, link)
info->print(info);
}
static void
destroy_info(void *data)
{
struct global_info *global = data;
global->destroy(data);
wl_list_remove(&global->link);
free(global->interface);
free(data);
}
static void
destroy_infos(struct wl_list *infos)
{
struct global_info *info, *tmp;
wl_list_for_each_safe(info, tmp, infos, link)
destroy_info(info);
}
int
main(int argc, char **argv)
{
struct weston_info info;
info.display = wl_display_connect(NULL);
if (!info.display) {
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
wl_list_init(&info.infos);
info.registry = wl_display_get_registry(info.display);
wl_registry_add_listener(info.registry, &registry_listener, &info);
do {
info.roundtrip_needed = false;
wl_display_roundtrip(info.display);
} while (info.roundtrip_needed);
print_infos(&info.infos);
destroy_infos(&info.infos);
wl_registry_destroy(info.registry);
wl_display_disconnect(info.display);
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -27,12 +27,11 @@
#include "config.h"
#include <stdint.h>
#include <time.h>
#include <xkbcommon/xkbcommon.h>
#include <wayland-client.h>
#include <cairo.h>
#include <libweston/config-parser.h>
#include <libweston/zalloc.h>
#include "shared/config-parser.h"
#include "shared/zalloc.h"
#include "shared/platform.h"
struct window;
@ -40,8 +39,6 @@ struct widget;
struct display;
struct input;
struct output;
struct tablet;
struct tablet_tool;
struct task {
void (*run)(struct task *task, uint32_t events);
@ -73,12 +70,12 @@ display_get_display(struct display *display);
int
display_has_subcompositor(struct display *display);
cairo_device_t *
display_get_cairo_device(struct display *display);
struct wl_compositor *
display_get_compositor(struct display *display);
struct wp_single_pixel_buffer_manager_v1 *
display_get_single_pixel_buffer_manager(struct display *display);
struct output *
display_get_output(struct display *display);
@ -116,11 +113,29 @@ display_set_output_configure_handler(struct display *display,
struct wl_data_source *
display_create_data_source(struct display *display);
#ifdef EGL_NO_DISPLAY
EGLDisplay
display_get_egl_display(struct display *d);
EGLConfig
display_get_argb_egl_config(struct display *d);
int
display_acquire_window_surface(struct display *display,
struct window *window,
EGLContext ctx);
void
display_release_window_surface(struct display *display,
struct window *window);
#endif
#define SURFACE_OPAQUE 0x01
#define SURFACE_SHM 0x02
#define SURFACE_HINT_RESIZE 0x10
#define SURFACE_HINT_RGB565 0x100
cairo_surface_t *
display_create_surface(struct display *display,
struct wl_surface *surface,
@ -270,57 +285,6 @@ typedef void (*widget_axis_handler_t)(struct widget *widget,
uint32_t axis,
wl_fixed_t value,
void *data);
typedef int (*widget_tablet_tool_motion_handler_t)(struct widget *widget,
struct tablet_tool *tool,
float x, float y,
void *data);
typedef void (*widget_tablet_tool_down_handler_t)(struct widget *widget,
struct tablet_tool *tool,
void *data);
typedef void (*widget_tablet_tool_up_handler_t)(struct widget *widget,
struct tablet_tool *tool,
void *data);
typedef void (*widget_tablet_tool_pressure_handler_t)(struct widget *widget,
struct tablet_tool *tool,
uint32_t pressure,
void *data);
typedef void (*widget_tablet_tool_distance_handler_t)(struct widget *widget,
struct tablet_tool *tool,
uint32_t distance,
void *data);
typedef void (*widget_tablet_tool_tilt_handler_t)(struct widget *widget,
struct tablet_tool *tool,
int32_t tilt_x, int32_t tilt_y,
void *data);
typedef void (*widget_tablet_tool_rotation_handler_t)(struct widget *widget,
struct tablet_tool *tool,
int32_t rotation,
void *data);
typedef void (*widget_tablet_tool_slider_handler_t)(struct widget *widget,
struct tablet_tool *tool,
int32_t slider,
void *data);
typedef void (*widget_tablet_tool_wheel_handler_t)(struct widget *widget,
struct tablet_tool *tool,
wl_fixed_t degrees,
int32_t clicks,
void *data);
typedef void (*widget_tablet_tool_proximity_in_handler_t)(struct widget *widget,
struct tablet_tool *tool,
struct tablet *tablet,
void *data);
typedef void (*widget_tablet_tool_proximity_out_handler_t)(struct widget *widget,
struct tablet_tool *tool,
void *data);
typedef void (*widget_tablet_tool_button_handler_t)(struct widget *widget,
struct tablet_tool *tool,
uint32_t button,
uint32_t state,
void *data);
typedef void (*widget_tablet_tool_frame_handler_t)(struct widget *widget,
struct tablet_tool *tool,
uint32_t time,
void *data);
typedef void (*widget_pointer_frame_handler_t)(struct widget *widget,
struct input *input,
@ -453,6 +417,7 @@ struct wl_subsurface *
widget_get_wl_subsurface(struct widget *widget);
enum window_buffer_type {
WINDOW_BUFFER_TYPE_EGL_WINDOW,
WINDOW_BUFFER_TYPE_SHM,
};
@ -533,48 +498,23 @@ void
window_set_locked_pointer_motion_handler(
struct window *window, window_locked_pointer_motion_handler_t handler);
void
window_set_shadow(struct window *window);
void
window_unset_shadow(struct window *window);
void
window_set_title(struct window *window, const char *title);
void
window_set_appid(struct window *window, const char *appid);
const char *
window_get_title(struct window *window);
const char *
window_get_appid(struct window *window);
void
window_set_text_cursor_position(struct window *window, int32_t x, int32_t y);
enum render_intent {
RENDER_INTENT_PERCEPTUAL,
RENDER_INTENT_RELATIVE,
RENDER_INTENT_RELATIVE_BPC,
RENDER_INTENT_SATURATION,
RENDER_INTENT_ABSOLUTE,
enum preferred_format {
WINDOW_PREFERRED_FORMAT_NONE,
WINDOW_PREFERRED_FORMAT_RGB565
};
struct render_intent_info {
enum render_intent intent;
uint32_t protocol_intent;
const char *desc;
};
const struct render_intent_info *
render_intent_info_from(enum render_intent intent);
bool
widget_set_image_description_icc(struct widget *widget, int icc_fd,
uint32_t length, uint32_t offset,
enum render_intent intent, char **err_msg);
void
window_set_preferred_format(struct window *window,
enum preferred_format format);
int
widget_set_tooltip(struct widget *parent, char *entry, float x, float y);
@ -590,8 +530,6 @@ widget_destroy(struct widget *widget);
void
widget_set_default_cursor(struct widget *widget, int cursor);
void
widget_set_default_tablet_cursor(struct widget *widget, int cursor);
void
widget_get_allocation(struct widget *widget, struct rectangle *allocation);
void
@ -613,9 +551,6 @@ widget_cairo_create(struct widget *widget);
struct wl_surface *
widget_get_wl_surface(struct widget *widget);
void
widget_surface_flush(struct widget *widget);
uint32_t
widget_get_last_time(struct widget *widget);
@ -667,49 +602,12 @@ widget_set_axis_handlers(struct widget *widget,
widget_axis_source_handler_t axis_source_handler,
widget_axis_stop_handler_t axis_stop_handler,
widget_axis_discrete_handler_t axis_discrete_handler);
void
widget_set_tablet_tool_axis_handlers(struct widget *widget,
widget_tablet_tool_motion_handler_t motion,
widget_tablet_tool_pressure_handler_t pressure,
widget_tablet_tool_distance_handler_t distance,
widget_tablet_tool_tilt_handler_t tilt,
widget_tablet_tool_rotation_handler_t rotation,
widget_tablet_tool_slider_handler_t slider,
widget_tablet_tool_wheel_handler_t wheel);
void
widget_set_tablet_tool_up_handler(struct widget *widget,
widget_tablet_tool_up_handler_t handler);
void
widget_set_tablet_tool_down_handler(struct widget *widget,
widget_tablet_tool_down_handler_t handler);
void
widget_set_tablet_tool_proximity_handlers(struct widget *widget,
widget_tablet_tool_proximity_in_handler_t in_handler,
widget_tablet_tool_proximity_out_handler_t out_handler);
void
widget_set_tablet_tool_button_handler(struct widget *widget,
widget_tablet_tool_button_handler_t handler);
void
widget_set_tablet_tool_frame_handler(struct widget *widget,
widget_tablet_tool_frame_handler_t handler);
void
window_inhibit_redraw(struct window *window);
void
window_uninhibit_redraw(struct window *window);
void
widget_schedule_redraw(struct widget *widget);
void
widget_set_use_cairo(struct widget *widget, int use_cairo);
/*
* Sets the viewport destination for the widget's surface
* return 0 on success and -1 on failure. Set width and height to
* -1 to reset the viewport.
*/
int
widget_set_viewport_destination(struct widget *widget, int width, int height);
struct widget *
window_frame_create(struct window *window, void *data);
@ -815,42 +713,4 @@ xkb_mod_mask_t
keysym_modifiers_get_mask(struct wl_array *modifiers_map,
const char *name);
uint32_t
tablet_tool_get_type(struct tablet_tool *tool);
uint64_t
tablet_tool_get_serial(struct tablet_tool *tool);
uint64_t
tablet_tool_get_hwid(struct tablet_tool *tool);
void
tablet_tool_set_cursor_image(struct tablet_tool *tool, int cursor);
struct toytimer;
typedef void (*toytimer_cb)(struct toytimer *);
struct toytimer {
struct display *display;
struct task tsk;
int fd;
toytimer_cb callback;
};
void
toytimer_init(struct toytimer *tt, clockid_t clock, struct display *display,
toytimer_cb callback);
void
toytimer_fini(struct toytimer *tt);
void
toytimer_arm(struct toytimer *tt, const struct itimerspec *its);
void
toytimer_arm_once_usec(struct toytimer *tt, uint32_t usec);
void
toytimer_disarm(struct toytimer *tt);
#endif

568
compositor/cms-colord.c Normal file
View file

@ -0,0 +1,568 @@
/*
* Copyright © 2013 Richard Hughes
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <colord.h>
#include "compositor.h"
#include "weston.h"
#include "cms-helper.h"
#include "shared/helpers.h"
struct cms_colord {
struct weston_compositor *ec;
CdClient *client;
GHashTable *devices; /* key = device-id, value = cms_output */
GHashTable *pnp_ids; /* key = pnp-id, value = vendor */
gchar *pnp_ids_data;
GMainLoop *loop;
GThread *thread;
GList *pending;
GMutex pending_mutex;
struct wl_event_source *source;
int readfd;
int writefd;
struct wl_listener destroy_listener;
struct wl_listener output_created_listener;
};
struct cms_output {
CdDevice *device;
guint32 backlight_value;
struct cms_colord *cms;
struct weston_color_profile *p;
struct weston_output *o;
struct wl_listener destroy_listener;
};
static gint
colord_idle_find_output_cb(gconstpointer a, gconstpointer b)
{
struct cms_output *ocms = (struct cms_output *) a;
struct weston_output *o = (struct weston_output *) b;
return ocms->o == o ? 0 : -1;
}
static void
colord_idle_cancel_for_output(struct cms_colord *cms, struct weston_output *o)
{
GList *l;
/* cancel and remove any helpers that match the output */
g_mutex_lock(&cms->pending_mutex);
l = g_list_find_custom (cms->pending, o, colord_idle_find_output_cb);
if (l) {
struct cms_output *ocms = l->data;
cms->pending = g_list_remove (cms->pending, ocms);
}
g_mutex_unlock(&cms->pending_mutex);
}
static bool
edid_value_valid(const char *str)
{
if (str == NULL)
return false;
if (str[0] == '\0')
return false;
if (strcmp(str, "unknown") == 0)
return false;
return true;
}
static gchar *
get_output_id(struct cms_colord *cms, struct weston_output *o)
{
const gchar *tmp;
GString *device_id;
/* see https://github.com/hughsie/colord/blob/master/doc/device-and-profile-naming-spec.txt
* for format and allowed values */
device_id = g_string_new("xrandr");
if (edid_value_valid(o->make)) {
tmp = g_hash_table_lookup(cms->pnp_ids, o->make);
if (tmp == NULL)
tmp = o->make;
g_string_append_printf(device_id, "-%s", tmp);
}
if (edid_value_valid(o->model))
g_string_append_printf(device_id, "-%s", o->model);
if (edid_value_valid(o->serial_number))
g_string_append_printf(device_id, "-%s", o->serial_number);
/* no EDID data, so use fallback */
if (strcmp(device_id->str, "xrandr") == 0)
g_string_append_printf(device_id, "-drm-%i", o->id);
return g_string_free(device_id, FALSE);
}
static void
update_device_with_profile_in_idle(struct cms_output *ocms)
{
gboolean signal_write = FALSE;
ssize_t rc;
struct cms_colord *cms = ocms->cms;
colord_idle_cancel_for_output(cms, ocms->o);
g_mutex_lock(&cms->pending_mutex);
if (cms->pending == NULL)
signal_write = TRUE;
cms->pending = g_list_prepend(cms->pending, ocms);
g_mutex_unlock(&cms->pending_mutex);
/* signal we've got updates to do */
if (signal_write) {
gchar tmp = '\0';
rc = write(cms->writefd, &tmp, 1);
if (rc == 0)
weston_log("colord: failed to write to pending fd\n");
}
}
static void
colord_update_output_from_device (struct cms_output *ocms)
{
CdProfile *profile;
const gchar *tmp;
gboolean ret;
GError *error = NULL;
gint percentage;
/* old profile is no longer valid */
weston_cms_destroy_profile(ocms->p);
ocms->p = NULL;
ret = cd_device_connect_sync(ocms->device, NULL, &error);
if (!ret) {
weston_log("colord: failed to connect to device %s: %s\n",
cd_device_get_object_path (ocms->device),
error->message);
g_error_free(error);
goto out;
}
profile = cd_device_get_default_profile(ocms->device);
if (!profile) {
weston_log("colord: no assigned color profile for %s\n",
cd_device_get_id (ocms->device));
goto out;
}
ret = cd_profile_connect_sync(profile, NULL, &error);
if (!ret) {
weston_log("colord: failed to connect to profile %s: %s\n",
cd_profile_get_object_path (profile),
error->message);
g_error_free(error);
goto out;
}
/* get the calibration brightness level (only set for some profiles) */
tmp = cd_profile_get_metadata_item(profile, CD_PROFILE_METADATA_SCREEN_BRIGHTNESS);
if (tmp != NULL) {
percentage = atoi(tmp);
if (percentage > 0 && percentage <= 100)
ocms->backlight_value = percentage * 255 / 100;
}
ocms->p = weston_cms_load_profile(cd_profile_get_filename(profile));
if (ocms->p == NULL) {
weston_log("colord: warning failed to load profile %s: %s\n",
cd_profile_get_object_path (profile),
error->message);
g_error_free(error);
goto out;
}
out:
update_device_with_profile_in_idle(ocms);
}
static void
colord_device_changed_cb(CdDevice *device, struct cms_output *ocms)
{
weston_log("colord: device %s changed, update output\n",
cd_device_get_object_path (ocms->device));
colord_update_output_from_device(ocms);
}
static void
colord_notifier_output_destroy(struct wl_listener *listener, void *data)
{
struct cms_output *ocms =
container_of(listener, struct cms_output, destroy_listener);
struct weston_output *o = (struct weston_output *) data;
struct cms_colord *cms = ocms->cms;
gchar *device_id;
device_id = get_output_id(cms, o);
g_hash_table_remove (cms->devices, device_id);
g_free (device_id);
}
static void
colord_output_created(struct cms_colord *cms, struct weston_output *o)
{
CdDevice *device;
const gchar *tmp;
gchar *device_id;
GError *error = NULL;
GHashTable *device_props;
struct cms_output *ocms;
/* create device */
device_id = get_output_id(cms, o);
weston_log("colord: output added %s\n", device_id);
device_props = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, g_free);
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_KIND),
g_strdup(cd_device_kind_to_string (CD_DEVICE_KIND_DISPLAY)));
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_FORMAT),
g_strdup("ColorModel.OutputMode.OutputResolution"));
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_COLORSPACE),
g_strdup(cd_colorspace_to_string(CD_COLORSPACE_RGB)));
if (edid_value_valid(o->make)) {
tmp = g_hash_table_lookup(cms->pnp_ids, o->make);
if (tmp == NULL)
tmp = o->make;
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_VENDOR),
g_strdup(tmp));
}
if (edid_value_valid(o->model)) {
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_MODEL),
g_strdup(o->model));
}
if (edid_value_valid(o->serial_number)) {
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_SERIAL),
g_strdup(o->serial_number));
}
if (o->connection_internal) {
g_hash_table_insert (device_props,
g_strdup (CD_DEVICE_PROPERTY_EMBEDDED),
NULL);
}
device = cd_client_create_device_sync(cms->client,
device_id,
CD_OBJECT_SCOPE_TEMP,
device_props,
NULL,
&error);
if (g_error_matches (error,
CD_CLIENT_ERROR,
CD_CLIENT_ERROR_ALREADY_EXISTS)) {
g_clear_error(&error);
device = cd_client_find_device_sync (cms->client,
device_id,
NULL,
&error);
}
if (!device) {
weston_log("colord: failed to create new or "
"find existing device: %s\n",
error->message);
g_error_free(error);
goto out;
}
/* create object and watch for the output to be destroyed */
ocms = g_slice_new0(struct cms_output);
ocms->cms = cms;
ocms->o = o;
ocms->device = g_object_ref(device);
ocms->destroy_listener.notify = colord_notifier_output_destroy;
wl_signal_add(&o->destroy_signal, &ocms->destroy_listener);
/* add to local cache */
g_hash_table_insert (cms->devices, g_strdup(device_id), ocms);
g_signal_connect (ocms->device, "changed",
G_CALLBACK (colord_device_changed_cb), ocms);
/* get profiles */
colord_update_output_from_device (ocms);
out:
g_hash_table_unref (device_props);
if (device)
g_object_unref (device);
g_free (device_id);
}
static void
colord_notifier_output_created(struct wl_listener *listener, void *data)
{
struct weston_output *o = (struct weston_output *) data;
struct cms_colord *cms =
container_of(listener, struct cms_colord, destroy_listener);
weston_log("colord: output %s created\n", o->name);
colord_output_created(cms, o);
}
static gpointer
colord_run_loop_thread(gpointer data)
{
struct cms_colord *cms = (struct cms_colord *) data;
struct weston_output *o;
/* coldplug outputs */
wl_list_for_each(o, &cms->ec->output_list, link) {
weston_log("colord: output %s coldplugged\n", o->name);
colord_output_created(cms, o);
}
g_main_loop_run(cms->loop);
return NULL;
}
static int
colord_dispatch_all_pending(int fd, uint32_t mask, void *data)
{
gchar tmp;
GList *l;
ssize_t rc;
struct cms_colord *cms = data;
struct cms_output *ocms;
weston_log("colord: dispatching events\n");
g_mutex_lock(&cms->pending_mutex);
for (l = cms->pending; l != NULL; l = l->next) {
ocms = l->data;
/* optionally set backlight to calibration value */
if (ocms->o->set_backlight && ocms->backlight_value != 0) {
weston_log("colord: profile calibration backlight to %i/255\n",
ocms->backlight_value);
ocms->o->set_backlight(ocms->o, ocms->backlight_value);
}
weston_cms_set_color_profile(ocms->o, ocms->p);
}
g_list_free (cms->pending);
cms->pending = NULL;
g_mutex_unlock(&cms->pending_mutex);
/* done */
rc = read(cms->readfd, &tmp, 1);
if (rc == 0)
weston_log("colord: failed to read from pending fd\n");
return 1;
}
static void
colord_load_pnp_ids(struct cms_colord *cms)
{
gboolean ret = FALSE;
gchar *tmp;
GError *error = NULL;
guint i;
const gchar *pnp_ids_fn[] = { "/usr/share/hwdata/pnp.ids",
"/usr/share/misc/pnp.ids",
NULL };
/* find and load file */
for (i = 0; pnp_ids_fn[i] != NULL; i++) {
if (!g_file_test(pnp_ids_fn[i], G_FILE_TEST_EXISTS))
continue;
ret = g_file_get_contents(pnp_ids_fn[i],
&cms->pnp_ids_data,
NULL,
&error);
if (!ret) {
weston_log("colord: failed to load %s: %s\n",
pnp_ids_fn[i], error->message);
g_error_free(error);
return;
}
break;
}
if (!ret) {
weston_log("colord: no pnp.ids found\n");
return;
}
/* parse fixed offsets into lines */
tmp = cms->pnp_ids_data;
for (i = 0; cms->pnp_ids_data[i] != '\0'; i++) {
if (cms->pnp_ids_data[i] != '\n')
continue;
cms->pnp_ids_data[i] = '\0';
if (tmp[0] && tmp[1] && tmp[2] && tmp[3] == '\t' && tmp[4]) {
tmp[3] = '\0';
g_hash_table_insert(cms->pnp_ids, tmp, tmp+4);
tmp = &cms->pnp_ids_data[i+1];
}
}
}
static void
colord_module_destroy(struct cms_colord *cms)
{
if (cms->loop) {
g_main_loop_quit(cms->loop);
g_main_loop_unref(cms->loop);
}
if (cms->thread)
g_thread_join(cms->thread);
/* cms->devices must be destroyed before other resources, as
* the other resources are needed during output cleanup in
* cms->devices unref.
*/
if (cms->devices)
g_hash_table_unref(cms->devices);
if (cms->client)
g_object_unref(cms->client);
if (cms->readfd)
close(cms->readfd);
if (cms->writefd)
close(cms->writefd);
g_free(cms->pnp_ids_data);
g_hash_table_unref(cms->pnp_ids);
free(cms);
}
static void
colord_notifier_destroy(struct wl_listener *listener, void *data)
{
struct cms_colord *cms =
container_of(listener, struct cms_colord, destroy_listener);
colord_module_destroy(cms);
}
static void
colord_cms_output_destroy(gpointer data)
{
struct cms_output *ocms = (struct cms_output *) data;
struct cms_colord *cms = ocms->cms;
struct weston_output *o = ocms->o;
gboolean ret;
gchar *device_id;
GError *error = NULL;
colord_idle_cancel_for_output(cms, o);
device_id = get_output_id(cms, o);
weston_log("colord: output unplugged %s\n", device_id);
wl_list_remove(&ocms->destroy_listener.link);
g_signal_handlers_disconnect_by_data(ocms->device, ocms);
ret = cd_client_delete_device_sync (cms->client,
ocms->device,
NULL,
&error);
if (!ret) {
weston_log("colord: failed to delete device: %s\n",
error->message);
g_error_free(error);
}
g_object_unref(ocms->device);
g_slice_free(struct cms_output, ocms);
g_free (device_id);
}
WL_EXPORT int
wet_module_init(struct weston_compositor *ec,
int *argc, char *argv[])
{
gboolean ret;
GError *error = NULL;
int fd[2];
struct cms_colord *cms;
struct wl_event_loop *loop;
weston_log("colord: initialized\n");
/* create local state object */
cms = zalloc(sizeof *cms);
if (cms == NULL)
return -1;
cms->ec = ec;
#if !GLIB_CHECK_VERSION(2,36,0)
g_type_init();
#endif
cms->client = cd_client_new();
ret = cd_client_connect_sync(cms->client, NULL, &error);
if (!ret) {
weston_log("colord: failed to contact daemon: %s\n", error->message);
g_error_free(error);
colord_module_destroy(cms);
return -1;
}
g_mutex_init(&cms->pending_mutex);
cms->devices = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, colord_cms_output_destroy);
/* destroy */
cms->destroy_listener.notify = colord_notifier_destroy;
wl_signal_add(&ec->destroy_signal, &cms->destroy_listener);
/* devices added */
cms->output_created_listener.notify = colord_notifier_output_created;
wl_signal_add(&ec->output_created_signal, &cms->output_created_listener);
/* add all the PNP IDs */
cms->pnp_ids = g_hash_table_new_full(g_str_hash,
g_str_equal,
NULL,
NULL);
colord_load_pnp_ids(cms);
/* setup a thread for the GLib callbacks */
cms->loop = g_main_loop_new(NULL, FALSE);
cms->thread = g_thread_new("colord CMS main loop",
colord_run_loop_thread, cms);
/* batch device<->profile updates */
if (pipe2(fd, O_CLOEXEC) == -1) {
colord_module_destroy(cms);
return -1;
}
cms->readfd = fd[0];
cms->writefd = fd[1];
loop = wl_display_get_event_loop(ec->wl_display);
cms->source = wl_event_loop_add_fd (loop,
cms->readfd,
WL_EVENT_READABLE,
colord_dispatch_all_pending,
cms);
if (!cms->source) {
colord_module_destroy(cms);
return -1;
}
return 0;
}

136
compositor/cms-helper.c Normal file
View file

@ -0,0 +1,136 @@
/*
* Copyright © 2013 Richard Hughes
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#ifdef HAVE_LCMS
#include <lcms2.h>
#endif
#include "compositor.h"
#include "cms-helper.h"
#ifdef HAVE_LCMS
static void
weston_cms_gamma_clear(struct weston_output *o)
{
int i;
uint16_t *red;
if (!o->set_gamma)
return;
red = calloc(o->gamma_size, sizeof(uint16_t));
for (i = 0; i < o->gamma_size; i++)
red[i] = (uint32_t) 0xffff * (uint32_t) i / (uint32_t) (o->gamma_size - 1);
o->set_gamma(o, o->gamma_size, red, red, red);
free(red);
}
#endif
void
weston_cms_set_color_profile(struct weston_output *o,
struct weston_color_profile *p)
{
#ifdef HAVE_LCMS
cmsFloat32Number in;
const cmsToneCurve **vcgt;
int i;
int size;
uint16_t *red = NULL;
uint16_t *green = NULL;
uint16_t *blue = NULL;
if (!o->set_gamma)
return;
if (!p) {
weston_cms_gamma_clear(o);
return;
}
weston_log("Using ICC profile %s\n", p->filename);
vcgt = cmsReadTag (p->lcms_handle, cmsSigVcgtTag);
if (vcgt == NULL || vcgt[0] == NULL) {
weston_cms_gamma_clear(o);
return;
}
size = o->gamma_size;
red = calloc(size, sizeof(uint16_t));
green = calloc(size, sizeof(uint16_t));
blue = calloc(size, sizeof(uint16_t));
for (i = 0; i < size; i++) {
in = (cmsFloat32Number) i / (cmsFloat32Number) (size - 1);
red[i] = cmsEvalToneCurveFloat(vcgt[0], in) * (double) 0xffff;
green[i] = cmsEvalToneCurveFloat(vcgt[1], in) * (double) 0xffff;
blue[i] = cmsEvalToneCurveFloat(vcgt[2], in) * (double) 0xffff;
}
o->set_gamma(o, size, red, green, blue);
free(red);
free(green);
free(blue);
#endif
}
void
weston_cms_destroy_profile(struct weston_color_profile *p)
{
if (!p)
return;
#ifdef HAVE_LCMS
cmsCloseProfile(p->lcms_handle);
#endif
free(p->filename);
free(p);
}
struct weston_color_profile *
weston_cms_create_profile(const char *filename,
void *lcms_profile)
{
struct weston_color_profile *p;
p = zalloc(sizeof(struct weston_color_profile));
p->filename = strdup(filename);
p->lcms_handle = lcms_profile;
return p;
}
struct weston_color_profile *
weston_cms_load_profile(const char *filename)
{
struct weston_color_profile *p = NULL;
#ifdef HAVE_LCMS
cmsHPROFILE lcms_profile;
lcms_profile = cmsOpenProfileFromFile(filename, "r");
if (lcms_profile)
p = weston_cms_create_profile(filename, lcms_profile);
#endif
return p;
}

75
compositor/cms-helper.h Normal file
View file

@ -0,0 +1,75 @@
/*
* Copyright © 2013 Richard Hughes
*
* 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_CMS_H_
#define _WESTON_CMS_H_
#include "config.h"
#include "compositor.h"
/* General overview on how to be a CMS plugin:
*
* First, some nomenclature:
*
* CMF: Color management framework, i.e. "Use foo.icc for device $bar"
* CMM: Color management module that converts pixel colors, which is
* usually lcms2 on any modern OS.
* CMS: Color management system that encompasses both a CMF and CMM.
* ICC: International Color Consortium, the people that define the
* binary encoding of a .icc file.
* VCGT: Video Card Gamma Tag. An Apple extension to the ICC specification
* that allows the calibration state to be stored in the ICC profile
* Output: Physical port with a display attached, e.g. LVDS1
*
* As a CMF is probably something you don't want or need on an embedded install
* these functions will not be called if the icc_profile key is set for a
* specific [output] section in weston.ini
*
* Most desktop environments want the CMF to decide what profile to use in
* different situations, so that displays can be profiled and also so that
* the ICC profiles can be changed at runtime depending on the task or ambient
* environment.
*
* The CMF can be selected using the 'modules' key in the [core] section.
*/
struct weston_color_profile {
char *filename;
void *lcms_handle;
};
void
weston_cms_set_color_profile(struct weston_output *o,
struct weston_color_profile *p);
struct weston_color_profile *
weston_cms_create_profile(const char *filename,
void *lcms_profile);
struct weston_color_profile *
weston_cms_load_profile(const char *filename);
void
weston_cms_destroy_profile(struct weston_color_profile *p);
#endif

119
compositor/cms-static.c Normal file
View file

@ -0,0 +1,119 @@
/*
* Copyright © 2013 Richard Hughes
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "compositor.h"
#include "cms-helper.h"
#include "shared/helpers.h"
#include "weston.h"
struct cms_static {
struct weston_compositor *ec;
struct wl_listener destroy_listener;
struct wl_listener output_created_listener;
};
static void
cms_output_created(struct cms_static *cms, struct weston_output *o)
{
struct weston_color_profile *p;
struct weston_config_section *s;
char *profile;
weston_log("cms-static: output %i [%s] created\n", o->id, o->name);
if (o->name == NULL)
return;
s = weston_config_get_section(wet_get_config(cms->ec),
"output", "name", o->name);
if (s == NULL)
return;
if (weston_config_section_get_string(s, "icc_profile", &profile, NULL) < 0)
return;
p = weston_cms_load_profile(profile);
if (p == NULL && strlen(profile) > 0) {
weston_log("cms-static: failed to load %s\n", profile);
} else {
weston_log("cms-static: loading %s for %s\n",
(p != NULL) ? profile : "identity LUT",
o->name);
weston_cms_set_color_profile(o, p);
}
}
static void
cms_notifier_output_created(struct wl_listener *listener, void *data)
{
struct weston_output *o = (struct weston_output *) data;
struct cms_static *cms =
container_of(listener, struct cms_static, output_created_listener);
cms_output_created(cms, o);
}
static void
cms_module_destroy(struct cms_static *cms)
{
free(cms);
}
static void
cms_notifier_destroy(struct wl_listener *listener, void *data)
{
struct cms_static *cms = container_of(listener, struct cms_static, destroy_listener);
cms_module_destroy(cms);
}
WL_EXPORT int
wet_module_init(struct weston_compositor *ec,
int *argc, char *argv[])
{
struct cms_static *cms;
struct weston_output *output;
weston_log("cms-static: initialized\n");
/* create local state object */
cms = zalloc(sizeof *cms);
if (cms == NULL)
return -1;
cms->ec = ec;
cms->destroy_listener.notify = cms_notifier_destroy;
wl_signal_add(&ec->destroy_signal, &cms->destroy_listener);
cms->output_created_listener.notify = cms_notifier_output_created;
wl_signal_add(&ec->output_created_signal, &cms->output_created_listener);
/* discover outputs */
wl_list_for_each(output, &ec->output_list, link)
cms_output_created(cms, output);
return 0;
}

1998
compositor/main.c Normal file

File diff suppressed because it is too large Load diff

1130
compositor/screen-share.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -32,8 +32,8 @@
#include "shared/helpers.h"
#include "shared/string-helpers.h"
#include <libweston/zalloc.h>
#include <libweston/libweston.h>
#include "shared/zalloc.h"
#include "compositor.h"
#include "weston.h"
struct systemd_notifier {
@ -127,12 +127,10 @@ wet_module_init(struct weston_compositor *compositor,
if (notifier == NULL)
return -1;
if (!weston_compositor_add_destroy_listener_once(compositor,
&notifier->compositor_destroy_listener,
weston_compositor_destroy_listener)) {
free(notifier);
return 0;
}
notifier->compositor_destroy_listener.notify =
weston_compositor_destroy_listener;
wl_signal_add(&compositor->destroy_signal,
&notifier->compositor_destroy_listener);
if (add_systemd_sockets(compositor) < 0)
return -1;

View file

@ -33,13 +33,11 @@
#include <unistd.h>
#include <time.h>
#include <libweston/libweston.h>
#include "compositor.h"
#include "weston.h"
#include "text-input-unstable-v1-server-protocol.h"
#include "input-method-unstable-v1-server-protocol.h"
#include "shared/helpers.h"
#include "shared/timespec-util.h"
#include "shared/xalloc.h"
struct text_input_manager;
struct input_method;
@ -66,7 +64,7 @@ struct text_input_manager {
struct wl_global *text_input_manager_global;
struct wl_listener destroy_listener;
struct text_input *current_text_input;
struct text_input *current_panel;
struct weston_compositor *ec;
};
@ -104,11 +102,10 @@ struct text_backend {
struct {
char *path;
bool overlay_keyboard;
struct wl_client *client;
unsigned deathcount;
struct timespec deathstamp;
uint32_t deathstamp;
} input_method;
struct wl_listener client_listener;
@ -142,22 +139,12 @@ deactivate_input_method(struct input_method *input_method)
input_method->input = NULL;
input_method->context = NULL;
/* text_input_manager::destroy_listener by compositor shutdown */
if (!text_input->manager) {
zwp_text_input_v1_send_leave(text_input->resource);
return;
}
if (wl_list_empty(&text_input->input_methods) &&
text_input->input_panel_visible &&
text_input->manager->current_text_input == text_input) {
text_input->input_panel_visible) {
wl_signal_emit(&ec->hide_input_panel_signal, ec);
text_input->input_panel_visible = false;
text_input->manager->current_panel = NULL;
}
if (text_input->manager->current_text_input == text_input)
text_input->manager->current_text_input = NULL;
zwp_text_input_v1_send_leave(text_input->resource);
}
@ -201,14 +188,10 @@ text_input_activate(struct wl_client *client,
{
struct text_input *text_input = wl_resource_get_user_data(resource);
struct weston_seat *weston_seat = wl_resource_get_user_data(seat);
struct input_method *input_method;
struct input_method *input_method = weston_seat->input_method;
struct weston_compositor *ec = text_input->ec;
struct text_input *current;
if (!weston_seat)
return;
input_method = weston_seat->input_method;
if (input_method->input == text_input)
return;
@ -223,11 +206,12 @@ text_input_activate(struct wl_client *client,
input_method_context_create(text_input, input_method);
current = text_input->manager->current_text_input;
current = text_input->manager->current_panel;
if (current && current != text_input) {
current->input_panel_visible = false;
wl_signal_emit(&ec->hide_input_panel_signal, ec);
text_input->manager->current_panel = NULL;
}
if (text_input->input_panel_visible) {
@ -235,8 +219,8 @@ text_input_activate(struct wl_client *client,
text_input->surface);
wl_signal_emit(&ec->update_input_panel_signal,
&text_input->cursor_rectangle);
text_input->manager->current_panel = text_input;
}
text_input->manager->current_text_input = text_input;
zwp_text_input_v1_send_enter(text_input->resource,
text_input->surface->resource);
@ -249,7 +233,7 @@ text_input_deactivate(struct wl_client *client,
{
struct weston_seat *weston_seat = wl_resource_get_user_data(seat);
if (weston_seat && weston_seat->input_method->input)
if (weston_seat->input_method->input)
deactivate_input_method(weston_seat->input_method);
}
@ -351,8 +335,7 @@ text_input_show_input_panel(struct wl_client *client,
text_input->input_panel_visible = true;
if (!wl_list_empty(&text_input->input_methods) &&
text_input == text_input->manager->current_text_input) {
if (!wl_list_empty(&text_input->input_methods)) {
wl_signal_emit(&ec->show_input_panel_signal,
text_input->surface);
wl_signal_emit(&ec->update_input_panel_signal,
@ -370,8 +353,10 @@ text_input_hide_input_panel(struct wl_client *client,
text_input->input_panel_visible = false;
if (!wl_list_empty(&text_input->input_methods) &&
text_input == text_input->manager->current_text_input)
text_input == text_input->manager->current_panel) {
text_input->manager->current_panel = NULL;
wl_signal_emit(&ec->hide_input_panel_signal, ec);
}
}
static void
@ -413,7 +398,9 @@ static void text_input_manager_create_text_input(struct wl_client *client,
wl_resource_get_user_data(resource);
struct text_input *text_input;
text_input = xzalloc(sizeof *text_input);
text_input = zalloc(sizeof *text_input);
if (text_input == NULL)
return;
text_input->resource =
wl_resource_create(client, &zwp_text_input_v1_interface, 1, id);
@ -458,11 +445,8 @@ text_input_manager_notifier_destroy(struct wl_listener *listener, void *data)
struct text_input_manager,
destroy_listener);
wl_list_remove(&text_input_manager->destroy_listener.link);
wl_global_destroy(text_input_manager->text_input_manager_global);
if (text_input_manager->current_text_input)
text_input_manager->current_text_input->manager = NULL;
free(text_input_manager);
}
@ -471,7 +455,9 @@ text_input_manager_create(struct weston_compositor *ec)
{
struct text_input_manager *text_input_manager;
text_input_manager = xzalloc(sizeof *text_input_manager);
text_input_manager = zalloc(sizeof *text_input_manager);
if (text_input_manager == NULL)
return;
text_input_manager->ec = ec;
@ -621,13 +607,11 @@ unbind_keyboard(struct wl_resource *resource)
static void
input_method_context_grab_key(struct weston_keyboard_grab *grab,
const struct timespec *time, uint32_t key,
uint32_t state_w)
uint32_t time, uint32_t key, uint32_t state_w)
{
struct weston_keyboard *keyboard = grab->keyboard;
struct wl_display *display;
uint32_t serial;
uint32_t msecs;
if (!keyboard->input_method_resource)
return;
@ -635,9 +619,8 @@ input_method_context_grab_key(struct weston_keyboard_grab *grab,
display = wl_client_get_display(
wl_resource_get_client(keyboard->input_method_resource));
serial = wl_display_next_serial(display);
msecs = timespec_to_msec(time);
wl_keyboard_send_key(keyboard->input_method_resource,
serial, msecs, key, state_w);
serial, time, key, state_w);
}
static void
@ -681,15 +664,14 @@ input_method_context_grab_keyboard(struct wl_client *client,
struct weston_seat *seat = context->input_method->seat;
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
if (!keyboard)
return;
cr = wl_resource_create(client, &wl_keyboard_interface, 1, id);
wl_resource_set_implementation(cr, NULL, context, unbind_keyboard);
context->keyboard = cr;
weston_keyboard_send_keymap(keyboard, cr);
wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
keyboard->xkb_info->keymap_fd,
keyboard->xkb_info->keymap_size);
if (keyboard->grab != &keyboard->default_grab) {
weston_keyboard_end_grab(keyboard);
@ -711,11 +693,8 @@ input_method_context_key(struct wl_client *client,
struct weston_seat *seat = context->input_method->seat;
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
struct weston_keyboard_grab *default_grab = &keyboard->default_grab;
struct timespec ts;
timespec_from_msec(&ts, time);
default_grab->interface->key(default_grab, &ts, key, state_w);
default_grab->interface->key(default_grab, time, key, state_w);
}
static void
@ -811,7 +790,9 @@ input_method_context_create(struct text_input *input,
if (!input_method->input_method_binding)
return;
context = xzalloc(sizeof *context);
context = zalloc(sizeof *context);
if (context == NULL)
return;
binding = input_method->input_method_binding;
context->resource =
@ -856,9 +837,6 @@ unbind_input_method(struct wl_resource *resource)
{
struct input_method *input_method = wl_resource_get_user_data(resource);
if (!input_method)
return;
input_method->input_method_binding = NULL;
input_method->context = NULL;
}
@ -906,12 +884,8 @@ input_method_notifier_destroy(struct wl_listener *listener, void *data)
if (input_method->input)
deactivate_input_method(input_method);
if (input_method->input_method_binding)
wl_resource_set_user_data(input_method->input_method_binding, NULL);
wl_global_destroy(input_method->input_method_global);
wl_list_remove(&input_method->destroy_listener.link);
input_method->seat->input_method = NULL;
free(input_method);
}
@ -952,19 +926,16 @@ input_method_init_seat(struct weston_seat *seat)
seat->input_method->focus_listener_initialized = true;
}
static void launch_input_method(void *data);
static void launch_input_method(struct text_backend *text_backend);
static void
respawn_input_method_process(struct text_backend *text_backend)
{
struct timespec time;
int64_t tdiff;
uint32_t time;
/* if input_method dies more than 5 times in 10 seconds, give up */
weston_compositor_get_time(&time);
tdiff = timespec_sub_to_msec(&time,
&text_backend->input_method.deathstamp);
if (tdiff > 10000) {
time = weston_compositor_get_time();
if (time - text_backend->input_method.deathstamp > 10000) {
text_backend->input_method.deathstamp = time;
text_backend->input_method.deathcount = 0;
}
@ -988,28 +959,21 @@ input_method_client_notifier(struct wl_listener *listener, void *data)
client_listener);
text_backend->input_method.client = NULL;
if (!text_backend->compositor->shutting_down)
respawn_input_method_process(text_backend);
respawn_input_method_process(text_backend);
}
static void
launch_input_method(void *data)
launch_input_method(struct text_backend *text_backend)
{
struct text_backend *text_backend = data;
if (!text_backend->input_method.path)
return;
if (strcmp(text_backend->input_method.path, "") == 0)
return;
if (text_backend->input_method.overlay_keyboard)
setenv("WESTON_KEYBOARD_SURFACE_TYPE", "overlay", 1);
text_backend->input_method.client =
wet_client_start(text_backend->compositor,
text_backend->input_method.path);
weston_client_start(text_backend->compositor,
text_backend->input_method.path);
if (!text_backend->input_method.client) {
weston_log("not able to start %s\n",
@ -1029,7 +993,9 @@ text_backend_seat_created(struct text_backend *text_backend,
struct input_method *input_method;
struct weston_compositor *ec = seat->compositor;
input_method = xzalloc(sizeof *input_method);
input_method = zalloc(sizeof *input_method);
if (input_method == NULL)
return;
input_method->seat = seat;
input_method->input = NULL;
@ -1065,24 +1031,23 @@ text_backend_configuration(struct text_backend *text_backend)
struct weston_config *config = wet_get_config(text_backend->compositor);
struct weston_config_section *section;
char *client;
int ret;
section = weston_config_get_section(config,
"input-method", NULL, NULL);
client = wet_get_libexec_path("weston-keyboard");
ret = asprintf(&client, "%s/weston-keyboard",
weston_config_get_libexec_dir());
if (ret < 0)
client = NULL;
weston_config_section_get_string(section, "path",
&text_backend->input_method.path,
client);
weston_config_section_get_bool(section, "overlay-keyboard",
&text_backend->input_method.overlay_keyboard,
false);
free(client);
}
WL_EXPORT void
text_backend_destroy(struct text_backend *text_backend)
{
wl_list_remove(&text_backend->seat_created_listener.link);
if (text_backend->input_method.client) {
/* disable respawn */
wl_list_remove(&text_backend->client_listener.link);
@ -1098,9 +1063,10 @@ text_backend_init(struct weston_compositor *ec)
{
struct text_backend *text_backend;
struct weston_seat *seat;
struct wl_event_loop *loop;
text_backend = xzalloc(sizeof(*text_backend));
text_backend = zalloc(sizeof(*text_backend));
if (text_backend == NULL)
return NULL;
text_backend->compositor = ec;
@ -1114,8 +1080,7 @@ text_backend_init(struct weston_compositor *ec)
text_input_manager_create(ec);
loop = wl_display_get_event_loop(ec->wl_display);
wl_event_loop_add_idle(loop, launch_input_method, text_backend);
launch_input_method(text_backend);
return text_backend;
}

View file

@ -0,0 +1,192 @@
/*
* 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 <stdint.h>
#include <linux/input.h>
#include "compositor.h"
#include "weston.h"
#include "weston-screenshooter-server-protocol.h"
#include "shared/helpers.h"
struct screenshooter {
struct weston_compositor *ec;
struct wl_global *global;
struct wl_client *client;
struct weston_process process;
struct wl_listener destroy_listener;
struct weston_recorder *recorder;
};
static void
screenshooter_done(void *data, enum weston_screenshooter_outcome outcome)
{
struct wl_resource *resource = data;
switch (outcome) {
case WESTON_SCREENSHOOTER_SUCCESS:
weston_screenshooter_send_done(resource);
break;
case WESTON_SCREENSHOOTER_NO_MEMORY:
wl_resource_post_no_memory(resource);
break;
default:
break;
}
}
static void
screenshooter_shoot(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *output_resource,
struct wl_resource *buffer_resource)
{
struct weston_output *output =
wl_resource_get_user_data(output_resource);
struct weston_buffer *buffer =
weston_buffer_from_resource(buffer_resource);
if (buffer == NULL) {
wl_resource_post_no_memory(resource);
return;
}
weston_screenshooter_shoot(output, buffer, screenshooter_done, resource);
}
struct weston_screenshooter_interface screenshooter_implementation = {
screenshooter_shoot
};
static void
bind_shooter(struct wl_client *client,
void *data, uint32_t version, uint32_t id)
{
struct screenshooter *shooter = data;
struct wl_resource *resource;
resource = wl_resource_create(client,
&weston_screenshooter_interface, 1, id);
if (client != shooter->client) {
wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
"screenshooter failed: permission denied");
return;
}
wl_resource_set_implementation(resource, &screenshooter_implementation,
data, NULL);
}
static void
screenshooter_sigchld(struct weston_process *process, int status)
{
struct screenshooter *shooter =
container_of(process, struct screenshooter, process);
shooter->client = NULL;
}
static void
screenshooter_binding(struct weston_keyboard *keyboard, uint32_t time,
uint32_t key, void *data)
{
struct screenshooter *shooter = data;
char *screenshooter_exe;
int ret;
ret = asprintf(&screenshooter_exe, "%s/%s",
weston_config_get_libexec_dir(),
"/weston-screenshooter");
if (ret < 0) {
weston_log("Could not construct screenshooter path.\n");
return;
}
if (!shooter->client)
shooter->client = weston_client_launch(shooter->ec,
&shooter->process,
screenshooter_exe, screenshooter_sigchld);
free(screenshooter_exe);
}
static void
recorder_binding(struct weston_keyboard *keyboard, uint32_t time,
uint32_t key, void *data)
{
struct weston_compositor *ec = keyboard->seat->compositor;
struct weston_output *output;
struct screenshooter *shooter = data;
struct weston_recorder *recorder = shooter->recorder;;
static const char filename[] = "capture.wcap";
if (recorder) {
weston_recorder_stop(recorder);
shooter->recorder = NULL;
} else {
if (keyboard->focus && keyboard->focus->output)
output = keyboard->focus->output;
else
output = container_of(ec->output_list.next,
struct weston_output, link);
shooter->recorder = weston_recorder_start(output, filename);
}
}
static void
screenshooter_destroy(struct wl_listener *listener, void *data)
{
struct screenshooter *shooter =
container_of(listener, struct screenshooter, destroy_listener);
wl_global_destroy(shooter->global);
free(shooter);
}
WL_EXPORT void
screenshooter_create(struct weston_compositor *ec)
{
struct screenshooter *shooter;
shooter = zalloc(sizeof *shooter);
if (shooter == NULL)
return;
shooter->ec = ec;
shooter->global = wl_global_create(ec->wl_display,
&weston_screenshooter_interface, 1,
shooter, bind_shooter);
weston_compositor_add_key_binding(ec, KEY_S, MODIFIER_SUPER,
screenshooter_binding, shooter);
weston_compositor_add_key_binding(ec, KEY_R, MODIFIER_SUPER,
recorder_binding, shooter);
shooter->destroy_listener.notify = screenshooter_destroy;
wl_signal_add(&ec->destroy_signal, &shooter->destroy_listener);
}

View file

@ -30,44 +30,39 @@
extern "C" {
#endif
#include <libweston/libweston.h>
#include <libweston/config-parser.h>
#include <compositor.h>
void
screenshooter_create(struct weston_compositor *ec);
struct wet_process;
typedef void (*wet_process_cleanup_func_t)(struct wet_process *process,
int status,
void *data);
struct weston_process;
typedef void (*weston_process_cleanup_func_t)(struct weston_process *process,
int status);
struct wet_process {
struct weston_process {
pid_t pid;
char *path;
wet_process_cleanup_func_t cleanup;
void *cleanup_data;
weston_process_cleanup_func_t cleanup;
struct wl_list link;
};
struct custom_env;
struct wet_process *
wet_client_launch(struct weston_compositor *compositor,
struct custom_env *custom_env,
int *fds_no_cloexec,
size_t num_fds_no_cloexec,
wet_process_cleanup_func_t cleanup,
void *cleanup_data);
struct wl_client *
weston_client_launch(struct weston_compositor *compositor,
struct weston_process *proc,
const char *path,
weston_process_cleanup_func_t cleanup);
struct wl_client *
wet_client_start(struct weston_compositor *compositor, const char *path);
weston_client_start(struct weston_compositor *compositor, const char *path);
void
wet_process_destroy(struct wet_process *process, int status, bool call_cleanup);
weston_watch_process(struct weston_process *process);
struct weston_config *
wet_get_config(struct weston_compositor *compositor);
void *
wet_load_module_entrypoint(const char *name, const char *entrypoint);
int
wet_shell_init(struct weston_compositor *ec,
int *argc, char *argv[]);
@ -82,18 +77,9 @@ int
module_init(struct weston_compositor *compositor,
int *argc, char *argv[]);
char *
wet_get_libexec_path(const char *name);
char *
wet_get_bindir_path(const char *name);
void *
int
wet_load_xwayland(struct weston_compositor *comp);
void
wet_xwayland_destroy(struct weston_compositor *comp, void *data);
struct text_backend;
struct text_backend *
@ -102,15 +88,6 @@ text_backend_init(struct weston_compositor *ec);
void
text_backend_destroy(struct text_backend *text_backend);
/*
* Return value from wet_main() when
* weston_testsuite_quirks::required_capabilities are not met.
*/
#define WET_MAIN_RET_MISSING_CAPS 77
int
wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data);
#ifdef __cplusplus
}
#endif

12
compositor/weston.pc.in Normal file
View file

@ -0,0 +1,12 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
libexecdir=@libexecdir@
pkglibexecdir=${libexecdir}/@PACKAGE@
Name: Weston Plugin API
Description: Header files for Weston plugin development
Version: @WESTON_VERSION@
Requires.private: libweston-@LIBWESTON_MAJOR@
Cflags: -I${includedir}/weston

209
compositor/xwayland.c Normal file
View file

@ -0,0 +1,209 @@
/*
* Copyright © 2011 Intel Corporation
* Copyright © 2016 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.
*/
#include "config.h"
#include <signal.h>
#include <sys/socket.h>
#include "compositor.h"
#include "compositor/weston.h"
#include "xwayland/xwayland-api.h"
#include "shared/helpers.h"
struct wet_xwayland {
struct weston_compositor *compositor;
const struct weston_xwayland_api *api;
struct weston_xwayland *xwayland;
struct wl_event_source *sigusr1_source;
struct wl_client *client;
int wm_fd;
struct weston_process process;
};
static int
handle_sigusr1(int signal_number, void *data)
{
struct wet_xwayland *wxw = data;
/* We'd be safer if we actually had the struct
* signalfd_siginfo from the signalfd data and could verify
* this came from Xwayland.*/
wxw->api->xserver_loaded(wxw->xwayland, wxw->client, wxw->wm_fd);
wl_event_source_remove(wxw->sigusr1_source);
return 1;
}
static pid_t
spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd)
{
struct wet_xwayland *wxw = user_data;
pid_t pid;
char s[8], abstract_fd_str[8], unix_fd_str[8], wm_fd_str[8];
int sv[2], wm[2], fd;
char *xserver = NULL;
struct weston_config *config = wet_get_config(wxw->compositor);
struct weston_config_section *section;
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
weston_log("wl connection socketpair failed\n");
return 1;
}
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wm) < 0) {
weston_log("X wm connection socketpair failed\n");
return 1;
}
pid = fork();
switch (pid) {
case 0:
/* SOCK_CLOEXEC closes both ends, so we need to unset
* the flag on the client fd. */
fd = dup(sv[1]);
if (fd < 0)
goto fail;
snprintf(s, sizeof s, "%d", fd);
setenv("WAYLAND_SOCKET", s, 1);
fd = dup(abstract_fd);
if (fd < 0)
goto fail;
snprintf(abstract_fd_str, sizeof abstract_fd_str, "%d", fd);
fd = dup(unix_fd);
if (fd < 0)
goto fail;
snprintf(unix_fd_str, sizeof unix_fd_str, "%d", fd);
fd = dup(wm[1]);
if (fd < 0)
goto fail;
snprintf(wm_fd_str, sizeof wm_fd_str, "%d", fd);
section = weston_config_get_section(config,
"xwayland", NULL, NULL);
weston_config_section_get_string(section, "path",
&xserver, XSERVER_PATH);
/* Ignore SIGUSR1 in the child, which will make the X
* server send SIGUSR1 to the parent (weston) when
* it's done with initialization. During
* initialization the X server will round trip and
* block on the wayland compositor, so avoid making
* blocking requests (like xcb_connect_to_fd) until
* it's done with that. */
signal(SIGUSR1, SIG_IGN);
if (execl(xserver,
xserver,
display,
"-rootless",
"-listen", abstract_fd_str,
"-listen", unix_fd_str,
"-wm", wm_fd_str,
"-terminate",
NULL) < 0)
weston_log("exec of '%s %s -rootless "
"-listen %s -listen %s -wm %s "
"-terminate' failed: %m\n",
xserver, display,
abstract_fd_str, unix_fd_str, wm_fd_str);
fail:
_exit(EXIT_FAILURE);
default:
close(sv[1]);
wxw->client = wl_client_create(wxw->compositor->wl_display, sv[0]);
close(wm[1]);
wxw->wm_fd = wm[0];
wxw->process.pid = pid;
weston_watch_process(&wxw->process);
break;
case -1:
weston_log("Failed to fork to spawn xserver process\n");
break;
}
return pid;
}
static void
xserver_cleanup(struct weston_process *process, int status)
{
struct wet_xwayland *wxw =
container_of(process, struct wet_xwayland, process);
struct wl_event_loop *loop =
wl_display_get_event_loop(wxw->compositor->wl_display);
wxw->api->xserver_exited(wxw->xwayland, status);
wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1,
handle_sigusr1, wxw);
wxw->client = NULL;
}
int
wet_load_xwayland(struct weston_compositor *comp)
{
const struct weston_xwayland_api *api;
struct weston_xwayland *xwayland;
struct wet_xwayland *wxw;
struct wl_event_loop *loop;
if (weston_compositor_load_xwayland(comp) < 0)
return -1;
api = weston_xwayland_get_api(comp);
if (!api) {
weston_log("Failed to get the xwayland module API.\n");
return -1;
}
xwayland = api->get(comp);
if (!xwayland) {
weston_log("Failed to get the xwayland object.\n");
return -1;
}
wxw = zalloc(sizeof *wxw);
if (!wxw)
return -1;
wxw->compositor = comp;
wxw->api = api;
wxw->xwayland = xwayland;
wxw->process.cleanup = xserver_cleanup;
if (api->listen(xwayland, wxw, spawn_xserver) < 0)
return -1;
loop = wl_display_get_event_loop(comp->wl_display);
wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1,
handle_sigusr1, wxw);
return 0;
}

727
configure.ac Normal file
View file

@ -0,0 +1,727 @@
m4_define([weston_major_version], [2])
m4_define([weston_minor_version], [0])
m4_define([weston_micro_version], [0])
m4_define([weston_version],
[weston_major_version.weston_minor_version.weston_micro_version])
m4_define([libweston_major_version], [2])
m4_define([libweston_minor_version], [0])
m4_define([libweston_patch_version], [0])
AC_PREREQ([2.64])
AC_INIT([weston],
[weston_version],
[https://bugs.freedesktop.org/enter_bug.cgi?product=Wayland&component=weston&version=weston_version],
[weston],
[http://wayland.freedesktop.org])
WAYLAND_PREREQ_VERSION="1.12.0"
AC_SUBST([WESTON_VERSION_MAJOR], [weston_major_version])
AC_SUBST([WESTON_VERSION_MINOR], [weston_minor_version])
AC_SUBST([WESTON_VERSION_MICRO], [weston_micro_version])
AC_SUBST([WESTON_VERSION], [weston_version])
AC_SUBST([LIBWESTON_MAJOR], [libweston_major_version])
# We use minor as current and age since on ABI/API break/removal we bump major
# so minor will be reset to 0.
m4_define([lt_current], [libweston_minor_version])
m4_define([lt_revision], [libweston_patch_version])
m4_define([lt_age], [libweston_minor_version])
AC_SUBST([LT_VERSION_INFO], [lt_current:lt_revision:lt_age])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
AC_USE_SYSTEM_EXTENSIONS
AC_SYS_LARGEFILE
AM_INIT_AUTOMAKE([1.11 parallel-tests foreign no-dist-gzip dist-xz color-tests subdir-objects])
AM_SILENT_RULES([yes])
# Check Weston and libweston version consistency
m4_if(m4_cmp(weston_micro_version, [90]), [-1],
[
dnl micro < 90
dnl A final or stable release, not a pre-release:
dnl Weston and libweston versions must match.
m4_if(weston_version, libweston_major_version[.]libweston_minor_version[.]libweston_patch_version,
[],
[AC_MSG_ERROR([Weston and libweston version mismatch for a final release])])
],
[
dnl A pre-release:
dnl libweston must be equal or greater than weston.
m4_case(m4_list_cmp([weston_major_version, weston_minor_version, weston_micro_version],
[libweston_major_version, libweston_minor_version, libweston_patch_version]),
[-1], [
dnl weston < libweston
dnl libweston must be weston_major+1.0.0
m4_if(m4_eval(weston_major_version+1)[.0.0],
libweston_major_version[.]libweston_minor_version[.]libweston_patch_version,
[],
[AC_MSG_ERROR([libweston version is greater but not (weston_major+1).0.0])])
],
[0], [
dnl weston == libweston, all ok
],
[1], [
dnl weston > libweston, wrong
AC_MSG_ERROR([Weston version is greater than libweston.])
])
])
# Check for programs
AC_PROG_CC
AC_PROG_SED
# Initialize libtool
LT_PREREQ([2.2])
LT_INIT([disable-static])
AC_ARG_VAR([WESTON_NATIVE_BACKEND],
[Set the native backend to use, if Weston is not running under Wayland nor X11. @<:@default=drm-backend.so@:>@])
AC_ARG_VAR([WESTON_SHELL_CLIENT],
[Set the default desktop shell client to load if none is specified in weston.ini. @<:@default=weston-desktop-shell@:>@])
PKG_PROG_PKG_CONFIG()
WESTON_SEARCH_LIBS([DLOPEN], [dl], [dlopen])
# In old glibc versions (< 2.17) clock_gettime() and clock_getres() are in librt
WESTON_SEARCH_LIBS([CLOCK_GETTIME], [rt], [clock_gettime])
WESTON_SEARCH_LIBS([CLOCK_GETRES], [rt], [clock_getres])
AC_CHECK_DECL(SFD_CLOEXEC,[],
[AC_MSG_ERROR("SFD_CLOEXEC is needed to compile weston")],
[[#include <sys/signalfd.h>]])
AC_CHECK_DECL(TFD_CLOEXEC,[],
[AC_MSG_ERROR("TFD_CLOEXEC is needed to compile weston")],
[[#include <sys/timerfd.h>]])
AC_CHECK_DECL(CLOCK_MONOTONIC,[],
[AC_MSG_ERROR("CLOCK_MONOTONIC is needed to compile weston")],
[[#include <time.h>]])
AC_CHECK_HEADERS([execinfo.h])
AC_CHECK_FUNCS([mkostemp strchrnul initgroups posix_fallocate])
COMPOSITOR_MODULES="wayland-server >= $WAYLAND_PREREQ_VERSION pixman-1 >= 0.25.2"
AC_CONFIG_FILES([doc/doxygen/tools.doxygen doc/doxygen/tooldev.doxygen])
AC_ARG_ENABLE(devdocs,
AS_HELP_STRING([--disable-devdocs],
[do not enable building of developer documentation]),,
enable_devdocs=auto)
if test "x$enable_devdocs" != "xno"; then
AC_CHECK_PROGS([DOXYGEN], [doxygen])
if test "x$DOXYGEN" = "x" -a "x$enable_devdocs" = "xyes"; then
AC_MSG_ERROR([Developer documentation explicitly requested, but Doxygen couldn't be found])
fi
if test "x$DOXYGEN" != "x"; then
enable_devdocs=yes
else
enable_devdocs=no
fi
fi
AM_CONDITIONAL(ENABLE_DEVDOCS, test "x$enable_devdocs" = "xyes")
AC_ARG_ENABLE(egl, [ --disable-egl],,
enable_egl=yes)
AM_CONDITIONAL(ENABLE_EGL, test x$enable_egl = xyes)
if test x$enable_egl = xyes; then
AC_DEFINE([ENABLE_EGL], [1], [Build Weston with EGL support])
PKG_CHECK_MODULES(EGL, [egl glesv2])
PKG_CHECK_MODULES([EGL_TESTS], [egl glesv2 wayland-client wayland-egl])
PKG_CHECK_MODULES([GL_RENDERER], [libdrm])
fi
AC_ARG_ENABLE(xkbcommon,
AS_HELP_STRING([--disable-xkbcommon], [Disable libxkbcommon
support: This is only useful in environments
where you do not have a hardware keyboard. If
libxkbcommon support is disabled clients will not
be sent a keymap and must know how to interpret
the keycode sent for any key event.]),,
enable_xkbcommon=yes)
if test x$enable_xkbcommon = xyes; then
AC_DEFINE(ENABLE_XKBCOMMON, [1], [Build Weston with libxkbcommon support])
COMPOSITOR_MODULES="$COMPOSITOR_MODULES xkbcommon >= 0.3.0"
PKG_CHECK_MODULES(XKBCOMMON_COMPOSE, [xkbcommon >= 0.5.0],
[AC_DEFINE(HAVE_XKBCOMMON_COMPOSE, 1,
[Define if xkbcommon is 0.5.0 or newer])],
true)
fi
AC_ARG_ENABLE(setuid-install, [ --enable-setuid-install],,
enable_setuid_install=yes)
AM_CONDITIONAL(ENABLE_SETUID_INSTALL, test x$enable_setuid_install = xyes)
AC_ARG_ENABLE(xwayland, [ --enable-xwayland],,
enable_xwayland=yes)
AC_ARG_ENABLE(xwayland-test, [ --enable-xwayland-test],,
enable_xwayland_test=yes)
AM_CONDITIONAL(ENABLE_XWAYLAND, test x$enable_xwayland = xyes)
AM_CONDITIONAL(ENABLE_XWAYLAND_TEST, test x$enable_xwayland = xyes -a x$enable_xwayland_test = xyes)
if test x$enable_xwayland = xyes; then
PKG_CHECK_MODULES([XWAYLAND], xcb xcb-xfixes xcb-composite xcursor cairo-xcb)
AC_DEFINE([BUILD_XWAYLAND], [1], [Build the X server launcher])
AC_ARG_WITH(xserver-path, AS_HELP_STRING([--with-xserver-path=PATH],
[Path to X server]), [XSERVER_PATH="$withval"],
[XSERVER_PATH="/usr/bin/Xwayland"])
AC_SUBST([XSERVER_PATH])
if test x$enable_xwayland_test = xyes; then
PKG_CHECK_MODULES([XWAYLAND_TEST], x11)
fi
fi
PKG_CHECK_MODULES(LIBDRM, [libdrm],
[AC_DEFINE(HAVE_LIBDRM, 1, [Define if libdrm is available]) have_libdrm=yes], have_libdrm=no)
AC_ARG_ENABLE(x11-compositor, [ --enable-x11-compositor],,
enable_x11_compositor=yes)
AM_CONDITIONAL(ENABLE_X11_COMPOSITOR, test x$enable_x11_compositor = xyes)
have_xcb_xkb=no
if test x$enable_x11_compositor = xyes; then
PKG_CHECK_MODULES([XCB], xcb >= 1.8)
X11_COMPOSITOR_MODULES="x11 x11-xcb xcb-shm"
PKG_CHECK_MODULES(X11_COMPOSITOR_XKB, [xcb-xkb >= 1.9],
[have_xcb_xkb="yes"], [have_xcb_xkb="no"])
if test "x$have_xcb_xkb" = xyes; then
X11_COMPOSITOR_MODULES="$X11_COMPOSITOR_MODULES xcb-xkb"
AC_DEFINE([HAVE_XCB_XKB], [1], [libxcb supports XKB protocol])
fi
PKG_CHECK_MODULES(X11_COMPOSITOR, [$X11_COMPOSITOR_MODULES])
AC_DEFINE([BUILD_X11_COMPOSITOR], [1], [Build the X11 compositor])
fi
AC_ARG_ENABLE(drm-compositor, [ --enable-drm-compositor],,
enable_drm_compositor=yes)
AM_CONDITIONAL(ENABLE_DRM_COMPOSITOR, test x$enable_drm_compositor = xyes)
if test x$enable_drm_compositor = xyes; then
AC_DEFINE([BUILD_DRM_COMPOSITOR], [1], [Build the DRM compositor])
PKG_CHECK_MODULES(DRM_COMPOSITOR, [libudev >= 136 libdrm >= 2.4.30 gbm mtdev >= 1.1.0])
PKG_CHECK_MODULES(DRM_COMPOSITOR_GBM, [gbm >= 10.2],
[AC_DEFINE([HAVE_GBM_FD_IMPORT], 1, [gbm supports dmabuf import])],
[AC_MSG_WARN([gbm does not support dmabuf import, will omit that capability])])
fi
PKG_CHECK_MODULES(LIBINPUT_BACKEND, [libinput >= 0.8.0])
PKG_CHECK_MODULES(COMPOSITOR, [$COMPOSITOR_MODULES])
PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.7],
[ac_wayland_protocols_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`])
AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, $ac_wayland_protocols_pkgdatadir)
AC_ARG_ENABLE(wayland-compositor, [ --enable-wayland-compositor],,
enable_wayland_compositor=yes)
AM_CONDITIONAL(ENABLE_WAYLAND_COMPOSITOR,
test x$enable_wayland_compositor = xyes)
if test x$enable_wayland_compositor = xyes; then
AC_DEFINE([BUILD_WAYLAND_COMPOSITOR], [1],
[Build the Wayland (nested) compositor])
PKG_CHECK_MODULES(WAYLAND_COMPOSITOR, [wayland-client >= $WAYLAND_PREREQ_VERSION wayland-cursor])
if test x$enable_egl = xyes; then
PKG_CHECK_MODULES(WAYLAND_COMPOSITOR_EGL, [wayland-egl])
fi
fi
AC_ARG_ENABLE(headless-compositor, [ --enable-headless-compositor],,
enable_headless_compositor=yes)
AM_CONDITIONAL(ENABLE_HEADLESS_COMPOSITOR,
test x$enable_headless_compositor = xyes)
if test x$enable_headless_compositor = xyes; then
AC_DEFINE([BUILD_HEADLESS_COMPOSITOR], [1], [Build the headless compositor])
fi
AC_ARG_ENABLE([fbdev-compositor], [ --enable-fbdev-compositor],,
enable_fbdev_compositor=yes)
AM_CONDITIONAL([ENABLE_FBDEV_COMPOSITOR],
[test x$enable_fbdev_compositor = xyes])
AS_IF([test x$enable_fbdev_compositor = xyes], [
AC_DEFINE([BUILD_FBDEV_COMPOSITOR], [1], [Build the fbdev compositor])
PKG_CHECK_MODULES([FBDEV_COMPOSITOR], [libudev >= 136 mtdev >= 1.1.0])
])
AC_ARG_ENABLE([rdp-compositor], [ --enable-rdp-compositor],,
enable_rdp_compositor=no)
AM_CONDITIONAL([ENABLE_RDP_COMPOSITOR],
[test x$enable_rdp_compositor = xyes])
if test x$enable_rdp_compositor = xyes; then
AC_DEFINE([BUILD_RDP_COMPOSITOR], [1], [Build the RDP compositor])
PKG_CHECK_MODULES(RDP_COMPOSITOR, [freerdp2 >= 2.0.0],
[],
[PKG_CHECK_MODULES(RDP_COMPOSITOR, [freerdp >= 1.1.0],[])]
)
SAVED_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $RDP_COMPOSITOR_CFLAGS"
AC_CHECK_HEADERS([freerdp/version.h])
CPPFLAGS="$SAVED_CPPFLAGS"
fi
AC_ARG_ENABLE([screen-sharing], [ --enable-screen-sharing],,
enable_screen_sharing=no)
AM_CONDITIONAL([ENABLE_SCREEN_SHARING],
[test x$enable_screen_sharing = xyes])
if test x$enable_screen_sharing = xyes; then
PKG_CHECK_MODULES(SCREEN_SHARE, [wayland-client])
if test x$enable_rdp_compositor != xyes; then
AC_MSG_WARN([The screen-share.so module requires the RDP backend.])
fi
fi
AC_ARG_WITH(cairo,
AS_HELP_STRING([--with-cairo=@<:@image|gl|glesv2@:>@]
[Which Cairo renderer to use for the clients]),
[],[with_cairo="image"])
if test "x$with_cairo" = "ximage"; then
cairo_modules="cairo"
else
if test "x$with_cairo" = "xgl"; then
cairo_modules="cairo-gl"
AC_MSG_WARN([The --with-cairo=gl option can cause increased resource usage and potential instability, and thus is not recommended. It is needed only for a few special demo programs.])
else
if test "x$with_cairo" = "xglesv2"; then
cairo_modules="cairo-glesv2"
AC_MSG_WARN([The --with-cairo=gles2 option can cause increased resource usage and potential instability, and thus is not recommended. It is needed only for a few special demo programs.])
else
AC_ERROR([Unknown cairo renderer requested])
fi
fi
fi
# Included for legacy compat
AC_ARG_WITH(cairo-glesv2,
AS_HELP_STRING([--with-cairo-glesv2],
[Use GLESv2 cairo]))
if test "x$with_cairo_glesv2" = "xyes"; then
cairo_modules="cairo-glesv2"
with_cairo="glesv2"
fi
if test "x$cairo_modules" = "xcairo-glesv2"; then
AC_DEFINE([USE_CAIRO_GLESV2], [1], [Use the GLESv2 GL cairo backend])
fi
PKG_CHECK_MODULES(PIXMAN, [pixman-1])
PKG_CHECK_MODULES(PNG, [libpng])
AC_ARG_WITH([jpeg],
AS_HELP_STRING([--without-jpeg],
[Use jpeglib for JPEG decoding support [default=auto]]))
AS_IF([test "x$with_jpeg" != "xno"],
[WESTON_SEARCH_LIBS([JPEG], [jpeg], [jpeg_CreateDecompress], [have_jpeglib=yes], [have_jpeglib=no])],
[have_jpeglib=no])
AS_IF([test "x$have_jpeglib" = "xyes"],
[AC_DEFINE([HAVE_JPEG], [1], [Have jpeglib])],
[AS_IF([test "x$with_jpeg" = "xyes"],
[AC_MSG_ERROR([JPEG support explicitly requested, but jpeglib couldn't be found])])])
AC_ARG_WITH([webp],
AS_HELP_STRING([--without-webp],
[Use libwebp for WebP decoding support [default=auto]]))
AS_IF([test "x$with_webp" != "xno"],
[PKG_CHECK_MODULES(WEBP, [libwebp], [have_webp=yes], [have_webp=no])],
[have_webp=no])
AS_IF([test "x$have_webp" = "xyes"],
[AC_DEFINE([HAVE_WEBP], [1], [Have webp])],
[AS_IF([test "x$with_webp" = "xyes"],
[AC_MSG_ERROR([WebP support explicitly requested, but libwebp couldn't be found])])])
AC_ARG_ENABLE(vaapi-recorder, [ --enable-vaapi-recorder],,
enable_vaapi_recorder=auto)
have_libva=no
if test x$enable_vaapi_recorder != xno; then
PKG_CHECK_MODULES(LIBVA, [libva >= 0.34.0 libva-drm >= 0.34.0],
[have_libva=yes], [have_libva=no])
if test "x$have_libva" = "xno" -a "x$enable_vaapi_recorder" = "xyes"; then
AC_MSG_ERROR([vaapi-recorder explicitly enabled, but libva couldn't be found])
fi
AS_IF([test "x$have_libva" = "xyes"],
[AC_DEFINE([BUILD_VAAPI_RECORDER], [1], [Build the vaapi recorder])])
fi
AM_CONDITIONAL(ENABLE_VAAPI_RECORDER, test "x$have_libva" = xyes)
PKG_CHECK_MODULES(CAIRO, [cairo])
PKG_CHECK_MODULES(TEST_CLIENT, [wayland-client >= $WAYLAND_PREREQ_VERSION pixman-1])
AC_ARG_ENABLE(simple-clients,
AS_HELP_STRING([--disable-simple-clients],
[do not build the simple wl_shm clients]),,
enable_simple_clients=yes)
AM_CONDITIONAL(BUILD_SIMPLE_CLIENTS, test "x$enable_simple_clients" = "xyes")
if test x$enable_simple_clients = xyes; then
PKG_CHECK_MODULES(SIMPLE_CLIENT, [wayland-client])
fi
AC_ARG_ENABLE(simple-egl-clients,
AS_HELP_STRING([--disable-simple-egl-clients],
[do not build the simple EGL clients]),,
enable_simple_egl_clients="$enable_egl")
AM_CONDITIONAL(BUILD_SIMPLE_EGL_CLIENTS, test "x$enable_simple_egl_clients" = "xyes")
if test x$enable_simple_egl_clients = xyes; then
PKG_CHECK_MODULES(SIMPLE_EGL_CLIENT,
[egl glesv2 wayland-client wayland-egl wayland-cursor])
fi
AC_ARG_ENABLE(simple-dmabuf-intel-client,
AS_HELP_STRING([--disable-simple-dmabuf-intel-client],
[do not build the simple dmabuf intel client]),,
enable_simple_dmabuf_intel_client="auto")
if ! test "x$enable_simple_dmabuf_intel_client" = "xno"; then
PKG_CHECK_MODULES(SIMPLE_DMABUF_INTEL_CLIENT, [wayland-client libdrm libdrm_intel],
have_simple_dmabuf_intel_client=yes, have_simple_dmabuf_intel_client=no)
if test "x$have_simple_dmabuf_intel_client" = "xno" -a "x$enable_simple_dmabuf_intel_client" = "xyes"; then
AC_MSG_ERROR([Intel dmabuf client explicitly enabled, but libdrm_intel couldn't be found])
fi
enable_simple_dmabuf_intel_client="$have_simple_dmabuf_intel_client"
fi
AM_CONDITIONAL(BUILD_SIMPLE_DMABUF_INTEL_CLIENT, test "x$enable_simple_dmabuf_intel_client" = "xyes")
AC_ARG_ENABLE(simple-dmabuf-v4l-client,
AS_HELP_STRING([--disable-simple-dmabuf-v4l-client],
[do not build the simple dmabuf v4l client]),,
enable_simple_dmabuf_v4l_client="auto")
if ! test "x$enable_simple_dmabuf_v4l_client" = "xno"; then
PKG_CHECK_MODULES(SIMPLE_DMABUF_V4L_CLIENT, [wayland-client libdrm],
have_simple_dmabuf_v4l_client=yes, have_simple_dmabuf_v4l_client=no)
if test "x$have_simple_dmabuf_v4l_client" = "xno" -a "x$enable_simple_dmabuf_v4l_client" = "xyes"; then
AC_MSG_ERROR([V4L dmabuf client explicitly enabled, but libdrm couldn't be found])
fi
enable_simple_dmabuf_v4l_client="$have_simple_dmabuf_v4l_client"
fi
AM_CONDITIONAL(BUILD_SIMPLE_DMABUF_V4L_CLIENT, test "x$enable_simple_dmabuf_v4l_client" = "xyes")
AC_ARG_ENABLE(clients, [ --enable-clients],, enable_clients=yes)
AM_CONDITIONAL(BUILD_CLIENTS, test x$enable_clients = xyes)
have_cairo_egl=no
if test x$enable_clients = xyes; then
AC_DEFINE([BUILD_CLIENTS], [1], [Build the Wayland clients])
PKG_CHECK_MODULES(CLIENT, [wayland-client >= $WAYLAND_PREREQ_VERSION cairo >= 1.10.0 xkbcommon wayland-cursor])
PKG_CHECK_MODULES(SERVER, [wayland-server])
PKG_CHECK_MODULES(WESTON_INFO, [wayland-client >= $WAYLAND_PREREQ_VERSION])
# Only check for cairo-egl if a GL or GLES renderer requested
AS_IF([test "x$cairo_modules" = "xcairo-gl" -o "x$cairo_modules" = "xcairo-glesv2"], [
PKG_CHECK_MODULES(CAIRO_EGL, [wayland-egl egl cairo-egl >= 1.11.3 $cairo_modules],
[have_cairo_egl=yes], [have_cairo_egl=no])
AS_IF([test "x$have_cairo_egl" = "xyes"],
[AC_DEFINE([HAVE_CAIRO_EGL], [1], [Have cairo-egl])],
[AC_ERROR([cairo-egl not used because $CAIRO_EGL_PKG_ERRORS])])],
[have_cairo_egl=no])
PKG_CHECK_MODULES(PANGO, [pangocairo pango glib-2.0 >= 2.36], [have_pango=yes], [have_pango=no])
fi
AC_ARG_ENABLE(resize-optimization,
AS_HELP_STRING([--disable-resize-optimization],
[disable resize optimization allocating a big buffer in toytoolkit]),,
enable_resize_optimization=yes)
AS_IF([test "x$enable_resize_optimization" = "xyes"],
[AC_DEFINE([USE_RESIZE_POOL], [1], [Use resize memory pool as a performance optimization])])
AC_ARG_ENABLE(weston-launch, [ --enable-weston-launch],, enable_weston_launch=yes)
AM_CONDITIONAL(BUILD_WESTON_LAUNCH, test x$enable_weston_launch = xyes)
if test x$enable_weston_launch = xyes; then
WESTON_SEARCH_LIBS([PAM], [pam], [pam_open_session], [have_pam=yes], [have_pam=no])
if test x$have_pam = xno; then
AC_ERROR([weston-launch requires pam])
fi
fi
AM_CONDITIONAL(HAVE_PANGO, test "x$have_pango" = "xyes")
AM_CONDITIONAL(HAVE_CAIRO_GLESV2,
[test "x$have_cairo_egl" = "xyes" -a "x$cairo_modules" = "xcairo-glesv2" -a "x$enable_egl" = "xyes"])
AM_CONDITIONAL(BUILD_FULL_GL_CLIENTS,
test x$cairo_modules = "xcairo-gl" -a "x$have_cairo_egl" = "xyes" -a "x$enable_egl" = "xyes")
AM_CONDITIONAL(BUILD_SUBSURFACES_CLIENT,
[test '(' "x$have_cairo_egl" != "xyes" -o "x$cairo_modules" = "xcairo-glesv2" ')' -a "x$enable_simple_egl_clients" = "xyes"])
AM_CONDITIONAL(ENABLE_DESKTOP_SHELL, true)
AC_ARG_ENABLE(fullscreen-shell,
AS_HELP_STRING([--disable-fullscreen-shell],
[do not build fullscreen-shell server plugin]),,
enable_fullscreen_shell=yes)
AM_CONDITIONAL(ENABLE_FULLSCREEN_SHELL,
test "x$enable_fullscreen_shell" = "xyes")
# CMS modules
AC_ARG_ENABLE(colord,
AS_HELP_STRING([--disable-colord],
[do not build colord CMS support]),,
enable_colord=auto)
have_colord=no
if test "x$enable_colord" != "xno"; then
PKG_CHECK_MODULES(COLORD,
colord >= 0.1.27,
have_colord=yes,
have_colord=no)
if test "x$have_colord" = "xno" -a "x$enable_colord" = "xyes"; then
AC_MSG_ERROR([colord support explicitly requested, but colord couldn't be found])
fi
if test "x$have_colord" = "xyes"; then
enable_colord=yes
fi
fi
AM_CONDITIONAL(ENABLE_COLORD, test "x$enable_colord" = "xyes")
# dbus support
AC_ARG_ENABLE(dbus,
AS_HELP_STRING([--disable-dbus],
[do not build with dbus support]),,
enable_dbus=auto)
if test "x$enable_dbus" != "xno"; then
PKG_CHECK_MODULES(DBUS,
dbus-1 >= 1.6,
have_dbus=yes,
have_dbus=no)
if test "x$have_dbus" = "xno" -a "x$enable_dbus" = "xyes"; then
AC_MSG_ERROR([dbus support explicitly requested, but libdbus couldn't be found])
fi
if test "x$have_dbus" = "xyes"; then
enable_dbus=yes
AC_DEFINE([HAVE_DBUS], [1], [Build with dbus support])
else
enable_dbus=no
fi
fi
AM_CONDITIONAL(ENABLE_DBUS, test "x$enable_dbus" = "xyes")
# systemd-login support
AC_ARG_ENABLE(systemd-login,
AS_HELP_STRING([--enable-systemd-login],
[Enable logind support]),,
enable_systemd_login=auto)
if test x$enable_systemd_login != xno -a x$have_dbus != xno; then
PKG_CHECK_MODULES(SYSTEMD_LOGIN,
[libsystemd >= 209],
[have_systemd_login_209=yes;have_systemd_login=yes],
[have_systemd_login_209=no;have_systemd_login=no])
# Older versions of systemd package systemd-login separately. Fall back on that
AS_IF([test x$have_systemd_login != xyes],[
PKG_CHECK_MODULES(SYSTEMD_LOGIN,
[libsystemd-login >= 198],
[have_systemd_login=yes],
[have_systemd_login=no])
])
else
have_systemd_login=no
fi
if test "x$have_systemd_login" = "xno" -a "x$enable_systemd_login" = "xyes"; then
AC_MSG_ERROR([systemd-login support explicitly enabled, but can't find libsystemd>=209, libsystemd-login or dbus])
fi
AS_IF([test "x$have_systemd_login" = "xyes"],
[AC_DEFINE([HAVE_SYSTEMD_LOGIN], [1], [Have systemd-login])])
AM_CONDITIONAL(HAVE_SYSTEMD_LOGIN, test "x$have_systemd_login" = "xyes")
AS_IF([test "x$have_systemd_login_209" = "xyes"],
[AC_DEFINE([HAVE_SYSTEMD_LOGIN_209], [1], [Have systemd-login >= 209])])
# Note that other features might want libxml2, or this feature might use
# alternative xml libraries at some point. Therefore the feature and
# pre-requisite concepts are split.
AC_ARG_ENABLE(junit_xml,
AS_HELP_STRING([--disable-junit-xml],
[do not build with JUnit XML output]),,
enable_junit_xml=auto)
if test "x$enable_junit_xml" != "xno"; then
PKG_CHECK_MODULES(LIBXML2,
[libxml-2.0 >= 2.6],
have_libxml2=yes,
have_libxml2=no)
if test "x$have_libxml2" = "xno" -a "x$enable_junit_xml" = "xyes"; then
AC_MSG_ERROR([JUnit XML support explicitly requested, but libxml2 couldn't be found])
fi
if test "x$have_libxml2" = "xyes"; then
enable_junit_xml=yes
AC_DEFINE(ENABLE_JUNIT_XML, [1], [Build Weston with JUnit output support])
else
enable_junit_xml=no
fi
fi
AM_CONDITIONAL(ENABLE_JUNIT_XML, test "x$enable_junit_xml" = "xyes")
# ivi-shell support
AC_ARG_ENABLE(ivi-shell,
AS_HELP_STRING([--disable-ivi-shell],
[do not build ivi-shell server plugin and client]),,
enable_ivi_shell=yes)
AM_CONDITIONAL(ENABLE_IVI_SHELL, test "x$enable_ivi_shell" = "xyes")
AC_ARG_ENABLE(wcap-tools, [ --disable-wcap-tools],, enable_wcap_tools=yes)
AM_CONDITIONAL(BUILD_WCAP_TOOLS, test x$enable_wcap_tools = xyes)
if test x$enable_wcap_tools = xyes; then
AC_DEFINE([BUILD_WCAP_TOOLS], [1], [Build the wcap tools])
PKG_CHECK_MODULES(WCAP, [cairo])
WCAP_LIBS="$WCAP_LIBS -lm"
fi
PKG_CHECK_MODULES(SETBACKLIGHT, [libudev libdrm], enable_setbacklight=yes, enable_setbacklight=no)
AM_CONDITIONAL(BUILD_SETBACKLIGHT, test "x$enable_setbacklight" = "xyes")
if test "x$GCC" = "xyes"; then
GCC_CFLAGS="-Wall -Wextra -Wno-unused-parameter \
-Wno-shift-negative-value -Wno-missing-field-initializers \
-g -fvisibility=hidden \
-Wstrict-prototypes -Wmissing-prototypes -Wsign-compare"
fi
AC_SUBST(GCC_CFLAGS)
AC_ARG_ENABLE(libunwind,
AS_HELP_STRING([--disable-libunwind],
[Disable libunwind usage for backtraces]),,
enable_libunwind=auto)
AM_CONDITIONAL(HAVE_LIBUNWIND, [test "x$enable_libunwind" = xyes])
have_libunwind=no
if test "x$enable_libunwind" != "xno"; then
PKG_CHECK_MODULES(LIBUNWIND,
libunwind,
have_libunwind=yes,
have_libunwind=no)
if test "x$have_libunwind" = "xno" -a "x$enable_libunwind" = "xyes"; then
AC_MSG_ERROR([libunwind support explicitly requested, but libunwind couldn't be found])
fi
if test "x$have_libunwind" = "xyes"; then
enable_libunwind=yes
AC_DEFINE(HAVE_LIBUNWIND, 1, [Have libunwind support])
fi
fi
if test "x$WESTON_NATIVE_BACKEND" = "x"; then
WESTON_NATIVE_BACKEND="drm-backend.so"
fi
AC_MSG_NOTICE([Weston's native backend: $WESTON_NATIVE_BACKEND])
AC_DEFINE_UNQUOTED([WESTON_NATIVE_BACKEND], ["$WESTON_NATIVE_BACKEND"],
[The default backend to load, if not wayland nor x11.])
if test "x$WESTON_SHELL_CLIENT" = "x"; then
WESTON_SHELL_CLIENT="weston-desktop-shell"
fi
AC_MSG_NOTICE([Weston's default desktop shell client: $WESTON_SHELL_CLIENT])
AC_DEFINE_UNQUOTED([WESTON_SHELL_CLIENT], ["$WESTON_SHELL_CLIENT"],
[The default desktop shell client to load.])
AC_ARG_ENABLE(demo-clients-install,
AS_HELP_STRING([--enable-demo-clients-install],
[Install demo clients built with weston]),,
enable_demo_clients_install=no)
AM_CONDITIONAL(INSTALL_DEMO_CLIENTS, [test "x$enable_demo_clients_install" = "xyes"])
AC_ARG_ENABLE(lcms,
AS_HELP_STRING([--disable-lcms],
[Disable lcms support]),,
enable_lcms=auto)
have_lcms=no
if test "x$enable_lcms" != "xno"; then
PKG_CHECK_MODULES(LCMS,
lcms2,
have_lcms=yes,
have_lcms=no)
if test "x$have_lcms" = "xno" -a "x$enable_lcms" = "xyes"; then
AC_MSG_ERROR([lcms support explicitly requested, but lcms couldn't be found])
fi
if test "x$have_lcms" = "xyes"; then
enable_lcms=yes
AC_DEFINE(HAVE_LCMS, 1, [Have lcms support])
fi
fi
AM_CONDITIONAL(HAVE_LCMS, [test "x$enable_lcms" = xyes])
AC_PATH_PROG([wayland_scanner], [wayland-scanner])
if test x$wayland_scanner = x; then
PKG_CHECK_MODULES(WAYLAND_SCANNER, [wayland-scanner])
wayland_scanner=`$PKG_CONFIG --variable=wayland_scanner wayland-scanner`
fi
AC_ARG_ENABLE(systemd_notify,
AS_HELP_STRING([--enable-systemd-notify],
[Enables systemd notifications to
notify systemd about weston state
and update watchdog.
Also sockets provided by systemd
in case of socket-base activation
are added to wayland display]),,
enable_systemd_notify=no)
AM_CONDITIONAL(SYSTEMD_NOTIFY_SUPPORT, test x$enable_systemd_notify = xyes)
if test "x$enable_systemd_notify" = "xyes"; then
AC_DEFINE([SYSTEMD_NOTIFY_SUPPORT], [1], [Build the systemd sd_notify support])
PKG_CHECK_MODULES(SYSTEMD_DAEMON, [libsystemd])
fi
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([libweston/libweston-]libweston_major_version[.pc:libweston/libweston.pc.in])
AC_CONFIG_FILES([libweston-desktop/libweston-desktop-]libweston_major_version[.pc:libweston-desktop/libweston-desktop.pc.in])
AM_CONDITIONAL([HAVE_GIT_REPO], [test -f $srcdir/.git/logs/HEAD])
AC_OUTPUT
AC_MSG_RESULT([
Native Backend ${WESTON_NATIVE_BACKEND}
setuid Install ${enable_setuid_install}
Cairo Renderer ${with_cairo}
EGL ${enable_egl}
libxkbcommon ${enable_xkbcommon}
xcb_xkb ${have_xcb_xkb}
XWayland ${enable_xwayland}
dbus ${enable_dbus}
ivi-shell ${enable_ivi_shell}
Build wcap utility ${enable_wcap_tools}
Build Fullscreen Shell ${enable_fullscreen_shell}
Enable developer documentation ${enable_devdocs}
weston-launch utility ${enable_weston_launch}
systemd-login support ${have_systemd_login}
systemd notify support ${enable_systemd_notify}
DRM Compositor ${enable_drm_compositor}
X11 Compositor ${enable_x11_compositor}
Wayland Compositor ${enable_wayland_compositor}
Headless Compositor ${enable_headless_compositor}
FBDEV Compositor ${enable_fbdev_compositor}
RDP Compositor ${enable_rdp_compositor}
Screen Sharing ${enable_screen_sharing}
JUnit XML output ${enable_junit_xml}
Build Clients ${enable_clients}
Build EGL Clients ${have_cairo_egl}
Build Simple Clients ${enable_simple_clients}
Build Simple EGL Clients ${enable_simple_egl_clients}
Install Demo Clients ${enable_demo_clients_install}
Colord Support ${have_colord}
LCMS2 Support ${have_lcms}
libjpeg Support ${have_jpeglib}
libwebp Support ${have_webp}
libunwind Support ${have_libunwind}
VA H.264 encoding Support ${have_libva}
])

View file

@ -1,29 +0,0 @@
prog_gcovr = find_program('gcovr', required: false, disabler: true)
# Configure the build:
# $ meson configure -Db_sanitize=none -Db_coverage=true -Dwerror=true
#
# Ensure there are no stale .gcno or .gcda files around:
# $ meson setup --wipe ~/git/weston
#
# Run the test suite:
# $ meson compile
# $ meson test
#
# Generate the reports:
# $ meson compile gcovr-report
run_target(
'gcovr-report',
command: [
prog_gcovr,
'--root', '@SOURCE_ROOT@',
'--cobertura', meson.current_build_dir() / 'cobertura.xml',
'--html', meson.current_build_dir() / 'index.html',
'--html-nested',
'--html-theme', 'github.green',
'--html-title', 'Weston test suite coverage',
'--print-summary',
meson.project_build_root(),
],
)

View file

@ -33,7 +33,6 @@ home.png
icon_ivi_clickdot.png
icon_ivi_flower.png
icon_ivi_simple-egl.png
icon_ivi_simple-egl-vertical.png
icon_ivi_simple-shm.png
icon_ivi_smoke.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 737 B

After

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 B

After

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 B

After

Width:  |  Height:  |  Size: 161 B

View file

@ -1,30 +0,0 @@
install_data(
[
'background.png',
'border.png',
'fullscreen.png',
'home.png',
'icon_editor.png',
'icon_flower.png',
'icon_ivi_clickdot.png',
'icon_ivi_flower.png',
'icon_ivi_simple-egl.png',
'icon_ivi_simple-egl-vertical.png',
'icon_ivi_simple-shm.png',
'icon_ivi_smoke.png',
'icon_terminal.png',
'icon_window.png',
'panel.png',
'pattern.png',
'random.png',
'sidebyside.png',
'sign_close.png',
'sign_maximize.png',
'sign_minimize.png',
'terminal.png',
'tiling.png',
'wayland.png',
'wayland.svg',
],
install_dir: dir_data / 'weston'
)

Some files were not shown because too many files have changed in this diff Show more