Compare commits

..

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

668 changed files with 53834 additions and 144009 deletions

2
.gitignore vendored
View file

@ -9,5 +9,3 @@ cscope.out
TAGS
00*.patch
build/

View file

@ -1,656 +1,79 @@
# 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-06-15-linux-7.1-mesa-26.1.2'
UPSTREAM_REPO: wayland/weston
DEBIAN_VERSION: buster
DEBIAN_EXEC: 'bash .gitlab-ci/debian-install.sh'
DEBIAN_TAG: '2019-06-26.0'
DEBIAN_CONTAINER_IMAGE: $CI_REGISTRY_IMAGE/debian/$DEBIAN_VERSION:$DEBIAN_TAG
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
- project: 'wayland/ci-templates'
ref: c73dae8b84697ef18e2dbbf4fed7386d9652b0cd
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"
- container_prep
- build
- 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'
container_prep:
extends: .debian@container-ifnot-exists
stage: container_prep
.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
.build-native:
stage: build
image: $DEBIAN_CONTAINER_IMAGE
before_script:
- export PATH=~/.local/bin:$PATH
- git clone --depth=1 https://gitlab.freedesktop.org/wayland/wayland-protocols
- export WAYLAND_PROTOCOLS_DIR="$(pwd)/prefix-wayland-protocols"
- export PKG_CONFIG_PATH="$WAYLAND_PROTOCOLS_DIR/share/pkgconfig:$PKG_CONFIG_PATH"
- export MAKEFLAGS="-j4"
- cd wayland-protocols
- git show -s HEAD
- mkdir build
- cd build
- ../autogen.sh --prefix="$WAYLAND_PROTOCOLS_DIR"
- make install
- cd ../../
- 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
- export BUILD_ID="weston-$CI_JOB_NAME"
- export PREFIX="$(pwd)/prefix-$BUILD_ID"
- export BUILDDIR="$(pwd)/build-$BUILD_ID"
- 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"
build-native-meson:
extends: .build-native
script:
- "${CI_PROJECT_DIR}/.gitlab-ci/build.sh"
- "${CI_PROJECT_DIR}/.gitlab-ci/test.sh"
- export PATH=~/.local/bin:$PATH
- cd "$BUILDDIR"
- meson --prefix="$PREFIX" -Dsimple-dmabuf-drm=intel -Dwerror=true -Ddoc=true ..
- ninja -k0
- ninja install
- ninja test
- ninja clean
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
- build-*/meson-logs
- prefix-*
# 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
dependencies:
- build-native-meson
script:
- export PREFIX=$(pwd)/prefix-weston-docs-build
- export PREFIX=$(pwd)/prefix-weston-build-native-meson
- 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
only:
- master

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=v7.1 https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.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.1.2 --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 v1.0.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 v1.0.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 +1,16 @@
#!/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
set -o xtrace
echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list
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 \
apt-get -y --no-install-recommends install build-essential automake autoconf libtool pkg-config libexpat1-dev libffi-dev libxml2-dev libpixman-1-dev libpng-dev libjpeg-dev libcolord-dev mesa-common-dev libglu1-mesa-dev libegl1-mesa-dev libgles2-mesa-dev libwayland-dev libxcb1-dev libxcb-composite0-dev libxcb-xfixes0-dev libxcb-xkb-dev libx11-xcb-dev libx11-dev libudev-dev libgbm-dev libxkbcommon-dev libcairo2-dev libpango1.0-dev libgdk-pixbuf2.0-dev libxcursor-dev libmtdev-dev libpam0g-dev libvpx-dev libsystemd-dev libevdev-dev libinput-dev libwebp-dev libjpeg-dev libva-dev liblcms2-dev git libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev freerdp2-dev curl python3-pip python3-setuptools doxygen ninja-build libdbus-1-dev libpipewire-0.2-dev
if [ "$FREERDP_VERSION" -ne 0 ] ; then
apt-get -y --no-install-recommends install freerdp${FREERDP_VERSION}-dev
fi
pip3 install --user git+https://github.com/mesonbuild/meson.git@0.49
# for documentation
pip3 install sphinx==2.1.0 --user
pip3 install breathe==4.13.0.post0 --user
pip3 install sphinx_rtd_theme==0.4.3 --user
# 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
mkdir -p /tmp/.X11-unix
chmod 777 /tmp/.X11-unix

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,31 +1,6 @@
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
----------------------------
@ -43,13 +18,31 @@ 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
Many developers also use IRC through [Freenode](https://freenode.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.
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.
Weston formerly accepted patches via `git-send-email`, sent to
**wayland-devel\@lists.freedesktop.org**; these were
[tracked using Patchwork](https://patchwork.freedesktop.org/projects/wayland/).
Some old patches continue to be sent this way, and we may accept small new
patches sent to the list, but please send all new patches through GitLab merge
requests.
Formatting and separating commits
---------------------------------
@ -104,20 +97,17 @@ 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
Your patches should also include a Signed-off-by line with your name 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
also gather S-o-b's by them (and/or whomever gave the patch to you.) 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.
We won't reject patches that lack S-o-b, but it is strongly recommended.
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
@ -216,10 +206,6 @@ my_function(void)
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
=======

341
README.md
View file

@ -3,17 +3,38 @@ Weston
![screenshot of skeletal Weston desktop](doc/wayland-screenshot.jpg)
Weston is a Wayland compositor designed for correctness, reliability,
predictability, and performance.
Weston is the reference implementation of a Wayland compositor, as well as a
useful environment in and of itself.
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.
industrial, kiosks, set-top boxes and TVs. It also provides a library allowing
other projects to build their own full-featured environments on top of Weston's
core.
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.
The core focus of Weston is correctness and reliability. Weston aims to be lean
and fast, but more importantly, to be predictable. Whilst Weston does have known
bugs and shortcomings, we avoid unknown or variable behaviour as much as
possible, including variable performance such as occasional spikes in frame
display time.
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.
If you are after a more mainline desktop experience, the
[GNOME](https://www.gnome.org) and [KDE](https://www.kde.org) projects provide
full-featured desktop environments built on the Wayland protocol. Many other
projects also exist providing Wayland clients and desktop environments: you are
not limited to just what you can find in Weston.
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.
Building Weston
===============
@ -21,7 +42,7 @@ 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).
[wayland-protocols](https://cgit.freedesktop.org/wayland/wayland-protocols).
If necessary, the latest Meson can be installed as a user with:
@ -41,12 +62,14 @@ several features if you want to avoid certain dependencies.
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).
that line, e.g. `meson build/ --prefix=... -Dsimple-dmabuf-drm=intel`.
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`.
option, you can do e.g.
`meson configure build/ -Dsimple-dmabuf-drm=intel`.
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
@ -56,8 +79,7 @@ More [detailed documentation on building Weston](https://wayland.freedesktop.org
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).
For building the documentation see [weston-doc](#weston-doc).
Running Weston
==============
@ -68,72 +90,30 @@ 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.
In some special cases, such as when running remotely or without logind's session
control, Weston may not be able to run directly from a text console. In these
situations, you can instead execute the `weston-launch` helper, which will gain
privileged access to input and output devices by running as root, then granting
access to the main Weston binary running as your user. Running Weston this way
is not recommended unless necessary.
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/).
Weston-doc
==========
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.
together with [breathe](https://breathe.readthedocs.io/en/latest/) that
understands XMLs databases generated by doxygen. So far, this is a compromise
until better tools are available in order to remove the doxygen
dependency. You should be able to install both sphinx and 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
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`).
@ -157,3 +137,226 @@ $ 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.
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.
Libweston's primary purpose is exporting an API for creating Wayland
compositors. Libweston's secondary purpose is to export the weston_config API
so that third party plugins and helper programs can read `weston.ini` if they
want to. However, these two scopes are orthogonal and independent. At no point
will the compositor functionality use or depend on the weston_config
functionality.
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 meson.build. 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.
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.

View file

@ -117,11 +117,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 +129,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 +138,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);
}
@ -232,8 +232,6 @@ 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;

View file

@ -281,8 +281,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;

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"
@ -54,103 +50,141 @@
#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 +206,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 +242,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 +251,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 +306,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 +341,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 +352,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 +377,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 +423,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 +444,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 +514,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 +565,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();

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;
}

512
clients/confine.c Normal file
View file

@ -0,0 +1,512 @@
/*
* 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 <unistd.h>
#include <errno.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;
struct toytimer cursor_timeout;
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)
{
toytimer_arm_once_usec(&confine->cursor_timeout, 500 * 1000);
}
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 toytimer *tt)
{
struct confine *confine =
container_of(tt, struct confine, cursor_timeout);
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;
toytimer_init(&confine->cursor_timeout, CLOCK_MONOTONIC,
display, cursor_timeout_func);
return confine;
}
static void
confine_destroy(struct confine *confine)
{
toytimer_fini(&confine->cursor_timeout);
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: %s\n",
strerror(errno));
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

@ -123,7 +123,7 @@ draw_content(cairo_surface_t *surface, int x, int y, int width, int height,
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_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, 15);
if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_0)
@ -263,7 +263,7 @@ buttons_redraw_handler(struct widget *widget, void *data)
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_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, 10);
cairo_move_to(cr, allocation.x + 5, allocation.y + 15);
@ -367,9 +367,7 @@ int main(int argc, char *argv[])
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");
window_set_title(pc_player->window, "Player");
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);

View file

@ -41,30 +41,24 @@
#include <assert.h>
#include <wayland-client.h>
#include "window.h"
#include "shared/cairo-util.h"
#include <libweston/config-parser.h>
#include <libweston/zalloc.h>
#include "shared/helpers.h"
#include "shared/xalloc.h"
#include "shared/cairo-util.h"
#include <libweston/zalloc.h>
#include "shared/file-util.h"
#include "shared/process-util.h"
#include "shared/timespec-util.h"
#include "window.h"
#include "single-pixel-buffer-v1-client-protocol.h"
#include "tablet-unstable-v2-client-protocol.h"
#include "weston-desktop-shell-client-protocol.h"
#define DEFAULT_CLOCK_FORMAT CLOCK_FORMAT_MINUTES
#define DEFAULT_SPACING 10
extern char **environ; /* defined by libc */
enum clock_format {
CLOCK_FORMAT_MINUTES,
CLOCK_FORMAT_SECONDS,
CLOCK_FORMAT_MINUTES_24H,
CLOCK_FORMAT_SECONDS_24H,
CLOCK_FORMAT_NONE
};
@ -83,7 +77,7 @@ struct desktop {
struct widget *grab_widget;
struct weston_config *config;
bool locking;
int locking;
enum cursor_type grab_cursor;
@ -145,11 +139,9 @@ struct panel_launcher {
cairo_surface_t *icon;
int focused, pressed;
char *path;
char *displayname;
struct wl_list link;
struct custom_env env;
char * const *argp;
char * const *envp;
struct wl_array envp;
struct wl_array argv;
};
struct panel_clock {
@ -216,6 +208,7 @@ check_desktop_ready(struct window *window)
static void
panel_launcher_activate(struct panel_launcher *widget)
{
char **argv;
pid_t pid;
pid = fork();
@ -227,11 +220,13 @@ panel_launcher_activate(struct panel_launcher *widget)
if (pid)
return;
argv = widget->argv.data;
if (setsid() == -1)
exit(EXIT_FAILURE);
if (execve(widget->argp[0], widget->argp, widget->envp) < 0) {
fprintf(stderr, "execl '%s' failed: %s\n", widget->argp[0],
if (execve(argv[0], argv, widget->envp.data) < 0) {
fprintf(stderr, "execl '%s' failed: %s\n", argv[0],
strerror(errno));
exit(1);
}
@ -279,7 +274,7 @@ panel_launcher_motion_handler(struct widget *widget, struct input *input,
{
struct panel_launcher *launcher = data;
widget_set_tooltip(widget, launcher->displayname, x, y);
widget_set_tooltip(widget, basename((char *)launcher->path), x, y);
return CURSOR_LEFT_PTR;
}
@ -376,66 +371,12 @@ panel_launcher_touch_up_handler(struct widget *widget, struct input *input,
panel_launcher_activate(launcher);
}
static void
panel_launcher_tablet_tool_proximity_in_handler(struct widget *widget,
struct tablet_tool *tool,
struct tablet *tablet, void *data)
{
struct panel_launcher *launcher;
launcher = widget_get_user_data(widget);
launcher->focused = 1;
widget_schedule_redraw(widget);
}
static void
panel_launcher_tablet_tool_proximity_out_handler(struct widget *widget,
struct tablet_tool *tool, void *data)
{
struct panel_launcher *launcher;
launcher = widget_get_user_data(widget);
launcher->focused = 0;
widget_schedule_redraw(widget);
}
static void
panel_launcher_tablet_tool_up_handler(struct widget *widget,
struct tablet_tool *tool,
void *data)
{
struct panel_launcher *launcher;
launcher = widget_get_user_data(widget);
panel_launcher_activate(launcher);
}
static void
panel_launcher_tablet_tool_button_handler(struct widget *widget,
struct tablet_tool *tool,
uint32_t button,
uint32_t state_w,
void *data)
{
struct panel_launcher *launcher;
enum zwp_tablet_tool_v2_button_state state = state_w;
launcher = widget_get_user_data(widget);
if (state == ZWP_TABLET_TOOL_V2_BUTTON_STATE_RELEASED)
panel_launcher_activate(launcher);
}
static int clock_timer_reset(struct panel_clock *clock);
static void
clock_func(struct toytimer *tt)
{
struct panel_clock *clock = container_of(tt, struct panel_clock, timer);
widget_schedule_redraw(clock->widget);
clock_timer_reset(clock);
}
static void
@ -480,19 +421,13 @@ static int
clock_timer_reset(struct panel_clock *clock)
{
struct itimerspec its;
struct timespec ts;
struct tm *tm;
clock_gettime(CLOCK_REALTIME, &ts);
tm = localtime(&ts.tv_sec);
its.it_interval.tv_sec = 0;
its.it_interval.tv_sec = clock->refresh_timer;
its.it_interval.tv_nsec = 0;
its.it_value.tv_sec = clock->refresh_timer - tm->tm_sec % clock->refresh_timer;
its.it_value.tv_nsec = 10000000; /* 10 ms late to ensure the clock digit has actually changed */
timespec_add_nsec(&its.it_value, &its.it_value, -ts.tv_nsec);
its.it_value.tv_sec = clock->refresh_timer;
its.it_value.tv_nsec = 0;
toytimer_arm(&clock->timer, &its);
return 0;
}
@ -522,14 +457,6 @@ panel_add_clock(struct panel *panel)
clock->format_string = "%a %b %d, %I:%M:%S %p";
clock->refresh_timer = 1;
break;
case CLOCK_FORMAT_MINUTES_24H:
clock->format_string = "%a %b %d, %H:%M";
clock->refresh_timer = 60;
break;
case CLOCK_FORMAT_SECONDS_24H:
clock->format_string = "%a %b %d, %H:%M:%S";
clock->refresh_timer = 1;
break;
case CLOCK_FORMAT_NONE:
assert(!"not reached");
}
@ -568,7 +495,7 @@ panel_resize_handler(struct widget *widget,
if (panel->clock_format == CLOCK_FORMAT_SECONDS)
w = 170;
else /* CLOCK_FORMAT_MINUTES and 24H versions */
else /* CLOCK_FORMAT_MINUTES */
w = 150;
if (horizontal)
@ -615,8 +542,6 @@ panel_configure(void *data,
width = 32;
break;
case CLOCK_FORMAT_MINUTES:
case CLOCK_FORMAT_MINUTES_24H:
case CLOCK_FORMAT_SECONDS_24H:
width = 150;
break;
case CLOCK_FORMAT_SECONDS:
@ -631,10 +556,10 @@ panel_configure(void *data,
static void
panel_destroy_launcher(struct panel_launcher *launcher)
{
custom_env_fini(&launcher->env);
wl_array_release(&launcher->argv);
wl_array_release(&launcher->envp);
free(launcher->path);
free(launcher->displayname);
cairo_surface_destroy(launcher->icon);
@ -734,19 +659,58 @@ load_icon_or_fallback(const char *icon)
}
static void
panel_add_launcher(struct panel *panel, const char *icon, const char *path, const char *displayname)
panel_add_launcher(struct panel *panel, const char *icon, const char *path)
{
struct panel_launcher *launcher;
char *start, *p, *eq, **ps;
int i, j, k;
launcher = xzalloc(sizeof *launcher);
launcher->icon = load_icon_or_fallback(icon);
launcher->path = xstrdup(path);
launcher->displayname = xstrdup(displayname);
custom_env_init_from_environ(&launcher->env);
custom_env_add_from_exec_string(&launcher->env, launcher->path);
launcher->envp = custom_env_get_envp(&launcher->env);
launcher->argp = custom_env_get_argp(&launcher->env);
wl_array_init(&launcher->envp);
wl_array_init(&launcher->argv);
for (i = 0; environ[i]; i++) {
ps = wl_array_add(&launcher->envp, sizeof *ps);
*ps = environ[i];
}
j = 0;
start = launcher->path;
while (*start) {
for (p = start, eq = NULL; *p && !isspace(*p); p++)
if (*p == '=')
eq = p;
if (eq && j == 0) {
ps = launcher->envp.data;
for (k = 0; k < i; k++)
if (strncmp(ps[k], start, eq - start) == 0) {
ps[k] = start;
break;
}
if (k == i) {
ps = wl_array_add(&launcher->envp, sizeof *ps);
*ps = start;
i++;
}
} else {
ps = wl_array_add(&launcher->argv, sizeof *ps);
*ps = start;
j++;
}
while (*p && isspace(*p))
*p++ = '\0';
start = p;
}
ps = wl_array_add(&launcher->envp, sizeof *ps);
*ps = NULL;
ps = wl_array_add(&launcher->argv, sizeof *ps);
*ps = NULL;
launcher->panel = panel;
wl_list_insert(panel->launcher_list.prev, &launcher->link);
@ -762,13 +726,6 @@ panel_add_launcher(struct panel *panel, const char *icon, const char *path, cons
panel_launcher_touch_down_handler);
widget_set_touch_up_handler(launcher->widget,
panel_launcher_touch_up_handler);
widget_set_tablet_tool_up_handler(launcher->widget,
panel_launcher_tablet_tool_up_handler);
widget_set_tablet_tool_proximity_handlers(launcher->widget,
panel_launcher_tablet_tool_proximity_in_handler,
panel_launcher_tablet_tool_proximity_out_handler);
widget_set_tablet_tool_button_handler(launcher->widget,
panel_launcher_tablet_tool_button_handler);
widget_set_redraw_handler(launcher->widget,
panel_launcher_redraw_handler);
widget_set_motion_handler(launcher->widget,
@ -778,148 +735,95 @@ panel_add_launcher(struct panel *panel, const char *icon, const char *path, cons
enum {
BACKGROUND_SCALE,
BACKGROUND_SCALE_CROP,
BACKGROUND_SCALE_FIT,
BACKGROUND_TILE,
BACKGROUND_CENTERED
};
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
background_draw(struct widget *widget, void *data)
{
struct background *background = data;
cairo_surface_t *surface, *image;
cairo_pattern_t *pattern;
cairo_matrix_t matrix;
cairo_t *cr;
double im_w, im_h;
double sx, sy, s;
double tx, ty;
struct rectangle allocation;
if (!background->image && background->color) {
struct display *display = window_get_display(background->window);
struct wp_single_pixel_buffer_manager_v1 *sp_manager;
struct wl_surface *wl_surface;
struct wl_buffer *wl_buffer;
uint32_t r8, g8, b8;
uint32_t r32, g32, b32;
surface = window_get_surface(background->window);
/* Single pixel buffer must use scale 1 */
window_set_buffer_scale(background->window, 1);
cr = widget_cairo_create(background->widget);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
if (background->color == 0)
cairo_set_source_rgba(cr, 0.0, 0.0, 0.2, 1.0);
else
set_hex_color(cr, background->color);
cairo_paint(cr);
sp_manager = display_get_single_pixel_buffer_manager(display);
assert(sp_manager);
wl_surface = widget_get_wl_surface(background->widget);
assert(wl_surface);
widget_get_allocation(widget, &allocation);
image = NULL;
if (background->image)
image = load_cairo_surface(background->image);
else if (background->color == 0) {
char *name = file_name_with_datadir("pattern.png");
r8 = (background->color >> 16) & 0xff;
g8 = (background->color >> 8) & 0xff;
b8 = (background->color >> 0) & 0xff;
r32 = r8 << 24 | r8 << 16 | r8 << 8 | r8;
g32 = g8 << 24 | g8 << 16 | g8 << 8 | g8;
b32 = b8 << 24 | b8 << 16 | b8 << 8 | b8;
wl_buffer =
wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer(sp_manager,
r32,
g32,
b32,
0xffffffff);
assert(wl_buffer);
wl_surface_attach(wl_surface, wl_buffer, 0, 0);
wl_buffer_add_listener(wl_buffer, &buffer_listener, NULL);
widget_surface_flush(widget);
} else {
cairo_surface_t *surface, *image;
cairo_pattern_t *pattern;
cairo_matrix_t matrix;
cairo_t *cr;
double im_w, im_h;
double sx, sy, s;
double tx, ty;
struct rectangle allocation;
surface = window_get_surface(background->window);
cr = widget_cairo_create(background->widget);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
if (background->color == 0)
cairo_set_source_rgba(cr, 0.0, 0.0, 0.2, 1.0);
else
set_hex_color(cr, background->color);
cairo_paint(cr);
widget_get_allocation(widget, &allocation);
image = NULL;
if (background->image)
image = load_cairo_surface(background->image);
else if (background->color == 0) {
char *name = file_name_with_datadir("pattern.png");
image = load_cairo_surface(name);
free(name);
}
if (image && background->type != -1) {
im_w = cairo_image_surface_get_width(image);
im_h = cairo_image_surface_get_height(image);
sx = im_w / allocation.width;
sy = im_h / allocation.height;
pattern = cairo_pattern_create_for_surface(image);
switch (background->type) {
case BACKGROUND_SCALE:
cairo_matrix_init_scale(&matrix, sx, sy);
cairo_pattern_set_matrix(pattern, &matrix);
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
break;
case BACKGROUND_SCALE_CROP:
case BACKGROUND_SCALE_FIT:
if (background->type == BACKGROUND_SCALE_CROP)
s = (sx < sy) ? sx : sy;
else
s = (sx > sy) ? sx : sy;
/* align center */
tx = (im_w - s * allocation.width) * 0.5;
ty = (im_h - s * allocation.height) * 0.5;
cairo_matrix_init_translate(&matrix, tx, ty);
cairo_matrix_scale(&matrix, s, s);
cairo_pattern_set_matrix(pattern, &matrix);
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
break;
case BACKGROUND_TILE:
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
break;
case BACKGROUND_CENTERED:
s = (sx < sy) ? sx : sy;
if (s < 1.0)
s = 1.0;
/* align center */
tx = (im_w - s * allocation.width) * 0.5;
ty = (im_h - s * allocation.height) * 0.5;
cairo_matrix_init_translate(&matrix, tx, ty);
cairo_matrix_scale(&matrix, s, s);
cairo_pattern_set_matrix(pattern, &matrix);
break;
}
cairo_set_source(cr, pattern);
cairo_pattern_destroy (pattern);
cairo_surface_destroy(image);
cairo_mask(cr, pattern);
}
cairo_destroy(cr);
cairo_surface_destroy(surface);
image = load_cairo_surface(name);
free(name);
}
if (image && background->type != -1) {
im_w = cairo_image_surface_get_width(image);
im_h = cairo_image_surface_get_height(image);
sx = im_w / allocation.width;
sy = im_h / allocation.height;
pattern = cairo_pattern_create_for_surface(image);
switch (background->type) {
case BACKGROUND_SCALE:
cairo_matrix_init_scale(&matrix, sx, sy);
cairo_pattern_set_matrix(pattern, &matrix);
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
break;
case BACKGROUND_SCALE_CROP:
s = (sx < sy) ? sx : sy;
/* align center */
tx = (im_w - s * allocation.width) * 0.5;
ty = (im_h - s * allocation.height) * 0.5;
cairo_matrix_init_translate(&matrix, tx, ty);
cairo_matrix_scale(&matrix, s, s);
cairo_pattern_set_matrix(pattern, &matrix);
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
break;
case BACKGROUND_TILE:
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
break;
case BACKGROUND_CENTERED:
s = (sx < sy) ? sx : sy;
if (s < 1.0)
s = 1.0;
/* align center */
tx = (im_w - s * allocation.width) * 0.5;
ty = (im_h - s * allocation.height) * 0.5;
cairo_matrix_init_translate(&matrix, tx, ty);
cairo_matrix_scale(&matrix, s, s);
cairo_pattern_set_matrix(pattern, &matrix);
break;
}
cairo_set_source(cr, pattern);
cairo_pattern_destroy (pattern);
cairo_surface_destroy(image);
cairo_mask(cr, pattern);
}
cairo_destroy(cr);
cairo_surface_destroy(surface);
background->painted = 1;
check_desktop_ready(background->window);
}
@ -945,13 +849,6 @@ background_configure(void *data,
return;
}
if (!background->image && background->color) {
widget_set_use_cairo(background->widget, 0);
widget_set_viewport_destination(background->widget, width, height);
width = 1;
height = 1;
}
widget_schedule_resize(background->widget, width, height);
}
@ -1246,10 +1143,6 @@ background_create(struct desktop *desktop, struct output *output)
weston_config_section_get_color(s, "background-color",
&background->color, 0x00000000);
/* Backgrounds must be fully opaque. */
if (background->color != 0)
background->color |= 0xFF000000;
weston_config_section_get_string(s, "background-type",
&type, "tile");
if (type == NULL) {
@ -1261,8 +1154,6 @@ background_create(struct desktop *desktop, struct output *output)
background->type = BACKGROUND_SCALE;
} else if (strcmp(type, "scale-crop") == 0) {
background->type = BACKGROUND_SCALE_CROP;
} else if (strcmp(type, "scale-fit") == 0) {
background->type = BACKGROUND_SCALE_FIT;
} else if (strcmp(type, "tile") == 0) {
background->type = BACKGROUND_TILE;
} else if (strcmp(type, "centered") == 0) {
@ -1382,11 +1273,10 @@ output_handle_scale(void *data,
int32_t scale)
{
struct output *output = data;
struct background *background = output->background;
if (output->panel)
window_set_buffer_scale(output->panel->window, scale);
if (background && !background->color)
if (output->background)
window_set_buffer_scale(output->background->window, scale);
}
@ -1532,7 +1422,7 @@ static void
panel_add_launchers(struct panel *panel, struct desktop *desktop)
{
struct weston_config_section *s;
char *icon, *path, *displayname;
char *icon, *path;
const char *name;
int count;
@ -1544,12 +1434,9 @@ panel_add_launchers(struct panel *panel, struct desktop *desktop)
weston_config_section_get_string(s, "icon", &icon, NULL);
weston_config_section_get_string(s, "path", &path, NULL);
weston_config_section_get_string(s, "displayname", &displayname, NULL);
if (displayname == NULL)
displayname = xstrdup(basename(path));
if (icon != NULL && path != NULL) {
panel_add_launcher(panel, icon, path, displayname);
panel_add_launcher(panel, icon, path);
count++;
} else {
fprintf(stderr, "invalid launcher section\n");
@ -1557,17 +1444,15 @@ panel_add_launchers(struct panel *panel, struct desktop *desktop)
free(icon);
free(path);
free(displayname);
}
if (count == 0) {
char *name = file_name_with_datadir("terminal.png");
char *name = file_name_with_datadir("terminal.png");
/* add default launcher */
panel_add_launcher(panel,
name,
BINDIR "/weston-terminal",
"Terminal");
BINDIR "/weston-terminal");
free(name);
}
}
@ -1607,10 +1492,6 @@ parse_clock_format(struct desktop *desktop, struct weston_config_section *s)
desktop->clock_format = CLOCK_FORMAT_MINUTES;
else if (strcmp(clock_format, "seconds") == 0)
desktop->clock_format = CLOCK_FORMAT_SECONDS;
else if (strcmp(clock_format, "minutes-24h") == 0)
desktop->clock_format = CLOCK_FORMAT_MINUTES_24H;
else if (strcmp(clock_format, "seconds-24h") == 0)
desktop->clock_format = CLOCK_FORMAT_SECONDS_24H;
else if (strcmp(clock_format, "none") == 0)
desktop->clock_format = CLOCK_FORMAT_NONE;
else
@ -1631,7 +1512,7 @@ int main(int argc, char *argv[])
config_file = weston_config_get_name_from_env();
desktop.config = weston_config_parse(config_file);
s = weston_config_get_section(desktop.config, "shell", NULL, NULL);
weston_config_section_get_bool(s, "locking", &desktop.locking, true);
weston_config_section_get_bool(s, "locking", &desktop.locking, 1);
parse_panel_position(&desktop, s);
parse_clock_format(&desktop, s);
@ -1639,7 +1520,6 @@ int main(int argc, char *argv[])
if (desktop.display == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
weston_config_destroy(desktop.config);
return -1;
}
@ -1652,7 +1532,7 @@ int main(int argc, char *argv[])
if (desktop.want_panel)
weston_desktop_shell_set_panel_position(desktop.shell, desktop.panel_position);
wl_list_for_each(output, &desktop.outputs, link)
if (!output->background)
if (!output->panel)
output_init(output, &desktop);
grab_surface_create(&desktop);
@ -1668,7 +1548,6 @@ int main(int argc, char *argv[])
unlock_dialog_destroy(desktop.unlock_dialog);
weston_desktop_shell_destroy(desktop.shell);
display_destroy(desktop.display);
weston_config_destroy(desktop.config);
return 0;
}

View file

@ -416,9 +416,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 +515,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
@ -546,11 +546,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 +789,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;

View file

@ -77,7 +77,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;
};
@ -1504,10 +1504,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;
@ -1652,7 +1652,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

@ -47,7 +47,6 @@
/** window title */
static char *title = "EventDemo";
static char *appid = "org.freedesktop.weston.eventdemo";
/** window width */
static int width = 500;
@ -56,7 +55,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 +64,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
@ -351,6 +350,8 @@ 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 absolute x position
* \param y absolute y position
* \param x x position relative to the window
* \param y y position relative to the window
* \param data user data associated to the window
@ -399,7 +400,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,7 +513,7 @@ 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);

View file

@ -187,7 +187,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

@ -36,6 +36,7 @@
#include <linux/input.h>
#include <wayland-client.h>
#include "window.h"
#include "fullscreen-shell-unstable-v1-client-protocol.h"
#include <libweston/zalloc.h>
struct fs_output {
@ -47,6 +48,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 +80,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 +122,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 +148,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 +253,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 +280,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 +287,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 +421,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 +471,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 +496,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 +511,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
@ -411,17 +525,28 @@ int main(int argc, char *argv[])
}
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 +562,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);

503
clients/gears.c Normal file
View file

@ -0,0 +1,503 @@
/*
* 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 <errno.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: %s\n",
strerror(errno));
return -1;
}
gears = gears_create(d);
display_run(d);
gears_destroy(gears);
display_destroy(d);
return 0;
}

View file

@ -43,25 +43,10 @@
#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 +64,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 +82,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 +106,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 +148,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 +215,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 +237,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 +279,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 +310,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 +349,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,157 +381,43 @@ 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",
@ -624,7 +426,7 @@ main(int argc, char *argv[])
}
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

@ -143,8 +143,6 @@ hmi_homescreen_launcher {
uint32_t workspace_id;
char *icon;
char *path;
char **argv;
struct wl_list link;
};
@ -310,10 +308,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;
}
@ -1065,32 +1065,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
*/
@ -1203,7 +1177,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 +1186,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 +1287,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 +1312,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

@ -60,8 +60,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 {
@ -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[])
{
@ -1069,8 +1037,5 @@ main(int argc, char *argv[])
display_run(virtual_keyboard.display);
keyboard_destroy(&virtual_keyboard);
display_destroy(virtual_keyboard.display);
return 0;
}

View file

@ -4,8 +4,6 @@ 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,
@ -14,19 +12,12 @@ srcs_toytoolkit = [
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'),
@ -34,7 +25,7 @@ deps_toytoolkit = [
lib_toytoolkit = static_library(
'toytoolkit',
srcs_toytoolkit,
include_directories: common_inc,
include_directories: include_directories('..', '../shared'),
dependencies: deps_toytoolkit,
install: false,
)
@ -42,10 +33,6 @@ 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 = [
{
@ -56,36 +43,11 @@ simple_clients = [
viewporter_protocol_c,
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
fullscreen_shell_unstable_v1_client_protocol_h,
fullscreen_shell_unstable_v1_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': [
@ -94,19 +56,17 @@ simple_clients = [
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,
xdg_shell_unstable_v6_client_protocol_h,
xdg_shell_unstable_v6_protocol_c,
fullscreen_shell_unstable_v1_client_protocol_h,
fullscreen_shell_unstable_v1_protocol_c,
],
'dep_objs': [
dep_wayland_client,
dep_libdrm,
dep_libm,
dep_matrix_c,
dep_libm
],
'deps': [ 'egl', 'glesv2', 'gbm' ],
'options': [ 'renderer-gl' ]
'deps': [ 'egl', 'glesv2', 'gbm' ]
},
{
'name': 'dmabuf-v4l',
@ -116,37 +76,22 @@ simple_clients = [
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,
fullscreen_shell_unstable_v1_client_protocol_h,
fullscreen_shell_unstable_v1_protocol_c,
],
'dep_objs': [ dep_wayland_client, dep_libdrm_headers ],
'deps': [ 'wayland-cursor' ]
'dep_objs': [ dep_wayland_client, dep_libdrm_headers ]
},
{
'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' ]
'dep_objs': [ dep_wayland_client, dep_libshared, dep_libm ],
'deps': [ 'egl', 'wayland-egl', 'glesv2', 'wayland-cursor' ]
},
# weston-simple-im is handled specially separately due to install_dir and odd window.h usage
{
@ -155,120 +100,24 @@ simple_clients = [
'simple-shm.c',
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
fullscreen_shell_unstable_v1_client_protocol_h,
fullscreen_shell_unstable_v1_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
simple_clients_enabled = get_option('simple-clients')
simple_build_all = simple_clients_enabled.contains('all')
foreach t : simple_clients
if simple_build_all or simple_clients_enabled.contains(t.get('name'))
t_name = 'weston-simple-' + t.get('name')
@ -281,15 +130,9 @@ foreach t : simple_clients
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,
include_directories: [ include_directories('..'), public_inc ],
dependencies: t_deps,
install: true
)
@ -303,7 +146,7 @@ if simple_build_all or simple_clients_enabled.contains('im')
input_method_unstable_v1_client_protocol_h,
input_method_unstable_v1_protocol_c,
],
include_directories: common_inc,
include_directories: include_directories('..'),
dependencies: [
dep_libshared,
dep_wayland_client,
@ -320,8 +163,11 @@ tools_enabled = get_option('tools')
tools_list = [
{
'name': 'calibrator',
'sources': [ 'calibrator.c' ],
'deps': [ dep_toytoolkit, dep_matrix_c ],
'sources': [
'calibrator.c',
'../shared/matrix.c',
],
'deps': [ dep_toytoolkit ],
},
{
'name': 'debug',
@ -332,6 +178,21 @@ tools_list = [
],
'deps': [ dep_wayland_client ]
},
{
'name': 'info',
'sources': [
'weston-info.c',
presentation_time_client_protocol_h,
presentation_time_protocol_c,
linux_dmabuf_unstable_v1_client_protocol_h,
linux_dmabuf_unstable_v1_protocol_c,
tablet_unstable_v2_client_protocol_h,
tablet_unstable_v2_protocol_c,
xdg_output_unstable_v1_client_protocol_h,
xdg_output_unstable_v1_protocol_c,
],
'deps': [ dep_wayland_client, dep_libshared ]
},
{
'name': 'terminal',
'sources': [ 'terminal.c' ],
@ -341,10 +202,11 @@ tools_list = [
'name': 'touch-calibrator',
'sources': [
'touch-calibrator.c',
'../shared/matrix.c',
weston_touch_calibration_client_protocol_h,
weston_touch_calibration_protocol_c,
],
'deps': [ dep_toytoolkit, dep_matrix_c ],
'deps': [ dep_toytoolkit ],
},
]
@ -353,7 +215,7 @@ foreach t : tools_list
executable(
'weston-@0@'.format(t.get('name')),
t.get('sources'),
include_directories: common_inc,
include_directories: [ include_directories('..', '../shared'), public_inc ],
dependencies: t.get('deps', []),
install: true
)
@ -364,24 +226,9 @@ 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,
],
'dep_objs': dep_vertex_clipping
},
{ 'basename': 'confine' },
{
'basename': 'content_protection',
'add_sources': [
@ -403,22 +250,18 @@ demo_clients = [
{ 'basename': 'flower' },
{
'basename': 'fullscreen',
},
{ 'basename': 'image' },
{
'basename': 'multi-resource',
'add_sources': [
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
fullscreen_shell_unstable_v1_client_protocol_h,
fullscreen_shell_unstable_v1_protocol_c,
]
},
{ 'basename': 'image' },
{ 'basename': 'multi-resource' },
{
'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' },
@ -435,13 +278,6 @@ demo_clients = [
'basename': 'subsurfaces',
'deps': [ 'egl', 'glesv2', 'wayland-egl' ]
},
{
'basename': 'tablet',
'add_sources': [
tablet_unstable_v2_client_protocol_h,
tablet_unstable_v2_protocol_c,
],
},
{ 'basename': 'transformed' },
]
@ -460,13 +296,56 @@ if get_option('demo-clients')
executable(
t_name, t_srcs,
include_directories: common_inc,
include_directories: include_directories('..', '../shared'),
dependencies: t_deps,
install: true
)
endforeach
endif
simple_dmabuf_drm_opts = get_option('simple-dmabuf-drm')
simple_dmabuf_drm_deps = []
foreach driver : [ 'etnaviv', 'intel', 'freedreno' ]
if simple_dmabuf_drm_opts.contains(driver)
required = true
enabled = true
elif simple_dmabuf_drm_opts.contains('auto')
required = get_option('auto_features').enabled()
enabled = not get_option('auto_features').disabled()
else
enabled = false
endif
if enabled
dep = dependency('libdrm_' + driver, required: false)
if dep.found()
simple_dmabuf_drm_deps += dep
config_h.set('HAVE_LIBDRM_' + driver.to_upper(), 1)
elif required
error('simple-dmabuf-drm is configured to use @0@ but it was not found. Or, you can remove @1@ from \'-Dsimple-dmabuf-drm\' list.'.format('libdrm_' + driver, driver))
endif
endif
endforeach
if simple_dmabuf_drm_deps.length() > 0
executable(
'weston-simple-dmabuf-drm',
'simple-dmabuf-drm.c',
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
fullscreen_shell_unstable_v1_client_protocol_h,
fullscreen_shell_unstable_v1_protocol_c,
linux_dmabuf_unstable_v1_client_protocol_h,
linux_dmabuf_unstable_v1_protocol_c,
include_directories: [ include_directories('..'), public_inc ],
dependencies: [
dep_wayland_client,
dep_libdrm,
simple_dmabuf_drm_deps
],
install: true
)
endif
if get_option('shell-desktop')
exe_keyboard = executable(
'weston-keyboard',
@ -475,21 +354,31 @@ if get_option('shell-desktop')
text_input_unstable_v1_protocol_c,
input_method_unstable_v1_client_protocol_h,
input_method_unstable_v1_protocol_c,
include_directories: common_inc,
include_directories: include_directories('..'),
dependencies: dep_toytoolkit,
install_dir: get_option('libexecdir'),
install: true
)
env_modmap += 'weston-keyboard=@0@;'.format(exe_keyboard.full_path())
exe_shooter = executable(
'weston-screenshooter',
'screenshot.c',
weston_screenshooter_client_protocol_h,
weston_screenshooter_protocol_c,
include_directories: include_directories('..'),
dependencies: dep_toytoolkit,
install_dir: get_option('bindir'),
install: true
)
env_modmap += 'weston-screenshooter=@0@;'.format(exe_shooter.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,
include_directories: include_directories('..'),
dependencies: dep_toytoolkit,
install_dir: get_option('libexecdir'),
install: true
@ -497,24 +386,6 @@ if get_option('shell-desktop')
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(
@ -524,7 +395,7 @@ if get_option('shell-ivi')
ivi_hmi_controller_protocol_c,
ivi_application_client_protocol_h,
ivi_application_protocol_c,
include_directories: common_inc,
include_directories: include_directories('..'),
dependencies: dep_toytoolkit,
install: true,
install_dir: get_option('libexecdir')

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"),
@ -45,10 +44,6 @@
#include "shared/xalloc.h"
#include <libweston/zalloc.h>
#include "xdg-shell-client-protocol.h"
static int running = 1;
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
@ -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;
@ -443,8 +399,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 +414,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);
eglTerminate(client->egl_display);
eglReleaseThread();
wl_display_flush(client->display);
wl_display_disconnect(client->display);
}
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;
}

1137
clients/nested.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -446,7 +446,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;
@ -765,7 +765,7 @@ registry_handle_global(void *data, struct wl_registry *registry,
} else if (strcmp(interface, "xdg_wm_base") == 0) {
d->wm_base =
wl_registry_bind(registry, name,
&xdg_wm_base_interface, 1);
&xdg_wm_base_interface, version);
} else if (strcmp(interface, "wl_shm") == 0) {
d->shm = wl_registry_bind(registry,
name, &wl_shm_interface, 1);
@ -775,7 +775,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);
}

View file

@ -392,8 +392,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);

View file

@ -303,7 +303,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 +316,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,338 +23,135 @@
#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"
#include "shared/file-util.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;
int force_width;
int force_height;
};
struct screenshooter_buffer {
struct client_buffer *buf;
pixman_image_t *image;
enum weston_capture_v1_source src_type;
};
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 wl_output *output;
struct wl_buffer *buffer;
int width, height, offset_x, offset_y;
void *data;
struct wl_list link;
};
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;
struct screenshooter_data {
struct wl_shm *shm;
struct wl_list output_list;
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;
struct screenshooter_app *app = output->app;
assert(width > 0);
assert(height > 0);
output->buffer_width = width;
output->buffer_height = height;
if (app->force_width)
output->buffer_width = app->force_width;
if (app->force_height)
output->buffer_height = app->force_height;
if (output->app->verbose) {
printf("Got size %dx%d\n", width, height);
if (output->buffer_width != width ||
output->buffer_height != height)
printf("\tOverridden with: %dx%d\n",
output->buffer_width, output->buffer_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 weston_screenshooter *screenshooter;
int buffer_copy_done;
};
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)
{
struct screenshooter_data *sh_data = data;
sh_data->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;
struct screenshooter_data *sh_data = data;
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(&sh_data->output_list, &output->link);
wl_output_add_listener(output->output, &output_listener, output);
} else if (strcmp(interface, "wl_shm") == 0) {
sh_data->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
} else if (strcmp(interface, "weston_screenshooter") == 0) {
sh_data->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 = {
@ -363,87 +159,80 @@ static const struct wl_registry_listener registry_listener = {
handle_global_remove
};
static void
screenshooter_output_capture(struct screenshooter_output *output)
static struct wl_buffer *
screenshot_create_shm_buffer(int width, int height, void **data_out,
struct wl_shm *shm)
{
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: %s\n",
size, strerror(errno));
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: %s\n", strerror(errno));
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)
{
pixman_image_t *shot;
int output_stride, buffer_stride, i;
cairo_surface_t *surface;
struct screenshooter_output *output;
void *data, *d, *s;
struct screenshooter_output *output, *next;
FILE *fp;
char filepath[PATH_MAX];
shot = pixman_image_create_bits(PIXMAN_a8r8g8b8,
buff_size->width, buff_size->height,
NULL, 0);
abort_oom_if_null(shot);
buffer_stride = buff_size->width * 4;
wl_list_for_each(output, output_list, link) {
client_buffer_util_maybe_sync_dmabuf_start(output->buffer->buf);
data = xmalloc(buffer_stride * buff_size->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 - buff_size->min_y) * buffer_stride +
(output->offset_x - buff_size->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));
buff_size->width,
buff_size->height,
buffer_stride);
fp = file_create_dated(getenv("XDG_PICTURES_DIR"), "wayland-screenshot-",
".png", filepath, sizeof(filepath));
@ -452,324 +241,89 @@ screenshot_write_png(const struct buffer_size *buff_size,
cairo_surface_write_to_png(surface, filepath);
}
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)
screenshot_set_buffer_size(struct buffer_size *buff_size, struct wl_list *output_list)
{
struct screenshooter_output *output;
int min_x = INT_MAX, min_y = INT_MAX;
int max_x = INT_MIN, max_y = INT_MIN;
buff_size->min_x = buff_size->min_y = INT_MAX;
buff_size->max_x = buff_size->max_y = INT_MIN;
int position = 0;
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) {
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->buffer_width);
max_y = MAX(max_y, output->offset_y + output->buffer_height);
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->width);
buff_size->max_y =
MAX(buff_size->max_y, output->offset_y + output->height);
}
if (max_x <= min_x || max_y <= min_y)
if (buff_size->max_x <= buff_size->min_x ||
buff_size->max_y <= buff_size->min_y)
return -1;
buff_size->width = max_x - min_x;
buff_size->height = max_y - min_y;
buff_size->width = buff_size->max_x - buff_size->min_x;
buff_size->height = buff_size->max_y - buff_size->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;
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"
"\t-W Force all outputs to a specified buffer width\n"
"\t-H Force all outputs to a specified buffer height\n"
"\t\tForced dimensions require writeback source and may not be supported by the driver.\n"
"\t\tThey must be even to avoid problems with subsampled formats.\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;
struct screenshooter_data sh_data = {};
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:W:H:",
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;
case 'W':
app.force_width = atoi(optarg);
break;
case 'H':
app.force_height = atoi(optarg);
break;
default:
print_usage_and_exit();
}
}
wl_list_init(&app.output_list);
app.display = wl_display_connect(NULL);
if (app.display == NULL) {
display = wl_display_connect(NULL);
if (display == NULL) {
fprintf(stderr, "failed to create display: %s\n",
strerror(errno));
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.src_type != WESTON_CAPTURE_V1_SOURCE_WRITEBACK &&
(app.force_width > 0 || app.force_height > 0)) {
fprintf(stderr, "Error: forced dimensions only valid with writeback source\n");
wl_list_init(&sh_data.output_list);
registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, &sh_data);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (sh_data.screenshooter == NULL) {
fprintf(stderr, "display doesn't support screenshooter\n");
return -1;
}
if (app.force_width % 2) {
fprintf(stderr, "Error: forced width must be an even number\n");
weston_screenshooter_add_listener(sh_data.screenshooter,
&screenshooter_listener,
&sh_data);
if (screenshot_set_buffer_size(&buff_size, &sh_data.output_list))
return -1;
wl_list_for_each(output, &sh_data.output_list, link) {
output->buffer =
screenshot_create_shm_buffer(output->width,
output->height,
&output->data,
sh_data.shm);
weston_screenshooter_shoot(sh_data.screenshooter,
output->output,
output->buffer);
sh_data.buffer_copy_done = 0;
while (!sh_data.buffer_copy_done)
wl_display_roundtrip(display);
}
if (app.force_height % 2) {
fprintf(stderr, "Error: forced height must be an even number\n");
return -1;
}
if (!app.capture_factory) {
fprintf(stderr, "Error: display does not support weston_capture_v1\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");
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");
}
/* 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);
screenshot_write_png(&buff_size, &sh_data.output_list);
return 0;
}

View file

@ -41,6 +41,7 @@
#include "shared/os-compatibility.h"
#include <libweston/zalloc.h>
#include "xdg-shell-client-protocol.h"
#include "fullscreen-shell-unstable-v1-client-protocol.h"
#include "viewporter-client-protocol.h"
int print_debug = 0;
@ -52,6 +53,7 @@ struct display {
struct wl_compositor *compositor;
struct wp_viewporter *viewporter;
struct xdg_wm_base *wm_base;
struct zwp_fullscreen_shell_v1 *fshell;
struct wl_shm *shm;
uint32_t formats;
};
@ -335,11 +337,14 @@ create_window(struct display *display, int width, int height,
&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");
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);
}
@ -456,32 +461,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 +502,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 +570,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 +689,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);
@ -759,6 +748,9 @@ registry_handle_global(void *data, struct wl_registry *registry,
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_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 +774,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);
@ -820,6 +812,9 @@ destroy_display(struct display *display)
if (display->wm_base)
xdg_wm_base_destroy(display->wm_base);
if (display->fshell)
zwp_fullscreen_shell_v1_release(display->fshell);
if (display->viewporter)
wp_viewporter_destroy(display->viewporter);
@ -954,7 +949,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

1014
clients/simple-dmabuf-drm.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -39,17 +39,17 @@
#include <unistd.h>
#include <sys/time.h>
#include <drm_fourcc.h>
#include <xf86drm.h>
#include <gbm.h>
#include <wayland-client.h>
#include "shared/helpers.h"
#include "shared/platform.h"
#include "shared/weston-drm-fourcc.h"
#include <libweston/zalloc.h>
#include "xdg-shell-client-protocol.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 "linux-explicit-synchronization-unstable-v1-client-protocol.h"
#include <EGL/egl.h>
@ -57,27 +57,28 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <libweston/matrix.h>
#include "shared/weston-egl-ext.h"
#ifndef DRM_FORMAT_MOD_INVALID
#define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
#endif
/* Possible options that affect the displayed image */
#define OPT_IMMEDIATE (1 << 0) /* create wl_buffer immediately */
#define OPT_IMPLICIT_SYNC (1 << 1) /* force implicit sync */
#define OPT_MANDELBROT (1 << 2) /* render mandelbrot */
#define OPT_DIRECT_DISPLAY (1 << 3) /* direct-display */
#define BUFFER_FORMAT DRM_FORMAT_XRGB8888
#define MAX_BUFFER_PLANES 4
struct display {
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
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 zwp_linux_explicit_synchronization_v1 *explicit_sync;
uint32_t format;
bool format_supported;
uint64_t *modifiers;
int modifiers_count;
int req_dmabuf_immediate;
@ -85,9 +86,7 @@ struct display {
struct {
EGLDisplay display;
EGLContext context;
EGLConfig conf;
bool has_dma_buf_import_modifiers;
bool has_no_config_context;
PFNEGLQUERYDMABUFMODIFIERSEXTPROC query_dma_buf_modifiers;
PFNEGLCREATEIMAGEKHRPROC create_image;
PFNEGLDESTROYIMAGEKHRPROC destroy_image;
@ -130,14 +129,14 @@ struct buffer {
int release_fence_fd;
};
#define NUM_BUFFERS 4
#define NUM_BUFFERS 3
struct window {
struct display *display;
int width, height;
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 zwp_linux_surface_synchronization_v1 *surface_sync;
struct buffer buffers[NUM_BUFFERS];
struct wl_callback *callback;
@ -148,7 +147,6 @@ struct window {
GLuint pos;
GLuint color;
GLuint offset_uniform;
GLuint reflection_uniform;
} gl;
bool render_mandelbrot;
};
@ -327,29 +325,32 @@ create_fbo_for_buffer(struct display *display, struct buffer *buffer)
static int
create_dmabuf_buffer(struct display *display, struct buffer *buffer,
int width, int height, uint32_t opts)
int width, int height)
{
static uint32_t flags = 0;
/* Y-Invert the buffer image, since we are going to renderer to the
* buffer through a FBO. */
static const uint32_t flags = ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT;
struct zwp_linux_buffer_params_v1 *params;
int i;
buffer->display = display;
buffer->width = width;
buffer->height = height;
buffer->format = display->format;
buffer->format = BUFFER_FORMAT;
buffer->release_fence_fd = -1;
#ifdef HAVE_GBM_MODIFIERS
if (display->modifiers_count > 0) {
buffer->bo = gbm_bo_create_with_modifiers2(display->gbm.device,
buffer->width,
buffer->height,
buffer->format,
display->modifiers,
display->modifiers_count,
GBM_BO_USE_RENDERING);
buffer->bo = gbm_bo_create_with_modifiers(display->gbm.device,
buffer->width,
buffer->height,
buffer->format,
display->modifiers,
display->modifiers_count);
if (buffer->bo)
buffer->modifier = gbm_bo_get_modifier(buffer->bo);
}
#endif
if (!buffer->bo) {
buffer->bo = gbm_bo_create(display->gbm.device,
@ -365,6 +366,7 @@ create_dmabuf_buffer(struct display *display, struct buffer *buffer,
goto error;
}
#ifdef HAVE_GBM_MODIFIERS
buffer->plane_count = gbm_bo_get_plane_count(buffer->bo);
for (i = 0; i < buffer->plane_count; ++i) {
int ret;
@ -385,12 +387,17 @@ create_dmabuf_buffer(struct display *display, struct buffer *buffer,
buffer->strides[i] = gbm_bo_get_stride_for_plane(buffer->bo, i);
buffer->offsets[i] = gbm_bo_get_offset(buffer->bo, i);
}
#else
buffer->plane_count = 1;
buffer->strides[0] = gbm_bo_get_stride(buffer->bo);
buffer->dmabuf_fds[0] = gbm_bo_get_fd(buffer->bo);
if (buffer->dmabuf_fds[0] < 0) {
fprintf(stderr, "error: failed to get dmabuf_fd\n");
goto error;
}
#endif
params = zwp_linux_dmabuf_v1_create_params(display->dmabuf);
if ((opts & OPT_DIRECT_DISPLAY) && display->direct_display)
weston_direct_display_v1_enable(display->direct_display, params);
for (i = 0; i < buffer->plane_count; ++i) {
zwp_linux_buffer_params_v1_add(params,
buffer->dmabuf_fds[i],
@ -437,48 +444,47 @@ error:
}
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)
{
}
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 const char *vert_shader_text =
"uniform float offset;\n"
"uniform mat4 reflection;\n"
"attribute vec4 pos;\n"
"attribute vec4 color;\n"
"varying vec4 v_color;\n"
"void main() {\n"
" gl_Position = reflection * (pos + vec4(offset, offset, 0.0, 0.0));\n"
" gl_Position = pos + vec4(offset, offset, 0.0, 0.0);\n"
" v_color = color;\n"
"}\n";
@ -491,12 +497,11 @@ static const char *frag_shader_text =
static const char *vert_shader_mandelbrot_text =
"uniform float offset;\n"
"uniform mat4 reflection;\n"
"attribute vec4 pos;\n"
"varying vec2 v_pos;\n"
"void main() {\n"
" v_pos = pos.xy;\n"
" gl_Position = reflection * (pos + vec4(offset, offset, 0.0, 0.0));\n"
" gl_Position = pos + vec4(offset, offset, 0.0, 0.0);\n"
"}\n";
@ -541,7 +546,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);
return 0;
@ -565,7 +570,7 @@ create_and_link_program(GLuint vert, GLuint frag)
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);
return 0;
}
@ -596,8 +601,6 @@ window_set_up_gl(struct window *window)
window->gl.offset_uniform =
glGetUniformLocation(window->gl.program, "offset");
window->gl.reflection_uniform =
glGetUniformLocation(window->gl.program, "reflection");
return window->gl.program != 0;
}
@ -619,9 +622,9 @@ destroy_window(struct window *window)
}
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->surface_sync)
zwp_linux_surface_synchronization_v1_destroy(window->surface_sync);
wl_surface_destroy(window->surface);
@ -645,30 +648,33 @@ create_window(struct display *display, int width, int height, int opts)
window->height = height;
window->surface = wl_compositor_create_surface(display->compositor);
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-dmabuf-egl");
xdg_toplevel_set_app_id(window->xdg_toplevel,
"org.freedesktop.weston.simple-dmabuf-egl");
zxdg_toplevel_v6_set_title(window->xdg_toplevel, "simple-dmabuf-egl");
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);
}
@ -689,7 +695,7 @@ create_window(struct display *display, int width, int height, int opts)
for (i = 0; i < NUM_BUFFERS; ++i) {
ret = create_dmabuf_buffer(display, &window->buffers[i],
width, height, opts);
width, height);
if (ret < 0)
goto error;
@ -772,7 +778,6 @@ render(struct window *window, struct buffer *buffer)
GLfloat offset;
struct timeval tv;
uint64_t time_ms;
struct weston_matrix reflection;
gettimeofday(&tv, NULL);
time_ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
@ -781,32 +786,12 @@ render(struct window *window, struct buffer *buffer)
* to offsets in the [-0.5, 0.5) range. */
offset = (time_ms % iteration_ms) / (float) iteration_ms - 0.5;
weston_matrix_init(&reflection);
/* perform a reflection about x-axis to keep the same orientation of
* the vertices colors, as outlined in the comment at the beginning
* of this function.
*
* We need to render upside-down, because rendering through an FBO
* causes the bottom of the image to be written to the top pixel row of
* the buffer, y-flipping the image.
*
* Reflection is a specialized version of scaling with the
* following matrix:
*
* [1, 0, 0]
* [0, -1, 0]
* [0, 0, 1]
*/
weston_matrix_scale(&reflection, 1, -1, 1);
/* Direct all GL draws to the buffer through the FBO */
glBindFramebuffer(GL_FRAMEBUFFER, buffer->gl_fbo);
glViewport(0, 0, window->width, window->height);
glUniform1f(window->gl.offset_uniform, offset);
glUniformMatrix4fv(window->gl.reflection_uniform, 1, GL_FALSE,
(GLfloat *) reflection.M.colmaj);
glClearColor(0.0,0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
@ -836,7 +821,6 @@ render_mandelbrot(struct window *window, struct buffer *buffer)
struct timeval tv;
uint64_t time_ms;
int i;
struct weston_matrix reflection;
gettimeofday(&tv, NULL);
time_ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
@ -845,17 +829,12 @@ render_mandelbrot(struct window *window, struct buffer *buffer)
* to offsets in the [-0.5, 0.5) range. */
offset = (time_ms % iteration_ms) / (float) iteration_ms - 0.5;
weston_matrix_init(&reflection);
weston_matrix_scale(&reflection, 1, -1, 1);
/* Direct all GL draws to the buffer through the FBO */
glBindFramebuffer(GL_FRAMEBUFFER, buffer->gl_fbo);
glViewport(0, 0, window->width, window->height);
glUniform1f(window->gl.offset_uniform, offset);
glUniformMatrix4fv(window->gl.reflection_uniform, 1, GL_FALSE,
(GLfloat *) reflection.M.colmaj);
glClearColor(0.6, 0.6, 0.6, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
@ -982,7 +961,7 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
zwp_linux_buffer_release_v1_add_listener(
buffer->buffer_release, &buffer_release_listener, buffer);
} else {
glFlush();
glFinish();
}
wl_surface_attach(window->surface, buffer->buffer, 0, 0);
@ -1006,19 +985,17 @@ dmabuf_modifiers(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->format) {
return;
}
d->format_supported = true;
if (modifier != DRM_FORMAT_MOD_INVALID) {
switch (format) {
case BUFFER_FORMAT:
++d->modifiers_count;
d->modifiers = realloc(d->modifiers,
d->modifiers_count * sizeof(*d->modifiers));
d->modifiers[d->modifiers_count - 1] = modifier;
d->modifiers[d->modifiers_count - 1] =
((uint64_t)modifier_hi << 32) | modifier_lo;
break;
default:
break;
}
}
@ -1034,13 +1011,13 @@ static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
};
static void
xdg_wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial)
xdg_shell_ping(void *data, struct zxdg_shell_v6 *shell, uint32_t serial)
{
xdg_wm_base_pong(wm_base, 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
@ -1053,10 +1030,13 @@ 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, "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) {
if (version < 3)
return;
@ -1067,9 +1047,6 @@ registry_handle_global(void *data, struct wl_registry *registry,
d->explicit_sync = wl_registry_bind(
registry, id,
&zwp_linux_explicit_synchronization_v1_interface, 1);
} else if (strcmp(interface, "weston_direct_display_v1") == 0) {
d->direct_display = wl_registry_bind(registry,
id, &weston_direct_display_v1_interface, 1);
}
}
@ -1101,17 +1078,14 @@ destroy_display(struct display *display)
free(display->modifiers);
if (display->direct_display)
weston_direct_display_v1_destroy(display->direct_display);
if (display->explicit_sync)
zwp_linux_explicit_synchronization_v1_destroy(display->explicit_sync);
if (display->dmabuf)
zwp_linux_dmabuf_v1_destroy(display->dmabuf);
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);
@ -1134,20 +1108,10 @@ display_set_up_egl(struct display *display)
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLint major, minor, ret, count;
EGLint major, minor;
const char *egl_extensions = NULL;
const char *gl_extensions = NULL;
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
};
display->egl.display =
weston_platform_get_egl_display(EGL_PLATFORM_GBM_KHR,
display->gbm.device, NULL);
@ -1181,23 +1145,14 @@ display_set_up_egl(struct display *display)
goto error;
}
if (weston_check_egl_extension(egl_extensions,
if (!weston_check_egl_extension(egl_extensions,
"EGL_KHR_no_config_context")) {
display->egl.has_no_config_context = true;
}
if (display->egl.has_no_config_context) {
display->egl.conf = EGL_NO_CONFIG_KHR;
} else {
fprintf(stderr,
"Warning: EGL_KHR_no_config_context not supported\n");
ret = eglChooseConfig(display->egl.display, config_attribs,
&display->egl.conf, 1, &count);
assert(ret && count >= 1);
fprintf(stderr, "EGL_KHR_no_config_context not supported\n");
goto error;
}
display->egl.context = eglCreateContext(display->egl.display,
display->egl.conf,
EGL_NO_CONFIG_KHR,
EGL_NO_CONTEXT,
context_attribs);
if (display->egl.context == EGL_NO_CONTEXT) {
@ -1277,36 +1232,30 @@ display_update_supported_modifiers_for_egl(struct display *d)
int num_egl_modifiers = 0;
EGLBoolean ret;
int i;
bool try_modifiers = d->egl.has_dma_buf_import_modifiers;
if (try_modifiers) {
ret = d->egl.query_dma_buf_modifiers(d->egl.display,
d->format,
0, /* max_modifiers */
NULL, /* modifiers */
NULL, /* external_only */
&num_egl_modifiers);
if (ret == EGL_FALSE) {
fprintf(stderr, "Failed to query num EGL modifiers for format\n");
goto error;
}
}
if (!num_egl_modifiers)
try_modifiers = false;
/* If EGL doesn't support modifiers, don't use them at all. */
if (!try_modifiers) {
if (!d->egl.has_dma_buf_import_modifiers) {
d->modifiers_count = 0;
free(d->modifiers);
d->modifiers = NULL;
return true;
}
ret = d->egl.query_dma_buf_modifiers(d->egl.display,
BUFFER_FORMAT,
0, /* max_modifiers */
NULL, /* modifiers */
NULL, /* external_only */
&num_egl_modifiers);
if (ret == EGL_FALSE || num_egl_modifiers == 0) {
fprintf(stderr, "Failed to query num EGL modifiers for format\n");
goto error;
}
egl_modifiers = zalloc(num_egl_modifiers * sizeof(*egl_modifiers));
ret = d->egl.query_dma_buf_modifiers(d->egl.display,
d->format,
BUFFER_FORMAT,
num_egl_modifiers,
egl_modifiers,
NULL, /* external_only */
@ -1366,7 +1315,7 @@ display_set_up_gbm(struct display *display, char const* drm_render_node)
}
static struct display *
create_display(char const *drm_render_node, uint32_t format, int opts)
create_display(char const *drm_render_node, int opts)
{
struct display *display = NULL;
@ -1381,7 +1330,6 @@ create_display(char const *drm_render_node, uint32_t format, int opts)
display->display = wl_display_connect(NULL);
assert(display->display);
display->format = format;
display->req_dmabuf_immediate = opts & OPT_IMMEDIATE;
display->registry = wl_display_get_registry(display->display);
@ -1395,9 +1343,8 @@ create_display(char const *drm_render_node, uint32_t format, int opts)
wl_display_roundtrip(display->display);
if (!display->format_supported) {
fprintf(stderr, "format 0x%"PRIX32" is not available\n",
display->format);
if (!display->modifiers_count) {
fprintf(stderr, "format XRGB8888 is not available\n");
goto error;
}
@ -1463,15 +1410,8 @@ print_usage_and_exit(void)
"\t'-e,--explicit-sync=<>'"
"\n\t\t0 to disable explicit sync, "
"\n\t\t1 to enable explicit sync (default: 1)\n"
"\t'-f,--format=0x<>'"
"\n\t\tthe DRM format code to use\n"
"\t'-m,--mandelbrot'"
"\n\t\trender a mandelbrot set with multiple draw calls\n"
"\t'-g,--direct-display'"
"\n\t\tenables weston-direct-display extension to attempt "
"direct scan-out;\n\t\tnote this will cause the image to be "
"displayed inverted as GL uses a\n\t\tdifferent texture "
"coordinate system\n");
"\n\t\trender a mandelbrot set with multiple draw calls\n");
exit(0);
}
@ -1494,7 +1434,6 @@ main(int argc, char **argv)
struct sigaction sigint;
struct display *display;
struct window *window;
uint32_t format = DRM_FORMAT_XRGB8888;
int opts = 0;
char const *drm_render_node = "/dev/dri/renderD128";
int c, option_index, ret = 0;
@ -1505,14 +1444,12 @@ main(int argc, char **argv)
{"drm-render-node", required_argument, 0, 'd' },
{"size", required_argument, 0, 's' },
{"explicit-sync", required_argument, 0, 'e' },
{"format", required_argument, 0, 'f' },
{"mandelbrot", no_argument, 0, 'm' },
{"direct-display", no_argument, 0, 'g' },
{"help", no_argument , 0, 'h' },
{0, 0, 0, 0}
};
while ((c = getopt_long(argc, argv, "hi:d:s:e:f:mg",
while ((c = getopt_long(argc, argv, "hi:d:s:e:m",
long_options, &option_index)) != -1) {
switch (c) {
case 'i':
@ -1532,18 +1469,12 @@ main(int argc, char **argv)
case 'm':
opts |= OPT_MANDELBROT;
break;
case 'g':
opts |= OPT_DIRECT_DISPLAY;
break;
case 'f':
format = strtoul(optarg, NULL, 0);
break;
default:
print_usage_and_exit();
}
}
display = create_display(drm_render_node, format, opts);
display = create_display(drm_render_node, opts);
if (!display)
return 1;
window = create_window(display, window_size, window_size, opts);

File diff suppressed because it is too large Load diff

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 "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 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,7 +120,7 @@ struct buffer {
int data_offsets[VIDEO_MAX_PLANES];
};
#define NUM_BUFFERS 4
#define NUM_BUFFERS 3
struct window {
struct display *display;
@ -147,11 +129,8 @@ struct window {
struct xdg_toplevel *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,
@ -723,41 +557,6 @@ xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *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
@ -772,7 +571,7 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = {
};
static struct window *
create_window(struct display *display, uint32_t win_flags)
create_window(struct display *display)
{
struct window *window;
@ -785,12 +584,6 @@ create_window(struct display *display, uint32_t win_flags)
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);
}
window->xdg_surface =
xdg_wm_base_get_xdg_surface(display->wm_base,
window->surface);
@ -809,16 +602,14 @@ create_window(struct display *display, uint32_t win_flags)
&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;
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,9 +626,6 @@ 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);
if (window->xdg_surface)
@ -890,7 +678,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 +695,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
@ -1029,12 +740,7 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
if (!d->wm_base)
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 +766,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);
@ -1097,44 +795,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) {
} 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, zwp_linux_dmabuf_v1_interface.name) == 0) {
} 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 +832,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,37 +857,29 @@ 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->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 +892,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 +901,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 +912,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 +934,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

@ -30,7 +30,6 @@
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <signal.h>
#include <linux/input.h>
@ -106,8 +105,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 +276,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 +394,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,17 +481,10 @@ 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));
@ -524,31 +514,9 @@ 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));
return -1;

View file

@ -34,129 +34,39 @@
#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"
#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 "fullscreen-shell-unstable-v1-client-protocol.h"
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 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 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 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 +74,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,20 +87,15 @@ 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) {
@ -261,90 +114,16 @@ 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,
uint32_t serial)
@ -366,39 +145,8 @@ static const struct xdg_surface_listener xdg_surface_listener = {
static void
handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *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
@ -416,7 +164,6 @@ static struct window *
create_window(struct display *display, int width, int height)
{
struct window *window;
int i;
window = zalloc(sizeof *window);
if (!window)
@ -426,11 +173,7 @@ 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) {
window->xdg_surface =
@ -447,32 +190,30 @@ create_window(struct display *display, int width, int height)
&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");
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 {
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);
@ -485,34 +226,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 +298,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 +306,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 +314,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 +338,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 = {
@ -759,10 +370,9 @@ registry_handle_global(void *data, struct wl_registry *registry,
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, "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,11 +392,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 +404,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 +456,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);
}
@ -866,6 +473,9 @@ destroy_display(struct display *display)
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);
@ -881,86 +491,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;

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"),
@ -37,51 +36,35 @@
#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;
@ -93,47 +76,22 @@ create_shm_buffer(struct touch *touch)
exit(1);
}
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
touch->data =
mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (touch->data == MAP_FAILED) {
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
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 +100,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 +130,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 +149,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 +241,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 +274,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 +297,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 +310,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 +321,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 +353,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

@ -285,7 +285,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

@ -35,24 +35,14 @@
#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 +63,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 +107,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 +134,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 +150,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 +200,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 +248,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 +275,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,10 +286,8 @@ 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) {
@ -349,13 +298,11 @@ main(int argc, char *argv[])
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

@ -56,8 +56,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 +296,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 +325,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 +733,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);

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>
@ -50,15 +49,14 @@
#include "shared/xalloc.h"
#include "window.h"
static bool option_fullscreen;
static bool option_maximize;
static int option_fullscreen;
static int option_maximize;
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);
@ -869,8 +867,8 @@ resize_handler(struct widget *widget,
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 +1123,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;
@ -1712,7 +1710,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 +1892,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++;
@ -2950,8 +2948,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 +3020,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 +3045,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
@ -3101,9 +3086,6 @@ terminal_run(struct terminal *terminal, const char *path)
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));
exit(EXIT_FAILURE);
@ -3143,9 +3125,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 +3136,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);
@ -3176,16 +3157,6 @@ int main(int argc, char *argv[])
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",
@ -3200,9 +3171,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

@ -330,9 +330,9 @@ compute_calibration(struct calibrator *cal, float *result)
*/
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.d[i + 0] = cal->samples[i].touched.x;
m.d[i + 4] = cal->samples[i].touched.y;
m.d[i + 8] = 1.0f;
}
m.type = WESTON_MATRIX_TRANSFORM_OTHER;
@ -342,20 +342,20 @@ compute_calibration(struct calibrator *cal, float *result)
}
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.f[i] = cal->samples[i].drawn_cal.x;
y_calib.f[i] = cal->samples[i].drawn_cal.y;
}
x_calib.v.el[3] = 0.0f;
y_calib.v.el[3] = 0.0f;
x_calib.f[3] = 0.0f;
y_calib.f[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];
result[i] = x_calib.f[i];
for (i = 0; i < 3; i++)
result[i + 3] = y_calib.v.el[i];
result[i + 3] = y_calib.f[i];
return 0;
}
@ -557,8 +557,6 @@ calibrator_create(struct display *display, const char *match_name)
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);

View file

@ -227,13 +227,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);
}
@ -277,8 +275,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

@ -135,7 +135,6 @@ stream_destroy(struct debug_stream *stream)
weston_debug_stream_v1_destroy(stream->obj);
wl_list_remove(&stream->link);
free(stream->desc);
free(stream->name);
free(stream);
}

1885
clients/weston-info.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -40,8 +40,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 +71,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 +114,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 +286,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 +418,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 +499,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 +531,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 +552,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,31 +603,6 @@ 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);
@ -702,14 +613,6 @@ 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,18 +718,6 @@ 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 *);

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

@ -0,0 +1,583 @@
/*
* 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 <libweston/libweston.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)
{
struct weston_head *head;
const gchar *tmp;
GString *device_id;
/* XXX: What to do with multiple heads?
* This is potentially unstable, if head configuration is changed
* while the output is enabled. */
head = weston_output_get_first_head(o);
if (wl_list_length(&o->head_list) > 1) {
weston_log("colord: WARNING: multiple heads are not supported (output %s).\n",
o->name);
}
/* 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(head->make)) {
tmp = g_hash_table_lookup(cms->pnp_ids, head->make);
if (tmp == NULL)
tmp = head->make;
g_string_append_printf(device_id, "-%s", tmp);
}
if (edid_value_valid(head->model))
g_string_append_printf(device_id, "-%s", head->model);
if (edid_value_valid(head->serial_number))
g_string_append_printf(device_id, "-%s", head->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)
{
struct weston_head *head;
CdDevice *device;
const gchar *tmp;
gchar *device_id;
GError *error = NULL;
GHashTable *device_props;
struct cms_output *ocms;
/* XXX: What to do with multiple heads? */
head = weston_output_get_first_head(o);
/* 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(head->make)) {
tmp = g_hash_table_lookup(cms->pnp_ids, head->make);
if (tmp == NULL)
tmp = head->make;
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_VENDOR),
g_strdup(tmp));
}
if (edid_value_valid(head->model)) {
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_MODEL),
g_strdup(head->model));
}
if (edid_value_valid(head->serial_number)) {
g_hash_table_insert (device_props,
g_strdup(CD_DEVICE_PROPERTY_SERIAL),
g_strdup(head->serial_number));
}
if (head->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 <libweston/libweston.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 <libweston/libweston.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 <libweston/libweston.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;
}

3419
compositor/main.c Normal file

File diff suppressed because it is too large Load diff

167
compositor/meson.build Normal file
View file

@ -0,0 +1,167 @@
srcs_weston = [
git_version_h,
'main.c',
'text-backend.c',
'weston-screenshooter.c',
text_input_unstable_v1_server_protocol_h,
text_input_unstable_v1_protocol_c,
input_method_unstable_v1_server_protocol_h,
input_method_unstable_v1_protocol_c,
weston_screenshooter_server_protocol_h,
weston_screenshooter_protocol_c,
]
deps_weston = [
dep_libshared,
dep_libweston,
dep_libinput,
dep_libevdev,
dep_libdl,
dep_threads,
]
if get_option('xwayland')
config_h.set('BUILD_XWAYLAND', '1')
srcs_weston += 'xwayland.c'
config_h.set_quoted('XSERVER_PATH', get_option('xwayland-path'))
endif
exe_weston = executable(
'weston',
srcs_weston,
include_directories: include_directories('..', '../shared'),
link_args: [ '-Wl,-export-dynamic' ],
dependencies: deps_weston,
install: true
)
install_headers('weston.h', subdir: 'weston')
pkgconfig.generate(
filebase: 'weston',
name: 'Weston Plugin API',
version: version_weston,
description: 'Header files for Weston plugin development',
requires_private: [ lib_weston ],
variables: [
'libexecdir=' + join_paths('${prefix}', get_option('libexecdir')),
'pkglibexecdir=${libexecdir}/weston'
],
subdirs: 'weston'
)
install_data(
'weston.desktop',
install_dir: join_paths(dir_data, 'wayland-sessions')
)
if get_option('screenshare')
srcs_screenshare = [
'screen-share.c',
fullscreen_shell_unstable_v1_client_protocol_h,
fullscreen_shell_unstable_v1_protocol_c,
]
deps_screenshare = [
dep_libshared,
dep_libweston,
dep_wayland_client,
]
plugin_screenshare = shared_library(
'screen-share',
srcs_screenshare,
include_directories: include_directories('..', '../shared'),
dependencies: deps_screenshare,
name_prefix: '',
install: true,
install_dir: dir_module_weston
)
env_modmap += 'screen-share.so=@0@;'.format(plugin_screenshare.full_path())
endif
if get_option('color-management-lcms')
config_h.set('HAVE_LCMS', '1')
srcs_lcms = [
'cms-static.c',
'cms-helper.c',
]
dep_lcms2 = dependency('lcms2', required: false)
if not dep_lcms2.found()
error('cms-static requires lcms2 which was not found. Or, you can use \'-Dcolor-management-lcms=false\'.')
endif
plugin_lcms = shared_library(
'cms-static',
srcs_lcms,
include_directories: include_directories('..', '../shared'),
dependencies: [ dep_libweston, dep_lcms2 ],
name_prefix: '',
install: true,
install_dir: dir_module_weston
)
env_modmap += 'cms-static.so=@0@;'.format(plugin_lcms.full_path())
endif
if get_option('color-management-colord')
if not get_option('color-management-lcms')
error('LCMS must be enabled to support colord')
endif
srcs_colord = [
'cms-colord.c',
'cms-helper.c',
]
dep_colord = dependency('colord', version: '>= 0.1.27', required: false)
if not dep_colord.found()
error('cms-colord requires colord >= 0.1.27 which was not found. Or, you can use \'-Dcolor-management-colord=false\'.')
endif
plugin_colord_deps = [ dep_libweston, dep_colord ]
foreach depname : [ 'glib-2.0', 'gobject-2.0' ]
dep = dependency(depname, required: false)
if not dep.found()
error('cms-colord requires \'@0@\' which was not found. If you rather not build this, set \'-Dcolor-management-colord=false\'.'.format(depname))
endif
plugin_colord_deps += dep
endforeach
plugin_colord = shared_library(
'cms-colord',
srcs_colord,
include_directories: include_directories('..', '../shared'),
dependencies: plugin_colord_deps,
name_prefix: '',
install: true,
install_dir: dir_module_weston
)
env_modmap += 'cms-colord.so=@0@;'.format(plugin_colord.full_path())
endif
if get_option('systemd')
dep_libsystemd = dependency('libsystemd', required: false)
if not dep_libsystemd.found()
error('systemd-notify requires libsystemd which was not found. Or, you can use \'-Dsystemd=false\'.')
endif
plugin_systemd_notify = shared_library(
'systemd-notify',
'systemd-notify.c',
include_directories: include_directories('..', '../shared'),
dependencies: [ dep_libweston, dep_libsystemd ],
name_prefix: '',
install: true,
install_dir: dir_module_weston
)
env_modmap += 'systemd-notify.so=@0@;'.format(plugin_systemd_notify.full_path())
endif
weston_ini_config = configuration_data()
weston_ini_config.set('bindir', dir_bin)
weston_ini_config.set('libexecdir', dir_libexec)
configure_file(
input: '../weston.ini.in',
output: 'weston.ini',
configuration: weston_ini_config
)

1157
compositor/screen-share.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -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

@ -39,7 +39,6 @@
#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;
@ -104,7 +103,6 @@ struct text_backend {
struct {
char *path;
bool overlay_keyboard;
struct wl_client *client;
unsigned deathcount;
@ -142,12 +140,6 @@ 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) {
@ -413,7 +405,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);
@ -461,8 +455,6 @@ text_input_manager_notifier_destroy(struct wl_listener *listener, void *data)
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 +463,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,15 +615,13 @@ unbind_keyboard(struct wl_resource *resource)
static void
input_method_context_grab_key(struct weston_keyboard_grab *grab,
const struct weston_key_event *key_event)
const struct timespec *time, uint32_t key,
uint32_t state_w)
{
struct weston_keyboard *keyboard = grab->keyboard;
struct wl_display *display;
uint32_t serial;
uint32_t msecs;
uint32_t key = key_event->key;
struct timespec time = key_event->base.ts;
uint32_t state_w = key_event->key_state;
if (!keyboard->input_method_resource)
return;
@ -637,7 +629,7 @@ 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);
msecs = timespec_to_msec(time);
wl_keyboard_send_key(keyboard->input_method_resource,
serial, msecs, key, state_w);
}
@ -683,9 +675,6 @@ 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);
@ -714,14 +703,10 @@ input_method_context_key(struct wl_client *client,
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
struct weston_keyboard_grab *default_grab = &keyboard->default_grab;
struct timespec ts;
struct weston_key_event key_event;
timespec_from_msec(&ts, time);
weston_key_event_init(&key_event, &ts, seat, key,
state_w, STATE_UPDATE_NONE);
default_grab->interface->key(default_grab, &key_event);
default_grab->interface->key(default_grab, &ts, key, state_w);
}
static void
@ -817,7 +802,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 =
@ -862,9 +849,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;
}
@ -912,12 +896,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);
}
@ -958,7 +938,7 @@ 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)
@ -994,28 +974,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",
@ -1035,7 +1008,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;
@ -1078,9 +1053,6 @@ text_backend_configuration(struct text_backend *text_backend)
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);
}
@ -1104,9 +1076,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;
@ -1120,8 +1093,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,200 @@
/*
* 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 <libweston/libweston.h>
#include "weston.h"
#include "weston-screenshooter-server-protocol.h"
#include "shared/helpers.h"
#include <libweston/weston-log.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 =
weston_head_from_resource(output_resource)->output;
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;
bool debug_enabled =
weston_compositor_is_debug_protocol_enabled(shooter->ec);
resource = wl_resource_create(client,
&weston_screenshooter_interface, 1, id);
if (!debug_enabled && !shooter->client) {
wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
"screenshooter failed: permission denied. "\
"Debug protocol must be enabled");
return;
} else if (!debug_enabled && 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,
const struct timespec *time, uint32_t key, void *data)
{
struct screenshooter *shooter = data;
char *screenshooter_exe;
screenshooter_exe = wet_get_bindir_path("weston-screenshooter");
if (!screenshooter_exe) {
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, const struct timespec *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_list_remove(&shooter->destroy_listener.link);
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

@ -36,38 +36,34 @@ extern "C" {
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[]);
@ -88,12 +84,9 @@ 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 +95,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

212
compositor/xwayland.c Normal file
View file

@ -0,0 +1,212 @@
/*
* 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 <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <libweston/libweston.h>
#include "compositor/weston.h"
#include <libweston/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[12], abstract_fd_str[12], unix_fd_str[12], wm_fd_str[12];
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: %s\n",
xserver, display,
abstract_fd_str, unix_fd_str, wm_fd_str,
strerror(errno));
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;
}

View file

@ -1,5 +0,0 @@
exclude-branches-by-pattern = \s*(?:weston_assert_)
html-nested = yes
html-theme = github.green
html-title = Weston test suite coverage
print-summary = yes

View file

@ -1,26 +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@',
'--config', files('gcovr.cfg'),
'--cobertura', meson.current_build_dir() / 'cobertura.xml',
'--html', meson.current_build_dir() / 'index.html',
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: 4.8 KiB

View file

@ -9,7 +9,6 @@ install_data(
'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',
@ -26,5 +25,5 @@ install_data(
'wayland.png',
'wayland.svg',
],
install_dir: dir_data / 'weston'
install_dir: join_paths(dir_data, 'weston')
)

695
desktop-shell/exposay.c Normal file
View file

@ -0,0 +1,695 @@
/*
* Copyright © 2010-2012 Intel Corporation
* Copyright © 2011-2012 Collabora, Ltd.
* Copyright © 2013 Raspberry Pi Foundation
*
* 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 "shell.h"
#include "shared/helpers.h"
struct exposay_surface {
struct desktop_shell *shell;
struct exposay_output *eoutput;
struct weston_surface *surface;
struct weston_view *view;
struct wl_listener view_destroy_listener;
struct wl_list link;
int x;
int y;
int width;
int height;
double scale;
int row;
int column;
/* The animations only apply a transformation for their own lifetime,
* and don't have an option to indefinitely maintain the
* transformation in a steady state - so, we apply our own once the
* animation has finished. */
struct weston_transform transform;
};
static void exposay_set_state(struct desktop_shell *shell,
enum exposay_target_state state,
struct weston_seat *seat);
static void exposay_check_state(struct desktop_shell *shell);
static void
exposay_surface_destroy(struct exposay_surface *esurface)
{
wl_list_remove(&esurface->link);
wl_list_remove(&esurface->view_destroy_listener.link);
if (esurface->shell->exposay.focus_current == esurface->view)
esurface->shell->exposay.focus_current = NULL;
if (esurface->shell->exposay.focus_prev == esurface->view)
esurface->shell->exposay.focus_prev = NULL;
free(esurface);
}
static void
exposay_in_flight_inc(struct desktop_shell *shell)
{
shell->exposay.in_flight++;
}
static void
exposay_in_flight_dec(struct desktop_shell *shell)
{
if (--shell->exposay.in_flight > 0)
return;
exposay_check_state(shell);
}
static void
exposay_animate_in_done(struct weston_view_animation *animation, void *data)
{
struct exposay_surface *esurface = data;
wl_list_insert(&esurface->view->geometry.transformation_list,
&esurface->transform.link);
weston_matrix_init(&esurface->transform.matrix);
weston_matrix_scale(&esurface->transform.matrix,
esurface->scale, esurface->scale, 1.0f);
weston_matrix_translate(&esurface->transform.matrix,
esurface->x - esurface->view->geometry.x,
esurface->y - esurface->view->geometry.y,
0);
weston_view_geometry_dirty(esurface->view);
weston_compositor_schedule_repaint(esurface->view->surface->compositor);
exposay_in_flight_dec(esurface->shell);
}
static void
exposay_animate_in(struct exposay_surface *esurface)
{
exposay_in_flight_inc(esurface->shell);
weston_move_scale_run(esurface->view,
esurface->x - esurface->view->geometry.x,
esurface->y - esurface->view->geometry.y,
1.0, esurface->scale, 0,
exposay_animate_in_done, esurface);
}
static void
exposay_animate_out_done(struct weston_view_animation *animation, void *data)
{
struct exposay_surface *esurface = data;
struct desktop_shell *shell = esurface->shell;
exposay_surface_destroy(esurface);
exposay_in_flight_dec(shell);
}
static void
exposay_animate_out(struct exposay_surface *esurface)
{
exposay_in_flight_inc(esurface->shell);
/* Remove the static transformation set up by
* exposay_transform_in_done(). */
wl_list_remove(&esurface->transform.link);
weston_view_geometry_dirty(esurface->view);
weston_move_scale_run(esurface->view,
esurface->x - esurface->view->geometry.x,
esurface->y - esurface->view->geometry.y,
1.0, esurface->scale, 1,
exposay_animate_out_done, esurface);
}
static void
exposay_highlight_surface(struct desktop_shell *shell,
struct exposay_surface *esurface)
{
struct weston_view *view = esurface->view;
if (shell->exposay.focus_current == view)
return;
shell->exposay.row_current = esurface->row;
shell->exposay.column_current = esurface->column;
shell->exposay.cur_output = esurface->eoutput;
activate(shell, view, shell->exposay.seat,
WESTON_ACTIVATE_FLAG_NONE);
shell->exposay.focus_current = view;
}
static int
exposay_is_animating(struct desktop_shell *shell)
{
if (shell->exposay.state_cur == EXPOSAY_LAYOUT_INACTIVE ||
shell->exposay.state_cur == EXPOSAY_LAYOUT_OVERVIEW)
return 0;
return (shell->exposay.in_flight > 0);
}
static void
exposay_pick(struct desktop_shell *shell, int x, int y)
{
struct exposay_surface *esurface;
if (exposay_is_animating(shell))
return;
wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
if (x < esurface->x || x > esurface->x + esurface->width)
continue;
if (y < esurface->y || y > esurface->y + esurface->height)
continue;
exposay_highlight_surface(shell, esurface);
return;
}
}
static void
handle_view_destroy(struct wl_listener *listener, void *data)
{
struct exposay_surface *esurface = container_of(listener,
struct exposay_surface,
view_destroy_listener);
exposay_surface_destroy(esurface);
}
/* Pretty lame layout for now; just tries to make a square. Should take
* aspect ratio into account really. Also needs to be notified of surface
* addition and removal and adjust layout/animate accordingly. */
static enum exposay_layout_state
exposay_layout(struct desktop_shell *shell, struct shell_output *shell_output)
{
struct workspace *workspace = shell->exposay.workspace;
struct weston_output *output = shell_output->output;
struct exposay_output *eoutput = &shell_output->eoutput;
struct weston_view *view;
struct exposay_surface *esurface, *highlight = NULL;
int w, h;
int i;
int last_row_removed = 0;
eoutput->num_surfaces = 0;
wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) {
if (!get_shell_surface(view->surface))
continue;
if (view->output != output)
continue;
eoutput->num_surfaces++;
}
if (eoutput->num_surfaces == 0) {
eoutput->grid_size = 0;
eoutput->hpadding_outer = 0;
eoutput->vpadding_outer = 0;
eoutput->padding_inner = 0;
eoutput->surface_size = 0;
return EXPOSAY_LAYOUT_OVERVIEW;
}
/* Lay the grid out as square as possible, losing surfaces from the
* bottom row if required. Start with fixed padding of a 10% margin
* around the outside and 80px internal padding between surfaces, and
* maximise the area made available to surfaces after this, but only
* to a maximum of 1/3rd the total output size.
*
* If we can't make a square grid, add one extra row at the bottom
* which will have a smaller number of columns.
*
* XXX: Surely there has to be a better way to express this maths,
* right?!
*/
eoutput->grid_size = floor(sqrtf(eoutput->num_surfaces));
if (pow(eoutput->grid_size, 2) != eoutput->num_surfaces)
eoutput->grid_size++;
last_row_removed = pow(eoutput->grid_size, 2) - eoutput->num_surfaces;
eoutput->hpadding_outer = (output->width / 10);
eoutput->vpadding_outer = (output->height / 10);
eoutput->padding_inner = 80;
w = output->width - (eoutput->hpadding_outer * 2);
w -= eoutput->padding_inner * (eoutput->grid_size - 1);
w /= eoutput->grid_size;
h = output->height - (eoutput->vpadding_outer * 2);
h -= eoutput->padding_inner * (eoutput->grid_size - 1);
h /= eoutput->grid_size;
eoutput->surface_size = (w < h) ? w : h;
if (eoutput->surface_size > (output->width / 2))
eoutput->surface_size = output->width / 2;
if (eoutput->surface_size > (output->height / 2))
eoutput->surface_size = output->height / 2;
i = 0;
wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) {
int pad;
pad = eoutput->surface_size + eoutput->padding_inner;
if (!get_shell_surface(view->surface))
continue;
if (view->output != output)
continue;
esurface = malloc(sizeof(*esurface));
if (!esurface) {
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL,
shell->exposay.seat);
break;
}
wl_list_insert(&shell->exposay.surface_list, &esurface->link);
esurface->shell = shell;
esurface->eoutput = eoutput;
esurface->view = view;
esurface->row = i / eoutput->grid_size;
esurface->column = i % eoutput->grid_size;
esurface->x = output->x + eoutput->hpadding_outer;
esurface->x += pad * esurface->column;
esurface->y = output->y + eoutput->vpadding_outer;
esurface->y += pad * esurface->row;
if (esurface->row == eoutput->grid_size - 1)
esurface->x += (eoutput->surface_size + eoutput->padding_inner) * last_row_removed / 2;
if (view->surface->width > view->surface->height)
esurface->scale = eoutput->surface_size / (float) view->surface->width;
else
esurface->scale = eoutput->surface_size / (float) view->surface->height;
esurface->width = view->surface->width * esurface->scale;
esurface->height = view->surface->height * esurface->scale;
if (shell->exposay.focus_current == esurface->view)
highlight = esurface;
exposay_animate_in(esurface);
/* We want our destroy handler to be after the animation
* destroy handler in the list, this way when the view is
* destroyed, the animation can safely call the animation
* completion callback before we free the esurface in our
* destroy handler.
*/
esurface->view_destroy_listener.notify = handle_view_destroy;
wl_signal_add(&view->destroy_signal, &esurface->view_destroy_listener);
i++;
}
if (highlight) {
shell->exposay.focus_current = NULL;
exposay_highlight_surface(shell, highlight);
}
weston_compositor_schedule_repaint(shell->compositor);
return EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW;
}
static void
exposay_focus(struct weston_pointer_grab *grab)
{
}
static void
exposay_motion(struct weston_pointer_grab *grab,
const struct timespec *time,
struct weston_pointer_motion_event *event)
{
struct desktop_shell *shell =
container_of(grab, struct desktop_shell, exposay.grab_ptr);
weston_pointer_move(grab->pointer, event);
exposay_pick(shell,
wl_fixed_to_int(grab->pointer->x),
wl_fixed_to_int(grab->pointer->y));
}
static void
exposay_button(struct weston_pointer_grab *grab, const struct timespec *time,
uint32_t button, uint32_t state_w)
{
struct desktop_shell *shell =
container_of(grab, struct desktop_shell, exposay.grab_ptr);
struct weston_seat *seat = grab->pointer->seat;
enum wl_pointer_button_state state = state_w;
if (button != BTN_LEFT)
return;
/* Store the surface we clicked on, and don't do anything if we end up
* releasing on a different surface. */
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
shell->exposay.clicked = shell->exposay.focus_current;
return;
}
if (shell->exposay.focus_current == shell->exposay.clicked)
exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
else
shell->exposay.clicked = NULL;
}
static void
exposay_axis(struct weston_pointer_grab *grab,
const struct timespec *time,
struct weston_pointer_axis_event *event)
{
}
static void
exposay_axis_source(struct weston_pointer_grab *grab, uint32_t source)
{
}
static void
exposay_frame(struct weston_pointer_grab *grab)
{
}
static void
exposay_pointer_grab_cancel(struct weston_pointer_grab *grab)
{
struct desktop_shell *shell =
container_of(grab, struct desktop_shell, exposay.grab_ptr);
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
}
static const struct weston_pointer_grab_interface exposay_ptr_grab = {
exposay_focus,
exposay_motion,
exposay_button,
exposay_axis,
exposay_axis_source,
exposay_frame,
exposay_pointer_grab_cancel,
};
static int
exposay_maybe_move(struct desktop_shell *shell, int row, int column)
{
struct exposay_surface *esurface;
wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
if (esurface->eoutput != shell->exposay.cur_output ||
esurface->row != row || esurface->column != column)
continue;
exposay_highlight_surface(shell, esurface);
return 1;
}
return 0;
}
static void
exposay_key(struct weston_keyboard_grab *grab, const struct timespec *time,
uint32_t key, uint32_t state_w)
{
struct weston_seat *seat = grab->keyboard->seat;
struct desktop_shell *shell =
container_of(grab, struct desktop_shell, exposay.grab_kbd);
enum wl_keyboard_key_state state = state_w;
if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
return;
switch (key) {
case KEY_ESC:
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
break;
case KEY_ENTER:
exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
break;
case KEY_UP:
exposay_maybe_move(shell, shell->exposay.row_current - 1,
shell->exposay.column_current);
break;
case KEY_DOWN:
/* Special case for trying to move to the bottom row when it
* has fewer items than all the others. */
if (!exposay_maybe_move(shell, shell->exposay.row_current + 1,
shell->exposay.column_current) &&
shell->exposay.row_current < (shell->exposay.cur_output->grid_size - 1)) {
exposay_maybe_move(shell, shell->exposay.row_current + 1,
(shell->exposay.cur_output->num_surfaces %
shell->exposay.cur_output->grid_size) - 1);
}
break;
case KEY_LEFT:
exposay_maybe_move(shell, shell->exposay.row_current,
shell->exposay.column_current - 1);
break;
case KEY_RIGHT:
exposay_maybe_move(shell, shell->exposay.row_current,
shell->exposay.column_current + 1);
break;
case KEY_TAB:
/* Try to move right, then down (and to the leftmost column),
* then if all else fails, to the top left. */
if (!exposay_maybe_move(shell, shell->exposay.row_current,
shell->exposay.column_current + 1) &&
!exposay_maybe_move(shell, shell->exposay.row_current + 1, 0))
exposay_maybe_move(shell, 0, 0);
break;
default:
break;
}
}
static void
exposay_modifier(struct weston_keyboard_grab *grab, uint32_t serial,
uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group)
{
struct desktop_shell *shell =
container_of(grab, struct desktop_shell, exposay.grab_kbd);
struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat;
/* We want to know when mod has been pressed and released.
* FIXME: There is a problem here: if mod is pressed, then a key
* is pressed and released, then mod is released, we will treat that
* as if only mod had been pressed and released. */
if (seat->modifier_state) {
if (seat->modifier_state == shell->binding_modifier) {
shell->exposay.mod_pressed = true;
} else {
shell->exposay.mod_invalid = true;
}
} else {
if (shell->exposay.mod_pressed && !shell->exposay.mod_invalid)
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
shell->exposay.mod_invalid = false;
shell->exposay.mod_pressed = false;
}
return;
}
static void
exposay_cancel(struct weston_keyboard_grab *grab)
{
struct desktop_shell *shell =
container_of(grab, struct desktop_shell, exposay.grab_kbd);
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
}
static const struct weston_keyboard_grab_interface exposay_kbd_grab = {
exposay_key,
exposay_modifier,
exposay_cancel,
};
/**
* Called when the transition from overview -> inactive has completed.
*/
static enum exposay_layout_state
exposay_set_inactive(struct desktop_shell *shell)
{
struct weston_seat *seat = shell->exposay.seat;
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
if (pointer)
weston_pointer_end_grab(pointer);
if (keyboard) {
weston_keyboard_end_grab(keyboard);
if (keyboard->input_method_resource)
keyboard->grab = &keyboard->input_method_grab;
}
return EXPOSAY_LAYOUT_INACTIVE;
}
/**
* Begins the transition from overview to inactive. */
static enum exposay_layout_state
exposay_transition_inactive(struct desktop_shell *shell, int switch_focus)
{
struct exposay_surface *esurface;
/* Call activate() before we start the animations to avoid
* animating back the old state and then immediately transitioning
* to the new. */
if (switch_focus && shell->exposay.focus_current)
activate(shell, shell->exposay.focus_current,
shell->exposay.seat,
WESTON_ACTIVATE_FLAG_CONFIGURE);
else if (shell->exposay.focus_prev)
activate(shell, shell->exposay.focus_prev,
shell->exposay.seat,
WESTON_ACTIVATE_FLAG_CONFIGURE);
wl_list_for_each(esurface, &shell->exposay.surface_list, link)
exposay_animate_out(esurface);
weston_compositor_schedule_repaint(shell->compositor);
return EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE;
}
static enum exposay_layout_state
exposay_transition_active(struct desktop_shell *shell)
{
struct weston_seat *seat = shell->exposay.seat;
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
struct shell_output *shell_output;
bool animate = false;
shell->exposay.workspace = get_current_workspace(shell);
shell->exposay.focus_prev = get_default_view(keyboard->focus);
shell->exposay.focus_current = get_default_view(keyboard->focus);
shell->exposay.clicked = NULL;
wl_list_init(&shell->exposay.surface_list);
lower_fullscreen_layer(shell, NULL);
shell->exposay.grab_kbd.interface = &exposay_kbd_grab;
weston_keyboard_start_grab(keyboard,
&shell->exposay.grab_kbd);
weston_keyboard_set_focus(keyboard, NULL);
shell->exposay.grab_ptr.interface = &exposay_ptr_grab;
if (pointer) {
weston_pointer_start_grab(pointer,
&shell->exposay.grab_ptr);
weston_pointer_clear_focus(pointer);
}
wl_list_for_each(shell_output, &shell->output_list, link) {
enum exposay_layout_state state;
state = exposay_layout(shell, shell_output);
if (state == EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW)
animate = true;
}
return animate ? EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW
: EXPOSAY_LAYOUT_OVERVIEW;
}
static void
exposay_check_state(struct desktop_shell *shell)
{
enum exposay_layout_state state_new = shell->exposay.state_cur;
int do_switch = 0;
/* Don't do anything whilst animations are running, just store up
* target state changes and only act on them when the animations have
* completed. */
if (exposay_is_animating(shell))
return;
switch (shell->exposay.state_target) {
case EXPOSAY_TARGET_OVERVIEW:
switch (shell->exposay.state_cur) {
case EXPOSAY_LAYOUT_OVERVIEW:
goto out;
case EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW:
state_new = EXPOSAY_LAYOUT_OVERVIEW;
break;
default:
state_new = exposay_transition_active(shell);
break;
}
break;
case EXPOSAY_TARGET_SWITCH:
do_switch = 1; /* fallthrough */
case EXPOSAY_TARGET_CANCEL:
switch (shell->exposay.state_cur) {
case EXPOSAY_LAYOUT_INACTIVE:
goto out;
case EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE:
state_new = exposay_set_inactive(shell);
break;
default:
state_new = exposay_transition_inactive(shell, do_switch);
break;
}
break;
}
out:
shell->exposay.state_cur = state_new;
}
static void
exposay_set_state(struct desktop_shell *shell, enum exposay_target_state state,
struct weston_seat *seat)
{
shell->exposay.state_target = state;
shell->exposay.seat = seat;
exposay_check_state(shell);
}
void
exposay_binding(struct weston_keyboard *keyboard, enum weston_keyboard_modifier modifier,
void *data)
{
struct desktop_shell *shell = data;
exposay_set_state(shell, EXPOSAY_TARGET_OVERVIEW, keyboard->seat);
}

View file

@ -36,6 +36,7 @@
struct input_panel_surface {
struct wl_resource *resource;
struct wl_signal destroy_signal;
struct desktop_shell *shell;
@ -58,41 +59,13 @@ input_panel_slide_done(struct weston_view_animation *animation, void *data)
ipsurf->anim = NULL;
}
static int
calc_input_panel_position(struct input_panel_surface *ip_surface, struct weston_coord_global *out_pos)
{
struct desktop_shell *shell = ip_surface->shell;
struct weston_coord_global pos;
if (ip_surface->panel) {
struct weston_view *view = get_default_view(shell->text_input.surface);
if (view == NULL)
return -1;
pos = weston_view_get_pos_offset_global(view);
pos.c.x += shell->text_input.cursor_rectangle.x2;
pos.c.y += shell->text_input.cursor_rectangle.y2;
} else {
pos = ip_surface->output->pos;
pos.c.x += (ip_surface->output->width - ip_surface->surface->width) / 2;
pos.c.y += ip_surface->output->height - ip_surface->surface->height;
}
*out_pos = pos;
return 0;
}
static void
show_input_panel_surface(struct input_panel_surface *ipsurf)
{
struct desktop_shell *shell = ipsurf->shell;
struct weston_seat *seat;
struct weston_surface *focus;
struct weston_coord_global pos;
if (!weston_surface_is_mapped(ipsurf->surface))
return;
if (weston_view_is_mapped(ipsurf->view))
return;
float x, y;
wl_list_for_each(seat, &shell->compositor->seat_list, link) {
struct weston_keyboard *keyboard =
@ -104,15 +77,19 @@ show_input_panel_surface(struct input_panel_surface *ipsurf)
if (!focus)
continue;
ipsurf->output = focus->output;
if (calc_input_panel_position(ipsurf, &pos))
continue;
weston_view_set_position(ipsurf->view, pos);
weston_view_move_to_layer(ipsurf->view,
&shell->input_panel_layer.view_list);
break;
x = ipsurf->output->x + (ipsurf->output->width - ipsurf->surface->width) / 2;
y = ipsurf->output->y + ipsurf->output->height - ipsurf->surface->height;
weston_view_set_position(ipsurf->view, x, y);
}
weston_layer_entry_insert(&shell->input_panel_layer.view_list,
&ipsurf->view->layer_link);
weston_view_geometry_dirty(ipsurf->view);
weston_view_update_transform(ipsurf->view);
ipsurf->surface->is_mapped = true;
ipsurf->view->is_mapped = true;
weston_surface_damage(ipsurf->surface);
if (ipsurf->anim)
weston_view_animation_destroy(ipsurf->anim);
@ -143,6 +120,9 @@ show_input_panels(struct wl_listener *listener, void *data)
wl_list_for_each_safe(ipsurf, next,
&shell->input_panel.surfaces, link) {
if (ipsurf->surface->width == 0)
continue;
show_input_panel_surface(ipsurf);
}
}
@ -166,7 +146,7 @@ hide_input_panels(struct wl_listener *listener, void *data)
wl_list_for_each_safe(view, next,
&shell->input_panel_layer.view_list.link,
layer_link.link)
weston_view_move_to_layer(view, NULL);
weston_view_unmap(view);
}
static void
@ -179,33 +159,50 @@ update_input_panels(struct wl_listener *listener, void *data)
memcpy(&shell->text_input.cursor_rectangle, data, sizeof(pixman_box32_t));
}
static int
input_panel_get_label(struct weston_surface *surface, char *buf, size_t len)
{
return snprintf(buf, len, "input panel");
}
static void
input_panel_committed(struct weston_surface *surface,
struct weston_coord_surface new_origin)
input_panel_committed(struct weston_surface *surface, int32_t sx, int32_t sy)
{
struct input_panel_surface *ip_surface = surface->committed_private;
struct desktop_shell *shell = ip_surface->shell;
struct weston_view *view;
float x, y;
if (!weston_surface_has_content(surface))
if (surface->width == 0)
return;
if (weston_surface_is_mapped(surface))
return;
if (ip_surface->panel) {
view = get_default_view(shell->text_input.surface);
if (view == NULL)
return;
x = view->geometry.x + shell->text_input.cursor_rectangle.x2;
y = view->geometry.y + shell->text_input.cursor_rectangle.y2;
} else {
x = ip_surface->output->x + (ip_surface->output->width - surface->width) / 2;
y = ip_surface->output->y + ip_surface->output->height - surface->height;
}
weston_surface_map(surface);
weston_view_set_position(ip_surface->view, x, y);
if (shell->showing_input_panels)
if (!weston_surface_is_mapped(surface) && shell->showing_input_panels)
show_input_panel_surface(ip_surface);
}
static void
destroy_input_panel_surface(struct input_panel_surface *input_panel_surface)
{
wl_signal_emit(&input_panel_surface->destroy_signal, input_panel_surface);
wl_list_remove(&input_panel_surface->surface_destroy_listener.link);
wl_list_remove(&input_panel_surface->link);
input_panel_surface->surface->committed = NULL;
weston_surface_set_label(input_panel_surface->surface, NULL);
weston_surface_set_label_func(input_panel_surface->surface, NULL);
weston_view_destroy(input_panel_surface->view);
free(input_panel_surface);
@ -247,15 +244,15 @@ create_input_panel_surface(struct desktop_shell *shell,
surface->committed = input_panel_committed;
surface->committed_private = input_panel_surface;
weston_surface_set_label_static(surface, "input panel");
weston_surface_set_label_func(surface, input_panel_get_label);
input_panel_surface->shell = shell;
input_panel_surface->surface = surface;
input_panel_surface->view = weston_view_create(surface);
input_panel_surface->surface_destroy_listener.notify =
input_panel_handle_surface_destroy;
wl_signal_init(&input_panel_surface->destroy_signal);
input_panel_surface->surface_destroy_listener.notify = input_panel_handle_surface_destroy;
wl_signal_add(&surface->destroy_signal,
&input_panel_surface->surface_destroy_listener);
@ -273,15 +270,14 @@ input_panel_surface_set_toplevel(struct wl_client *client,
struct input_panel_surface *input_panel_surface =
wl_resource_get_user_data(resource);
struct desktop_shell *shell = input_panel_surface->shell;
struct weston_head *head = weston_head_from_resource(output_resource);
struct weston_head *head;
if (head) {
wl_list_insert(&shell->input_panel.surfaces,
&input_panel_surface->link);
wl_list_insert(&shell->input_panel.surfaces,
&input_panel_surface->link);
input_panel_surface->output = head->output;
input_panel_surface->panel = 0;
}
head = weston_head_from_resource(output_resource);
input_panel_surface->output = head->output;
input_panel_surface->panel = 0;
}
static void

View file

@ -3,27 +3,27 @@ if get_option('shell-desktop')
srcs_shell_desktop = [
'shell.c',
'exposay.c',
'input-panel.c',
'../shared/matrix.c',
weston_desktop_shell_server_protocol_h,
weston_desktop_shell_protocol_c,
input_method_unstable_v1_server_protocol_h,
input_method_unstable_v1_protocol_c,
]
deps_shell_desktop = [
dep_libm,
dep_libexec_weston,
dep_libshared,
dep_libweston_public,
dep_lib_desktop,
dep_libweston,
]
plugin_shell_desktop = shared_library(
'desktop-shell',
srcs_shell_desktop,
include_directories: common_inc,
include_directories: include_directories('..', '../shared'),
dependencies: deps_shell_desktop,
name_prefix: '',
install: true,
install_dir: dir_module_weston,
install_rpath: '$ORIGIN'
install_dir: dir_module_weston
)
env_modmap += 'desktop-shell.so=@0@;'.format(plugin_shell_desktop.full_path())
endif

File diff suppressed because it is too large Load diff

View file

@ -23,9 +23,6 @@
* DEALINGS IN THE SOFTWARE.
*/
#ifndef WESTON_DESKTOP_SHELL_H
#define WESTON_DESKTOP_SHELL_H
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
@ -35,170 +32,6 @@
#include "weston-desktop-shell-server-protocol.h"
struct focus_state {
struct desktop_shell *shell;
struct weston_seat *seat;
struct workspace *ws;
struct weston_surface *keyboard_focus;
struct wl_list link;
struct wl_listener seat_destroy_listener;
struct wl_listener surface_destroy_listener;
};
/*
* Surface stacking and ordering.
*
* This is handled using several linked lists of surfaces, organised into
* layers. The layers are ordered, and each of the surfaces in one layer are
* above all of the surfaces in the layer below. The set of layers is static and
* in the following order (top-most first):
* Lock layer (only ever displayed on its own)
* Cursor layer
* Input panel layer
* Fullscreen layer
* Panel layer
* Workspace layers
* Background layer
*
* The list of layers may be manipulated to remove whole layers of surfaces from
* display. For example, when locking the screen, all layers except the lock
* layer are removed.
*
* A surfaces layer is modified on configuring the surface, in
* set_surface_type() (which is only called when the surfaces type change is
* _committed_). If a surfaces type changes (e.g. when making a window
* fullscreen) its layer changes too.
*
* In order to allow popup and transient surfaces to be correctly stacked above
* their parent surfaces, each surface tracks both its parent surface, and a
* linked list of its children. When a surfaces layer is updated, so are the
* layers of its children. Note that child surfaces are *not* the same as
* subsurfaces child/parent surfaces are purely for maintaining stacking
* order.
*
* The children_link list of siblings of a surface (i.e. those surfaces which
* have the same parent) only contains weston_surfaces which have a
* shell_surface. Stacking is not implemented for non-shell_surface
* weston_surfaces. This means that the following implication does *not* hold:
* (shsurf->parent != NULL) !wl_list_is_empty(shsurf->children_link)
*/
struct shell_surface {
struct wl_signal destroy_signal;
struct weston_desktop_surface *desktop_surface;
struct weston_view *view;
struct weston_surface *wsurface_anim_fade;
struct weston_view *wview_anim_fade;
int32_t last_width, last_height;
struct desktop_shell *shell;
struct wl_list children_list;
struct wl_list children_link;
struct weston_coord_global saved_pos;
bool saved_position_valid;
bool saved_rotation_valid;
int unresponsive, grabbed;
uint32_t resize_edges;
uint32_t orientation;
struct {
struct weston_transform transform;
struct weston_matrix rotation;
} rotation;
struct {
struct weston_curtain *black_view;
} fullscreen;
struct shell_output *fullscreen_output;
struct shell_output *output;
struct wl_listener output_destroy_listener;
struct wl_listener surface_label_update;
struct surface_state {
bool fullscreen;
bool maximized;
bool lowered;
} state;
struct {
bool is_set;
struct weston_coord_global pos;
} xwayland;
int focus_count;
bool destroying;
struct wl_list link; /** desktop_shell::shsurf_list */
};
struct shell_grab {
struct weston_pointer_grab grab;
struct shell_surface *shsurf;
struct wl_listener shsurf_destroy_listener;
};
struct shell_touch_grab {
struct weston_touch_grab grab;
struct shell_surface *shsurf;
struct wl_listener shsurf_destroy_listener;
struct weston_touch *touch;
};
struct shell_tablet_tool_grab {
struct weston_tablet_tool_grab grab;
struct shell_surface *shsurf;
struct wl_listener shsurf_destroy_listener;
struct weston_tablet_tool *tool;
};
struct weston_move_grab {
struct shell_grab base;
struct weston_coord_global delta;
bool client_initiated;
};
struct weston_touch_move_grab {
struct shell_touch_grab base;
int active;
struct weston_coord_global delta;
};
struct weston_tablet_tool_move_grab {
struct shell_tablet_tool_grab base;
wl_fixed_t dx, dy;
};
struct rotate_grab {
struct shell_grab base;
struct weston_matrix rotation;
struct {
float x;
float y;
} center;
};
struct shell_seat {
struct weston_seat *seat;
struct wl_listener seat_destroy_listener;
struct weston_surface *focused_surface;
struct wl_listener caps_changed_listener;
struct wl_listener pointer_focus_listener;
struct wl_listener keyboard_focus_listener;
struct wl_listener tablet_tool_added_listener;
struct wl_list link; /** shell::seat_list */
};
struct tablet_tool_listener {
struct wl_listener base;
struct wl_listener removed_listener;
};
enum animation_type {
ANIMATION_NONE,
@ -212,8 +45,58 @@ enum fade_type {
FADE_OUT
};
enum exposay_target_state {
EXPOSAY_TARGET_OVERVIEW, /* show all windows */
EXPOSAY_TARGET_CANCEL, /* return to normal, same focus */
EXPOSAY_TARGET_SWITCH, /* return to normal, switch focus */
};
enum exposay_layout_state {
EXPOSAY_LAYOUT_INACTIVE = 0, /* normal desktop */
EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE, /* in transition to normal */
EXPOSAY_LAYOUT_OVERVIEW, /* show all windows */
EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW, /* in transition to all windows */
};
struct exposay_output {
int num_surfaces;
int grid_size;
int surface_size;
int hpadding_outer;
int vpadding_outer;
int padding_inner;
};
struct exposay {
/* XXX: Make these exposay_surfaces. */
struct weston_view *focus_prev;
struct weston_view *focus_current;
struct weston_view *clicked;
struct workspace *workspace;
struct weston_seat *seat;
struct wl_list surface_list;
struct weston_keyboard_grab grab_kbd;
struct weston_pointer_grab grab_ptr;
enum exposay_target_state state_target;
enum exposay_layout_state state_cur;
int in_flight; /* number of animations still running */
int row_current;
int column_current;
struct exposay_output *cur_output;
bool mod_pressed;
bool mod_invalid;
};
struct focus_surface {
struct weston_curtain *curtain;
struct weston_surface *surface;
struct weston_view *view;
struct weston_transform workspace_transform;
};
struct workspace {
@ -230,17 +113,22 @@ struct workspace {
struct shell_output {
struct desktop_shell *shell;
struct weston_output *output;
struct exposay_output eoutput;
struct wl_listener destroy_listener;
struct wl_list link;
struct weston_surface *panel_surface;
struct weston_view *panel_view;
struct wl_listener panel_surface_listener;
struct weston_coord_global panel_offset;
struct weston_surface *background_surface;
struct weston_view *background_view;
struct wl_listener background_surface_listener;
struct {
struct weston_view *view;
struct weston_view_animation *animation;
enum fade_type type;
struct wl_event_source *startup_timer;
} fade;
};
struct weston_desktop;
@ -251,14 +139,12 @@ struct desktop_shell {
struct wl_listener idle_listener;
struct wl_listener wake_listener;
struct wl_listener sleep_listener;
struct wl_listener transform_listener;
struct wl_listener resized_listener;
struct wl_listener destroy_listener;
struct wl_listener show_input_panel_listener;
struct wl_listener hide_input_panel_listener;
struct wl_listener update_input_panel_listener;
struct wl_listener session_listener;
struct weston_layer fullscreen_layer;
struct weston_layer panel_layer;
@ -291,25 +177,33 @@ struct desktop_shell {
struct weston_surface *lock_surface;
struct wl_listener lock_surface_listener;
struct weston_view *lock_view;
struct workspace workspace;
struct {
struct wl_array array;
unsigned int current;
unsigned int num;
struct wl_list client_list;
struct weston_animation animation;
struct wl_list anim_sticky_list;
int anim_dir;
struct timespec anim_timestamp;
double anim_current;
struct workspace *anim_from;
struct workspace *anim_to;
} workspaces;
struct {
struct wl_resource *binding;
struct wl_list surfaces;
} input_panel;
struct {
struct weston_curtain *curtain;
struct weston_view_animation *animation;
enum fade_type type;
struct wl_event_source *startup_timer;
} fade;
struct exposay exposay;
bool allow_zap;
bool disallow_output_changed_move;
uint32_t binding_modifier;
uint32_t exposay_modifier;
enum animation_type win_animation_type;
enum animation_type win_close_animation_type;
enum animation_type startup_animation_type;
@ -321,8 +215,6 @@ struct desktop_shell {
struct wl_listener output_create_listener;
struct wl_listener output_move_listener;
struct wl_list output_list;
struct wl_list seat_list;
struct wl_list shsurf_list;
enum weston_desktop_shell_panel_position panel_position;
@ -343,19 +235,18 @@ get_shell_surface(struct weston_surface *surface);
struct workspace *
get_current_workspace(struct desktop_shell *shell);
void
get_output_work_area(struct desktop_shell *shell,
struct shell_output *output,
pixman_rectangle32_t *area);
void
lower_fullscreen_layer(struct desktop_shell *shell,
struct shell_output *lowering_output);
struct weston_output *lowering_output);
void
activate(struct desktop_shell *shell, struct weston_view *view,
struct weston_seat *seat, uint32_t flags);
void
exposay_binding(struct weston_keyboard *keyboard,
enum weston_keyboard_modifier modifier,
void *data);
int
input_panel_setup(struct desktop_shell *shell);
void
@ -368,5 +259,3 @@ void
shell_for_each_layer(struct desktop_shell *shell,
shell_for_each_layer_func_t func,
void *data);
#endif /* WESTON_DESKTOP_SHELL_H */

View file

@ -1,19 +0,0 @@
buffers {
size_kb: 16384
fill_policy: RING_BUFFER
}
data_sources {
config {
name: "track_event"
track_event_config {
enabled_categories: "mesa.default"
enabled_categories: "mesa.slow"
}
}
}
duration_ms: 5000
write_into_file: true
file_write_period_ms: 500
flush_period_ms: 500

View file

@ -50,7 +50,7 @@ SERIAL=$(udevadm info "$SYSPATH" --query=property | \
[ -z "$SERIAL" ] && exit 1
# You'd have this write a file instead.
echo "ACTION!=\"remove\",SUBSYSTEM==\"input\",ENV{ID_SERIAL}==\"$SERIAL\",ENV{LIBINPUT_CALIBRATION_MATRIX}=\"$MATRIX\""
echo "ACTION==\"add|change\",SUBSYSTEM==\"input\",ENV{ID_SERIAL}==\"$SERIAL\",ENV{LIBINPUT_CALIBRATION_MATRIX}=\"$MATRIX\""
# Then you'd tell udev to reload the rules:
#udevadm control --reload

View file

@ -1,103 +0,0 @@
#
# Copyright © 2019 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.
#
# Usage: source this script then 'display_flight_rec'
#
import gdb
class DisplayFlightRecorder(gdb.Command):
def __init__(self):
self.rb = ''
symbol_found = False
ring_buff = gdb.lookup_global_symbol("weston_primary_flight_recorder_ring_buffer")
if ring_buff == None:
print("'weston_ring_buffer' symbol not found!")
print("Either weston is too old or weston hasn't been loaded in memory")
else:
self.rb = ring_buff
self.display_rb_data(self.rb)
symbol_found = True
if symbol_found:
super(DisplayFlightRecorder, self).__init__("display_flight_rec",
gdb.COMMAND_DATA)
def display_rb_data(self, rb):
print("Flight recorder data found. Use 'display_flight_rec' "
"to display its contents")
# display this data (only) if symbol is not empty (happens if the program is not ran at all)
if rb.value():
print("Data at byte {append}, Size: {size}B, "
"Overlapped: {overlap}".format(append=rb.value()['append_pos'],
size=rb.value()['size'],
overlap=rb.value()['overlap']))
# poor's man fwrite()
def gen_contents(self, start, stop):
_str = ''
for j in range(start, stop):
_str += chr(self.rb.value()['buf'][j])
return _str
# mirrors C version, as to make sure we're not reading other parts...
def display_flight_rec_contents(self):
# symbol is there but not loaded, we're not far enough
if self.rb.value() == 0x0:
print("Flight recorder found, but not loaded yet!")
return
else:
print("Displaying flight recorder contents:")
append_pos = self.rb.value()['append_pos']
size = self.rb.value()['size']
overlap = self.rb.value()['overlap']
# if we haven't overflown and we're still at 0 means
# we still aren't far enough to be populated
if append_pos == 0 and not overlap:
print("Flight recorder doesn't have anything to display right now")
return
# now we can print stuff
rb_data = ''
if not overlap:
if append_pos:
rb_data = self.gen_contents(0, append_pos)
else:
rb_data = self.gen_contents(0, size)
else:
rb_data = self.gen_contents(append_pos, size)
rb_data += self.gen_contents(0, append_pos)
print("{data}".format(data=rb_data))
# called when invoking 'display_flight_rec'
def invoke(self, arg, from_tty):
self.display_flight_rec_contents()
DisplayFlightRecorder()

View file

@ -1,47 +0,0 @@
#!/bin/bash
# Copyright © 2024 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.
#
# Example usage:
# stream-pipewire.sh 62 192.168.50.56 5557
#
# Use remoting-client-receive.bash script on the remote side to
# display incoming content
if [ -z $1 -o -z $2 -o -z $3 ]; then
echo "stream-pipewire.sh pipewire_id host_ip host_port"
exit 127
fi
pipewire_id=$1
host=$2
port=$3
gst-launch-1.0 rtpbin name=rtpbin ! pipewiresrc path=${pipewire_id} ! videoconvert ! \
video/x-raw,format=I420 ! jpegenc ! rtpjpegpay ! \
rtpbin.send_rtp_sink_0 rtpbin.send_rtp_src_0 ! \
udpsink name=sink host=${host} port=$port rtpbin.send_rtcp_src_0 ! \
udpsink host=${host} port=$(($port + 1)) sync=false async=false udpsrc port=$(($port + 2)) ! \
rtpbin.recv_rtcp_sink_0

View file

@ -21,7 +21,7 @@ sys.path.append(os.path.abspath('sphinxext'))
# -- Project information -----------------------------------------------------
project = u'weston'
copyright = u'Weston community - @VERSION@'
copyright = u'2019, Weston community'
author = u'Weston community '
@ -71,7 +71,7 @@ master_doc = 'index'
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'en'
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
@ -84,9 +84,6 @@ pygments_style = None
# default domain
primary_domain = 'cpp'
# To automatically number figures, tables, etc. and be able to reference them.
numfig = True
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
@ -196,8 +193,7 @@ epub_exclude_files = ['search.html']
# -- Options for intersphinx extension ---------------------------------------
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'python': ('https://docs.python.org/3', None)}
intersphinx_mapping = {'https://docs.python.org/3': None}
# -- Options for todo extension ----------------------------------------------

View file

@ -243,6 +243,12 @@ ALIASES += "endrst=\endverbatim"
ALIASES += "rststar=\verbatim embed:rst:leading-asterisk"
ALIASES += "endrststar=\endverbatim"
# This tag can be used to specify a number of word-keyword mappings (TCL only).
# A mapping has the form "name=value". For example adding "class=itcl::class"
# will allow you to use the command class in the itcl::class meaning.
TCL_SUBST =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
@ -759,7 +765,7 @@ WARN_NO_PARAMDOC = NO
# a warning is encountered.
# The default value is: NO.
WARN_AS_ERROR = @MESON_WERROR@
WARN_AS_ERROR = YES
# The WARN_FORMAT tag determines the format of the warning messages that doxygen
# can produce. The string should contain the $file, $line, and $text tags, which
@ -787,10 +793,7 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = @SRC_ROOT@/libweston \
@SRC_ROOT@/include/libweston \
@SRC_ROOT@/shared/config-parser.c \
@SRC_ROOT@/tests
INPUT = @SRC_ROOT@/libweston @SRC_ROOT@/include/libweston
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@ -1081,6 +1084,13 @@ VERBATIM_HEADERS = YES
ALPHABETICAL_INDEX = YES
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
# which the alphabetical index list will be split.
# Minimum value: 1, maximum value: 20, default value: 5.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored
@ -1208,6 +1218,15 @@ HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this
# to YES can help to show when doxygen was last run and thus if the
# documentation is up to date.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = NO
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
# page has loaded.
@ -1478,6 +1497,17 @@ EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers.
#
# Note that when changing this option you need to delete any form_*.png files in
# the HTML output directory before the changes have effect.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_TRANSPARENT = YES
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# http://www.mathjax.org) which uses client side Javascript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
@ -1613,6 +1643,241 @@ EXTERNAL_SEARCH_ID =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
# The default value is: YES.
GENERATE_LATEX = NO
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: latex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked.
#
# Note that when enabling USE_PDFLATEX this option is only used for generating
# bitmaps for formulas in the HTML output, but not in the Makefile that is
# written to the output directory.
# The default file is: latex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
# index for LaTeX.
# The default file is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
# documents. This may be useful for small projects and may help to save some
# trees in general.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used by the
# printer.
# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
# 14 inches) and executive (7.25 x 10.5 inches).
# The default value is: a4.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PAPER_TYPE = a4
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. The package can be specified just
# by its name or with the correct syntax as to be used with the LaTeX
# \usepackage command. To get the times font for instance you can specify :
# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
# To use the option intlimits with the amsmath package you can specify:
# EXTRA_PACKAGES=[intlimits]{amsmath}
# If left blank no extra packages will be included.
# This tag requires that the tag GENERATE_LATEX is set to YES.
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
# generated LaTeX document. The header should contain everything until the first
# chapter. If it is left blank doxygen will generate a standard header. See
# section "Doxygen usage" for information on how to let doxygen write the
# default header to a separate file.
#
# Note: Only use a user-defined header if you know what you are doing! The
# following commands have a special meaning inside the header: $title,
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
# string, for the replacement values of the other commands the user is referred
# to HTML_HEADER.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
# generated LaTeX document. The footer should contain everything after the last
# chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
# special commands can be used inside the footer.
#
# Note: Only use a user-defined footer if you know what you are doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# LaTeX style sheets that are included after the standard style sheets created
# by doxygen. Using this option one can overrule certain style aspects. Doxygen
# will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list).
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_STYLESHEET =
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the LATEX_OUTPUT output
# directory. Note that the files will be copied as-is; there are no commands or
# markers available.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_FILES =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
# contain links (just like the HTML output) instead of page references. This
# makes the output suitable for online browsing using a PDF viewer.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PDF_HYPERLINKS = YES
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
# the PDF file directly from the LaTeX files. Set this option to YES, to get a
# higher quality PDF documentation.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = YES
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
# if errors occur, instead of asking the user for help. This option is also used
# when generating formulas in HTML.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BATCHMODE = NO
# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
# index chapters (such as File Index, Compound Index, etc.) in the output.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HIDE_INDICES = NO
# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
# code with syntax highlighting in the LaTeX output.
#
# Note that which sources are shown also depends on other settings such as
# SOURCE_BROWSER.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_SOURCE_CODE = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# The default value is: plain.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_TIMESTAMP = NO
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
# RTF output is optimized for Word 97 and may not look too pretty with other RTF
# readers/editors.
# The default value is: NO.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: rtf.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
# documents. This may be useful for small projects and may help to save some
# trees in general.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
# contain hyperlink fields. The RTF file will contain links (just like the HTML
# output) instead of page references. This makes the output suitable for online
# browsing using Word or some other Word compatible readers that support those
# fields.
#
# Note: WordPad (write) and others do not support links.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's config
# file, i.e. a series of assignments. You only have to provide replacements,
# missing definitions are set to their default value.
#
# See also section "Doxygen usage" for information on how to generate the
# default style sheet that doxygen normally uses.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an RTF document. Syntax is
# similar to doxygen's config file. A template extensions file can be generated
# using doxygen -e rtf extensionFile.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
# with syntax highlighting in the RTF output.
#
# Note that which sources are shown also depends on other settings such as
# SOURCE_BROWSER.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_SOURCE_CODE = NO
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
@ -1702,6 +1967,15 @@ GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
# program listings (including syntax highlighting and cross-referencing
# information) to the DOCBOOK output. Note that enabling this will significantly
# increase the size of the DOCBOOK output.
# The default value is: NO.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
@ -1809,7 +2083,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = WL_EXPORT= WL_PRINTF(x,y)= __attribute__(x)=
PREDEFINED = WL_EXPORT= WL_PRINTF(x,y)=
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
@ -1876,10 +2150,34 @@ EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of 'which perl').
# The default file (with absolute path) is: /usr/bin/perl.
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
# NO turns the diagrams off. Note that this option also works with HAVE_DOT
# disabled, but it is recommended to install and use dot, since it yields more
# powerful graphs.
# The default value is: YES.
CLASS_DIAGRAMS = YES
# You can define message sequence charts within doxygen comments using the \msc
# command. Doxygen will then run the mscgen tool (see:
# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
# documentation. The MSCGEN_PATH tag allows you to specify the directory where
# the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path.
MSCGEN_PATH =
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
@ -1900,7 +2198,7 @@ HIDE_UNDOC_RELATIONS = YES
# set to NO
# The default value is: YES.
HAVE_DOT = YES
HAVE_DOT = NO
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
# to run in parallel. When set to 0 doxygen will base this on the number of
@ -1912,6 +2210,23 @@ HAVE_DOT = YES
DOT_NUM_THREADS = 0
# When you want a differently looking font in the dot files that doxygen
# generates you can specify the font name using DOT_FONTNAME. You need to make
# sure dot is able to find the font, which can be done by putting it in a
# standard location or by setting the DOTFONTPATH environment variable or by
# setting DOT_FONTPATH to the directory containing the font.
# The default value is: Helvetica.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTNAME = Helvetica
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
# dot graphs.
# Minimum value: 4, maximum value: 24, default value: 10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTSIZE = 10
# By default doxygen will tell dot to use the default font as specified with
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
# the path where dot can find it using this tag.
@ -2112,7 +2427,7 @@ PLANTUML_INCLUDE_PATH =
# Minimum value: 0, maximum value: 10000, default value: 50.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_GRAPH_MAX_NODES = 250
DOT_GRAPH_MAX_NODES = 50
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
# generated by dot. A depth value of 3 means that only nodes reachable from the
@ -2126,6 +2441,18 @@ DOT_GRAPH_MAX_NODES = 250
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
# background. This is disabled by default, because dot on Windows does not seem
# to support this out of the box.
#
# Warning: Depending on the platform used, enabling this option may lead to
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
# read).
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_TRANSPARENT = YES
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support

View file

@ -5,25 +5,19 @@ Welcome to Weston documentation!
:maxdepth: 2
:caption: Contents:
toc/running-weston.rst
toc/libweston.rst
toc/test-suite.rst
toc/kiosk-shell.rst
toc/ivi-shell.rst
Weston
------
Weston is a Wayland compositor designed for correctness, reliability,
predictability, and performance.
Weston is the reference implementation of a Wayland compositor, as well as a
useful environment in and of itself.
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 :ref:`libweston-label` which allows
users to build their own custom full-featured environments on top of
Weston's core.
industrial, kiosks, set-top boxes and TVs. It also provides a library allowing
other projects to build their own full-featured environments on top of Weston's
core.
The core focus of Weston is correctness and reliability. Weston aims to be lean
and fast, but more importantly, to be predictable. Whilst Weston does have

View file

@ -1,11 +1,10 @@
sphinx = find_program('sphinx-build', required: true)
doxygen = find_program('doxygen', required: true)
dot = find_program('dot', required: true)
breathe = find_program('breathe-apidoc', required: true)
sphinx_c = run_command(sphinx, '--version', check: true)
breathe_c = run_command(breathe, '--version', check: true)
doxygen_c = run_command(doxygen, '--version', check: true)
sphinx_c = run_command(sphinx, '--version')
breathe_c = run_command(breathe, '--version')
doxygen_c = run_command(doxygen, '--version')
sphinx_v = sphinx_c.stdout().split(' ')[1].strip()
breathe_v = breathe_c.stdout().split(' ')[2].strip()
@ -37,9 +36,8 @@ sphinx_conf = configure_file(
)
doxy_conf_data = configuration_data()
doxy_conf_data.set('SRC_ROOT', meson.project_source_root())
doxy_conf_data.set('SRC_ROOT', meson.source_root())
doxy_conf_data.set('OUTPUT_DIR', doxygen_database)
doxy_conf_data.set('MESON_WERROR', get_option('werror') == true ? 'YES' : 'NO')
doxygen_conf_weston = configure_file(
input: 'doxygen.ini.in',
output: 'doxygen.ini',
@ -48,15 +46,10 @@ doxygen_conf_weston = configure_file(
script_data = configuration_data()
script_data.set('SRCDIR', meson.current_build_dir())
script_data.set('OUTDIR', meson.current_build_dir() / 'weston')
# Set a different directory for doctrees to avoid installing them
script_data.set('DOCTREES_DIR', meson.current_build_dir() + '/doctrees')
script_data.set('OUTDIR', meson.current_build_dir() + '/doc')
script_data.set('DOXYGEN_CONF', meson.current_build_dir() + '/doxygen.ini')
script_data.set('DOXYGEN_CMD', doxygen.full_path())
script_data.set('MESON_WERROR', get_option('werror') == true ? 'YES' : 'NO')
script_data.set('SPHINX_CMD', sphinx.full_path())
script_data.set('DOXYGEN_CMD', doxygen.path())
script_data.set('SPHINX_CMD', sphinx.path())
script_doxy_sphinx = configure_file(
input: 'run_doxygen_sphinx.sh.in',
output: 'run_doxygen_sphinx.sh',
@ -74,19 +67,11 @@ endforeach
# and those in toc
subdir('toc')
sphinx_env = []
if doxygen_v.version_compare('< 1.9.7')
sphinx_env += 'CAIRO_DEBUG_PDF=1'
endif
sphinx_doc = custom_target(
'weston-doc-breathe',
command: script_doxy_sphinx,
output: 'weston',
output: 'doc',
build_by_default: true,
env: sphinx_env,
install: true,
install_dir: dir_data / 'doc',
)
# we need this because we will have a stale 'doc' directory
@ -95,3 +80,9 @@ docs = run_target(
'docs',
command: script_doxy_sphinx,
)
install_subdir(
sphinx_doc.full_path(),
install_dir: join_paths(dir_data, 'doc', 'weston'),
strip_directory: true,
)

View file

@ -1,20 +1,2 @@
#!/bin/sh
if [ "@MESON_WERROR@" = "YES" ]; then
SPHINX_WERROR="-W"
else
SPHINX_WERROR=""
fi
set -e
BUILDINFO_ORIG="@OUTDIR@/.buildinfo"
BUILDINFO_SAVE="@SRCDIR@/buildinfo.save"
[ -f "$BUILDINFO_SAVE" ] && mv -f "$BUILDINFO_SAVE" "$BUILDINFO_ORIG"
@DOXYGEN_CMD@ @DOXYGEN_CONF@
@SPHINX_CMD@ $SPHINX_WERROR -E -q -j auto -d @DOCTREES_DIR@ @SRCDIR@ @OUTDIR@
mv -f "$BUILDINFO_ORIG" "$BUILDINFO_SAVE"
@DOXYGEN_CMD@ @DOXYGEN_CONF@ && @SPHINX_CMD@ -E -W -q -j auto @SRCDIR@ @OUTDIR@

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

View file

@ -1,9 +0,0 @@
# Sphinx does not know look for these files in the source directory, so
# they must be copied to the build directory.
files = [
'ivi-shell.png',
]
foreach file : files
configure_file(input: file, output: file, copy: true)
endforeach

View file

@ -1,112 +0,0 @@
Weston IVI-shell
================
Weston's IVI-shell is a highly customizable shell targeted at use cases which
need custom control over the shell's window layout with one or more applications
without interactive configuration of the layout by the user.
Example use cases for the IVI-shell are IVI applications or industrial human
machine interfaces. In general, whenever the user interface requires the exact
positioning of multiple application surfaces on one or more screens.
The IVI-shell also provides a means for applications to identify themselves to
the shell by application IDs via the ivi_application Wayland protocol.
IVI-shell client protocol
-------------------------
Wayland clients can implement the ``ivi_application`` Wayland protocol, which
allows them to specify an ``ivi_id`` to allow the IVI controller to identify the
application. This allows the controller to implement special behavior for
well-known applications.
The IVI-shell is able to also handle clients that use the ``xdg-shell``
protocol, but in these cases the IVI-shell needs other means to identify client
applications.
See ``ivi-application.xml`` for the protocol specification.
IVI-shell Weston modules
------------------------
The IVI-shell consists of two main components: The ``ivi-shell.so`` and custom
IVI controller (with the ``hmi-controller.so`` example implementation).
The ``ivi-shell.so`` is responsible for handling the application IDs and for
providing abstractions to configure the window layout via the
``ivi_layout_interface``. This interface is discussed in `IVI-shell compositor
implementation`.
The IVI controller uses the ``ivi_layout_interface`` to implement a window
manager and is responsible for configuring the window layout, i.e. the position
of the applications on the screens.
Due to this separation, both modules must be loaded in your ``weston.ini`` to
use the IVI-shell.
.. code-block:: ini
[core]
shell=ivi-shell.so
modules=hmi-controller.so
If you are using your custom controller, replace ``hmi-controller.so`` with the
name of your own controller module.
.. figure:: images/ivi-shell.png
:alt: IVI-shell architecture overview
Controlling the IVI-shell
-------------------------
The IVI-shell provides the ``ivi_layout_interface`` API that a controller must
use to control the window layout of the IVI-shell. See
``ivi-shell/ivi-layout-export.h`` for the definition of this API.
For the initial configuration, the controller has to create at least one
``ivi_layout_layer`` and add the ``ivi_layout_layer`` to a ``weston_output``.
The layers allow to group multiple applications surfaces and control them
together and are the main mechanism to group and organize surfaces. These are
always necessary to show something using the IVI-shell. The IVI-shell will
internally create an ``ivi_layout_screen``, but a controller always uses the
``weston_output`` directly.
To get control over the client surfaces, the controller must use notifiers that
trigger whenever there are changes to the client surfaces. The client surfaces
then show up as ``ivi_layout_surface``. These have an ID, which allows the
controller to identify the surface and reconfigure the window layout
accordingly.
The controller must add the ``ivi_layout_surface`` to an ``ivi_layout_layer``
and configure it's position and z-order wrt. the other surfaces in the layer.
Otherwise, the newly added surface will not show up on the screen.
The IVI-shell will internally create an ``ivi_layout_view`` for each layer that
the surface was added to. However, the views are not provided to the IVI
controller.
After configuring all expected changes, the controller must call the
``commit_changes`` to atomically update the display layout and call
``screen_ready`` to inform the compositor that it can start issueing repaints.
IVI-shell example implementation
--------------------------------
The IVI-shell comes with an example implementation of an IVI controller -- the
`hmi-controller`. The hmi-controller will usually replaced by a custom
implementation that implements the use-case-specific behavior.
The hmi-controller is split into two parts:
The ``hmi-controller.so`` is a Weston Plugin that uses the
``ivi_layout_interface`` to perform the window manager tasks. It allows some
reconfiguration of the window layout via the ``ivi_hmi_controller`` protocol.
Other implementations may keep all window management inside the module or may
expose even more window management via a custom protocol to an external process.
The ``weston-ivi-shell-user-interface`` is an example hmi-controller helper
client that serves as a user interface for controlling the hmi-controller.
The hmi-controller can be customized using the ``[ivi-shell]`` section in the
``weston.ini``. An example configuration will be generated in
``<build_dir>/ivi-shell/weston.ini``.

View file

@ -1,24 +0,0 @@
Weston kiosk-shell
==================
Weston's kiosk-shell is a simple shell targeted at single-app/kiosk use cases.
It makes all top-level application windows fullscreen, and supports defining
which applications to place on particular outputs. This is achieved with the
``app-ids=`` field in the corresponding output section in weston.ini. For
example:
.. code-block:: ini
[output]
name=screen0
app-ids=org.domain.app1,com.domain.app2
x11-wm-name=xterm,Mozilla Firefox
x11-wm-class=Navigator
Xwayland windows can be specified either using ``x11-wm-name=``, which matches
the ``WM_NAME`` X11 property and ``x11-wm-class=``, which matches `WM_CLASS`
one. If the Xwayland window has both a ``WM_CLASS`` or a ``WM_NAME`` set, then
it will be checked in both ``x11-wm-name`` and in ``x11-wm-class`` list entry.
To run weston with kiosk-shell set ``shell=kiosk-shell.so`` in weston.ini, or
use the ``--shell=kiosk-shell.so`` command-line option.

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