diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..e0fc85315 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = tab +indent_size = 8 +max_line_length = 80 + +[*.xml] +indent_style = space +indent_size = 2 +tab_width = 8 diff --git a/.gitignore b/.gitignore index ac76fd9f2..26d9ae9ed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,112 +1,12 @@ -*.announce -*.deps -*.jpg -*.la -*.lo -*.log -*.o -*.pc -*.sig -*.so *.swp .*.sw? .sw? *.sublime-project *.sublime-workspace -*.tar.xz -*.trs *~ ctags cscope.out -.libs -.dirstamp -/aclocal.m4 -/autom4te.cache -/build-aux/ -/config.guess -/config.h -/config.h.in -/config.log -/config.mk -/config.status -/config.sub -/configure -/depcomp -/doc/doxygen/*.doxygen -/docs/developer -/docs/tools -/install-sh -/libtool -/ltmain.sh -/logs -/missing -/stamp-h1 -/test-driver -/weston.ini -Makefile -Makefile.in TAGS -protocol/.*.valid -protocol/*.[ch] 00*.patch - -weston-calibrator -weston-clickdot -weston-cliptest -weston-confine -weston-dnd -weston-editor -weston-eventdemo -weston-flower -weston-fullscreen -weston-gears -weston-image -weston-nested -weston-nested-client -weston-presentation-shm -weston-resizor -weston-scaler -weston-simple-dmabuf-intel -weston-simple-dmabuf-v4l -weston-simple-egl -weston-simple-shm -weston-simple-touch -weston-simple-damage -weston-smoke -weston-stacking -weston-subsurfaces -weston-transformed -weston-view - -weston-keyboard -libtoytoolkit.a -weston-desktop-shell -weston-ivi-shell-user-interface -weston-info -weston-screenshooter -weston-tablet-shell -weston-terminal -weston-multi-resource -weston-simple-im -weston -weston-launch -spring-tool - -*.weston -*.test -*.ivi -wcap-decode -matrix-test -setbacklight -weston.1 -weston-drm.7 -weston.ini.5 - -/libweston/git-version.h -/libweston/version.h - -/tests/weston-ivi.ini -internal-screenshot-00.png - -/zuctest +build/* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..9c24dd80f --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,111 @@ +# vim: set expandtab shiftwidth=2 tabstop=8 textwidth=0: + +variables: + FDO_UPSTREAM_REPO: wayland/weston + + +include: + - project: 'freedesktop/ci-templates' + ref: 59de540b620c45739871d1a073d76d5521989d11 + file: '/templates/debian.yml' + + +stages: + - container_prep + - build + - pages + + +.debian: + variables: + FDO_DISTRIBUTION_VERSION: buster + FDO_DISTRIBUTION_EXEC: 'bash .gitlab-ci/debian-install.sh' + FDO_DISTRIBUTION_TAG: '2020-06-24.0' + + +container_prep: + extends: + - .debian + - .fdo.container-build@debian + stage: container_prep + + +.build-native: + extends: + - .debian + - .fdo.distribution-image@debian + stage: build + before_script: + - 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 BUILD_ID="weston-$CI_JOB_NAME" + - export PREFIX="$(pwd)/prefix-$BUILD_ID" + - export BUILDDIR="$(pwd)/build-$BUILD_ID" + - export TESTS_RES_PATH="$BUILDDIR/tests-res.txt" + - mkdir "$BUILDDIR" "$PREFIX" + +.build-native-meson: + extends: .build-native + tags: + - kvm + script: + - export PATH=~/.local/bin:$PATH + - cd "$BUILDDIR" + - meson --prefix="$PREFIX" ${MESON_OPTIONS} .. + - ninja -k0 + - ninja install + - virtme-run --rw --pwd --kimg /weston-virtme/bzImage --script-dir ../.gitlab-ci/virtme-scripts + - TEST_RES=$(cat $TESTS_RES_PATH) + - rm $TESTS_RES_PATH + - ninja clean + - cp -R /weston-virtme ./ + - rm weston-virtme/bzImage + - exit $TEST_RES + artifacts: + name: weston-$CI_COMMIT_SHA + when: always + paths: + - build-*/meson-logs + - build-*/weston-virtme + - prefix-* + +build-native-meson-default-options: + variables: + MESON_OPTIONS: > + -Dwerror=true + -Ddoc=true + extends: .build-native-meson + +build-native-meson-no-gl-renderer: + variables: + MESON_OPTIONS: > + -Dsimple-clients=damage,im,shm,touch,dmabuf-v4l + -Drenderer-gl=false + -Dremoting=false + -Dpipewire=false + -Dwerror=true + extends: .build-native-meson + +pages: + stage: pages + dependencies: + - build-native-meson-default-options + script: + - export PREFIX=$(pwd)/prefix-weston-build-native-meson-default-options + - mkdir public + - cp -R $PREFIX/share/doc/weston/* public/ + artifacts: + paths: + - public + only: + - master diff --git a/.gitlab-ci/debian-install.sh b/.gitlab-ci/debian-install.sh new file mode 100644 index 000000000..12fb4e3f1 --- /dev/null +++ b/.gitlab-ci/debian-install.sh @@ -0,0 +1,150 @@ +#!/bin/bash + +set -o xtrace -o errexit + +# These get temporary installed for building Linux and then force-removed. +LINUX_DEV_PKGS=" + bc + bison + flex + libelf-dev +" + +# These get temporary installed for building Mesa and then force-removed. +MESA_DEV_PKGS=" + bison + flex + gettext + libwayland-egl-backend-dev + libxrandr-dev + llvm-8-dev + python-mako + python3-mako + wayland-protocols +" + +# Needed for running the custom-built mesa +MESA_RUNTIME_PKGS=" + libllvm8 +" + +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 \ + curl \ + doxygen \ + freerdp2-dev \ + git \ + libcairo2-dev \ + libcolord-dev \ + libdbus-1-dev \ + libegl1-mesa-dev \ + libevdev-dev \ + libexpat1-dev \ + libffi-dev \ + libgbm-dev \ + libgdk-pixbuf2.0-dev \ + libgles2-mesa-dev \ + libglu1-mesa-dev \ + libgstreamer1.0-dev \ + libgstreamer-plugins-base1.0-dev \ + libinput-dev \ + libjpeg-dev \ + libjpeg-dev \ + liblcms2-dev \ + libmtdev-dev \ + libpam0g-dev \ + libpango1.0-dev \ + libpipewire-0.2-dev \ + libpixman-1-dev \ + libpng-dev \ + libsystemd-dev \ + libtool \ + libudev-dev \ + libva-dev \ + libvpx-dev \ + libwayland-dev \ + libwebp-dev \ + libx11-dev \ + libx11-xcb-dev \ + libxcb1-dev \ + libxcb-composite0-dev \ + libxcb-xfixes0-dev \ + libxcb-xkb-dev \ + libxcursor-dev \ + libxkbcommon-dev \ + libxml2-dev \ + mesa-common-dev \ + ninja-build \ + pkg-config \ + python3-pip \ + python3-setuptools \ + qemu-system \ + sysvinit-core \ + xwayland \ + $MESA_RUNTIME_PKGS + + +pip3 install --user git+https://github.com/mesonbuild/meson.git@0.49 +export PATH=$HOME/.local/bin:$PATH +# 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 + +apt-get -y --no-install-recommends install $LINUX_DEV_PKGS +git clone --depth=1 --branch=drm-next-2020-06-11-1 https://anongit.freedesktop.org/git/drm/drm.git linux +cd linux +make x86_64_defconfig +make kvmconfig +./scripts/config --enable CONFIG_DRM_VKMS +make oldconfig +make -j8 +cd .. +mkdir /weston-virtme +mv linux/arch/x86/boot/bzImage /weston-virtme/bzImage +mv linux/.config /weston-virtme/.config +rm -rf linux + +# Link to upstream virtme: https://github.com/amluto/virtme +# +# The reason why we are using a fork here is that it adds a patch to have the +# --script-dir command line option. With that we can run scripts that are in a +# certain folder when virtme starts, which is necessary in our use case. +# +# The upstream also has some commands that could help us to reach the same +# results: --script-sh and --script-exec. Unfornutately they are not completely +# implemented yet, so we had some trouble to use them and it was becoming +# hackery. +# +git clone https://github.com/ezequielgarcia/virtme +cd virtme +git checkout -b snapshot 69e3cb83b3405edc99fcf9611f50012a4f210f78 +./setup.py install +cd .. + +git clone --branch 1.17.0 --depth=1 https://gitlab.freedesktop.org/wayland/wayland +export MAKEFLAGS="-j4" +cd wayland +git show -s HEAD +mkdir build +cd build +../autogen.sh --disable-documentation +make install +cd ../../ + +apt-get -y --no-install-recommends install $MESA_DEV_PKGS +git clone --single-branch --branch master --shallow-since='2020-02-15' https://gitlab.freedesktop.org/mesa/mesa.git mesa +cd mesa +git checkout -b snapshot c7617d8908a970124321ce731b43d5996c3c5775 +meson build -Dauto_features=disabled \ + -Dgallium-drivers=swrast -Dvulkan-drivers= -Ddri-drivers= +ninja -C build install +cd .. +rm -rf mesa + +apt-get -y --autoremove purge $LINUX_DEV_PKGS +apt-get -y --autoremove purge $MESA_DEV_PKGS \ No newline at end of file diff --git a/.gitlab-ci/virtme-scripts/run-weston-tests.sh b/.gitlab-ci/virtme-scripts/run-weston-tests.sh new file mode 100755 index 000000000..bbcebfd7b --- /dev/null +++ b/.gitlab-ci/virtme-scripts/run-weston-tests.sh @@ -0,0 +1,30 @@ +#!/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 WESTON_TEST_SUITE_DRM_DEVICE=card0 + +# 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). +# 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 + +# run the tests and save the exit status +ninja test +TEST_RES=$? + +# 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 + +# shutdown virtme +sync +poweroff -f diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..98fa565d4 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,372 @@ +Contributing to Weston +======================= + +Finding something to work on +---------------------------- + +Weston's development is [tracked on GitLab](https://gitlab.freedesktop.org/wayland/weston). +In addition to reviewing code submissions (see below), we use the issue tracker +to discuss both bugfixes and development of new features. + +The '[good for new contributors](https://gitlab.freedesktop.org/wayland/weston/issues?label_name%5B%5D=Good+for+new+contributors)' +label is used for issues the development team thinks are a good place to begin +working on Weston. These issues cover features or bugfixes which are small, +self-contained, don't require much specific background knowledge, and aren't +blocked by more complex work. + +If you have picked an issue you would like to work on, you may want to mention +in the issue tracker that you would like to pick it up. You can also discuss +it with the developers in the issue tracker, or on the +[mailing list](https://lists.freedesktop.org/mailman/listinfo/wayland-devel). +Many developers also use IRC through [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 +--------------------------------- + +Unlike many projects using GitHub and GitLab, Weston has a +[linear, 'recipe' style history](http://www.bitsnbites.eu/git-history-work-log-vs-recipe/). +This means that every commit should be small, digestible, stand-alone, and +functional. Rather than a purely chronological commit history like this: + + doc: final docs for view transforms + fix tests when disabled, redo broken doc formatting + better transformed-view iteration (thanks Hannah!) + try to catch more cases in tests + tests: add new spline test + fix compilation on splines + doc: notes on reticulating splines + compositor: add spline reticulation for view transforms + +we aim to have a clean history which only reflects the final state, broken up +into functional groupings: + + compositor: add spline reticulation for view transforms + compositor: new iterator for view transforms + tests: add view-transform correctness tests + doc: fix Doxygen formatting for view transforms + +This ensures that the final patch series only contains the final state, +without the changes and missteps taken along the development process. + +The first line of a commit message should contain a prefix indicating +what part is affected by the patch followed by one sentence that +describes the change. For examples: + + compositor-drm: Support modifiers for drm_fb + +and + + input: do not forward unmatched touch-ups + +If in doubt what prefix to use, look at other commits that change the +same file(s) as the patch being sent. + +The body of the commit message should describe what the patch changes +and why, and also note any particular side effects. This shouldn't be +empty on most of the cases. It shouldn't take a lot of effort to write +a commit message for an obvious change, so an empty commit message +body is only acceptable if the questions "What?" and "Why?" are already +answered on the one-line summary. + +The lines of the commit message should have at most 76 characters, to +cope with the way git log presents them. + +See [notes on commit messages] for a recommended reading on writing commit +messages. + +Your patches 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 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. + +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 +merge request. If you have already received Reviewed-by or Acked-by tags, you +should evaluate whether they still apply and include them in the respective +commit messages. Otherwise the tags may be lost, reviewers miss the credit they +deserve, and the patches may cause redundant review effort. + + +Tracking patches and following up +--------------------------------- + +Once submitted to GitLab, your patches will be reviewed by the Weston +development team on GitLab. Review may be entirely positive and result in your +code landing instantly, in which case, great! You're done. However, we may ask +you to make some revisions: fixing some bugs we've noticed, working to a +slightly different design, or adding documentation and tests. + +If you do get asked to revise the patches, please bear in mind the notes above. +You should use `git rebase -i` to make revisions, so that your patches follow +the clear linear split documented above. Following that split makes it easier +for reviewers to understand your work, and to verify that the code you're +submitting is correct. + +A common request is to split single large patch into multiple patches. This can +happen, for example, if when adding a new feature you notice a bug in Weston's +core which you need to fix to progress. Separating these changes into separate +commits will allow us to verify and land the bugfix quickly, pushing part of +your work for the good of everyone, whilst revision and discussion continues on +the larger feature part. It also allows us to direct you towards reviewers who +best understand the different areas you are working on. + +When you have made any requested changes, please rebase the commits, verify +that they still individually look good, then force-push your new branch to +GitLab. This will update the merge request and notify everyone subscribed to +your merge request, so they can review it again. + +There are also +[many GitLab CLI clients](https://about.gitlab.com/applications/#cli-clients), +if you prefer to avoid the web interface. It may be difficult to follow review +comments without using the web interface though, so we do recommend using this +to go through the review process, even if you use other clients to track the +list of available patches. + + +Coding style +------------ + +You should follow the style of the file you're editing. In general, we +try to follow the rules below. + +**Note: this file uses spaces due to markdown rendering issues for tabs. + Code must be indented using tabs.** + +- indent with tabs, and a tab is always 8 characters wide +- opening braces are on the same line as the if statement; +- no braces in an if-body with just one statement; +- if one of the branches of an if-else condition has braces, then the + other branch should also have braces; +- there is always an empty line between variable declarations and the + code; + +```c +static int +my_function(void) +{ + int a = 0; + + if (a) + b(); + else + c(); + + if (a) { + b(); + c(); + } else { + d(); + } +} +``` + +- lines should be less than 80 characters wide; +- when breaking lines with functions calls, the parameters are aligned + with the opening parentheses; +- when assigning a variable with the result of a function call, if the + line would be longer we break it around the equal '=' sign if it makes + sense; + +```c + long_variable_name = + function_with_a_really_long_name(parameter1, parameter2, + parameter3, parameter4); + + x = function_with_a_really_long_name(parameter1, parameter2, + parameter3, parameter4); +``` + +Conduct +======= + +As a freedesktop.org project, Wayland follows the Contributor Covenant, +found at: +https://www.freedesktop.org/wiki/CodeOfConduct + +Please conduct yourself in a respectful and civilised manner when +interacting with community members on mailing lists, IRC, or bug +trackers. The community represents the project as a whole, and abusive +or bullying behaviour is not tolerated by the project. + + +Licensing +========= + +Weston is licensed with the intention to be usable anywhere X.org is. +Originally, X.org was covered under the MIT X11 license, but changed to +the MIT Expat license. Similarly, Weston was covered initially as MIT +X11 licensed, but changed to the MIT Expat license, following in X.org's +footsteps. Other than wording, the two licenses are substantially the +same, with the exception of a no-advertising clause in X11 not included +in Expat. + +New source code files should specify the MIT Expat license in their +boilerplate, as part of the copyright statement. + + +Review +====== + +All patches, even trivial ones, require at least one positive review +(Reviewed-by). Additionally, if no Reviewed-by's have been given by +people with commit access, there needs to be at least one Acked-by from +someone with commit access. A person with commit access is expected to be +able to evaluate the patch with respect to the project scope and architecture. + +The below review guidelines are intended to be interpreted in spirit, not by +the letter. There may be circumstances where some guidelines are better +ignored. We rely very much on the judgement of reviewers and commit rights +holders. + +During review, the following matters should be checked: + +- The commit message explains why the change is being made. + +- The code fits the project's scope. + +- The code license is the same MIT licence the project generally uses. + +- Stable ABI or API is not broken. + +- Stable ABI or API additions must be justified by actual use cases, not only +by speculation. They must also be documented, and it is strongly recommended to +include tests exercising the additions in the test suite. + +- The code fits the existing software architecture, e.g. no layering +violations. + +- The code is correct and does not introduce new failures for existing users, +does not add new corner-case bugs, and does not introduce new compiler +warnings. + +- The patch does what it says in the commit message and changes nothing else. + +- The patch is a single logical change. If the commit message addresses +multiple points, it is a hint that the commit might need splitting up. + +- A bug fix should target the underlying root cause instead of hiding symptoms. +If a complete fix is not practical, partial fixes are acceptable if they come +with code comments and filed Gitlab issues for the remaining bugs. + +- The bug root cause rule applies to external software components as well, e.g. +do not work around kernel driver issues in userspace. + +- The test suite passes. + +- The code does not depend on API or ABI which has no working free open source +implementation. + +- The code is not dead or untestable. E.g. if there are no free open source +software users for it then it is effectively dead code. + +- The code is written to be easy to understand, or if code cannot be clear +enough on its own there are code comments to explain it. + +- The code is minimal, i.e. prefer refactor and re-use when possible unless +clarity suffers. + +- The code adheres to the style guidelines. + +- In a patch series, every intermediate step adheres to the above guidelines. + + +Commit rights +============= + +Commit rights will be granted to anyone who requests them and fulfills the +below criteria: + +- Submitted some (10 as a rule of thumb) non-trivial (not just simple + spelling fixes and whitespace adjustment) patches that have been merged + already. + +- Are actively participating in public discussions about their work (on the + mailing list or IRC). This should not be interpreted as a requirement to + review other peoples patches but just make sure that patch submission isn't + one-way communication. Cross-review is still highly encouraged. + +- Will be regularly contributing further patches. This includes regular + contributors to other parts of the open source graphics stack who only + do the occasional development in this project. + +- Agrees to use their commit rights in accordance with the documented merge + criteria, tools, and processes. + +To apply for commit rights, create a new issue in gitlab for the respective +project and give it the "accounts" label. + +Committers are encouraged to request their commit rights get removed when they +no longer contribute to the project. Commit rights will be reinstated when they +come back to the project. + +Maintainers and committers should encourage contributors to request commit +rights, especially junior contributors tend to underestimate their skills. + + +Stabilising for releases +======================== + +A release cycle ends with a stable release which also starts a new cycle and +lifts any code freezes. Gradual code freezing towards a stable release starts +with an alpha release. The release stages of a cycle are: + +- **Alpha release**: + Signified by version number #.#.91. + Major features must have landed before this. Major features include + invasive code motion and refactoring, high risk changes, and new stable + library ABI. + +- **Beta release**: + Signified by version number #.#.92. + Minor features must have landed before this. Minor features include all + new features that are not major, low risk changes, clean-ups, and + documentation. Stable ABI that was new in the alpha release can be removed + before a beta release if necessary. + +- **Release candidates (RC)**: + Signified by version number #.#.93 and up to #.#.99. + Bug fixes that are not release critical must have landed before this. + Release critical bug fixes can still be landed after this, but they may + call for another RC. + +- **Stable release**: + Signified by version number #.#.0. + Ideally no changes since the last RC. + +Mind that version #.#.90 is never released. It is used during development when +no code freeze is in effect. Stable branches and point releases are not covered +by the above. + + +[git documentation]: http://git-scm.com/documentation +[notes on commit messages]: http://who-t.blogspot.de/2009/12/on-commit-messages.html diff --git a/DCO-1.1.txt b/DCO-1.1.txt new file mode 100644 index 000000000..8201f9921 --- /dev/null +++ b/DCO-1.1.txt @@ -0,0 +1,37 @@ +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +1 Letterman Drive +Suite D4700 +San Francisco, CA, 94129 + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index cdf82ab43..000000000 --- a/Makefile.am +++ /dev/null @@ -1,1603 +0,0 @@ -ACLOCAL_AMFLAGS = -I m4 - -bin_PROGRAMS = -noinst_PROGRAMS = -libexec_PROGRAMS = -moduledir = $(libdir)/weston -module_LTLIBRARIES = -libweston_moduledir = $(libdir)/libweston-$(LIBWESTON_MAJOR) -libweston_module_LTLIBRARIES = -noinst_LTLIBRARIES = -BUILT_SOURCES = - -AM_DISTCHECK_CONFIGURE_FLAGS = --disable-setuid-install - -EXTRA_DIST = weston.ini.in ivi-shell/weston.ini.in - -weston.ini : $(srcdir)/weston.ini.in - $(AM_V_GEN)$(SED) \ - -e 's|@bindir[@]|$(bindir)|g' \ - -e 's|@abs_top_builddir[@]|$(abs_top_builddir)|g' \ - -e 's|@libexecdir[@]|$(libexecdir)|g' \ - $< > $@ - -ivi-shell/weston.ini : $(srcdir)/ivi-shell/weston.ini.in - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(SED) \ - -e 's|@bindir[@]|$(bindir)|g' \ - -e 's|@abs_top_builddir[@]|$(abs_top_builddir)|g' \ - -e 's|@abs_top_srcdir[@]|$(abs_top_srcdir)|g' \ - -e 's|@libexecdir[@]|$(libexecdir)|g' \ - -e 's|@plugin_prefix[@]||g' \ - $< > $@ - -tests/weston-ivi.ini : $(srcdir)/ivi-shell/weston.ini.in - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(SED) \ - -e 's|@bindir[@]|$(bindir)|g' \ - -e 's|@abs_top_builddir[@]|$(abs_top_builddir)|g' \ - -e 's|@abs_top_srcdir[@]|$(abs_top_srcdir)|g' \ - -e 's|@libexecdir[@]|$(abs_builddir)|g' \ - -e 's|@plugin_prefix[@]|$(abs_top_builddir)/.libs/|g' \ - $< > $@ - -all-local : weston.ini ivi-shell/weston.ini - -AM_CFLAGS = $(GCC_CFLAGS) - -AM_CPPFLAGS = \ - -I$(top_builddir)/libweston \ - -I$(top_srcdir)/libweston \ - -I$(top_builddir)/clients \ - -I$(top_builddir)/tests \ - -I$(top_srcdir)/shared \ - -I$(top_builddir)/protocol \ - -DDATADIR='"$(datadir)"' \ - -DLIBWESTON_MODULEDIR='"$(libweston_moduledir)"' \ - -DLIBEXECDIR='"$(libexecdir)"' \ - -DBINDIR='"$(bindir)"' - -CLEANFILES = weston.ini \ - ivi-shell/weston.ini \ - tests/weston-ivi.ini \ - internal-screenshot-00.png \ - $(BUILT_SOURCES) - -# Libtool race fix -# libweston-desktop depends on libweston, and desktop-shell depends on both. -# This leads to a libtool race at installation, because libtool re-links -# everything. -# If you add more fixes, you may need a workaround to keep automake generated -# targets. See http://debbugs.gnu.org/cgi/bugreport.cgi?bug=7328 -install-libweston_moduleLTLIBRARIES install-moduleLTLIBRARIES: install-libLTLIBRARIES - -lib_LTLIBRARIES = libweston-@LIBWESTON_MAJOR@.la -libweston_@LIBWESTON_MAJOR@_la_CPPFLAGS = $(AM_CPPFLAGS) -DIN_WESTON -libweston_@LIBWESTON_MAJOR@_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) $(LIBUNWIND_CFLAGS) -libweston_@LIBWESTON_MAJOR@_la_LIBADD = $(COMPOSITOR_LIBS) $(LIBUNWIND_LIBS) \ - $(DLOPEN_LIBS) -lm $(CLOCK_GETTIME_LIBS) \ - $(LIBINPUT_BACKEND_LIBS) libshared.la -libweston_@LIBWESTON_MAJOR@_la_LDFLAGS = -version-info $(LT_VERSION_INFO) - -libweston_@LIBWESTON_MAJOR@_la_SOURCES = \ - libweston/git-version.h \ - libweston/log.c \ - libweston/compositor.c \ - libweston/compositor.h \ - libweston/compositor-drm.h \ - libweston/compositor-fbdev.h \ - libweston/compositor-headless.h \ - libweston/compositor-rdp.h \ - libweston/compositor-wayland.h \ - libweston/compositor-x11.h \ - libweston/input.c \ - libweston/data-device.c \ - libweston/screenshooter.c \ - libweston/clipboard.c \ - libweston/zoom.c \ - libweston/bindings.c \ - libweston/animation.c \ - libweston/noop-renderer.c \ - libweston/pixman-renderer.c \ - libweston/pixman-renderer.h \ - libweston/plugin-registry.c \ - libweston/plugin-registry.h \ - libweston/timeline.c \ - libweston/timeline.h \ - libweston/timeline-object.h \ - libweston/linux-dmabuf.c \ - libweston/linux-dmabuf.h \ - shared/helpers.h \ - shared/matrix.c \ - shared/matrix.h \ - shared/timespec-util.h \ - shared/zalloc.h \ - shared/platform.h \ - shared/weston-egl-ext.h - -lib_LTLIBRARIES += libweston-desktop-@LIBWESTON_MAJOR@.la -libweston_desktop_@LIBWESTON_MAJOR@_la_CPPFLAGS = $(AM_CPPFLAGS) -DIN_WESTON -libweston_desktop_@LIBWESTON_MAJOR@_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) -libweston_desktop_@LIBWESTON_MAJOR@_la_LIBADD = \ - libweston-@LIBWESTON_MAJOR@.la \ - $(COMPOSITOR_LIBS) -libweston_desktop_@LIBWESTON_MAJOR@_la_LDFLAGS = -version-info $(LT_VERSION_INFO) - -libweston_desktop_@LIBWESTON_MAJOR@_la_SOURCES = \ - libweston-desktop/client.c \ - libweston-desktop/internal.h \ - libweston-desktop/libweston-desktop.c \ - libweston-desktop/libweston-desktop.h \ - libweston-desktop/seat.c \ - libweston-desktop/surface.c \ - libweston-desktop/wl-shell.c \ - libweston-desktop/xdg-shell-v6.c \ - libweston-desktop/xdg-shell-v5.c \ - libweston-desktop/xwayland.c - -nodist_libweston_desktop_@LIBWESTON_MAJOR@_la_SOURCES = \ - protocol/xdg-shell-unstable-v6-protocol.c \ - protocol/xdg-shell-unstable-v6-server-protocol.h \ - protocol/xdg-shell-unstable-v5-protocol.c \ - protocol/xdg-shell-unstable-v5-server-protocol.h - -BUILT_SOURCES += $(nodist_libweston_desktop_@LIBWESTON_MAJOR@_la_SOURCES) - -libweston-desktop-@LIBWESTON_MAJOR@.la libweston-desktop/libweston_desktop_@LIBWESTON_MAJOR@_la-xdg-shell-v6.lo: protocol/xdg-shell-unstable-v6-server-protocol.h -libweston-desktop-@LIBWESTON_MAJOR@.la libweston-desktop/libweston_desktop_@LIBWESTON_MAJOR@_la-xdg-shell-v5.lo: protocol/xdg-shell-unstable-v5-server-protocol.h - -if SYSTEMD_NOTIFY_SUPPORT -module_LTLIBRARIES += systemd-notify.la -systemd_notify_la_LDFLAGS = -module -avoid-version -systemd_notify_la_LIBADD = libweston-@LIBWESTON_MAJOR@.la $(SYSTEMD_DAEMON_LIBS) -systemd_notify_la_CFLAGS = \ - $(COMPOSITOR_CFLAGS) \ - $(SYSTEMD_DAEMON_CFLAGS) \ - $(PIXMAN_CFLAGS) \ - $(AM_CFLAGS) -systemd_notify_la_SOURCES = \ - compositor/systemd-notify.c \ - shared/helpers.h \ - shared/zalloc.h \ - libweston/compositor.h -endif - -nodist_libweston_@LIBWESTON_MAJOR@_la_SOURCES = \ - protocol/weston-screenshooter-protocol.c \ - protocol/weston-screenshooter-server-protocol.h \ - protocol/text-cursor-position-protocol.c \ - protocol/text-cursor-position-server-protocol.h \ - protocol/text-input-unstable-v1-protocol.c \ - protocol/text-input-unstable-v1-server-protocol.h \ - protocol/input-method-unstable-v1-protocol.c \ - protocol/input-method-unstable-v1-server-protocol.h \ - protocol/presentation-time-protocol.c \ - protocol/presentation-time-server-protocol.h \ - protocol/viewporter-protocol.c \ - protocol/viewporter-server-protocol.h \ - protocol/linux-dmabuf-unstable-v1-protocol.c \ - protocol/linux-dmabuf-unstable-v1-server-protocol.h \ - protocol/relative-pointer-unstable-v1-protocol.c \ - protocol/relative-pointer-unstable-v1-server-protocol.h \ - protocol/pointer-constraints-unstable-v1-protocol.c \ - protocol/pointer-constraints-unstable-v1-server-protocol.h - -BUILT_SOURCES += $(nodist_libweston_@LIBWESTON_MAJOR@_la_SOURCES) - -bin_PROGRAMS += weston - -weston_LDFLAGS = -export-dynamic -weston_CPPFLAGS = $(AM_CPPFLAGS) -DIN_WESTON \ - -DMODULEDIR='"$(moduledir)"' \ - -DXSERVER_PATH='"@XSERVER_PATH@"' -weston_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) $(LIBUNWIND_CFLAGS) -weston_LDADD = libshared.la libweston-@LIBWESTON_MAJOR@.la \ - $(COMPOSITOR_LIBS) $(LIBUNWIND_LIBS) \ - $(DLOPEN_LIBS) $(LIBINPUT_BACKEND_LIBS) \ - $(CLOCK_GETRES_LIBS) \ - -lm - -weston_SOURCES = \ - compositor/main.c \ - compositor/weston-screenshooter.c \ - compositor/text-backend.c \ - compositor/xwayland.c - -# Track this dependency explicitly instead of using BUILT_SOURCES. We -# add BUILT_SOURCES to CLEANFILES, but we want to keep git-version.h -# in case we're building from tarballs. - -compositor/main.c : $(top_builddir)/libweston/git-version.h -libweston/compositor.c : $(top_builddir)/libweston/git-version.h - -noinst_LTLIBRARIES += \ - libsession-helper.la - -libsession_helper_la_SOURCES = \ - libweston/launcher-util.c \ - libweston/launcher-util.h \ - libweston/launcher-impl.h \ - libweston/weston-launch.h \ - libweston/launcher-weston-launch.c \ - libweston/launcher-direct.c -libsession_helper_la_CFLAGS = $(AM_CFLAGS) $(LIBDRM_CFLAGS) $(PIXMAN_CFLAGS) $(COMPOSITOR_CFLAGS) -libsession_helper_la_LIBADD = libweston-@LIBWESTON_MAJOR@.la $(LIBDRM_LIBS) - -if ENABLE_DBUS -if HAVE_SYSTEMD_LOGIN -libsession_helper_la_SOURCES += \ - libweston/dbus.h \ - libweston/dbus.c \ - libweston/launcher-logind.c -libsession_helper_la_CFLAGS += $(SYSTEMD_LOGIN_CFLAGS) $(DBUS_CFLAGS) -libsession_helper_la_LIBADD += $(SYSTEMD_LOGIN_LIBS) $(DBUS_LIBS) -endif -endif - -if HAVE_GIT_REPO -libweston/git-version.h : $(top_srcdir)/.git/logs/HEAD - $(AM_V_GEN)echo "#define BUILD_ID \"$(shell git --git-dir=$(top_srcdir)/.git describe --always --dirty) $(shell git --git-dir=$(top_srcdir)/.git log -1 --format='%s (%ci)')\"" > $@ -else -libweston/git-version.h : - $(AM_V_GEN)echo "#define BUILD_ID \"unknown (not built from git or tarball)\"" > $@ - -endif - -.FORCE : - -if BUILD_WESTON_LAUNCH -bin_PROGRAMS += weston-launch -weston_launch_SOURCES = libweston/weston-launch.c libweston/weston-launch.h -weston_launch_CPPFLAGS = -DBINDIR='"$(bindir)"' -weston_launch_CFLAGS= \ - $(AM_CFLAGS) \ - $(PAM_CFLAGS) \ - $(SYSTEMD_LOGIN_CFLAGS) \ - $(LIBDRM_CFLAGS) -weston_launch_LDADD = $(PAM_LIBS) $(SYSTEMD_LOGIN_LIBS) $(LIBDRM_LIBS) - -if ENABLE_SETUID_INSTALL -install-exec-hook: - can_suid_files=no; \ - chown root $(DESTDIR)$(bindir)/weston-launch \ - && chmod u+s $(DESTDIR)$(bindir)/weston-launch \ - && can_suid_files=yes;\ - if test $$can_suid_files = no; then \ - echo 'Error: unable to unable to change ownership/setuid on weston-launch.'; \ - echo 'To skip this step, re-run ./configure using --disable-setuid-install'; \ - false; \ - fi -endif - -endif # BUILD_WESTON_LAUNCH - -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = \ - libweston/libweston-${LIBWESTON_MAJOR}.pc \ - libweston-desktop/libweston-desktop-${LIBWESTON_MAJOR}.pc \ - compositor/weston.pc - -wayland_sessiondir = $(datadir)/wayland-sessions -dist_wayland_session_DATA = compositor/weston.desktop - -libwestonincludedir = $(includedir)/libweston-${LIBWESTON_MAJOR} -libwestoninclude_HEADERS = \ - libweston/version.h \ - libweston/compositor.h \ - libweston/compositor-drm.h \ - libweston/compositor-fbdev.h \ - libweston/compositor-headless.h \ - libweston/compositor-rdp.h \ - libweston/compositor-wayland.h \ - libweston/compositor-x11.h \ - libweston/windowed-output-api.h \ - libweston/plugin-registry.h \ - libweston/timeline-object.h \ - shared/matrix.h \ - shared/config-parser.h \ - shared/zalloc.h - -libwestoninclude_HEADERS += \ - libweston-desktop/libweston-desktop.h - -westonincludedir = $(includedir)/weston -westoninclude_HEADERS = compositor/weston.h - -if ENABLE_IVI_SHELL -westoninclude_HEADERS += \ - ivi-shell/ivi-layout-export.h -endif - -if ENABLE_EGL -libweston_module_LTLIBRARIES += gl-renderer.la -gl_renderer_la_LDFLAGS = -module -avoid-version -gl_renderer_la_LIBADD = \ - libweston-@LIBWESTON_MAJOR@.la \ - $(EGL_LIBS) \ - $(COMPOSITOR_LIBS) -gl_renderer_la_CFLAGS = \ - $(COMPOSITOR_CFLAGS) \ - $(EGL_CFLAGS) \ - $(GL_RENDERER_CFLAGS) \ - $(AM_CFLAGS) -gl_renderer_la_SOURCES = \ - libweston/gl-renderer.h \ - libweston/gl-renderer.c \ - libweston/vertex-clipping.c \ - libweston/vertex-clipping.h \ - shared/helpers.h -endif - -if ENABLE_X11_COMPOSITOR -libweston_module_LTLIBRARIES += x11-backend.la -x11_backend_la_LDFLAGS = -module -avoid-version -x11_backend_la_LIBADD = \ - libshared-cairo.la \ - libweston-@LIBWESTON_MAJOR@.la \ - $(X11_COMPOSITOR_LIBS) \ - $(COMPOSITOR_LIBS) -x11_backend_la_CFLAGS = \ - $(AM_CFLAGS) \ - $(COMPOSITOR_CFLAGS) \ - $(EGL_CFLAGS) \ - $(PIXMAN_CFLAGS) \ - $(CAIRO_CFLAGS) \ - $(X11_COMPOSITOR_CFLAGS) -x11_backend_la_SOURCES = \ - libweston/compositor-x11.c \ - libweston/compositor-x11.h \ - shared/helpers.h -endif - -INPUT_BACKEND_LIBS = $(LIBINPUT_BACKEND_LIBS) -INPUT_BACKEND_SOURCES = \ - libweston/libinput-seat.c \ - libweston/libinput-seat.h \ - libweston/libinput-device.c \ - libweston/libinput-device.h \ - shared/helpers.h - -if ENABLE_DRM_COMPOSITOR -libweston_module_LTLIBRARIES += drm-backend.la -drm_backend_la_LDFLAGS = -module -avoid-version -drm_backend_la_LIBADD = \ - libsession-helper.la \ - libweston-@LIBWESTON_MAJOR@.la \ - $(COMPOSITOR_LIBS) \ - $(DRM_COMPOSITOR_LIBS) \ - $(INPUT_BACKEND_LIBS) \ - libshared.la \ - $(CLOCK_GETTIME_LIBS) -drm_backend_la_CFLAGS = \ - $(COMPOSITOR_CFLAGS) \ - $(EGL_CFLAGS) \ - $(DRM_COMPOSITOR_CFLAGS) \ - $(AM_CFLAGS) -drm_backend_la_SOURCES = \ - libweston/compositor-drm.c \ - libweston/compositor-drm.h \ - $(INPUT_BACKEND_SOURCES) \ - shared/helpers.h \ - shared/timespec-util.h \ - libweston/libbacklight.c \ - libweston/libbacklight.h - -if ENABLE_VAAPI_RECORDER -drm_backend_la_SOURCES += libweston/vaapi-recorder.c libweston/vaapi-recorder.h -drm_backend_la_LIBADD += $(LIBVA_LIBS) -drm_backend_la_LDFLAGS += -pthread -drm_backend_la_CFLAGS += $(LIBVA_CFLAGS) -endif -endif - -if ENABLE_WAYLAND_COMPOSITOR -libweston_module_LTLIBRARIES += wayland-backend.la -wayland_backend_la_LDFLAGS = -module -avoid-version -wayland_backend_la_LIBADD = \ - libshared-cairo.la \ - libweston-@LIBWESTON_MAJOR@.la \ - $(COMPOSITOR_LIBS) \ - $(WAYLAND_COMPOSITOR_EGL_LIBS) \ - $(WAYLAND_COMPOSITOR_LIBS) -wayland_backend_la_CFLAGS = \ - $(COMPOSITOR_CFLAGS) \ - $(EGL_CFLAGS) \ - $(PIXMAN_CFLAGS) \ - $(CAIRO_CFLAGS) \ - $(WAYLAND_COMPOSITOR_CFLAGS) \ - $(AM_CFLAGS) -wayland_backend_la_SOURCES = \ - libweston/compositor-wayland.c \ - libweston/compositor-wayland.h \ - shared/helpers.h -nodist_wayland_backend_la_SOURCES = \ - protocol/fullscreen-shell-unstable-v1-protocol.c \ - protocol/fullscreen-shell-unstable-v1-client-protocol.h \ - protocol/xdg-shell-unstable-v6-protocol.c \ - protocol/xdg-shell-unstable-v6-client-protocol.h -endif - -if ENABLE_HEADLESS_COMPOSITOR -libweston_module_LTLIBRARIES += headless-backend.la -headless_backend_la_LDFLAGS = -module -avoid-version -headless_backend_la_LIBADD = \ - libshared.la \ - libweston-@LIBWESTON_MAJOR@.la \ - $(COMPOSITOR_LIBS) -headless_backend_la_CFLAGS = $(COMPOSITOR_CFLAGS) $(AM_CFLAGS) -headless_backend_la_SOURCES = \ - libweston/compositor-headless.c \ - libweston/compositor-headless.h \ - shared/helpers.h -endif - -if ENABLE_FBDEV_COMPOSITOR -libweston_module_LTLIBRARIES += fbdev-backend.la -fbdev_backend_la_LDFLAGS = -module -avoid-version -fbdev_backend_la_LIBADD = \ - libshared.la \ - libsession-helper.la \ - libweston-@LIBWESTON_MAJOR@.la \ - $(COMPOSITOR_LIBS) \ - $(FBDEV_COMPOSITOR_LIBS) \ - $(INPUT_BACKEND_LIBS) -fbdev_backend_la_CFLAGS = \ - $(COMPOSITOR_CFLAGS) \ - $(EGL_CFLAGS) \ - $(FBDEV_COMPOSITOR_CFLAGS) \ - $(PIXMAN_CFLAGS) \ - $(AM_CFLAGS) -fbdev_backend_la_SOURCES = \ - libweston/compositor-fbdev.c \ - libweston/compositor-fbdev.h \ - shared/helpers.h \ - $(INPUT_BACKEND_SOURCES) -endif - -if ENABLE_RDP_COMPOSITOR -libweston_module_LTLIBRARIES += rdp-backend.la -rdp_backend_la_LDFLAGS = -module -avoid-version -rdp_backend_la_LIBADD = \ - libshared.la \ - libweston-@LIBWESTON_MAJOR@.la \ - $(COMPOSITOR_LIBS) \ - $(RDP_COMPOSITOR_LIBS) -rdp_backend_la_CFLAGS = \ - $(COMPOSITOR_CFLAGS) \ - $(RDP_COMPOSITOR_CFLAGS) \ - $(AM_CFLAGS) -rdp_backend_la_SOURCES = \ - libweston/compositor-rdp.c \ - libweston/compositor-rdp.h \ - shared/helpers.h -endif - -if HAVE_LCMS -module_LTLIBRARIES += cms-static.la -cms_static_la_LDFLAGS = -module -avoid-version -cms_static_la_LIBADD = \ - libshared.la \ - libweston-@LIBWESTON_MAJOR@.la \ - $(LCMS_LIBS) \ - $(COMPOSITOR_LIBS) -cms_static_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) $(LCMS_CFLAGS) -cms_static_la_SOURCES = \ - compositor/cms-static.c \ - compositor/cms-helper.c \ - compositor/cms-helper.h \ - shared/helpers.h -if ENABLE_COLORD -module_LTLIBRARIES += cms-colord.la -cms_colord_la_LDFLAGS = -module -avoid-version -cms_colord_la_LIBADD = \ - libshared.la \ - libweston-@LIBWESTON_MAJOR@.la \ - $(COLORD_LIBS) \ - $(LCMS_LIBS) \ - $(COMPOSITOR_LIBS) -cms_colord_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) $(COLORD_CFLAGS) -cms_colord_la_SOURCES = \ - compositor/cms-colord.c \ - compositor/cms-helper.c \ - compositor/cms-helper.h \ - shared/helpers.h -endif -endif - -noinst_PROGRAMS += spring-tool -spring_tool_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) -spring_tool_LDADD = $(COMPOSITOR_LIBS) -lm -spring_tool_SOURCES = \ - libweston/spring-tool.c \ - libweston/animation.c \ - shared/matrix.c \ - shared/matrix.h \ - libweston/compositor.h - -if BUILD_CLIENTS - -bin_PROGRAMS += weston-terminal weston-info - -libexec_PROGRAMS += \ - weston-desktop-shell \ - weston-screenshooter \ - weston-keyboard \ - weston-simple-im - -if ENABLE_IVI_SHELL -libexec_PROGRAMS += \ - weston-ivi-shell-user-interface -endif - -demo_clients = \ - weston-flower \ - weston-image \ - weston-cliptest \ - weston-dnd \ - weston-smoke \ - weston-resizor \ - weston-eventdemo \ - weston-clickdot \ - weston-confine \ - weston-transformed \ - weston-fullscreen \ - weston-stacking \ - weston-calibrator \ - weston-scaler - -if INSTALL_DEMO_CLIENTS -bin_PROGRAMS += $(demo_clients) -else -noinst_PROGRAMS += $(demo_clients) -endif - - -if BUILD_SIMPLE_CLIENTS -demo_clients += \ - weston-simple-shm \ - weston-simple-damage \ - weston-simple-touch \ - weston-presentation-shm \ - weston-multi-resource - -weston_simple_shm_SOURCES = clients/simple-shm.c -nodist_weston_simple_shm_SOURCES = \ - protocol/xdg-shell-unstable-v6-protocol.c \ - protocol/xdg-shell-unstable-v6-client-protocol.h \ - protocol/fullscreen-shell-unstable-v1-protocol.c \ - protocol/fullscreen-shell-unstable-v1-client-protocol.h \ - protocol/ivi-application-protocol.c \ - protocol/ivi-application-client-protocol.h -weston_simple_shm_CFLAGS = $(AM_CFLAGS) $(SIMPLE_CLIENT_CFLAGS) -weston_simple_shm_LDADD = $(SIMPLE_CLIENT_LIBS) libshared.la - -weston_simple_damage_SOURCES = clients/simple-damage.c -nodist_weston_simple_damage_SOURCES = \ - protocol/viewporter-protocol.c \ - protocol/viewporter-client-protocol.h \ - protocol/xdg-shell-unstable-v6-protocol.c \ - protocol/xdg-shell-unstable-v6-client-protocol.h \ - protocol/fullscreen-shell-unstable-v1-protocol.c \ - protocol/fullscreen-shell-unstable-v1-client-protocol.h -weston_simple_damage_CFLAGS = $(AM_CFLAGS) $(SIMPLE_CLIENT_CFLAGS) -weston_simple_damage_LDADD = $(SIMPLE_CLIENT_LIBS) libshared.la - -weston_simple_touch_SOURCES = \ - clients/simple-touch.c \ - shared/helpers.h -weston_simple_touch_CFLAGS = $(AM_CFLAGS) $(SIMPLE_CLIENT_CFLAGS) -weston_simple_touch_LDADD = $(SIMPLE_CLIENT_LIBS) libshared.la - -weston_presentation_shm_SOURCES = \ - clients/presentation-shm.c \ - shared/helpers.h -nodist_weston_presentation_shm_SOURCES = \ - protocol/presentation-time-protocol.c \ - protocol/presentation-time-client-protocol.h -weston_presentation_shm_CFLAGS = $(AM_CFLAGS) $(SIMPLE_CLIENT_CFLAGS) -weston_presentation_shm_LDADD = $(SIMPLE_CLIENT_LIBS) libshared.la -lm $(CLOCK_GETTIME_LIBS) - -weston_multi_resource_SOURCES = clients/multi-resource.c -weston_multi_resource_CFLAGS = $(AM_CFLAGS) $(SIMPLE_CLIENT_CFLAGS) -weston_multi_resource_LDADD = $(SIMPLE_CLIENT_LIBS) libshared.la $(CLOCK_GETTIME_LIBS) -lm -endif - -if BUILD_SIMPLE_EGL_CLIENTS -demo_clients += weston-simple-egl -weston_simple_egl_SOURCES = clients/simple-egl.c -nodist_weston_simple_egl_SOURCES = \ - protocol/xdg-shell-unstable-v6-protocol.c \ - protocol/xdg-shell-unstable-v6-client-protocol.h \ - protocol/ivi-application-protocol.c \ - protocol/ivi-application-client-protocol.h -weston_simple_egl_CFLAGS = $(AM_CFLAGS) $(SIMPLE_EGL_CLIENT_CFLAGS) -weston_simple_egl_LDADD = $(SIMPLE_EGL_CLIENT_LIBS) -lm -endif - -if BUILD_SIMPLE_DMABUF_INTEL_CLIENT -demo_clients += weston-simple-dmabuf-intel -weston_simple_dmabuf_intel_SOURCES = clients/simple-dmabuf-intel.c -nodist_weston_simple_dmabuf_intel_SOURCES = \ - protocol/xdg-shell-unstable-v6-protocol.c \ - protocol/xdg-shell-unstable-v6-client-protocol.h \ - protocol/fullscreen-shell-unstable-v1-protocol.c \ - protocol/fullscreen-shell-unstable-v1-client-protocol.h \ - protocol/linux-dmabuf-unstable-v1-protocol.c \ - protocol/linux-dmabuf-unstable-v1-client-protocol.h -weston_simple_dmabuf_intel_CFLAGS = $(AM_CFLAGS) $(SIMPLE_DMABUF_INTEL_CLIENT_CFLAGS) -weston_simple_dmabuf_intel_LDADD = $(SIMPLE_DMABUF_INTEL_CLIENT_LIBS) libshared.la -BUILT_SOURCES += protocol/linux-dmabuf-unstable-v1-client-protocol.h -endif - -if BUILD_SIMPLE_DMABUF_V4L_CLIENT -demo_clients += weston-simple-dmabuf-v4l -weston_simple_dmabuf_v4l_SOURCES = clients/simple-dmabuf-v4l.c -nodist_weston_simple_dmabuf_v4l_SOURCES = \ - protocol/xdg-shell-unstable-v6-protocol.c \ - protocol/xdg-shell-unstable-v6-client-protocol.h \ - protocol/fullscreen-shell-unstable-v1-protocol.c \ - protocol/fullscreen-shell-unstable-v1-client-protocol.h \ - protocol/linux-dmabuf-unstable-v1-protocol.c \ - protocol/linux-dmabuf-unstable-v1-client-protocol.h -weston_simple_dmabuf_v4l_CFLAGS = $(AM_CFLAGS) $(SIMPLE_DMABUF_V4L_CLIENT_CFLAGS) -weston_simple_dmabuf_v4l_LDADD = $(SIMPLE_DMABUF_V4L_CLIENT_LIBS) libshared.la -BUILT_SOURCES += protocol/linux-dmabuf-unstable-v1-client-protocol.h -endif - -noinst_LTLIBRARIES += libtoytoolkit.la - -libtoytoolkit_la_SOURCES = \ - clients/window.c \ - clients/window.h \ - shared/helpers.h - -nodist_libtoytoolkit_la_SOURCES = \ - protocol/text-cursor-position-protocol.c \ - protocol/text-cursor-position-client-protocol.h \ - protocol/viewporter-protocol.c \ - protocol/viewporter-client-protocol.h \ - protocol/xdg-shell-unstable-v6-protocol.c \ - protocol/xdg-shell-unstable-v6-client-protocol.h \ - protocol/ivi-application-protocol.c \ - protocol/ivi-application-client-protocol.h \ - protocol/pointer-constraints-unstable-v1-protocol.c \ - protocol/pointer-constraints-unstable-v1-client-protocol.h \ - protocol/relative-pointer-unstable-v1-protocol.c \ - protocol/relative-pointer-unstable-v1-client-protocol.h - -BUILT_SOURCES += $(nodist_libtoytoolkit_la_SOURCES) - - -libtoytoolkit_la_LIBADD = \ - $(CLIENT_LIBS) \ - $(CAIRO_EGL_LIBS) \ - libshared-cairo.la $(CLOCK_GETTIME_LIBS) -lm -libtoytoolkit_la_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) $(CAIRO_EGL_CFLAGS) - -weston_flower_SOURCES = clients/flower.c -weston_flower_LDADD = libtoytoolkit.la -weston_flower_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_screenshooter_SOURCES = \ - clients/screenshot.c -nodist_weston_screenshooter_SOURCES = \ - protocol/weston-screenshooter-protocol.c \ - protocol/weston-screenshooter-client-protocol.h -weston_screenshooter_LDADD = $(CLIENT_LIBS) libshared.la -weston_screenshooter_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_terminal_SOURCES = \ - clients/terminal.c \ - shared/helpers.h -weston_terminal_LDADD = libtoytoolkit.la -lutil -weston_terminal_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_image_SOURCES = clients/image.c -weston_image_LDADD = libtoytoolkit.la -weston_image_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_cliptest_SOURCES = \ - clients/cliptest.c \ - libweston/vertex-clipping.c \ - libweston/vertex-clipping.h -weston_cliptest_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) -weston_cliptest_LDADD = libtoytoolkit.la - -weston_dnd_SOURCES = \ - clients/dnd.c \ - shared/helpers.h -weston_dnd_LDADD = libtoytoolkit.la -weston_dnd_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_smoke_SOURCES = clients/smoke.c -weston_smoke_LDADD = libtoytoolkit.la -weston_smoke_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_resizor_SOURCES = clients/resizor.c -weston_resizor_LDADD = libtoytoolkit.la -weston_resizor_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_scaler_SOURCES = clients/scaler.c -weston_scaler_LDADD = libtoytoolkit.la -weston_scaler_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -if HAVE_CAIRO_GLESV2 -demo_clients += weston-nested weston-nested-client - -weston_nested_SOURCES = \ - clients/nested.c \ - shared/helpers.h -weston_nested_LDADD = libtoytoolkit.la $(SERVER_LIBS) -weston_nested_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_nested_client_SOURCES = clients/nested-client.c -weston_nested_client_LDADD = $(SIMPLE_EGL_CLIENT_LIBS) -lm -weston_nested_client_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) -endif - -weston_eventdemo_SOURCES = \ - clients/eventdemo.c \ - shared/helpers.h -weston_eventdemo_LDADD = libtoytoolkit.la -weston_eventdemo_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_clickdot_SOURCES = \ - clients/clickdot.c \ - shared/helpers.h -weston_clickdot_LDADD = libtoytoolkit.la -weston_clickdot_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_confine_SOURCES = \ - clients/confine.c \ - shared/helpers.h -weston_confine_LDADD = libtoytoolkit.la -weston_confine_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_transformed_SOURCES = clients/transformed.c -weston_transformed_LDADD = libtoytoolkit.la -weston_transformed_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_fullscreen_SOURCES = clients/fullscreen.c -nodist_weston_fullscreen_SOURCES = \ - protocol/fullscreen-shell-unstable-v1-protocol.c \ - protocol/fullscreen-shell-unstable-v1-client-protocol.h -weston_fullscreen_LDADD = libtoytoolkit.la -weston_fullscreen_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_stacking_SOURCES = \ - clients/stacking.c \ - shared/helpers.h -weston_stacking_LDADD = libtoytoolkit.la -weston_stacking_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_calibrator_SOURCES = \ - clients/calibrator.c \ - shared/helpers.h \ - shared/matrix.c \ - shared/matrix.h -weston_calibrator_LDADD = libtoytoolkit.la -weston_calibrator_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -if BUILD_SUBSURFACES_CLIENT -demo_clients += weston-subsurfaces -weston_subsurfaces_SOURCES = \ - clients/subsurfaces.c \ - shared/helpers.h -weston_subsurfaces_CFLAGS = \ - $(AM_CFLAGS) \ - $(SIMPLE_EGL_CLIENT_CFLAGS) \ - $(CLIENT_CFLAGS) -weston_subsurfaces_LDADD = libtoytoolkit.la $(SIMPLE_EGL_CLIENT_LIBS) -lm -endif - -if HAVE_PANGO -demo_clients += weston-editor -weston_editor_SOURCES = \ - clients/editor.c \ - shared/helpers.h -nodist_weston_editor_SOURCES = \ - protocol/text-input-unstable-v1-protocol.c \ - protocol/text-input-unstable-v1-client-protocol.h -weston_editor_LDADD = libtoytoolkit.la $(PANGO_LIBS) -weston_editor_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) $(PANGO_CFLAGS) -endif - -weston_keyboard_SOURCES = clients/keyboard.c -nodist_weston_keyboard_SOURCES = \ - protocol/weston-desktop-shell-client-protocol.h \ - protocol/weston-desktop-shell-protocol.c \ - protocol/input-method-unstable-v1-protocol.c \ - protocol/input-method-unstable-v1-client-protocol.h -weston_keyboard_LDADD = libtoytoolkit.la -weston_keyboard_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_simple_im_SOURCES = clients/simple-im.c -nodist_weston_simple_im_SOURCES = \ - protocol/input-method-unstable-v1-protocol.c \ - protocol/input-method-unstable-v1-client-protocol.h -weston_simple_im_LDADD = $(CLIENT_LIBS) -weston_simple_im_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_info_SOURCES = \ - clients/weston-info.c \ - shared/helpers.h -nodist_weston_info_SOURCES = \ - protocol/presentation-time-protocol.c \ - protocol/presentation-time-client-protocol.h -weston_info_LDADD = $(WESTON_INFO_LIBS) libshared.la -weston_info_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -weston_desktop_shell_SOURCES = \ - clients/desktop-shell.c \ - shared/helpers.h -nodist_weston_desktop_shell_SOURCES = \ - protocol/weston-desktop-shell-client-protocol.h \ - protocol/weston-desktop-shell-protocol.c -weston_desktop_shell_LDADD = libtoytoolkit.la -weston_desktop_shell_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) - -if ENABLE_IVI_SHELL -weston_ivi_shell_user_interface_SOURCES = \ - clients/ivi-shell-user-interface.c \ - shared/helpers.h -nodist_weston_ivi_shell_user_interface_SOURCES = \ - protocol/ivi-hmi-controller-client-protocol.h \ - protocol/ivi-hmi-controller-protocol.c \ - protocol/ivi-application-client-protocol.h \ - protocol/ivi-application-protocol.c -weston_ivi_shell_user_interface_LDADD = libtoytoolkit.la -weston_ivi_shell_user_interface_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) -endif - -if BUILD_FULL_GL_CLIENTS -demo_clients += weston-gears -weston_gears_SOURCES = clients/gears.c -weston_gears_LDADD = libtoytoolkit.la -weston_gears_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) -endif - -endif - -BUILT_SOURCES += \ - protocol/weston-screenshooter-protocol.c \ - protocol/weston-screenshooter-client-protocol.h \ - protocol/text-cursor-position-client-protocol.h \ - protocol/text-cursor-position-protocol.c \ - protocol/text-input-unstable-v1-protocol.c \ - protocol/text-input-unstable-v1-client-protocol.h \ - protocol/input-method-unstable-v1-protocol.c \ - protocol/input-method-unstable-v1-client-protocol.h \ - protocol/weston-desktop-shell-client-protocol.h \ - protocol/weston-desktop-shell-protocol.c \ - protocol/viewporter-client-protocol.h \ - protocol/viewporter-protocol.c \ - protocol/presentation-time-protocol.c \ - protocol/presentation-time-client-protocol.h \ - protocol/fullscreen-shell-unstable-v1-protocol.c \ - protocol/fullscreen-shell-unstable-v1-client-protocol.h \ - protocol/xdg-shell-unstable-v6-protocol.c \ - protocol/xdg-shell-unstable-v6-client-protocol.h \ - protocol/ivi-hmi-controller-protocol.c \ - protocol/ivi-hmi-controller-client-protocol.h \ - protocol/ivi-application-protocol.c \ - protocol/ivi-application-client-protocol.h - -westondatadir = $(datadir)/weston -dist_westondata_DATA = \ - data/wayland.svg \ - data/wayland.png \ - data/pattern.png \ - data/terminal.png \ - data/border.png \ - data/icon_editor.png \ - data/icon_flower.png \ - data/icon_terminal.png \ - data/icon_window.png \ - data/sign_close.png \ - data/sign_maximize.png \ - data/sign_minimize.png - -if ENABLE_IVI_SHELL -dist_westondata_DATA += \ - data/background.png \ - data/tiling.png \ - data/fullscreen.png \ - data/panel.png \ - data/random.png \ - data/sidebyside.png \ - data/home.png \ - data/icon_ivi_clickdot.png \ - data/icon_ivi_flower.png \ - data/icon_ivi_simple-egl.png \ - data/icon_ivi_simple-shm.png \ - data/icon_ivi_smoke.png -endif - - -if BUILD_WCAP_TOOLS -bin_PROGRAMS += wcap-decode - -wcap_decode_SOURCES = \ - wcap/main.c \ - wcap/wcap-decode.c \ - wcap/wcap-decode.h - -wcap_decode_CFLAGS = $(AM_CFLAGS) $(WCAP_CFLAGS) -wcap_decode_LDADD = $(WCAP_LIBS) -endif - - -if ENABLE_DESKTOP_SHELL - -module_LTLIBRARIES += desktop-shell.la - -desktop_shell_la_CPPFLAGS = \ - -I$(top_builddir)/protocol \ - -I$(top_srcdir)/shared \ - -I$(top_builddir)/libweston \ - -I$(top_srcdir)/libweston \ - -I$(top_builddir)/desktop-shell \ - -DDATADIR='"$(datadir)"' \ - -DMODULEDIR='"$(moduledir)"' \ - -DLIBEXECDIR='"$(libexecdir)"' \ - -DIN_WESTON - -desktop_shell_la_LDFLAGS = -module -avoid-version -desktop_shell_la_LIBADD = libshared.la libweston-desktop-@LIBWESTON_MAJOR@.la $(COMPOSITOR_LIBS) -desktop_shell_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) -desktop_shell_la_SOURCES = \ - desktop-shell/shell.h \ - desktop-shell/shell.c \ - desktop-shell/exposay.c \ - desktop-shell/input-panel.c \ - shared/helpers.h -nodist_desktop_shell_la_SOURCES = \ - protocol/weston-desktop-shell-protocol.c \ - protocol/weston-desktop-shell-server-protocol.h - -BUILT_SOURCES += $(nodist_desktop_shell_la_SOURCES) -endif - -if ENABLE_FULLSCREEN_SHELL - -module_LTLIBRARIES += fullscreen-shell.la - -fullscreen_shell_la_CPPFLAGS = \ - -I$(top_builddir)/protocol \ - -I$(top_srcdir)/shared \ - -I$(top_builddir)/libweston \ - -I$(top_srcdir)/libweston \ - -DIN_WESTON - -fullscreen_shell_la_LDFLAGS = -module -avoid-version -fullscreen_shell_la_LIBADD = \ - libweston-@LIBWESTON_MAJOR@.la \ - $(COMPOSITOR_LIBS) -fullscreen_shell_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) -fullscreen_shell_la_SOURCES = \ - fullscreen-shell/fullscreen-shell.c \ - shared/helpers.h -nodist_fullscreen_shell_la_SOURCES = \ - protocol/fullscreen-shell-unstable-v1-protocol.c \ - protocol/fullscreen-shell-unstable-v1-server-protocol.h - -BUILT_SOURCES += $(nodist_fullscreen_shell_la_SOURCES) -endif - -if ENABLE_IVI_SHELL - -module_LTLIBRARIES += \ - $(ivi_shell) \ - $(hmi_controller) - -ivi_shell = ivi-shell.la -ivi_shell_la_LDFLAGS = -module -avoid-version -ivi_shell_la_LIBADD = \ - libshared.la \ - libweston-@LIBWESTON_MAJOR@.la \ - $(COMPOSITOR_LIBS) -ivi_shell_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) -ivi_shell_la_SOURCES = \ - ivi-shell/ivi-layout-export.h \ - ivi-shell/ivi-layout-private.h \ - ivi-shell/ivi-layout-shell.h \ - ivi-shell/ivi-layout.c \ - ivi-shell/ivi-layout-transition.c \ - ivi-shell/ivi-shell.h \ - ivi-shell/ivi-shell.c \ - ivi-shell/input-panel-ivi.c \ - shared/helpers.h -nodist_ivi_shell_la_SOURCES = \ - protocol/ivi-application-protocol.c \ - protocol/ivi-application-server-protocol.h - -BUILT_SOURCES += $(nodist_ivi_shell_la_SOURCES) - -hmi_controller = hmi-controller.la -hmi_controller_la_LDFLAGS = -module -avoid-version -hmi_controller_la_LIBADD = \ - libshared.la \ - libweston-@LIBWESTON_MAJOR@.la \ - $(COMPOSITOR_LIBS) -hmi_controller_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) -hmi_controller_la_SOURCES = \ - ivi-shell/ivi-layout-export.h \ - ivi-shell/hmi-controller.c \ - shared/helpers.h -nodist_hmi_controller_la_SOURCES = \ - protocol/ivi-hmi-controller-protocol.c \ - protocol/ivi-hmi-controller-server-protocol.h - -BUILT_SOURCES += $(nodist_hmi_controller_la_SOURCES) - -endif - - -if ENABLE_SCREEN_SHARING - -module_LTLIBRARIES += screen-share.la - -screen_share_la_CPPFLAGS = $(AM_CPPFLAGS) -DBINDIR='"$(bindir)"' -screen_share_la_LDFLAGS = -module -avoid-version -screen_share_la_LIBADD = \ - libshared-cairo.la \ - libweston-@LIBWESTON_MAJOR@.la \ - $(COMPOSITOR_LIBS) \ - $(SCREEN_SHARE_LIBS) -screen_share_la_CFLAGS = \ - $(COMPOSITOR_CFLAGS) \ - $(SCREEN_SHARE_CFLAGS) \ - $(AM_CFLAGS) -screen_share_la_SOURCES = \ - compositor/screen-share.c \ - shared/helpers.h -nodist_screen_share_la_SOURCES = \ - protocol/fullscreen-shell-unstable-v1-protocol.c \ - protocol/fullscreen-shell-unstable-v1-client-protocol.h - -endif - -if ENABLE_XWAYLAND - -libweston_module_LTLIBRARIES += xwayland.la - -xwayland_la_CPPFLAGS = \ - -I$(top_builddir)/protocol \ - -I$(top_srcdir)/shared \ - -I$(top_builddir)/libweston \ - -I$(top_srcdir)/libweston \ - -I$(top_builddir)/xwayland \ - -DDATADIR='"$(datadir)"' \ - -DMODULEDIR='"$(moduledir)"' \ - -DLIBEXECDIR='"$(libexecdir)"' - -xwayland_la_LDFLAGS = -module -avoid-version -xwayland_la_LIBADD = \ - libshared-cairo.la \ - libweston-@LIBWESTON_MAJOR@.la \ - $(XWAYLAND_LIBS) -xwayland_la_CFLAGS = \ - $(AM_CFLAGS) \ - $(COMPOSITOR_CFLAGS) \ - $(PIXMAN_CFLAGS) \ - $(CAIRO_CFLAGS) -xwayland_la_SOURCES = \ - xwayland/xwayland.h \ - xwayland/xwayland-internal-interface.h \ - xwayland/window-manager.c \ - xwayland/selection.c \ - xwayland/dnd.c \ - xwayland/launcher.c \ - xwayland/hash.c \ - xwayland/hash.h \ - shared/helpers.h - -libwestoninclude_HEADERS += xwayland/xwayland-api.h - -endif - - -# -# Shared utilities -# - -noinst_LTLIBRARIES += libshared.la libshared-cairo.la \ - libzunitc.la libzunitcmain.la - -libshared_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) - -libshared_la_SOURCES = \ - shared/config-parser.c \ - shared/option-parser.c \ - shared/config-parser.h \ - shared/file-util.c \ - shared/file-util.h \ - shared/helpers.h \ - shared/os-compatibility.c \ - shared/os-compatibility.h \ - shared/xalloc.c \ - shared/xalloc.h - -libshared_cairo_la_CFLAGS = \ - -DDATADIR='"$(datadir)"' \ - $(AM_CFLAGS) \ - $(COMPOSITOR_CFLAGS) \ - $(PIXMAN_CFLAGS) \ - $(CAIRO_CFLAGS) \ - $(PNG_CFLAGS) \ - $(WEBP_CFLAGS) - -libshared_cairo_la_LIBADD = \ - $(PIXMAN_LIBS) \ - $(CAIRO_LIBS) \ - $(PNG_LIBS) \ - $(WEBP_LIBS) \ - $(JPEG_LIBS) - -libshared_cairo_la_SOURCES = \ - $(libshared_la_SOURCES) \ - shared/helpers.h \ - shared/image-loader.c \ - shared/image-loader.h \ - shared/cairo-util.c \ - shared/frame.c \ - shared/cairo-util.h - -libzunitc_la_SOURCES = \ - tools/zunitc/inc/zunitc/zunitc.h \ - tools/zunitc/inc/zunitc/zunitc_impl.h \ - tools/zunitc/src/zuc_base_logger.c \ - tools/zunitc/src/zuc_base_logger.h \ - tools/zunitc/src/zuc_collector.c \ - tools/zunitc/src/zuc_collector.h \ - tools/zunitc/src/zuc_context.h \ - tools/zunitc/src/zuc_event.h \ - tools/zunitc/src/zuc_event_listener.h \ - tools/zunitc/src/zuc_junit_reporter.c \ - tools/zunitc/src/zuc_junit_reporter.h \ - tools/zunitc/src/zuc_types.h \ - tools/zunitc/src/zunitc_impl.c \ - shared/helpers.h - -libzunitc_la_CFLAGS = \ - $(AM_CFLAGS) \ - -I$(top_srcdir)/tools/zunitc/inc - -libzunitc_la_LIBADD = \ - libshared.la \ - $(CLOCK_GETTIME_LIBS) - -if ENABLE_JUNIT_XML -libzunitc_la_CFLAGS += \ - $(LIBXML2_CFLAGS) -libzunitc_la_LIBADD += \ - $(LIBXML2_LIBS) -endif - -libzunitcmain_la_SOURCES = \ - tools/zunitc/src/main.c - -libzunitcmain_la_CFLAGS = \ - $(AM_CFLAGS) \ - -I$(top_srcdir)/tools/zunitc/inc - -libzunitcmain_la_LIBADD = \ - libzunitc.la \ - libshared.la - -# -# tests subdirectory -# - -TESTS = $(internal_tests) $(shared_tests) $(module_tests) $(weston_tests) $(ivi_tests) - -internal_tests = \ - internal-screenshot.weston - -shared_tests = \ - config-parser.test \ - string.test \ - vertex-clip.test \ - zuctest - -module_tests = \ - plugin-registry-test.la \ - surface-test.la \ - surface-global-test.la - -weston_tests = \ - bad_buffer.weston \ - keyboard.weston \ - event.weston \ - button.weston \ - text.weston \ - presentation.weston \ - viewporter.weston \ - roles.weston \ - subsurface.weston \ - subsurface-shot.weston \ - devices.weston - -ivi_tests = - -$(ivi_tests) : $(builddir)/tests/weston-ivi.ini - -AM_TESTS_ENVIRONMENT = \ - abs_builddir='$(abs_builddir)'; export abs_builddir; \ - abs_top_srcdir='$(abs_top_srcdir)'; export abs_top_srcdir; - -TEST_EXTENSIONS = .la .weston -LA_LOG_COMPILER = $(srcdir)/tests/weston-tests-env -WESTON_LOG_COMPILER = $(srcdir)/tests/weston-tests-env - -clean-local: - -rm -rf logs - -rm -rf $(DOCDIRS) - -# To remove when automake 1.11 support is dropped -export abs_builddir - -noinst_LTLIBRARIES += \ - weston-test.la \ - weston-test-desktop-shell.la \ - $(module_tests) \ - libtest-runner.la \ - libtest-client.la - -noinst_PROGRAMS += \ - $(setbacklight) \ - $(internal_tests) \ - $(shared_tests) \ - $(weston_tests) \ - $(ivi_tests) \ - matrix-test - -test_module_ldflags = -module -avoid-version -rpath $(libdir) -test_module_libadd = \ - libweston-@LIBWESTON_MAJOR@.la \ - $(COMPOSITOR_LIBS) - -plugin_registry_test_la_SOURCES = tests/plugin-registry-test.c -plugin_registry_test_la_LIBADD = $(test_module_libadd) -plugin_registry_test_la_LDFLAGS = $(test_module_ldflags) -plugin_registry_test_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) - -surface_global_test_la_SOURCES = tests/surface-global-test.c -surface_global_test_la_LIBADD = $(test_module_libadd) -surface_global_test_la_LDFLAGS = $(test_module_ldflags) -surface_global_test_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) - -surface_test_la_SOURCES = tests/surface-test.c -surface_test_la_LIBADD = $(test_module_libadd) -surface_test_la_LDFLAGS = $(test_module_ldflags) -surface_test_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) - -weston_test_la_LIBADD = libshared.la $(test_module_libadd) -weston_test_la_LDFLAGS = $(test_module_ldflags) -weston_test_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) -weston_test_la_SOURCES = \ - tests/weston-test.c \ - shared/helpers.h -nodist_weston_test_la_SOURCES = \ - protocol/weston-test-protocol.c \ - protocol/weston-test-server-protocol.h - -weston_test_desktop_shell_la_LIBADD = libshared.la libweston-desktop-@LIBWESTON_MAJOR@.la $(COMPOSITOR_LIBS) -weston_test_desktop_shell_la_LDFLAGS = $(test_module_ldflags) -weston_test_desktop_shell_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) -weston_test_desktop_shell_la_SOURCES = \ - tests/weston-test-desktop-shell.c \ - shared/helpers.h - -if ENABLE_EGL -weston_test_la_CFLAGS += $(EGL_TESTS_CFLAGS) -weston_test_la_LDFLAGS += $(EGL_TESTS_LIBS) -endif - -libtest_runner_la_SOURCES = \ - tests/weston-test-runner.c \ - tests/weston-test-runner.h -libtest_runner_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) - -config_parser_test_SOURCES = tests/config-parser-test.c -config_parser_test_LDADD = \ - libshared.la \ - $(COMPOSITOR_LIBS) \ - libzunitc.la \ - libzunitcmain.la -config_parser_test_CFLAGS = \ - $(AM_CFLAGS) \ - -I$(top_srcdir)/tools/zunitc/inc - -string_test_SOURCES = \ - tests/string-test.c \ - shared/string-helpers.h -string_test_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -string_test_LDADD = libtest-client.la - -vertex_clip_test_SOURCES = \ - tests/vertex-clip-test.c \ - shared/helpers.h \ - libweston/vertex-clipping.c \ - libweston/vertex-clipping.h -vertex_clip_test_LDADD = libtest-runner.la -lm $(CLOCK_GETTIME_LIBS) - -libtest_client_la_SOURCES = \ - tests/weston-test-client-helper.c \ - tests/weston-test-client-helper.h -nodist_libtest_client_la_SOURCES = \ - protocol/weston-test-protocol.c \ - protocol/weston-test-client-protocol.h -libtest_client_la_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) $(CAIRO_CFLAGS) -libtest_client_la_LIBADD = libshared.la libtest-runner.la $(TEST_CLIENT_LIBS) $(CAIRO_LIBS) - - -# -# Internal tests - tests functionality of the testsuite itself -# - -internal_screenshot_weston_SOURCES = tests/internal-screenshot-test.c -internal_screenshot_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -internal_screenshot_weston_LDADD = libtest-client.la - - -# -# Weston Tests -# - -bad_buffer_weston_SOURCES = tests/bad-buffer-test.c -bad_buffer_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -bad_buffer_weston_LDADD = libtest-client.la - -keyboard_weston_SOURCES = tests/keyboard-test.c -keyboard_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -keyboard_weston_LDADD = libtest-client.la - -event_weston_SOURCES = tests/event-test.c -event_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -event_weston_LDADD = libtest-client.la - -button_weston_SOURCES = tests/button-test.c -button_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -button_weston_LDADD = libtest-client.la - -devices_weston_SOURCES = tests/devices-test.c -devices_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -devices_weston_LDADD = libtest-client.la - -text_weston_SOURCES = tests/text-test.c -nodist_text_weston_SOURCES = \ - protocol/text-input-unstable-v1-protocol.c \ - protocol/text-input-unstable-v1-client-protocol.h -text_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -text_weston_LDADD = libtest-client.la - -subsurface_weston_SOURCES = tests/subsurface-test.c -subsurface_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -subsurface_weston_LDADD = libtest-client.la - -subsurface_shot_weston_SOURCES = tests/subsurface-shot-test.c -subsurface_shot_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -subsurface_shot_weston_LDADD = libtest-client.la - -presentation_weston_SOURCES = \ - tests/presentation-test.c \ - shared/helpers.h -nodist_presentation_weston_SOURCES = \ - protocol/presentation-time-protocol.c \ - protocol/presentation-time-client-protocol.h -presentation_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -presentation_weston_LDADD = libtest-client.la - -roles_weston_SOURCES = tests/roles-test.c -roles_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -roles_weston_LDADD = libtest-client.la - -viewporter_weston_SOURCES = \ - tests/viewporter-test.c \ - shared/helpers.h -nodist_viewporter_weston_SOURCES = \ - protocol/viewporter-protocol.c \ - protocol/viewporter-client-protocol.h -viewporter_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -viewporter_weston_LDADD = libtest-client.la - -if ENABLE_XWAYLAND_TEST -weston_tests += xwayland-test.weston -xwayland_test_weston_SOURCES = tests/xwayland-test.c -xwayland_test_weston_CFLAGS = \ - $(AM_CFLAGS) $(XWAYLAND_TEST_CFLAGS) -DXSERVER_PATH='"@XSERVER_PATH@"' -xwayland_test_weston_LDADD = libtest-client.la $(XWAYLAND_TEST_LIBS) -endif - -matrix_test_SOURCES = \ - tests/matrix-test.c \ - shared/matrix.c \ - shared/matrix.h -matrix_test_CPPFLAGS = -DUNIT_TEST -matrix_test_LDADD = -lm $(CLOCK_GETTIME_LIBS) - -if ENABLE_IVI_SHELL -module_tests += \ - ivi-layout-internal-test.la \ - ivi-layout-test.la - -ivi_layout_internal_test_la_LIBADD = $(test_module_libadd) -ivi_layout_internal_test_la_LDFLAGS = $(test_module_ldflags) -ivi_layout_internal_test_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) -ivi_layout_internal_test_la_SOURCES = \ - tests/ivi_layout-internal-test.c - -ivi_layout_test_la_LIBADD = $(test_module_libadd) -ivi_layout_test_la_LDFLAGS = $(test_module_ldflags) -ivi_layout_test_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) -ivi_layout_test_la_SOURCES = \ - tests/ivi_layout-test-plugin.c \ - tests/ivi-test.h \ - shared/helpers.h -nodist_ivi_layout_test_la_SOURCES = \ - protocol/weston-test-protocol.c \ - protocol/weston-test-server-protocol.h - -ivi_tests += \ - ivi-shell-app.weston - -ivi_shell_app_weston_SOURCES = tests/ivi-shell-app-test.c -nodist_ivi_shell_app_weston_SOURCES = \ - protocol/ivi-application-protocol.c \ - protocol/ivi-application-client-protocol.h -ivi_shell_app_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -ivi_shell_app_weston_LDADD = libtest-client.la - -noinst_PROGRAMS += ivi-layout.ivi - -ivi_layout_ivi_SOURCES = \ - tests/ivi_layout-test.c \ - tests/ivi-test.h \ - shared/helpers.h -nodist_ivi_layout_ivi_SOURCES = \ - protocol/ivi-application-protocol.c \ - protocol/ivi-application-client-protocol.h -ivi_layout_ivi_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) -ivi_layout_ivi_LDADD = libtest-client.la -endif - -if BUILD_SETBACKLIGHT -noinst_PROGRAMS += setbacklight -setbacklight_SOURCES = \ - tests/setbacklight.c \ - libweston/libbacklight.c \ - libweston/libbacklight.h -setbacklight_CFLAGS = $(AM_CFLAGS) $(SETBACKLIGHT_CFLAGS) -setbacklight_LDADD = $(SETBACKLIGHT_LIBS) -endif - -all-local: zuctest$(EXEEXT) - -noinst_PROGRAMS += zuctest$(EXEEXT) - -zuctest_LDADD = \ - libzunitc.la \ - libzunitcmain.la - -zuctest_CFLAGS = \ - $(AM_CFLAGS) \ - -I$(top_srcdir)/tools/zunitc/inc - -zuctest_SOURCES = \ - tools/zunitc/test/fixtures_test.c \ - tools/zunitc/test/zunitc_test.c - -EXTRA_DIST += \ - tests/internal-screenshot.ini \ - tests/reference/internal-screenshot-bad-00.png \ - tests/reference/internal-screenshot-good-00.png \ - tests/reference/subsurface_z_order-00.png \ - tests/reference/subsurface_z_order-01.png \ - tests/reference/subsurface_z_order-02.png \ - tests/reference/subsurface_z_order-03.png \ - tests/reference/subsurface_z_order-04.png \ - tests/weston-tests-env - -BUILT_SOURCES += \ - protocol/weston-test-protocol.c \ - protocol/weston-test-server-protocol.h \ - protocol/weston-test-client-protocol.h \ - protocol/text-input-unstable-v1-protocol.c \ - protocol/text-input-unstable-v1-client-protocol.h - -EXTRA_DIST += \ - protocol/weston-desktop-shell.xml \ - protocol/weston-screenshooter.xml \ - protocol/text-cursor-position.xml \ - protocol/weston-test.xml \ - protocol/ivi-application.xml \ - protocol/ivi-hmi-controller.xml - -# -# manual test modules in tests subdirectory -# - -noinst_LTLIBRARIES += \ - surface-screenshot.la - -surface_screenshot_la_LIBADD = libshared.la $(test_module_libadd) -surface_screenshot_la_LDFLAGS = $(test_module_ldflags) -surface_screenshot_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) -surface_screenshot_la_SOURCES = tests/surface-screenshot.c - - -# -# Documentation -# - -man_MANS = weston.1 weston.ini.5 - -if ENABLE_DRM_COMPOSITOR -man_MANS += weston-drm.7 -endif - -MAN_SUBSTS = \ - -e 's|__weston_native_backend__|$(WESTON_NATIVE_BACKEND)|g' \ - -e 's|__weston_modules_dir__|$(pkglibdir)|g' \ - -e 's|__weston_shell_client__|$(WESTON_SHELL_CLIENT)|g' \ - -e 's|__version__|$(PACKAGE_VERSION)|g' - -SUFFIXES = .1 .5 .7 .man - -%.1 %.5 %.7 : man/%.man - $(AM_V_GEN)$(SED) $(MAN_SUBSTS) < $< > $@ - -EXTRA_DIST += \ - man/weston.man \ - man/weston-drm.man \ - man/weston.ini.man - -CLEANFILES += $(man_MANS) - -if ENABLE_DEVDOCS -DOXYGEN_INDICES = docs/developer/html/index.html docs/tools/html/index.html - -docs/developer/html/index.html: doc/doxygen/tooldev.doxygen | docs/developer - cd doc/doxygen && $(DOXYGEN) tooldev.doxygen - -docs/tools/html/index.html: doc/doxygen/tools.doxygen | docs/tools - cd doc/doxygen && $(DOXYGEN) tools.doxygen -endif - -DOCDIRS = \ - docs/developer \ - docs/tools - -$(DOCDIRS): - $(MKDIR_P) $@ - -.PHONY: doc $(DOXYGEN_INDICES) - -doc: $(DOXYGEN_INDICES) - -.SECONDEXPANSION: - -define protostability -$(if $(findstring unstable,$1),unstable,stable) -endef - -define protoname -$(shell echo $1 | sed 's/\([a-z\-]\+\)-[a-z]\+-v[0-9]\+/\1/') -endef - -protocol/%-protocol.c : $(WAYLAND_PROTOCOLS_DATADIR)/$$(call protostability,$$*)/$$(call protoname,$$*)/$$*.xml - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) code < $< > $@ - -protocol/%-server-protocol.h : $(WAYLAND_PROTOCOLS_DATADIR)/$$(call protostability,$$*)/$$(call protoname,$$*)/$$*.xml - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) server-header < $< > $@ - -protocol/%-client-protocol.h : $(WAYLAND_PROTOCOLS_DATADIR)/$$(call protostability,$$*)/$$(call protoname,$$*)/$$*.xml - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) client-header < $< > $@ - -protocol/%-protocol.c : $(top_srcdir)/protocol/%.xml - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) code < $< > $@ - -protocol/%-server-protocol.h : $(top_srcdir)/protocol/%.xml - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) server-header < $< > $@ - -protocol/%-client-protocol.h : $(top_srcdir)/protocol/%.xml - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) client-header < $< > $@ diff --git a/README b/README.md similarity index 53% rename from README rename to README.md index 84c30a4ca..8947443d3 100644 --- a/README +++ b/README.md @@ -1,33 +1,143 @@ - Weston - ====== - -Weston is the reference implementation of a Wayland compositor, and a -useful compositor in its own right. Weston has various backends that -lets it run on Linux kernel modesetting and evdev input as well as -under X11. Weston ships with a few example clients, from simple -clients that demonstrate certain aspects of the protocol to more -complete clients and a simplistic toolkit. There is also a quite -capable terminal emulator (weston-terminal) and an toy/example desktop -shell. Finally, weston also provides integration with the Xorg server -and can pull X clients into the Wayland desktop and act as an X window -manager. - -Refer to http://wayland.freedesktop.org/building.html for building -weston and its dependencies. - -The test suite can be invoked via `make check`; see -http://wayland.freedesktop.org/testing.html for additional details. - -Developer documentation can be built via `make doc`. Output will be in -the build root under - -docs/developer/html/index.html -docs/tools/html/index.html - - - - Libweston - ========= +Weston +====== + +![screenshot of skeletal Weston desktop](doc/wayland-screenshot.jpg) + +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 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 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 +=============== + +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://cgit.freedesktop.org/wayland/wayland-protocols). + +If necessary, the latest Meson can be installed as a user with: + + $ pip3 install --user meson + +Weston's Meson build does not do autodetection and it defaults to all +features enabled, which means you likely hit missing dependencies on the first +try. If a dependency is avoidable through a build option, the error message +should tell you what option can be used to avoid it. You may need to disable +several features if you want to avoid certain dependencies. + + $ git clone https://gitlab.freedesktop.org/wayland/weston.git + $ cd weston + $ meson build/ --prefix=... + $ ninja -C build/ install + $ cd .. + +The `meson` command populates the build directory. This step can +fail due to missing dependencies. Any build options you want can be added on +that line, e.g. `meson build/ --prefix=... -Ddemo-clients=false`. All the build +options can be found in the file [meson_options.txt](meson_options.txt). + +Once the build directory has been successfully populated, you can inspect the +configuration with `meson configure build/`. If you need to change an +option, you can do e.g. `meson configure build/ -Ddemo-clients=false`. + +Every push to the Weston master repository and its forks is built using GitLab +CI. [Reading the configuration](.gitlab-ci.yml) may provide a useful example of +how to build and install Weston. + +More [detailed documentation on building Weston](https://wayland.freedesktop.org/building.html) +is available on the Wayland site. There are also more details on +[how to run and write tests](https://wayland.freedesktop.org/testing.html). + +For building the documentation see [weston-doc](#weston-doc). + +Running Weston +============== + +Once Weston is installed, most users can simply run it by typing `weston`. This +will launch Weston inside whatever environment you launch it from: when launched +from a text console, it will take over that console. When launched from inside +an existing Wayland or X11 session, it will start a 'nested' instance of Weston +inside a window in that session. + +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`. + +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. + +Weston-doc +========== + +For documenting weston we use [sphinx](http://www.sphinx-doc.org/en/master/) +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 +building the documentation. Installation will place the documentation in the +prefix's path under datadir (i.e., `share/doc`). + +Adding and improving documentation +---------------------------------- + +For re-generating the documentation a special `docs` target has been added. +Although first time you build (and subsequently install) weston, you'll see the +documentation being built, updates to the spinx documentation files or to the +source files will only be updated when using `docs` target! + +Example: + +~~~~ +$ ninja install # generates and installs the documentation +# time passes, hack hack, add doc in sources or rST files +$ ninja install # not sufficient, docs will not be updated +$ ninja docs && ninja install # run 'docs' then install +~~~~ + +Improving/adding documentation can be done by modifying rST files under +`doc/sphinx/` directory or by modifying the source code using doxygen +directives. + +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 @@ -40,6 +150,13 @@ 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 ------------------------------------------------- @@ -83,14 +200,14 @@ The document provides the full details, with the gist summed below: - Minor - new backward compatible features. - Patch - internal (implementation specific) fixes. -Weston and libweston have separate version numbers in configure.ac. All +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 and with minor and patch versions zero. +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 @@ -123,18 +240,22 @@ 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. @@ -221,7 +342,7 @@ would be roughly like this: + desktop shell + ivi-shell + fullscreen shell - + weston-info, weston-terminal, etc. we install by default + + weston-info (deprecated), weston-terminal, etc. we install by default + screen-share - weston demos (not parallel-installable) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..869fdfe2b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/autogen.sh b/autogen.sh deleted file mode 100755 index 916169a42..000000000 --- a/autogen.sh +++ /dev/null @@ -1,9 +0,0 @@ -#! /bin/sh - -test -n "$srcdir" || srcdir=`dirname "$0"` -test -n "$srcdir" || srcdir=. -( - cd "$srcdir" && - autoreconf --force -v --install -) || exit -test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 000000000..e54bd1b90 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,137 @@ +# This pipeline will just build Weston to be sure we dont break + +resources: + repositories: + - repository: FreeRDP + type: github + endpoint: GitHub connection 1 + name: microsoft/FreeRDP-mirror + ref: working + +trigger: + - working + +pool: + vmImage: 'ubuntu-20.04' + +variables: + prefix: '/usr/local' + +steps: + - checkout: FreeRDP + - checkout: self + + - script: sudo apt-get update && sudo apt-get install --no-install-recommends -y + build-essential + cmake + git + libcairo2-dev + libcolord-dev + libdbus-glib-1-dev + libdrm-dev + libffi-dev + libgbm-dev + libgles2-mesa-dev + libgstreamer-plugins-base1.0-dev + libgstreamer1.0-dev + libinput-dev + libjpeg-dev + liblcms2-dev + libpam-dev + libpango1.0-dev + libpixman-1-dev + libssl-dev + libsystemd-dev + libtool + libudev-dev + libudev-dev + libusb-1.0-0-dev + libva-dev + libwebp-dev + libx11-dev + libx11-xcb-dev + libxcb-composite0-dev + libxcb-xkb-dev + libxcursor-dev + libxdamage-dev + libxext-dev + libxfixes-dev + libxi-dev + libxinerama-dev + libxkbcommon-dev + libxkbfile-dev + libxml2-dev + libxrandr-dev + libxrender-dev + libxtst-dev + libxv-dev + lsb-release + python3-setuptools + python3-pip + ninja-build + pkg-config + software-properties-common + squashfs-tools + uuid-dev + libwayland-dev + wayland-protocols + wget + displayName: 'Install Dependencies' + + + + - script: cmake -G Ninja + -B build + -DCMAKE_INSTALL_PREFIX=$(prefix) + -DCMAKE_INSTALL_LIBDIR=$(prefix)/lib + -DCMAKE_BUILD_TYPE=RelWithDebInfo + -DWITH_SERVER=ON + -DWITH_CHANNEL_GFXREDIR=ON + -DWITH_CHANNEL_RDPAPPLIST=ON + -DWITH_CLIENT=OFF + -DWITH_CLIENT_COMMON=OFF + -DWITH_CLIENT_CHANNELS=OFF + -DWITH_CLIENT_INTERFACE=OFF + -DWITH_PROXY=OFF + -DWITH_SHADOW=OFF + -DWITH_SAMPLE=OFF + workingDirectory: ./FreeRDP-mirror + displayName: 'CMake (Ninja)' + + - script: ninja -C build -j8 + workingDirectory: ./FreeRDP-mirror + displayName: 'Ninja build' + + - script: sudo ninja -C build install + workingDirectory: ./FreeRDP-mirror + displayName: 'Ninja Install (FreeRDP)' + + - script: sudo pip3 install meson + displayName: 'Install Meson (from PIP)' + + - script: meson --prefix=$(prefix) build + -Dbackend-default=rdp + -Dbackend-drm=false + -Dbackend-drm-screencast-vaapi=false + -Dbackend-headless=false + -Dbackend-wayland=false + -Dbackend-x11=false + -Dbackend-fbdev=false + -Dscreenshare=false + -Dremoting=false + -Dpipewire=false + -Dshell-fullscreen=false + -Dshell-ivi=false + -Dshell-kiosk=false + -Ddemo-clients=false + -Dsimple-clients=[] + -Dtools=[] + -Dresize-pool=false + -Dwcap-decode=false + -Dtest-junit-xml=false + workingDirectory: ./weston-mirror + displayName: 'Meson (weston)' + + - script: ninja -C build -j8 + workingDirectory: ./weston-mirror + displayName: 'Ninja (weston)' diff --git a/clients/calibrator.c b/clients/calibrator.c index 04c1cfcbc..21ca876f7 100644 --- a/clients/calibrator.c +++ b/clients/calibrator.c @@ -24,19 +24,22 @@ #include "config.h" #include +#include #include #include #include #include #include #include +#include +#include #include #include #include "window.h" #include "shared/helpers.h" -#include "shared/matrix.h" +#include /* Our points for the calibration must be not be on a line */ static const struct { @@ -218,7 +221,7 @@ redraw_handler(struct widget *widget, void *data) } static struct calibrator * -calibrator_create(struct display *display) +calibrator_create(struct display *display, bool enable_button) { struct calibrator *calibrator; @@ -233,7 +236,8 @@ calibrator_create(struct display *display) calibrator->current_test = ARRAY_LENGTH(test_ratios) - 1; - widget_set_button_handler(calibrator->widget, button_handler); + if (enable_button) + widget_set_button_handler(calibrator->widget, button_handler); widget_set_touch_down_handler(calibrator->widget, touch_handler); widget_set_redraw_handler(calibrator->widget, redraw_handler); @@ -250,21 +254,49 @@ calibrator_destroy(struct calibrator *calibrator) free(calibrator); } +static void +help(const char *name) +{ + fprintf(stderr, "Usage: %s [args...]\n", name); + fprintf(stderr, " -m, --enable-mouse Enable mouse for testing the touchscreen\n"); + fprintf(stderr, " -h, --help Display this help message\n"); +} int main(int argc, char *argv[]) { struct display *display; struct calibrator *calibrator; + int c; + bool enable_mouse = 0; + struct option opts[] = { + { "enable-mouse", no_argument, NULL, 'm' }, + { "help", no_argument, NULL, 'h' }, + { 0, 0, NULL, 0 } + }; + + while ((c = getopt_long(argc, argv, "mh", opts, NULL)) != -1) { + switch (c) { + case 'm': + enable_mouse = 1; + break; + case 'h': + help(argv[0]); + exit(EXIT_FAILURE); + default: + break; + } + } display = display_create(&argc, argv); if (display == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } - calibrator = calibrator_create(display); + calibrator = calibrator_create(display, enable_mouse); if (!calibrator) return -1; diff --git a/clients/clickdot.c b/clients/clickdot.c index f52fbf048..4e8a945e7 100644 --- a/clients/clickdot.c +++ b/clients/clickdot.c @@ -32,9 +32,9 @@ #include #include #include -#include -#include #include +#include +#include #include #include @@ -62,8 +62,7 @@ struct clickdot { int reset; struct input *cursor_timeout_input; - int cursor_timeout_fd; - struct task cursor_timeout_task; + struct toytimer cursor_timeout; }; static void @@ -224,14 +223,7 @@ button_handler(struct widget *widget, static void cursor_timeout_reset(struct clickdot *clickdot) { - const long cursor_timeout = 500; - struct itimerspec its; - - its.it_interval.tv_sec = 0; - its.it_interval.tv_nsec = 0; - its.it_value.tv_sec = cursor_timeout / 1000; - its.it_value.tv_nsec = (cursor_timeout % 1000) * 1000 * 1000; - timerfd_settime(clickdot->cursor_timeout_fd, 0, &its, NULL); + toytimer_arm_once_usec(&clickdot->cursor_timeout, 500 * 1000); } static int @@ -271,15 +263,10 @@ leave_handler(struct widget *widget, } static void -cursor_timeout_func(struct task *task, uint32_t events) +cursor_timeout_func(struct toytimer *tt) { struct clickdot *clickdot = - container_of(task, struct clickdot, cursor_timeout_task); - uint64_t exp; - - if (read(clickdot->cursor_timeout_fd, &exp, sizeof (uint64_t)) != - sizeof(uint64_t)) - abort(); + container_of(tt, struct clickdot, cursor_timeout); input_set_pointer_image(clickdot->cursor_timeout_input, CURSOR_LEFT_PTR); @@ -317,12 +304,8 @@ clickdot_create(struct display *display) clickdot->line.old_y = -1; clickdot->reset = 0; - clickdot->cursor_timeout_fd = - timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); - clickdot->cursor_timeout_task.run = cursor_timeout_func; - display_watch_fd(window_get_display(clickdot->window), - clickdot->cursor_timeout_fd, - EPOLLIN, &clickdot->cursor_timeout_task); + toytimer_init(&clickdot->cursor_timeout, CLOCK_MONOTONIC, + display, cursor_timeout_func); return clickdot; } @@ -330,9 +313,7 @@ clickdot_create(struct display *display) static void clickdot_destroy(struct clickdot *clickdot) { - display_unwatch_fd(window_get_display(clickdot->window), - clickdot->cursor_timeout_fd); - close(clickdot->cursor_timeout_fd); + toytimer_fini(&clickdot->cursor_timeout); if (clickdot->buffer) cairo_surface_destroy(clickdot->buffer); widget_destroy(clickdot->widget); @@ -348,7 +329,8 @@ main(int argc, char *argv[]) display = display_create(&argc, argv); if (display == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/cliptest.c b/clients/cliptest.c index 57aefdab4..89983850a 100644 --- a/clients/cliptest.c +++ b/clients/cliptest.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -621,7 +622,8 @@ main(int argc, char *argv[]) d = display_create(&argc, argv); if (d == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/confine.c b/clients/confine.c index c0d908fb2..6f3845712 100644 --- a/clients/confine.c +++ b/clients/confine.c @@ -33,9 +33,8 @@ #include #include #include -#include -#include #include +#include #include #include @@ -46,8 +45,8 @@ #define NUM_COMPLEX_REGION_RECTS 9 -static int32_t option_complex_confine_region; -static int32_t option_help; +static bool option_complex_confine_region; +static bool option_help; struct confine { struct display *display; @@ -64,8 +63,7 @@ struct confine { int reset; struct input *cursor_timeout_input; - int cursor_timeout_fd; - struct task cursor_timeout_task; + struct toytimer cursor_timeout; bool pointer_confined; @@ -347,14 +345,7 @@ button_handler(struct widget *widget, static void cursor_timeout_reset(struct confine *confine) { - const long cursor_timeout = 500; - struct itimerspec its; - - its.it_interval.tv_sec = 0; - its.it_interval.tv_nsec = 0; - its.it_value.tv_sec = cursor_timeout / 1000; - its.it_value.tv_nsec = (cursor_timeout % 1000) * 1000 * 1000; - timerfd_settime(confine->cursor_timeout_fd, 0, &its, NULL); + toytimer_arm_once_usec(&confine->cursor_timeout, 500 * 1000); } static int @@ -406,15 +397,10 @@ leave_handler(struct widget *widget, } static void -cursor_timeout_func(struct task *task, uint32_t events) +cursor_timeout_func(struct toytimer *tt) { struct confine *confine = - container_of(task, struct confine, cursor_timeout_task); - uint64_t exp; - - if (read(confine->cursor_timeout_fd, &exp, sizeof (uint64_t)) != - sizeof(uint64_t)) - abort(); + container_of(tt, struct confine, cursor_timeout); input_set_pointer_image(confine->cursor_timeout_input, CURSOR_LEFT_PTR); @@ -461,12 +447,8 @@ confine_create(struct display *display) confine->line.old_y = -1; confine->reset = 0; - confine->cursor_timeout_fd = - timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); - confine->cursor_timeout_task.run = cursor_timeout_func; - display_watch_fd(window_get_display(confine->window), - confine->cursor_timeout_fd, - EPOLLIN, &confine->cursor_timeout_task); + toytimer_init(&confine->cursor_timeout, CLOCK_MONOTONIC, + display, cursor_timeout_func); return confine; } @@ -474,9 +456,7 @@ confine_create(struct display *display) static void confine_destroy(struct confine *confine) { - display_unwatch_fd(window_get_display(confine->window), - confine->cursor_timeout_fd); - close(confine->cursor_timeout_fd); + toytimer_fini(&confine->cursor_timeout); if (confine->buffer) cairo_surface_destroy(confine->buffer); widget_destroy(confine->widget); @@ -511,7 +491,8 @@ main(int argc, char *argv[]) display = display_create(&argc, argv); if (display == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/content_protection.c b/clients/content_protection.c new file mode 100644 index 000000000..27e2b8a9d --- /dev/null +++ b/clients/content_protection.c @@ -0,0 +1,384 @@ +/* + * Copyright © 2018 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "weston-content-protection-client-protocol.h" +#include "window.h" +#include + +#define WIDTH 500 +#define HEIGHT 400 +#define FRAME_H 18 +#define FRAME_W 5 +#define BUTTON_WIDTH 65 +#define BUTTON_HEIGHT 20 + +enum protection_mode { + RELAXED, + ENFORCED +}; + +struct protected_content_player { + struct weston_content_protection *protection; + struct weston_protected_surface *psurface; + struct display *display; + struct window *window; + struct widget *widget; + struct button_t *b0, *b1, *off, *enforced, *relaxed; + int width, height, x, y; + enum weston_protected_surface_type protection_type; + enum protection_mode mode; +}; + +struct button_t { + struct window *window; + struct widget *widget; + struct protected_content_player *pc_player; + const char *name; +}; +/** + * An event to tell the client that there is a change in protection status + * + * This event is sent whenever there is a change in content + * protection. The content protection status can be ON or OFF. ON + * in case of the desired protection type is accepted on all + * connectors, and Off in case of any of the connector + * content-protection property is changed from "enabled" + */ +static void +handle_status_changed(void *data, struct weston_protected_surface *psurface, + uint32_t status) +{ + struct protected_content_player *pc_player = data; + enum weston_protected_surface_type event_status = status; + + switch (event_status) { + case WESTON_PROTECTED_SURFACE_TYPE_HDCP_0: + pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_HDCP_0; + break; + case WESTON_PROTECTED_SURFACE_TYPE_HDCP_1: + pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_HDCP_1; + break; + case WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED: + default: + pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED; + } + window_schedule_redraw(pc_player->window); +} + +static const struct weston_protected_surface_listener pc_player_listener = { + handle_status_changed, +}; + +static void +draw_content(cairo_surface_t *surface, int x, int y, int width, int height, + enum weston_protected_surface_type type, enum protection_mode mode) +{ + cairo_t *cr; + cairo_text_extents_t extents; + const char *content_text; + const char *mode_text; + + cr = cairo_create(surface); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_rectangle(cr, x, y, width, height); + if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_0) + cairo_set_source_rgba(cr, 0, 1.0, 0, 1.0); + else if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_1) + cairo_set_source_rgba(cr, 0, 0, 1.0, 1.0); + else + cairo_set_source_rgba(cr, 1.0, 0, 0, 1.0); + cairo_fill(cr); + + cairo_set_source_rgba(cr, 0, 0, 0, 1.0); + cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 15); + if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_0) + content_text = "Content-Type : Type-0"; + else if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_1) + content_text = "Content-Type : Type-1"; + else + content_text = "Content-Type : Unprotected"; + cairo_text_extents(cr, content_text, &extents); + cairo_move_to(cr, width/2 - (extents.width/2), + height/2 - (extents.height/2)); + cairo_show_text(cr, content_text); + + if (mode == ENFORCED) + mode_text = "Mode : Enforced"; + else + mode_text = "Mode : Relaxed"; + cairo_text_extents(cr, mode_text, &extents); + cairo_move_to(cr, width / 2 - (extents.width / 2), + 2 * height / 3 - (2 * extents.height / 3)); + cairo_show_text(cr, mode_text); + + cairo_fill(cr); + cairo_destroy(cr); +} + +static void +redraw_handler(struct widget *widget, void *data) +{ + struct protected_content_player *pc_player = data; + cairo_surface_t *surface; + struct rectangle rect; + + widget_get_allocation(pc_player->widget, &rect); + surface = window_get_surface(pc_player->window); + if (surface == NULL || + cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { + fprintf(stderr, "failed to create cairo egl surface\n"); + return; + } + draw_content(surface, rect.x, rect.y, rect.width, rect.height, + pc_player->protection_type, pc_player->mode); + cairo_surface_destroy(surface); +} + +static void +resize_handler(struct widget *widget, int32_t width, int32_t height, void *data) +{ + struct rectangle allocation; + struct protected_content_player *pc_player = data; + + widget_get_allocation(pc_player->widget, &allocation); + widget_set_allocation(pc_player->b0->widget, + allocation.x + 20, allocation.y + 30, + BUTTON_WIDTH, BUTTON_HEIGHT); + widget_set_allocation(pc_player->b1->widget, + allocation.x + 20 + BUTTON_WIDTH + 5, + allocation.y + 30, + BUTTON_WIDTH, BUTTON_HEIGHT); + widget_set_allocation(pc_player->off->widget, + allocation.x + 20 + 2 * (BUTTON_WIDTH + 5), + allocation.y + 30, + BUTTON_WIDTH, BUTTON_HEIGHT); + widget_set_allocation(pc_player->enforced->widget, + allocation.x + 20 + 3 * (BUTTON_WIDTH + 5), + allocation.y + 30, + BUTTON_WIDTH, BUTTON_HEIGHT); + widget_set_allocation(pc_player->relaxed->widget, + allocation.x + 20 + 4 * (BUTTON_WIDTH + 5), + allocation.y + 30, + BUTTON_WIDTH, BUTTON_HEIGHT); +} + +static void +buttons_handler(struct widget *widget, struct input *input, uint32_t time, + uint32_t button, enum wl_pointer_button_state state, void *data) +{ + struct button_t *b = data; + struct protected_content_player *pc_player = b->pc_player; + struct wl_surface *surface; + + if (strcmp(b->name, "ENFORCED") == 0) { + weston_protected_surface_enforce(pc_player->psurface); + pc_player->mode = ENFORCED; + window_schedule_redraw(pc_player->window); + } + else if (strcmp(b->name, "RELAXED") == 0) { + weston_protected_surface_relax(pc_player->psurface); + pc_player->mode = RELAXED; + window_schedule_redraw(pc_player->window); + } + else if (strcmp(b->name, "TYPE-0") == 0) + weston_protected_surface_set_type(pc_player->psurface, + WESTON_PROTECTED_SURFACE_TYPE_HDCP_0); + else if (strcmp(b->name, "TYPE-1") == 0) + weston_protected_surface_set_type(pc_player->psurface, + WESTON_PROTECTED_SURFACE_TYPE_HDCP_1); + else + weston_protected_surface_set_type(pc_player->psurface, + WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED); + + surface = window_get_wl_surface(pc_player->window); + wl_surface_commit(surface); +} + +static void +handle_global(struct display *display, uint32_t name, const char *interface, + uint32_t version, void *data) +{ + struct protected_content_player *pc_player = data; + + if (strcmp(interface, "weston_content_protection") == 0) { + pc_player->protection = display_bind(display, name, + &weston_content_protection_interface, + 1); + } +} + +static void +buttons_redraw_handler(struct widget *widget, void *data) +{ + struct button_t *b = data; + cairo_surface_t *surface; + struct rectangle allocation; + cairo_t *cr; + + surface = window_get_surface(b->window); + widget_get_allocation(b->widget, &allocation); + + cr = cairo_create(surface); + cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, + allocation.height); + + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + + cairo_set_source_rgba(cr, 1, 1, 1, 1); + cairo_fill(cr); + + cairo_set_source_rgba(cr, 0, 0, 0, 1.0); + cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 10); + cairo_move_to(cr, allocation.x + 5, allocation.y + 15); + cairo_show_text(cr, b->name); + cairo_fill(cr); + + cairo_destroy(cr); + cairo_surface_destroy(surface); +} + +static struct button_t* +create_button(struct protected_content_player *pc_player, const char *name) +{ + struct button_t *b; + + b = zalloc(sizeof(struct button_t)); + if (b == NULL) { + fprintf(stderr, "Failed to allocate memory for button.\n"); + exit(0); + } + b->widget = widget_add_widget(pc_player->widget, b); + b->window = pc_player->window; + b->pc_player = pc_player; + b->name = name; + widget_set_redraw_handler(b->widget, buttons_redraw_handler); + widget_set_button_handler(b->widget, buttons_handler); + return b; +} + +static void +destroy_button(struct button_t *b) +{ + if (!b) + return; + widget_destroy(b->widget); + free(b); +} + +static void free_pc_player(struct protected_content_player *pc_player) +{ + if (!pc_player) + return; + + destroy_button(pc_player->b0); + destroy_button(pc_player->b1); + destroy_button(pc_player->off); + destroy_button(pc_player->enforced); + destroy_button(pc_player->relaxed); + widget_destroy(pc_player->widget); + window_destroy(pc_player->window); + free(pc_player); +} + +int main(int argc, char *argv[]) +{ + struct protected_content_player *pc_player; + struct display *d; + static const char str_type_0[] = "TYPE-0"; + static const char str_type_1[] = "TYPE-1"; + static const char str_type_off[] = "OFF"; + static const char str_type_enforced[] = "ENFORCED"; + static const char str_type_relaxed[] = "RELAXED"; + struct wl_surface *surface; + + pc_player = zalloc(sizeof(struct protected_content_player)); + if (pc_player == NULL) { + fprintf(stderr, "failed to allocate memory: %m\n"); + return -1; + } + d = display_create(&argc, argv); + if (d == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return -1; + } + pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED; + pc_player->mode = RELAXED; + pc_player->width = WIDTH * 2.0/4.0; + pc_player->height = HEIGHT * 2.0/4.0; + pc_player->x = WIDTH * 1.0/4.0; + pc_player->y = HEIGHT * 1.0/4.0; + pc_player->window = window_create(d); + pc_player->widget = window_frame_create(pc_player->window, pc_player); + pc_player->display = d; + display_set_user_data(d, pc_player); + + display_set_global_handler(d, handle_global); + surface = window_get_wl_surface(pc_player->window); + if (pc_player->protection == NULL) { + printf("The content-protection object is NULL\n"); + return -1; + } + pc_player->psurface = weston_content_protection_get_protection(pc_player->protection, + surface); + weston_protected_surface_add_listener(pc_player->psurface, + &pc_player_listener, + pc_player); + + pc_player->b0 = create_button(pc_player, str_type_0); + pc_player->b1 = create_button(pc_player, str_type_1); + pc_player->off = create_button(pc_player, str_type_off); + pc_player->enforced = create_button(pc_player, str_type_enforced); + pc_player->relaxed = create_button(pc_player, str_type_relaxed); + + window_set_title(pc_player->window, "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); + widget_schedule_redraw(pc_player->b0->widget); + widget_schedule_redraw(pc_player->b1->widget); + widget_schedule_redraw(pc_player->off->widget); + + display_run(d); + weston_protected_surface_destroy(pc_player->psurface); + weston_content_protection_destroy(pc_player->protection); + free_pc_player(pc_player); + display_destroy(d); + return 0; +} diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c index b133d86e8..bde5dc820 100644 --- a/clients/desktop-shell.c +++ b/clients/desktop-shell.c @@ -34,8 +34,6 @@ #include #include #include -#include -#include #include #include #include @@ -45,14 +43,16 @@ #include #include "window.h" #include "shared/cairo-util.h" -#include "shared/config-parser.h" +#include #include "shared/helpers.h" #include "shared/xalloc.h" -#include "shared/zalloc.h" +#include +#include "shared/file-util.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 */ @@ -77,7 +77,7 @@ struct desktop { struct widget *grab_widget; struct weston_config *config; - int locking; + bool locking; enum cursor_type grab_cursor; @@ -91,8 +91,13 @@ struct surface { int32_t width, int32_t height); }; +struct output; + struct panel { struct surface base; + + struct output *owner; + struct window *window; struct widget *widget; struct wl_list launcher_list; @@ -105,6 +110,9 @@ struct panel { struct background { struct surface base; + + struct output *owner; + struct window *window; struct widget *widget; int painted; @@ -119,6 +127,8 @@ struct output { uint32_t server_output_id; struct wl_list link; + int x; + int y; struct panel *panel; struct background *background; }; @@ -137,8 +147,7 @@ struct panel_launcher { struct panel_clock { struct widget *widget; struct panel *panel; - struct task clock_task; - int clock_fd; + struct toytimer timer; char *format_string; time_t refresh_timer; }; @@ -204,7 +213,7 @@ panel_launcher_activate(struct panel_launcher *widget) pid = fork(); if (pid < 0) { - fprintf(stderr, "fork failed: %m\n"); + fprintf(stderr, "fork failed: %s\n", strerror(errno)); return; } @@ -212,8 +221,13 @@ panel_launcher_activate(struct panel_launcher *widget) return; argv = widget->argv.data; + + if (setsid() == -1) + exit(EXIT_FAILURE); + if (execve(argv[0], argv, widget->envp.data) < 0) { - fprintf(stderr, "execl '%s' failed: %m\n", argv[0]); + fprintf(stderr, "execl '%s' failed: %s\n", argv[0], + strerror(errno)); exit(1); } } @@ -228,6 +242,14 @@ panel_launcher_redraw_handler(struct widget *widget, void *data) cr = widget_cairo_create(launcher->panel->widget); widget_get_allocation(widget, &allocation); + allocation.x += allocation.width / 2 - + cairo_image_surface_get_width(launcher->icon) / 2; + if (allocation.width > allocation.height) + allocation.x += allocation.width / 2 - allocation.height / 2; + allocation.y += allocation.height / 2 - + cairo_image_surface_get_height(launcher->icon) / 2; + if (allocation.height > allocation.width) + allocation.y += allocation.height / 2 - allocation.width / 2; if (launcher->pressed) { allocation.x++; allocation.y++; @@ -350,14 +372,10 @@ panel_launcher_touch_up_handler(struct widget *widget, struct input *input, } static void -clock_func(struct task *task, uint32_t events) +clock_func(struct toytimer *tt) { - struct panel_clock *clock = - container_of(task, struct panel_clock, clock_task); - uint64_t exp; + struct panel_clock *clock = container_of(tt, struct panel_clock, timer); - if (read(clock->clock_fd, &exp, sizeof exp) != sizeof exp) - abort(); widget_schedule_redraw(clock->widget); } @@ -368,7 +386,6 @@ panel_clock_redraw_handler(struct widget *widget, void *data) cairo_t *cr; struct rectangle allocation; cairo_text_extents_t extents; - cairo_font_extents_t font_extents; time_t rawtime; struct tm * timeinfo; char string[128]; @@ -382,19 +399,20 @@ panel_clock_redraw_handler(struct widget *widget, void *data) return; cr = widget_cairo_create(clock->panel->widget); - cairo_select_font_face(cr, "sans", - CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(cr, 14); cairo_text_extents(cr, string, &extents); - cairo_font_extents (cr, &font_extents); - cairo_move_to(cr, allocation.x + 5, - allocation.y + 3 * (allocation.height >> 2) + 1); - cairo_set_source_rgb(cr, 0, 0, 0); + if (allocation.x > 0) + allocation.x += + allocation.width - DEFAULT_SPACING * 1.5 - extents.width; + else + allocation.x += + allocation.width / 2 - extents.width / 2; + allocation.y += allocation.height / 2 - 1 + extents.height / 2; + cairo_move_to(cr, allocation.x + 1, allocation.y + 1); + cairo_set_source_rgba(cr, 0, 0, 0, 0.85); cairo_show_text(cr, string); - cairo_move_to(cr, allocation.x + 4, - allocation.y + 3 * (allocation.height >> 2)); - cairo_set_source_rgb(cr, 1, 1, 1); + cairo_move_to(cr, allocation.x, allocation.y); + cairo_set_source_rgba(cr, 1, 1, 1, 0.85); cairo_show_text(cr, string); cairo_destroy(cr); } @@ -408,10 +426,7 @@ clock_timer_reset(struct panel_clock *clock) its.it_interval.tv_nsec = 0; its.it_value.tv_sec = clock->refresh_timer; its.it_value.tv_nsec = 0; - if (timerfd_settime(clock->clock_fd, 0, &its, NULL) < 0) { - fprintf(stderr, "could not set timerfd\n: %m"); - return -1; - } + toytimer_arm(&clock->timer, &its); return 0; } @@ -420,9 +435,7 @@ static void panel_destroy_clock(struct panel_clock *clock) { widget_destroy(clock->widget); - - close(clock->clock_fd); - + toytimer_fini(&clock->timer); free(clock); } @@ -430,18 +443,10 @@ static void panel_add_clock(struct panel *panel) { struct panel_clock *clock; - int timerfd; - - timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); - if (timerfd < 0) { - fprintf(stderr, "could not create timerfd\n: %m"); - return; - } clock = xzalloc(sizeof *clock); clock->panel = panel; panel->clock = clock; - clock->clock_fd = timerfd; switch (panel->clock_format) { case CLOCK_FORMAT_MINUTES: @@ -456,9 +461,8 @@ panel_add_clock(struct panel *panel) assert(!"not reached"); } - clock->clock_task.run = clock_func; - display_watch_fd(window_get_display(panel->window), clock->clock_fd, - EPOLLIN, &clock->clock_task); + toytimer_init(&clock->timer, CLOCK_MONOTONIC, + window_get_display(panel->window), clock_func); clock_timer_reset(clock); clock->widget = widget_add_widget(panel->widget, clock); @@ -471,50 +475,42 @@ panel_resize_handler(struct widget *widget, { struct panel_launcher *launcher; struct panel *panel = data; - int bx = width / 2; - int by = height / 2; - int spacing = 10; - int x = spacing; - int y = spacing; - int w, h; + int x = 0; + int y = 0; + int w = height > width ? width : height; + int h = w; int horizontal = panel->panel_position == WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP || panel->panel_position == WESTON_DESKTOP_SHELL_PANEL_POSITION_BOTTOM; + int first_pad_h = horizontal ? 0 : DEFAULT_SPACING / 2; + int first_pad_w = horizontal ? DEFAULT_SPACING / 2 : 0; wl_list_for_each(launcher, &panel->launcher_list, link) { - w = cairo_image_surface_get_width(launcher->icon); - h = cairo_image_surface_get_height(launcher->icon); - - if (horizontal) - y = by - h / 2; - else - x = bx - w / 2; - widget_set_allocation(launcher->widget, - x, y, w + 1, h + 1); + widget_set_allocation(launcher->widget, x, y, + w + first_pad_w + 1, h + first_pad_h + 1); if (horizontal) - x += w + spacing; + x += w + first_pad_w; else - y += h + spacing; + y += h + first_pad_h; + first_pad_h = first_pad_w = 0; } - h = 20; - if (panel->clock_format == CLOCK_FORMAT_SECONDS) - w = 190; - else /* CLOCK_FORMAT_MINUTES */ w = 170; + else /* CLOCK_FORMAT_MINUTES */ + w = 150; - if (horizontal) { - x = width - w - spacing; - y = by - h / 2; - } else { - x = bx - w / 2; - y = height - h - spacing; - } + if (horizontal) + x = width - w; + else + y = height - (h = DEFAULT_SPACING * 3); if (panel->clock) widget_set_allocation(panel->clock->widget, x, y, w + 1, h + 1); } +static void +panel_destroy(struct panel *panel); + static void panel_configure(void *data, struct weston_desktop_shell *desktop_shell, @@ -524,6 +520,15 @@ panel_configure(void *data, struct desktop *desktop = data; struct surface *surface = window_get_user_data(window); struct panel *panel = container_of(surface, struct panel, base); + struct output *owner; + + if (width < 1 || height < 1) { + /* Shell plugin configures 0x0 for redundant panel. */ + owner = panel->owner; + panel_destroy(panel); + owner->panel = NULL; + return; + } switch (desktop->panel_position) { case WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP: @@ -537,10 +542,10 @@ panel_configure(void *data, width = 32; break; case CLOCK_FORMAT_MINUTES: - width = 170; + width = 150; break; case CLOCK_FORMAT_SECONDS: - width = 190; + width = 170; break; } break; @@ -583,14 +588,14 @@ panel_destroy(struct panel *panel) } static struct panel * -panel_create(struct desktop *desktop) +panel_create(struct desktop *desktop, struct output *output) { struct panel *panel; struct weston_config_section *s; - char *clock_format_option = NULL; panel = xzalloc(sizeof *panel); + panel->owner = output; panel->base.configure = panel_configure; panel->window = window_create_custom(desktop->display); panel->widget = window_add_widget(panel->window, panel); @@ -607,8 +612,6 @@ panel_create(struct desktop *desktop) if (panel->clock_format != CLOCK_FORMAT_NONE) panel_add_clock(panel); - free (clock_format_option); - s = weston_config_get_section(desktop->config, "shell", NULL, NULL); weston_config_section_get_color(s, "panel-color", &panel->color, 0xaa000000); @@ -732,7 +735,8 @@ panel_add_launcher(struct panel *panel, const char *icon, const char *path) enum { BACKGROUND_SCALE, BACKGROUND_SCALE_CROP, - BACKGROUND_TILE + BACKGROUND_TILE, + BACKGROUND_CENTERED }; static void @@ -752,15 +756,22 @@ background_draw(struct widget *widget, void *data) cr = widget_cairo_create(background->widget); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba(cr, 0.0, 0.0, 0.2, 1.0); + 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) - image = load_cairo_surface(DATADIR "/weston/pattern.png"); + 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); @@ -789,16 +800,27 @@ background_draw(struct widget *widget, void *data) 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); - } else { - set_hex_color(cr, background->color); + cairo_mask(cr, pattern); } - cairo_paint(cr); cairo_destroy(cr); cairo_surface_destroy(surface); @@ -806,15 +828,33 @@ background_draw(struct widget *widget, void *data) check_desktop_ready(background->window); } +static void +background_destroy(struct background *background); + static void background_configure(void *data, struct weston_desktop_shell *desktop_shell, uint32_t edges, struct window *window, int32_t width, int32_t height) { + struct output *owner; struct background *background = (struct background *) window_get_user_data(window); + if (width < 1 || height < 1) { + /* Shell plugin configures 0x0 for redundant background. */ + owner = background->owner; + background_destroy(background); + owner->background = NULL; + return; + } + + if (!background->image) { + widget_set_viewport_destination(background->widget, width, height); + width = 1; + height = 1; + } + widget_schedule_resize(background->widget, width, height); } @@ -1088,13 +1128,14 @@ background_destroy(struct background *background) } static struct background * -background_create(struct desktop *desktop) +background_create(struct desktop *desktop, struct output *output) { struct background *background; struct weston_config_section *s; char *type; background = xzalloc(sizeof *background); + background->owner = output; background->base.configure = background_configure; background->window = window_create_custom(desktop->display); background->widget = window_add_widget(background->window, background); @@ -1121,6 +1162,8 @@ background_create(struct desktop *desktop) background->type = BACKGROUND_SCALE_CROP; } else if (strcmp(type, "tile") == 0) { background->type = BACKGROUND_TILE; + } else if (strcmp(type, "centered") == 0) { + background->type = BACKGROUND_CENTERED; } else { background->type = -1; fprintf(stderr, "invalid background-type: %s\n", @@ -1172,7 +1215,8 @@ grab_surface_create(struct desktop *desktop) static void output_destroy(struct output *output) { - background_destroy(output->background); + if (output->background) + background_destroy(output->background); if (output->panel) panel_destroy(output->panel); wl_output_destroy(output->output); @@ -1204,9 +1248,13 @@ output_handle_geometry(void *data, { struct output *output = data; + output->x = x; + output->y = y; + if (output->panel) window_set_buffer_transform(output->panel->window, transform); - window_set_buffer_transform(output->background->window, transform); + if (output->background) + window_set_buffer_transform(output->background->window, transform); } static void @@ -1234,7 +1282,8 @@ output_handle_scale(void *data, if (output->panel) window_set_buffer_scale(output->panel->window, scale); - window_set_buffer_scale(output->background->window, scale); + if (output->background) + window_set_buffer_scale(output->background->window, scale); } static const struct wl_output_listener output_listener = { @@ -1250,13 +1299,13 @@ output_init(struct output *output, struct desktop *desktop) struct wl_surface *surface; if (desktop->want_panel) { - output->panel = panel_create(desktop); + output->panel = panel_create(desktop, output); surface = window_get_wl_surface(output->panel->window); weston_desktop_shell_set_panel(desktop->shell, output->output, surface); } - output->background = background_create(desktop); + output->background = background_create(desktop, output); surface = window_get_wl_surface(output->background->window); weston_desktop_shell_set_background(desktop->shell, output->output, surface); @@ -1285,6 +1334,60 @@ create_output(struct desktop *desktop, uint32_t id) output_init(output, desktop); } +static void +output_remove(struct desktop *desktop, struct output *output) +{ + struct output *cur; + struct output *rep = NULL; + + if (!output->background) { + output_destroy(output); + return; + } + + /* Find a wl_output that is a clone of the removed wl_output. + * We don't want to leave the clone without a background or panel. */ + wl_list_for_each(cur, &desktop->outputs, link) { + if (cur == output) + continue; + + /* XXX: Assumes size matches. */ + if (cur->x == output->x && cur->y == output->y) { + rep = cur; + break; + } + } + + if (rep) { + /* If found and it does not already have a background or panel, + * hand over the background and panel so they don't get + * destroyed. + * + * We never create multiple backgrounds or panels for clones, + * but if the compositor moves outputs, a pair of wl_outputs + * might become "clones". This may happen temporarily when + * an output is about to be removed and the rest are reflowed. + * In this case it is correct to let the background/panel be + * destroyed. + */ + + if (!rep->background) { + rep->background = output->background; + output->background = NULL; + rep->background->owner = rep; + } + + if (!rep->panel) { + rep->panel = output->panel; + output->panel = NULL; + if (rep->panel) + rep->panel->owner = rep; + } + } + + output_destroy(output); +} + static void global_handler(struct display *display, uint32_t id, const char *interface, uint32_t version, void *data) @@ -1314,7 +1417,7 @@ global_handler_remove(struct display *display, uint32_t id, if (!strcmp(interface, "wl_output")) { wl_list_for_each(output, &desktop->outputs, link) { if (output->server_output_id == id) { - output_destroy(output); + output_remove(desktop, output); break; } } @@ -1350,10 +1453,13 @@ panel_add_launchers(struct panel *panel, struct desktop *desktop) } if (count == 0) { + char *name = file_name_with_datadir("terminal.png"); + /* add default launcher */ panel_add_launcher(panel, - DATADIR "/weston/terminal.png", + name, BINDIR "/weston-terminal"); + free(name); } } @@ -1412,13 +1518,14 @@ 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, 1); + weston_config_section_get_bool(s, "locking", &desktop.locking, true); parse_panel_position(&desktop, s); parse_clock_format(&desktop, s); desktop.display = display_create(&argc, argv); if (desktop.display == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/dnd.c b/clients/dnd.c index 41e532ef8..8323f4fdc 100644 --- a/clients/dnd.c +++ b/clients/dnd.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -534,6 +535,10 @@ create_drag_source(struct dnd *dnd, } else { dnd_drag->data_source = display_create_data_source(dnd->display); + if (!dnd_drag->data_source) { + fprintf(stderr, "No data device manager\n"); + abort(); + } wl_data_source_add_listener(dnd_drag->data_source, &data_source_listener, dnd_drag); @@ -844,7 +849,8 @@ main(int argc, char *argv[]) d = display_create(&argc, argv); if (d == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/editor.c b/clients/editor.c index a0cc97af0..a59f9679a 100644 --- a/clients/editor.c +++ b/clients/editor.c @@ -38,7 +38,7 @@ #include -#include "shared/config-parser.h" +#include #include "shared/helpers.h" #include "shared/xalloc.h" #include "window.h" @@ -49,6 +49,7 @@ struct text_entry { struct window *window; char *text; int active; + bool panel_visible; uint32_t cursor; uint32_t anchor; struct { @@ -76,7 +77,7 @@ struct text_entry { uint32_t serial; uint32_t reset_serial; uint32_t content_purpose; - uint32_t click_to_show; + bool click_to_show; char *preferred_language; bool button_pressed; }; @@ -499,8 +500,10 @@ text_input_leave(void *data, text_entry_commit_and_reset(entry); entry->active--; - if (!entry->active) + if (!entry->active) { zwp_text_input_v1_hide_input_panel(text_input); + entry->panel_visible = false; + } widget_schedule_redraw(entry->widget); } @@ -577,7 +580,7 @@ data_source_send(void *data, struct editor *editor = data; if (write(fd, editor->selected_text, strlen(editor->selected_text) + 1) < 0) - fprintf(stderr, "write failed: %m\n"); + fprintf(stderr, "write failed: %s\n", strerror(errno)); close(fd); } @@ -636,6 +639,9 @@ editor_copy_cut(struct editor *editor, struct input *input, bool cut) editor->selection = display_create_data_source(editor->display); + if (!editor->selection) + return; + wl_data_source_offer(editor->selection, "text/plain;charset=utf-8"); wl_data_source_add_listener(editor->selection, @@ -699,6 +705,7 @@ text_entry_create(struct editor *editor, const char *text) entry->window = editor->window; entry->text = strdup(text); entry->active = 0; + entry->panel_visible = false; entry->cursor = strlen(text); entry->anchor = entry->cursor; entry->text_input = @@ -787,7 +794,12 @@ text_entry_activate(struct text_entry *entry, struct wl_surface *surface = window_get_wl_surface(entry->window); if (entry->click_to_show && entry->active) { - zwp_text_input_v1_show_input_panel(entry->text_input); + entry->panel_visible = !entry->panel_visible; + + if (entry->panel_visible) + zwp_text_input_v1_show_input_panel(entry->text_input); + else + zwp_text_input_v1_hide_input_panel(entry->text_input); return; } @@ -1492,10 +1504,10 @@ global_handler(struct display *display, uint32_t name, } /** Display help for command line options, and exit */ -static uint32_t opt_help = 0; +static bool opt_help = false; /** Require a distinct click to show the input panel (virtual keyboard) */ -static uint32_t opt_click_to_show = 0; +static bool opt_click_to_show = false; /** Set a specific (RFC-3066) language. Used for the virtual keyboard, etc. */ static const char *opt_preferred_language = NULL; @@ -1597,7 +1609,8 @@ main(int argc, char *argv[]) text_buffer = read_file(argv[1]); if (text_buffer == NULL) { - fprintf(stderr, "could not read file '%s': %m\n", argv[1]); + fprintf(stderr, "could not read file '%s': %s\n", + argv[1], strerror(errno)); return -1; } } @@ -1606,7 +1619,9 @@ main(int argc, char *argv[]) editor.display = display_create(&argc, argv); if (editor.display == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); + free(text_buffer); return -1; } @@ -1615,6 +1630,8 @@ main(int argc, char *argv[]) if (editor.text_input_manager == NULL) { fprintf(stderr, "No text input manager global\n"); + display_destroy(editor.display); + free(text_buffer); return -1; } diff --git a/clients/eventdemo.c b/clients/eventdemo.c index d8eef5b53..0a1a7ca91 100644 --- a/clients/eventdemo.c +++ b/clients/eventdemo.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include @@ -53,7 +55,7 @@ static int width = 500; static int height = 400; /** set if window has no borders */ -static int noborder = 0; +static bool noborder = false; /** if non-zero, maximum window width */ static int width_max = 0; @@ -62,25 +64,25 @@ static int width_max = 0; static int height_max = 0; /** set to log redrawing */ -static int log_redraw = 0; +static bool log_redraw = false; /** set to log resizing */ -static int log_resize = 0; +static bool log_resize = false; /** set to log keyboard focus */ -static int log_focus = 0; +static bool log_focus = false; /** set to log key events */ -static int log_key = 0; +static bool log_key = false; /** set to log button events */ -static int log_button = 0; +static bool log_button = false; /** set to log axis events */ -static int log_axis = 0; +static bool log_axis = false; /** set to log motion events */ -static int log_motion = 0; +static bool log_motion = false; /** * \struct eventdemo @@ -191,10 +193,11 @@ keyboard_focus_handler(struct window *window, /** * \brief CALLBACK function, Wayland informs about key event * \param window window + * \param input input + * \param time time * \param key keycode * \param unicode associated character * \param state pressed or released - * \param modifiers modifiers: ctrl, alt, meta etc. * \param data user data associated to the window */ static void @@ -349,8 +352,8 @@ axis_discrete_handler(struct widget *widget, struct input *input, * \param time time the event happened * \param x absolute x position * \param y absolute y position - * \param sx x position relative to the window - * \param sy y position relative to the window + * \param x x position relative to the window + * \param y y position relative to the window * \param data user data associated to the window * * Demonstrates the use of different cursors @@ -510,19 +513,21 @@ 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 = 1; + log_button = log_axis = log_motion = true; /* Connect to the display and have the arguments parsed */ d = display_create(&argc, argv); if (d == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } /* Create new eventdemo window */ e = eventdemo_create(d); if (e == NULL) { - fprintf(stderr, "failed to create eventdemo: %m\n"); + fprintf(stderr, "failed to create eventdemo: %s\n", + strerror(errno)); return -1; } diff --git a/clients/flower.c b/clients/flower.c index 34287fd8a..e3471ce7e 100644 --- a/clients/flower.c +++ b/clients/flower.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -172,7 +173,8 @@ int main(int argc, char *argv[]) d = display_create(&argc, argv); if (d == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/fullscreen.c b/clients/fullscreen.c index e2e6477f1..1b44ad535 100644 --- a/clients/fullscreen.c +++ b/clients/fullscreen.c @@ -30,13 +30,14 @@ #include #include #include +#include #include #include #include #include "window.h" #include "fullscreen-shell-unstable-v1-client-protocol.h" -#include "shared/zalloc.h" +#include struct fs_output { struct wl_list link; @@ -279,6 +280,9 @@ 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]; @@ -518,7 +522,8 @@ int main(int argc, char *argv[]) d = display_create(&argc, argv); if (d == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/gears.c b/clients/gears.c index 3c57c4a74..6090a850a 100644 --- a/clients/gears.c +++ b/clients/gears.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -488,7 +489,8 @@ int main(int argc, char *argv[]) d = display_create(&argc, argv); if (d == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } gears = gears_create(d); diff --git a/clients/image.c b/clients/image.c index db9ccd647..0a8fb5b5b 100644 --- a/clients/image.c +++ b/clients/image.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -419,7 +420,8 @@ main(int argc, char *argv[]) d = display_create(&argc, argv); if (d == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/ivi-shell-user-interface.c b/clients/ivi-shell-user-interface.c index f4e061d08..7d2d1a20f 100644 --- a/clients/ivi-shell-user-interface.c +++ b/clients/ivi-shell-user-interface.c @@ -35,14 +35,16 @@ #include #include #include +#include #include #include #include "shared/cairo-util.h" -#include "shared/config-parser.h" +#include #include "shared/helpers.h" #include "shared/os-compatibility.h" #include "shared/xalloc.h" -#include "shared/zalloc.h" +#include +#include "shared/file-util.h" #include "ivi-application-client-protocol.h" #include "ivi-hmi-controller-client-protocol.h" @@ -805,8 +807,8 @@ createShmBuffer(struct wlContextStruct *p_wlCtx) fd = os_create_anonymous_file(size); if (fd < 0) { - fprintf(stderr, "creating a buffer file for %d B failed: %m\n", - size); + fprintf(stderr, "creating a buffer file for %d B failed: %s\n", + size, strerror(errno)); return ; } @@ -814,7 +816,7 @@ createShmBuffer(struct wlContextStruct *p_wlCtx) mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (MAP_FAILED == p_wlCtx->data) { - fprintf(stderr, "mmap failed: %m\n"); + fprintf(stderr, "mmap failed: %s\n", strerror(errno)); close(fd); return; } @@ -827,7 +829,8 @@ createShmBuffer(struct wlContextStruct *p_wlCtx) WL_SHM_FORMAT_ARGB8888); if (NULL == p_wlCtx->wlBuffer) { - fprintf(stderr, "wl_shm_create_buffer failed: %m\n"); + fprintf(stderr, "wl_shm_create_buffer failed: %s\n", + strerror(errno)); close(fd); return; } @@ -1076,6 +1079,7 @@ hmi_homescreen_setting_create(void) const char *name = NULL; uint32_t workspace_layer_id; uint32_t icon_surface_id = 0; + char *filename; wl_list_init(&setting->workspace_list); wl_list_init(&setting->launcher_list); @@ -1095,51 +1099,65 @@ hmi_homescreen_setting_create(void) weston_config_section_get_uint( shellSection, "workspace-layer-id", &workspace_layer_id, 3000); + filename = file_name_with_datadir("background.png"); weston_config_section_get_string( shellSection, "background-image", &setting->background.filePath, - DATADIR "/weston/background.png"); + filename); + free(filename); weston_config_section_get_uint( shellSection, "background-id", &setting->background.id, 1001); + filename = file_name_with_datadir("panel.png"); weston_config_section_get_string( shellSection, "panel-image", &setting->panel.filePath, - DATADIR "/weston/panel.png"); + filename); + free(filename); weston_config_section_get_uint( shellSection, "panel-id", &setting->panel.id, 1002); + filename = file_name_with_datadir("tiling.png"); weston_config_section_get_string( shellSection, "tiling-image", &setting->tiling.filePath, - DATADIR "/weston/tiling.png"); + filename); + free(filename); weston_config_section_get_uint( shellSection, "tiling-id", &setting->tiling.id, 1003); + filename = file_name_with_datadir("sidebyside.png"); weston_config_section_get_string( shellSection, "sidebyside-image", &setting->sidebyside.filePath, - DATADIR "/weston/sidebyside.png"); + filename); + free(filename); weston_config_section_get_uint( shellSection, "sidebyside-id", &setting->sidebyside.id, 1004); + filename = file_name_with_datadir("fullscreen.png"); weston_config_section_get_string( shellSection, "fullscreen-image", &setting->fullscreen.filePath, - DATADIR "/weston/fullscreen.png"); + filename); + free(filename); weston_config_section_get_uint( shellSection, "fullscreen-id", &setting->fullscreen.id, 1005); + filename = file_name_with_datadir("random.png"); weston_config_section_get_string( shellSection, "random-image", &setting->random.filePath, - DATADIR "/weston/random.png"); + filename); + free(filename); weston_config_section_get_uint( shellSection, "random-id", &setting->random.id, 1006); + filename = file_name_with_datadir("home.png"); weston_config_section_get_string( shellSection, "home-image", &setting->home.filePath, - DATADIR "/weston/home.png"); + filename); + free(filename); weston_config_section_get_uint( shellSection, "home-id", &setting->home.id, 1007); diff --git a/clients/keyboard.c b/clients/keyboard.c index 9af3f419b..e39d76dd2 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -273,11 +274,10 @@ static void __attribute__ ((format (printf, 1, 2))) dbg(const char *fmt, ...) { #ifdef DEBUG - int l; va_list argp; va_start(argp, fmt); - l = vfprintf(stderr, fmt, argp); + vfprintf(stderr, fmt, argp); va_end(argp); #endif } @@ -1020,7 +1020,8 @@ main(int argc, char *argv[]) virtual_keyboard.display = display_create(&argc, argv); if (virtual_keyboard.display == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/meson.build b/clients/meson.build new file mode 100644 index 000000000..af4148f86 --- /dev/null +++ b/clients/meson.build @@ -0,0 +1,388 @@ +if get_option('resize-pool') + config_h.set('USE_RESIZE_POOL', '1') +endif + +srcs_toytoolkit = [ + 'window.c', + xdg_shell_client_protocol_h, + xdg_shell_protocol_c, + text_cursor_position_client_protocol_h, + text_cursor_position_protocol_c, + relative_pointer_unstable_v1_client_protocol_h, + relative_pointer_unstable_v1_protocol_c, + pointer_constraints_unstable_v1_client_protocol_h, + pointer_constraints_unstable_v1_protocol_c, + 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_xkbcommon, + dependency('wayland-cursor'), + cc.find_library('util'), +] +lib_toytoolkit = static_library( + 'toytoolkit', + srcs_toytoolkit, + include_directories: common_inc, + dependencies: deps_toytoolkit, + install: false, +) +dep_toytoolkit = declare_dependency( + link_with: lib_toytoolkit, + dependencies: deps_toytoolkit, +) + +simple_clients = [ + { + 'name': 'damage', + 'sources': [ + 'simple-damage.c', + viewporter_client_protocol_h, + 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-egl', + 'sources': [ + 'simple-dmabuf-egl.c', + linux_dmabuf_unstable_v1_client_protocol_h, + linux_dmabuf_unstable_v1_protocol_c, + linux_explicit_synchronization_unstable_v1_client_protocol_h, + linux_explicit_synchronization_unstable_v1_protocol_c, + xdg_shell_client_protocol_h, + xdg_shell_protocol_c, + weston_direct_display_client_protocol_h, + weston_direct_display_protocol_c, + fullscreen_shell_unstable_v1_client_protocol_h, + fullscreen_shell_unstable_v1_protocol_c, + ], + 'dep_objs': [ + dep_wayland_client, + dep_libdrm, + dep_libm + ], + 'deps': [ 'egl', 'glesv2', 'gbm' ], + 'options': [ 'renderer-gl' ] + }, + { + 'name': 'dmabuf-v4l', + 'sources': [ + 'simple-dmabuf-v4l.c', + linux_dmabuf_unstable_v1_client_protocol_h, + linux_dmabuf_unstable_v1_protocol_c, + xdg_shell_client_protocol_h, + xdg_shell_protocol_c, + weston_direct_display_client_protocol_h, + weston_direct_display_protocol_c, + fullscreen_shell_unstable_v1_client_protocol_h, + fullscreen_shell_unstable_v1_protocol_c, + ], + 'dep_objs': [ dep_wayland_client, dep_libdrm_headers ] + }, + { + 'name': 'egl', + 'sources': [ + 'simple-egl.c', + xdg_shell_client_protocol_h, + xdg_shell_protocol_c, + ivi_application_client_protocol_h, + ivi_application_protocol_c, + ], + 'dep_objs': [ dep_wayland_client, dep_libshared, dep_libm ], + 'deps': [ 'egl', 'wayland-egl', 'glesv2', 'wayland-cursor' ], + 'options': [ 'renderer-gl' ] + }, + # weston-simple-im is handled specially separately due to install_dir and odd window.h usage + { + 'name': 'shm', + 'sources': [ + 'simple-shm.c', + xdg_shell_client_protocol_h, + xdg_shell_protocol_c, + 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': 'touch', + 'sources': [ + 'simple-touch.c', + ], + 'dep_objs': [ dep_wayland_client, dep_libshared ] + }, +] + +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') + t_deps = t.get('dep_objs', []) + foreach depname : t.get('deps', []) + dep = dependency(depname, required: false) + if not dep.found() + error('@0@ requires @1@ which was not found. If you rather not build this, drop "@2@" from simple-clients option.'.format(t_name, depname, t.get('name'))) + endif + t_deps += dep + endforeach + + foreach optname : t.get('options', []) + if not get_option(optname) + error('@0@ requires option @1@ which is not enabled. If you rather not build this, drop "@2@" from simple-clients option.'.format(t_name, optname, t.get('name'))) + endif + endforeach + + executable( + t_name, t.get('sources'), + include_directories: common_inc, + dependencies: t_deps, + install: true + ) + endif +endforeach + +if simple_build_all or simple_clients_enabled.contains('im') + executable( + 'weston-simple-im', [ + 'simple-im.c', + input_method_unstable_v1_client_protocol_h, + input_method_unstable_v1_protocol_c, + ], + include_directories: common_inc, + dependencies: [ + dep_libshared, + dep_wayland_client, + dep_xkbcommon, + dependency('wayland-cursor'), + dependency('cairo') + ], + install: true, + install_dir: dir_libexec + ) +endif + +tools_enabled = get_option('tools') +tools_list = [ + { + 'name': 'calibrator', + 'sources': [ 'calibrator.c' ], + 'deps': [ dep_toytoolkit, dep_matrix_c ], + }, + { + 'name': 'debug', + 'sources': [ + 'weston-debug.c', + weston_debug_client_protocol_h, + weston_debug_protocol_c, + ], + 'deps': [ dep_wayland_client ] + }, + { + 'name': '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' ], + 'deps': [ dep_toytoolkit ], + }, + { + 'name': 'touch-calibrator', + 'sources': [ + 'touch-calibrator.c', + weston_touch_calibration_client_protocol_h, + weston_touch_calibration_protocol_c, + ], + 'deps': [ dep_toytoolkit, dep_matrix_c ], + }, +] + +foreach t : tools_list + if tools_enabled.contains(t.get('name')) + executable( + 'weston-@0@'.format(t.get('name')), + t.get('sources'), + include_directories: common_inc, + dependencies: t.get('deps', []), + install: true + ) + endif +endforeach + +demo_clients = [ + { 'basename': 'clickdot' }, + { + 'basename': 'cliptest', + 'dep_objs': dep_vertex_clipping + }, + { 'basename': 'confine' }, + { + 'basename': 'content_protection', + 'add_sources': [ + weston_content_protection_client_protocol_h, + weston_content_protection_protocol_c, + ] + }, + + { 'basename': 'dnd' }, + { + 'basename': 'editor', + 'add_sources': [ + text_input_unstable_v1_client_protocol_h, + text_input_unstable_v1_protocol_c, + ], + 'deps': [ 'pangocairo', 'gobject-2.0' ] + }, + { 'basename': 'eventdemo' }, + { 'basename': 'flower' }, + { + 'basename': 'fullscreen', + 'add_sources': [ + 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' }, + { + 'basename': 'scaler', + 'add_sources': [ + viewporter_client_protocol_h, + viewporter_protocol_c, + ] + }, + { 'basename': 'smoke' }, + { 'basename': 'stacking' }, + { + 'basename': 'subsurfaces', + 'deps': [ 'egl', 'glesv2', 'wayland-egl' ] + }, + { 'basename': 'transformed' }, +] + +if get_option('demo-clients') + foreach t : demo_clients + t_name = 'weston-' + t.get('basename') + t_srcs = [ t.get('basename') + '.c' ] + t.get('add_sources', []) + t_deps = [ dep_toytoolkit, t.get('dep_objs', []) ] + foreach depname : t.get('deps', []) + dep = dependency(depname, required: false) + if not dep.found() + error('@0@ requires \'@1@\' which was not found. If you rather not build this, set \'-Ddemo-clients=false\'.'.format(t_name, depname)) + endif + t_deps += dep + endforeach + + executable( + t_name, t_srcs, + include_directories: common_inc, + dependencies: t_deps, + install: true + ) + endforeach +endif + +if get_option('shell-desktop') + exe_keyboard = executable( + 'weston-keyboard', + 'keyboard.c', + text_input_unstable_v1_client_protocol_h, + text_input_unstable_v1_protocol_c, + input_method_unstable_v1_client_protocol_h, + input_method_unstable_v1_protocol_c, + include_directories: common_inc, + dependencies: dep_toytoolkit, + install_dir: get_option('libexecdir'), + install: true + ) + env_modmap += 'weston-keyboard=@0@;'.format(exe_keyboard.full_path()) + + exe_shooter = executable( + 'weston-screenshooter', + 'screenshot.c', + weston_screenshooter_client_protocol_h, + weston_screenshooter_protocol_c, + include_directories: common_inc, + 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, + include_directories: common_inc, + dependencies: dep_toytoolkit, + install_dir: get_option('libexecdir'), + install: true + ) + env_modmap += 'weston-desktop-shell=@0@;'.format(exe_shell_desktop.full_path()) +endif + + +if get_option('shell-ivi') + exe_shell_ivi_ui = executable( + 'weston-ivi-shell-user-interface', + 'ivi-shell-user-interface.c', + ivi_hmi_controller_client_protocol_h, + ivi_hmi_controller_protocol_c, + ivi_application_client_protocol_h, + ivi_application_protocol_c, + include_directories: common_inc, + dependencies: dep_toytoolkit, + install: true, + install_dir: get_option('libexecdir') + ) + env_modmap += 'weston-ivi-shell-user-interface=@0@;'.format(exe_shell_ivi_ui.full_path()) +endif + + +if get_option('shell-rdprail') + exe_shell_rdprail = executable( + 'weston-rdprail-shell', + 'rdprail-shell.c', + weston_rdprail_shell_client_protocol_h, + weston_rdprail_shell_protocol_c, + include_directories: common_inc, + dependencies: dep_toytoolkit, + install: true, + install_dir: get_option('libexecdir') + ) + env_modmap += 'weston-rdprail-shell=@0@;'.format(exe_shell_rdprail.full_path()) +endif diff --git a/clients/multi-resource.c b/clients/multi-resource.c index 2be0a7e30..b86503db9 100644 --- a/clients/multi-resource.c +++ b/clients/multi-resource.c @@ -35,14 +35,14 @@ #include #include #include -#include +#include #include #include #include #include "shared/os-compatibility.h" #include "shared/xalloc.h" -#include "shared/zalloc.h" +#include struct device { enum { KEYBOARD, POINTER } type; @@ -97,8 +97,8 @@ attach_buffer(struct window *window, int width, int height) fd = os_create_anonymous_file(size); if (fd < 0) { - fprintf(stderr, "creating a buffer file for %d B failed: %m\n", - size); + fprintf(stderr, "creating a buffer file for %d B failed: %s\n", + size, strerror(errno)); return -1; } @@ -296,6 +296,8 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { + /* Just so we don’t leak the keymap fd */ + close(fd); } static void diff --git a/clients/nested-client.c b/clients/nested-client.c index 5027075c8..a9e034efb 100644 --- a/clients/nested-client.c +++ b/clients/nested-client.c @@ -34,7 +34,7 @@ #include #include -#include "../shared/platform.h" +#include "shared/platform.h" struct window; struct seat; @@ -81,7 +81,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; @@ -111,7 +111,7 @@ create_program(struct nested_client *client, char log[1000]; GLsizei len; glGetProgramInfoLog(client->program, 1000, &len, log); - fprintf(stderr, "Error: linking:\n%*s\n", len, log); + fprintf(stderr, "Error: linking:\n%.*s\n", len, log); exit(1); } @@ -343,10 +343,10 @@ nested_client_destroy(struct nested_client *client) wl_compositor_destroy(client->compositor); wl_registry_destroy(client->registry); - wl_display_flush(client->display); - wl_display_disconnect(client->display); eglTerminate(client->egl_display); eglReleaseThread(); + wl_display_flush(client->display); + wl_display_disconnect(client->display); } int diff --git a/clients/nested.c b/clients/nested.c index 173076a68..4bbaa27ea 100644 --- a/clients/nested.c +++ b/clients/nested.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -50,10 +51,10 @@ #include "shared/xalloc.h" #include "window.h" -#include "weston-egl-ext.h" +#include "shared/weston-egl-ext.h" -static int option_blit; +static bool option_blit; struct nested { struct display *display; @@ -228,8 +229,7 @@ nested_buffer_reference(struct nested_buffer_reference *ref, ref->buffer->busy_count--; if (ref->buffer->busy_count == 0) { assert(wl_resource_get_client(ref->buffer->resource)); - wl_resource_queue_event(ref->buffer->resource, - WL_BUFFER_RELEASE); + wl_buffer_send_release(ref->buffer->resource); } wl_list_remove(&ref->destroy_listener.link); } @@ -331,8 +331,8 @@ launch_client(struct nested *nested, const char *path) if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) { fprintf(stderr, "launch_client: " - "socketpair failed while launching '%s': %m\n", - path); + "socketpair failed while launching '%s': %s\n", + path, strerror(errno)); free(client); return NULL; } @@ -343,7 +343,8 @@ launch_client(struct nested *nested, const char *path) close(sv[1]); free(client); fprintf(stderr, "launch_client: " - "fork failed while launching '%s': %m\n", path); + "fork failed while launching '%s': %s\n", path, + strerror(errno)); return NULL; } @@ -355,7 +356,8 @@ launch_client(struct nested *nested, const char *path) * get a non-CLOEXEC fd to pass through exec. */ clientfd = dup(sv[1]); if (clientfd == -1) { - fprintf(stderr, "compositor: dup failed: %m\n"); + fprintf(stderr, "compositor: dup failed: %s\n", + strerror(errno)); exit(-1); } @@ -364,8 +366,8 @@ launch_client(struct nested *nested, const char *path) execl(path, path, NULL); - fprintf(stderr, "compositor: executing '%s' failed: %m\n", - path); + fprintf(stderr, "compositor: executing '%s' failed: %s\n", + path, strerror(errno)); exit(-1); } @@ -748,7 +750,7 @@ nested_init_compositor(struct nested *nested) nested->egl_display = display_get_egl_display(nested->display); extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS); - if (weston_check_egl_extension(extensions, "EGL_WL_bind_wayland_display") == NULL) { + if (!weston_check_egl_extension(extensions, "EGL_WL_bind_wayland_display")) { fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n"); return -1; } @@ -1117,7 +1119,8 @@ main(int argc, char *argv[]) display = display_create(&argc, argv); if (display == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/presentation-shm.c b/clients/presentation-shm.c index c9fb66cca..d5d73a2a8 100644 --- a/clients/presentation-shm.c +++ b/clients/presentation-shm.c @@ -35,12 +35,15 @@ #include #include #include +#include #include #include "shared/helpers.h" -#include "shared/zalloc.h" +#include +#include "shared/timespec-util.h" #include "shared/os-compatibility.h" #include "presentation-time-client-protocol.h" +#include "xdg-shell-client-protocol.h" enum run_mode { RUN_MODE_FEEDBACK, @@ -64,7 +67,7 @@ struct display { struct wl_display *display; struct wl_registry *registry; struct wl_compositor *compositor; - struct wl_shell *shell; + struct xdg_wm_base *wm_base; struct wl_shm *shm; uint32_t formats; @@ -97,7 +100,9 @@ struct window { int width, height; enum run_mode mode; struct wl_surface *surface; - struct wl_shell_surface *shell_surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + uint32_t configure_serial; struct buffer *buffers; int num_buffers; @@ -140,14 +145,14 @@ create_shm_buffers(struct display *display, struct buffer **buffers, fd = os_create_anonymous_file(size); if (fd < 0) { - fprintf(stderr, "creating a buffer file for %d B failed: %m\n", - size); + 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: %m\n"); + fprintf(stderr, "mmap failed: %s\n", strerror(errno)); close(fd); return -1; } @@ -179,27 +184,48 @@ create_shm_buffers(struct display *display, struct buffer **buffers, } static void -handle_ping(void *data, struct wl_shell_surface *shell_surface, - uint32_t serial) +xdg_wm_base_handle_ping(void *data, struct xdg_wm_base *xdg_wm_base, + uint32_t serial) { - wl_shell_surface_pong(shell_surface, serial); + xdg_wm_base_pong(xdg_wm_base, serial); } +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + .ping = xdg_wm_base_handle_ping, +}; + +static void +xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, + uint32_t serial) +{ + struct window *window = data; + + window->configure_serial = serial; +} + +static const struct xdg_surface_listener xdg_surface_listener = { + .configure = xdg_surface_handle_configure, +}; + + static void -handle_configure(void *data, struct wl_shell_surface *shell_surface, - uint32_t edges, int32_t width, int32_t height) +xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *xdg_toplevel, + int32_t width, int32_t height, + struct wl_array *states) { + /* noop */ } static void -handle_popup_done(void *data, struct wl_shell_surface *shell_surface) +xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) { + fprintf(stderr, "presentation-shm exiting\n"); + exit(0); } -static const struct wl_shell_surface_listener shell_surface_listener = { - handle_ping, - handle_configure, - handle_popup_done +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + .configure = xdg_toplevel_handle_configure, + .close = xdg_toplevel_handle_close, }; static struct window * @@ -226,16 +252,30 @@ 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); - if (window->shell_surface) - wl_shell_surface_add_listener(window->shell_surface, - &shell_surface_listener, window); + if (!window->xdg_surface) + return NULL; + + window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface); + + if (!window->xdg_toplevel) + return NULL; + + xdg_wm_base_add_listener(display->wm_base, &xdg_wm_base_listener, + NULL); + xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, + window); + xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener, + window); - wl_shell_surface_set_title(window->shell_surface, title); + xdg_toplevel_set_title(window->xdg_toplevel, title); + xdg_toplevel_set_min_size(window->xdg_toplevel, width, height); + xdg_toplevel_set_max_size(window->xdg_toplevel, width, height); - wl_shell_surface_set_toplevel(window->shell_surface); + wl_surface_commit(window->surface); + wl_display_roundtrip(window->display->display); window->num_buffers = 60; window->refresh_nsec = NSEC_PER_SEC / 60; /* 60 Hz guess */ @@ -275,7 +315,7 @@ destroy_window(struct window *window) if (window->callback) wl_callback_destroy(window->callback); - wl_shell_surface_destroy(window->shell_surface); + xdg_surface_destroy(window->xdg_surface); wl_surface_destroy(window->surface); for (i = 0; i < window->num_buffers; i++) @@ -383,14 +423,6 @@ timespec_to_ms(const struct timespec *ts) return (uint32_t)ts->tv_sec * 1000 + ts->tv_nsec / 1000000; } -static void -timespec_from_proto(struct timespec *tm, uint32_t tv_sec_hi, - uint32_t tv_sec_lo, uint32_t tv_nsec) -{ - tm->tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo; - tm->tv_nsec = tv_nsec; -} - static int timespec_diff_to_usec(const struct timespec *a, const struct timespec *b) { @@ -487,7 +519,7 @@ window_emulate_rendering(struct window *window) ret = nanosleep(&delay, NULL); if (ret) - printf("nanosleep failed: %m\n"); + printf("nanosleep failed: %s\n", strerror(errno)); } static void @@ -528,6 +560,12 @@ window_commit_next(struct window *window) buffer = window_next_buffer(window); assert(buffer); + if (window->configure_serial) { + xdg_surface_ack_configure(window->xdg_surface, + window->configure_serial); + window->configure_serial = 0; + } + wl_surface_attach(window->surface, buffer->buffer, 0, 0); wl_surface_damage(window->surface, 0, 0, window->width, window->height); wl_surface_commit(window->surface); @@ -724,9 +762,10 @@ registry_handle_global(void *data, struct wl_registry *registry, d->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); - } else if (strcmp(interface, "wl_shell") == 0) { - d->shell = wl_registry_bind(registry, - name, &wl_shell_interface, 1); + } else if (strcmp(interface, "xdg_wm_base") == 0) { + d->wm_base = + wl_registry_bind(registry, name, + &xdg_wm_base_interface, version); } else if (strcmp(interface, "wl_shm") == 0) { d->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); @@ -812,8 +851,8 @@ destroy_display(struct display *display) if (display->shm) wl_shm_destroy(display->shm); - if (display->shell) - wl_shell_destroy(display->shell); + if (display->wm_base) + xdg_wm_base_destroy(display->wm_base); if (display->compositor) wl_compositor_destroy(display->compositor); diff --git a/clients/rdprail-shell.c b/clients/rdprail-shell.c new file mode 100644 index 000000000..69d6af8ca --- /dev/null +++ b/clients/rdprail-shell.c @@ -0,0 +1,182 @@ +/* + * Copyright © 2011 Kristian Høgsberg + * Copyright © 2011 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "window.h" +#include "shared/cairo-util.h" +#include +#include "shared/helpers.h" +#include "shared/xalloc.h" +#include +#include "shared/file-util.h" + +#include "weston-rdprail-shell-client-protocol.h" + +struct focus_proxy_window { + struct window *window; + struct widget *widget; +}; + +struct desktop { + struct display *display; + struct weston_rdprail_shell *shell; + + struct focus_proxy_window *focus_proxy_window; +}; + +static void +focus_proxy_window_redraw_handler(struct widget *widget, void *data) +{ +} + +static void +focus_proxy_window_resize_handler(struct widget *widget, + int32_t width, int32_t height, void *data) +{ +} + +static struct focus_proxy_window * +focus_proxy_create(struct desktop *desktop) +{ + struct focus_proxy_window *focus_proxy_window = NULL; + + focus_proxy_window = xzalloc(sizeof *focus_proxy_window); + if (!focus_proxy_window) + goto error_exit; + + focus_proxy_window->window = window_create(desktop->display); + if (!focus_proxy_window->window) + goto error_exit; + + focus_proxy_window->widget = window_add_widget(focus_proxy_window->window, focus_proxy_window); + if (!focus_proxy_window->widget) + goto error_exit; + + widget_set_allocation(focus_proxy_window->widget, 0, 0, 0, 0); + + window_set_title(focus_proxy_window->window, "rdprail-shell focus proxy window"); + window_set_user_data(focus_proxy_window->window, focus_proxy_window); + + widget_set_redraw_handler(focus_proxy_window->widget, focus_proxy_window_redraw_handler); + widget_set_resize_handler(focus_proxy_window->widget, focus_proxy_window_resize_handler); + + struct wl_surface *s = window_get_wl_surface(focus_proxy_window->window); + weston_rdprail_shell_set_focus_proxy(desktop->shell, s); + + return focus_proxy_window; + +error_exit: + if (focus_proxy_window && focus_proxy_window->widget) + widget_destroy(focus_proxy_window->widget); + + if (focus_proxy_window && focus_proxy_window->window) + window_destroy(focus_proxy_window->window); + + if (focus_proxy_window) + free(focus_proxy_window); + + return NULL; +} + +static void +focus_proxy_destroy(struct focus_proxy_window* focus_proxy_window) +{ + widget_destroy(focus_proxy_window->widget); + window_destroy(focus_proxy_window->window); + + free(focus_proxy_window); +} + +static void +global_handler(struct display *display, uint32_t id, + const char *interface, uint32_t version, void *data) +{ + struct desktop *desktop = data; + + if (!strcmp(interface, "weston_rdprail_shell")) { + desktop->shell = display_bind(desktop->display, + id, + &weston_rdprail_shell_interface, + 1); + } +} + +static void +sigchild_handler(int s) +{ + int status; + pid_t pid; + + while (pid = waitpid(-1, &status, WNOHANG), pid > 0) + fprintf(stderr, "child %d exited\n", pid); +} + +int main(int argc, char *argv[]) +{ + struct desktop desktop = { 0 }; + + desktop.display = display_create(&argc, argv); + if (desktop.display == NULL) { + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); + return -1; + } + + display_set_user_data(desktop.display, &desktop); + display_set_global_handler(desktop.display, global_handler); + + desktop.focus_proxy_window = focus_proxy_create(&desktop); + if (desktop.focus_proxy_window == NULL) { + fprintf(stderr, "failed to create focus proxy window.\n"); + return -1; + } + + signal(SIGCHLD, sigchild_handler); + + display_run(desktop.display); + + focus_proxy_destroy(desktop.focus_proxy_window); + weston_rdprail_shell_destroy(desktop.shell); + display_destroy(desktop.display); + + return 0; +} diff --git a/clients/resizor.c b/clients/resizor.c index 5d342e1fc..cfc5d419e 100644 --- a/clients/resizor.c +++ b/clients/resizor.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -53,6 +54,7 @@ struct resizor { struct spring height; struct wl_callback *frame_callback; bool pointer_locked; + bool locked_frame_callback_registered; struct input *locked_input; float pointer_x; float pointer_y; @@ -330,11 +332,15 @@ button_handler(struct widget *widget, handle_pointer_unlocked); resizor->locked_input = input; + if (resizor->locked_frame_callback_registered) + return; + surface = window_get_wl_surface(resizor->window); callback = wl_surface_frame(surface); wl_callback_add_listener(callback, &locked_pointer_frame_listener, resizor); + resizor->locked_frame_callback_registered = true; } else if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_RELEASED) { input_set_pointer_image(input, CURSOR_LEFT_PTR); @@ -434,7 +440,8 @@ main(int argc, char *argv[]) display = display_create(&argc, argv); if (display == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/scaler.c b/clients/scaler.c index 23cc3a495..91736fb36 100644 --- a/clients/scaler.c +++ b/clients/scaler.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -288,7 +289,8 @@ main(int argc, char *argv[]) d = display_create(&argc, argv); if (d == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/screenshot.c b/clients/screenshot.c index 6e43d5ce4..bbf2e6bdf 100644 --- a/clients/screenshot.c +++ b/clients/screenshot.c @@ -39,16 +39,12 @@ #include "weston-screenshooter-client-protocol.h" #include "shared/os-compatibility.h" #include "shared/xalloc.h" +#include "shared/file-util.h" /* 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 */ -static struct wl_shm *shm; -static struct weston_screenshooter *screenshooter; -static struct wl_list output_list; -int min_x, min_y, max_x, max_y; -int buffer_copy_done; struct screenshooter_output { struct wl_output *output; @@ -58,6 +54,22 @@ struct screenshooter_output { struct wl_list link; }; +struct buffer_size { + int width, height; + + int min_x, min_y; + int max_x, max_y; +}; + +struct screenshooter_data { + struct wl_shm *shm; + struct wl_list output_list; + + struct weston_screenshooter *screenshooter; + int buffer_copy_done; +}; + + static void display_handle_geometry(void *data, struct wl_output *wl_output, @@ -106,7 +118,8 @@ static const struct wl_output_listener output_listener = { static void screenshot_done(void *data, struct weston_screenshooter *screenshooter) { - buffer_copy_done = 1; + struct screenshooter_data *sh_data = data; + sh_data->buffer_copy_done = 1; } static const struct weston_screenshooter_listener screenshooter_listener = { @@ -118,19 +131,20 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { static struct screenshooter_output *output; + struct screenshooter_data *sh_data = data; if (strcmp(interface, "wl_output") == 0) { output = xmalloc(sizeof *output); output->output = wl_registry_bind(registry, name, &wl_output_interface, 1); - wl_list_insert(&output_list, &output->link); + wl_list_insert(&sh_data->output_list, &output->link); wl_output_add_listener(output->output, &output_listener, output); } else if (strcmp(interface, "wl_shm") == 0) { - shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + sh_data->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, "weston_screenshooter") == 0) { - screenshooter = wl_registry_bind(registry, name, - &weston_screenshooter_interface, - 1); + sh_data->screenshooter = wl_registry_bind(registry, name, + &weston_screenshooter_interface, + 1); } } @@ -146,7 +160,8 @@ static const struct wl_registry_listener registry_listener = { }; static struct wl_buffer * -create_shm_buffer(int width, int height, void **data_out) +screenshot_create_shm_buffer(int width, int height, void **data_out, + struct wl_shm *shm) { struct wl_shm_pool *pool; struct wl_buffer *buffer; @@ -158,14 +173,14 @@ create_shm_buffer(int width, int height, void **data_out) fd = os_create_anonymous_file(size); if (fd < 0) { - fprintf(stderr, "creating a buffer file for %d B failed: %m\n", - size); + fprintf(stderr, "creating a buffer file for %d B failed: %s\n", + size, strerror(errno)); return NULL; } data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { - fprintf(stderr, "mmap failed: %m\n"); + fprintf(stderr, "mmap failed: %s\n", strerror(errno)); close(fd); return NULL; } @@ -182,24 +197,27 @@ create_shm_buffer(int width, int height, void **data_out) } static void -write_png(int width, int height) +screenshot_write_png(const struct buffer_size *buff_size, + struct wl_list *output_list) { int output_stride, buffer_stride, i; cairo_surface_t *surface; void *data, *d, *s; struct screenshooter_output *output, *next; + FILE *fp; + char filepath[PATH_MAX]; - buffer_stride = width * 4; + buffer_stride = buff_size->width * 4; - data = xmalloc(buffer_stride * height); + data = xmalloc(buffer_stride * buff_size->height); if (!data) return; - wl_list_for_each_safe(output, next, &output_list, link) { + wl_list_for_each_safe(output, next, output_list, link) { output_stride = output->width * 4; s = output->data; - d = data + (output->offset_y - min_y) * buffer_stride + - (output->offset_x - min_x) * 4; + d = data + (output->offset_y - buff_size->min_y) * buffer_stride + + (output->offset_x - buff_size->min_x) * 4; for (i = 0; i < output->height; i++) { memcpy(d, s, output_stride); @@ -212,37 +230,48 @@ write_png(int width, int height) surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, - width, height, buffer_stride); - cairo_surface_write_to_png(surface, "wayland-screenshot.png"); + buff_size->width, + buff_size->height, + buffer_stride); + + fp = file_create_dated(getenv("XDG_PICTURES_DIR"), "wayland-screenshot-", + ".png", filepath, sizeof(filepath)); + if (fp) { + fclose (fp); + cairo_surface_write_to_png(surface, filepath); + } cairo_surface_destroy(surface); free(data); } static int -set_buffer_size(int *width, int *height) +screenshot_set_buffer_size(struct buffer_size *buff_size, struct wl_list *output_list) { struct screenshooter_output *output; - min_x = min_y = INT_MAX; - max_x = 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) { + wl_list_for_each_reverse(output, output_list, link) { output->offset_x = position; 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->width); - max_y = MAX(max_y, output->offset_y + output->height); + wl_list_for_each(output, output_list, link) { + buff_size->min_x = MIN(buff_size->min_x, output->offset_x); + buff_size->min_y = MIN(buff_size->min_y, output->offset_y); + buff_size->max_x = + MAX(buff_size->max_x, output->offset_x + output->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; - *width = max_x - min_x; - *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; } @@ -252,50 +281,49 @@ int main(int argc, char *argv[]) struct wl_display *display; struct wl_registry *registry; struct screenshooter_output *output; - int width, height; - - if (getenv("WAYLAND_SOCKET") == NULL) { - fprintf(stderr, "%s must be launched by weston.\n" - "Use the MOD+S shortcut to take a screenshot.\n", - program_invocation_short_name); - return -1; - } + struct buffer_size buff_size = {}; + struct screenshooter_data sh_data = {}; display = wl_display_connect(NULL); if (display == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } - wl_list_init(&output_list); + wl_list_init(&sh_data.output_list); registry = wl_display_get_registry(display); - wl_registry_add_listener(registry, ®istry_listener, NULL); + wl_registry_add_listener(registry, ®istry_listener, &sh_data); wl_display_dispatch(display); wl_display_roundtrip(display); - if (screenshooter == NULL) { + if (sh_data.screenshooter == NULL) { fprintf(stderr, "display doesn't support screenshooter\n"); return -1; } - weston_screenshooter_add_listener(screenshooter, + weston_screenshooter_add_listener(sh_data.screenshooter, &screenshooter_listener, - screenshooter); + &sh_data); - if (set_buffer_size(&width, &height)) + if (screenshot_set_buffer_size(&buff_size, &sh_data.output_list)) return -1; - wl_list_for_each(output, &output_list, link) { - output->buffer = create_shm_buffer(output->width, output->height, &output->data); - weston_screenshooter_shoot(screenshooter, + 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); - buffer_copy_done = 0; - while (!buffer_copy_done) + sh_data.buffer_copy_done = 0; + while (!sh_data.buffer_copy_done) wl_display_roundtrip(display); } - write_png(width, height); + screenshot_write_png(&buff_size, &sh_data.output_list); return 0; } diff --git a/clients/simple-damage.c b/clients/simple-damage.c index ea2d3f94b..821b42b54 100644 --- a/clients/simple-damage.c +++ b/clients/simple-damage.c @@ -35,11 +35,12 @@ #include #include #include +#include #include #include "shared/os-compatibility.h" -#include "shared/zalloc.h" -#include "xdg-shell-unstable-v6-client-protocol.h" +#include +#include "xdg-shell-client-protocol.h" #include "fullscreen-shell-unstable-v1-client-protocol.h" #include "viewporter-client-protocol.h" @@ -51,7 +52,7 @@ struct display { int compositor_version; struct wl_compositor *compositor; struct wp_viewporter *viewporter; - struct zxdg_shell_v6 *shell; + struct xdg_wm_base *wm_base; struct zwp_fullscreen_shell_v1 *fshell; struct wl_shm *shm; uint32_t formats; @@ -74,8 +75,8 @@ struct window { int width, height, border; struct wl_surface *surface; struct wp_viewport *viewport; - struct zxdg_surface_v6 *xdg_surface; - struct zxdg_toplevel_v6 *xdg_toplevel; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; struct wl_callback *callback; struct buffer buffers[2]; struct buffer *prev_buffer; @@ -123,14 +124,14 @@ create_shm_buffer(struct display *display, struct buffer *buffer, fd = os_create_anonymous_file(size); if (fd < 0) { - fprintf(stderr, "creating a buffer file for %d B failed: %m\n", - size); + 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: %m\n"); + fprintf(stderr, "mmap failed: %s\n", strerror(errno)); close(fd); return -1; } @@ -149,12 +150,12 @@ create_shm_buffer(struct display *display, struct buffer *buffer, } static void -xdg_surface_handle_configure(void *data, struct zxdg_surface_v6 *surface, +xdg_surface_handle_configure(void *data, struct xdg_surface *surface, uint32_t serial) { struct window *window = data; - zxdg_surface_v6_ack_configure(surface, serial); + xdg_surface_ack_configure(surface, serial); if (window->wait_for_configure) { redraw(window, NULL, 0); @@ -162,24 +163,24 @@ xdg_surface_handle_configure(void *data, struct zxdg_surface_v6 *surface, } } -static const struct zxdg_surface_v6_listener xdg_surface_listener = { +static const struct xdg_surface_listener xdg_surface_listener = { xdg_surface_handle_configure, }; static void -xdg_toplevel_handle_configure(void *data, struct zxdg_toplevel_v6 *toplevel, +xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *states) { } static void -xdg_toplevel_handle_close(void *data, struct zxdg_toplevel_v6 *xdg_toplevel) +xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) { running = 0; } -static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = { +static const struct xdg_toplevel_listener xdg_toplevel_listener = { xdg_toplevel_handle_configure, xdg_toplevel_handle_close, }; @@ -317,25 +318,25 @@ create_window(struct display *display, int width, int height, window->viewport = wp_viewporter_get_viewport(display->viewporter, window->surface); - if (display->shell) { + if (display->wm_base) { window->xdg_surface = - zxdg_shell_v6_get_xdg_surface(display->shell, - window->surface); + xdg_wm_base_get_xdg_surface(display->wm_base, + window->surface); assert(window->xdg_surface); - zxdg_surface_v6_add_listener(window->xdg_surface, - &xdg_surface_listener, window); + xdg_surface_add_listener(window->xdg_surface, + &xdg_surface_listener, window); window->xdg_toplevel = - zxdg_surface_v6_get_toplevel(window->xdg_surface); + xdg_surface_get_toplevel(window->xdg_surface); assert(window->xdg_toplevel); - zxdg_toplevel_v6_add_listener(window->xdg_toplevel, - &xdg_toplevel_listener, window); + xdg_toplevel_add_listener(window->xdg_toplevel, + &xdg_toplevel_listener, window); - zxdg_toplevel_v6_set_title(window->xdg_toplevel, "simple-damage"); + xdg_toplevel_set_title(window->xdg_toplevel, "simple-damage"); window->wait_for_configure = true; wl_surface_commit(window->surface); @@ -370,9 +371,9 @@ destroy_window(struct window *window) wl_buffer_destroy(window->buffers[1].buffer); if (window->xdg_toplevel) - zxdg_toplevel_v6_destroy(window->xdg_toplevel); + xdg_toplevel_destroy(window->xdg_toplevel); if (window->xdg_surface) - zxdg_surface_v6_destroy(window->xdg_surface); + xdg_surface_destroy(window->xdg_surface); if (window->viewport) wp_viewport_destroy(window->viewport); wl_surface_destroy(window->surface); @@ -460,32 +461,32 @@ window_get_transformed_ball(struct window *window, float *bx, float *by) *by = wy; break; case WL_OUTPUT_TRANSFORM_90: - *bx = window->height - wy; - *by = wx; + *bx = wy; + *by = window->width - wx; break; case WL_OUTPUT_TRANSFORM_180: *bx = window->width - wx; *by = window->height - wy; break; case WL_OUTPUT_TRANSFORM_270: - *bx = wy; - *by = window->width - wx; + *bx = window->height - wy; + *by = wx; break; case WL_OUTPUT_TRANSFORM_FLIPPED: *bx = window->width - wx; *by = wy; break; case WL_OUTPUT_TRANSFORM_FLIPPED_90: - *bx = window->height - wy; - *by = window->width - wx; + *bx = wy; + *by = wx; break; case WL_OUTPUT_TRANSFORM_FLIPPED_180: *bx = wx; *by = window->height - wy; break; case WL_OUTPUT_TRANSFORM_FLIPPED_270: - *bx = wy; - *by = wx; + *bx = window->height - wy; + *by = window->width - wx; break; } @@ -569,32 +570,32 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) off_x = tx; break; case WL_OUTPUT_TRANSFORM_90: - off_y = tx; - off_x = bwidth - ty; + off_y = bheight - tx; + off_x = ty; break; case WL_OUTPUT_TRANSFORM_180: off_y = bheight - ty; off_x = bwidth - tx; break; case WL_OUTPUT_TRANSFORM_270: - off_y = bheight - tx; - off_x = ty; + off_y = tx; + off_x = bwidth - ty; break; case WL_OUTPUT_TRANSFORM_FLIPPED: off_y = ty; off_x = bwidth - tx; break; case WL_OUTPUT_TRANSFORM_FLIPPED_90: - off_y = bheight - tx; - off_x = bwidth - ty; + off_y = tx; + off_x = ty; break; case WL_OUTPUT_TRANSFORM_FLIPPED_180: off_y = bheight - ty; off_x = tx; break; case WL_OUTPUT_TRANSFORM_FLIPPED_270: - off_y = tx; - off_x = ty; + off_y = bheight - tx; + off_x = bwidth - ty; break; } wp_viewport_set_source(window->viewport, @@ -711,13 +712,13 @@ struct wl_shm_listener shm_listener = { }; static void -xdg_shell_ping(void *data, struct zxdg_shell_v6*shell, uint32_t serial) +xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) { - zxdg_shell_v6_pong(shell, serial); + xdg_wm_base_pong(shell, serial); } -static const struct zxdg_shell_v6_listener xdg_shell_listener = { - xdg_shell_ping, +static const struct xdg_wm_base_listener wm_base_listener = { + xdg_wm_base_ping, }; static void @@ -743,10 +744,10 @@ registry_handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, "wp_viewporter") == 0) { d->viewporter = wl_registry_bind(registry, id, &wp_viewporter_interface, 1); - } else if (strcmp(interface, "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, "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_fullscreen_shell_v1") == 0) { d->fshell = wl_registry_bind(registry, id, &zwp_fullscreen_shell_v1_interface, 1); @@ -808,8 +809,8 @@ destroy_display(struct display *display) if (display->shm) wl_shm_destroy(display->shm); - if (display->shell) - zxdg_shell_v6_destroy(display->shell); + if (display->wm_base) + xdg_wm_base_destroy(display->wm_base); if (display->fshell) zwp_fullscreen_shell_v1_release(display->fshell); diff --git a/clients/simple-dmabuf-egl.c b/clients/simple-dmabuf-egl.c new file mode 100644 index 000000000..10e72a9bc --- /dev/null +++ b/clients/simple-dmabuf-egl.c @@ -0,0 +1,1562 @@ +/* + * Copyright © 2011 Benjamin Franzke + * Copyright © 2010 Intel Corporation + * Copyright © 2014,2018 Collabora Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "shared/helpers.h" +#include "shared/platform.h" +#include +#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 "linux-explicit-synchronization-unstable-v1-client-protocol.h" + +#include +#include +#include +#include + +#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 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; + uint64_t *modifiers; + int modifiers_count; + int req_dmabuf_immediate; + bool use_explicit_sync; + 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; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d; + PFNEGLCREATESYNCKHRPROC create_sync; + PFNEGLDESTROYSYNCKHRPROC destroy_sync; + PFNEGLCLIENTWAITSYNCKHRPROC client_wait_sync; + PFNEGLDUPNATIVEFENCEFDANDROIDPROC dup_native_fence_fd; + PFNEGLWAITSYNCKHRPROC wait_sync; + } egl; + struct { + int drm_fd; + struct gbm_device *device; + } gbm; +}; + +struct buffer { + struct display *display; + struct wl_buffer *buffer; + int busy; + + struct gbm_bo *bo; + + int width; + int height; + int format; + uint64_t modifier; + int plane_count; + int dmabuf_fds[MAX_BUFFER_PLANES]; + uint32_t strides[MAX_BUFFER_PLANES]; + uint32_t offsets[MAX_BUFFER_PLANES]; + + EGLImageKHR egl_image; + GLuint gl_texture; + GLuint gl_fbo; + + struct zwp_linux_buffer_release_v1 *buffer_release; + /* The buffer owns the release_fence_fd, until it passes ownership + * to it to EGL (see wait_for_buffer_release_fence). */ + int release_fence_fd; +}; + +#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 zwp_linux_surface_synchronization_v1 *surface_sync; + struct buffer buffers[NUM_BUFFERS]; + struct wl_callback *callback; + bool initialized; + bool wait_for_configure; + struct { + GLuint program; + GLuint pos; + GLuint color; + GLuint offset_uniform; + } gl; + bool render_mandelbrot; +}; + +static sig_atomic_t running = 1; + +static void +redraw(void *data, struct wl_callback *callback, uint32_t time); + +static void +buffer_release(void *data, struct wl_buffer *buffer) +{ + struct buffer *mybuf = data; + + mybuf->busy = 0; +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_release +}; + +static void +buffer_free(struct buffer *buf) +{ + int i; + + if (buf->release_fence_fd >= 0) + close(buf->release_fence_fd); + + if (buf->buffer_release) + zwp_linux_buffer_release_v1_destroy(buf->buffer_release); + + if (buf->gl_fbo) + glDeleteFramebuffers(1, &buf->gl_fbo); + + if (buf->gl_texture) + glDeleteTextures(1, &buf->gl_texture); + + if (buf->egl_image) { + buf->display->egl.destroy_image(buf->display->egl.display, + buf->egl_image); + } + + if (buf->buffer) + wl_buffer_destroy(buf->buffer); + + if (buf->bo) + gbm_bo_destroy(buf->bo); + + for (i = 0; i < buf->plane_count; ++i) { + if (buf->dmabuf_fds[i] >= 0) + close(buf->dmabuf_fds[i]); + } +} + +static void +create_succeeded(void *data, + struct zwp_linux_buffer_params_v1 *params, + struct wl_buffer *new_buffer) +{ + struct buffer *buffer = data; + + buffer->buffer = new_buffer; + /* When not using explicit synchronization listen to wl_buffer.release + * for release notifications, otherwise we are going to use + * zwp_linux_buffer_release_v1. */ + if (!buffer->display->use_explicit_sync) { + wl_buffer_add_listener(buffer->buffer, &buffer_listener, + buffer); + } + + zwp_linux_buffer_params_v1_destroy(params); +} + +static void +create_failed(void *data, struct zwp_linux_buffer_params_v1 *params) +{ + struct buffer *buffer = data; + + buffer->buffer = NULL; + running = 0; + + zwp_linux_buffer_params_v1_destroy(params); + + fprintf(stderr, "Error: zwp_linux_buffer_params.create failed.\n"); +} + +static const struct zwp_linux_buffer_params_v1_listener params_listener = { + create_succeeded, + create_failed +}; + +static bool +create_fbo_for_buffer(struct display *display, struct buffer *buffer) +{ + static const int general_attribs = 3; + static const int plane_attribs = 5; + static const int entries_per_attrib = 2; + EGLint attribs[(general_attribs + plane_attribs * MAX_BUFFER_PLANES) * + entries_per_attrib + 1]; + unsigned int atti = 0; + + attribs[atti++] = EGL_WIDTH; + attribs[atti++] = buffer->width; + attribs[atti++] = EGL_HEIGHT; + attribs[atti++] = buffer->height; + attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; + attribs[atti++] = buffer->format; + +#define ADD_PLANE_ATTRIBS(plane_idx) { \ + attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _FD_EXT; \ + attribs[atti++] = buffer->dmabuf_fds[plane_idx]; \ + attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _OFFSET_EXT; \ + attribs[atti++] = (int) buffer->offsets[plane_idx]; \ + attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _PITCH_EXT; \ + attribs[atti++] = (int) buffer->strides[plane_idx]; \ + if (display->egl.has_dma_buf_import_modifiers) { \ + attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_LO_EXT; \ + attribs[atti++] = buffer->modifier & 0xFFFFFFFF; \ + attribs[atti++] = EGL_DMA_BUF_PLANE ## plane_idx ## _MODIFIER_HI_EXT; \ + attribs[atti++] = buffer->modifier >> 32; \ + } \ + } + + if (buffer->plane_count > 0) + ADD_PLANE_ATTRIBS(0); + + if (buffer->plane_count > 1) + ADD_PLANE_ATTRIBS(1); + + if (buffer->plane_count > 2) + ADD_PLANE_ATTRIBS(2); + + if (buffer->plane_count > 3) + ADD_PLANE_ATTRIBS(3); + +#undef ADD_PLANE_ATTRIBS + + attribs[atti] = EGL_NONE; + + assert(atti < ARRAY_LENGTH(attribs)); + + buffer->egl_image = display->egl.create_image(display->egl.display, + EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, + NULL, attribs); + if (buffer->egl_image == EGL_NO_IMAGE_KHR) { + fprintf(stderr, "EGLImageKHR creation failed\n"); + return false; + } + + eglMakeCurrent(display->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, + display->egl.context); + + glGenTextures(1, &buffer->gl_texture); + glBindTexture(GL_TEXTURE_2D, buffer->gl_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + display->egl.image_target_texture_2d(GL_TEXTURE_2D, buffer->egl_image); + + glGenFramebuffers(1, &buffer->gl_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, buffer->gl_fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, buffer->gl_texture, 0); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + fprintf(stderr, "FBO creation failed\n"); + return false; + } + + return true; +} + + +static int +create_dmabuf_buffer(struct display *display, struct buffer *buffer, + int width, int height, uint32_t opts) +{ + /* Y-Invert the buffer image, since we are going to renderer to the + * buffer through a FBO. */ + static 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 = BUFFER_FORMAT; + buffer->release_fence_fd = -1; + +#ifdef HAVE_GBM_MODIFIERS + if (display->modifiers_count > 0) { + 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, + buffer->width, + buffer->height, + buffer->format, + GBM_BO_USE_RENDERING); + buffer->modifier = DRM_FORMAT_MOD_INVALID; + } + + if (!buffer->bo) { + fprintf(stderr, "create_bo failed\n"); + 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; + union gbm_bo_handle handle; + + handle = gbm_bo_get_handle_for_plane(buffer->bo, i); + if (handle.s32 == -1) { + fprintf(stderr, "error: failed to get gbm_bo_handle\n"); + goto error; + } + + ret = drmPrimeHandleToFD(display->gbm.drm_fd, handle.u32, 0, + &buffer->dmabuf_fds[i]); + if (ret < 0 || buffer->dmabuf_fds[i] < 0) { + fprintf(stderr, "error: failed to get dmabuf_fd\n"); + goto error; + } + 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); + /* turn off Y_INVERT otherwise linux-dmabuf will reject it and + * we need all dmabuf flags turned off */ + flags &= ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT; + + fprintf(stdout, "image is y-inverted as direct-display flag was set, " + "dmabuf y-inverted attribute flag was removed\n"); + } + + for (i = 0; i < buffer->plane_count; ++i) { + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fds[i], + i, + buffer->offsets[i], + buffer->strides[i], + buffer->modifier >> 32, + buffer->modifier & 0xffffffff); + } + + zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, buffer); + if (display->req_dmabuf_immediate) { + buffer->buffer = + zwp_linux_buffer_params_v1_create_immed(params, + buffer->width, + buffer->height, + buffer->format, + flags); + /* When not using explicit synchronization listen to + * wl_buffer.release for release notifications, otherwise we + * are going to use zwp_linux_buffer_release_v1. */ + if (!buffer->display->use_explicit_sync) { + wl_buffer_add_listener(buffer->buffer, + &buffer_listener, + buffer); + } + } + else { + zwp_linux_buffer_params_v1_create(params, + buffer->width, + buffer->height, + buffer->format, + flags); + } + + if (!create_fbo_for_buffer(display, buffer)) + goto error; + + return 0; + +error: + buffer_free(buffer); + return -1; +} + +static void +xdg_surface_handle_configure(void *data, struct xdg_surface *surface, + uint32_t serial) +{ + struct window *window = data; + + xdg_surface_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 = { + xdg_surface_handle_configure, +}; + +static void +xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *toplevel, + int32_t width, int32_t height, + struct wl_array *states) +{ +} + +static void +xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) +{ + running = 0; +} + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + xdg_toplevel_handle_configure, + xdg_toplevel_handle_close, +}; + +static const char *vert_shader_text = + "uniform float offset;\n" + "attribute vec4 pos;\n" + "attribute vec4 color;\n" + "varying vec4 v_color;\n" + "void main() {\n" + " gl_Position = pos + vec4(offset, offset, 0.0, 0.0);\n" + " v_color = color;\n" + "}\n"; + +static const char *frag_shader_text = + "precision mediump float;\n" + "varying vec4 v_color;\n" + "void main() {\n" + " gl_FragColor = v_color;\n" + "}\n"; + +static const char *vert_shader_mandelbrot_text = + "uniform float offset;\n" + "attribute vec4 pos;\n" + "varying vec2 v_pos;\n" + "void main() {\n" + " v_pos = pos.xy;\n" + " gl_Position = pos + vec4(offset, offset, 0.0, 0.0);\n" + "}\n"; + + +/* Mandelbrot set shader using the escape time algorithm. */ +static const char *frag_shader_mandelbrot_text = + "precision mediump float;\n" + "varying vec2 v_pos;\n" + "void main() {\n" + " const int max_iteration = 500;\n" + " // Scale and translate position to get a nice mandelbrot drawing for\n" + " // the used v_pos x and y range (-0.5 to 0.5).\n" + " float x0 = 3.0 * v_pos.x - 0.5;\n" + " float y0 = 3.0 * v_pos.y;\n" + " float x = 0.0;\n" + " float y = 0.0;\n" + " int iteration = 0;\n" + " while (x * x + y * y <= 4.0 && iteration < max_iteration) {\n" + " float xtemp = x * x - y * y + x0;\n" + " y = 2.0 * x * y + y0;\n" + " x = xtemp;\n" + " ++iteration;\n" + " }\n" + " float red = iteration == max_iteration ?\n" + " 0.0 : 1.0 - fract(float(iteration) / 20.0);\n" + " gl_FragColor = vec4(red, 0.0, 0.0, 1.0);\n" + "}\n"; + +static GLuint +create_shader(const char *source, GLenum shader_type) +{ + GLuint shader; + GLint status; + + shader = glCreateShader(shader_type); + assert(shader != 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 GLuint +create_and_link_program(GLuint vert, GLuint frag) +{ + GLint status; + GLuint program = glCreateProgram(); + + glAttachShader(program, vert); + glAttachShader(program, frag); + glLinkProgram(program); + + glGetProgramiv(program, GL_LINK_STATUS, &status); + if (!status) { + char log[1000]; + GLsizei len; + glGetProgramInfoLog(program, 1000, &len, log); + fprintf(stderr, "Error: linking:\n%.*s\n", len, log); + return 0; + } + + return program; +} + +static bool +window_set_up_gl(struct window *window) +{ + GLuint vert = create_shader( + window->render_mandelbrot ? vert_shader_mandelbrot_text : + vert_shader_text, + GL_VERTEX_SHADER); + GLuint frag = create_shader( + window->render_mandelbrot ? frag_shader_mandelbrot_text : + frag_shader_text, + GL_FRAGMENT_SHADER); + + window->gl.program = create_and_link_program(vert, frag); + + glDeleteShader(vert); + glDeleteShader(frag); + + window->gl.pos = glGetAttribLocation(window->gl.program, "pos"); + window->gl.color = glGetAttribLocation(window->gl.program, "color"); + + glUseProgram(window->gl.program); + + window->gl.offset_uniform = + glGetUniformLocation(window->gl.program, "offset"); + + return window->gl.program != 0; +} + +static void +destroy_window(struct window *window) +{ + int i; + + if (window->gl.program) + glDeleteProgram(window->gl.program); + + if (window->callback) + wl_callback_destroy(window->callback); + + for (i = 0; i < NUM_BUFFERS; i++) { + if (window->buffers[i].buffer) + buffer_free(&window->buffers[i]); + } + + if (window->xdg_toplevel) + xdg_toplevel_destroy(window->xdg_toplevel); + if (window->xdg_surface) + xdg_surface_destroy(window->xdg_surface); + if (window->surface_sync) + zwp_linux_surface_synchronization_v1_destroy(window->surface_sync); + wl_surface_destroy(window->surface); + free(window); +} + +static struct window * +create_window(struct display *display, int width, int height, int opts) +{ + struct window *window; + int i; + int ret; + + window = zalloc(sizeof *window); + if (!window) + return NULL; + + window->callback = NULL; + window->display = display; + window->width = width; + window->height = height; + window->surface = wl_compositor_create_surface(display->compositor); + + if (display->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-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); + } + + if (display->explicit_sync) { + window->surface_sync = + zwp_linux_explicit_synchronization_v1_get_synchronization( + display->explicit_sync, window->surface); + assert(window->surface_sync); + } + + for (i = 0; i < NUM_BUFFERS; ++i) { + int j; + for (j = 0; j < MAX_BUFFER_PLANES; ++j) + window->buffers[i].dmabuf_fds[j] = -1; + + } + + for (i = 0; i < NUM_BUFFERS; ++i) { + ret = create_dmabuf_buffer(display, &window->buffers[i], + width, height, opts); + + if (ret < 0) + goto error; + } + + window->render_mandelbrot = opts & OPT_MANDELBROT; + + if (!window_set_up_gl(window)) + goto error; + + return window; + +error: + if (window) + destroy_window(window); + + return NULL; +} + +static int +create_egl_fence_fd(struct window *window) +{ + struct display *d = window->display; + EGLSyncKHR sync = d->egl.create_sync(d->egl.display, + EGL_SYNC_NATIVE_FENCE_ANDROID, + NULL); + int fd; + + assert(sync != EGL_NO_SYNC_KHR); + /* We need to flush before we can get the fence fd. */ + glFlush(); + fd = d->egl.dup_native_fence_fd(d->egl.display, sync); + assert(fd >= 0); + + d->egl.destroy_sync(d->egl.display, sync); + + return fd; +} + +static struct buffer * +window_next_buffer(struct window *window) +{ + int i; + + for (i = 0; i < NUM_BUFFERS; i++) + if (!window->buffers[i].busy) + return &window->buffers[i]; + + return NULL; +} + +static const struct wl_callback_listener frame_listener; + +/* Renders a square moving from the lower left corner to the + * upper right corner of the window. The square's vertices have + * the following colors: + * + * green +-----+ yellow + * | | + * | | + * red +-----+ blue + */ +static void +render(struct window *window, struct buffer *buffer) +{ + /* Complete a movement iteration in 5000 ms. */ + static const uint64_t iteration_ms = 5000; + static const GLfloat verts[4][2] = { + { -0.5, -0.5 }, + { -0.5, 0.5 }, + { 0.5, -0.5 }, + { 0.5, 0.5 } + }; + static const GLfloat colors[4][3] = { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 }, + { 1, 1, 0 } + }; + GLfloat offset; + struct timeval tv; + uint64_t time_ms; + + gettimeofday(&tv, NULL); + time_ms = tv.tv_sec * 1000 + tv.tv_usec / 1000; + + /* Split time_ms in repeating windows of [0, iteration_ms) and map them + * to offsets in the [-0.5, 0.5) range. */ + offset = (time_ms % iteration_ms) / (float) iteration_ms - 0.5; + + /* 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); + + glClearColor(0.0,0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + glVertexAttribPointer(window->gl.pos, 2, GL_FLOAT, GL_FALSE, 0, verts); + glVertexAttribPointer(window->gl.color, 3, GL_FLOAT, GL_FALSE, 0, colors); + glEnableVertexAttribArray(window->gl.pos); + glEnableVertexAttribArray(window->gl.color); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableVertexAttribArray(window->gl.pos); + glDisableVertexAttribArray(window->gl.color); +} + +static void +render_mandelbrot(struct window *window, struct buffer *buffer) +{ + /* Complete a movement iteration in 5000 ms. */ + static const uint64_t iteration_ms = 5000; + /* Split drawing in a square grid consisting of grid_side * grid_side + * cells. */ + static const int grid_side = 4; + GLfloat norm_cell_side = 1.0 / grid_side; + int num_cells = grid_side * grid_side; + GLfloat offset; + struct timeval tv; + uint64_t time_ms; + int i; + + gettimeofday(&tv, NULL); + time_ms = tv.tv_sec * 1000 + tv.tv_usec / 1000; + + /* Split time_ms in repeating windows of [0, iteration_ms) and map them + * to offsets in the [-0.5, 0.5) range. */ + offset = (time_ms % iteration_ms) / (float) iteration_ms - 0.5; + + /* 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); + + glClearColor(0.6, 0.6, 0.6, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + for (i = 0; i < num_cells; ++i) { + /* Calculate the vertex coordinates of the current grid cell. */ + int row = i / grid_side; + int col = i % grid_side; + GLfloat left = -0.5 + norm_cell_side * col; + GLfloat right = left + norm_cell_side; + GLfloat top = 0.5 - norm_cell_side * row; + GLfloat bottom = top - norm_cell_side; + GLfloat verts[4][2] = { + { left, bottom }, + { left, top }, + { right, bottom }, + { right, top } + }; + + /* ... and draw it. */ + glVertexAttribPointer(window->gl.pos, 2, GL_FLOAT, GL_FALSE, 0, verts); + glEnableVertexAttribArray(window->gl.pos); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableVertexAttribArray(window->gl.pos); + } +} + +static void +buffer_fenced_release(void *data, + struct zwp_linux_buffer_release_v1 *release, + int32_t fence) +{ + struct buffer *buffer = data; + + assert(release == buffer->buffer_release); + assert(buffer->release_fence_fd == -1); + + buffer->busy = 0; + buffer->release_fence_fd = fence; + zwp_linux_buffer_release_v1_destroy(buffer->buffer_release); + buffer->buffer_release = NULL; +} + +static void +buffer_immediate_release(void *data, + struct zwp_linux_buffer_release_v1 *release) +{ + struct buffer *buffer = data; + + assert(release == buffer->buffer_release); + assert(buffer->release_fence_fd == -1); + + buffer->busy = 0; + zwp_linux_buffer_release_v1_destroy(buffer->buffer_release); + buffer->buffer_release = NULL; +} + +static const struct zwp_linux_buffer_release_v1_listener buffer_release_listener = { + buffer_fenced_release, + buffer_immediate_release, +}; + +static void +wait_for_buffer_release_fence(struct buffer *buffer) +{ + struct display *d = buffer->display; + EGLint attrib_list[] = { + EGL_SYNC_NATIVE_FENCE_FD_ANDROID, buffer->release_fence_fd, + EGL_NONE, + }; + EGLSyncKHR sync = d->egl.create_sync(d->egl.display, + EGL_SYNC_NATIVE_FENCE_ANDROID, + attrib_list); + int ret; + + assert(sync); + + /* EGLSyncKHR takes ownership of the fence fd. */ + buffer->release_fence_fd = -1; + + if (d->egl.wait_sync) + ret = d->egl.wait_sync(d->egl.display, sync, 0); + else + ret = d->egl.client_wait_sync(d->egl.display, sync, 0, + EGL_FOREVER_KHR); + assert(ret == EGL_TRUE); + + ret = d->egl.destroy_sync(d->egl.display, sync); + assert(ret == EGL_TRUE); +} + +static void +redraw(void *data, struct wl_callback *callback, uint32_t time) +{ + struct window *window = data; + struct buffer *buffer; + + buffer = window_next_buffer(window); + if (!buffer) { + fprintf(stderr, + !callback ? "Failed to create the first buffer.\n" : + "All buffers busy at redraw(). Server bug?\n"); + abort(); + } + + if (buffer->release_fence_fd >= 0) + wait_for_buffer_release_fence(buffer); + + if (window->render_mandelbrot) + render_mandelbrot(window, buffer); + else + render(window, buffer); + + if (window->display->use_explicit_sync) { + int fence_fd = create_egl_fence_fd(window); + zwp_linux_surface_synchronization_v1_set_acquire_fence( + window->surface_sync, fence_fd); + close(fence_fd); + + buffer->buffer_release = + zwp_linux_surface_synchronization_v1_get_release(window->surface_sync); + zwp_linux_buffer_release_v1_add_listener( + buffer->buffer_release, &buffer_release_listener, buffer); + } else { + glFinish(); + } + + wl_surface_attach(window->surface, buffer->buffer, 0, 0); + wl_surface_damage(window->surface, 0, 0, window->width, window->height); + + if (callback) + wl_callback_destroy(callback); + + window->callback = wl_surface_frame(window->surface); + wl_callback_add_listener(window->callback, &frame_listener, window); + wl_surface_commit(window->surface); + buffer->busy = 1; +} + +static const struct wl_callback_listener frame_listener = { + redraw +}; + +static void +dmabuf_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; + + 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] = + ((uint64_t)modifier_hi << 32) | modifier_lo; + break; + default: + break; + } +} + +static void +dmabuf_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format) +{ + /* XXX: deprecated */ +} + +static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = { + dmabuf_format, + dmabuf_modifiers +}; + +static void +xdg_wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial) +{ + xdg_wm_base_pong(wm_base, serial); +} + +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + xdg_wm_base_ping, +}; + +static void +registry_handle_global(void *data, struct wl_registry *registry, + uint32_t id, const char *interface, uint32_t version) +{ + struct display *d = data; + + if (strcmp(interface, "wl_compositor") == 0) { + d->compositor = + wl_registry_bind(registry, + id, &wl_compositor_interface, 1); + } else if (strcmp(interface, "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, "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; + d->dmabuf = wl_registry_bind(registry, + id, &zwp_linux_dmabuf_v1_interface, 3); + zwp_linux_dmabuf_v1_add_listener(d->dmabuf, &dmabuf_listener, d); + } else if (strcmp(interface, "zwp_linux_explicit_synchronization_v1") == 0) { + 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); + } +} + +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 void +destroy_display(struct display *display) +{ + if (display->gbm.device) + gbm_device_destroy(display->gbm.device); + + if (display->gbm.drm_fd >= 0) + close(display->gbm.drm_fd); + + if (display->egl.context != EGL_NO_CONTEXT) + eglDestroyContext(display->egl.display, display->egl.context); + + if (display->egl.display != EGL_NO_DISPLAY) + eglTerminate(display->egl.display); + + free(display->modifiers); + + if (display->dmabuf) + zwp_linux_dmabuf_v1_destroy(display->dmabuf); + + 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); + + if (display->registry) + wl_registry_destroy(display->registry); + + if (display->display) { + wl_display_flush(display->display); + wl_display_disconnect(display->display); + } + + free(display); +} + +static bool +display_set_up_egl(struct display *display) +{ + static const EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + EGLint major, minor, ret, count; + 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); + if (display->egl.display == EGL_NO_DISPLAY) { + fprintf(stderr, "Failed to create EGLDisplay\n"); + goto error; + } + + if (eglInitialize(display->egl.display, &major, &minor) == EGL_FALSE) { + fprintf(stderr, "Failed to initialize EGLDisplay\n"); + goto error; + } + + if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) { + fprintf(stderr, "Failed to bind OpenGL ES API\n"); + goto error; + } + + egl_extensions = eglQueryString(display->egl.display, EGL_EXTENSIONS); + assert(egl_extensions != NULL); + + if (!weston_check_egl_extension(egl_extensions, + "EGL_EXT_image_dma_buf_import")) { + fprintf(stderr, "EGL_EXT_image_dma_buf_import not supported\n"); + goto error; + } + + if (!weston_check_egl_extension(egl_extensions, + "EGL_KHR_surfaceless_context")) { + fprintf(stderr, "EGL_KHR_surfaceless_context not supported\n"); + goto error; + } + + 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); + } + + display->egl.context = eglCreateContext(display->egl.display, + display->egl.conf, + EGL_NO_CONTEXT, + context_attribs); + if (display->egl.context == EGL_NO_CONTEXT) { + fprintf(stderr, "Failed to create EGLContext\n"); + goto error; + } + + eglMakeCurrent(display->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, + display->egl.context); + + gl_extensions = (const char *) glGetString(GL_EXTENSIONS); + assert(gl_extensions != NULL); + + if (!weston_check_egl_extension(gl_extensions, + "GL_OES_EGL_image")) { + fprintf(stderr, "GL_OES_EGL_image not supported\n"); + goto error; + } + + if (weston_check_egl_extension(egl_extensions, + "EGL_EXT_image_dma_buf_import_modifiers")) { + display->egl.has_dma_buf_import_modifiers = true; + display->egl.query_dma_buf_modifiers = + (void *) eglGetProcAddress("eglQueryDmaBufModifiersEXT"); + assert(display->egl.query_dma_buf_modifiers); + } + + display->egl.create_image = + (void *) eglGetProcAddress("eglCreateImageKHR"); + assert(display->egl.create_image); + + display->egl.destroy_image = + (void *) eglGetProcAddress("eglDestroyImageKHR"); + assert(display->egl.destroy_image); + + display->egl.image_target_texture_2d = + (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES"); + assert(display->egl.image_target_texture_2d); + + if (weston_check_egl_extension(egl_extensions, "EGL_KHR_fence_sync") && + weston_check_egl_extension(egl_extensions, + "EGL_ANDROID_native_fence_sync")) { + display->egl.create_sync = + (void *) eglGetProcAddress("eglCreateSyncKHR"); + assert(display->egl.create_sync); + + display->egl.destroy_sync = + (void *) eglGetProcAddress("eglDestroySyncKHR"); + assert(display->egl.destroy_sync); + + display->egl.client_wait_sync = + (void *) eglGetProcAddress("eglClientWaitSyncKHR"); + assert(display->egl.client_wait_sync); + + display->egl.dup_native_fence_fd = + (void *) eglGetProcAddress("eglDupNativeFenceFDANDROID"); + assert(display->egl.dup_native_fence_fd); + } + + if (weston_check_egl_extension(egl_extensions, + "EGL_KHR_wait_sync")) { + display->egl.wait_sync = + (void *) eglGetProcAddress("eglWaitSyncKHR"); + assert(display->egl.wait_sync); + } + + return true; + +error: + return false; +} + +static bool +display_update_supported_modifiers_for_egl(struct display *d) +{ + uint64_t *egl_modifiers = NULL; + 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, + BUFFER_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) { + d->modifiers_count = 0; + free(d->modifiers); + d->modifiers = NULL; + return true; + } + + egl_modifiers = zalloc(num_egl_modifiers * sizeof(*egl_modifiers)); + + ret = d->egl.query_dma_buf_modifiers(d->egl.display, + BUFFER_FORMAT, + num_egl_modifiers, + egl_modifiers, + NULL, /* external_only */ + &num_egl_modifiers); + if (ret == EGL_FALSE) { + fprintf(stderr, "Failed to query EGL modifiers for format\n"); + goto error; + } + + /* Poor person's set intersection: d->modifiers INTERSECT + * egl_modifiers. If a modifier is not supported, replace it with + * DRM_FORMAT_MOD_INVALID in the d->modifiers array. + */ + for (i = 0; i < d->modifiers_count; ++i) { + uint64_t mod = d->modifiers[i]; + bool egl_supported = false; + int j; + + for (j = 0; j < num_egl_modifiers; ++j) { + if (egl_modifiers[j] == mod) { + egl_supported = true; + break; + } + } + + if (!egl_supported) + d->modifiers[i] = DRM_FORMAT_MOD_INVALID; + } + + free(egl_modifiers); + + return true; + +error: + free(egl_modifiers); + + return false; +} + +static bool +display_set_up_gbm(struct display *display, char const* drm_render_node) +{ + display->gbm.drm_fd = open(drm_render_node, O_RDWR); + if (display->gbm.drm_fd < 0) { + fprintf(stderr, "Failed to open drm render node %s\n", + drm_render_node); + return false; + } + + display->gbm.device = gbm_create_device(display->gbm.drm_fd); + if (display->gbm.device == NULL) { + fprintf(stderr, "Failed to create gbm device\n"); + return false; + } + + return true; +} + +static struct display * +create_display(char const *drm_render_node, int opts) +{ + struct display *display = NULL; + + display = zalloc(sizeof *display); + if (display == NULL) { + fprintf(stderr, "out of memory\n"); + goto error; + } + + display->gbm.drm_fd = -1; + + display->display = wl_display_connect(NULL); + assert(display->display); + + display->req_dmabuf_immediate = opts & OPT_IMMEDIATE; + + display->registry = wl_display_get_registry(display->display); + wl_registry_add_listener(display->registry, + ®istry_listener, display); + wl_display_roundtrip(display->display); + if (display->dmabuf == NULL) { + fprintf(stderr, "No zwp_linux_dmabuf global\n"); + goto error; + } + + wl_display_roundtrip(display->display); + + if (!display->modifiers_count) { + fprintf(stderr, "format XRGB8888 is not available\n"); + goto error; + } + + /* GBM needs to be initialized before EGL, so that we have a valid + * render node gbm_device to create the EGL display from. */ + if (!display_set_up_gbm(display, drm_render_node)) + goto error; + + if (!display_set_up_egl(display)) + goto error; + + if (!display_update_supported_modifiers_for_egl(display)) + goto error; + + /* We use explicit synchronization only if the user hasn't disabled it, + * the compositor supports it, we can handle fence fds. */ + display->use_explicit_sync = + !(opts & OPT_IMPLICIT_SYNC) && + display->explicit_sync && + display->egl.dup_native_fence_fd; + + if (opts & OPT_IMPLICIT_SYNC) { + fprintf(stderr, "Warning: Not using explicit sync, disabled by user\n"); + } else if (!display->explicit_sync) { + fprintf(stderr, + "Warning: zwp_linux_explicit_synchronization_v1 not supported,\n" + " will not use explicit synchronization\n"); + } else if (!display->egl.dup_native_fence_fd) { + fprintf(stderr, + "Warning: EGL_ANDROID_native_fence_sync not supported,\n" + " will not use explicit synchronization\n"); + } else if (!display->egl.wait_sync) { + fprintf(stderr, + "Warning: EGL_KHR_wait_sync not supported,\n" + " will not use server-side wait\n"); + } + + return display; + +error: + if (display != NULL) + destroy_display(display); + return NULL; +} + +static void +signal_int(int signum) +{ + running = 0; +} + +static void +print_usage_and_exit(void) +{ + printf("usage flags:\n" + "\t'-i,--import-immediate=<>'" + "\n\t\t0 to import dmabuf via roundtrip, " + "\n\t\t1 to enable import without roundtrip\n" + "\t'-d,--drm-render-node=<>'" + "\n\t\tthe full path to the drm render node to use\n" + "\t'-s,--size=<>'" + "\n\t\tthe window size in pixels (default: 256)\n" + "\t'-e,--explicit-sync=<>'" + "\n\t\t0 to disable explicit sync, " + "\n\t\t1 to enable explicit sync (default: 1)\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"); + exit(0); +} + +static int +is_true(const char* c) +{ + if (!strcmp(c, "1")) + return 1; + else if (!strcmp(c, "0")) + return 0; + else + print_usage_and_exit(); + + return 0; +} + +int +main(int argc, char **argv) +{ + struct sigaction sigint; + struct display *display; + struct window *window; + int opts = 0; + char const *drm_render_node = "/dev/dri/renderD128"; + int c, option_index, ret = 0; + int window_size = 256; + + static struct option long_options[] = { + {"import-immediate", required_argument, 0, 'i' }, + {"drm-render-node", required_argument, 0, 'd' }, + {"size", required_argument, 0, 's' }, + {"explicit-sync", required_argument, 0, 'e' }, + {"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:mg", + long_options, &option_index)) != -1) { + switch (c) { + case 'i': + if (is_true(optarg)) + opts |= OPT_IMMEDIATE; + break; + case 'd': + drm_render_node = optarg; + break; + case 's': + window_size = strtol(optarg, NULL, 10); + break; + case 'e': + if (!is_true(optarg)) + opts |= OPT_IMPLICIT_SYNC; + break; + case 'm': + opts |= OPT_MANDELBROT; + break; + case 'g': + opts |= OPT_DIRECT_DISPLAY; + break; + default: + print_usage_and_exit(); + } + } + + display = create_display(drm_render_node, opts); + if (!display) + return 1; + window = create_window(display, window_size, window_size, opts); + if (!window) + return 1; + + sigint.sa_handler = signal_int; + sigemptyset(&sigint.sa_mask); + sigint.sa_flags = SA_RESETHAND; + sigaction(SIGINT, &sigint, NULL); + + /* Here we retrieve the linux-dmabuf objects if executed without immed, + * or error */ + wl_display_roundtrip(display->display); + + if (!running) + return 1; + + window->initialized = true; + + if (!window->wait_for_configure) + redraw(window, NULL, 0); + + while (running && ret != -1) + ret = wl_display_dispatch(display->display); + + fprintf(stderr, "simple-dmabuf-egl exiting\n"); + destroy_window(window); + destroy_display(display); + + return 0; +} diff --git a/clients/simple-dmabuf-intel.c b/clients/simple-dmabuf-intel.c deleted file mode 100644 index 67850b0bd..000000000 --- a/clients/simple-dmabuf-intel.c +++ /dev/null @@ -1,624 +0,0 @@ -/* - * Copyright © 2011 Benjamin Franzke - * Copyright © 2010 Intel Corporation - * Copyright © 2014 Collabora Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include "shared/zalloc.h" -#include "xdg-shell-unstable-v6-client-protocol.h" -#include "fullscreen-shell-unstable-v1-client-protocol.h" -#include "linux-dmabuf-unstable-v1-client-protocol.h" - -struct display { - struct wl_display *display; - struct wl_registry *registry; - struct wl_compositor *compositor; - struct zxdg_shell_v6 *shell; - struct zwp_fullscreen_shell_v1 *fshell; - struct zwp_linux_dmabuf_v1 *dmabuf; - int xrgb8888_format_found; -}; - -struct buffer { - struct wl_buffer *buffer; - int busy; - - int drm_fd; - - drm_intel_bufmgr *bufmgr; - drm_intel_bo *bo; - - uint32_t gem_handle; - int dmabuf_fd; - uint8_t *mmap; - - int width; - int height; - int bpp; - unsigned long stride; -}; - -#define NUM_BUFFERS 3 - -struct window { - struct display *display; - int width, height; - struct wl_surface *surface; - struct zxdg_surface_v6 *xdg_surface; - struct zxdg_toplevel_v6 *xdg_toplevel; - struct buffer buffers[NUM_BUFFERS]; - struct buffer *prev_buffer; - struct wl_callback *callback; - bool initialized; - bool wait_for_configure; -}; - -static int running = 1; - -static void -redraw(void *data, struct wl_callback *callback, uint32_t time); - -static void -buffer_release(void *data, struct wl_buffer *buffer) -{ - struct buffer *mybuf = data; - - mybuf->busy = 0; -} - -static const struct wl_buffer_listener buffer_listener = { - buffer_release -}; - -static int -drm_connect(struct buffer *my_buf) -{ - /* This won't work with card0 as we need to be authenticated; instead, - * boot with drm.rnodes=1 and use that. */ - my_buf->drm_fd = open("/dev/dri/renderD128", O_RDWR); - if (my_buf->drm_fd < 0) - return 0; - - my_buf->bufmgr = drm_intel_bufmgr_gem_init(my_buf->drm_fd, 32); - if (!my_buf->bufmgr) - return 0; - - return 1; -} - -static void -drm_shutdown(struct buffer *my_buf) -{ - drm_intel_bufmgr_destroy(my_buf->bufmgr); - close(my_buf->drm_fd); -} - -static int -alloc_bo(struct buffer *my_buf) -{ - /* XXX: try different tiling modes for testing FB modifiers. */ - uint32_t tiling = I915_TILING_NONE; - - assert(my_buf->bufmgr); - - my_buf->bo = drm_intel_bo_alloc_tiled(my_buf->bufmgr, "test", - my_buf->width, my_buf->height, - (my_buf->bpp / 8), &tiling, - &my_buf->stride, 0); - - printf("buffer allocated w %d, h %d, stride %lu, size %lu\n", - my_buf->width, my_buf->height, my_buf->stride, my_buf->bo->size); - - if (!my_buf->bo) - return 0; - - if (tiling != I915_TILING_NONE) - return 0; - - return 1; -} - -static void -free_bo(struct buffer *my_buf) -{ - drm_intel_bo_unreference(my_buf->bo); -} - -static int -map_bo(struct buffer *my_buf) -{ - if (drm_intel_gem_bo_map_gtt(my_buf->bo) != 0) - return 0; - - my_buf->mmap = my_buf->bo->virtual; - - return 1; -} - -static void -fill_content(struct buffer *my_buf) -{ - int x = 0, y = 0; - uint32_t *pix; - - assert(my_buf->mmap); - - for (y = 0; y < my_buf->height; y++) { - pix = (uint32_t *)(my_buf->mmap + y * my_buf->stride); - for (x = 0; x < my_buf->width; x++) { - *pix++ = (0xff << 24) | ((x % 256) << 16) | - ((y % 256) << 8) | 0xf0; - } - } -} - -static void -unmap_bo(struct buffer *my_buf) -{ - drm_intel_gem_bo_unmap_gtt(my_buf->bo); -} - -static void -create_succeeded(void *data, - struct zwp_linux_buffer_params_v1 *params, - struct wl_buffer *new_buffer) -{ - struct buffer *buffer = data; - - buffer->buffer = new_buffer; - wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer); - - zwp_linux_buffer_params_v1_destroy(params); -} - -static void -create_failed(void *data, struct zwp_linux_buffer_params_v1 *params) -{ - struct buffer *buffer = data; - - buffer->buffer = NULL; - running = 0; - - zwp_linux_buffer_params_v1_destroy(params); - - fprintf(stderr, "Error: zwp_linux_buffer_params.create failed.\n"); -} - -static const struct zwp_linux_buffer_params_v1_listener params_listener = { - create_succeeded, - create_failed -}; - -static int -create_dmabuf_buffer(struct display *display, struct buffer *buffer, - int width, int height) -{ - struct zwp_linux_buffer_params_v1 *params; - uint64_t modifier; - uint32_t flags; - - if (!drm_connect(buffer)) { - fprintf(stderr, "drm_connect failed\n"); - goto error; - } - - buffer->width = width; - buffer->height = height; - buffer->bpp = 32; /* hardcoded XRGB8888 format */ - - if (!alloc_bo(buffer)) { - fprintf(stderr, "alloc_bo failed\n"); - goto error1; - } - - if (!map_bo(buffer)) { - fprintf(stderr, "map_bo failed\n"); - goto error2; - } - fill_content(buffer); - unmap_bo(buffer); - - if (drm_intel_bo_gem_export_to_prime(buffer->bo, &buffer->dmabuf_fd) != 0) { - fprintf(stderr, "drm_intel_bo_gem_export_to_prime failed\n"); - goto error2; - } - if (buffer->dmabuf_fd < 0) { - fprintf(stderr, "error: dmabuf_fd < 0\n"); - goto error2; - } - - /* We now have a dmabuf! It should contain 2x2 tiles (i.e. each tile - * is 256x256) of misc colours, and be mappable, either as ARGB8888, or - * XRGB8888. */ - modifier = 0; - flags = 0; - - params = zwp_linux_dmabuf_v1_create_params(display->dmabuf); - zwp_linux_buffer_params_v1_add(params, - buffer->dmabuf_fd, - 0, /* plane_idx */ - 0, /* offset */ - buffer->stride, - modifier >> 32, - modifier & 0xffffffff); - zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, buffer); - zwp_linux_buffer_params_v1_create(params, - buffer->width, - buffer->height, - DRM_FORMAT_XRGB8888, - flags); - - return 0; - -error2: - free_bo(buffer); -error1: - drm_shutdown(buffer); -error: - return -1; -} - -static void -xdg_surface_handle_configure(void *data, struct zxdg_surface_v6 *surface, - uint32_t serial) -{ - struct window *window = data; - - zxdg_surface_v6_ack_configure(surface, serial); - - if (window->initialized && window->wait_for_configure) - redraw(window, NULL, 0); - window->wait_for_configure = false; -} - -static const struct zxdg_surface_v6_listener xdg_surface_listener = { - xdg_surface_handle_configure, -}; - -static void -xdg_toplevel_handle_configure(void *data, struct zxdg_toplevel_v6 *toplevel, - int32_t width, int32_t height, - struct wl_array *states) -{ -} - -static void -xdg_toplevel_handle_close(void *data, struct zxdg_toplevel_v6 *xdg_toplevel) -{ - running = 0; -} - -static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = { - xdg_toplevel_handle_configure, - xdg_toplevel_handle_close, -}; - -static struct window * -create_window(struct display *display, int width, int height) -{ - struct window *window; - int i; - int ret; - - window = zalloc(sizeof *window); - if (!window) - return NULL; - - window->callback = NULL; - window->display = display; - window->width = width; - window->height = height; - window->surface = wl_compositor_create_surface(display->compositor); - - if (display->shell) { - window->xdg_surface = - zxdg_shell_v6_get_xdg_surface(display->shell, - window->surface); - - assert(window->xdg_surface); - - zxdg_surface_v6_add_listener(window->xdg_surface, - &xdg_surface_listener, window); - - window->xdg_toplevel = - zxdg_surface_v6_get_toplevel(window->xdg_surface); - - assert(window->xdg_toplevel); - - zxdg_toplevel_v6_add_listener(window->xdg_toplevel, - &xdg_toplevel_listener, window); - - zxdg_toplevel_v6_set_title(window->xdg_toplevel, "simple-dmabuf"); - - window->wait_for_configure = true; - wl_surface_commit(window->surface); - } else if (display->fshell) { - zwp_fullscreen_shell_v1_present_surface(display->fshell, - window->surface, - ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT, - NULL); - } else { - assert(0); - } - - for (i = 0; i < NUM_BUFFERS; ++i) { - ret = create_dmabuf_buffer(display, &window->buffers[i], - width, height); - - if (ret < 0) - return NULL; - } - - return window; -} - -static void -destroy_window(struct window *window) -{ - int i; - - if (window->callback) - wl_callback_destroy(window->callback); - - for (i = 0; i < NUM_BUFFERS; i++) { - if (!window->buffers[i].buffer) - continue; - - wl_buffer_destroy(window->buffers[i].buffer); - free_bo(&window->buffers[i]); - close(window->buffers[i].dmabuf_fd); - drm_shutdown(&window->buffers[i]); - } - - if (window->xdg_toplevel) - zxdg_toplevel_v6_destroy(window->xdg_toplevel); - if (window->xdg_surface) - zxdg_surface_v6_destroy(window->xdg_surface); - wl_surface_destroy(window->surface); - free(window); -} - -static struct buffer * -window_next_buffer(struct window *window) -{ - int i; - - for (i = 0; i < NUM_BUFFERS; i++) - if (!window->buffers[i].busy) - return &window->buffers[i]; - - return NULL; -} - -static const struct wl_callback_listener frame_listener; - -static void -redraw(void *data, struct wl_callback *callback, uint32_t time) -{ - struct window *window = data; - struct buffer *buffer; - - buffer = window_next_buffer(window); - if (!buffer) { - fprintf(stderr, - !callback ? "Failed to create the first buffer.\n" : - "All buffers busy at redraw(). Server bug?\n"); - abort(); - } - - /* XXX: would be nice to draw something that changes here... */ - - wl_surface_attach(window->surface, buffer->buffer, 0, 0); - wl_surface_damage(window->surface, 0, 0, window->width, window->height); - - if (callback) - wl_callback_destroy(callback); - - window->callback = wl_surface_frame(window->surface); - wl_callback_add_listener(window->callback, &frame_listener, window); - wl_surface_commit(window->surface); - buffer->busy = 1; -} - -static const struct wl_callback_listener frame_listener = { - redraw -}; - -static void -dmabuf_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format) -{ - struct display *d = data; - - if (format == DRM_FORMAT_XRGB8888) - d->xrgb8888_format_found = 1; -} - -static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = { - dmabuf_format -}; - -static void -xdg_shell_ping(void *data, struct zxdg_shell_v6 *shell, uint32_t serial) -{ - zxdg_shell_v6_pong(shell, serial); -} - -static const struct zxdg_shell_v6_listener xdg_shell_listener = { - xdg_shell_ping, -}; - -static void -registry_handle_global(void *data, struct wl_registry *registry, - uint32_t id, const char *interface, uint32_t version) -{ - struct display *d = data; - - if (strcmp(interface, "wl_compositor") == 0) { - d->compositor = - wl_registry_bind(registry, - id, &wl_compositor_interface, 1); - } else if (strcmp(interface, "zxdg_shell_v6") == 0) { - d->shell = wl_registry_bind(registry, - id, &zxdg_shell_v6_interface, 1); - zxdg_shell_v6_add_listener(d->shell, &xdg_shell_listener, d); - } else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) { - d->fshell = wl_registry_bind(registry, - id, &zwp_fullscreen_shell_v1_interface, 1); - } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) { - d->dmabuf = wl_registry_bind(registry, - id, &zwp_linux_dmabuf_v1_interface, 1); - zwp_linux_dmabuf_v1_add_listener(d->dmabuf, &dmabuf_listener, d); - } -} - -static void -registry_handle_global_remove(void *data, struct wl_registry *registry, - uint32_t name) -{ -} - -static const struct wl_registry_listener registry_listener = { - registry_handle_global, - registry_handle_global_remove -}; - -static struct display * -create_display(void) -{ - struct display *display; - - display = malloc(sizeof *display); - if (display == NULL) { - fprintf(stderr, "out of memory\n"); - exit(1); - } - display->display = wl_display_connect(NULL); - assert(display->display); - - /* XXX: fake, because the compositor does not yet advertise anything */ - display->xrgb8888_format_found = 1; - - display->registry = wl_display_get_registry(display->display); - wl_registry_add_listener(display->registry, - ®istry_listener, display); - wl_display_roundtrip(display->display); - if (display->dmabuf == NULL) { - fprintf(stderr, "No zwp_linux_dmabuf global\n"); - exit(1); - } - - wl_display_roundtrip(display->display); - - if (!display->xrgb8888_format_found) { - fprintf(stderr, "DRM_FORMAT_XRGB8888 not available\n"); - exit(1); - } - - return display; -} - -static void -destroy_display(struct display *display) -{ - if (display->dmabuf) - zwp_linux_dmabuf_v1_destroy(display->dmabuf); - - if (display->shell) - zxdg_shell_v6_destroy(display->shell); - - if (display->fshell) - zwp_fullscreen_shell_v1_release(display->fshell); - - if (display->compositor) - wl_compositor_destroy(display->compositor); - - wl_registry_destroy(display->registry); - wl_display_flush(display->display); - wl_display_disconnect(display->display); - free(display); -} - -static void -signal_int(int signum) -{ - running = 0; -} - -int -main(int argc, char **argv) -{ - struct sigaction sigint; - struct display *display; - struct window *window; - int ret = 0; - - display = create_display(); - window = create_window(display, 250, 250); - if (!window) - return 1; - - sigint.sa_handler = signal_int; - sigemptyset(&sigint.sa_mask); - sigint.sa_flags = SA_RESETHAND; - sigaction(SIGINT, &sigint, NULL); - - /* Here we retrieve the linux-dmabuf objects, or error */ - wl_display_roundtrip(display->display); - - if (!running) - return 1; - - window->initialized = true; - - if (!window->wait_for_configure) - redraw(window, NULL, 0); - - while (running && ret != -1) - ret = wl_display_dispatch(display->display); - - fprintf(stderr, "simple-dmabuf exiting\n"); - destroy_window(window); - destroy_display(display); - - return 0; -} diff --git a/clients/simple-dmabuf-v4l.c b/clients/simple-dmabuf-v4l.c index b49d62fd9..331f049f8 100644 --- a/clients/simple-dmabuf-v4l.c +++ b/clients/simple-dmabuf-v4l.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -46,12 +47,17 @@ #include #include -#include "shared/zalloc.h" -#include "xdg-shell-unstable-v6-client-protocol.h" +#include +#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 "shared/helpers.h" #define CLEAR(x) memset(&(x), 0, sizeof(x)) +#define OPT_FLAG_INVERT (1 << 0) +#define OPT_FLAG_DIRECT_DISPLAY (1 << 1) static void redraw(void *data, struct wl_callback *callback, uint32_t time); @@ -100,10 +106,12 @@ struct display { struct wl_compositor *compositor; struct wl_seat *seat; struct wl_keyboard *keyboard; - struct zxdg_shell_v6 *shell; + 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; bool requested_format_found; + uint32_t opts; int v4l_fd; struct buffer_format format; @@ -125,8 +133,8 @@ struct buffer { struct window { struct display *display; struct wl_surface *surface; - struct zxdg_surface_v6 *xdg_surface; - struct zxdg_toplevel_v6 *xdg_toplevel; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; struct buffer buffers[NUM_BUFFERS]; struct wl_callback *callback; bool wait_for_configure; @@ -245,6 +253,8 @@ 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); @@ -262,6 +272,16 @@ 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) @@ -365,12 +385,21 @@ create_dmabuf_buffer(struct display *display, struct buffer *buffer) modifier = 0; flags = 0; - /* 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 - */ + if (display->opts & OPT_FLAG_INVERT) + 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"); + } + } + for (i = 0; i < display->format.num_planes; ++i) zwp_linux_buffer_params_v1_add(params, buffer->dmabuf_fds[i], @@ -457,8 +486,6 @@ dequeue(struct display *display) return -1; } - assert(buf.flags & V4L2_BUF_FLAG_DONE); - return buf.index; } @@ -538,36 +565,36 @@ start_capture(struct display *display) } static void -xdg_surface_handle_configure(void *data, struct zxdg_surface_v6 *surface, +xdg_surface_handle_configure(void *data, struct xdg_surface *surface, uint32_t serial) { struct window *window = data; - zxdg_surface_v6_ack_configure(surface, serial); + xdg_surface_ack_configure(surface, serial); if (window->initialized && window->wait_for_configure) redraw(window, NULL, 0); window->wait_for_configure = false; } -static const struct zxdg_surface_v6_listener xdg_surface_listener = { +static const struct xdg_surface_listener xdg_surface_listener = { xdg_surface_handle_configure, }; static void -xdg_toplevel_handle_configure(void *data, struct zxdg_toplevel_v6 *toplevel, +xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *states) { } static void -xdg_toplevel_handle_close(void *data, struct zxdg_toplevel_v6 *xdg_toplevel) +xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) { running = 0; } -static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = { +static const struct xdg_toplevel_listener xdg_toplevel_listener = { xdg_toplevel_handle_configure, xdg_toplevel_handle_close, }; @@ -585,25 +612,25 @@ create_window(struct display *display) window->display = display; window->surface = wl_compositor_create_surface(display->compositor); - if (display->shell) { + if (display->wm_base) { window->xdg_surface = - zxdg_shell_v6_get_xdg_surface(display->shell, - window->surface); + xdg_wm_base_get_xdg_surface(display->wm_base, + window->surface); assert(window->xdg_surface); - zxdg_surface_v6_add_listener(window->xdg_surface, - &xdg_surface_listener, window); + xdg_surface_add_listener(window->xdg_surface, + &xdg_surface_listener, window); window->xdg_toplevel = - zxdg_surface_v6_get_toplevel(window->xdg_surface); + xdg_surface_get_toplevel(window->xdg_surface); assert(window->xdg_toplevel); - zxdg_toplevel_v6_add_listener(window->xdg_toplevel, - &xdg_toplevel_listener, window); + xdg_toplevel_add_listener(window->xdg_toplevel, + &xdg_toplevel_listener, window); - zxdg_toplevel_v6_set_title(window->xdg_toplevel, "simple-dmabuf-v4l"); + xdg_toplevel_set_title(window->xdg_toplevel, "simple-dmabuf-v4l"); window->wait_for_configure = true; wl_surface_commit(window->surface); @@ -629,9 +656,9 @@ destroy_window(struct window *window) wl_callback_destroy(window->callback); if (window->xdg_toplevel) - zxdg_toplevel_v6_destroy(window->xdg_toplevel); + xdg_toplevel_destroy(window->xdg_toplevel); if (window->xdg_surface) - zxdg_surface_v6_destroy(window->xdg_surface); + xdg_surface_destroy(window->xdg_surface); wl_surface_destroy(window->surface); for (i = 0; i < NUM_BUFFERS; i++) { @@ -698,17 +725,27 @@ static const struct wl_callback_listener frame_listener = { }; static void -dmabuf_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, - uint32_t format) +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 = ((uint64_t) modifier_hi << 32 ) | modifier_lo; - if (format == d->drm_format) + 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 */ +} + static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = { - dmabuf_format + dmabuf_format, + dmabuf_modifier }; static void @@ -739,7 +776,7 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard, { struct display *d = data; - if (!d->shell) + if (!d->wm_base) return; if (key == KEY_ESC && state) @@ -782,13 +819,13 @@ static const struct wl_seat_listener seat_listener = { }; static void -xdg_shell_ping(void *data, struct zxdg_shell_v6 *shell, uint32_t serial) +xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) { - zxdg_shell_v6_pong(shell, serial); + xdg_wm_base_pong(shell, serial); } -static const struct zxdg_shell_v6_listener xdg_shell_listener = { - xdg_shell_ping, +static const struct xdg_wm_base_listener wm_base_listener = { + xdg_wm_base_ping, }; static void @@ -805,20 +842,22 @@ registry_handle_global(void *data, struct wl_registry *registry, d->seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); wl_seat_add_listener(d->seat, &seat_listener, d); - } else if (strcmp(interface, "zxdg_shell_v6") == 0) { - d->shell = wl_registry_bind(registry, - id, &zxdg_shell_v6_interface, 1); - zxdg_shell_v6_add_listener(d->shell, &xdg_shell_listener, d); + } else if (strcmp(interface, "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_fullscreen_shell_v1") == 0) { d->fshell = wl_registry_bind(registry, id, &zwp_fullscreen_shell_v1_interface, 1); } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) { d->dmabuf = wl_registry_bind(registry, - id, &zwp_linux_dmabuf_v1_interface, - 1); + id, &zwp_linux_dmabuf_v1_interface, 3); zwp_linux_dmabuf_v1_add_listener(d->dmabuf, &dmabuf_listener, d); + } else if (strcmp(interface, "weston_direct_display_v1") == 0) { + d->direct_display = wl_registry_bind(registry, + id, &weston_direct_display_v1_interface, 1); } } @@ -834,11 +873,11 @@ static const struct wl_registry_listener registry_listener = { }; static struct display * -create_display(uint32_t requested_format) +create_display(uint32_t requested_format, uint32_t opt_flags) { struct display *display; - display = malloc(sizeof *display); + display = zalloc(sizeof *display); if (display == NULL) { fprintf(stderr, "out of memory\n"); exit(1); @@ -859,14 +898,14 @@ create_display(uint32_t requested_format) wl_display_roundtrip(display->display); - /* XXX: fake, because the compositor does not yet advertise anything */ - display->requested_format_found = true; - if (!display->requested_format_found) { - fprintf(stderr, "DRM_FORMAT_YUYV not available\n"); + fprintf(stderr, "0x%lx requested DRM format not available\n", + (unsigned long) requested_format); exit(1); } + if (opt_flags) + display->opts = opt_flags; return display; } @@ -876,8 +915,8 @@ destroy_display(struct display *display) if (display->dmabuf) zwp_linux_dmabuf_v1_destroy(display->dmabuf); - if (display->shell) - zxdg_shell_v6_destroy(display->shell); + if (display->wm_base) + xdg_wm_base_destroy(display->wm_base); if (display->fshell) zwp_fullscreen_shell_v1_release(display->fshell); @@ -894,7 +933,7 @@ destroy_display(struct display *display) static void usage(const char *argv0) { - printf("Usage: %s [V4L2 device] [V4L2 format] [DRM format]\n" + printf("Usage: %s [-v v4l2_device] [-f v4l2_format] [-d drm_format] [-i|--y-invert] [-g|--d-display]\n" "\n" "The default V4L2 device is /dev/video0\n" "\n" @@ -903,7 +942,14 @@ usage(const char *argv0) "DRM formats are defined in \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", argv0); + "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", + argv0); printf("\n" "How to set up Vivid the virtual video driver for testing:\n" @@ -914,8 +960,11 @@ 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 /dev/video0 XR24 XR24\n" + " $ %s -v /dev/video0 -f XR24 -d 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" @@ -936,29 +985,57 @@ main(int argc, char **argv) struct sigaction sigint; struct display *display; struct window *window; - const char *v4l_device; - uint32_t v4l_format, drm_format; - int ret = 0; + const char *v4l_device = NULL; + uint32_t v4l_format = 0x0; + uint32_t drm_format = 0x0; + uint32_t opts_flags = 0x0; + int c, opt_index, 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' }, + { "help", no_argument, NULL, 'h' }, + { 0, 0, NULL, 0 } + }; + + while ((c = getopt_long(argc, argv, "hiv:d:f:g", 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; + default: + case 'h': + usage(argv[0]); + break; + } + } - if (argc < 2) { + if (!v4l_device) v4l_device = "/dev/video0"; - } else if (!strcmp(argv[1], "--help")) { - usage(argv[0]); - } else { - v4l_device = argv[1]; - } - if (argc < 3) + if (v4l_format == 0x0) v4l_format = parse_format("YUYV"); - else - v4l_format = parse_format(argv[2]); - if (argc < 4) + if (drm_format == 0x0) drm_format = v4l_format; - else - drm_format = parse_format(argv[3]); - display = create_display(drm_format); + display = create_display(drm_format, opts_flags); display->format.format = v4l_format; window = create_window(display); diff --git a/clients/simple-egl.c b/clients/simple-egl.c index 9b6fa1f2f..cd7408e94 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -42,15 +42,13 @@ #include #include -#include "xdg-shell-unstable-v6-client-protocol.h" +#include "xdg-shell-client-protocol.h" #include #include -#include "ivi-application-client-protocol.h" -#define IVI_SURFACE_ID 9000 #include "shared/helpers.h" #include "shared/platform.h" -#include "weston-egl-ext.h" +#include "shared/weston-egl-ext.h" struct window; struct seat; @@ -59,7 +57,7 @@ struct display { struct wl_display *display; struct wl_registry *registry; struct wl_compositor *compositor; - struct zxdg_shell_v6 *shell; + struct xdg_wm_base *wm_base; struct wl_seat *seat; struct wl_pointer *pointer; struct wl_touch *touch; @@ -74,7 +72,6 @@ struct display { EGLConfig conf; } egl; struct window *window; - struct ivi_application *ivi_application; PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage; }; @@ -95,12 +92,11 @@ struct window { uint32_t benchmark_time, frames; struct wl_egl_window *native; struct wl_surface *surface; - struct zxdg_surface_v6 *xdg_surface; - struct zxdg_toplevel_v6 *xdg_toplevel; - struct ivi_surface *ivi_surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; EGLSurface egl_surface; struct wl_callback *callback; - int fullscreen, opaque, buffer_size, frame_sync; + int fullscreen, maximized, opaque, buffer_size, frame_sync, delay; bool wait_for_configure; }; @@ -247,7 +243,7 @@ create_shader(struct window *window, 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); @@ -276,7 +272,7 @@ init_gl(struct window *window) 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); } @@ -294,22 +290,22 @@ init_gl(struct window *window) } static void -handle_surface_configure(void *data, struct zxdg_surface_v6 *surface, +handle_surface_configure(void *data, struct xdg_surface *surface, uint32_t serial) { struct window *window = data; - zxdg_surface_v6_ack_configure(surface, serial); + xdg_surface_ack_configure(surface, serial); window->wait_for_configure = false; } -static const struct zxdg_surface_v6_listener xdg_surface_listener = { +static const struct xdg_surface_listener xdg_surface_listener = { handle_surface_configure }; static void -handle_toplevel_configure(void *data, struct zxdg_toplevel_v6 *toplevel, +handle_toplevel_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *states) { @@ -317,23 +313,27 @@ handle_toplevel_configure(void *data, struct zxdg_toplevel_v6 *toplevel, uint32_t *p; window->fullscreen = 0; + window->maximized = 0; wl_array_for_each(p, states) { uint32_t state = *p; switch (state) { - case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN: + case XDG_TOPLEVEL_STATE_FULLSCREEN: window->fullscreen = 1; break; + case XDG_TOPLEVEL_STATE_MAXIMIZED: + window->maximized = 1; + break; } } if (width > 0 && height > 0) { - if (!window->fullscreen) { + if (!window->fullscreen && !window->maximized) { window->window_size.width = width; window->window_size.height = height; } window->geometry.width = width; window->geometry.height = height; - } else if (!window->fullscreen) { + } else if (!window->fullscreen && !window->maximized) { window->geometry = window->window_size; } @@ -344,71 +344,16 @@ handle_toplevel_configure(void *data, struct zxdg_toplevel_v6 *toplevel, } static void -handle_toplevel_close(void *data, struct zxdg_toplevel_v6 *xdg_toplevel) +handle_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) { running = 0; } -static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = { +static const struct xdg_toplevel_listener xdg_toplevel_listener = { handle_toplevel_configure, handle_toplevel_close, }; -static void -handle_ivi_surface_configure(void *data, struct ivi_surface *ivi_surface, - int32_t width, int32_t height) -{ - struct window *window = data; - - wl_egl_window_resize(window->native, width, height, 0, 0); - - window->geometry.width = width; - window->geometry.height = height; - - if (!window->fullscreen) - window->window_size = window->geometry; -} - -static const struct ivi_surface_listener ivi_surface_listener = { - handle_ivi_surface_configure, -}; - -static void -create_xdg_surface(struct window *window, struct display *display) -{ - window->xdg_surface = zxdg_shell_v6_get_xdg_surface(display->shell, - window->surface); - zxdg_surface_v6_add_listener(window->xdg_surface, - &xdg_surface_listener, window); - - window->xdg_toplevel = - zxdg_surface_v6_get_toplevel(window->xdg_surface); - zxdg_toplevel_v6_add_listener(window->xdg_toplevel, - &xdg_toplevel_listener, window); - - zxdg_toplevel_v6_set_title(window->xdg_toplevel, "simple-egl"); - - window->wait_for_configure = true; - wl_surface_commit(window->surface); -} - -static void -create_ivi_surface(struct window *window, struct display *display) -{ - uint32_t id_ivisurf = IVI_SURFACE_ID + (uint32_t)getpid(); - window->ivi_surface = - ivi_application_surface_create(display->ivi_application, - id_ivisurf, window->surface); - - if (window->ivi_surface == NULL) { - fprintf(stderr, "Failed to create ivi_client_surface\n"); - abort(); - } - - ivi_surface_add_listener(window->ivi_surface, - &ivi_surface_listener, window); -} - static void create_surface(struct window *window) { @@ -426,14 +371,20 @@ create_surface(struct window *window) display->egl.conf, window->native, NULL); + window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base, + window->surface); + xdg_surface_add_listener(window->xdg_surface, + &xdg_surface_listener, window); - if (display->shell) { - create_xdg_surface(window, display); - } else if (display->ivi_application ) { - create_ivi_surface(window, display); - } else { - assert(0); - } + window->xdg_toplevel = + xdg_surface_get_toplevel(window->xdg_surface); + xdg_toplevel_add_listener(window->xdg_toplevel, + &xdg_toplevel_listener, window); + + xdg_toplevel_set_title(window->xdg_toplevel, "simple-egl"); + + window->wait_for_configure = true; + wl_surface_commit(window->surface); ret = eglMakeCurrent(window->display->egl.dpy, window->egl_surface, window->egl_surface, window->display->egl.ctx); @@ -442,11 +393,11 @@ create_surface(struct window *window) if (!window->frame_sync) eglSwapInterval(display->egl.dpy, 0); - if (!display->shell) + if (!display->wm_base) return; if (window->fullscreen) - zxdg_toplevel_v6_set_fullscreen(window->xdg_toplevel, NULL); + xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL); } static void @@ -462,11 +413,9 @@ destroy_surface(struct window *window) wl_egl_window_destroy(window->native); if (window->xdg_toplevel) - zxdg_toplevel_v6_destroy(window->xdg_toplevel); + xdg_toplevel_destroy(window->xdg_toplevel); if (window->xdg_surface) - zxdg_surface_v6_destroy(window->xdg_surface); - if (window->display->ivi_application) - ivi_surface_destroy(window->ivi_surface); + xdg_surface_destroy(window->xdg_surface); wl_surface_destroy(window->surface); if (window->callback) @@ -548,6 +497,8 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) glDisableVertexAttribArray(window->gl.pos); glDisableVertexAttribArray(window->gl.col); + usleep(window->delay); + if (window->opaque || window->fullscreen) { region = wl_compositor_create_region(window->display->compositor); wl_region_add(region, 0, 0, @@ -624,8 +575,8 @@ pointer_handle_button(void *data, struct wl_pointer *wl_pointer, return; if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) - zxdg_toplevel_v6_move(display->window->xdg_toplevel, - display->seat, serial); + xdg_toplevel_move(display->window->xdg_toplevel, + display->seat, serial); } static void @@ -649,10 +600,10 @@ touch_handle_down(void *data, struct wl_touch *wl_touch, { struct display *d = (struct display *)data; - if (!d->shell) + if (!d->wm_base) return; - zxdg_toplevel_v6_move(d->window->xdg_toplevel, d->seat, serial); + xdg_toplevel_move(d->window->xdg_toplevel, d->seat, serial); } static void @@ -689,6 +640,8 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { + /* Just so we don’t leak the keymap fd */ + close(fd); } static void @@ -711,15 +664,14 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard, { struct display *d = data; - if (!d->shell) + if (!d->wm_base) return; if (key == KEY_F11 && state) { if (d->window->fullscreen) - zxdg_toplevel_v6_unset_fullscreen(d->window->xdg_toplevel); + xdg_toplevel_unset_fullscreen(d->window->xdg_toplevel); else - zxdg_toplevel_v6_set_fullscreen(d->window->xdg_toplevel, - NULL); + xdg_toplevel_set_fullscreen(d->window->xdg_toplevel, NULL); } else if (key == KEY_ESC && state) running = 0; } @@ -777,13 +729,13 @@ static const struct wl_seat_listener seat_listener = { }; static void -xdg_shell_ping(void *data, struct zxdg_shell_v6 *shell, uint32_t serial) +xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) { - zxdg_shell_v6_pong(shell, serial); + xdg_wm_base_pong(shell, serial); } -static const struct zxdg_shell_v6_listener xdg_shell_listener = { - xdg_shell_ping, +static const struct xdg_wm_base_listener wm_base_listener = { + xdg_wm_base_ping, }; static void @@ -795,11 +747,12 @@ registry_handle_global(void *data, struct wl_registry *registry, if (strcmp(interface, "wl_compositor") == 0) { d->compositor = wl_registry_bind(registry, name, - &wl_compositor_interface, 1); - } else if (strcmp(interface, "zxdg_shell_v6") == 0) { - d->shell = wl_registry_bind(registry, name, - &zxdg_shell_v6_interface, 1); - zxdg_shell_v6_add_listener(d->shell, &xdg_shell_listener, d); + &wl_compositor_interface, + MIN(version, 4)); + } else if (strcmp(interface, "xdg_wm_base") == 0) { + d->wm_base = wl_registry_bind(registry, name, + &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(d->wm_base, &wm_base_listener, d); } else if (strcmp(interface, "wl_seat") == 0) { d->seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); @@ -818,10 +771,6 @@ registry_handle_global(void *data, struct wl_registry *registry, fprintf(stderr, "unable to load default left pointer\n"); // TODO: abort ? } - } else if (strcmp(interface, "ivi_application") == 0) { - d->ivi_application = - wl_registry_bind(registry, name, - &ivi_application_interface, 1); } } @@ -846,6 +795,7 @@ static void usage(int error_code) { fprintf(stderr, "Usage: simple-egl [OPTIONS]\n\n" + " -d \tBuffer swap delay in microseconds\n" " -f\tRun in fullscreen mode\n" " -o\tCreate an opaque surface\n" " -s\tUse a 16 bpp EGL config\n" @@ -870,9 +820,12 @@ main(int argc, char **argv) window.window_size = window.geometry; window.buffer_size = 32; window.frame_sync = 1; + window.delay = 0; for (i = 1; i < argc; i++) { - if (strcmp("-f", argv[i]) == 0) + if (strcmp("-d", argv[i]) == 0 && i+1 < argc) + window.delay = atoi(argv[++i]); + else if (strcmp("-f", argv[i]) == 0) window.fullscreen = 1; else if (strcmp("-o", argv[i]) == 0) window.opaque = 1; @@ -913,9 +866,9 @@ main(int argc, char **argv) * queued up as a side effect. */ while (running && ret != -1) { if (window.wait_for_configure) { - wl_display_dispatch(display.display); + ret = wl_display_dispatch(display.display); } else { - wl_display_dispatch_pending(display.display); + ret = wl_display_dispatch_pending(display.display); redraw(&window, NULL, 0); } } @@ -929,11 +882,8 @@ main(int argc, char **argv) if (display.cursor_theme) wl_cursor_theme_destroy(display.cursor_theme); - if (display.shell) - zxdg_shell_v6_destroy(display.shell); - - if (display.ivi_application) - ivi_application_destroy(display.ivi_application); + if (display.wm_base) + xdg_wm_base_destroy(display.wm_base); if (display.compositor) wl_compositor_destroy(display.compositor); diff --git a/clients/simple-im.c b/clients/simple-im.c index 280589b18..a5b834ded 100644 --- a/clients/simple-im.c +++ b/clients/simple-im.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -490,7 +491,8 @@ main(int argc, char *argv[]) simple_im.display = wl_display_connect(NULL); if (simple_im.display == NULL) { - fprintf(stderr, "Failed to connect to server: %m\n"); + fprintf(stderr, "Failed to connect to server: %s\n", + strerror(errno)); return -1; } @@ -516,7 +518,7 @@ main(int argc, char *argv[]) ret = wl_display_dispatch(simple_im.display); if (ret == -1) { - fprintf(stderr, "Dispatch error: %m\n"); + fprintf(stderr, "Dispatch error: %s\n", strerror(errno)); return -1; } diff --git a/clients/simple-shm.c b/clients/simple-shm.c index 9fa2e214c..90892c87f 100644 --- a/clients/simple-shm.c +++ b/clients/simple-shm.c @@ -33,26 +33,22 @@ #include #include #include +#include #include #include "shared/os-compatibility.h" -#include "shared/zalloc.h" -#include "xdg-shell-unstable-v6-client-protocol.h" +#include +#include "xdg-shell-client-protocol.h" #include "fullscreen-shell-unstable-v1-client-protocol.h" -#include -#include "ivi-application-client-protocol.h" -#define IVI_SURFACE_ID 9000 - struct display { struct wl_display *display; struct wl_registry *registry; struct wl_compositor *compositor; - struct zxdg_shell_v6 *shell; + struct xdg_wm_base *wm_base; struct zwp_fullscreen_shell_v1 *fshell; struct wl_shm *shm; bool has_xrgb; - struct ivi_application *ivi_application; }; struct buffer { @@ -65,9 +61,8 @@ struct window { struct display *display; int width, height; struct wl_surface *surface; - struct zxdg_surface_v6 *xdg_surface; - struct zxdg_toplevel_v6 *xdg_toplevel; - struct ivi_surface *ivi_surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; struct buffer buffers[2]; struct buffer *prev_buffer; struct wl_callback *callback; @@ -104,14 +99,14 @@ create_shm_buffer(struct display *display, struct buffer *buffer, fd = os_create_anonymous_file(size); if (fd < 0) { - fprintf(stderr, "creating a buffer file for %d B failed: %m\n", - size); + 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: %m\n"); + fprintf(stderr, "mmap failed: %s\n", strerror(errno)); close(fd); return -1; } @@ -130,12 +125,12 @@ create_shm_buffer(struct display *display, struct buffer *buffer, } static void -handle_xdg_surface_configure(void *data, struct zxdg_surface_v6 *surface, +handle_xdg_surface_configure(void *data, struct xdg_surface *surface, uint32_t serial) { struct window *window = data; - zxdg_surface_v6_ack_configure(surface, serial); + xdg_surface_ack_configure(surface, serial); if (window->wait_for_configure) { redraw(window, NULL, 0); @@ -143,39 +138,28 @@ handle_xdg_surface_configure(void *data, struct zxdg_surface_v6 *surface, } } -static const struct zxdg_surface_v6_listener xdg_surface_listener = { +static const struct xdg_surface_listener xdg_surface_listener = { handle_xdg_surface_configure, }; static void -handle_xdg_toplevel_configure(void *data, struct zxdg_toplevel_v6 *xdg_toplevel, +handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *state) { } static void -handle_xdg_toplevel_close(void *data, struct zxdg_toplevel_v6 *xdg_toplevel) +handle_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) { running = 0; } -static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = { +static const struct xdg_toplevel_listener xdg_toplevel_listener = { handle_xdg_toplevel_configure, handle_xdg_toplevel_close, }; -static void -handle_ivi_surface_configure(void *data, struct ivi_surface *ivi_surface, - int32_t width, int32_t height) -{ - /* Simple-shm is resizable */ -} - -static const struct ivi_surface_listener ivi_surface_listener = { - handle_ivi_surface_configure, -}; - static struct window * create_window(struct display *display, int width, int height) { @@ -191,21 +175,21 @@ create_window(struct display *display, int width, int height) window->height = height; window->surface = wl_compositor_create_surface(display->compositor); - if (display->shell) { + if (display->wm_base) { window->xdg_surface = - zxdg_shell_v6_get_xdg_surface(display->shell, - window->surface); + xdg_wm_base_get_xdg_surface(display->wm_base, + window->surface); assert(window->xdg_surface); - zxdg_surface_v6_add_listener(window->xdg_surface, - &xdg_surface_listener, window); + xdg_surface_add_listener(window->xdg_surface, + &xdg_surface_listener, window); window->xdg_toplevel = - zxdg_surface_v6_get_toplevel(window->xdg_surface); + xdg_surface_get_toplevel(window->xdg_surface); assert(window->xdg_toplevel); - zxdg_toplevel_v6_add_listener(window->xdg_toplevel, - &xdg_toplevel_listener, window); + xdg_toplevel_add_listener(window->xdg_toplevel, + &xdg_toplevel_listener, window); - zxdg_toplevel_v6_set_title(window->xdg_toplevel, "simple-shm"); + xdg_toplevel_set_title(window->xdg_toplevel, "simple-shm"); wl_surface_commit(window->surface); window->wait_for_configure = true; } else if (display->fshell) { @@ -213,19 +197,6 @@ create_window(struct display *display, int width, int height) window->surface, ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT, NULL); - } else if (display->ivi_application ) { - uint32_t id_ivisurf = IVI_SURFACE_ID + (uint32_t)getpid(); - window->ivi_surface = - ivi_application_surface_create(display->ivi_application, - id_ivisurf, window->surface); - if (window->ivi_surface == NULL) { - fprintf(stderr, "Failed to create ivi_client_surface\n"); - abort(); - } - - ivi_surface_add_listener(window->ivi_surface, - &ivi_surface_listener, window); - } else { assert(0); } @@ -245,9 +216,9 @@ destroy_window(struct window *window) wl_buffer_destroy(window->buffers[1].buffer); if (window->xdg_toplevel) - zxdg_toplevel_v6_destroy(window->xdg_toplevel); + xdg_toplevel_destroy(window->xdg_toplevel); if (window->xdg_surface) - zxdg_surface_v6_destroy(window->xdg_surface); + xdg_surface_destroy(window->xdg_surface); wl_surface_destroy(window->surface); free(window); } @@ -376,13 +347,13 @@ struct wl_shm_listener shm_listener = { }; static void -xdg_shell_ping(void *data, struct zxdg_shell_v6 *shell, uint32_t serial) +xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) { - zxdg_shell_v6_pong(shell, serial); + xdg_wm_base_pong(shell, serial); } -static const struct zxdg_shell_v6_listener xdg_shell_listener = { - xdg_shell_ping, +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + xdg_wm_base_ping, }; static void @@ -395,10 +366,10 @@ registry_handle_global(void *data, struct wl_registry *registry, d->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 1); - } else if (strcmp(interface, "zxdg_shell_v6") == 0) { - d->shell = wl_registry_bind(registry, - id, &zxdg_shell_v6_interface, 1); - zxdg_shell_v6_add_listener(d->shell, &xdg_shell_listener, d); + } else if (strcmp(interface, "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, "zwp_fullscreen_shell_v1") == 0) { d->fshell = wl_registry_bind(registry, id, &zwp_fullscreen_shell_v1_interface, 1); @@ -407,11 +378,6 @@ registry_handle_global(void *data, struct wl_registry *registry, id, &wl_shm_interface, 1); wl_shm_add_listener(d->shm, &shm_listener, d); } - else if (strcmp(interface, "ivi_application") == 0) { - d->ivi_application = - wl_registry_bind(registry, id, - &ivi_application_interface, 1); - } } static void @@ -504,8 +470,8 @@ destroy_display(struct display *display) if (display->shm) wl_shm_destroy(display->shm); - if (display->shell) - zxdg_shell_v6_destroy(display->shell); + if (display->wm_base) + xdg_wm_base_destroy(display->wm_base); if (display->fshell) zwp_fullscreen_shell_v1_release(display->fshell); @@ -555,11 +521,6 @@ main(int argc, char **argv) fprintf(stderr, "simple-shm exiting\n"); - if (window->display->ivi_application) { - ivi_surface_destroy(window->ivi_surface); - ivi_application_destroy(window->display->ivi_application); - } - destroy_window(window); destroy_display(display); diff --git a/clients/simple-touch.c b/clients/simple-touch.c index 4d0114716..385188c3c 100644 --- a/clients/simple-touch.c +++ b/clients/simple-touch.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -70,15 +71,15 @@ create_shm_buffer(struct touch *touch) fd = os_create_anonymous_file(size); if (fd < 0) { - fprintf(stderr, "creating a buffer file for %d B failed: %m\n", - size); + fprintf(stderr, "creating a buffer file for %d B failed: %s\n", + size, strerror(errno)); exit(1); } touch->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (touch->data == MAP_FAILED) { - fprintf(stderr, "mmap failed: %m\n"); + fprintf(stderr, "mmap failed: %s\n", strerror(errno)); close(fd); exit(1); } diff --git a/clients/smoke.c b/clients/smoke.c index cedd17f4d..f1b90ec7a 100644 --- a/clients/smoke.c +++ b/clients/smoke.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -273,7 +274,8 @@ int main(int argc, char *argv[]) d = display_create(&argc, argv); if (d == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/stacking.c b/clients/stacking.c index b034cf2ab..5e9084f87 100644 --- a/clients/stacking.c +++ b/clients/stacking.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -290,7 +291,8 @@ main(int argc, char *argv[]) stacking.display = display_create(&argc, argv); if (stacking.display == NULL) { - fprintf(stderr, "Failed to create display: %m\n"); + fprintf(stderr, "Failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/subsurfaces.c b/clients/subsurfaces.c index d88b8617c..df4372f0d 100644 --- a/clients/subsurfaces.c +++ b/clients/subsurfaces.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -43,7 +44,7 @@ #include "shared/helpers.h" #include "shared/xalloc.h" -#include "shared/zalloc.h" +#include #include "window.h" #if 0 @@ -55,8 +56,8 @@ static int32_t option_red_mode; static int32_t option_triangle_mode; -static int32_t option_no_triangle; -static int32_t option_help; +static bool option_no_triangle; +static bool option_help; static const struct weston_option options[] = { { WESTON_OPTION_INTEGER, "red-mode", 'r', &option_red_mode }, @@ -295,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); @@ -324,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); } @@ -788,7 +789,8 @@ main(int argc, char *argv[]) display = display_create(&argc, argv); if (display == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/terminal.c b/clients/terminal.c index 5c25fa8d2..66e2bf549 100644 --- a/clients/terminal.c +++ b/clients/terminal.c @@ -23,6 +23,7 @@ #include "config.h" +#include #include #include #include @@ -38,17 +39,19 @@ #include #include #include +#include #include #include -#include "shared/config-parser.h" +#include #include "shared/helpers.h" #include "shared/xalloc.h" #include "window.h" -static int option_fullscreen; +static bool option_fullscreen; +static bool option_maximize; static char *option_font; static int option_font_size; static char *option_term; @@ -480,6 +483,7 @@ struct terminal { int selection_start_row, selection_start_col; int selection_end_row, selection_end_col; struct wl_list link; + int pace_pipe; }; /* Create default tab stops, every 8 characters */ @@ -859,6 +863,10 @@ resize_handler(struct widget *widget, struct terminal *terminal = data; int32_t columns, rows, m; + if (terminal->pace_pipe >= 0) { + close(terminal->pace_pipe); + terminal->pace_pipe = -1; + } m = 2 * terminal->margin; columns = (width - m) / (int32_t) terminal->average_width; rows = (height - m) / (int32_t) terminal->extents.height; @@ -1679,6 +1687,7 @@ handle_escape(struct terminal *terminal) fprintf(stderr, "Unimplemented windowOp %d\n", args[0]); break; } + break; case 'u': /* Restore cursor location */ terminal->row = terminal->saved_row; terminal->column = terminal->saved_column; @@ -2199,10 +2208,29 @@ data_source_cancelled(void *data, struct wl_data_source *source) wl_data_source_destroy(source); } +static void +data_source_dnd_drop_performed(void *data, struct wl_data_source *source) +{ +} + +static void +data_source_dnd_finished(void *data, struct wl_data_source *source) +{ +} + +static void +data_source_action(void *data, + struct wl_data_source *source, uint32_t dnd_action) +{ +} + static const struct wl_data_source_listener data_source_listener = { data_source_target, data_source_send, - data_source_cancelled + data_source_cancelled, + data_source_dnd_drop_performed, + data_source_dnd_finished, + data_source_action }; static const char text_mime_type[] = "text/plain;charset=utf-8"; @@ -2257,6 +2285,9 @@ terminal_copy(struct terminal *terminal, struct input *input) { terminal->selection = display_create_data_source(terminal->display); + if (!terminal->selection) + return; + wl_data_source_offer(terminal->selection, "text/plain;charset=utf-8"); wl_data_source_add_listener(terminal->selection, @@ -2599,7 +2630,7 @@ recompute_selection(struct terminal *terminal) int side_margin, top_margin; int start_x, end_x; int cw, ch; - union utf8_char *data; + union utf8_char *data = NULL; cw = terminal->average_width; ch = terminal->extents.height; @@ -3026,21 +3057,49 @@ terminal_run(struct terminal *terminal, const char *path) { int master; pid_t pid; + int pipes[2]; + + /* Awkwardness: There's a sticky race condition here. If + * anything prints after the forkpty() but before the window has + * a size then we'll segfault. So we make a pipe and wait on + * it before actually exec()ing the terminal. The resize + * handler closes it in the parent process and the child continues + * on to launch a shell. + * + * The reason we don't just do terminal_run() after the window + * has a size is that we'd prefer to perform the fork() before + * the process opens a wayland connection. + */ + if (pipe(pipes) == -1) { + fprintf(stderr, "Can't create pipe for pacing.\n"); + exit(EXIT_FAILURE); + } pid = forkpty(&master, NULL, NULL, NULL); if (pid == 0) { + int ret; + + close(pipes[1]); + do { + char tmp; + ret = read(pipes[0], &tmp, 1); + } while (ret == -1 && errno == EINTR); + close(pipes[0]); setenv("TERM", option_term, 1); setenv("COLORTERM", option_term, 1); if (execl(path, path, NULL)) { - printf("exec failed: %m\n"); + printf("exec failed: %s\n", strerror(errno)); exit(EXIT_FAILURE); } } else if (pid < 0) { - fprintf(stderr, "failed to fork and create pty (%m).\n"); + fprintf(stderr, "failed to fork and create pty (%s).\n", + strerror(errno)); return -1; } + close(pipes[0]); terminal->master = master; + terminal->pace_pipe = pipes[1]; fcntl(master, F_SETFL, O_NONBLOCK); terminal->io_task.run = io_handler; display_watch_fd(terminal->display, terminal->master, @@ -3048,6 +3107,8 @@ terminal_run(struct terminal *terminal, const char *path) if (option_fullscreen) window_set_fullscreen(terminal->window, 1); + else if (option_maximize) + window_set_maximized(terminal->window, 1); else terminal_resize(terminal, 80, 24); @@ -3056,6 +3117,7 @@ terminal_run(struct terminal *terminal, const char *path) static const struct weston_option terminal_options[] = { { WESTON_OPTION_BOOLEAN, "fullscreen", 'f', &option_fullscreen }, + { WESTON_OPTION_BOOLEAN, "maximized", 'm', &option_maximize }, { WESTON_OPTION_STRING, "font", 0, &option_font }, { WESTON_OPTION_INTEGER, "font-size", 0, &option_font_size }, { WESTON_OPTION_STRING, "shell", 0, &option_shell }, @@ -3066,6 +3128,7 @@ int main(int argc, char *argv[]) struct display *d; struct terminal *terminal; const char *config_file; + struct sigaction sigpipe; struct weston_config *config; struct weston_config_section *s; @@ -3089,15 +3152,25 @@ int main(int argc, char *argv[]) ARRAY_LENGTH(terminal_options), &argc, argv) > 1) { printf("Usage: %s [OPTIONS]\n" " --fullscreen or -f\n" + " --maximized or -m\n" " --font=NAME\n" " --font-size=SIZE\n" " --shell=NAME\n", argv[0]); return 1; } + /* Disable SIGPIPE so that paste operations do not crash the program + * when the file descriptor provided to receive data is a pipe or + * socket whose reading end has been closed */ + sigpipe.sa_handler = SIG_IGN; + sigemptyset(&sigpipe.sa_mask); + sigpipe.sa_flags = 0; + sigaction(SIGPIPE, &sigpipe, NULL); + d = display_create(&argc, argv); if (d == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/touch-calibrator.c b/clients/touch-calibrator.c new file mode 100644 index 000000000..66208d16f --- /dev/null +++ b/clients/touch-calibrator.c @@ -0,0 +1,970 @@ +/* + * Copyright 2012 Intel Corporation + * Copyright 2017-2018 Collabora, Ltd. + * Copyright 2017-2018 General Electric Company + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clients/window.h" +#include "shared/helpers.h" +#include + +#include "weston-touch-calibration-client-protocol.h" + +enum exit_code { + CAL_EXIT_SUCCESS = 0, + CAL_EXIT_ERROR = 1, + CAL_EXIT_CANCELLED = 2, +}; + +static int debug_; +static int verbose_; + +#define pr_ver(...) do { \ + if (verbose_) \ + printf(__VA_ARGS__); \ +} while (0) + +#define pr_dbg(...) do { \ + if (debug_) \ + fprintf(stderr, __VA_ARGS__); \ +} while (0) + +static void +pr_err(const char *fmt, ...) WL_PRINTF(1, 2); + +/* Our points for the calibration must be not be on a line */ +static const struct { + float x_ratio, y_ratio; +} test_ratios[] = { + { 0.15, 0.10 }, /* three points for calibration */ + { 0.85, 0.13 }, + { 0.20, 0.80 }, + { 0.70, 0.75 } /* and one for verification */ +}; + +#define NR_SAMPLES ((int)ARRAY_LENGTH(test_ratios)) + +struct point { + double x; + double y; +}; + +struct sample { + int ind; + struct point drawn; /**< drawn point, pixels */ + struct weston_touch_coordinate *pending; + struct point drawn_cal; /**< drawn point, converted */ + bool conv_done; + struct point touched; /**< touch point, normalized */ + bool touch_done; +}; + +struct poly { + struct color { + double r, g, b, a; + } color; + int n_verts; + const struct point *verts; +}; + +/** Touch event handling state machine + * + * Only a complete down->up->frame sequence should be accepted with user + * feedback "right", and anything that deviates from that (invalid_touch, + * cancel, multiple touch-downs) needs to undo the current sample and + * possibly show user feedback "wrong". + * + * \ + * - \: \ + * + * IDLE + * - touch down: sample, -> DOWN + * - touch up: no-op + * - frame: no-op + * - invalid_touch: (undo), wrong, -> WAIT + * - cancel: no-op + * DOWN (first touch down) + * - touch down: undo, wrong, -> WAIT + * - touch up: -> UP + * - frame: no-op + * - invalid_touch: undo, wrong, -> WAIT + * - cancel: undo, -> IDLE + * UP (first touch was down and up) + * - touch down: undo, wrong, -> WAIT + * - touch up: no-op + * - frame: right, touch finish, -> WAIT + * - invalid_touch: undo, wrong, -> WAIT + * - cancel: undo, -> IDLE + * WAIT (show user feedback) + * - touch down: no-op + * - touch up: no-op + * - frame, cancel, timer: if num_tp == 0 && timer_done -> IDLE + * - invalid_touch: no-op + */ +enum touch_state { + STATE_IDLE, + STATE_DOWN, + STATE_UP, + STATE_WAIT +}; + +struct calibrator { + struct sample samples[NR_SAMPLES]; + int current_sample; + + struct display *display; + struct weston_touch_calibration *calibration; + struct weston_touch_calibrator *calibrator; + struct window *window; + struct widget *widget; + + int n_devices_listed; + char *match_name; + char *device_name; + + int width; + int height; + + bool cancelled; + + const struct poly *current_poly; + bool exiting; + + struct toytimer wait_timer; + bool timer_pending; + enum touch_state state; + + int num_tp; /* touch points down count */ +}; + +static struct sample * +current_sample(struct calibrator *cal) +{ + return &cal->samples[cal->current_sample]; +} + +static void +sample_start(struct calibrator *cal, int i) +{ + struct sample *s = &cal->samples[i]; + + assert(i >= 0 && i < NR_SAMPLES); + + s->ind = i; + s->drawn.x = round(test_ratios[i].x_ratio * cal->width); + s->drawn.y = round(test_ratios[i].y_ratio * cal->height); + s->pending = NULL; + s->conv_done = false; + s->touch_done = false; + + cal->current_sample = i; +} + +static struct point +wire_to_point(uint32_t xu, uint32_t yu) +{ + struct point p = { + .x = (double)xu / 0xffffffff, + .y = (double)yu / 0xffffffff + }; + + return p; +} + +static void +sample_touch_down(struct calibrator *cal, uint32_t xu, uint32_t yu) +{ + struct sample *s = current_sample(cal); + + s->touched = wire_to_point(xu, yu); + s->touch_done = true; + + pr_dbg("Down[%d] (%f, %f)\n", s->ind, s->touched.x, s->touched.y); +} + +static void +coordinate_result_handler(void *data, struct weston_touch_coordinate *interface, + uint32_t xu, uint32_t yu) +{ + struct sample *s = data; + + weston_touch_coordinate_destroy(s->pending); + s->pending = NULL; + + s->drawn_cal = wire_to_point(xu, yu); + s->conv_done = true; + + pr_dbg("Conv[%d] (%f, %f)\n", s->ind, s->drawn_cal.x, s->drawn_cal.y); +} + +struct weston_touch_coordinate_listener coordinate_listener = { + coordinate_result_handler +}; + +static void +sample_undo(struct calibrator *cal) +{ + struct sample *s = current_sample(cal); + + pr_dbg("Undo[%d]\n", s->ind); + + s->touch_done = false; + s->conv_done = false; + if (s->pending) { + weston_touch_coordinate_destroy(s->pending); + s->pending = NULL; + } +} + +static void +sample_finish(struct calibrator *cal) +{ + struct sample *s = current_sample(cal); + + pr_dbg("Finish[%d]\n", s->ind); + + assert(!s->pending && !s->conv_done); + + s->pending = weston_touch_calibrator_convert(cal->calibrator, + (int32_t)s->drawn.x, + (int32_t)s->drawn.y); + weston_touch_coordinate_add_listener(s->pending, + &coordinate_listener, s); + + if (cal->current_sample + 1 < NR_SAMPLES) { + sample_start(cal, cal->current_sample + 1); + } else { + pr_dbg("got all touches\n"); + cal->exiting = true; + } +} + +/* + * Calibration algorithm: + * + * The equation we want to apply at event time where x' and y' are the + * calibrated co-ordinates. + * + * x' = Ax + By + C + * y' = Dx + Ey + F + * + * For example "zero calibration" would be A=1.0 B=0.0 C=0.0, D=0.0, E=1.0, + * and F=0.0. + * + * With 6 unknowns we need 6 equations to find the constants: + * + * x1' = Ax1 + By1 + C + * y1' = Dx1 + Ey1 + F + * ... + * x3' = Ax3 + By3 + C + * y3' = Dx3 + Ey3 + F + * + * In matrix form: + * + * x1' x1 y1 1 A + * x2' = x2 y2 1 x B + * x3' x3 y3 1 C + * + * So making the matrix M we can find the constants with: + * + * A x1' + * B = M^-1 x x2' + * C x3' + * + * (and similarly for D, E and F) + * + * For the calibration the desired values x, y are the same values at which + * we've drawn at. + * + */ +static int +compute_calibration(struct calibrator *cal, float *result) +{ + struct weston_matrix m; + struct weston_matrix inverse; + struct weston_vector x_calib; + struct weston_vector y_calib; + int i; + + assert(NR_SAMPLES >= 3); + + /* + * x1 y1 1 0 + * x2 y2 1 0 + * x3 y3 1 0 + * 0 0 0 1 + */ + weston_matrix_init(&m); + for (i = 0; i < 3; i++) { + m.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; + + if (weston_matrix_invert(&inverse, &m) < 0) { + pr_err("non-invertible matrix during computation\n"); + return -1; + } + + for (i = 0; i < 3; i++) { + x_calib.f[i] = cal->samples[i].drawn_cal.x; + y_calib.f[i] = cal->samples[i].drawn_cal.y; + } + 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.f[i]; + for (i = 0; i < 3; i++) + result[i + 3] = y_calib.f[i]; + + return 0; +} + +static int +verify_calibration(struct calibrator *cal, const float *r) +{ + double thr = 0.1; /* accepted error radius */ + struct point e; /* expected value; error */ + const struct sample *s = &cal->samples[3]; + + /* transform raw touches through the matrix */ + e.x = r[0] * s->touched.x + r[1] * s->touched.y + r[2]; + e.y = r[3] * s->touched.x + r[4] * s->touched.y + r[5]; + + /* compute error */ + e.x -= s->drawn_cal.x; + e.y -= s->drawn_cal.y; + + pr_dbg("calibration test error: %f, %f\n", e.x, e.y); + + if (e.x * e.x + e.y * e.y < thr * thr) + return 0; + + pr_err("Calibration verification failed, too large error.\n"); + return -1; +} + +static void +send_calibration(struct calibrator *cal, float *values) +{ + struct wl_array matrix; + float *f; + int i; + + wl_array_init(&matrix); + for (i = 0; i < 6; i++) { + f = wl_array_add(&matrix, sizeof *f); + *f = values[i]; + } + weston_touch_calibration_save(cal->calibration, + cal->device_name, &matrix); + wl_array_release(&matrix); +} + +static const struct point cross_verts[] = { + { 0.1, 0.2 }, + { 0.2, 0.1 }, + { 0.5, 0.4 }, + { 0.8, 0.1 }, + { 0.9, 0.2 }, + { 0.6, 0.5 }, + { 0.9, 0.8 }, + { 0.8, 0.9 }, + { 0.5, 0.6 }, + { 0.2, 0.9 }, + { 0.1, 0.8 }, + { 0.4, 0.5 }, +}; + +/* a red cross, for "wrong" */ +static const struct poly cross = { + .color = { 0.7, 0.0, 0.0, 1.0 }, + .n_verts = ARRAY_LENGTH(cross_verts), + .verts = cross_verts +}; + +static const struct point check_verts[] = { + { 0.5, 0.7 }, + { 0.8, 0.1 }, + { 0.9, 0.1 }, + { 0.55, 0.8 }, + { 0.45, 0.8 }, + { 0.3, 0.5 }, + { 0.4, 0.5 } +}; + +/* a green check mark, for "right" */ +static const struct poly check = { + .color = { 0.0, 0.7, 0.0, 1.0 }, + .n_verts = ARRAY_LENGTH(check_verts), + .verts = check_verts +}; + +static void +draw_poly(cairo_t *cr, const struct poly *poly) +{ + int i; + + cairo_set_source_rgba(cr, poly->color.r, poly->color.g, + poly->color.b, poly->color.a); + cairo_move_to(cr, poly->verts[0].x, poly->verts[0].y); + for (i = 1; i < poly->n_verts; i++) + cairo_line_to(cr, poly->verts[i].x, poly->verts[i].y); + cairo_close_path(cr); + cairo_fill(cr); +} + +static void +feedback_show(struct calibrator *cal, const struct poly *what) +{ + cal->current_poly = what; + widget_schedule_redraw(cal->widget); + + toytimer_arm_once_usec(&cal->wait_timer, 1000 * 1000); + cal->timer_pending = true; +} + +static void +feedback_hide(struct calibrator *cal) +{ + cal->current_poly = NULL; + widget_schedule_redraw(cal->widget); +} + +static void +try_enter_state_idle(struct calibrator *cal) +{ + if (cal->num_tp != 0) + return; + + if (cal->timer_pending) + return; + + cal->state = STATE_IDLE; + + feedback_hide(cal); + + if (cal->exiting) + display_exit(cal->display); +} + +static void +enter_state_wait(struct calibrator *cal) +{ + assert(cal->timer_pending); + cal->state = STATE_WAIT; +} + +static void +wait_timer_done(struct toytimer *tt) +{ + struct calibrator *cal = container_of(tt, struct calibrator, wait_timer); + + assert(cal->state == STATE_WAIT); + cal->timer_pending = false; + try_enter_state_idle(cal); +} + +static void +redraw_handler(struct widget *widget, void *data) +{ + struct calibrator *cal = data; + struct sample *s = current_sample(cal); + struct rectangle allocation; + cairo_surface_t *surface; + cairo_t *cr; + + widget_get_allocation(cal->widget, &allocation); + assert(allocation.width == cal->width); + assert(allocation.height == cal->height); + + surface = window_get_surface(cal->window); + cr = cairo_create(surface); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); + cairo_paint(cr); + + if (!cal->current_poly) { + cairo_translate(cr, s->drawn.x, s->drawn.y); + cairo_set_line_width(cr, 2.0); + cairo_set_source_rgb(cr, 0.7, 0.0, 0.0); + cairo_move_to(cr, 0, -10.0); + cairo_line_to(cr, 0, 10.0); + cairo_stroke(cr); + cairo_move_to(cr, -10.0, 0); + cairo_line_to(cr, 10.0, 0.0); + cairo_stroke(cr); + } else { + cairo_scale(cr, allocation.width, allocation.height); + draw_poly(cr, cal->current_poly); + } + + cairo_destroy(cr); + cairo_surface_destroy(surface); +} + +static struct calibrator * +calibrator_create(struct display *display, const char *match_name) +{ + struct calibrator *cal; + + cal = zalloc(sizeof *cal); + if (!cal) + abort(); + + cal->match_name = match_name ? strdup(match_name) : NULL; + cal->window = window_create_custom(display); + cal->widget = window_add_widget(cal->window, cal); + window_inhibit_redraw(cal->window); + window_set_title(cal->window, "Touchscreen calibrator"); + cal->display = display; + + widget_set_redraw_handler(cal->widget, redraw_handler); + + toytimer_init(&cal->wait_timer, CLOCK_MONOTONIC, + display, wait_timer_done); + + cal->state = STATE_IDLE; + cal->num_tp = 0; + + return cal; +} + +static void +configure_handler(void *data, struct weston_touch_calibrator *interface, + int32_t width, int32_t height) +{ + struct calibrator *cal = data; + + pr_dbg("Configure calibrator window to size %ix%i\n", width, height); + cal->width = width; + cal->height = height; + window_schedule_resize(cal->window, width, height); + window_uninhibit_redraw(cal->window); + + sample_start(cal, 0); + widget_schedule_redraw(cal->widget); +} + +static void +cancel_calibration_handler(void *data, struct weston_touch_calibrator *interface) +{ + struct calibrator *cal = data; + + pr_dbg("calibration cancelled by the display server, quitting.\n"); + cal->cancelled = true; + display_exit(cal->display); +} + +static void +invalid_touch_handler(void *data, struct weston_touch_calibrator *interface) +{ + struct calibrator *cal = data; + + pr_dbg("invalid touch\n"); + + switch (cal->state) { + case STATE_IDLE: + case STATE_DOWN: + case STATE_UP: + sample_undo(cal); + feedback_show(cal, &cross); + enter_state_wait(cal); + break; + case STATE_WAIT: + /* no-op */ + break; + } +} + +static void +down_handler(void *data, struct weston_touch_calibrator *interface, + uint32_t time, int32_t id, uint32_t xu, uint32_t yu) +{ + struct calibrator *cal = data; + + cal->num_tp++; + + switch (cal->state) { + case STATE_IDLE: + sample_touch_down(cal, xu, yu); + cal->state = STATE_DOWN; + break; + case STATE_DOWN: + case STATE_UP: + sample_undo(cal); + feedback_show(cal, &cross); + enter_state_wait(cal); + break; + case STATE_WAIT: + /* no-op */ + break; + } + + if (cal->current_poly) + return; +} + +static void +up_handler(void *data, struct weston_touch_calibrator *interface, + uint32_t time, int32_t id) +{ + struct calibrator *cal = data; + + cal->num_tp--; + if (cal->num_tp < 0) { + pr_dbg("Unmatched touch up.\n"); + cal->num_tp = 0; + } + + switch (cal->state) { + case STATE_DOWN: + cal->state = STATE_UP; + break; + case STATE_IDLE: + case STATE_UP: + case STATE_WAIT: + /* no-op */ + break; + } +} + +static void +motion_handler(void *data, struct weston_touch_calibrator *interface, + uint32_t time, int32_t id, uint32_t xu, uint32_t yu) +{ + /* motion is ignored */ +} + +static void +frame_handler(void *data, struct weston_touch_calibrator *interface) +{ + struct calibrator *cal = data; + + switch (cal->state) { + case STATE_IDLE: + case STATE_DOWN: + /* no-op */ + break; + case STATE_UP: + feedback_show(cal, &check); + sample_finish(cal); + enter_state_wait(cal); + break; + case STATE_WAIT: + try_enter_state_idle(cal); + break; + } +} + +static void +cancel_handler(void *data, struct weston_touch_calibrator *interface) +{ + struct calibrator *cal = data; + + cal->num_tp = 0; + + switch (cal->state) { + case STATE_IDLE: + /* no-op */ + break; + case STATE_DOWN: + case STATE_UP: + sample_undo(cal); + try_enter_state_idle(cal); + break; + case STATE_WAIT: + try_enter_state_idle(cal); + break; + } +} + +struct weston_touch_calibrator_listener calibrator_listener = { + configure_handler, + cancel_calibration_handler, + invalid_touch_handler, + down_handler, + up_handler, + motion_handler, + frame_handler, + cancel_handler +}; + +static void +calibrator_show(struct calibrator *cal) +{ + struct wl_surface *surface = window_get_wl_surface(cal->window); + + cal->calibrator = + weston_touch_calibration_create_calibrator(cal->calibration, + surface, + cal->device_name); + weston_touch_calibrator_add_listener(cal->calibrator, + &calibrator_listener, cal); +} + +static void +calibrator_destroy(struct calibrator *cal) +{ + toytimer_fini(&cal->wait_timer); + if (cal->calibrator) + weston_touch_calibrator_destroy(cal->calibrator); + if (cal->calibration) + weston_touch_calibration_destroy(cal->calibration); + if (cal->widget) + widget_destroy(cal->widget); + if (cal->window) + window_destroy(cal->window); + free(cal->match_name); + free(cal->device_name); + free(cal); +} + +static void +touch_device_handler(void *data, struct weston_touch_calibration *c, + const char *device, const char *head) +{ + struct calibrator *cal = data; + + cal->n_devices_listed++; + + if (!cal->match_name) { + printf("device \"%s\" - head \"%s\"\n", device, head); + return; + } + + if (cal->device_name) + return; + + if (strcmp(cal->match_name, device) == 0 || + strcmp(cal->match_name, head) == 0) + cal->device_name = strdup(device); +} + +struct weston_touch_calibration_listener touch_calibration_listener = { + touch_device_handler +}; + +static void +global_handler(struct display *display, uint32_t name, + const char *interface, uint32_t version, void *data) +{ + struct calibrator *cal = data; + + if (strcmp(interface, "weston_touch_calibration") == 0) { + cal->calibration = display_bind(display, name, + &weston_touch_calibration_interface, 1); + weston_touch_calibration_add_listener(cal->calibration, + &touch_calibration_listener, + cal); + } +} + +static int +calibrator_run(struct calibrator *cal) +{ + struct wl_display *dpy; + struct sample *s; + bool wait; + int i; + int ret; + float result[6]; + + calibrator_show(cal); + display_run(cal->display); + + if (cal->cancelled) + return CAL_EXIT_CANCELLED; + + /* remove the window, no more input events */ + widget_destroy(cal->widget); + cal->widget = NULL; + window_destroy(cal->window); + cal->window = NULL; + + /* wait for all conversions to return */ + dpy = display_get_display(cal->display); + do { + wait = false; + + for (i = 0; i < NR_SAMPLES; i++) + if (cal->samples[i].pending) + wait = true; + + if (wait) { + ret = wl_display_roundtrip(dpy); + if (ret < 0) + return CAL_EXIT_ERROR; + } + } while (wait); + + for (i = 0; i < NR_SAMPLES; i++) { + s = &cal->samples[i]; + if (!s->conv_done || !s->touch_done) + return CAL_EXIT_ERROR; + } + + if (compute_calibration(cal, result) < 0) + return CAL_EXIT_ERROR; + + if (verify_calibration(cal, result) < 0) + return CAL_EXIT_ERROR; + + pr_ver("Calibration values:"); + for (i = 0; i < 6; i++) + pr_ver(" %f", result[i]); + pr_ver("\n"); + + send_calibration(cal, result); + ret = wl_display_roundtrip(dpy); + if (ret < 0) + return CAL_EXIT_ERROR; + + return CAL_EXIT_SUCCESS; +} + +static void +pr_err(const char *fmt, ...) +{ + va_list argp; + + va_start(argp, fmt); + fprintf(stderr, "%s error: ", program_invocation_short_name); + vfprintf(stderr, fmt, argp); + va_end(argp); +} + +static void +help(void) +{ + fprintf(stderr, "Compute a touchscreen calibration matrix for " + "a Wayland compositor by\n" + "having the user touch points on the screen.\n\n"); + fprintf(stderr, "Usage: %s [options...] name\n\n", + program_invocation_short_name); + fprintf(stderr, + "Where 'name' can be a touch device sys path or a head name.\n" + "If 'name' is not given, all devices available for " + "calibration will be listed.\n" + "If 'name' is given, it must be exactly as listed.\n" + "Options:\n" + " --debug Print messages to help debugging.\n" + " -h, --help Display this help message\n" + " -v, --verbose Print list header and calibration result.\n"); +} + +int +main(int argc, char *argv[]) +{ + struct display *display; + struct calibrator *cal; + int c; + char *match_name = NULL; + int exit_code = CAL_EXIT_SUCCESS; + static const struct option opts[] = { + { "help", no_argument, NULL, 'h' }, + { "debug", no_argument, &debug_, 1 }, + { "verbose", no_argument, &verbose_, 1 }, + { 0, 0, NULL, 0 } + }; + + while ((c = getopt_long(argc, argv, "hv", opts, NULL)) != -1) { + switch (c) { + case 'h': + help(); + return CAL_EXIT_SUCCESS; + case 'v': + verbose_ = 1; + break; + case 0: + break; + default: + return CAL_EXIT_ERROR; + } + } + + if (optind < argc) + match_name = argv[optind++]; + + if (optind < argc) { + pr_err("extra arguments given.\n\n"); + help(); + return CAL_EXIT_ERROR; + } + + display = display_create(&argc, argv); + if (!display) + return CAL_EXIT_ERROR; + + cal = calibrator_create(display, match_name); + if (!cal) + return CAL_EXIT_ERROR; + + display_set_user_data(display, cal); + display_set_global_handler(display, global_handler); + + if (!match_name) + pr_ver("Available touch devices:\n"); + + /* Roundtrip to get list of available touch devices, + * first globals, then touch_device events */ + wl_display_roundtrip(display_get_display(display)); + wl_display_roundtrip(display_get_display(display)); + + if (!cal->calibration) { + exit_code = CAL_EXIT_ERROR; + pr_err("the Wayland server does not expose the calibration interface.\n"); + } else if (cal->device_name) { + exit_code = calibrator_run(cal); + } else if (match_name) { + exit_code = CAL_EXIT_ERROR; + pr_err("\"%s\" was not found.\n", match_name); + } else if (cal->n_devices_listed == 0) { + fprintf(stderr, "No devices listed.\n"); + } + + calibrator_destroy(cal); + display_destroy(display); + + return exit_code; +} diff --git a/clients/transformed.c b/clients/transformed.c index 6687a4a80..59f44bcaa 100644 --- a/clients/transformed.c +++ b/clients/transformed.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -226,11 +227,13 @@ static void usage(int error_code) { fprintf(stderr, "Usage: transformed [OPTIONS]\n\n" - " -d\t\tUse \"driver\" fullscreen method\n" " -w \tSet window width to \n" " -h \tSet window height to \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); } @@ -263,7 +266,8 @@ int main(int argc, char *argv[]) d = display_create(&argc, argv); if (d == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } diff --git a/clients/weston-debug.c b/clients/weston-debug.c new file mode 100644 index 000000000..3060dec97 --- /dev/null +++ b/clients/weston-debug.c @@ -0,0 +1,501 @@ +/* + * Copyright © 2017 Pekka Paalanen + * Copyright © 2018 Zodiac Inflight Innovations + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "shared/helpers.h" +#include +#include "weston-debug-client-protocol.h" + +struct debug_app { + struct { + bool help; + bool list; + bool bind_all; + char *output; + char *outfd; + } opt; + + int out_fd; + struct wl_display *dpy; + struct wl_registry *registry; + struct weston_debug_v1 *debug_iface; + struct wl_list stream_list; +}; + +struct debug_stream { + struct wl_list link; + bool should_bind; + char *name; + char *desc; + struct weston_debug_stream_v1 *obj; +}; + +/** + * Called either through stream_find in response to an advertisement + * event (see comment on stream_find) when we have all the information, + * or directly from option parsing to make a placeholder entry when the + * stream was explicitly named on the command line to bind to. + */ +static struct debug_stream * +stream_alloc(struct debug_app *app, const char *name, const char *desc) +{ + struct debug_stream *stream; + + stream = zalloc(sizeof *stream); + if (!stream) + return NULL; + + stream->name = strdup(name); + if (!stream->name) { + free(stream); + return NULL; + } + + if (desc) { + stream->desc = strdup(desc); + if (!stream->desc) { + free(stream->name); + free(stream); + return NULL; + } + } + + stream->should_bind = app->opt.bind_all; + wl_list_insert(app->stream_list.prev, &stream->link); + + return stream; +} + +/** + * Called in response to a stream advertisement event. If our stream was + * manually specified on the command line, then it will already have a + * dummy entry in stream_list: we fill in its description and return. + * If there's no entry in the list, we make a new one and return that. + */ +static struct debug_stream * +stream_find(struct debug_app *app, const char *name, const char *desc) +{ + struct debug_stream *stream; + + wl_list_for_each(stream, &app->stream_list, link) { + if (strcmp(stream->name, name) == 0) { + assert(stream->desc == NULL); + if (desc) + stream->desc = strdup(desc); + return stream; + } + } + + return stream_alloc(app, name, desc); +} + +static void +stream_destroy(struct debug_stream *stream) +{ + if (stream->obj) + weston_debug_stream_v1_destroy(stream->obj); + + wl_list_remove(&stream->link); + free(stream->name); + free(stream); +} + +static void +destroy_streams(struct debug_app *app) +{ + struct debug_stream *stream; + struct debug_stream *tmp; + + wl_list_for_each_safe(stream, tmp, &app->stream_list, link) + stream_destroy(stream); +} + +static void +debug_advertise(void *data, struct weston_debug_v1 *debug, const char *name, + const char *desc) +{ + struct debug_app *app = data; + (void) stream_find(app, name, desc); +} + +static const struct weston_debug_v1_listener debug_listener = { + debug_advertise, +}; + +static void +global_handler(void *data, struct wl_registry *registry, uint32_t id, + const char *interface, uint32_t version) +{ + struct debug_app *app = data; + uint32_t myver; + + assert(app->registry == registry); + + if (!strcmp(interface, weston_debug_v1_interface.name)) { + if (app->debug_iface) + return; + + myver = MIN(1, version); + app->debug_iface = + wl_registry_bind(registry, id, + &weston_debug_v1_interface, myver); + weston_debug_v1_add_listener(app->debug_iface, &debug_listener, + app); + } +} + +static void +global_remove_handler(void *data, struct wl_registry *registry, uint32_t name) +{ +} + +static const struct wl_registry_listener registry_listener = { + global_handler, + global_remove_handler +}; + +static void +handle_stream_complete(void *data, struct weston_debug_stream_v1 *obj) +{ + struct debug_stream *stream = data; + + assert(stream->obj == obj); + + stream_destroy(stream); +} + +static void +handle_stream_failure(void *data, struct weston_debug_stream_v1 *obj, + const char *msg) +{ + struct debug_stream *stream = data; + + assert(stream->obj == obj); + + fprintf(stderr, "Debug stream '%s' aborted: %s\n", stream->name, msg); + + stream_destroy(stream); +} + +static const struct weston_debug_stream_v1_listener stream_listener = { + handle_stream_complete, + handle_stream_failure +}; + +static void +start_streams(struct debug_app *app) +{ + struct debug_stream *stream; + + wl_list_for_each(stream, &app->stream_list, link) { + if (!stream->should_bind) + continue; + + stream->obj = weston_debug_v1_subscribe(app->debug_iface, + stream->name, + app->out_fd); + weston_debug_stream_v1_add_listener(stream->obj, + &stream_listener, stream); + } +} + +static void +list_streams(struct debug_app *app) +{ + struct debug_stream *stream; + + fprintf(stderr, "Available debug streams:\n"); + + wl_list_for_each(stream, &app->stream_list, link) { + if (stream->should_bind && stream->desc) { + fprintf(stderr, " %s [will bind]\n", stream->name); + fprintf(stderr, " %s\n", stream->desc); + } else if (stream->should_bind) { + fprintf(stderr, " %s [wanted but not found]\n", + stream->name); + } else { + fprintf(stderr, " %s [will not bind]\n", + stream->name); + fprintf(stderr, " %s\n", stream->desc); + } + } +} + +static int +setup_out_fd(const char *output, const char *outfd) +{ + int fd = -1; + int flags; + + assert(!(output && outfd)); + + if (output) { + if (strcmp(output, "-") == 0) { + fd = STDOUT_FILENO; + } else { + fd = open(output, + O_WRONLY | O_APPEND | O_CREAT, 0644); + if (fd < 0) { + fprintf(stderr, + "Error: opening file '%s' failed: %s\n", + output, strerror(errno)); + } + return fd; + } + } else if (outfd) { + fd = atoi(outfd); + } else { + fd = STDOUT_FILENO; + } + + flags = fcntl(fd, F_GETFL); + if (flags == -1) { + fprintf(stderr, + "Error: cannot use file descriptor %d: %s\n", fd, + strerror(errno)); + return -1; + } + + if ((flags & O_ACCMODE) != O_WRONLY && + (flags & O_ACCMODE) != O_RDWR) { + fprintf(stderr, + "Error: file descriptor %d is not writable.\n", fd); + return -1; + } + + return fd; +} + +static void +print_help(void) +{ + fprintf(stderr, + "Usage: weston-debug [options] [names]\n" + "Where options may be:\n" + " -h, --help\n" + " This help text, and exit with success.\n" + " -l, --list\n" + " Print a list of available debug streams to stderr.\n" + " -a, --all-streams\n" + " Bind to all available streams.\n" + " -o FILE, --output FILE\n" + " Direct output to file named FILE. Use - for stdout.\n" + " Stdout is the default. Mutually exclusive with -f.\n" + " -f FD, --outfd FD\n" + " Direct output to the file descriptor FD.\n" + " Stdout (1) is the default. Mutually exclusive with -o.\n" + "Names are whatever debug stream names the compositor supports.\n" + ); +} + +static int +parse_cmdline(struct debug_app *app, int argc, char **argv) +{ + static const struct option opts[] = { + { "help", no_argument, NULL, 'h' }, + { "list", no_argument, NULL, 'l' }, + { "all-streams", no_argument, NULL, 'a' }, + { "output", required_argument, NULL, 'o' }, + { "outfd", required_argument, NULL, 'f' }, + { 0 } + }; + static const char optstr[] = "hlao:f:"; + int c; + bool failed = false; + + while (1) { + c = getopt_long(argc, argv, optstr, opts, NULL); + if (c == -1) + break; + + switch (c) { + case 'h': + app->opt.help = true; + break; + case 'l': + app->opt.list = true; + break; + case 'a': + app->opt.bind_all = true; + break; + case 'o': + free(app->opt.output); + app->opt.output = strdup(optarg); + break; + case 'f': + free(app->opt.outfd); + app->opt.outfd = strdup(optarg); + break; + case '?': + failed = true; + break; + default: + fprintf(stderr, "huh? getopt => %c (%d)\n", c, c); + failed = true; + } + } + + if (failed) + return -1; + + while (optind < argc) { + struct debug_stream *stream = + stream_alloc(app, argv[optind++], NULL); + stream->should_bind = true; + } + + return 0; +} + +int +main(int argc, char **argv) +{ + struct debug_app app = {}; + int ret = 0; + + wl_list_init(&app.stream_list); + app.out_fd = -1; + + if (parse_cmdline(&app, argc, argv) < 0) { + ret = 1; + goto out_parse; + } + + if (app.opt.help) { + print_help(); + goto out_parse; + } + + if (!app.opt.list && !app.opt.bind_all && + wl_list_empty(&app.stream_list)) { + fprintf(stderr, "Error: no options given.\n\n"); + ret = 1; + print_help(); + goto out_parse; + } + + if (app.opt.bind_all && !wl_list_empty(&app.stream_list)) { + fprintf(stderr, "Error: --all and specific stream names cannot be used simultaneously.\n"); + ret = 1; + goto out_parse; + } + + if (app.opt.output && app.opt.outfd) { + fprintf(stderr, "Error: options --output and --outfd cannot be used simultaneously.\n"); + ret = 1; + goto out_parse; + } + + app.out_fd = setup_out_fd(app.opt.output, app.opt.outfd); + if (app.out_fd < 0) { + ret = 1; + goto out_parse; + } + + app.dpy = wl_display_connect(NULL); + if (!app.dpy) { + fprintf(stderr, "Error: Could not connect to Wayland display: %s\n", + strerror(errno)); + ret = 1; + goto out_parse; + } + + app.registry = wl_display_get_registry(app.dpy); + wl_registry_add_listener(app.registry, ®istry_listener, &app); + wl_display_roundtrip(app.dpy); + + if (!app.debug_iface) { + ret = 1; + fprintf(stderr, + "The Wayland server does not support %s interface.\n", + weston_debug_v1_interface.name); + goto out_conn; + } + + wl_display_roundtrip(app.dpy); /* for weston_debug_v1::advertise */ + + if (app.opt.list) + list_streams(&app); + + start_streams(&app); + + weston_debug_v1_destroy(app.debug_iface); + + while (1) { + struct debug_stream *stream; + bool empty = true; + + wl_list_for_each(stream, &app.stream_list, link) { + if (stream->obj) { + empty = false; + break; + } + } + + if (empty) + break; + + if (wl_display_dispatch(app.dpy) < 0) { + ret = 1; + break; + } + } + +out_conn: + destroy_streams(&app); + + /* Wait for server to close all files */ + wl_display_roundtrip(app.dpy); + + wl_registry_destroy(app.registry); + wl_display_disconnect(app.dpy); + +out_parse: + if (app.out_fd != -1) + close(app.out_fd); + + destroy_streams(&app); + free(app.opt.output); + free(app.opt.outfd); + + return ret; +} diff --git a/clients/weston-info.c b/clients/weston-info.c index 712346a01..977252732 100644 --- a/clients/weston-info.c +++ b/clients/weston-info.c @@ -30,14 +30,20 @@ #include #include #include +#include +#include +#include #include #include "shared/helpers.h" #include "shared/os-compatibility.h" #include "shared/xalloc.h" -#include "shared/zalloc.h" +#include #include "presentation-time-client-protocol.h" +#include "linux-dmabuf-unstable-v1-client-protocol.h" +#include "tablet-unstable-v2-client-protocol.h" +#include "xdg-output-unstable-v1-client-protocol.h" typedef void (*print_info_t)(void *info); typedef void (*destroy_info_t)(void *info); @@ -63,6 +69,7 @@ struct output_mode { struct output_info { struct global_info global; + struct wl_list global_link; struct wl_output *output; @@ -94,11 +101,27 @@ struct shm_info { struct wl_list formats; }; +struct linux_dmabuf_modifier { + struct wl_list link; + + uint32_t format; + uint64_t modifier; +}; + +struct linux_dmabuf_info { + struct global_info global; + struct zwp_linux_dmabuf_v1 *dmabuf; + + struct wl_list modifiers; +}; + struct seat_info { struct global_info global; + struct wl_list global_link; struct wl_seat *seat; struct weston_info *info; + struct wl_keyboard *keyboard; uint32_t capabilities; char *name; @@ -106,6 +129,97 @@ struct seat_info { int32_t repeat_delay; }; +struct tablet_v2_path { + struct wl_list link; + char *path; +}; + +struct tablet_tool_info { + struct wl_list link; + struct zwp_tablet_tool_v2 *tool; + + uint64_t hardware_serial; + uint64_t hardware_id_wacom; + enum zwp_tablet_tool_v2_type type; + + bool has_tilt; + bool has_pressure; + bool has_distance; + bool has_rotation; + bool has_slider; + bool has_wheel; +}; + +struct tablet_pad_group_info { + struct wl_list link; + struct zwp_tablet_pad_group_v2 *group; + + uint32_t modes; + size_t button_count; + int *buttons; + size_t strips; + size_t rings; +}; + +struct tablet_pad_info { + struct wl_list link; + struct zwp_tablet_pad_v2 *pad; + + uint32_t buttons; + struct wl_list paths; + struct wl_list groups; +}; + +struct tablet_info { + struct wl_list link; + struct zwp_tablet_v2 *tablet; + + char *name; + uint32_t vid, pid; + struct wl_list paths; +}; + +struct tablet_seat_info { + struct wl_list link; + + struct zwp_tablet_seat_v2 *seat; + struct seat_info *seat_info; + + struct wl_list tablets; + struct wl_list tools; + struct wl_list pads; +}; + +struct tablet_v2_info { + struct global_info global; + struct zwp_tablet_manager_v2 *manager; + struct weston_info *info; + + struct wl_list seats; +}; + +struct xdg_output_v1_info { + struct wl_list link; + + struct zxdg_output_v1 *xdg_output; + struct output_info *output; + + struct { + int32_t x, y; + int32_t width, height; + } logical; + + char *name, *description; +}; + +struct xdg_output_manager_v1_info { + struct global_info global; + struct zxdg_output_manager_v1 *manager; + struct weston_info *info; + + struct wl_list outputs; +}; + struct presentation_info { struct global_info global; struct wp_presentation *presentation; @@ -119,281 +233,1237 @@ struct weston_info { struct wl_list infos; bool roundtrip_needed; + + /* required for tablet-unstable-v2 */ + struct wl_list seats; + struct tablet_v2_info *tablet_info; + + /* required for xdg-output-unstable-v1 */ + struct wl_list outputs; + struct xdg_output_manager_v1_info *xdg_output_manager_v1_info; }; static void -print_global_info(void *data) +print_global_info(void *data) +{ + struct global_info *global = data; + + printf("interface: '%s', version: %u, name: %u\n", + global->interface, global->version, global->id); +} + +static void +init_global_info(struct weston_info *info, + struct global_info *global, uint32_t id, + const char *interface, uint32_t version) +{ + global->id = id; + global->version = version; + global->interface = xstrdup(interface); + + wl_list_insert(info->infos.prev, &global->link); +} + +static void +print_output_info(void *data) +{ + struct output_info *output = data; + struct output_mode *mode; + const char *subpixel_orientation; + const char *transform; + + print_global_info(data); + + switch (output->geometry.subpixel) { + case WL_OUTPUT_SUBPIXEL_UNKNOWN: + subpixel_orientation = "unknown"; + break; + case WL_OUTPUT_SUBPIXEL_NONE: + subpixel_orientation = "none"; + break; + case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: + subpixel_orientation = "horizontal rgb"; + break; + case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: + subpixel_orientation = "horizontal bgr"; + break; + case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: + subpixel_orientation = "vertical rgb"; + break; + case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: + subpixel_orientation = "vertical bgr"; + break; + default: + fprintf(stderr, "unknown subpixel orientation %u\n", + output->geometry.subpixel); + subpixel_orientation = "unexpected value"; + break; + } + + switch (output->geometry.output_transform) { + case WL_OUTPUT_TRANSFORM_NORMAL: + transform = "normal"; + break; + case WL_OUTPUT_TRANSFORM_90: + transform = "90°"; + break; + case WL_OUTPUT_TRANSFORM_180: + transform = "180°"; + break; + case WL_OUTPUT_TRANSFORM_270: + transform = "270°"; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED: + transform = "flipped"; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + transform = "flipped 90°"; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + transform = "flipped 180°"; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + transform = "flipped 270°"; + break; + default: + fprintf(stderr, "unknown output transform %u\n", + output->geometry.output_transform); + transform = "unexpected value"; + break; + } + + printf("\tx: %d, y: %d,", + output->geometry.x, output->geometry.y); + if (output->version >= 2) + printf(" scale: %d,", output->geometry.scale); + printf("\n"); + + printf("\tphysical_width: %d mm, physical_height: %d mm,\n", + output->geometry.physical_width, + output->geometry.physical_height); + printf("\tmake: '%s', model: '%s',\n", + output->geometry.make, output->geometry.model); + printf("\tsubpixel_orientation: %s, output_transform: %s,\n", + subpixel_orientation, transform); + + wl_list_for_each(mode, &output->modes, link) { + printf("\tmode:\n"); + + printf("\t\twidth: %d px, height: %d px, refresh: %.3f Hz,\n", + mode->width, mode->height, + (float) mode->refresh / 1000); + + printf("\t\tflags:"); + if (mode->flags & WL_OUTPUT_MODE_CURRENT) + printf(" current"); + if (mode->flags & WL_OUTPUT_MODE_PREFERRED) + printf(" preferred"); + printf("\n"); + } +} + +static char +bits2graph(uint32_t value, unsigned bitoffset) +{ + int c = (value >> bitoffset) & 0xff; + + if (isgraph(c) || isspace(c)) + return c; + + return '?'; +} + +static void +fourcc2str(uint32_t format, char *str, int len) +{ + int i; + + assert(len >= 5); + + for (i = 0; i < 4; i++) + str[i] = bits2graph(format, i * 8); + str[i] = '\0'; +} + +static void +print_shm_info(void *data) +{ + char str[5]; + struct shm_info *shm = data; + struct shm_format *format; + + print_global_info(data); + + printf("\tformats:"); + + wl_list_for_each(format, &shm->formats, link) + switch (format->format) { + case WL_SHM_FORMAT_ARGB8888: + printf(" ARGB8888"); + break; + case WL_SHM_FORMAT_XRGB8888: + printf(" XRGB8888"); + break; + case WL_SHM_FORMAT_RGB565: + printf(" RGB565"); + break; + default: + fourcc2str(format->format, str, sizeof(str)); + printf(" '%s'(0x%08x)", str, format->format); + break; + } + + printf("\n"); +} + +static void +print_linux_dmabuf_info(void *data) +{ + char str[5]; + struct linux_dmabuf_info *dmabuf = data; + struct linux_dmabuf_modifier *modifier; + + print_global_info(data); + + printf("\tformats:"); + + wl_list_for_each(modifier, &dmabuf->modifiers, link) { + fourcc2str(modifier->format, str, sizeof(str)); + printf("\n\t'%s'(0x%08x), modifier: 0x%016"PRIx64, str, modifier->format, modifier->modifier); + } + + printf("\n"); +} + +static void +print_seat_info(void *data) +{ + struct seat_info *seat = data; + + print_global_info(data); + + printf("\tname: %s\n", seat->name); + printf("\tcapabilities:"); + + if (seat->capabilities & WL_SEAT_CAPABILITY_POINTER) + printf(" pointer"); + if (seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD) + printf(" keyboard"); + if (seat->capabilities & WL_SEAT_CAPABILITY_TOUCH) + printf(" touch"); + + printf("\n"); + + if (seat->repeat_rate > 0) + printf("\tkeyboard repeat rate: %d\n", seat->repeat_rate); + if (seat->repeat_delay > 0) + printf("\tkeyboard repeat delay: %d\n", seat->repeat_delay); +} + +static void +keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, + uint32_t format, int fd, uint32_t size) +{ + /* Just so we don’t 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) +{ +} + +static void +keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, + uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, uint32_t mods_locked, + uint32_t group) +{ +} + +static void +keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard, + int32_t rate, int32_t delay) +{ + struct seat_info *seat = data; + + seat->repeat_rate = rate; + seat->repeat_delay = delay; +} + +static const struct wl_keyboard_listener keyboard_listener = { + keyboard_handle_keymap, + keyboard_handle_enter, + keyboard_handle_leave, + keyboard_handle_key, + keyboard_handle_modifiers, + keyboard_handle_repeat_info, +}; + +static void +seat_handle_capabilities(void *data, struct wl_seat *wl_seat, + enum wl_seat_capability caps) +{ + struct seat_info *seat = data; + + seat->capabilities = caps; + + /* we want listen for repeat_info from wl_keyboard, but only + * do so if the seat info is >= 4 and if we actually have a + * keyboard */ + if (seat->global.version < 4) + return; + + if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { + seat->keyboard = wl_seat_get_keyboard(seat->seat); + wl_keyboard_add_listener(seat->keyboard, &keyboard_listener, + seat); + + seat->info->roundtrip_needed = true; + } +} + +static void +seat_handle_name(void *data, struct wl_seat *wl_seat, + const char *name) +{ + struct seat_info *seat = data; + seat->name = xstrdup(name); +} + +static const struct wl_seat_listener seat_listener = { + seat_handle_capabilities, + seat_handle_name, +}; + +static void +destroy_seat_info(void *data) +{ + struct seat_info *seat = data; + + wl_seat_destroy(seat->seat); + + if (seat->name != NULL) + free(seat->name); + + if (seat->keyboard) + wl_keyboard_destroy(seat->keyboard); + + wl_list_remove(&seat->global_link); +} + +static const char * +tablet_tool_type_to_str(enum zwp_tablet_tool_v2_type type) +{ + switch (type) { + case ZWP_TABLET_TOOL_V2_TYPE_PEN: + return "pen"; + case ZWP_TABLET_TOOL_V2_TYPE_ERASER: + return "eraser"; + case ZWP_TABLET_TOOL_V2_TYPE_BRUSH: + return "brush"; + case ZWP_TABLET_TOOL_V2_TYPE_PENCIL: + return "pencil"; + case ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH: + return "airbrush"; + case ZWP_TABLET_TOOL_V2_TYPE_FINGER: + return "finger"; + case ZWP_TABLET_TOOL_V2_TYPE_MOUSE: + return "mouse"; + case ZWP_TABLET_TOOL_V2_TYPE_LENS: + return "lens"; + } + + return "Unknown type"; +} + +static void +print_tablet_tool_info(const struct tablet_tool_info *info) +{ + printf("\t\ttablet_tool: %s\n", tablet_tool_type_to_str(info->type)); + if (info->hardware_serial) { + printf("\t\t\thardware serial: %" PRIx64 "\n", info->hardware_serial); + } + if (info->hardware_id_wacom) { + printf("\t\t\thardware wacom: %" PRIx64 "\n", info->hardware_id_wacom); + } + + printf("\t\t\tcapabilities:"); + + if (info->has_tilt) { + printf(" tilt"); + } + if (info->has_pressure) { + printf(" pressure"); + } + if (info->has_distance) { + printf(" distance"); + } + if (info->has_rotation) { + printf(" rotation"); + } + if (info->has_slider) { + printf(" slider"); + } + if (info->has_wheel) { + printf(" wheel"); + } + printf("\n"); +} + +static void +destroy_tablet_tool_info(struct tablet_tool_info *info) +{ + wl_list_remove(&info->link); + zwp_tablet_tool_v2_destroy(info->tool); + free(info); +} + +static void +print_tablet_pad_group_info(const struct tablet_pad_group_info *info) +{ + size_t i; + printf("\t\t\tgroup:\n"); + printf("\t\t\t\tmodes: %u\n", info->modes); + printf("\t\t\t\tstrips: %zu\n", info->strips); + printf("\t\t\t\trings: %zu\n", info->rings); + printf("\t\t\t\tbuttons:"); + + for (i = 0; i < info->button_count; ++i) { + printf(" %d", info->buttons[i]); + } + + printf("\n"); +} + +static void +destroy_tablet_pad_group_info(struct tablet_pad_group_info *info) +{ + wl_list_remove(&info->link); + zwp_tablet_pad_group_v2_destroy(info->group); + + if (info->buttons) { + free(info->buttons); + } + free(info); +} + +static void +print_tablet_pad_info(const struct tablet_pad_info *info) +{ + const struct tablet_v2_path *path; + const struct tablet_pad_group_info *group; + + printf("\t\tpad:\n"); + printf("\t\t\tbuttons: %u\n", info->buttons); + + wl_list_for_each(path, &info->paths, link) { + printf("\t\t\tpath: %s\n", path->path); + } + + wl_list_for_each(group, &info->groups, link) { + print_tablet_pad_group_info(group); + } +} + +static void +destroy_tablet_pad_info(struct tablet_pad_info *info) +{ + struct tablet_v2_path *path; + struct tablet_v2_path *tmp_path; + struct tablet_pad_group_info *group; + struct tablet_pad_group_info *tmp_group; + + wl_list_remove(&info->link); + zwp_tablet_pad_v2_destroy(info->pad); + + wl_list_for_each_safe(path, tmp_path, &info->paths, link) { + wl_list_remove(&path->link); + free(path->path); + free(path); + } + + wl_list_for_each_safe(group, tmp_group, &info->groups, link) { + destroy_tablet_pad_group_info(group); + } + + free(info); +} + +static void +print_tablet_info(const struct tablet_info *info) +{ + const struct tablet_v2_path *path; + + printf("\t\ttablet: %s\n", info->name); + printf("\t\t\tvendor: %u\n", info->vid); + printf("\t\t\tproduct: %u\n", info->pid); + + wl_list_for_each(path, &info->paths, link) { + printf("\t\t\tpath: %s\n", path->path); + } +} + +static void +destroy_tablet_info(struct tablet_info *info) +{ + struct tablet_v2_path *path; + struct tablet_v2_path *tmp; + + wl_list_remove(&info->link); + zwp_tablet_v2_destroy(info->tablet); + + if (info->name) { + free(info->name); + } + + wl_list_for_each_safe(path, tmp, &info->paths, link) { + wl_list_remove(&path->link); + free(path->path); + free(path); + } + + free(info); +} + +static void +print_tablet_seat_info(const struct tablet_seat_info *info) +{ + const struct tablet_info *tablet; + const struct tablet_pad_info *pad; + const struct tablet_tool_info *tool; + + printf("\ttablet_seat: %s\n", info->seat_info->name); + + wl_list_for_each(tablet, &info->tablets, link) { + print_tablet_info(tablet); + } + + wl_list_for_each(pad, &info->pads, link) { + print_tablet_pad_info(pad); + } + + wl_list_for_each(tool, &info->tools, link) { + print_tablet_tool_info(tool); + } +} + +static void +destroy_tablet_seat_info(struct tablet_seat_info *info) +{ + struct tablet_info *tablet; + struct tablet_info *tmp_tablet; + struct tablet_pad_info *pad; + struct tablet_pad_info *tmp_pad; + struct tablet_tool_info *tool; + struct tablet_tool_info *tmp_tool; + + wl_list_remove(&info->link); + zwp_tablet_seat_v2_destroy(info->seat); + + wl_list_for_each_safe(tablet, tmp_tablet, &info->tablets, link) { + destroy_tablet_info(tablet); + } + + wl_list_for_each_safe(pad, tmp_pad, &info->pads, link) { + destroy_tablet_pad_info(pad); + } + + wl_list_for_each_safe(tool, tmp_tool, &info->tools, link) { + destroy_tablet_tool_info(tool); + } + + free(info); +} + +static void +print_tablet_v2_info(void *data) +{ + struct tablet_v2_info *info = data; + struct tablet_seat_info *seat; + print_global_info(data); + + wl_list_for_each(seat, &info->seats, link) { + /* Skip tablet_seats without a tablet, they are irrelevant */ + if (wl_list_empty(&seat->pads) && + wl_list_empty(&seat->tablets) && + wl_list_empty(&seat->tools)) { + continue; + } + + print_tablet_seat_info(seat); + } +} + +static void +destroy_tablet_v2_info(void *data) +{ + struct tablet_v2_info *info = data; + struct tablet_seat_info *seat; + struct tablet_seat_info *tmp; + + zwp_tablet_manager_v2_destroy(info->manager); + + wl_list_for_each_safe(seat, tmp, &info->seats, link) { + destroy_tablet_seat_info(seat); + } +} + +static void +handle_tablet_v2_tablet_tool_done(void *data, struct zwp_tablet_tool_v2 *tool) +{ + /* don't bother waiting for this; there's no good reason a + * compositor will wait more than one roundtrip before sending + * these initial events. */ +} + +static void +handle_tablet_v2_tablet_tool_removed(void *data, struct zwp_tablet_tool_v2 *tool) +{ + /* don't bother waiting for this; we never make any request either way. */ +} + +static void +handle_tablet_v2_tablet_tool_type(void *data, struct zwp_tablet_tool_v2 *tool, + uint32_t tool_type) +{ + struct tablet_tool_info *info = data; + info->type = tool_type; +} + +static void +handle_tablet_v2_tablet_tool_hardware_serial(void *data, + struct zwp_tablet_tool_v2 *tool, + uint32_t serial_hi, + uint32_t serial_lo) +{ + struct tablet_tool_info *info = data; + + info->hardware_serial = ((uint64_t) serial_hi) << 32 | + (uint64_t) serial_lo; +} + +static void +handle_tablet_v2_tablet_tool_hardware_id_wacom(void *data, + struct zwp_tablet_tool_v2 *tool, + uint32_t id_hi, uint32_t id_lo) +{ + struct tablet_tool_info *info = data; + + info->hardware_id_wacom = ((uint64_t) id_hi) << 32 | (uint64_t) id_lo; +} + +static void +handle_tablet_v2_tablet_tool_capability(void *data, + struct zwp_tablet_tool_v2 *tool, + uint32_t capability) +{ + struct tablet_tool_info *info = data; + enum zwp_tablet_tool_v2_capability cap = capability; + + switch(cap) { + case ZWP_TABLET_TOOL_V2_CAPABILITY_TILT: + info->has_tilt = true; + break; + case ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE: + info->has_pressure = true; + break; + case ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE: + info->has_distance = true; + break; + case ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION: + info->has_rotation = true; + break; + case ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER: + info->has_slider = true; + break; + case ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL: + info->has_wheel = true; + break; + } +} + +static void +handle_tablet_v2_tablet_tool_proximity_in(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + uint32_t serial, struct zwp_tablet_v2 *tablet, + struct wl_surface *surface) +{ + +} + +static void +handle_tablet_v2_tablet_tool_proximity_out(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) +{ + +} + +static void +handle_tablet_v2_tablet_tool_down(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + uint32_t serial) +{ + +} + +static void +handle_tablet_v2_tablet_tool_up(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) +{ + +} + + +static void +handle_tablet_v2_tablet_tool_motion(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + wl_fixed_t x, + wl_fixed_t y) +{ + +} + +static void +handle_tablet_v2_tablet_tool_pressure(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + uint32_t pressure) +{ + +} + +static void +handle_tablet_v2_tablet_tool_distance(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + uint32_t distance) +{ + +} + +static void +handle_tablet_v2_tablet_tool_tilt(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + wl_fixed_t tilt_x, + wl_fixed_t tilt_y) +{ + +} + +static void +handle_tablet_v2_tablet_tool_rotation(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + wl_fixed_t degrees) +{ + +} + +static void +handle_tablet_v2_tablet_tool_slider(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + int32_t position) +{ + +} + +static void +handle_tablet_v2_tablet_tool_wheel(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + wl_fixed_t degrees, + int32_t clicks) +{ + +} + +static void +handle_tablet_v2_tablet_tool_button(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + uint32_t serial, + uint32_t button, + uint32_t state) +{ + +} + +static void +handle_tablet_v2_tablet_tool_frame(void *data, + struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, + uint32_t time) +{ + +} + +static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = { + .removed = handle_tablet_v2_tablet_tool_removed, + .done = handle_tablet_v2_tablet_tool_done, + .type = handle_tablet_v2_tablet_tool_type, + .hardware_serial = handle_tablet_v2_tablet_tool_hardware_serial, + .hardware_id_wacom = handle_tablet_v2_tablet_tool_hardware_id_wacom, + .capability = handle_tablet_v2_tablet_tool_capability, + + .proximity_in = handle_tablet_v2_tablet_tool_proximity_in, + .proximity_out = handle_tablet_v2_tablet_tool_proximity_out, + .down = handle_tablet_v2_tablet_tool_down, + .up = handle_tablet_v2_tablet_tool_up, + + .motion = handle_tablet_v2_tablet_tool_motion, + .pressure = handle_tablet_v2_tablet_tool_pressure, + .distance = handle_tablet_v2_tablet_tool_distance, + .tilt = handle_tablet_v2_tablet_tool_tilt, + .rotation = handle_tablet_v2_tablet_tool_rotation, + .slider = handle_tablet_v2_tablet_tool_slider, + .wheel = handle_tablet_v2_tablet_tool_wheel, + .button = handle_tablet_v2_tablet_tool_button, + .frame = handle_tablet_v2_tablet_tool_frame, +}; + +static void add_tablet_v2_tablet_tool_info(void *data, + struct zwp_tablet_seat_v2 *tablet_seat_v2, + struct zwp_tablet_tool_v2 *tool) +{ + struct tablet_seat_info *tablet_seat = data; + struct tablet_tool_info *tool_info = xzalloc(sizeof *tool_info); + + tool_info->tool = tool; + wl_list_insert(&tablet_seat->tools, &tool_info->link); + + zwp_tablet_tool_v2_add_listener(tool, &tablet_tool_listener, tool_info); +} + +static void +handle_tablet_v2_tablet_pad_group_mode_switch(void *data, + struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2, + uint32_t time, uint32_t serial, uint32_t mode) +{ + /* This shouldn't ever happen */ +} + +static void +handle_tablet_v2_tablet_pad_group_done(void *data, + struct zwp_tablet_pad_group_v2 *group) +{ + /* don't bother waiting for this; there's no good reason a + * compositor will wait more than one roundtrip before sending + * these initial events. */ +} + +static void +handle_tablet_v2_tablet_pad_group_modes(void *data, + struct zwp_tablet_pad_group_v2 *group, + uint32_t modes) +{ + struct tablet_pad_group_info *info = data; + info->modes = modes; +} + +static void +handle_tablet_v2_tablet_pad_group_buttons(void *data, + struct zwp_tablet_pad_group_v2 *group, + struct wl_array *buttons) +{ + struct tablet_pad_group_info *info = data; + + info->button_count = buttons->size / sizeof(int); + info->buttons = xzalloc(buttons->size); + memcpy(info->buttons, buttons->data, buttons->size); +} + +static void +handle_tablet_v2_tablet_pad_group_ring(void *data, + struct zwp_tablet_pad_group_v2 *group, + struct zwp_tablet_pad_ring_v2 *ring) +{ + struct tablet_pad_group_info *info = data; + ++info->rings; + + zwp_tablet_pad_ring_v2_destroy(ring); +} + +static void +handle_tablet_v2_tablet_pad_group_strip(void *data, + struct zwp_tablet_pad_group_v2 *group, + struct zwp_tablet_pad_strip_v2 *strip) +{ + struct tablet_pad_group_info *info = data; + ++info->strips; + + zwp_tablet_pad_strip_v2_destroy(strip); +} + +static const struct zwp_tablet_pad_group_v2_listener tablet_pad_group_listener = { + .buttons = handle_tablet_v2_tablet_pad_group_buttons, + .modes = handle_tablet_v2_tablet_pad_group_modes, + .ring = handle_tablet_v2_tablet_pad_group_ring, + .strip = handle_tablet_v2_tablet_pad_group_strip, + .done = handle_tablet_v2_tablet_pad_group_done, + .mode_switch = handle_tablet_v2_tablet_pad_group_mode_switch, +}; + +static void +handle_tablet_v2_tablet_pad_group(void *data, + struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2, + struct zwp_tablet_pad_group_v2 *pad_group) +{ + struct tablet_pad_info *pad_info = data; + struct tablet_pad_group_info *group = xzalloc(sizeof *group); + + wl_list_insert(&pad_info->groups, &group->link); + group->group = pad_group; + zwp_tablet_pad_group_v2_add_listener(pad_group, + &tablet_pad_group_listener, group); +} + +static void +handle_tablet_v2_tablet_pad_path(void *data, struct zwp_tablet_pad_v2 *pad, + const char *path) +{ + struct tablet_pad_info *pad_info = data; + struct tablet_v2_path *path_elem = xzalloc(sizeof *path_elem); + path_elem->path = xstrdup(path); + + wl_list_insert(&pad_info->paths, &path_elem->link); +} + +static void +handle_tablet_v2_tablet_pad_buttons(void *data, struct zwp_tablet_pad_v2 *pad, + uint32_t buttons) +{ + struct tablet_pad_info *pad_info = data; + + pad_info->buttons = buttons; +} + +static void +handle_tablet_v2_tablet_pad_done(void *data, struct zwp_tablet_pad_v2 *pad) +{ + /* don't bother waiting for this; there's no good reason a + * compositor will wait more than one roundtrip before sending + * these initial events. */ +} + +static void +handle_tablet_v2_tablet_pad_removed(void *data, struct zwp_tablet_pad_v2 *pad) +{ + /* don't bother waiting for this; We never make any request that's not + * allowed to be issued either way. */ +} + +static void +handle_tablet_v2_tablet_pad_button(void *data, struct zwp_tablet_pad_v2 *pad, + uint32_t time, uint32_t button, uint32_t state) +{ + /* we don't have a surface, so this can't ever happen */ +} + +static void +handle_tablet_v2_tablet_pad_enter(void *data, struct zwp_tablet_pad_v2 *pad, + uint32_t serial, + struct zwp_tablet_v2 *tablet, + struct wl_surface *surface) +{ + /* we don't have a surface, so this can't ever happen */ +} + +static void +handle_tablet_v2_tablet_pad_leave(void *data, struct zwp_tablet_pad_v2 *pad, + uint32_t serial, struct wl_surface *surface) +{ + /* we don't have a surface, so this can't ever happen */ +} + +static const struct zwp_tablet_pad_v2_listener tablet_pad_listener = { + .group = handle_tablet_v2_tablet_pad_group, + .path = handle_tablet_v2_tablet_pad_path, + .buttons = handle_tablet_v2_tablet_pad_buttons, + .done = handle_tablet_v2_tablet_pad_done, + .removed = handle_tablet_v2_tablet_pad_removed, + .button = handle_tablet_v2_tablet_pad_button, + .enter = handle_tablet_v2_tablet_pad_enter, + .leave = handle_tablet_v2_tablet_pad_leave, +}; + +static void add_tablet_v2_tablet_pad_info(void *data, + struct zwp_tablet_seat_v2 *tablet_seat_v2, + struct zwp_tablet_pad_v2 *pad) +{ + struct tablet_seat_info *tablet_seat = data; + struct tablet_pad_info *pad_info = xzalloc(sizeof *pad_info); + + wl_list_init(&pad_info->paths); + wl_list_init(&pad_info->groups); + pad_info->pad = pad; + wl_list_insert(&tablet_seat->pads, &pad_info->link); + + zwp_tablet_pad_v2_add_listener(pad, &tablet_pad_listener, pad_info); +} + +static void +handle_tablet_v2_tablet_name(void *data, struct zwp_tablet_v2 *zwp_tablet_v2, + const char *name) +{ + struct tablet_info *tablet_info = data; + tablet_info->name = xstrdup(name); +} + +static void +handle_tablet_v2_tablet_path(void *data, struct zwp_tablet_v2 *zwp_tablet_v2, + const char *path) { - struct global_info *global = data; + struct tablet_info *tablet_info = data; + struct tablet_v2_path *path_elem = xzalloc(sizeof *path_elem); + path_elem->path = xstrdup(path); - printf("interface: '%s', version: %u, name: %u\n", - global->interface, global->version, global->id); + wl_list_insert(&tablet_info->paths, &path_elem->link); } static void -init_global_info(struct weston_info *info, - struct global_info *global, uint32_t id, - const char *interface, uint32_t version) +handle_tablet_v2_tablet_id(void *data, struct zwp_tablet_v2 *zwp_tablet_v2, + uint32_t vid, uint32_t pid) { - global->id = id; - global->version = version; - global->interface = xstrdup(interface); + struct tablet_info *tablet_info = data; - wl_list_insert(info->infos.prev, &global->link); + tablet_info->vid = vid; + tablet_info->pid = pid; } static void -print_output_info(void *data) +handle_tablet_v2_tablet_done(void *data, struct zwp_tablet_v2 *zwp_tablet_v2) { - struct output_info *output = data; - struct output_mode *mode; - const char *subpixel_orientation; - const char *transform; + /* don't bother waiting for this; there's no good reason a + * compositor will wait more than one roundtrip before sending + * these initial events. */ +} - print_global_info(data); +static void +handle_tablet_v2_tablet_removed(void *data, struct zwp_tablet_v2 *zwp_tablet_v2) +{ + /* don't bother waiting for this; We never make any request that's not + * allowed to be issued either way. */ +} - switch (output->geometry.subpixel) { - case WL_OUTPUT_SUBPIXEL_UNKNOWN: - subpixel_orientation = "unknown"; - break; - case WL_OUTPUT_SUBPIXEL_NONE: - subpixel_orientation = "none"; - break; - case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: - subpixel_orientation = "horizontal rgb"; - break; - case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: - subpixel_orientation = "horizontal bgr"; - break; - case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: - subpixel_orientation = "vertical rgb"; - break; - case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: - subpixel_orientation = "vertical bgr"; - break; - default: - fprintf(stderr, "unknown subpixel orientation %u\n", - output->geometry.subpixel); - subpixel_orientation = "unexpected value"; - break; - } +static const struct zwp_tablet_v2_listener tablet_listener = { + .name = handle_tablet_v2_tablet_name, + .id = handle_tablet_v2_tablet_id, + .path = handle_tablet_v2_tablet_path, + .done = handle_tablet_v2_tablet_done, + .removed = handle_tablet_v2_tablet_removed +}; - switch (output->geometry.output_transform) { - case WL_OUTPUT_TRANSFORM_NORMAL: - transform = "normal"; - break; - case WL_OUTPUT_TRANSFORM_90: - transform = "90°"; - break; - case WL_OUTPUT_TRANSFORM_180: - transform = "180°"; - break; - case WL_OUTPUT_TRANSFORM_270: - transform = "270°"; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED: - transform = "flipped"; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - transform = "flipped 90°"; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - transform = "flipped 180°"; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - transform = "flipped 270°"; - break; - default: - fprintf(stderr, "unknown output transform %u\n", - output->geometry.output_transform); - transform = "unexpected value"; - break; - } +static void +add_tablet_v2_tablet_info(void *data, struct zwp_tablet_seat_v2 *tablet_seat_v2, + struct zwp_tablet_v2 *tablet) +{ + struct tablet_seat_info *tablet_seat = data; + struct tablet_info *tablet_info = xzalloc(sizeof *tablet_info); - printf("\tx: %d, y: %d,", - output->geometry.x, output->geometry.y); - if (output->version >= 2) - printf(" scale: %d,", output->geometry.scale); - printf("\n"); + wl_list_init(&tablet_info->paths); + tablet_info->tablet = tablet; + wl_list_insert(&tablet_seat->tablets, &tablet_info->link); - printf("\tphysical_width: %d mm, physical_height: %d mm,\n", - output->geometry.physical_width, - output->geometry.physical_height); - printf("\tmake: '%s', model: '%s',\n", - output->geometry.make, output->geometry.model); - printf("\tsubpixel_orientation: %s, output_transform: %s,\n", - subpixel_orientation, transform); + zwp_tablet_v2_add_listener(tablet, &tablet_listener, tablet_info); +} - wl_list_for_each(mode, &output->modes, link) { - printf("\tmode:\n"); +static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = { + .tablet_added = add_tablet_v2_tablet_info, + .pad_added = add_tablet_v2_tablet_pad_info, + .tool_added = add_tablet_v2_tablet_tool_info, +}; - printf("\t\twidth: %d px, height: %d px, refresh: %.3f Hz,\n", - mode->width, mode->height, - (float) mode->refresh / 1000); +static void +add_tablet_seat_info(struct tablet_v2_info *tablet_info, struct seat_info *seat) +{ + struct tablet_seat_info *tablet_seat = xzalloc(sizeof *tablet_seat); - printf("\t\tflags:"); - if (mode->flags & WL_OUTPUT_MODE_CURRENT) - printf(" current"); - if (mode->flags & WL_OUTPUT_MODE_PREFERRED) - printf(" preferred"); - printf("\n"); - } + wl_list_insert(&tablet_info->seats, &tablet_seat->link); + tablet_seat->seat = zwp_tablet_manager_v2_get_tablet_seat( + tablet_info->manager, seat->seat); + zwp_tablet_seat_v2_add_listener(tablet_seat->seat, + &tablet_seat_listener, tablet_seat); + + wl_list_init(&tablet_seat->pads); + wl_list_init(&tablet_seat->tablets); + wl_list_init(&tablet_seat->tools); + tablet_seat->seat_info = seat; + + tablet_info->info->roundtrip_needed = true; } static void -print_shm_info(void *data) +add_tablet_v2_info(struct weston_info *info, uint32_t id, uint32_t version) { - struct shm_info *shm = data; - struct shm_format *format; + struct seat_info *seat; + struct tablet_v2_info *tablet = xzalloc(sizeof *tablet); - print_global_info(data); + wl_list_init(&tablet->seats); + tablet->info = info; - printf("\tformats:"); + init_global_info(info, &tablet->global, id, + zwp_tablet_manager_v2_interface.name, version); + tablet->global.print = print_tablet_v2_info; + tablet->global.destroy = destroy_tablet_v2_info; - wl_list_for_each(format, &shm->formats, link) - switch (format->format) { - case WL_SHM_FORMAT_ARGB8888: - printf(" ARGB8888"); - break; - case WL_SHM_FORMAT_XRGB8888: - printf(" XRGB8888"); - break; - case WL_SHM_FORMAT_RGB565: - printf(" RGB565"); - break; - default: - printf(" unknown(%08x)", format->format); - break; - } + tablet->manager = wl_registry_bind(info->registry, + id, &zwp_tablet_manager_v2_interface, 1); - printf("\n"); + wl_list_for_each(seat, &info->seats, global_link) { + add_tablet_seat_info(tablet, seat); + } + + info->tablet_info = tablet; } static void -print_seat_info(void *data) +destroy_xdg_output_v1_info(struct xdg_output_v1_info *info) { - struct seat_info *seat = data; - - print_global_info(data); + wl_list_remove(&info->link); + zxdg_output_v1_destroy(info->xdg_output); + free(info->name); + free(info->description); + free(info); +} - printf("\tname: %s\n", seat->name); - printf("\tcapabilities:"); +static void +print_xdg_output_v1_info(const struct xdg_output_v1_info *info) +{ + printf("\txdg_output_v1\n"); + printf("\t\toutput: %d\n", info->output->global.id); + if (info->name) + printf("\t\tname: '%s'\n", info->name); + if (info->description) + printf("\t\tdescription: '%s'\n", info->description); + printf("\t\tlogical_x: %d, logical_y: %d\n", + info->logical.x, info->logical.y); + printf("\t\tlogical_width: %d, logical_height: %d\n", + info->logical.width, info->logical.height); +} - if (seat->capabilities & WL_SEAT_CAPABILITY_POINTER) - printf(" pointer"); - if (seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD) - printf(" keyboard"); - if (seat->capabilities & WL_SEAT_CAPABILITY_TOUCH) - printf(" touch"); +static void +print_xdg_output_manager_v1_info(void *data) +{ + struct xdg_output_manager_v1_info *info = data; + struct xdg_output_v1_info *output; - printf("\n"); + print_global_info(data); - if (seat->repeat_rate > 0) - printf("\tkeyboard repeat rate: %d\n", seat->repeat_rate); - if (seat->repeat_delay > 0) - printf("\tkeyboard repeat delay: %d\n", seat->repeat_delay); + wl_list_for_each(output, &info->outputs, link) + print_xdg_output_v1_info(output); } static void -keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, - uint32_t format, int fd, uint32_t size) +destroy_xdg_output_manager_v1_info(void *data) { + struct xdg_output_manager_v1_info *info = data; + struct xdg_output_v1_info *output, *tmp; + + zxdg_output_manager_v1_destroy(info->manager); + + wl_list_for_each_safe(output, tmp, &info->outputs, link) + destroy_xdg_output_v1_info(output); } static void -keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, - uint32_t serial, struct wl_surface *surface, - struct wl_array *keys) +handle_xdg_output_v1_logical_position(void *data, struct zxdg_output_v1 *output, + int32_t x, int32_t y) { + struct xdg_output_v1_info *xdg_output = data; + xdg_output->logical.x = x; + xdg_output->logical.y = y; } static void -keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, - uint32_t serial, struct wl_surface *surface) +handle_xdg_output_v1_logical_size(void *data, struct zxdg_output_v1 *output, + int32_t width, int32_t height) { + struct xdg_output_v1_info *xdg_output = data; + xdg_output->logical.width = width; + xdg_output->logical.height = height; } static void -keyboard_handle_key(void *data, struct wl_keyboard *keyboard, - uint32_t serial, uint32_t time, uint32_t key, - uint32_t state) +handle_xdg_output_v1_done(void *data, struct zxdg_output_v1 *output) { + /* Don't bother waiting for this; there's no good reason a + * compositor will wait more than one roundtrip before sending + * these initial events. */ } static void -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) +handle_xdg_output_v1_name(void *data, struct zxdg_output_v1 *output, + const char *name) { + struct xdg_output_v1_info *xdg_output = data; + xdg_output->name = strdup(name); } static void -keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard, - int32_t rate, int32_t delay) +handle_xdg_output_v1_description(void *data, struct zxdg_output_v1 *output, + const char *description) { - struct seat_info *seat = data; - - seat->repeat_rate = rate; - seat->repeat_delay = delay; + struct xdg_output_v1_info *xdg_output = data; + xdg_output->description = strdup(description); } -static const struct wl_keyboard_listener keyboard_listener = { - keyboard_handle_keymap, - keyboard_handle_enter, - keyboard_handle_leave, - keyboard_handle_key, - keyboard_handle_modifiers, - keyboard_handle_repeat_info, +static const struct zxdg_output_v1_listener xdg_output_v1_listener = { + .logical_position = handle_xdg_output_v1_logical_position, + .logical_size = handle_xdg_output_v1_logical_size, + .done = handle_xdg_output_v1_done, + .name = handle_xdg_output_v1_name, + .description = handle_xdg_output_v1_description, }; static void -seat_handle_capabilities(void *data, struct wl_seat *wl_seat, - enum wl_seat_capability caps) +add_xdg_output_v1_info(struct xdg_output_manager_v1_info *manager_info, + struct output_info *output) { - struct seat_info *seat = data; - - seat->capabilities = caps; - - /* we want listen for repeat_info from wl_keyboard, but only - * do so if the seat info is >= 4 and if we actually have a - * keyboard */ - if (seat->global.version < 4) - return; + struct xdg_output_v1_info *xdg_output = xzalloc(sizeof *xdg_output); - if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { - struct wl_keyboard *keyboard; + wl_list_insert(&manager_info->outputs, &xdg_output->link); + xdg_output->xdg_output = zxdg_output_manager_v1_get_xdg_output( + manager_info->manager, output->output); + zxdg_output_v1_add_listener(xdg_output->xdg_output, + &xdg_output_v1_listener, xdg_output); - keyboard = wl_seat_get_keyboard(seat->seat); - wl_keyboard_add_listener(keyboard, &keyboard_listener, - seat); + xdg_output->output = output; - seat->info->roundtrip_needed = true; - } + manager_info->info->roundtrip_needed = true; } static void -seat_handle_name(void *data, struct wl_seat *wl_seat, - const char *name) +add_xdg_output_manager_v1_info(struct weston_info *info, uint32_t id, + uint32_t version) { - struct seat_info *seat = data; - seat->name = xstrdup(name); -} + struct output_info *output; + struct xdg_output_manager_v1_info *manager = xzalloc(sizeof *manager); -static const struct wl_seat_listener seat_listener = { - seat_handle_capabilities, - seat_handle_name, -}; + wl_list_init(&manager->outputs); + manager->info = info; -static void -destroy_seat_info(void *data) -{ - struct seat_info *seat = data; + init_global_info(info, &manager->global, id, + zxdg_output_manager_v1_interface.name, version); + manager->global.print = print_xdg_output_manager_v1_info; + manager->global.destroy = destroy_xdg_output_manager_v1_info; - wl_seat_destroy(seat->seat); + manager->manager = wl_registry_bind(info->registry, id, + &zxdg_output_manager_v1_interface, version > 2 ? 2 : version); - if (seat->name != NULL) - free(seat->name); + wl_list_for_each(output, &info->outputs, global_link) + add_xdg_output_v1_info(manager, output); + + info->xdg_output_manager_v1_info = manager; } static void @@ -416,6 +1486,11 @@ add_seat_info(struct weston_info *info, uint32_t id, uint32_t version) seat->repeat_rate = seat->repeat_delay = -1; info->roundtrip_needed = true; + wl_list_insert(&info->seats, &seat->global_link); + + if (info->tablet_info) { + add_tablet_seat_info(info->tablet_info, seat); + } } static void @@ -464,6 +1539,62 @@ add_shm_info(struct weston_info *info, uint32_t id, uint32_t version) info->roundtrip_needed = true; } +static void +linux_dmabuf_handle_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format) +{ + /* This is a deprecated event, don’t use it. */ +} + +static void +linux_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) +{ + struct linux_dmabuf_info *dmabuf = data; + struct linux_dmabuf_modifier *linux_dmabuf_modifier = xzalloc(sizeof *linux_dmabuf_modifier); + + wl_list_insert(&dmabuf->modifiers, &linux_dmabuf_modifier->link); + linux_dmabuf_modifier->format = format; + linux_dmabuf_modifier->modifier = ((uint64_t)modifier_hi) << 32 | modifier_lo; +} + +static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_listener = { + linux_dmabuf_handle_format, + linux_dmabuf_handle_modifier, +}; + +static void +destroy_linux_dmabuf_info(void *data) +{ + struct linux_dmabuf_info *dmabuf = data; + struct linux_dmabuf_modifier *modifier, *tmp; + + wl_list_for_each_safe(modifier, tmp, &dmabuf->modifiers, link) { + wl_list_remove(&modifier->link); + free(modifier); + } + + zwp_linux_dmabuf_v1_destroy(dmabuf->dmabuf); +} + +static void +add_linux_dmabuf_info(struct weston_info *info, uint32_t id, uint32_t version) +{ + struct linux_dmabuf_info *dmabuf = xzalloc(sizeof *dmabuf); + + init_global_info(info, &dmabuf->global, id, "zwp_linux_dmabuf_v1", version); + dmabuf->global.print = print_linux_dmabuf_info; + dmabuf->global.destroy = destroy_linux_dmabuf_info; + + wl_list_init(&dmabuf->modifiers); + + if (version >= 3) { + dmabuf->dmabuf = wl_registry_bind(info->registry, + id, &zwp_linux_dmabuf_v1_interface, 3); + zwp_linux_dmabuf_v1_add_listener(dmabuf->dmabuf, &linux_dmabuf_listener, dmabuf); + + info->roundtrip_needed = true; + } +} + static void output_handle_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, @@ -562,6 +1693,11 @@ add_output_info(struct weston_info *info, uint32_t id, uint32_t version) output); info->roundtrip_needed = true; + wl_list_insert(&info->outputs, &output->global_link); + + if (info->xdg_output_manager_v1_info) + add_xdg_output_v1_info(info->xdg_output_manager_v1_info, + output); } static void @@ -661,10 +1797,16 @@ global_handler(void *data, struct wl_registry *registry, uint32_t id, add_seat_info(info, id, version); else if (!strcmp(interface, "wl_shm")) add_shm_info(info, id, version); + else if (!strcmp(interface, "zwp_linux_dmabuf_v1")) + add_linux_dmabuf_info(info, id, version); else if (!strcmp(interface, "wl_output")) add_output_info(info, id, version); else if (!strcmp(interface, wp_presentation_interface.name)) add_presentation_info(info, id, version); + else if (!strcmp(interface, zwp_tablet_manager_v2_interface.name)) + add_tablet_v2_info(info, id, version); + else if (!strcmp(interface, zxdg_output_manager_v1_interface.name)) + add_xdg_output_manager_v1_info(info, id, version); else add_global_info(info, id, interface, version); } @@ -714,11 +1856,21 @@ main(int argc, char **argv) info.display = wl_display_connect(NULL); if (!info.display) { - fprintf(stderr, "failed to create display: %m\n"); + fprintf(stderr, "failed to create display: %s\n", + strerror(errno)); return -1; } + fprintf(stderr, "\n"); + fprintf(stderr, "*** Please use wayland-info instead\n"); + fprintf(stderr, "*** weston-info is deprecated and will be removed in a future version\n"); + fprintf(stderr, "\n"); + + info.tablet_info = NULL; + info.xdg_output_manager_v1_info = NULL; wl_list_init(&info.infos); + wl_list_init(&info.seats); + wl_list_init(&info.outputs); info.registry = wl_display_get_registry(info.display); wl_registry_add_listener(info.registry, ®istry_listener, &info); diff --git a/clients/window.c b/clients/window.c index 95796d465..ca7e62d07 100644 --- a/clients/window.c +++ b/clients/window.c @@ -73,22 +73,22 @@ typedef void *EGLContext; #include "shared/cairo-util.h" #include "shared/helpers.h" #include "shared/xalloc.h" -#include "shared/zalloc.h" -#include "xdg-shell-unstable-v6-client-protocol.h" +#include +#include "xdg-shell-client-protocol.h" #include "text-cursor-position-client-protocol.h" #include "pointer-constraints-unstable-v1-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h" #include "shared/os-compatibility.h" +#include "shared/string-helpers.h" #include "window.h" - -#include -#include "ivi-application-client-protocol.h" -#define IVI_SURFACE_ID 9000 +#include "viewporter-client-protocol.h" #define ZWP_RELATIVE_POINTER_MANAGER_V1_VERSION 1 #define ZWP_POINTER_CONSTRAINTS_V1_VERSION 1 +#define DEFAULT_XCURSOR_SIZE 32 + struct shm_pool; struct global { @@ -106,8 +106,7 @@ struct display { struct wl_shm *shm; struct wl_data_device_manager *data_device_manager; struct text_cursor_position *text_cursor_position; - struct zxdg_shell_v6 *xdg_shell; - struct ivi_application *ivi_application; /* ivi style shell */ + struct xdg_wm_base *xdg_shell; struct zwp_relative_pointer_manager_v1 *relative_pointer_manager; struct zwp_pointer_constraints_v1 *pointer_constraints; EGLDisplay dpy; @@ -147,8 +146,8 @@ struct display { cairo_surface_t *dummy_surface; void *dummy_surface_data; - int has_rgb565; int data_device_manager_version; + struct wp_viewporter *viewporter; }; struct window_output { @@ -224,6 +223,7 @@ struct surface { cairo_surface_t *cairo_surface; struct wl_list link; + struct wp_viewport *viewport; }; struct window { @@ -248,8 +248,6 @@ struct window { int fullscreen; int maximized; - enum preferred_format preferred_format; - window_key_handler_t key_handler; window_keyboard_focus_handler_t keyboard_focus_handler; window_data_handler_t data_handler; @@ -262,15 +260,13 @@ struct window { window_locked_pointer_motion_handler_t locked_pointer_motion_handler; struct surface *main_surface; - struct zxdg_surface_v6 *xdg_surface; - struct zxdg_toplevel_v6 *xdg_toplevel; - struct zxdg_popup_v6 *xdg_popup; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + struct xdg_popup *xdg_popup; struct window *parent; struct window *last_parent; - struct ivi_surface *ivi_surface; - struct window_frame *frame; /* struct surface::link, contains also main_surface */ @@ -278,7 +274,6 @@ struct window { struct zwp_relative_pointer_v1 *relative_pointer; struct zwp_locked_pointer_v1 *locked_pointer; - struct input *locked_input; bool pointer_locked; locked_pointer_locked_handler_t pointer_locked_handler; locked_pointer_unlocked_handler_t pointer_unlocked_handler; @@ -325,6 +320,8 @@ struct widget { * redraw handler is going to do completely custom rendering * such as using EGL directly */ int use_cairo; + int viewport_dest_width; + int viewport_dest_height; }; struct touch_point { @@ -344,14 +341,15 @@ struct input { struct window *pointer_focus; struct window *keyboard_focus; struct window *touch_focus; + struct window *locked_window; + struct window *confined_window; int current_cursor; uint32_t cursor_anim_start; struct wl_callback *cursor_frame_cb; uint32_t cursor_timer_start; uint32_t cursor_anim_current; - int cursor_delay_fd; + struct toytimer cursor_timer; bool cursor_timer_running; - struct task cursor_task; struct wl_surface *pointer_surface; uint32_t modifiers; uint32_t pointer_enter_serial; @@ -389,8 +387,7 @@ struct input { int32_t repeat_delay_sec; int32_t repeat_delay_nsec; - struct task repeat_task; - int repeat_timer_fd; + struct toytimer repeat_timer; uint32_t repeat_sym; uint32_t repeat_key; uint32_t repeat_time; @@ -440,8 +437,7 @@ struct tooltip { struct widget *parent; struct widget *widget; char *entry; - struct task tooltip_task; - int tooltip_fd; + struct toytimer timer; float x, y; }; @@ -746,14 +742,14 @@ make_shm_pool(struct display *display, int size, void **data) fd = os_create_anonymous_file(size); if (fd < 0) { - fprintf(stderr, "creating a buffer file for %d B failed: %m\n", - size); + fprintf(stderr, "creating a buffer file for %d B failed: %s\n", + size, strerror(errno)); return NULL; } *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (*data == MAP_FAILED) { - fprintf(stderr, "mmap failed: %m\n"); + fprintf(stderr, "mmap failed: %s\n", strerror(errno)); close(fd); return NULL; } @@ -831,7 +827,6 @@ display_create_shm_surface_from_pool(struct display *display, struct shm_surface_data *data; uint32_t format; cairo_surface_t *surface; - cairo_format_t cairo_format; int stride, length, offset; void *map; @@ -839,12 +834,8 @@ display_create_shm_surface_from_pool(struct display *display, if (data == NULL) return NULL; - if (flags & SURFACE_HINT_RGB565 && display->has_rgb565) - cairo_format = CAIRO_FORMAT_RGB16_565; - else - cairo_format = CAIRO_FORMAT_ARGB32; - - stride = cairo_format_stride_for_width (cairo_format, rectangle->width); + stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, + rectangle->width); length = stride * rectangle->height; data->pool = NULL; map = shm_pool_allocate(pool, length, &offset); @@ -855,7 +846,7 @@ display_create_shm_surface_from_pool(struct display *display, } surface = cairo_image_surface_create_for_data (map, - cairo_format, + CAIRO_FORMAT_ARGB32, rectangle->width, rectangle->height, stride); @@ -863,14 +854,10 @@ display_create_shm_surface_from_pool(struct display *display, cairo_surface_set_user_data(surface, &shm_surface_data_key, data, shm_surface_data_destroy); - if (flags & SURFACE_HINT_RGB565 && display->has_rgb565) - format = WL_SHM_FORMAT_RGB565; - else { - if (flags & SURFACE_OPAQUE) - format = WL_SHM_FORMAT_XRGB8888; - else - format = WL_SHM_FORMAT_ARGB8888; - } + if (flags & SURFACE_OPAQUE) + format = WL_SHM_FORMAT_XRGB8888; + else + format = WL_SHM_FORMAT_ARGB8888; data->buffer = wl_shm_pool_create_buffer(pool->pool, offset, rectangle->width, @@ -903,8 +890,7 @@ display_create_shm_surface(struct display *display, } } - pool = shm_pool_create(display, - data_length_for_shm_surface(rectangle)); + pool = shm_pool_create(display, data_length_for_shm_surface(rectangle)); if (!pool) return NULL; @@ -1342,16 +1328,25 @@ create_cursors(struct display *display) const char *config_file; struct weston_config *config; struct weston_config_section *s; - int size; - char *theme = NULL; + int size = DEFAULT_XCURSOR_SIZE; + char *theme = NULL, *size_str; unsigned int i, j; struct wl_cursor *cursor; + theme = getenv("XCURSOR_THEME"); + + size_str = getenv("XCURSOR_SIZE"); + if (size_str) { + safe_strtoint(size_str, &size); + if (size <= 0) + size = DEFAULT_XCURSOR_SIZE; + } + config_file = weston_config_get_name_from_env(); config = weston_config_parse(config_file); s = weston_config_get_section(config, "shell", NULL, NULL); - weston_config_section_get_string(s, "cursor-theme", &theme, NULL); - weston_config_section_get_int(s, "cursor-size", &size, 32); + weston_config_section_get_string(s, "cursor-theme", &theme, theme); + weston_config_section_get_int(s, "cursor-size", &size, size); weston_config_destroy(config); display->cursor_theme = wl_cursor_theme_load(theme, size, display->shm); @@ -1395,6 +1390,7 @@ display_get_pointer_image(struct display *display, int pointer) static void surface_flush(struct surface *surface) { + struct widget *widget = surface->widget; if (!surface->cairo_surface) return; @@ -1412,6 +1408,12 @@ surface_flush(struct surface *surface) surface->input_region = NULL; } + if (surface->viewport) { + wp_viewport_set_destination(surface->viewport, + widget->viewport_dest_width, + widget->viewport_dest_height); + } + surface->toysurface->swap(surface->toysurface, surface->buffer_transform, surface->buffer_scale, &surface->server_allocation); @@ -1441,19 +1443,6 @@ window_get_display(struct window *window) return window->display; } -static void -handle_ivi_surface_configure(void *data, struct ivi_surface *ivi_surface, - int32_t width, int32_t height) -{ - struct window *window = data; - - window_schedule_resize(window, width, height); -} - -static const struct ivi_surface_listener ivi_surface_listener = { - handle_ivi_surface_configure, -}; - static void surface_create_surface(struct surface *surface, uint32_t flags) { @@ -1489,9 +1478,6 @@ window_create_main_surface(struct window *window) if (window->resizing) flags |= SURFACE_HINT_RESIZE; - if (window->preferred_format == WINDOW_PREFERRED_FORMAT_RGB565) - flags |= SURFACE_HINT_RGB565; - surface_create_surface(surface, flags); } @@ -1578,12 +1564,26 @@ window_destroy(struct window *window) wl_list_remove(&window->redraw_task.link); wl_list_for_each(input, &display->input_list, link) { - if (input->touch_focus == window) + if (input->touch_focus == window) { + struct touch_point *tp, *tmp; + + wl_list_for_each_safe(tp, tmp, + &input->touch_point_list, + link) { + wl_list_remove(&tp->link); + free(tp); + } + input->touch_focus = NULL; + } if (input->pointer_focus == window) input->pointer_focus = NULL; if (input->keyboard_focus == window) input->keyboard_focus = NULL; + if (input->locked_window == window) + input->locked_window = NULL; + if (input->confined_window == window) + input->confined_window = NULL; if (input->focus_widget && input->focus_widget->window == window) input->focus_widget = NULL; @@ -1598,14 +1598,11 @@ window_destroy(struct window *window) window_frame_destroy(window->frame); if (window->xdg_toplevel) - zxdg_toplevel_v6_destroy(window->xdg_toplevel); + xdg_toplevel_destroy(window->xdg_toplevel); if (window->xdg_popup) - zxdg_popup_v6_destroy(window->xdg_popup); + xdg_popup_destroy(window->xdg_popup); if (window->xdg_surface) - zxdg_surface_v6_destroy(window->xdg_surface); - - if (window->ivi_surface) - ivi_surface_destroy(window->ivi_surface); + xdg_surface_destroy(window->xdg_surface); surface_destroy(window->main_surface); @@ -1619,6 +1616,8 @@ static struct widget * widget_find_widget(struct widget *widget, int32_t x, int32_t y) { struct widget *child, *target; + int alloc_x, alloc_y, width, height; + double scale; wl_list_for_each(child, &widget->child_list, link) { target = widget_find_widget(child, x, y); @@ -1626,10 +1625,24 @@ widget_find_widget(struct widget *widget, int32_t x, int32_t y) return target; } - if (widget->allocation.x <= x && - x < widget->allocation.x + widget->allocation.width && - widget->allocation.y <= y && - y < widget->allocation.y + widget->allocation.height) { + alloc_x = widget->allocation.x; + alloc_y = widget->allocation.y; + width = widget->allocation.width; + height = widget->allocation.height; + + if (widget->viewport_dest_width != -1 && + widget->viewport_dest_height != -1) { + scale = widget->viewport_dest_width / (double) width; + alloc_x = alloc_x * scale; + width = widget->viewport_dest_width; + + scale = widget->viewport_dest_height / (double) height; + alloc_y = alloc_y * scale; + height = widget->viewport_dest_height; + } + + if (alloc_x <= x && x < alloc_x + width && + alloc_y <= y && y < alloc_y + height) { return widget; } @@ -1667,6 +1680,8 @@ widget_create(struct window *window, struct surface *surface, void *data) widget->tooltip_count = 0; widget->default_cursor = CURSOR_LEFT_PTR; widget->use_cairo = 1; + widget->viewport_dest_width = -1; + widget->viewport_dest_height = -1; return widget; } @@ -1817,14 +1832,14 @@ widget_cairo_update_transform(struct widget *widget, cairo_t *cr) translate_y = 0; break; case WL_OUTPUT_TRANSFORM_90: - angle = M_PI_2; - translate_x = surface_height; - translate_y = 0; + angle = M_PI + M_PI_2; + translate_x = 0; + translate_y = surface_width; break; case WL_OUTPUT_TRANSFORM_FLIPPED_90: - angle = M_PI_2; - translate_x = surface_height; - translate_y = surface_width; + angle = M_PI + M_PI_2; + translate_x = 0; + translate_y = 0; break; case WL_OUTPUT_TRANSFORM_180: angle = M_PI; @@ -1837,14 +1852,14 @@ widget_cairo_update_transform(struct widget *widget, cairo_t *cr) translate_y = surface_height; break; case WL_OUTPUT_TRANSFORM_270: - angle = M_PI + M_PI_2; - translate_x = 0; - translate_y = surface_width; + angle = M_PI_2; + translate_x = surface_height; + translate_y = 0; break; case WL_OUTPUT_TRANSFORM_FLIPPED_270: - angle = M_PI + M_PI_2; - translate_x = 0; - translate_y = 0; + angle = M_PI_2; + translate_x = surface_height; + translate_y = surface_width; break; } @@ -2024,6 +2039,39 @@ widget_set_use_cairo(struct widget *widget, widget->use_cairo = use_cairo; } +int +widget_set_viewport_destination(struct widget *widget, int width, int height) +{ + struct window *window = widget->window; + struct display *display = window->display; + struct surface *surface = widget->surface; + if (!display->viewporter) + return -1; + + if (width == -1 && height == -1) { + if (surface->viewport) { + wp_viewport_destroy(surface->viewport); + surface->viewport = NULL; + } + + widget->viewport_dest_width = -1; + widget->viewport_dest_height = -1; + return 0; + } + + if (!surface->viewport) { + surface->viewport = wp_viewporter_get_viewport(display->viewporter, + surface->surface); + if (!surface->viewport) + return -1; + } + + widget->viewport_dest_width = width; + widget->viewport_dest_height = height; + + return 0; +} + cairo_surface_t * window_get_surface(struct window *window) { @@ -2062,8 +2110,9 @@ tooltip_redraw_handler(struct widget *widget, void *data) cairo_set_source_rgba(cr, 0.0, 0.0, 0.4, 0.8); cairo_fill(cr); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_move_to(cr, 10, 16); + cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.85); + cairo_move_to(cr, 10, 17); + cairo_set_font_size(cr, 14); cairo_show_text(cr, tooltip->entry); cairo_destroy(cr); } @@ -2079,6 +2128,7 @@ get_text_extents(struct display *display, struct tooltip *tooltip) * outside repaint, either. */ cr = cairo_create(display->dummy_surface); + cairo_set_font_size(cr, 14); cairo_text_extents(cr, tooltip->entry, &extents); cairo_destroy(cr); @@ -2122,21 +2172,17 @@ widget_destroy_tooltip(struct widget *parent) tooltip->widget = NULL; } - close(tooltip->tooltip_fd); + toytimer_fini(&tooltip->timer); free(tooltip->entry); free(tooltip); parent->tooltip = NULL; } static void -tooltip_func(struct task *task, uint32_t events) +tooltip_func(struct toytimer *tt) { - struct tooltip *tooltip = - container_of(task, struct tooltip, tooltip_task); - uint64_t exp; + struct tooltip *tooltip = container_of(tt, struct tooltip, timer); - if (read(tooltip->tooltip_fd, &exp, sizeof (uint64_t)) != sizeof (uint64_t)) - abort(); window_create_tooltip(tooltip); } @@ -2144,16 +2190,7 @@ tooltip_func(struct task *task, uint32_t events) static int tooltip_timer_reset(struct tooltip *tooltip) { - struct itimerspec its; - - its.it_interval.tv_sec = 0; - its.it_interval.tv_nsec = 0; - its.it_value.tv_sec = TOOLTIP_TIMEOUT / 1000; - its.it_value.tv_nsec = (TOOLTIP_TIMEOUT % 1000) * 1000 * 1000; - if (timerfd_settime(tooltip->tooltip_fd, 0, &its, NULL) < 0) { - fprintf(stderr, "could not set timerfd\n: %m"); - return -1; - } + toytimer_arm_once_usec(&tooltip->timer, TOOLTIP_TIMEOUT * 1000); return 0; } @@ -2186,15 +2223,8 @@ widget_set_tooltip(struct widget *parent, char *entry, float x, float y) tooltip->x = x; tooltip->y = y; tooltip->entry = strdup(entry); - tooltip->tooltip_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); - if (tooltip->tooltip_fd < 0) { - fprintf(stderr, "could not create timerfd\n: %m"); - return -1; - } - - tooltip->tooltip_task.run = tooltip_func; - display_watch_fd(parent->window->display, tooltip->tooltip_fd, - EPOLLIN, &tooltip->tooltip_task); + toytimer_init(&tooltip->timer, CLOCK_MONOTONIC, + parent->window->display, tooltip_func); tooltip_timer_reset(tooltip); return 0; @@ -2435,9 +2465,9 @@ frame_handle_status(struct window_frame *frame, struct input *input, if ((status & FRAME_STATUS_MOVE) && window->xdg_toplevel) { input_ungrab(input); - zxdg_toplevel_v6_move(window->xdg_toplevel, - input_get_seat(input), - window->display->serial); + xdg_toplevel_move(window->xdg_toplevel, + input_get_seat(input), + window->display->serial); frame_status_clear(frame->frame, FRAME_STATUS_MOVE); } @@ -2445,10 +2475,10 @@ frame_handle_status(struct window_frame *frame, struct input *input, if ((status & FRAME_STATUS_RESIZE) && window->xdg_toplevel) { input_ungrab(input); - zxdg_toplevel_v6_resize(window->xdg_toplevel, - input_get_seat(input), - window->display->serial, - location); + xdg_toplevel_resize(window->xdg_toplevel, + input_get_seat(input), + window->display->serial, + location); frame_status_clear(frame->frame, FRAME_STATUS_RESIZE); } @@ -2546,7 +2576,11 @@ window_frame_create(struct window *window, void *data) frame = xzalloc(sizeof *frame); frame->frame = frame_create(window->display->theme, 0, 0, - buttons, window->title); + buttons, window->title, NULL); + if (!frame->frame) { + free(frame); + return NULL; + } frame->widget = window_add_widget(window, frame); frame->child = widget_add_widget(frame->widget, data); @@ -2685,19 +2719,12 @@ input_ungrab(struct input *input) static void cursor_delay_timer_reset(struct input *input, uint32_t duration) { - struct itimerspec its; - if (!duration) input->cursor_timer_running = false; else input->cursor_timer_running = true; - its.it_interval.tv_sec = 0; - its.it_interval.tv_nsec = 0; - its.it_value.tv_sec = duration / 1000; - its.it_value.tv_nsec = (duration % 1000) * 1000 * 1000; - if (timerfd_settime(input->cursor_delay_fd, 0, &its, NULL) < 0) - fprintf(stderr, "could not set cursor timerfd\n: %m"); + toytimer_arm_once_usec(&input->cursor_timer, duration * 1000); } static void cancel_pointer_image_update(struct input *input) @@ -2939,13 +2966,8 @@ static void input_remove_keyboard_focus(struct input *input) { struct window *window = input->keyboard_focus; - struct itimerspec its; - its.it_interval.tv_sec = 0; - its.it_interval.tv_nsec = 0; - its.it_value.tv_sec = 0; - its.it_value.tv_nsec = 0; - timerfd_settime(input->repeat_timer_fd, 0, &its, NULL); + toytimer_disarm(&input->repeat_timer); if (!window) return; @@ -2958,18 +2980,10 @@ input_remove_keyboard_focus(struct input *input) } static void -keyboard_repeat_func(struct task *task, uint32_t events) +keyboard_repeat_func(struct toytimer *tt) { - struct input *input = - container_of(task, struct input, repeat_task); + struct input *input = container_of(tt, struct input, repeat_timer); struct window *window = input->keyboard_focus; - uint64_t exp; - - if (read(input->repeat_timer_fd, &exp, sizeof exp) != sizeof exp) - /* If we change the timer between the fd becoming - * readable and getting here, there'll be nothing to - * read and we get EAGAIN. */ - return; if (window && window->key_handler) { (*window->key_handler)(window, input, input->repeat_time, @@ -3003,7 +3017,7 @@ keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, return; } - map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); if (map_str == MAP_FAILED) { close(fd); return; @@ -3192,11 +3206,7 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard, if (state == WL_KEYBOARD_KEY_STATE_RELEASED && key == input->repeat_key) { - its.it_interval.tv_sec = 0; - its.it_interval.tv_nsec = 0; - its.it_value.tv_sec = 0; - its.it_value.tv_nsec = 0; - timerfd_settime(input->repeat_timer_fd, 0, &its, NULL); + toytimer_disarm(&input->repeat_timer); } else if (state == WL_KEYBOARD_KEY_STATE_PRESSED && xkb_keymap_key_repeats(input->xkb.keymap, code)) { input->repeat_sym = sym; @@ -3206,7 +3216,7 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard, its.it_interval.tv_nsec = input->repeat_rate_nsec; its.it_value.tv_sec = input->repeat_delay_sec; its.it_value.tv_nsec = input->repeat_delay_nsec; - timerfd_settime(input->repeat_timer_fd, 0, &its, NULL); + toytimer_arm(&input->repeat_timer, &its); } } @@ -3287,6 +3297,11 @@ touch_handle_down(void *data, struct wl_touch *wl_touch, float sx = wl_fixed_to_double(x_w); float sy = wl_fixed_to_double(y_w); + if (!surface) { + /* down event for a window we've just destroyed */ + return; + } + input->display->serial = serial; input->touch_focus = wl_surface_get_user_data(surface); if (!input->touch_focus) { @@ -3425,12 +3440,26 @@ touch_handle_cancel(void *data, struct wl_touch *wl_touch) } } +static void +touch_handle_shape(void *data, struct wl_touch *wl_touch, int32_t id, + wl_fixed_t major, wl_fixed_t minor) +{ +} + +static void +touch_handle_orientation(void *data, struct wl_touch *wl_touch, int32_t id, + wl_fixed_t orientation) +{ +} + static const struct wl_touch_listener touch_listener = { touch_handle_down, touch_handle_up, touch_handle_motion, touch_handle_frame, touch_handle_cancel, + touch_handle_shape, + touch_handle_orientation, }; static void @@ -3630,6 +3659,11 @@ data_device_enter(void *data, struct wl_data_device *data_device, float y = wl_fixed_to_double(y_w); char **p; + if (!surface) { + /* enter event for a window we've just destroyed */ + return; + } + window = wl_surface_get_user_data(surface); input->drag_enter_serial = serial; input->drag_focus = window, @@ -3888,20 +3922,16 @@ pointer_surface_frame_callback(void *data, struct wl_callback *callback, } static void -cursor_timer_func(struct task *task, uint32_t events) +cursor_timer_func(struct toytimer *tt) { - struct input *input = container_of(task, struct input, cursor_task); + struct input *input = container_of(tt, struct input, cursor_timer); struct timespec tp; struct wl_cursor *cursor; uint32_t time; - uint64_t exp; if (!input->cursor_timer_running) return; - if (read(input->cursor_delay_fd, &exp, sizeof (uint64_t)) != sizeof (uint64_t)) - return; - cursor = input->display->cursors[input->current_cursor]; if (!cursor) return; @@ -3979,8 +4009,9 @@ offer_io_func(struct task *task, uint32_t events) offer->x, offer->y, offer->user_data); if (len == 0) { - if (display->data_device_manager_version >= - WL_DATA_OFFER_FINISH_SINCE_VERSION) + if ((offer != offer->input->selection_offer) && + (display->data_device_manager_version >= + WL_DATA_OFFER_FINISH_SINCE_VERSION)) wl_data_offer_finish(offer->offer); close(offer->fd); data_offer_destroy(offer); @@ -4066,7 +4097,7 @@ window_move(struct window *window, struct input *input, uint32_t serial) if (!window->xdg_toplevel) return; - zxdg_toplevel_v6_move(window->xdg_toplevel, input->seat, serial); + xdg_toplevel_move(window->xdg_toplevel, input->seat, serial); } static void @@ -4270,7 +4301,7 @@ window_get_shadow_margin(struct window *window) return 0; } -static void +void window_inhibit_redraw(struct window *window) { window->redraw_inhibited = 1; @@ -4279,7 +4310,7 @@ window_inhibit_redraw(struct window *window) window->redraw_task_scheduled = 0; } -static void +void window_uninhibit_redraw(struct window *window) { window->redraw_inhibited = 0; @@ -4289,12 +4320,12 @@ window_uninhibit_redraw(struct window *window) static void xdg_surface_handle_configure(void *data, - struct zxdg_surface_v6 *zxdg_surface_v6, + struct xdg_surface *xdg_surface, uint32_t serial) { struct window *window = data; - zxdg_surface_v6_ack_configure(window->xdg_surface, serial); + xdg_surface_ack_configure(window->xdg_surface, serial); if (window->state_changed_handler) window->state_changed_handler(window, window->user_data); @@ -4302,12 +4333,12 @@ xdg_surface_handle_configure(void *data, window_uninhibit_redraw(window); } -static const struct zxdg_surface_v6_listener xdg_surface_listener = { +static const struct xdg_surface_listener xdg_surface_listener = { xdg_surface_handle_configure }; static void -xdg_toplevel_handle_configure(void *data, struct zxdg_toplevel_v6 *xdg_toplevel, +xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states) { @@ -4322,16 +4353,16 @@ xdg_toplevel_handle_configure(void *data, struct zxdg_toplevel_v6 *xdg_toplevel, wl_array_for_each(p, states) { uint32_t state = *p; switch (state) { - case ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED: + case XDG_TOPLEVEL_STATE_MAXIMIZED: window->maximized = 1; break; - case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN: + case XDG_TOPLEVEL_STATE_FULLSCREEN: window->fullscreen = 1; break; - case ZXDG_TOPLEVEL_V6_STATE_RESIZING: + case XDG_TOPLEVEL_STATE_RESIZING: window->resizing = 1; break; - case ZXDG_TOPLEVEL_V6_STATE_ACTIVATED: + case XDG_TOPLEVEL_STATE_ACTIVATED: window->focused = 1; break; default: @@ -4372,13 +4403,13 @@ xdg_toplevel_handle_configure(void *data, struct zxdg_toplevel_v6 *xdg_toplevel, } static void -xdg_toplevel_handle_close(void *data, struct zxdg_toplevel_v6 *xdg_surface) +xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_surface) { struct window *window = data; window_close(window); } -static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = { +static const struct xdg_toplevel_listener xdg_toplevel_listener = { xdg_toplevel_handle_configure, xdg_toplevel_handle_close, }; @@ -4386,7 +4417,7 @@ static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = { static void window_sync_parent(struct window *window) { - struct zxdg_toplevel_v6 *parent_toplevel; + struct xdg_toplevel *parent_toplevel; if (!window->xdg_surface) return; @@ -4399,7 +4430,7 @@ window_sync_parent(struct window *window) else parent_toplevel = NULL; - zxdg_toplevel_v6_set_parent(window->xdg_toplevel, parent_toplevel); + xdg_toplevel_set_parent(window->xdg_toplevel, parent_toplevel); window->last_parent = window->parent; } @@ -4431,11 +4462,11 @@ window_sync_geometry(struct window *window) geometry.height == window->last_geometry.height) return; - zxdg_surface_v6_set_window_geometry(window->xdg_surface, - geometry.x, - geometry.y, - geometry.width, - geometry.height); + xdg_surface_set_window_geometry(window->xdg_surface, + geometry.x, + geometry.y, + geometry.width, + geometry.height); window->last_geometry = geometry; } @@ -4642,9 +4673,9 @@ window_set_fullscreen(struct window *window, int fullscreen) return; if (fullscreen) - zxdg_toplevel_v6_set_fullscreen(window->xdg_toplevel, NULL); + xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL); else - zxdg_toplevel_v6_unset_fullscreen(window->xdg_toplevel); + xdg_toplevel_unset_fullscreen(window->xdg_toplevel); } int @@ -4663,9 +4694,9 @@ window_set_maximized(struct window *window, int maximized) return; if (maximized) - zxdg_toplevel_v6_set_maximized(window->xdg_toplevel); + xdg_toplevel_set_maximized(window->xdg_toplevel); else - zxdg_toplevel_v6_unset_maximized(window->xdg_toplevel); + xdg_toplevel_unset_maximized(window->xdg_toplevel); } int @@ -4680,7 +4711,7 @@ window_set_minimized(struct window *window) if (!window->xdg_toplevel) return; - zxdg_toplevel_v6_set_minimized(window->xdg_toplevel); + xdg_toplevel_set_minimized(window->xdg_toplevel); } void @@ -4784,7 +4815,7 @@ window_set_title(struct window *window, const char *title) widget_schedule_redraw(window->frame->widget); } if (window->xdg_toplevel) - zxdg_toplevel_v6_set_title(window->xdg_toplevel, title); + xdg_toplevel_set_title(window->xdg_toplevel, title); } const char * @@ -4840,7 +4871,10 @@ locked_pointer_locked(void *data, struct zwp_locked_pointer_v1 *locked_pointer) { struct input *input = data; - struct window *window = input->pointer_focus; + struct window *window = input->locked_window; + + if (!window) + return; window->pointer_locked = true; @@ -4856,10 +4890,15 @@ locked_pointer_unlocked(void *data, struct zwp_locked_pointer_v1 *locked_pointer) { struct input *input = data; - struct window *window = input->pointer_focus; + struct window *window = input->locked_window; + + if (!window) + return; window_unlock_pointer(window); + input->locked_window = NULL; + if (window->pointer_unlocked_handler) { window->pointer_unlocked_handler(window, input, @@ -4913,9 +4952,9 @@ window_lock_pointer(struct window *window, struct input *input) &locked_pointer_listener, input); - window->locked_input = input; window->locked_pointer = locked_pointer; window->relative_pointer = relative_pointer; + input->locked_window = window; return 0; } @@ -4931,7 +4970,6 @@ window_unlock_pointer(struct window *window) window->locked_pointer = NULL; window->relative_pointer = NULL; window->pointer_locked = false; - window->locked_input = NULL; } void @@ -4954,7 +4992,10 @@ confined_pointer_confined(void *data, struct zwp_confined_pointer_v1 *confined_pointer) { struct input *input = data; - struct window *window = input->pointer_focus; + struct window *window = input->confined_window; + + if (!window) + return; window->confined = true; @@ -4970,11 +5011,15 @@ confined_pointer_unconfined(void *data, struct zwp_confined_pointer_v1 *confined_pointer) { struct input *input = data; - struct window *window = input->pointer_focus; + struct window *window = input->confined_window; + + if (!window) + return; window_unconfine_pointer(window); window->confined = false; + input->confined_window = NULL; if (window->pointer_unconfined_handler) { window->pointer_unconfined_handler(window, @@ -5039,6 +5084,7 @@ window_confine_pointer_to_rectangles(struct window *window, window->confined_pointer = confined_pointer; window->confined_widget = NULL; + input->confined_window = window; return 0; } @@ -5172,6 +5218,7 @@ surface_create(struct window *window) wl_surface_add_listener(surface->surface, &surface_listener, window); wl_list_insert(&window->subsurface_list, &surface->link); + surface->viewport = NULL; return surface; } @@ -5200,10 +5247,9 @@ window_create_internal(struct display *display, int custom) surface = surface_create(window); window->main_surface = surface; - assert(custom || display->xdg_shell || display->ivi_application); + assert(custom || display->xdg_shell); window->custom = custom; - window->preferred_format = WINDOW_PREFERRED_FORMAT_NONE; surface->buffer_type = get_preferred_buffer_type(display); @@ -5220,39 +5266,28 @@ struct window * window_create(struct display *display) { struct window *window; - uint32_t id_ivisurf; window = window_create_internal(display, 0); if (window->display->xdg_shell) { window->xdg_surface = - zxdg_shell_v6_get_xdg_surface(window->display->xdg_shell, - window->main_surface->surface); + xdg_wm_base_get_xdg_surface(window->display->xdg_shell, + window->main_surface->surface); fail_on_null(window->xdg_surface, 0, __FILE__, __LINE__); - zxdg_surface_v6_add_listener(window->xdg_surface, - &xdg_surface_listener, window); + xdg_surface_add_listener(window->xdg_surface, + &xdg_surface_listener, window); window->xdg_toplevel = - zxdg_surface_v6_get_toplevel(window->xdg_surface); + xdg_surface_get_toplevel(window->xdg_surface); fail_on_null(window->xdg_toplevel, 0, __FILE__, __LINE__); - zxdg_toplevel_v6_add_listener(window->xdg_toplevel, - &xdg_toplevel_listener, window); + xdg_toplevel_add_listener(window->xdg_toplevel, + &xdg_toplevel_listener, window); window_inhibit_redraw(window); wl_surface_commit(window->main_surface->surface); - } else if (display->ivi_application) { - /* auto generation of ivi_id based on process id + basement of id */ - id_ivisurf = IVI_SURFACE_ID + (uint32_t)getpid(); - window->ivi_surface = - ivi_application_surface_create(display->ivi_application, - id_ivisurf, window->main_surface->surface); - fail_on_null(window->ivi_surface, 0, __FILE__, __LINE__); - - ivi_surface_add_listener(window->ivi_surface, - &ivi_surface_listener, window); } return window; @@ -5403,7 +5438,7 @@ menu_redraw_handler(struct widget *widget, void *data) static void xdg_popup_handle_configure(void *data, - struct zxdg_popup_v6 *zxdg_popup_v6, + struct xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, @@ -5412,7 +5447,7 @@ xdg_popup_handle_configure(void *data, } static void -xdg_popup_handle_popup_done(void *data, struct zxdg_popup_v6 *xdg_popup) +xdg_popup_handle_popup_done(void *data, struct xdg_popup *xdg_popup) { struct window *window = data; struct menu *menu = window->main_surface->widget->user_data; @@ -5421,7 +5456,7 @@ xdg_popup_handle_popup_done(void *data, struct zxdg_popup_v6 *xdg_popup) menu_destroy(menu); } -static const struct zxdg_popup_v6_listener xdg_popup_listener = { +static const struct xdg_popup_listener xdg_popup_listener = { xdg_popup_handle_configure, xdg_popup_handle_popup_done, }; @@ -5449,7 +5484,7 @@ create_menu(struct display *display, menu->user_data = user_data; menu->widget = window_add_widget(menu->window, menu); menu->frame = frame_create(window->display->theme, 0, 0, - FRAME_BUTTON_NONE, NULL); + FRAME_BUTTON_NONE, NULL, NULL); fail_on_null(menu->frame, 0, __FILE__, __LINE__); menu->entries = entries; menu->count = count; @@ -5477,22 +5512,20 @@ create_menu(struct display *display, return menu; } -static struct zxdg_positioner_v6 * +static struct xdg_positioner * create_simple_positioner(struct display *display, int x, int y, int w, int h) { - struct zxdg_positioner_v6 *positioner; + struct xdg_positioner *positioner; - positioner = zxdg_shell_v6_create_positioner(display->xdg_shell); + positioner = xdg_wm_base_create_positioner(display->xdg_shell); fail_on_null(positioner, 0, __FILE__, __LINE__); - zxdg_positioner_v6_set_anchor_rect(positioner, x, y, 1, 1); - zxdg_positioner_v6_set_size(positioner, w, h); - zxdg_positioner_v6_set_anchor(positioner, - ZXDG_POSITIONER_V6_ANCHOR_TOP | - ZXDG_POSITIONER_V6_ANCHOR_LEFT); - zxdg_positioner_v6_set_gravity(positioner, - ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | - ZXDG_POSITIONER_V6_ANCHOR_RIGHT); + xdg_positioner_set_anchor_rect(positioner, x, y, 1, 1); + xdg_positioner_set_size(positioner, w, h); + xdg_positioner_set_anchor(positioner, + XDG_POSITIONER_ANCHOR_TOP_LEFT); + xdg_positioner_set_gravity(positioner, + XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT); return positioner; } @@ -5507,7 +5540,7 @@ window_show_menu(struct display *display, struct window *window; int32_t ix, iy; struct rectangle parent_geometry; - struct zxdg_positioner_v6 *positioner; + struct xdg_positioner *positioner; menu = create_menu(display, input, time, func, entries, count, parent); @@ -5529,29 +5562,27 @@ window_show_menu(struct display *display, return; window->xdg_surface = - zxdg_shell_v6_get_xdg_surface(display->xdg_shell, - window->main_surface->surface); + xdg_wm_base_get_xdg_surface(display->xdg_shell, + window->main_surface->surface); fail_on_null(window->xdg_surface, 0, __FILE__, __LINE__); - zxdg_surface_v6_add_listener(window->xdg_surface, - &xdg_surface_listener, window); + xdg_surface_add_listener(window->xdg_surface, + &xdg_surface_listener, window); positioner = create_simple_positioner(display, window->x - (ix + parent_geometry.x), window->y - (iy + parent_geometry.y), frame_width(menu->frame), frame_height(menu->frame)); - window->xdg_popup = - zxdg_surface_v6_get_popup(window->xdg_surface, - parent->xdg_surface, - positioner); + window->xdg_popup = xdg_surface_get_popup(window->xdg_surface, + parent->xdg_surface, + positioner); fail_on_null(window->xdg_popup, 0, __FILE__, __LINE__); - zxdg_positioner_v6_destroy(positioner); - zxdg_popup_v6_grab(window->xdg_popup, - input->seat, - display_get_serial(window->display)); - zxdg_popup_v6_add_listener(window->xdg_popup, - &xdg_popup_listener, window); + xdg_positioner_destroy(positioner); + xdg_popup_grab(window->xdg_popup, input->seat, + display_get_serial(window->display)); + xdg_popup_add_listener(window->xdg_popup, + &xdg_popup_listener, window); window_inhibit_redraw(window); @@ -5570,13 +5601,6 @@ window_get_buffer_type(struct window *window) return window->main_surface->buffer_type; } -void -window_set_preferred_format(struct window *window, - enum preferred_format format) -{ - window->preferred_format = format; -} - struct widget * window_add_subsurface(struct window *window, void *data, enum subsurface_mode default_mode) @@ -5849,7 +5873,7 @@ static void display_add_input(struct display *d, uint32_t id, int display_seat_version) { struct input *input; - int seat_version = MIN(display_seat_version, 5); + int seat_version = MIN(display_seat_version, 7); input = xzalloc(sizeof *input); input->display = d; @@ -5876,19 +5900,36 @@ display_add_input(struct display *d, uint32_t id, int display_seat_version) } input->pointer_surface = wl_compositor_create_surface(d->compositor); - input->cursor_task.run = cursor_timer_func; - input->cursor_delay_fd = timerfd_create(CLOCK_MONOTONIC, - TFD_CLOEXEC | TFD_NONBLOCK); - display_watch_fd(d, input->cursor_delay_fd, EPOLLIN, - &input->cursor_task); + toytimer_init(&input->cursor_timer, CLOCK_MONOTONIC, d, + cursor_timer_func); + set_repeat_info(input, 40, 400); + toytimer_init(&input->repeat_timer, CLOCK_MONOTONIC, d, + keyboard_repeat_func); +} - input->repeat_timer_fd = timerfd_create(CLOCK_MONOTONIC, - TFD_CLOEXEC | TFD_NONBLOCK); - input->repeat_task.run = keyboard_repeat_func; - display_watch_fd(d, input->repeat_timer_fd, - EPOLLIN, &input->repeat_task); +static void +display_add_data_device(struct display *d, uint32_t id, int ddm_version) +{ + struct input *input; + + d->data_device_manager_version = MIN(ddm_version, 3); + d->data_device_manager = + wl_registry_bind(d->registry, id, + &wl_data_device_manager_interface, + d->data_device_manager_version); + + wl_list_for_each(input, &d->input_list, link) { + if (!input->data_device) { + input->data_device = + wl_data_device_manager_get_data_device(d->data_device_manager, + input->seat); + wl_data_device_add_listener(input->data_device, + &data_device_listener, + input); + } + } } static void @@ -5931,32 +5972,19 @@ input_destroy(struct input *input) wl_list_remove(&input->link); wl_seat_destroy(input->seat); - close(input->repeat_timer_fd); - close(input->cursor_delay_fd); + toytimer_fini(&input->repeat_timer); + toytimer_fini(&input->cursor_timer); free(input); } static void -shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) +xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) { - struct display *d = data; - - if (format == WL_SHM_FORMAT_RGB565) - d->has_rgb565 = 1; + xdg_wm_base_pong(shell, serial); } -struct wl_shm_listener shm_listener = { - shm_format -}; - -static void -xdg_shell_handle_ping(void *data, struct zxdg_shell_v6 *shell, uint32_t serial) -{ - zxdg_shell_v6_pong(shell, serial); -} - -static const struct zxdg_shell_v6_listener xdg_shell_listener = { - xdg_shell_handle_ping, +static const struct xdg_wm_base_listener wm_base_listener = { + xdg_wm_base_ping, }; static void @@ -5993,17 +6021,12 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, 1); } else if (strcmp(interface, "wl_shm") == 0) { d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); - wl_shm_add_listener(d->shm, &shm_listener, d); } else if (strcmp(interface, "wl_data_device_manager") == 0) { - d->data_device_manager_version = MIN(version, 3); - d->data_device_manager = - wl_registry_bind(registry, id, - &wl_data_device_manager_interface, - d->data_device_manager_version); - } else if (strcmp(interface, "zxdg_shell_v6") == 0) { + display_add_data_device(d, id, version); + } else if (strcmp(interface, "xdg_wm_base") == 0) { d->xdg_shell = wl_registry_bind(registry, id, - &zxdg_shell_v6_interface, 1); - zxdg_shell_v6_add_listener(d->xdg_shell, &xdg_shell_listener, d); + &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(d->xdg_shell, &wm_base_listener, d); } else if (strcmp(interface, "text_cursor_position") == 0) { d->text_cursor_position = wl_registry_bind(registry, id, @@ -6012,11 +6035,10 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, d->subcompositor = wl_registry_bind(registry, id, &wl_subcompositor_interface, 1); - } - else if (strcmp(interface, "ivi_application") == 0) { - d->ivi_application = + } else if (!strcmp(interface, "wp_viewporter")) { + d->viewporter = wl_registry_bind(registry, id, - &ivi_application_interface, 1); + &wp_viewporter_interface, 1); } if (d->global_handler) @@ -6216,12 +6238,13 @@ display_create(int *argc, char *argv[]) d->display = wl_display_connect(NULL); if (d->display == NULL) { - fprintf(stderr, "failed to connect to Wayland display: %m\n"); + fprintf(stderr, "failed to connect to Wayland display: %s\n", + strerror(errno)); free(d); return NULL; } - d->xkb_context = xkb_context_new(0); + d->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (d->xkb_context == NULL) { fprintf(stderr, "Failed to create XKB context\n"); free(d); @@ -6243,7 +6266,8 @@ display_create(int *argc, char *argv[]) wl_registry_add_listener(d->registry, ®istry_listener, d); if (wl_display_roundtrip(d->display) < 0) { - fprintf(stderr, "Failed to process Wayland connection: %m\n"); + fprintf(stderr, "Failed to process Wayland connection: %s\n", + strerror(errno)); return NULL; } @@ -6314,10 +6338,7 @@ display_destroy(struct display *display) wl_subcompositor_destroy(display->subcompositor); if (display->xdg_shell) - zxdg_shell_v6_destroy(display->xdg_shell); - - if (display->ivi_application) - ivi_application_destroy(display->ivi_application); + xdg_wm_base_destroy(display->xdg_shell); if (display->shm) wl_shm_destroy(display->shm); @@ -6562,3 +6583,91 @@ keysym_modifiers_get_mask(struct wl_array *modifiers_map, return 1 << index; } + +static void +toytimer_fire(struct task *tsk, uint32_t events) +{ + uint64_t e; + struct toytimer *tt; + + tt = container_of(tsk, struct toytimer, tsk); + + if (events != EPOLLIN) + fprintf(stderr, "unexpected timerfd events %x\n", events); + + if (!(events & EPOLLIN)) + return; + + if (read(tt->fd, &e, sizeof e) != sizeof e) { + /* If we change the timer between the fd becoming + * readable and getting here, there'll be nothing to + * read and we get EAGAIN. */ + if (errno != EAGAIN) + fprintf(stderr, "timer read failed: %s\n", + strerror(errno)); + return; + } + + tt->callback(tt); +} + +void +toytimer_init(struct toytimer *tt, clockid_t clock, struct display *display, + toytimer_cb callback) +{ + memset(tt, 0, sizeof *tt); + + tt->fd = timerfd_create(clock, TFD_CLOEXEC | TFD_NONBLOCK); + if (tt->fd == -1) { + fprintf(stderr, "creating timer failed: %s\n", + strerror(errno)); + abort(); + } + + tt->display = display; + tt->callback = callback; + tt->tsk.run = toytimer_fire; + display_watch_fd(display, tt->fd, EPOLLIN, &tt->tsk); +} + +void +toytimer_fini(struct toytimer *tt) +{ + display_unwatch_fd(tt->display, tt->fd); + close(tt->fd); + tt->fd = -1; +} + +void +toytimer_arm(struct toytimer *tt, const struct itimerspec *its) +{ + int ret; + + ret = timerfd_settime(tt->fd, 0, its, NULL); + if (ret < 0) { + fprintf(stderr, "timer setup failed: %s\n", strerror(errno)); + abort(); + } +} + +#define USEC_PER_SEC 1000000 + +void +toytimer_arm_once_usec(struct toytimer *tt, uint32_t usec) +{ + struct itimerspec its; + + its.it_interval.tv_sec = 0; + its.it_interval.tv_nsec = 0; + its.it_value.tv_sec = usec / USEC_PER_SEC; + its.it_value.tv_nsec = (usec % USEC_PER_SEC) * 1000; + toytimer_arm(tt, &its); +} + +void +toytimer_disarm(struct toytimer *tt) +{ + struct itimerspec its = {}; + + toytimer_arm(tt, &its); +} diff --git a/clients/window.h b/clients/window.h index 1ec9eac53..c66dd065e 100644 --- a/clients/window.h +++ b/clients/window.h @@ -27,11 +27,12 @@ #include "config.h" #include +#include #include #include #include -#include "shared/config-parser.h" -#include "shared/zalloc.h" +#include +#include #include "shared/platform.h" struct window; @@ -134,8 +135,6 @@ display_release_window_surface(struct display *display, #define SURFACE_HINT_RESIZE 0x10 -#define SURFACE_HINT_RGB565 0x100 - cairo_surface_t * display_create_surface(struct display *display, struct wl_surface *surface, @@ -507,15 +506,6 @@ window_get_title(struct window *window); void window_set_text_cursor_position(struct window *window, int32_t x, int32_t y); -enum preferred_format { - WINDOW_PREFERRED_FORMAT_NONE, - WINDOW_PREFERRED_FORMAT_RGB565 -}; - -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); @@ -603,11 +593,23 @@ widget_set_axis_handlers(struct widget *widget, widget_axis_stop_handler_t axis_stop_handler, widget_axis_discrete_handler_t axis_discrete_handler); +void +window_inhibit_redraw(struct window *window); +void +window_uninhibit_redraw(struct window *window); void widget_schedule_redraw(struct widget *widget); void widget_set_use_cairo(struct widget *widget, int use_cairo); +/* + * Sets the viewport destination for the widget's surface + * return 0 on success and -1 on failure. Set width and height to + * -1 to reset the viewport. + */ +int +widget_set_viewport_destination(struct widget *widget, int width, int height); + struct widget * window_frame_create(struct window *window, void *data); @@ -713,4 +715,30 @@ xkb_mod_mask_t keysym_modifiers_get_mask(struct wl_array *modifiers_map, const char *name); +struct toytimer; +typedef void (*toytimer_cb)(struct toytimer *); + +struct toytimer { + struct display *display; + struct task tsk; + int fd; + toytimer_cb callback; +}; + +void +toytimer_init(struct toytimer *tt, clockid_t clock, struct display *display, + toytimer_cb callback); + +void +toytimer_fini(struct toytimer *tt); + +void +toytimer_arm(struct toytimer *tt, const struct itimerspec *its); + +void +toytimer_arm_once_usec(struct toytimer *tt, uint32_t usec); + +void +toytimer_disarm(struct toytimer *tt); + #endif diff --git a/compositor/cms-colord.c b/compositor/cms-colord.c index 0daa2a7e9..d4efdb433 100644 --- a/compositor/cms-colord.c +++ b/compositor/cms-colord.c @@ -33,7 +33,7 @@ #include #include -#include "compositor.h" +#include #include "weston.h" #include "cms-helper.h" #include "shared/helpers.h" @@ -102,22 +102,33 @@ edid_value_valid(const char *str) 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(o->make)) { - tmp = g_hash_table_lookup(cms->pnp_ids, o->make); + if (edid_value_valid(head->make)) { + tmp = g_hash_table_lookup(cms->pnp_ids, head->make); if (tmp == NULL) - tmp = o->make; + tmp = head->make; g_string_append_printf(device_id, "-%s", tmp); } - if (edid_value_valid(o->model)) - g_string_append_printf(device_id, "-%s", o->model); - if (edid_value_valid(o->serial_number)) - g_string_append_printf(device_id, "-%s", o->serial_number); + 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) @@ -230,6 +241,7 @@ colord_notifier_output_destroy(struct wl_listener *listener, void *data) 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; @@ -237,6 +249,9 @@ colord_output_created(struct cms_colord *cms, struct weston_output *o) 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); @@ -251,25 +266,25 @@ colord_output_created(struct cms_colord *cms, struct weston_output *o) g_hash_table_insert (device_props, g_strdup(CD_DEVICE_PROPERTY_COLORSPACE), g_strdup(cd_colorspace_to_string(CD_COLORSPACE_RGB))); - if (edid_value_valid(o->make)) { - tmp = g_hash_table_lookup(cms->pnp_ids, o->make); + if (edid_value_valid(head->make)) { + tmp = g_hash_table_lookup(cms->pnp_ids, head->make); if (tmp == NULL) - tmp = o->make; + tmp = head->make; g_hash_table_insert (device_props, g_strdup(CD_DEVICE_PROPERTY_VENDOR), g_strdup(tmp)); } - if (edid_value_valid(o->model)) { + if (edid_value_valid(head->model)) { g_hash_table_insert (device_props, g_strdup(CD_DEVICE_PROPERTY_MODEL), - g_strdup(o->model)); + g_strdup(head->model)); } - if (edid_value_valid(o->serial_number)) { + if (edid_value_valid(head->serial_number)) { g_hash_table_insert (device_props, g_strdup(CD_DEVICE_PROPERTY_SERIAL), - g_strdup(o->serial_number)); + g_strdup(head->serial_number)); } - if (o->connection_internal) { + if (head->connection_internal) { g_hash_table_insert (device_props, g_strdup (CD_DEVICE_PROPERTY_EMBEDDED), NULL); @@ -451,6 +466,7 @@ colord_module_destroy(struct cms_colord *cms) g_free(cms->pnp_ids_data); g_hash_table_unref(cms->pnp_ids); + wl_list_remove(&cms->destroy_listener.link); free(cms); } @@ -512,6 +528,14 @@ wet_module_init(struct weston_compositor *ec, if (cms == NULL) return -1; cms->ec = ec; + + if (!weston_compositor_add_destroy_listener_once(ec, + &cms->destroy_listener, + colord_notifier_destroy)) { + free(cms); + return 0; + } + #if !GLIB_CHECK_VERSION(2,36,0) g_type_init(); #endif @@ -527,10 +551,6 @@ wet_module_init(struct weston_compositor *ec, 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); diff --git a/compositor/cms-helper.c b/compositor/cms-helper.c index 1784c46d3..bc56a9dca 100644 --- a/compositor/cms-helper.c +++ b/compositor/cms-helper.c @@ -34,7 +34,7 @@ #include #endif -#include "compositor.h" +#include #include "cms-helper.h" #ifdef HAVE_LCMS diff --git a/compositor/cms-helper.h b/compositor/cms-helper.h index 402652e69..4a5b711e5 100644 --- a/compositor/cms-helper.h +++ b/compositor/cms-helper.h @@ -28,7 +28,7 @@ #include "config.h" -#include "compositor.h" +#include /* General overview on how to be a CMS plugin: * diff --git a/compositor/cms-static.c b/compositor/cms-static.c index e24501b6c..540d6ad38 100644 --- a/compositor/cms-static.c +++ b/compositor/cms-static.c @@ -28,7 +28,7 @@ #include #include -#include "compositor.h" +#include #include "cms-helper.h" #include "shared/helpers.h" #include "weston.h" @@ -105,8 +105,13 @@ wet_module_init(struct weston_compositor *ec, return -1; cms->ec = ec; - cms->destroy_listener.notify = cms_notifier_destroy; - wl_signal_add(&ec->destroy_signal, &cms->destroy_listener); + + if (!weston_compositor_add_destroy_listener_once(ec, + &cms->destroy_listener, + cms_notifier_destroy)) { + free(cms); + return 0; + } cms->output_created_listener.notify = cms_notifier_output_created; wl_signal_add(&ec->output_created_signal, &cms->output_created_listener); diff --git a/shared/xalloc.c b/compositor/executable.c similarity index 70% rename from shared/xalloc.c rename to compositor/executable.c index 1cc5c12a0..0644077af 100644 --- a/shared/xalloc.c +++ b/compositor/executable.c @@ -1,5 +1,5 @@ /* - * Copyright © 2008 Kristian Høgsberg + * 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 @@ -25,26 +25,10 @@ #include "config.h" -#include -#include -#include -#include +#include "weston.h" -#include "xalloc.h" - -void * -fail_on_null(void *p, size_t size, char *file, int32_t line) +int +main(int argc, char *argv[]) { - if (p == NULL) { - fprintf(stderr, "[%s] ", program_invocation_short_name); - if (file) - fprintf(stderr, "%s:%d: ", file, line); - fprintf(stderr, "out of memory"); - if (size) - fprintf(stderr, " (%zd)", size); - fprintf(stderr, "\n"); - exit(EXIT_FAILURE); - } - - return p; + return wet_main(argc, argv); } diff --git a/compositor/main.c b/compositor/main.c index 72c3cd10a..2cca62e46 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -1,9 +1,10 @@ /* * Copyright © 2010-2011 Intel Corporation * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2012-2015 Collabora, Ltd. + * Copyright © 2012-2018 Collabora, Ltd. * Copyright © 2010-2011 Benjamin Franzke * Copyright © 2013 Jason Ekstrand + * Copyright © 2017, 2018 General Electric Company * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -40,32 +41,37 @@ #include #include #include +#include +#include #include #include -#ifdef HAVE_LIBUNWIND -#define UNW_LOCAL_ONLY -#include -#endif - #include "weston.h" -#include "compositor.h" -#include "../shared/os-compatibility.h" -#include "../shared/helpers.h" -#include "../shared/string-helpers.h" +#include +#include "shared/os-compatibility.h" +#include "shared/helpers.h" +#include "shared/string-helpers.h" #include "git-version.h" -#include "version.h" +#include #include "weston.h" -#include "compositor-drm.h" -#include "compositor-headless.h" -#include "compositor-rdp.h" -#include "compositor-fbdev.h" -#include "compositor-x11.h" -#include "compositor-wayland.h" -#include "windowed-output-api.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "../remoting/remoting-plugin.h" +#include "../pipewire/pipewire-plugin.h" + +#include +#include #define WINDOW_TITLE "Weston Compositor" +/* flight recorder size (in bytes) */ +#define DEFAULT_FLIGHT_REC_SIZE (5 * 1024 * 1024) struct wet_output_config { int width; @@ -74,64 +80,122 @@ struct wet_output_config { uint32_t transform; }; +struct wet_compositor; +struct wet_layoutput; + +struct wet_head_tracker { + struct wl_listener head_destroy_listener; +}; + +/** User data for each weston_output */ +struct wet_output { + struct weston_output *output; + struct wl_listener output_destroy_listener; + struct wet_layoutput *layoutput; + struct wl_list link; /**< in wet_layoutput::output_list */ +}; + +#define MAX_CLONE_HEADS 16 + +struct wet_head_array { + struct weston_head *heads[MAX_CLONE_HEADS]; /**< heads to add */ + unsigned n; /**< the number of heads */ +}; + +/** A layout output + * + * Contains wet_outputs that are all clones (independent CRTCs). + * Stores output layout information in the future. + */ +struct wet_layoutput { + struct wet_compositor *compositor; + struct wl_list compositor_link; /**< in wet_compositor::layoutput_list */ + struct wl_list output_list; /**< wet_output::link */ + char *name; + struct weston_config_section *section; + struct wet_head_array add; /**< tmp: heads to add as clones */ +}; + struct wet_compositor { + struct weston_compositor *compositor; struct weston_config *config; struct wet_output_config *parsed_options; - struct wl_listener pending_output_listener; bool drm_use_current_mode; + struct wl_listener heads_changed_listener; + int (*simple_output_configure)(struct weston_output *output); + bool init_failed; + struct wl_list layoutput_list; /**< wet_layoutput::compositor_link */ + struct wet_rdp_params rdp_params; }; static FILE *weston_logfile = NULL; - +static struct weston_log_scope *log_scope; +static struct weston_log_scope *protocol_scope; static int cached_tm_mday = -1; -static int weston_log_timestamp(void) +static char * +weston_log_timestamp(char *buf, size_t len) { struct timeval tv; struct tm *brokendown_time; - char string[128]; + char datestr[128]; + char timestr[128]; gettimeofday(&tv, NULL); brokendown_time = localtime(&tv.tv_sec); - if (brokendown_time == NULL) - return fprintf(weston_logfile, "[(NULL)localtime] "); + if (brokendown_time == NULL) { + snprintf(buf, len, "%s", "[(NULL)localtime] "); + return buf; + } + memset(datestr, 0, sizeof(datestr)); if (brokendown_time->tm_mday != cached_tm_mday) { - strftime(string, sizeof string, "%Y-%m-%d %Z", brokendown_time); - fprintf(weston_logfile, "Date: %s\n", string); - + strftime(datestr, sizeof(datestr), "Date: %Y-%m-%d %Z\n", + brokendown_time); cached_tm_mday = brokendown_time->tm_mday; } - strftime(string, sizeof string, "%H:%M:%S", brokendown_time); + strftime(timestr, sizeof(timestr), "%H:%M:%S", brokendown_time); + /* if datestr is empty it prints only timestr*/ + snprintf(buf, len, "%s[%s.%03li]", datestr, + timestr, (tv.tv_usec / 1000)); - return fprintf(weston_logfile, "[%s.%03li] ", string, tv.tv_usec/1000); + return buf; } static void custom_handler(const char *fmt, va_list arg) { - weston_log_timestamp(); - fprintf(weston_logfile, "libwayland: "); - vfprintf(weston_logfile, fmt, arg); + char timestr[512]; + + weston_log_scope_printf(log_scope, "%s libwayland: ", + weston_log_timestamp(timestr, + sizeof(timestr))); + weston_log_scope_vprintf(log_scope, fmt, arg); } -static void +static bool weston_log_file_open(const char *filename) { wl_log_set_handler_server(custom_handler); if (filename != NULL) { weston_logfile = fopen(filename, "a"); - if (weston_logfile) + if (weston_logfile) { os_fd_set_cloexec(fileno(weston_logfile)); + } else { + fprintf(stderr, "Failed to open %s: %s\n", filename, strerror(errno)); + return false; + } } if (weston_logfile == NULL) weston_logfile = stderr; else setvbuf(weston_logfile, NULL, _IOLBF, 256); + + return true; } static void @@ -145,18 +209,143 @@ weston_log_file_close(void) static int vlog(const char *fmt, va_list ap) { - int l; - - l = weston_log_timestamp(); - l += vfprintf(weston_logfile, fmt, ap); + const char *oom = "Out of memory"; + char timestr[128]; + int len = 0; + char *str; + + if (weston_log_scope_is_enabled(log_scope)) { + int len_va; + char *log_timestamp = weston_log_timestamp(timestr, + sizeof(timestr)); + len_va = vasprintf(&str, fmt, ap); + if (len_va >= 0) { + len = weston_log_scope_printf(log_scope, "%s %s", + log_timestamp, str); + free(str); + } else { + len = weston_log_scope_printf(log_scope, "%s %s", + log_timestamp, oom); + } + } - return l; + return len; } static int vlog_continue(const char *fmt, va_list argp) { - return vfprintf(weston_logfile, fmt, argp); + return weston_log_scope_vprintf(log_scope, fmt, argp); +} + +static const char * +get_next_argument(const char *signature, char* type) +{ + for(; *signature; ++signature) { + switch(*signature) { + case 'i': + case 'u': + case 'f': + case 's': + case 'o': + case 'n': + case 'a': + case 'h': + *type = *signature; + return signature + 1; + } + } + *type = '\0'; + return signature; +} + +static void +protocol_log_fn(void *user_data, + enum wl_protocol_logger_type direction, + const struct wl_protocol_logger_message *message) +{ + FILE *fp; + char *logstr; + size_t logsize; + char timestr[128]; + struct wl_resource *res = message->resource; + const char *signature = message->message->signature; + int i; + char type; + + if (!weston_log_scope_is_enabled(protocol_scope)) + return; + + fp = open_memstream(&logstr, &logsize); + if (!fp) + return; + + weston_log_scope_timestamp(protocol_scope, + timestr, sizeof timestr); + fprintf(fp, "%s ", timestr); + fprintf(fp, "client %p %s ", wl_resource_get_client(res), + direction == WL_PROTOCOL_LOGGER_REQUEST ? "rq" : "ev"); + fprintf(fp, "%s@%u.%s(", + wl_resource_get_class(res), + wl_resource_get_id(res), + message->message->name); + + for (i = 0; i < message->arguments_count; i++) { + signature = get_next_argument(signature, &type); + + if (i > 0) + fprintf(fp, ", "); + + switch (type) { + case 'u': + fprintf(fp, "%u", message->arguments[i].u); + break; + case 'i': + fprintf(fp, "%d", message->arguments[i].i); + break; + case 'f': + fprintf(fp, "%f", + wl_fixed_to_double(message->arguments[i].f)); + break; + case 's': + fprintf(fp, "\"%s\"", message->arguments[i].s); + break; + case 'o': + if (message->arguments[i].o) { + struct wl_resource* resource; + resource = (struct wl_resource*) message->arguments[i].o; + fprintf(fp, "%s@%u", + wl_resource_get_class(resource), + wl_resource_get_id(resource)); + } + else + fprintf(fp, "nil"); + break; + case 'n': + fprintf(fp, "new id %s@", + (message->message->types[i]) ? + message->message->types[i]->name : + "[unknown]"); + if (message->arguments[i].n != 0) + fprintf(fp, "%u", message->arguments[i].n); + else + fprintf(fp, "nil"); + break; + case 'a': + fprintf(fp, "array"); + break; + case 'h': + fprintf(fp, "fd %d", message->arguments[i].h); + break; + } + } + + fprintf(fp, ")\n"); + + if (fclose(fp) == 0) + weston_log_scope_write(protocol_scope, logstr, logsize); + + free(logstr); } static struct wl_list child_process_list; @@ -185,90 +374,11 @@ sigchld_handler(int signal_number, void *data) } if (pid < 0 && errno != ECHILD) - weston_log("waitpid error %m\n"); + weston_log("waitpid error %s\n", strerror(errno)); return 1; } -#ifdef HAVE_LIBUNWIND - -static void -print_backtrace(void) -{ - unw_cursor_t cursor; - unw_context_t context; - unw_word_t off; - unw_proc_info_t pip; - int ret, i = 0; - char procname[256]; - const char *filename; - Dl_info dlinfo; - - pip.unwind_info = NULL; - ret = unw_getcontext(&context); - if (ret) { - weston_log("unw_getcontext: %d\n", ret); - return; - } - - ret = unw_init_local(&cursor, &context); - if (ret) { - weston_log("unw_init_local: %d\n", ret); - return; - } - - ret = unw_step(&cursor); - while (ret > 0) { - ret = unw_get_proc_info(&cursor, &pip); - if (ret) { - weston_log("unw_get_proc_info: %d\n", ret); - break; - } - - ret = unw_get_proc_name(&cursor, procname, 256, &off); - if (ret && ret != -UNW_ENOMEM) { - if (ret != -UNW_EUNSPEC) - weston_log("unw_get_proc_name: %d\n", ret); - procname[0] = '?'; - procname[1] = 0; - } - - if (dladdr((void *)(pip.start_ip + off), &dlinfo) && dlinfo.dli_fname && - *dlinfo.dli_fname) - filename = dlinfo.dli_fname; - else - filename = "?"; - - weston_log("%u: %s (%s%s+0x%x) [%p]\n", i++, filename, procname, - ret == -UNW_ENOMEM ? "..." : "", (int)off, (void *)(pip.start_ip + off)); - - ret = unw_step(&cursor); - if (ret < 0) - weston_log("unw_step: %d\n", ret); - } -} - -#else - -static void -print_backtrace(void) -{ - void *buffer[32]; - int i, count; - Dl_info info; - - count = backtrace(buffer, ARRAY_LENGTH(buffer)); - for (i = 0; i < count; i++) { - dladdr(buffer[i], &info); - weston_log(" [%016lx] %s (%s)\n", - (long) buffer[i], - info.dli_sname ? info.dli_sname : "--", - info.dli_fname); - } -} - -#endif - static void child_client_exec(int sockfd, const char *path) { @@ -280,7 +390,7 @@ child_client_exec(int sockfd, const char *path) sigfillset(&allsigs); sigprocmask(SIG_UNBLOCK, &allsigs, NULL); - /* Launch clients as the user. Do not lauch clients with wrong euid.*/ + /* Launch clients as the user. Do not launch clients with wrong euid. */ if (seteuid(getuid()) == -1) { weston_log("compositor: failed seteuid\n"); return; @@ -290,7 +400,7 @@ child_client_exec(int sockfd, const char *path) * non-CLOEXEC fd to pass through exec. */ clientfd = dup(sockfd); if (clientfd == -1) { - weston_log("compositor: dup failed: %m\n"); + weston_log("compositor: dup failed: %s\n", strerror(errno)); return; } @@ -298,8 +408,8 @@ child_client_exec(int sockfd, const char *path) setenv("WAYLAND_SOCKET", s, 1); if (execl(path, path, NULL) < 0) - weston_log("compositor: executing '%s' failed: %m\n", - path); + weston_log("compositor: executing '%s' failed: %s\n", + path, strerror(errno)); } WL_EXPORT struct wl_client * @@ -316,8 +426,8 @@ weston_client_launch(struct weston_compositor *compositor, if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, sv) < 0) { weston_log("weston_client_launch: " - "socketpair failed while launching '%s': %m\n", - path); + "socketpair failed while launching '%s': %s\n", + path, strerror(errno)); return NULL; } @@ -326,11 +436,19 @@ weston_client_launch(struct weston_compositor *compositor, close(sv[0]); close(sv[1]); weston_log("weston_client_launch: " - "fork failed while launching '%s': %m\n", path); + "fork failed while launching '%s': %s\n", path, + strerror(errno)); return NULL; } if (pid == 0) { + /* Put the client in a new session so it won't catch signals + * intended for the parent. Sharing a session can be + * confusing when launching weston under gdb, as the ctrl-c + * intended for gdb will pass to the child, and weston + * will cleanly shut down when the child exits. + */ + setsid(); child_client_exec(sv[1], path); _exit(-1); } @@ -436,16 +554,6 @@ to_wet_compositor(struct weston_compositor *compositor) return weston_compositor_get_user_data(compositor); } -static void -wet_set_pending_output_handler(struct weston_compositor *ec, - wl_notify_func_t handler) -{ - struct wet_compositor *compositor = to_wet_compositor(ec); - - compositor->pending_output_listener.notify = handler; - wl_signal_add(&ec->output_pending_signal, &compositor->pending_output_listener); -} - static struct wet_output_config * wet_init_parsed_options(struct weston_compositor *ec) { @@ -522,7 +630,9 @@ verify_xdg_runtime_dir(void) static int usage(int error_code) { - fprintf(stderr, + FILE *out = error_code == EXIT_SUCCESS ? stdout : stderr; + + fprintf(out, "Usage: weston [OPTIONS]\n\n" "This is weston version " VERSION ", the Wayland reference compositor.\n" "Weston supports multiple backends, and depending on which backend is in use\n" @@ -553,47 +663,63 @@ usage(int error_code) " --shell=MODULE\tShell module, defaults to desktop-shell.so\n" " -S, --socket=NAME\tName of socket to listen on\n" " -i, --idle-time=SECS\tIdle time in seconds\n" +#if defined(BUILD_XWAYLAND) + " --xwayland\t\tLoad the xwayland module\n" +#endif " --modules\t\tLoad the comma-separated list of modules\n" " --log=FILE\t\tLog to the given file\n" " -c, --config=FILE\tConfig file to load, defaults to weston.ini\n" " --no-config\t\tDo not read weston.ini\n" + " --wait-for-debugger\tRaise SIGSTOP on start-up\n" + " --debug\t\tEnable debug extension\n" + " -l, --logger-scopes=SCOPE\n\t\t\tSpecify log scopes to " + "subscribe to.\n\t\t\tCan specify multiple scopes, " + "each followed by comma\n" + " -f, --flight-rec-scopes=SCOPE\n\t\t\tSpecify log scopes to " + "subscribe to.\n\t\t\tCan specify multiple scopes, " + "each followed by comma\n" " -h, --help\t\tThis help message\n\n"); #if defined(BUILD_DRM_COMPOSITOR) - fprintf(stderr, + fprintf(out, "Options for drm-backend.so:\n\n" - " --connector=ID\tBring up only this connector\n" - " --seat=SEAT\t\tThe seat that weston should run on\n" + " --seat=SEAT\t\tThe seat that weston should run on, instead of the seat defined in XDG_SEAT\n" " --tty=TTY\t\tThe tty to use\n" + " --drm-device=CARD\tThe DRM device to use, e.g. \"card0\".\n" " --use-pixman\t\tUse the pixman (CPU) renderer\n" - " --current-mode\tPrefer current KMS mode over EDID preferred mode\n\n"); + " --current-mode\tPrefer current KMS mode over EDID preferred mode\n" + " --continue-without-input\tAllow the compositor to start without input devices\n\n"); #endif #if defined(BUILD_FBDEV_COMPOSITOR) - fprintf(stderr, + fprintf(out, "Options for fbdev-backend.so:\n\n" " --tty=TTY\t\tThe tty to use\n" " --device=DEVICE\tThe framebuffer device to use\n" + " --seat=SEAT\t\tThe seat that weston should run on, instead of the seat defined in XDG_SEAT\n" "\n"); #endif #if defined(BUILD_HEADLESS_COMPOSITOR) - fprintf(stderr, + fprintf(out, "Options for headless-backend.so:\n\n" " --width=WIDTH\t\tWidth of memory surface\n" " --height=HEIGHT\tHeight of memory surface\n" + " --scale=SCALE\t\tScale factor of output\n" " --transform=TR\tThe output transformation, TR is one of:\n" "\tnormal 90 180 270 flipped flipped-90 flipped-180 flipped-270\n" " --use-pixman\t\tUse the pixman (CPU) renderer (default: no rendering)\n" + " --use-gl\t\tUse the GL renderer (default: no rendering)\n" " --no-outputs\t\tDo not create any virtual outputs\n" "\n"); #endif #if defined(BUILD_RDP_COMPOSITOR) - fprintf(stderr, + fprintf(out, "Options for rdp-backend.so:\n\n" " --width=WIDTH\t\tWidth of desktop\n" " --height=HEIGHT\tHeight of desktop\n" + " --scale=SCALE\t\tScale factor of desktop\n" " --env-socket\t\tUse socket defined in RDP_FD env variable as peer connection\n" " --address=ADDR\tThe address to bind\n" " --port=PORT\t\tThe port to listen on\n" @@ -605,7 +731,7 @@ usage(int error_code) #endif #if defined(BUILD_WAYLAND_COMPOSITOR) - fprintf(stderr, + fprintf(out, "Options for wayland-backend.so:\n\n" " --width=WIDTH\t\tWidth of Wayland surface\n" " --height=HEIGHT\tHeight of Wayland surface\n" @@ -618,7 +744,7 @@ usage(int error_code) #endif #if defined(BUILD_X11_COMPOSITOR) - fprintf(stderr, + fprintf(out, "Options for x11-backend.so:\n\n" " --width=WIDTH\t\tWidth of X window\n" " --height=HEIGHT\tHeight of X window\n" @@ -642,38 +768,6 @@ static int on_term_signal(int signal_number, void *data) return 1; } -static void -on_caught_signal(int s, siginfo_t *siginfo, void *context) -{ - /* This signal handler will do a best-effort backtrace, and - * then call the backend restore function, which will switch - * back to the vt we launched from or ungrab X etc and then - * raise SIGTRAP. If we run weston under gdb from X or a - * different vt, and tell gdb "handle *s* nostop", this - * will allow weston to switch back to gdb on crash and then - * gdb will catch the crash with SIGTRAP.*/ - - weston_log("caught signal: %d\n", s); - - print_backtrace(); - - segv_compositor->backend->restore(segv_compositor); - - raise(SIGTRAP); -} - -static void -catch_signals(void) -{ - struct sigaction action; - - action.sa_flags = SA_SIGINFO | SA_RESETHAND; - action.sa_sigaction = on_caught_signal; - sigemptyset(&action.sa_mask); - sigaction(SIGSEGV, &action, NULL); - sigaction(SIGABRT, &action, NULL); -} - static const char * clock_name(clockid_t clk_id) { @@ -745,13 +839,15 @@ weston_create_listening_socket(struct wl_display *display, const char *socket_na { if (socket_name) { if (wl_display_add_socket(display, socket_name)) { - weston_log("fatal: failed to add socket: %m\n"); + weston_log("fatal: failed to add socket: %s\n", + strerror(errno)); return -1; } } else { socket_name = wl_display_add_socket_auto(display); if (!socket_name) { - weston_log("fatal: failed to add socket: %m\n"); + weston_log("fatal: failed to add socket: %s\n", + strerror(errno)); return -1; } } @@ -764,7 +860,6 @@ weston_create_listening_socket(struct wl_display *display, const char *socket_na WL_EXPORT void * wet_load_module_entrypoint(const char *name, const char *entrypoint) { - const char *builddir = getenv("WESTON_BUILD_DIR"); char path[PATH_MAX]; void *module, *init; size_t len; @@ -773,10 +868,8 @@ wet_load_module_entrypoint(const char *name, const char *entrypoint) return NULL; if (name[0] != '/') { - if (builddir) - len = snprintf(path, sizeof path, "%s/.libs/%s", builddir, - name); - else + len = weston_module_path_from_env(name, path, sizeof path); + if (len == 0) len = snprintf(path, sizeof path, "%s/%s", MODULEDIR, name); } else { @@ -792,15 +885,13 @@ wet_load_module_entrypoint(const char *name, const char *entrypoint) module = dlopen(path, RTLD_NOW | RTLD_NOLOAD); if (module) { weston_log("Module '%s' already loaded\n", path); - dlclose(module); - return NULL; - } - - weston_log("Loading module '%s'\n", path); - module = dlopen(path, RTLD_NOW); - if (!module) { - weston_log("Failed to load module: %s\n", dlerror()); - return NULL; + } else { + weston_log("Loading module '%s'\n", path); + module = dlopen(path, RTLD_NOW); + if (!module) { + weston_log("Failed to load module: %s\n", dlerror()); + return NULL; + } } init = dlsym(module, entrypoint); @@ -813,7 +904,6 @@ wet_load_module_entrypoint(const char *name, const char *entrypoint) return init; } - WL_EXPORT int wet_load_module(struct weston_compositor *compositor, const char *name, int *argc, char *argv[]) @@ -844,9 +934,38 @@ wet_load_shell(struct weston_compositor *compositor, return 0; } +static char * +wet_get_binary_path(const char *name, const char *dir) +{ + char path[PATH_MAX]; + size_t len; + + len = weston_module_path_from_env(name, path, sizeof path); + if (len > 0) + return strdup(path); + + len = snprintf(path, sizeof path, "%s/%s", dir, name); + if (len >= sizeof path) + return NULL; + + return strdup(path); +} + +WL_EXPORT char * +wet_get_libexec_path(const char *name) +{ + return wet_get_binary_path(name, LIBEXECDIR); +} + +WL_EXPORT char * +wet_get_bindir_path(const char *name) +{ + return wet_get_binary_path(name, BINDIR); +} + static int load_modules(struct weston_compositor *ec, const char *modules, - int *argc, char *argv[], int32_t *xwayland) + int *argc, char *argv[]) { const char *p, *end; char buffer[256]; @@ -860,11 +979,11 @@ load_modules(struct weston_compositor *ec, const char *modules, snprintf(buffer, sizeof buffer, "%.*s", (int) (end - p), p); if (strstr(buffer, "xwayland.so")) { - weston_log("Old Xwayland module loading detected: " + weston_log("fatal: Old Xwayland module loading detected: " "Please use --xwayland command line option " "or set xwayland=true in the [core] section " "in weston.ini\n"); - *xwayland = 1; + return -1; } else { if (wet_load_module(ec, buffer, argc, argv) < 0) return -1; @@ -878,6 +997,64 @@ load_modules(struct weston_compositor *ec, const char *modules, return 0; } +static int +save_touch_device_calibration(struct weston_compositor *compositor, + struct weston_touch_device *device, + const struct weston_touch_device_matrix *calibration) +{ + struct weston_config_section *s; + struct weston_config *config = wet_get_config(compositor); + char *helper = NULL; + char *helper_cmd = NULL; + int ret = -1; + int status; + const float *m = calibration->m; + + s = weston_config_get_section(config, + "libinput", NULL, NULL); + + weston_config_section_get_string(s, "calibration_helper", + &helper, NULL); + + if (!helper || strlen(helper) == 0) { + ret = 0; + goto out; + } + + if (asprintf(&helper_cmd, "\"%s\" '%s' %f %f %f %f %f %f", + helper, device->syspath, + m[0], m[1], m[2], + m[3], m[4], m[5]) < 0) + goto out; + + status = system(helper_cmd); + free(helper_cmd); + + if (status < 0) { + weston_log("Error: failed to run calibration helper '%s'.\n", + helper); + goto out; + } + + if (!WIFEXITED(status)) { + weston_log("Error: calibration helper '%s' possibly killed.\n", + helper); + goto out; + } + + if (WEXITSTATUS(status) == 0) { + ret = 0; + } else { + weston_log("Calibration helper '%s' exited with status %d.\n", + helper, WEXITSTATUS(status)); + } + +out: + free(helper); + + return ret; +} + static int weston_compositor_init_config(struct weston_compositor *ec, struct weston_config *config) @@ -885,8 +1062,9 @@ weston_compositor_init_config(struct weston_compositor *ec, struct xkb_rule_names xkb_names; struct weston_config_section *s; int repaint_msec; - int vt_switching; + bool cal; + /* weston.ini [keyboard] */ s = weston_config_get_section(config, "keyboard", NULL, NULL); weston_config_section_get_string(s, "keymap_rules", (char **) &xkb_names.rules, NULL); @@ -908,9 +1086,9 @@ weston_compositor_init_config(struct weston_compositor *ec, &ec->kb_repeat_delay, 400); weston_config_section_get_bool(s, "vt-switching", - &vt_switching, true); - ec->vt_switching = vt_switching; + &ec->vt_switching, true); + /* weston.ini [core] */ s = weston_config_get_section(config, "core", NULL, NULL); weston_config_section_get_int(s, "repaint-window", &repaint_msec, ec->repaint_msec); @@ -923,6 +1101,13 @@ weston_compositor_init_config(struct weston_compositor *ec, weston_log("Output repaint window is %d ms maximum.\n", ec->repaint_msec); + /* weston.ini [libinput] */ + s = weston_config_get_section(config, "libinput", NULL, NULL); + weston_config_section_get_bool(s, "touchscreen_calibrator", &cal, 0); + if (cal) + weston_compositor_enable_touch_calibrator(ec, + save_touch_device_calibration); + return 0; } @@ -942,14 +1127,14 @@ weston_choose_default_backend(void) } static const struct { const char *name; uint32_t token; } transforms[] = { - { "normal", WL_OUTPUT_TRANSFORM_NORMAL }, - { "90", WL_OUTPUT_TRANSFORM_90 }, - { "180", WL_OUTPUT_TRANSFORM_180 }, - { "270", WL_OUTPUT_TRANSFORM_270 }, - { "flipped", WL_OUTPUT_TRANSFORM_FLIPPED }, - { "flipped-90", WL_OUTPUT_TRANSFORM_FLIPPED_90 }, - { "flipped-180", WL_OUTPUT_TRANSFORM_FLIPPED_180 }, - { "flipped-270", WL_OUTPUT_TRANSFORM_FLIPPED_270 }, + { "normal", WL_OUTPUT_TRANSFORM_NORMAL }, + { "rotate-90", WL_OUTPUT_TRANSFORM_90 }, + { "rotate-180", WL_OUTPUT_TRANSFORM_180 }, + { "rotate-270", WL_OUTPUT_TRANSFORM_270 }, + { "flipped", WL_OUTPUT_TRANSFORM_FLIPPED }, + { "flipped-rotate-90", WL_OUTPUT_TRANSFORM_FLIPPED_90 }, + { "flipped-rotate-180", WL_OUTPUT_TRANSFORM_FLIPPED_180 }, + { "flipped-rotate-270", WL_OUTPUT_TRANSFORM_FLIPPED_270 }, }; WL_EXPORT int @@ -1042,23 +1227,25 @@ wet_output_set_scale(struct weston_output *output, /* UINT32_MAX is treated as invalid because 0 is a valid * enumeration value and the parameter is unsigned */ -static void +static int wet_output_set_transform(struct weston_output *output, struct weston_config_section *section, uint32_t default_transform, uint32_t parsed_transform) { - char *t; + char *t = NULL; uint32_t transform = default_transform; if (section) { weston_config_section_get_string(section, - "transform", &t, "normal"); + "transform", &t, NULL); + } + if (t) { if (weston_parse_transform(t, &transform) < 0) { weston_log("Invalid transform \"%s\" for output %s\n", t, output->name); - transform = default_transform; + return -1; } free(t); } @@ -1067,6 +1254,21 @@ wet_output_set_transform(struct weston_output *output, transform = parsed_transform; weston_output_set_transform(output, transform); + + return 0; +} + +static void +allow_content_protection(struct weston_output *output, + struct weston_config_section *section) +{ + bool allow_hdcp = true; + + if (section) + weston_config_section_get_bool(section, "allow_hdcp", + &allow_hdcp, true); + + weston_output_allow_protection(output, allow_hdcp); } static int @@ -1106,6 +1308,8 @@ wet_configure_windowed_output_from_config(struct weston_output *output, free(mode); } + allow_content_protection(output, section); + if (parsed_options->width) width = parsed_options->width; @@ -1113,7 +1317,10 @@ wet_configure_windowed_output_from_config(struct weston_output *output, height = parsed_options->height; wet_output_set_scale(output, section, defaults->scale, parsed_options->scale); - wet_output_set_transform(output, section, defaults->transform, parsed_options->transform); + if (wet_output_set_transform(output, section, defaults->transform, + parsed_options->transform) < 0) { + return -1; + } if (api->output_set_size(output, width, height) < 0) { weston_log("Cannot configure output \"%s\" using weston_windowed_output_api.\n", @@ -1121,112 +1328,1251 @@ wet_configure_windowed_output_from_config(struct weston_output *output, return -1; } - weston_output_enable(output); - return 0; } -static void -configure_input_device(struct weston_compositor *compositor, - struct libinput_device *device) +static int +count_remaining_heads(struct weston_output *output, struct weston_head *to_go) { - struct weston_config_section *s; - struct weston_config *config = wet_get_config(compositor); - int enable_tap; - int enable_tap_default; + struct weston_head *iter = NULL; + int n = 0; - s = weston_config_get_section(config, - "libinput", NULL, NULL); - - if (libinput_device_config_tap_get_finger_count(device) > 0) { - enable_tap_default = - libinput_device_config_tap_get_default_enabled( - device); - weston_config_section_get_bool(s, "enable_tap", - &enable_tap, - enable_tap_default); - libinput_device_config_tap_set_enabled(device, - enable_tap); + while ((iter = weston_output_iterate_heads(output, iter))) { + if (iter != to_go) + n++; } + + return n; } static void -drm_backend_output_configure(struct wl_listener *listener, void *data) +wet_head_tracker_destroy(struct wet_head_tracker *track) { - struct weston_output *output = data; - struct weston_config *wc = wet_get_config(output->compositor); - struct wet_compositor *wet = to_wet_compositor(output->compositor); - struct weston_config_section *section; - const struct weston_drm_output_api *api = weston_drm_output_get_api(output->compositor); - enum weston_drm_backend_output_mode mode = - WESTON_DRM_BACKEND_OUTPUT_PREFERRED; + wl_list_remove(&track->head_destroy_listener.link); + free(track); +} - char *s; - char *modeline = NULL; - char *gbm_format = NULL; - char *seat = NULL; +static void +handle_head_destroy(struct wl_listener *listener, void *data) +{ + struct weston_head *head = data; + struct weston_output *output; + struct wet_head_tracker *track = + container_of(listener, struct wet_head_tracker, + head_destroy_listener); - if (!api) { - weston_log("Cannot use weston_drm_output_api.\n"); - return; - } + wet_head_tracker_destroy(track); - section = weston_config_get_section(wc, "output", "name", output->name); - weston_config_section_get_string(section, "mode", &s, "preferred"); + output = weston_head_get_output(head); - if (strcmp(s, "off") == 0) { - weston_output_disable(output); - free(s); + /* On shutdown path, the output might be already gone. */ + if (!output) return; - } else if (wet->drm_use_current_mode || strcmp(s, "current") == 0) { - mode = WESTON_DRM_BACKEND_OUTPUT_CURRENT; - } else if (strcmp(s, "preferred") != 0) { - modeline = s; - s = NULL; - } - free(s); - if (api->set_mode(output, mode, modeline) < 0) { - weston_log("Cannot configure an output using weston_drm_output_api.\n"); - free(modeline); + if (count_remaining_heads(output, head) > 0) return; - } - free(modeline); - wet_output_set_scale(output, section, 1, 0); - wet_output_set_transform(output, section, WL_OUTPUT_TRANSFORM_NORMAL, UINT32_MAX); - - weston_config_section_get_string(section, - "gbm-format", &gbm_format, NULL); - - api->set_gbm_format(output, gbm_format); - free(gbm_format); + weston_output_destroy(output); +} - weston_config_section_get_string(section, "seat", &seat, ""); +static struct wet_head_tracker * +wet_head_tracker_from_head(struct weston_head *head) +{ + struct wl_listener *lis; - api->set_seat(output, seat); - free(seat); + lis = weston_head_get_destroy_listener(head, handle_head_destroy); + if (!lis) + return NULL; - weston_output_enable(output); + return container_of(lis, struct wet_head_tracker, + head_destroy_listener); } -static int -load_drm_backend(struct weston_compositor *c, - int *argc, char **argv, struct weston_config *wc) +/* Listen for head destroy signal. + * + * If a head is destroyed and it was the last head on the output, we + * destroy the associated output. + * + * Do not bother destroying the head trackers on shutdown, the backend will + * destroy the heads which calls our handler to destroy the trackers. + */ +static void +wet_head_tracker_create(struct wet_compositor *compositor, + struct weston_head *head) { - struct weston_drm_backend_config config = {{ 0, }}; - struct weston_config_section *section; - struct wet_compositor *wet = to_wet_compositor(c); - int ret = 0; + struct wet_head_tracker *track; + + track = zalloc(sizeof *track); + if (!track) + return; + + track->head_destroy_listener.notify = handle_head_destroy; + weston_head_add_destroy_listener(head, &track->head_destroy_listener); +} + +/* Place output exactly to the right of the most recently enabled output. + * + * Historically, we haven't given much thought to output placement, + * simply adding outputs in a horizontal line as they're enabled. This + * function simply sets an output's x coordinate to the right of the + * most recently enabled output, and its y to zero. + * + * If you're adding new calls to this function, you're also not giving + * much thought to output placement, so please consider carefully if + * it's really doing what you want. + * + * You especially don't want to use this for any code that won't + * immediately enable the passed output. + */ +static void +weston_output_lazy_align(struct weston_output *output) +{ + struct weston_compositor *c; + struct weston_output *peer; + int next_x = 0; + + /* Put this output to the right of the most recently enabled output */ + c = output->compositor; + if (!wl_list_empty(&c->output_list)) { + peer = container_of(c->output_list.prev, + struct weston_output, link); + next_x = peer->x + peer->width; + } + output->x = next_x; + output->y = 0; +} + +static void +simple_head_enable(struct wet_compositor *wet, struct weston_head *head) +{ + struct weston_output *output; + int ret = 0; + + output = weston_compositor_create_output_with_head(wet->compositor, + head); + if (!output) { + weston_log("Could not create an output for head \"%s\".\n", + weston_head_get_name(head)); + wet->init_failed = true; + + return; + } + + weston_output_lazy_align(output); + + if (wet->simple_output_configure) + ret = wet->simple_output_configure(output); + if (ret < 0) { + weston_log("Cannot configure output \"%s\".\n", + weston_head_get_name(head)); + weston_output_destroy(output); + wet->init_failed = true; + + return; + } + + if (weston_output_enable(output) < 0) { + weston_log("Enabling output \"%s\" failed.\n", + weston_head_get_name(head)); + weston_output_destroy(output); + wet->init_failed = true; + + return; + } + + wet_head_tracker_create(wet, head); + + /* The weston_compositor will track and destroy the output on exit. */ +} + +static void +simple_head_disable(struct weston_head *head) +{ + struct weston_output *output; + struct wet_head_tracker *track; + + track = wet_head_tracker_from_head(head); + if (track) + wet_head_tracker_destroy(track); + + output = weston_head_get_output(head); + assert(output); + weston_output_destroy(output); +} + +static void +simple_heads_changed(struct wl_listener *listener, void *arg) +{ + struct weston_compositor *compositor = arg; + struct wet_compositor *wet = to_wet_compositor(compositor); + struct weston_head *head = NULL; + bool connected; + bool enabled; + bool changed; + bool non_desktop; + + while ((head = weston_compositor_iterate_heads(wet->compositor, head))) { + connected = weston_head_is_connected(head); + enabled = weston_head_is_enabled(head); + changed = weston_head_is_device_changed(head); + non_desktop = weston_head_is_non_desktop(head); + + if (connected && !enabled && !non_desktop) { + simple_head_enable(wet, head); + } else if (!connected && enabled) { + simple_head_disable(head); + } else if (enabled && changed) { + weston_log("Detected a monitor change on head '%s', " + "not bothering to do anything about it.\n", + weston_head_get_name(head)); + } + weston_head_reset_device_changed(head); + } +} + +static void +wet_set_simple_head_configurator(struct weston_compositor *compositor, + int (*fn)(struct weston_output *)) +{ + struct wet_compositor *wet = to_wet_compositor(compositor); + + wet->simple_output_configure = fn; + + wet->heads_changed_listener.notify = simple_heads_changed; + weston_compositor_add_heads_changed_listener(compositor, + &wet->heads_changed_listener); +} + +static void +configure_input_device_accel(struct weston_config_section *s, + struct libinput_device *device) +{ + char *profile_string = NULL; + int is_a_profile = 1; + uint32_t profiles; + enum libinput_config_accel_profile profile; + double speed; + + if (weston_config_section_get_string(s, "accel-profile", + &profile_string, NULL) == 0) { + if (strcmp(profile_string, "flat") == 0) + profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; + else if (strcmp(profile_string, "adaptive") == 0) + profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; + else { + weston_log("warning: no such accel-profile: %s\n", + profile_string); + is_a_profile = 0; + } + + profiles = libinput_device_config_accel_get_profiles(device); + if (is_a_profile && (profile & profiles) != 0) { + weston_log(" accel-profile=%s\n", + profile_string); + libinput_device_config_accel_set_profile(device, + profile); + } + } + + if (weston_config_section_get_double(s, "accel-speed", + &speed, 0) == 0 && + speed >= -1. && speed <= 1.) { + weston_log(" accel-speed=%.3f\n", speed); + libinput_device_config_accel_set_speed(device, speed); + } + + free(profile_string); +} + +static void +configure_input_device_scroll(struct weston_config_section *s, + struct libinput_device *device) +{ + bool natural; + char *method_string = NULL; + uint32_t methods; + enum libinput_config_scroll_method method; + char *button_string = NULL; + int button; + + if (libinput_device_config_scroll_has_natural_scroll(device) && + weston_config_section_get_bool(s, "natural-scroll", + &natural, false) == 0) { + weston_log(" natural-scroll=%s\n", + natural ? "true" : "false"); + libinput_device_config_scroll_set_natural_scroll_enabled( + device, natural); + } + + if (weston_config_section_get_string(s, "scroll-method", + &method_string, NULL) != 0) + goto done; + if (strcmp(method_string, "two-finger") == 0) + method = LIBINPUT_CONFIG_SCROLL_2FG; + else if (strcmp(method_string, "edge") == 0) + method = LIBINPUT_CONFIG_SCROLL_EDGE; + else if (strcmp(method_string, "button") == 0) + method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; + else if (strcmp(method_string, "none") == 0) + method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL; + else { + weston_log("warning: no such scroll-method: %s\n", + method_string); + goto done; + } + + methods = libinput_device_config_scroll_get_methods(device); + if (method != LIBINPUT_CONFIG_SCROLL_NO_SCROLL && + (method & methods) == 0) + goto done; + + weston_log(" scroll-method=%s\n", method_string); + libinput_device_config_scroll_set_method(device, method); + + if (method == LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) { + if (weston_config_section_get_string(s, "scroll-button", + &button_string, + NULL) != 0) + goto done; + + button = libevdev_event_code_from_name(EV_KEY, button_string); + if (button == -1) { + weston_log(" Bad scroll-button: %s\n", + button_string); + goto done; + } + + weston_log(" scroll-button=%s\n", button_string); + libinput_device_config_scroll_set_button(device, button); + } + +done: + free(method_string); + free(button_string); +} + +static void +configure_input_device(struct weston_compositor *compositor, + struct libinput_device *device) +{ + struct weston_config_section *s; + struct weston_config *config = wet_get_config(compositor); + bool has_enable_tap = false; + bool enable_tap; + bool disable_while_typing; + bool middle_emulation; + bool tap_and_drag; + bool tap_and_drag_lock; + bool left_handed; + unsigned int rotation; + + weston_log("libinput: configuring device \"%s\".\n", + libinput_device_get_name(device)); + + s = weston_config_get_section(config, + "libinput", NULL, NULL); + + if (libinput_device_config_tap_get_finger_count(device) > 0) { + if (weston_config_section_get_bool(s, "enable_tap", + &enable_tap, false) == 0) { + weston_log("!!DEPRECATION WARNING!!: In weston.ini, " + "enable_tap is deprecated in favour of " + "enable-tap. Support for it may be removed " + "at any time!"); + has_enable_tap = true; + } + if (weston_config_section_get_bool(s, "enable-tap", + &enable_tap, false) == 0) + has_enable_tap = true; + if (has_enable_tap) { + weston_log(" enable-tap=%s.\n", + enable_tap ? "true" : "false"); + libinput_device_config_tap_set_enabled(device, + enable_tap); + } + if (weston_config_section_get_bool(s, "tap-and-drag", + &tap_and_drag, false) == 0) { + weston_log(" tap-and-drag=%s.\n", + tap_and_drag ? "true" : "false"); + libinput_device_config_tap_set_drag_enabled(device, + tap_and_drag); + } + if (weston_config_section_get_bool(s, "tap-and-drag-lock", + &tap_and_drag_lock, false) == 0) { + weston_log(" tap-and-drag-lock=%s.\n", + tap_and_drag_lock ? "true" : "false"); + libinput_device_config_tap_set_drag_lock_enabled( + device, tap_and_drag_lock); + } + } + + if (libinput_device_config_dwt_is_available(device) && + weston_config_section_get_bool(s, "disable-while-typing", + &disable_while_typing, false) == 0) { + weston_log(" disable-while-typing=%s.\n", + disable_while_typing ? "true" : "false"); + libinput_device_config_dwt_set_enabled(device, + disable_while_typing); + } + + if (libinput_device_config_middle_emulation_is_available(device) && + weston_config_section_get_bool(s, "middle-button-emulation", + &middle_emulation, false) == 0) { + weston_log(" middle-button-emulation=%s\n", + middle_emulation ? "true" : "false"); + libinput_device_config_middle_emulation_set_enabled( + device, middle_emulation); + } + + if (libinput_device_config_left_handed_is_available(device) && + weston_config_section_get_bool(s, "left-handed", + &left_handed, false) == 0) { + weston_log(" left-handed=%s\n", + left_handed ? "true" : "false"); + libinput_device_config_left_handed_set(device, left_handed); + } + + if (libinput_device_config_rotation_is_available(device) && + weston_config_section_get_uint(s, "rotation", + &rotation, false) == 0) { + weston_log(" rotation=%u\n", rotation); + libinput_device_config_rotation_set_angle(device, rotation); + } + + if (libinput_device_config_accel_is_available(device)) + configure_input_device_accel(s, device); + + configure_input_device_scroll(s, device); +} + +static int +drm_backend_output_configure(struct weston_output *output, + struct weston_config_section *section) +{ + struct wet_compositor *wet = to_wet_compositor(output->compositor); + const struct weston_drm_output_api *api; + enum weston_drm_backend_output_mode mode = + WESTON_DRM_BACKEND_OUTPUT_PREFERRED; + uint32_t transform = WL_OUTPUT_TRANSFORM_NORMAL; + char *s; + char *modeline = NULL; + char *gbm_format = NULL; + char *seat = NULL; + + api = weston_drm_output_get_api(output->compositor); + if (!api) { + weston_log("Cannot use weston_drm_output_api.\n"); + return -1; + } + + weston_config_section_get_string(section, "mode", &s, "preferred"); + + if (strcmp(s, "off") == 0) { + assert(0 && "off was supposed to be pruned"); + return -1; + } else if (wet->drm_use_current_mode || strcmp(s, "current") == 0) { + mode = WESTON_DRM_BACKEND_OUTPUT_CURRENT; + } else if (strcmp(s, "preferred") != 0) { + modeline = s; + s = NULL; + } + free(s); + + if (api->set_mode(output, mode, modeline) < 0) { + weston_log("Cannot configure an output using weston_drm_output_api.\n"); + free(modeline); + return -1; + } + free(modeline); + + if (count_remaining_heads(output, NULL) == 1) { + struct weston_head *head = weston_output_get_first_head(output); + transform = weston_head_get_transform(head); + } + + wet_output_set_scale(output, section, 1, 0); + if (wet_output_set_transform(output, section, transform, + UINT32_MAX) < 0) { + return -1; + } + + weston_config_section_get_string(section, + "gbm-format", &gbm_format, NULL); + + api->set_gbm_format(output, gbm_format); + free(gbm_format); + + weston_config_section_get_string(section, "seat", &seat, ""); + + api->set_seat(output, seat); + free(seat); + + allow_content_protection(output, section); + + return 0; +} + +/* Find the output section to use for configuring the output with the + * named head. If an output section with the given name contains + * a "same-as" key, ignore all other settings in the output section and + * instead find an output section named by the "same-as". Do this + * recursively. + */ +static struct weston_config_section * +drm_config_find_controlling_output_section(struct weston_config *config, + const char *head_name) +{ + struct weston_config_section *section; + char *same_as; + int depth = 0; + + same_as = strdup(head_name); + do { + section = weston_config_get_section(config, "output", + "name", same_as); + if (!section && depth > 0) + weston_log("Configuration error: " + "output section referred to with " + "'same-as=%s' not found.\n", same_as); + + free(same_as); + + if (!section) + return NULL; + + if (++depth > 10) { + weston_log("Configuration error: " + "'same-as' nested too deep for output '%s'.\n", + head_name); + return NULL; + } + + weston_config_section_get_string(section, "same-as", + &same_as, NULL); + } while (same_as); + + return section; +} + +static struct wet_layoutput * +wet_compositor_create_layoutput(struct wet_compositor *compositor, + const char *name, + struct weston_config_section *section) +{ + struct wet_layoutput *lo; + + lo = zalloc(sizeof *lo); + if (!lo) + return NULL; + + lo->compositor = compositor; + wl_list_insert(compositor->layoutput_list.prev, &lo->compositor_link); + wl_list_init(&lo->output_list); + lo->name = strdup(name); + lo->section = section; + + return lo; +} + +static void +wet_layoutput_destroy(struct wet_layoutput *lo) +{ + wl_list_remove(&lo->compositor_link); + assert(wl_list_empty(&lo->output_list)); + free(lo->name); + free(lo); +} + +static void +wet_output_handle_destroy(struct wl_listener *listener, void *data) +{ + struct wet_output *output; + + output = wl_container_of(listener, output, output_destroy_listener); + assert(output->output == data); + + output->output = NULL; + wl_list_remove(&output->output_destroy_listener.link); +} + +static struct wet_output * +wet_layoutput_create_output(struct wet_layoutput *lo, const char *name) +{ + struct wet_output *output; + + output = zalloc(sizeof *output); + if (!output) + return NULL; + + output->output = + weston_compositor_create_output(lo->compositor->compositor, + name); + if (!output->output) { + free(output); + return NULL; + } + + output->layoutput = lo; + wl_list_insert(lo->output_list.prev, &output->link); + output->output_destroy_listener.notify = wet_output_handle_destroy; + weston_output_add_destroy_listener(output->output, + &output->output_destroy_listener); + + return output; +} + +static struct wet_output * +wet_output_from_weston_output(struct weston_output *base) +{ + struct wl_listener *lis; + + lis = weston_output_get_destroy_listener(base, + wet_output_handle_destroy); + if (!lis) + return NULL; + + return container_of(lis, struct wet_output, output_destroy_listener); +} + +static void +wet_output_destroy(struct wet_output *output) +{ + if (output->output) { + /* output->output destruction may be deferred in some cases (see + * drm_output_destroy()), so we need to forcibly trigger the + * destruction callback now, or otherwise would later access + * data that we are about to free + */ + struct weston_output *save = output->output; + wet_output_handle_destroy(&output->output_destroy_listener, save); + weston_output_destroy(save); + } + + wl_list_remove(&output->link); + free(output); +} + +static struct wet_layoutput * +wet_compositor_find_layoutput(struct wet_compositor *wet, const char *name) +{ + struct wet_layoutput *lo; + + wl_list_for_each(lo, &wet->layoutput_list, compositor_link) + if (strcmp(lo->name, name) == 0) + return lo; + + return NULL; +} + +static void +wet_compositor_layoutput_add_head(struct wet_compositor *wet, + const char *output_name, + struct weston_config_section *section, + struct weston_head *head) +{ + struct wet_layoutput *lo; + + lo = wet_compositor_find_layoutput(wet, output_name); + if (!lo) { + lo = wet_compositor_create_layoutput(wet, output_name, section); + if (!lo) + return; + } + + if (lo->add.n + 1 >= ARRAY_LENGTH(lo->add.heads)) + return; + + lo->add.heads[lo->add.n++] = head; +} + +static void +wet_compositor_destroy_layout(struct wet_compositor *wet) +{ + struct wet_layoutput *lo, *lo_tmp; + struct wet_output *output, *output_tmp; + + wl_list_for_each_safe(lo, lo_tmp, + &wet->layoutput_list, compositor_link) { + wl_list_for_each_safe(output, output_tmp, + &lo->output_list, link) { + wet_output_destroy(output); + } + wet_layoutput_destroy(lo); + } +} + +static void +drm_head_prepare_enable(struct wet_compositor *wet, + struct weston_head *head) +{ + const char *name = weston_head_get_name(head); + struct weston_config_section *section; + char *output_name = NULL; + char *mode = NULL; + + section = drm_config_find_controlling_output_section(wet->config, name); + if (section) { + /* skip outputs that are explicitly off, or non-desktop and not + * explicitly enabled. The backend turns them off automatically. + */ + weston_config_section_get_string(section, "mode", &mode, NULL); + if (mode && strcmp(mode, "off") == 0) { + free(mode); + return; + } + if (!mode && weston_head_is_non_desktop(head)) + return; + free(mode); + + weston_config_section_get_string(section, "name", + &output_name, NULL); + assert(output_name); + + wet_compositor_layoutput_add_head(wet, output_name, + section, head); + free(output_name); + } else { + wet_compositor_layoutput_add_head(wet, name, NULL, head); + } +} + +static bool +drm_head_should_force_enable(struct wet_compositor *wet, + struct weston_head *head) +{ + const char *name = weston_head_get_name(head); + struct weston_config_section *section; + bool force; + + section = drm_config_find_controlling_output_section(wet->config, name); + if (!section) + return false; + + weston_config_section_get_bool(section, "force-on", &force, false); + return force; +} + +static void +drm_try_attach(struct weston_output *output, + struct wet_head_array *add, + struct wet_head_array *failed) +{ + unsigned i; + + /* try to attach all heads, this probably succeeds */ + for (i = 0; i < add->n; i++) { + if (!add->heads[i]) + continue; + + if (weston_output_attach_head(output, add->heads[i]) < 0) { + assert(failed->n < ARRAY_LENGTH(failed->heads)); + + failed->heads[failed->n++] = add->heads[i]; + add->heads[i] = NULL; + } + } +} + +static int +drm_try_enable(struct weston_output *output, + struct wet_head_array *undo, + struct wet_head_array *failed) +{ + /* Try to enable, and detach heads one by one until it succeeds. */ + while (!output->enabled) { + weston_output_lazy_align(output); + + if (weston_output_enable(output) == 0) + return 0; + + /* the next head to drop */ + while (undo->n > 0 && undo->heads[--undo->n] == NULL) + ; + + /* No heads left to undo and failed to enable. */ + if (undo->heads[undo->n] == NULL) + return -1; + + assert(failed->n < ARRAY_LENGTH(failed->heads)); + + /* undo one head */ + weston_head_detach(undo->heads[undo->n]); + failed->heads[failed->n++] = undo->heads[undo->n]; + undo->heads[undo->n] = NULL; + } + + return 0; +} + +static int +drm_try_attach_enable(struct weston_output *output, struct wet_layoutput *lo) +{ + struct wet_head_array failed = {}; + unsigned i; + + assert(!output->enabled); + + drm_try_attach(output, &lo->add, &failed); + if (drm_backend_output_configure(output, lo->section) < 0) + return -1; + + if (drm_try_enable(output, &lo->add, &failed) < 0) + return -1; + + /* For all successfully attached/enabled heads */ + for (i = 0; i < lo->add.n; i++) + if (lo->add.heads[i]) + wet_head_tracker_create(lo->compositor, + lo->add.heads[i]); + + /* Push failed heads to the next round. */ + lo->add = failed; + + return 0; +} + +static int +drm_process_layoutput(struct wet_compositor *wet, struct wet_layoutput *lo) +{ + struct wet_output *output, *tmp; + char *name = NULL; + int ret; + + /* + * For each existing wet_output: + * try attach + * While heads left to enable: + * Create output + * try attach, try enable + */ + + wl_list_for_each_safe(output, tmp, &lo->output_list, link) { + struct wet_head_array failed = {}; + + if (!output->output) { + /* Clean up left-overs from destroyed heads. */ + wet_output_destroy(output); + continue; + } + + assert(output->output->enabled); + + drm_try_attach(output->output, &lo->add, &failed); + lo->add = failed; + if (lo->add.n == 0) + return 0; + } + + if (!weston_compositor_find_output_by_name(wet->compositor, lo->name)) + name = strdup(lo->name); + + while (lo->add.n > 0) { + if (!wl_list_empty(&lo->output_list)) { + weston_log("Error: independent-CRTC clone mode is not implemented.\n"); + return -1; + } + + if (!name) { + ret = asprintf(&name, "%s:%s", lo->name, + weston_head_get_name(lo->add.heads[0])); + if (ret < 0) + return -1; + } + output = wet_layoutput_create_output(lo, name); + free(name); + name = NULL; + + if (!output) + return -1; + + if (drm_try_attach_enable(output->output, lo) < 0) { + wet_output_destroy(output); + return -1; + } + } + + return 0; +} + +static int +drm_process_layoutputs(struct wet_compositor *wet) +{ + struct wet_layoutput *lo; + int ret = 0; + + wl_list_for_each(lo, &wet->layoutput_list, compositor_link) { + if (lo->add.n == 0) + continue; + + if (drm_process_layoutput(wet, lo) < 0) { + lo->add = (struct wet_head_array){}; + ret = -1; + } + } + + return ret; +} + +static void +drm_head_disable(struct weston_head *head) +{ + struct weston_output *output_base; + struct wet_output *output; + struct wet_head_tracker *track; + + track = wet_head_tracker_from_head(head); + if (track) + wet_head_tracker_destroy(track); + + output_base = weston_head_get_output(head); + assert(output_base); + output = wet_output_from_weston_output(output_base); + assert(output && output->output == output_base); + + weston_head_detach(head); + if (count_remaining_heads(output->output, NULL) == 0) + wet_output_destroy(output); +} + +static void +drm_heads_changed(struct wl_listener *listener, void *arg) +{ + struct weston_compositor *compositor = arg; + struct wet_compositor *wet = to_wet_compositor(compositor); + struct weston_head *head = NULL; + bool connected; + bool enabled; + bool changed; + bool forced; + + /* We need to collect all cloned heads into outputs before enabling the + * output. + */ + while ((head = weston_compositor_iterate_heads(compositor, head))) { + connected = weston_head_is_connected(head); + enabled = weston_head_is_enabled(head); + changed = weston_head_is_device_changed(head); + forced = drm_head_should_force_enable(wet, head); + + if ((connected || forced) && !enabled) { + drm_head_prepare_enable(wet, head); + } else if (!(connected || forced) && enabled) { + drm_head_disable(head); + } else if (enabled && changed) { + weston_log("Detected a monitor change on head '%s', " + "not bothering to do anything about it.\n", + weston_head_get_name(head)); + } + weston_head_reset_device_changed(head); + } + + if (drm_process_layoutputs(wet) < 0) + wet->init_failed = true; +} + +static int +drm_backend_remoted_output_configure(struct weston_output *output, + struct weston_config_section *section, + char *modeline, + const struct weston_remoting_api *api) +{ + char *gbm_format = NULL; + char *seat = NULL; + char *host = NULL; + char *pipeline = NULL; + int port, ret; + + ret = api->set_mode(output, modeline); + if (ret < 0) { + weston_log("Cannot configure an output \"%s\" using " + "weston_remoting_api. Invalid mode\n", + output->name); + return -1; + } + + wet_output_set_scale(output, section, 1, 0); + if (wet_output_set_transform(output, section, + WL_OUTPUT_TRANSFORM_NORMAL, + UINT32_MAX) < 0) { + return -1; + }; + + weston_config_section_get_string(section, "gbm-format", &gbm_format, + NULL); + api->set_gbm_format(output, gbm_format); + free(gbm_format); + + weston_config_section_get_string(section, "seat", &seat, ""); + + api->set_seat(output, seat); + free(seat); + + weston_config_section_get_string(section, "gst-pipeline", &pipeline, + NULL); + if (pipeline) { + api->set_gst_pipeline(output, pipeline); + free(pipeline); + return 0; + } + + weston_config_section_get_string(section, "host", &host, NULL); + weston_config_section_get_int(section, "port", &port, 0); + if (!host || port <= 0 || 65533 < port) { + weston_log("Cannot configure an output \"%s\". " + "Need to specify gst-pipeline or " + "host and port (1-65533).\n", output->name); + } + api->set_host(output, host); + free(host); + api->set_port(output, port); + + return 0; +} + +static void +remoted_output_init(struct weston_compositor *c, + struct weston_config_section *section, + const struct weston_remoting_api *api) +{ + struct weston_output *output = NULL; + char *output_name, *modeline = NULL; + int ret; + + weston_config_section_get_string(section, "name", &output_name, + NULL); + if (!output_name) + return; + + weston_config_section_get_string(section, "mode", &modeline, "off"); + if (strcmp(modeline, "off") == 0) + goto err; + + output = api->create_output(c, output_name); + if (!output) { + weston_log("Cannot create remoted output \"%s\".\n", + output_name); + goto err; + } + + ret = drm_backend_remoted_output_configure(output, section, modeline, + api); + if (ret < 0) { + weston_log("Cannot configure remoted output \"%s\".\n", + output_name); + goto err; + } + + if (weston_output_enable(output) < 0) { + weston_log("Enabling remoted output \"%s\" failed.\n", + output_name); + goto err; + } + + free(modeline); + free(output_name); + weston_log("remoted output '%s' enabled\n", output->name); + return; + +err: + free(modeline); + free(output_name); + if (output) + weston_output_destroy(output); +} + +static void +load_remoting(struct weston_compositor *c, struct weston_config *wc) +{ + const struct weston_remoting_api *api = NULL; + int (*module_init)(struct weston_compositor *ec); + struct weston_config_section *section = NULL; + const char *section_name; + + /* read remote-output section in weston.ini */ + while (weston_config_next_section(wc, §ion, §ion_name)) { + if (strcmp(section_name, "remote-output")) + continue; + + if (!api) { + char *module_name; + struct weston_config_section *core_section = + weston_config_get_section(wc, "core", NULL, + NULL); + + weston_config_section_get_string(core_section, + "remoting", + &module_name, + "remoting-plugin.so"); + module_init = weston_load_module(module_name, + "weston_module_init"); + free(module_name); + if (!module_init) { + weston_log("Can't load remoting-plugin\n"); + return; + } + if (module_init(c) < 0) { + weston_log("Remoting-plugin init failed\n"); + return; + } + + api = weston_remoting_get_api(c); + if (!api) + return; + } + + remoted_output_init(c, section, api); + } +} + +static int +drm_backend_pipewire_output_configure(struct weston_output *output, + struct weston_config_section *section, + char *modeline, + const struct weston_pipewire_api *api) +{ + char *seat = NULL; + int ret; + + ret = api->set_mode(output, modeline); + if (ret < 0) { + weston_log("Cannot configure an output \"%s\" using " + "weston_pipewire_api. Invalid mode\n", + output->name); + return -1; + } + + wet_output_set_scale(output, section, 1, 0); + if (wet_output_set_transform(output, section, + WL_OUTPUT_TRANSFORM_NORMAL, + UINT32_MAX) < 0) { + return -1; + } + + weston_config_section_get_string(section, "seat", &seat, ""); + + api->set_seat(output, seat); + free(seat); + + return 0; +} + +static void +pipewire_output_init(struct weston_compositor *c, + struct weston_config_section *section, + const struct weston_pipewire_api *api) +{ + struct weston_output *output = NULL; + char *output_name, *modeline = NULL; + int ret; + + weston_config_section_get_string(section, "name", &output_name, + NULL); + if (!output_name) + return; + + weston_config_section_get_string(section, "mode", &modeline, "off"); + if (strcmp(modeline, "off") == 0) + goto err; + + output = api->create_output(c, output_name); + if (!output) { + weston_log("Cannot create pipewire output \"%s\".\n", + output_name); + goto err; + } + + ret = drm_backend_pipewire_output_configure(output, section, modeline, + api); + if (ret < 0) { + weston_log("Cannot configure pipewire output \"%s\".\n", + output_name); + goto err; + } + + if (weston_output_enable(output) < 0) { + weston_log("Enabling pipewire output \"%s\" failed.\n", + output_name); + goto err; + } + + free(modeline); + free(output_name); + weston_log("pipewire output '%s' enabled\n", output->name); + return; + +err: + free(modeline); + free(output_name); + if (output) + weston_output_destroy(output); +} + +static void +load_pipewire(struct weston_compositor *c, struct weston_config *wc) +{ + const struct weston_pipewire_api *api = NULL; + int (*module_init)(struct weston_compositor *ec); + struct weston_config_section *section = NULL; + const char *section_name; + + /* read pipewire-output section in weston.ini */ + while (weston_config_next_section(wc, §ion, §ion_name)) { + if (strcmp(section_name, "pipewire-output")) + continue; + + if (!api) { + char *module_name; + struct weston_config_section *core_section = + weston_config_get_section(wc, "core", NULL, + NULL); + + weston_config_section_get_string(core_section, + "pipewire", + &module_name, + "pipewire-plugin.so"); + module_init = weston_load_module(module_name, + "weston_module_init"); + free(module_name); + if (!module_init) { + weston_log("Can't load pipewire-plugin\n"); + return; + } + if (module_init(c) < 0) { + weston_log("Pipewire-plugin init failed\n"); + return; + } + + api = weston_pipewire_get_api(c); + if (!api) + return; + } + + pipewire_output_init(c, section, api); + } +} + +static int +load_drm_backend(struct weston_compositor *c, + int *argc, char **argv, struct weston_config *wc) +{ + struct weston_drm_backend_config config = {{ 0, }}; + struct weston_config_section *section; + struct wet_compositor *wet = to_wet_compositor(c); + int ret = 0; wet->drm_use_current_mode = false; + section = weston_config_get_section(wc, "core", NULL, NULL); + weston_config_section_get_bool(section, "use-pixman", &config.use_pixman, + false); + const struct weston_option options[] = { - { WESTON_OPTION_INTEGER, "connector", 0, &config.connector }, { WESTON_OPTION_STRING, "seat", 0, &config.seat_id }, { WESTON_OPTION_INTEGER, "tty", 0, &config.tty }, + { WESTON_OPTION_STRING, "drm-device", 0, &config.specific_device }, { WESTON_OPTION_BOOLEAN, "current-mode", 0, &wet->drm_use_current_mode }, { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &config.use_pixman }, + { WESTON_OPTION_BOOLEAN, "continue-without-input", 0, &config.continue_without_input }, }; parse_options(options, ARRAY_LENGTH(options), argc, argv); @@ -1235,15 +2581,27 @@ load_drm_backend(struct weston_compositor *c, weston_config_section_get_string(section, "gbm-format", &config.gbm_format, NULL); + weston_config_section_get_uint(section, "pageflip-timeout", + &config.pageflip_timeout, 0); + weston_config_section_get_bool(section, "pixman-shadow", + &config.use_pixman_shadow, true); config.base.struct_version = WESTON_DRM_BACKEND_CONFIG_VERSION; config.base.struct_size = sizeof(struct weston_drm_backend_config); config.configure_device = configure_input_device; + wet->heads_changed_listener.notify = drm_heads_changed; + weston_compositor_add_heads_changed_listener(c, + &wet->heads_changed_listener); + ret = weston_compositor_load_backend(c, WESTON_BACKEND_DRM, &config.base); - wet_set_pending_output_handler(c, drm_backend_output_configure); + /* remoting */ + load_remoting(c, wc); + + /* pipewire */ + load_pipewire(c, wc); free(config.gbm_format); free(config.seat_id); @@ -1251,10 +2609,9 @@ load_drm_backend(struct weston_compositor *c, return ret; } -static void -headless_backend_output_configure(struct wl_listener *listener, void *data) +static int +headless_backend_output_configure(struct weston_output *output) { - struct weston_output *output = data; struct wet_output_config defaults = { .width = 1024, .height = 640, @@ -1262,8 +2619,7 @@ headless_backend_output_configure(struct wl_listener *listener, void *data) .transform = WL_OUTPUT_TRANSFORM_NORMAL }; - if (wet_configure_windowed_output_from_config(output, &defaults) < 0) - weston_log("Cannot configure output \"%s\".\n", output->name); + return wet_configure_windowed_output_from_config(output, &defaults); } static int @@ -1272,7 +2628,8 @@ load_headless_backend(struct weston_compositor *c, { const struct weston_windowed_output_api *api; struct weston_headless_backend_config config = {{ 0, }}; - int no_outputs = 0; + struct weston_config_section *section; + bool no_outputs = false; int ret = 0; char *transform = NULL; @@ -1280,10 +2637,18 @@ load_headless_backend(struct weston_compositor *c, if (!parsed_options) return -1; + section = weston_config_get_section(wc, "core", NULL, NULL); + weston_config_section_get_bool(section, "use-pixman", &config.use_pixman, + false); + weston_config_section_get_bool(section, "use-gl", &config.use_gl, + false); + const struct weston_option options[] = { { WESTON_OPTION_INTEGER, "width", 0, &parsed_options->width }, { WESTON_OPTION_INTEGER, "height", 0, &parsed_options->height }, + { WESTON_OPTION_INTEGER, "scale", 0, &parsed_options->scale }, { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &config.use_pixman }, + { WESTON_OPTION_BOOLEAN, "use-gl", 0, &config.use_gl }, { WESTON_OPTION_STRING, "transform", 0, &transform }, { WESTON_OPTION_BOOLEAN, "no-outputs", 0, &no_outputs }, }; @@ -1293,7 +2658,7 @@ load_headless_backend(struct weston_compositor *c, if (transform) { if (weston_parse_transform(transform, &parsed_options->transform) < 0) { weston_log("Invalid transform \"%s\"\n", transform); - parsed_options->transform = UINT32_MAX; + return -1; } free(transform); } @@ -1301,6 +2666,8 @@ load_headless_backend(struct weston_compositor *c, config.base.struct_version = WESTON_HEADLESS_BACKEND_CONFIG_VERSION; config.base.struct_size = sizeof(struct weston_headless_backend_config); + wet_set_simple_head_configurator(c, headless_backend_output_configure); + /* load the actual wayland backend and configure it */ ret = weston_compositor_load_backend(c, WESTON_BACKEND_HEADLESS, &config.base); @@ -1308,8 +2675,6 @@ load_headless_backend(struct weston_compositor *c, if (ret < 0) return ret; - wet_set_pending_output_handler(c, headless_backend_output_configure); - if (!no_outputs) { api = weston_windowed_output_get_api(c); @@ -1318,7 +2683,7 @@ load_headless_backend(struct weston_compositor *c, return -1; } - if (api->output_create(c, "headless") < 0) + if (api->create_head(c, "headless") < 0) return -1; } @@ -1326,63 +2691,120 @@ load_headless_backend(struct weston_compositor *c, } static void -rdp_backend_output_configure(struct wl_listener *listener, void *data) +weston_rdp_backend_config_init(struct weston_rdp_backend_config *config) { - struct weston_output *output = data; - struct wet_compositor *compositor = to_wet_compositor(output->compositor); - struct wet_output_config *parsed_options = compositor->parsed_options; - const struct weston_rdp_output_api *api = weston_rdp_output_get_api(output->compositor); - int width = 640; - int height = 480; + config->base.struct_version = WESTON_RDP_BACKEND_CONFIG_VERSION; + config->base.struct_size = sizeof(struct weston_rdp_backend_config); - assert(parsed_options); + config->bind_address = NULL; + config->port = 3389; + config->rdp_key = NULL; + config->server_cert = NULL; + config->server_key = NULL; + config->env_socket = 0; + config->no_clients_resize = 0; + config->force_no_compression = 0; + config->redirect_clipboard = false; + config->audio_in_setup = NULL; + config->audio_in_teardown = NULL; + config->audio_out_setup = NULL; + config->audio_out_teardown = NULL; + config->rdp_monitor_refresh_rate = WESTON_RDP_MODE_FREQ; + config->rail_config.use_rdpapplist = false; + config->rail_config.use_shared_memory = false; + config->rail_config.enable_hi_dpi_support = false; + config->rail_config.enable_fractional_hi_dpi_support = false; + config->rail_config.enable_fractional_hi_dpi_roundup = false; + config->rail_config.debug_desktop_scaling_factor = 0; + config->rail_config.enable_window_zorder_sync = false; + config->rail_config.enable_window_snap_arrange = false; + config->rail_config.enable_window_shadow_remoting = false; + config->rail_config.enable_distro_name_title = false; + config->rail_config.enable_copy_warning_title = false; + config->rail_config.enable_display_power_by_screenupdate = false; +} - if (!api) { - weston_log("Cannot use weston_rdp_output_api.\n"); - return; - } +static bool +read_rdp_config_bool(char *config_name, bool default_value) +{ + char *s; - if (parsed_options->width) - width = parsed_options->width; + s = getenv(config_name); + if (s) { + if (strcmp(s, "true") == 0) + return true; + else if (strcmp(s, "false") == 0) + return false; + else if (strcmp(s, "1") == 0) + return true; + else if (strcmp(s, "0") == 0) + return false; + } - if (parsed_options->height) - height = parsed_options->height; + return default_value; +} - weston_output_set_scale(output, 1); - weston_output_set_transform(output, WL_OUTPUT_TRANSFORM_NORMAL); +static int +read_rdp_config_int(char *config_name, int default_value) +{ + char *s; + int i; - if (api->output_set_size(output, width, height) < 0) { - weston_log("Cannot configure output \"%s\" using weston_rdp_output_api.\n", - output->name); - return; + s = getenv(config_name); + if (s) { + if (safe_strtoint(s, &i)) + return i; } - weston_output_enable(output); + return default_value; +} + +struct wet_rdp_params * +wet_get_rdp_params(struct weston_compositor *ec) +{ + struct wet_compositor *wet = to_wet_compositor(ec); + + return &wet->rdp_params; } static void -weston_rdp_backend_config_init(struct weston_rdp_backend_config *config) +rdp_heads_changed(struct wl_listener *listener, void *arg) { - config->base.struct_version = WESTON_RDP_BACKEND_CONFIG_VERSION; - config->base.struct_size = sizeof(struct weston_rdp_backend_config); + struct weston_compositor *compositor = arg; + struct wet_compositor *wet = to_wet_compositor(compositor); + struct weston_head *head = NULL; + + while ((head = weston_compositor_iterate_heads(compositor, head))) { + if (!head->output) { + struct weston_output *out; + + out = weston_compositor_create_output_with_head(head->compositor, + head); + wet_head_tracker_create(wet, head); + weston_output_attach_head(out, head); + } + } - config->bind_address = NULL; - config->port = 3389; - config->rdp_key = NULL; - config->server_cert = NULL; - config->server_key = NULL; - config->env_socket = 0; - config->no_clients_resize = 0; + disp_monitor_validate_and_compute_layout(compositor); + + while ((head = weston_compositor_iterate_heads(compositor, head))) { + if (!head->output->enabled) + weston_output_enable(head->output); + weston_head_reset_device_changed(head); + } } + static int load_rdp_backend(struct weston_compositor *c, int *argc, char *argv[], struct weston_config *wc) { struct weston_rdp_backend_config config = {{ 0, }}; int ret = 0; - struct wet_output_config *parsed_options = wet_init_parsed_options(c); + bool audio_tmp; + struct wet_compositor *wet = to_wet_compositor(c); + if (!parsed_options) return -1; @@ -1392,25 +2814,94 @@ load_rdp_backend(struct weston_compositor *c, { WESTON_OPTION_BOOLEAN, "env-socket", 0, &config.env_socket }, { WESTON_OPTION_INTEGER, "width", 0, &parsed_options->width }, { WESTON_OPTION_INTEGER, "height", 0, &parsed_options->height }, + { WESTON_OPTION_INTEGER, "scale", 0, &parsed_options->scale }, { WESTON_OPTION_STRING, "address", 0, &config.bind_address }, { WESTON_OPTION_INTEGER, "port", 0, &config.port }, { WESTON_OPTION_BOOLEAN, "no-clients-resize", 0, &config.no_clients_resize }, { WESTON_OPTION_STRING, "rdp4-key", 0, &config.rdp_key }, { WESTON_OPTION_STRING, "rdp-tls-cert", 0, &config.server_cert }, - { WESTON_OPTION_STRING, "rdp-tls-key", 0, &config.server_key } + { WESTON_OPTION_STRING, "rdp-tls-key", 0, &config.server_key }, + { WESTON_OPTION_BOOLEAN, "force-no-compression", 0, &config.force_no_compression }, }; parse_options(rdp_options, ARRAY_LENGTH(rdp_options), argc, argv); - ret = weston_compositor_load_backend(c, WESTON_BACKEND_RDP, - &config.base); + /* certain configurations are read from environment variables */ + config.redirect_clipboard = read_rdp_config_bool("WESTON_RDP_CLIPBOARD", true); - if (ret < 0) - goto out; + audio_tmp = read_rdp_config_bool("WESTON_RDP_AUDIO_PLAYBACK", true); + if (audio_tmp) { + config.audio_out_setup = rdp_audio_out_init; + config.audio_out_teardown = rdp_audio_out_destroy; + } + + audio_tmp = read_rdp_config_bool("WESTON_RDP_AUDIO_CAPTURE", true); + if (audio_tmp) { + config.audio_in_setup = rdp_audio_in_init; + config.audio_in_teardown = rdp_audio_in_destroy; + } - wet_set_pending_output_handler(c, rdp_backend_output_configure); + config.rdp_monitor_refresh_rate = read_rdp_config_int("WESTON_RDP_MONITOR_REFRESH_RATE", WESTON_RDP_MODE_FREQ); + + config.rail_config.use_rdpapplist = read_rdp_config_bool("WESTON_RDP_APPLIST", true); + config.rail_config.use_shared_memory = read_rdp_config_bool("WESTON_RDP_SHARED_MEMORY", true); + + /* Configure HI-DPI scaling */ + config.rail_config.enable_hi_dpi_support = read_rdp_config_bool("WESTON_RDP_HI_DPI_SCALING", true); + if (config.rail_config.enable_hi_dpi_support) { + /* Disable by default for now. */ + config.rail_config.enable_fractional_hi_dpi_support = + read_rdp_config_bool("WESTON_RDP_FRACTIONAL_HI_DPI_SCALING", false); + } else { + config.rail_config.enable_fractional_hi_dpi_support = false; + } + /* if fractional support is enabled, no round up */ + if (config.rail_config.enable_fractional_hi_dpi_support) { + config.rail_config.enable_fractional_hi_dpi_roundup = false; + } else { + config.rail_config.enable_fractional_hi_dpi_roundup = + read_rdp_config_bool("WESTON_RDP_FRACTIONAL_HI_DPI_SCALING_ROUNDUP", false); + } + if (config.rail_config.enable_hi_dpi_support) { + config.rail_config.debug_desktop_scaling_factor = + read_rdp_config_int("WESTON_RDP_DEBUG_DESKTOP_SCALING_FACTOR", 0); + if (config.rail_config.debug_desktop_scaling_factor != 0) { + if (config.rail_config.debug_desktop_scaling_factor < 100 || + config.rail_config.debug_desktop_scaling_factor > 500) { + config.rail_config.debug_desktop_scaling_factor = 0; + } + } + } else { + config.rail_config.debug_desktop_scaling_factor = 0; + } + + wet->rdp_params.enable_hi_dpi_support = config.rail_config.enable_hi_dpi_support; + wet->rdp_params.enable_fractional_hi_dpi_support = config.rail_config.enable_fractional_hi_dpi_support; + wet->rdp_params.enable_fractional_hi_dpi_roundup = config.rail_config.enable_fractional_hi_dpi_roundup; + wet->rdp_params.debug_desktop_scaling_factor = config.rail_config.debug_desktop_scaling_factor; + wet->rdp_params.default_width = parsed_options->width; + wet->rdp_params.default_height = parsed_options->height; + config.rail_config.enable_window_zorder_sync = read_rdp_config_bool("WESTON_RDP_WINDOW_ZORDER_SYNC", true); + config.rail_config.enable_window_snap_arrange = read_rdp_config_bool("WESTON_RDP_WINDOW_SNAP_ARRANGE", true); + config.rail_config.enable_window_shadow_remoting = read_rdp_config_bool("WESTON_RDP_WINDOW_SHADOW_REMOTING", true); + + config.rail_config.enable_display_power_by_screenupdate = + read_rdp_config_bool("WESTON_RDP_DISPLAY_POWER_BY_SCREENUPDATE", false); + + config.rail_config.enable_distro_name_title = read_rdp_config_bool("WESTON_RDP_APPEND_DISTRONAME_TITLE", true); +#if defined(__arm__) || defined(__aarch64__) + config.rail_config.enable_copy_warning_title = read_rdp_config_bool("WESTON_RDP_COPY_WARNING_TITLE", false); +#else + config.rail_config.enable_copy_warning_title = read_rdp_config_bool("WESTON_RDP_COPY_WARNING_TITLE", true); +#endif + + wet->heads_changed_listener.notify = rdp_heads_changed; + weston_compositor_add_heads_changed_listener(c, + &wet->heads_changed_listener); + + ret = weston_compositor_load_backend(c, WESTON_BACKEND_RDP, + &config.base); -out: free(config.bind_address); free(config.rdp_key); free(config.server_cert); @@ -1419,19 +2910,23 @@ load_rdp_backend(struct weston_compositor *c, return ret; } -static void -fbdev_backend_output_configure(struct wl_listener *listener, void *data) +static int +fbdev_backend_output_configure(struct weston_output *output) { - struct weston_output *output = data; struct weston_config *wc = wet_get_config(output->compositor); struct weston_config_section *section; section = weston_config_get_section(wc, "output", "name", "fbdev"); - wet_output_set_transform(output, section, WL_OUTPUT_TRANSFORM_NORMAL, UINT32_MAX); + if (wet_output_set_transform(output, section, + WL_OUTPUT_TRANSFORM_NORMAL, + UINT32_MAX) < 0) { + return -1; + } + weston_output_set_scale(output, 1); - weston_output_enable(output); + return 0; } static int @@ -1444,35 +2939,28 @@ load_fbdev_backend(struct weston_compositor *c, const struct weston_option fbdev_options[] = { { WESTON_OPTION_INTEGER, "tty", 0, &config.tty }, { WESTON_OPTION_STRING, "device", 0, &config.device }, + { WESTON_OPTION_STRING, "seat", 0, &config.seat_id }, }; parse_options(fbdev_options, ARRAY_LENGTH(fbdev_options), argc, argv); - if (!config.device) - config.device = strdup("/dev/fb0"); - config.base.struct_version = WESTON_FBDEV_BACKEND_CONFIG_VERSION; config.base.struct_size = sizeof(struct weston_fbdev_backend_config); config.configure_device = configure_input_device; + wet_set_simple_head_configurator(c, fbdev_backend_output_configure); + /* load the actual wayland backend and configure it */ ret = weston_compositor_load_backend(c, WESTON_BACKEND_FBDEV, &config.base); - if (ret < 0) - goto out; - - wet_set_pending_output_handler(c, fbdev_backend_output_configure); - -out: free(config.device); return ret; } -static void -x11_backend_output_configure(struct wl_listener *listener, void *data) +static int +x11_backend_output_configure(struct weston_output *output) { - struct weston_output *output = data; struct wet_output_config defaults = { .width = 1024, .height = 600, @@ -1480,8 +2968,7 @@ x11_backend_output_configure(struct wl_listener *listener, void *data) .transform = WL_OUTPUT_TRANSFORM_NORMAL }; - if (wet_configure_windowed_output_from_config(output, &defaults) < 0) - weston_log("Cannot configure output \"%s\".\n", output->name); + return wet_configure_windowed_output_from_config(output, &defaults); } static int @@ -1502,6 +2989,10 @@ load_x11_backend(struct weston_compositor *c, if (!parsed_options) return -1; + section = weston_config_get_section(wc, "core", NULL, NULL); + weston_config_section_get_bool(section, "use-pixman", &config.use_pixman, + false); + const struct weston_option options[] = { { WESTON_OPTION_INTEGER, "width", 0, &parsed_options->width }, { WESTON_OPTION_INTEGER, "height", 0, &parsed_options->height }, @@ -1517,6 +3008,8 @@ load_x11_backend(struct weston_compositor *c, config.base.struct_version = WESTON_X11_BACKEND_CONFIG_VERSION; config.base.struct_size = sizeof(struct weston_x11_backend_config); + wet_set_simple_head_configurator(c, x11_backend_output_configure); + /* load the actual backend and configure it */ ret = weston_compositor_load_backend(c, WESTON_BACKEND_X11, &config.base); @@ -1524,8 +3017,6 @@ load_x11_backend(struct weston_compositor *c, if (ret < 0) return ret; - wet_set_pending_output_handler(c, x11_backend_output_configure); - api = weston_windowed_output_get_api(c); if (!api) { @@ -1550,7 +3041,7 @@ load_x11_backend(struct weston_compositor *c, continue; } - if (api->output_create(c, output_name) < 0) { + if (api->create_head(c, output_name) < 0) { free(output_name); return -1; } @@ -1566,7 +3057,7 @@ load_x11_backend(struct weston_compositor *c, return -1; } - if (api->output_create(c, default_output) < 0) { + if (api->create_head(c, default_output) < 0) { free(default_output); return -1; } @@ -1576,19 +3067,9 @@ load_x11_backend(struct weston_compositor *c, return 0; } -static void -wayland_backend_output_configure_hotplug(struct wl_listener *listener, void *data) -{ - struct weston_output *output = data; - - /* This backend has all values hardcoded, so nothing can be configured here */ - weston_output_enable(output); -} - -static void -wayland_backend_output_configure(struct wl_listener *listener, void *data) +static int +wayland_backend_output_configure(struct weston_output *output) { - struct weston_output *output = data; struct wet_output_config defaults = { .width = 1024, .height = 640, @@ -1596,8 +3077,7 @@ wayland_backend_output_configure(struct wl_listener *listener, void *data) .transform = WL_OUTPUT_TRANSFORM_NORMAL }; - if (wet_configure_windowed_output_from_config(output, &defaults) < 0) - weston_log("Cannot configure output \"%s\".\n", output->name); + return wet_configure_windowed_output_from_config(output, &defaults); } static int @@ -1620,9 +3100,10 @@ load_wayland_backend(struct weston_compositor *c, config.cursor_size = 32; config.cursor_theme = NULL; config.display_name = NULL; - config.fullscreen = false; - config.sprawl = false; - config.use_pixman = false; + + section = weston_config_get_section(wc, "core", NULL, NULL); + weston_config_section_get_bool(section, "use-pixman", &config.use_pixman, + false); const struct weston_option wayland_options[] = { { WESTON_OPTION_INTEGER, "width", 0, &parsed_options->width }, @@ -1661,13 +3142,15 @@ load_wayland_backend(struct weston_compositor *c, if (api == NULL) { /* We will just assume if load_backend() finished cleanly and * windowed_output_api is not present that wayland backend is - * started with --sprawl or runs on fullscreen-shell. */ - wet_set_pending_output_handler(c, wayland_backend_output_configure_hotplug); + * started with --sprawl or runs on fullscreen-shell. + * In this case, all values are hardcoded, so nothing can be + * configured; simply create and enable an output. */ + wet_set_simple_head_configurator(c, NULL); return 0; } - wet_set_pending_output_handler(c, wayland_backend_output_configure); + wet_set_simple_head_configurator(c, wayland_backend_output_configure); section = NULL; while (weston_config_next_section(wc, §ion, §ion_name)) { @@ -1688,7 +3171,7 @@ load_wayland_backend(struct weston_compositor *c, continue; } - if (api->output_create(c, output_name) < 0) { + if (api->create_head(c, output_name) < 0) { free(output_name); return -1; } @@ -1701,7 +3184,7 @@ load_wayland_backend(struct weston_compositor *c, if (asprintf(&output_name, "wayland%d", i) < 0) return -1; - if (api->output_create(c, output_name) < 0) { + if (api->create_head(c, output_name) < 0) { free(output_name); return -1; } @@ -1753,51 +3236,135 @@ copy_command_line(int argc, char * const argv[]) return str; } -int main(int argc, char *argv[]) +#if !defined(BUILD_XWAYLAND) +int +wet_load_xwayland(struct weston_compositor *comp) +{ + return -1; +} +#endif + +static void +weston_log_setup_scopes(struct weston_log_context *log_ctx, + struct weston_log_subscriber *subscriber, + const char *names) +{ + assert(log_ctx); + assert(subscriber); + + char *tokenize = strdup(names); + char *token = strtok(tokenize, ","); + while (token) { + weston_log_subscribe(log_ctx, subscriber, token); + token = strtok(NULL, ","); + } + free(tokenize); +} + +static void +flight_rec_key_binding_handler(struct weston_keyboard *keyboard, + const struct timespec *time, uint32_t key, + void *data) +{ + struct weston_log_subscriber *flight_rec = data; + weston_log_subscriber_display_flight_rec(flight_rec); +} + +static void +weston_log_subscribe_to_scopes(struct weston_log_context *log_ctx, + struct weston_log_subscriber *logger, + struct weston_log_subscriber *flight_rec, + const char *log_scopes, + const char *flight_rec_scopes) +{ + if (log_scopes) + weston_log_setup_scopes(log_ctx, logger, log_scopes); + else + weston_log_subscribe(log_ctx, logger, "log"); + + if (flight_rec_scopes) { + weston_log_setup_scopes(log_ctx, flight_rec, flight_rec_scopes); + } else { + /* by default subscribe to 'log', and 'drm-backend' */ + weston_log_subscribe(log_ctx, flight_rec, "log"); + weston_log_subscribe(log_ctx, flight_rec, "drm-backend"); + } +} + +static void +sigint_helper(int sig) +{ + raise(SIGUSR2); +} + +WL_EXPORT int +wet_main(int argc, char *argv[]) { int ret = EXIT_FAILURE; char *cmdline; struct wl_display *display; - struct weston_compositor *ec; - struct wl_event_source *signals[4]; + struct wl_event_source *signals[3]; struct wl_event_loop *loop; int i, fd; char *backend = NULL; char *shell = NULL; - int32_t xwayland = 0; + bool xwayland = false; char *modules = NULL; char *option_modules = NULL; char *log = NULL; + char *log_scopes = NULL; + char *log_scopes_env = NULL; + char *flight_rec_scopes = NULL; char *server_socket = NULL; + char *idle_time_env = NULL; int32_t idle_time = -1; int32_t help = 0; char *socket_name = NULL; int32_t version = 0; int32_t noconfig = 0; - int32_t numlock_on; + char *debug_protocol_env = NULL; + int32_t debug_protocol = 0; + bool numlock_on; char *config_file = NULL; struct weston_config *config = NULL; struct weston_config_section *section; struct wl_client *primary_client; struct wl_listener primary_client_destroyed; struct weston_seat *seat; - struct wet_compositor user_data; - int require_input; + struct wet_compositor wet = { 0 }; + struct weston_log_context *log_ctx = NULL; + struct weston_log_subscriber *logger = NULL; + struct weston_log_subscriber *flight_rec = NULL; + sigset_t mask; + struct sigaction action; + + bool wait_for_debugger = false; + struct wl_protocol_logger *protologger = NULL; const struct weston_option core_options[] = { { WESTON_OPTION_STRING, "backend", 'B', &backend }, { WESTON_OPTION_STRING, "shell", 0, &shell }, { WESTON_OPTION_STRING, "socket", 'S', &socket_name }, { WESTON_OPTION_INTEGER, "idle-time", 'i', &idle_time }, +#if defined(BUILD_XWAYLAND) { WESTON_OPTION_BOOLEAN, "xwayland", 0, &xwayland }, +#endif { WESTON_OPTION_STRING, "modules", 0, &option_modules }, { WESTON_OPTION_STRING, "log", 0, &log }, { WESTON_OPTION_BOOLEAN, "help", 'h', &help }, { WESTON_OPTION_BOOLEAN, "version", 0, &version }, { WESTON_OPTION_BOOLEAN, "no-config", 0, &noconfig }, { WESTON_OPTION_STRING, "config", 'c', &config_file }, + { WESTON_OPTION_BOOLEAN, "wait-for-debugger", 0, &wait_for_debugger }, + { WESTON_OPTION_BOOLEAN, "debug", 0, &debug_protocol }, + { WESTON_OPTION_STRING, "logger-scopes", 'l', &log_scopes }, + { WESTON_OPTION_STRING, "flight-rec-scopes", 'f', &flight_rec_scopes }, }; + wl_list_init(&wet.layoutput_list); + + os_fd_set_cloexec(fileno(stdin)); + cmdline = copy_command_line(argc, argv); parse_options(core_options, ARRAY_LENGTH(core_options), &argc, argv); @@ -1813,8 +3380,29 @@ int main(int argc, char *argv[]) return EXIT_SUCCESS; } + log_ctx = weston_log_ctx_create(); + if (!log_ctx) { + fprintf(stderr, "Failed to initialize weston debug framework.\n"); + return EXIT_FAILURE; + } + + log_scope = weston_log_ctx_add_log_scope(log_ctx, "log", + "Weston and Wayland log\n", NULL, NULL, NULL); + + if (!weston_log_file_open(log)) + return EXIT_FAILURE; + weston_log_set_handler(vlog, vlog_continue); - weston_log_file_open(log); + + logger = weston_log_subscriber_create_log(weston_logfile); + flight_rec = weston_log_subscriber_create_flight_rec(DEFAULT_FLIGHT_REC_SIZE); + + weston_log_subscribe_to_scopes(log_ctx, logger, flight_rec, + log_scopes, flight_rec_scopes); + + log_scopes_env = getenv("WESTON_LOG_SCOPES"); + if (log_scopes_env) + weston_log_setup_scopes(log_ctx, logger, log_scopes_env); weston_log("%s\n" STAMP_SPACE "%s\n" @@ -1829,28 +3417,63 @@ int main(int argc, char *argv[]) verify_xdg_runtime_dir(); display = wl_display_create(); + if (display == NULL) { + weston_log("fatal: failed to create display\n"); + goto out_display; + } + + if (load_configuration(&config, noconfig, config_file) < 0) + goto out_config; + wet.config = config; + wet.parsed_options = NULL; + + section = weston_config_get_section(config, "core", NULL, NULL); loop = wl_display_get_event_loop(display); signals[0] = wl_event_loop_add_signal(loop, SIGTERM, on_term_signal, display); - signals[1] = wl_event_loop_add_signal(loop, SIGINT, on_term_signal, - display); - signals[2] = wl_event_loop_add_signal(loop, SIGQUIT, on_term_signal, + signals[1] = wl_event_loop_add_signal(loop, SIGUSR2, on_term_signal, display); wl_list_init(&child_process_list); - signals[3] = wl_event_loop_add_signal(loop, SIGCHLD, sigchld_handler, + signals[2] = wl_event_loop_add_signal(loop, SIGCHLD, sigchld_handler, NULL); - if (!signals[0] || !signals[1] || !signals[2] || !signals[3]) - goto out_signals; - - if (load_configuration(&config, noconfig, config_file) < 0) + /* When debugging weston, if use wl_event_loop_add_signal() to catch + * SIGINT, the debugger can't catch it, and attempting to stop + * weston from within the debugger results in weston exiting + * cleanly. + * + * Instead, use the sigaction() function, which sets up the signal + * in a way that gdb can successfully catch, but have the handler + * for SIGINT send SIGUSR2 (xwayland uses SIGUSR1), which we catch + * via wl_event_loop_add_signal(). + */ + action.sa_handler = sigint_helper; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + sigaction(SIGINT, &action, NULL); + if (!signals[0] || !signals[1] || !signals[2]) goto out_signals; - user_data.config = config; - user_data.parsed_options = NULL; - section = weston_config_get_section(config, "core", NULL, NULL); + /* Xwayland uses SIGUSR1 for communicating with weston. Since some + weston plugins may create additional threads, set up any necessary + signal blocking early so that these threads can inherit the settings + when created. */ + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + pthread_sigmask(SIG_BLOCK, &mask, NULL); + + if (!wait_for_debugger) { + weston_config_section_get_bool(section, "wait-for-debugger", + &wait_for_debugger, false); + } + if (wait_for_debugger) { + weston_log("Weston PID is %ld - " + "waiting for debugger, send SIGCONT to continue...\n", + (long)getpid()); + raise(SIGSTOP); + } if (!backend) { weston_config_section_get_string(section, "backend", &backend, @@ -1859,39 +3482,60 @@ int main(int argc, char *argv[]) backend = weston_choose_default_backend(); } - ec = weston_compositor_create(display, &user_data); - if (ec == NULL) { + wet.compositor = weston_compositor_create(display, log_ctx, &wet); + if (wet.compositor == NULL) { weston_log("fatal: failed to create compositor\n"); goto out; } + segv_compositor = wet.compositor; + + protocol_scope = + weston_log_ctx_add_log_scope(log_ctx, "proto", + "Wayland protocol dump for all clients.\n", + NULL, NULL, NULL); + + protologger = wl_display_add_protocol_logger(display, + protocol_log_fn, + NULL); + + debug_protocol_env = getenv("WESTON_DEBUG_PROTOCOL"); + if (debug_protocol_env && (strcmp(debug_protocol_env, "true") == 0)) + debug_protocol = 1; + if (debug_protocol) + weston_compositor_enable_debug_protocol(wet.compositor); + + weston_compositor_add_debug_binding(wet.compositor, KEY_D, + flight_rec_key_binding_handler, + flight_rec); - if (weston_compositor_init_config(ec, config) < 0) + if (weston_compositor_init_config(wet.compositor, config) < 0) goto out; weston_config_section_get_bool(section, "require-input", - &require_input, true); - ec->require_input = require_input; + &wet.compositor->require_input, true); - if (load_backend(ec, backend, &argc, argv, config) < 0) { + if (load_backend(wet.compositor, backend, &argc, argv, config) < 0) { weston_log("fatal: failed to create compositor backend\n"); goto out; } - weston_pending_output_coldplug(ec); - - catch_signals(); - segv_compositor = ec; + weston_compositor_flush_heads_changed(wet.compositor); + if (wet.init_failed) + goto out; + idle_time_env = getenv("WESTON_IDLE_TIME"); + if (!idle_time_env || !safe_strtoint(idle_time_env, &idle_time)) + idle_time = -1; if (idle_time < 0) weston_config_section_get_int(section, "idle-time", &idle_time, -1); if (idle_time < 0) idle_time = 300; /* default idle timeout, in seconds */ - ec->idle_time = idle_time; - ec->default_pointer_grab = NULL; - ec->exit = handle_exit; + wet.compositor->idle_time = idle_time; + wet.compositor->default_pointer_grab = NULL; + wet.compositor->exit = handle_exit; - weston_compositor_log_capabilities(ec); + weston_compositor_log_capabilities(wet.compositor); server_socket = getenv("WAYLAND_SERVER_SOCKET"); if (server_socket) { @@ -1905,7 +3549,8 @@ int main(int argc, char *argv[]) if (fd != -1) { primary_client = wl_client_create(display, fd); if (!primary_client) { - weston_log("fatal: failed to add client: %m\n"); + weston_log("fatal: failed to add client: %s\n", + strerror(errno)); goto out; } primary_client_destroyed.notify = @@ -1920,28 +3565,33 @@ int main(int argc, char *argv[]) weston_config_section_get_string(section, "shell", &shell, "desktop-shell.so"); - if (wet_load_shell(ec, shell, &argc, argv) < 0) - goto out; - - weston_config_section_get_string(section, "modules", &modules, ""); - if (load_modules(ec, modules, &argc, argv, &xwayland) < 0) - goto out; - - if (load_modules(ec, option_modules, &argc, argv, &xwayland) < 0) + if (wet_load_shell(wet.compositor, shell, &argc, argv) < 0) goto out; - if (!xwayland) + /* Load xwayland before other modules - this way if we're using + * the systemd-notify module it will notify after we're ready + * to receive xwayland connections. + */ + if (!xwayland) { weston_config_section_get_bool(section, "xwayland", &xwayland, false); + } if (xwayland) { - if (wet_load_xwayland(ec) < 0) + if (wet_load_xwayland(wet.compositor) < 0) goto out; } + weston_config_section_get_string(section, "modules", &modules, ""); + if (load_modules(wet.compositor, modules, &argc, argv) < 0) + goto out; + + if (load_modules(wet.compositor, option_modules, &argc, argv) < 0) + goto out; + section = weston_config_get_section(config, "keyboard", NULL, NULL); - weston_config_section_get_bool(section, "numlock-on", &numlock_on, 0); + weston_config_section_get_bool(section, "numlock-on", &numlock_on, false); if (numlock_on) { - wl_list_for_each(seat, &ec->seat_list, link) { + wl_list_for_each(seat, &wet.compositor->seat_list, link) { struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); @@ -1957,7 +3607,8 @@ int main(int argc, char *argv[]) if (argc > 1) goto out; - weston_compositor_wake(ec); + /* Until RDP connection is established, keep compositor sleep state */ + weston_compositor_sleep(wet.compositor); wl_display_run(display); @@ -1967,21 +3618,35 @@ int main(int argc, char *argv[]) * that want to indicate failure status to * testing infrastructure above */ - ret = ec->exit_code; + ret = wet.compositor->exit_code; out: + wet_compositor_destroy_layout(&wet); + /* free(NULL) is valid, and it won't be NULL if it's used */ - free(user_data.parsed_options); + free(wet.parsed_options); + + if (protologger) + wl_protocol_logger_destroy(protologger); - weston_compositor_destroy(ec); + weston_compositor_destroy(wet.compositor); + weston_log_scope_destroy(protocol_scope); + protocol_scope = NULL; + weston_log_scope_destroy(log_scope); + log_scope = NULL; + weston_log_subscriber_destroy(logger); + weston_log_subscriber_destroy(flight_rec); + weston_log_ctx_destroy(log_ctx); out_signals: for (i = ARRAY_LENGTH(signals) - 1; i >= 0; i--) if (signals[i]) wl_event_source_remove(signals[i]); +out_config: wl_display_destroy(display); +out_display: weston_log_file_close(); if (config) diff --git a/compositor/meson.build b/compositor/meson.build new file mode 100644 index 000000000..21b6b57f7 --- /dev/null +++ b/compositor/meson.build @@ -0,0 +1,205 @@ +srcs_weston = [ + git_version_h, + 'main.c', + 'rdpaudio.c', + 'rdpaudioin.c', + 'rdpdisp.c', + 'testsuite-util.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_public, + dep_libinput, + dep_libevdev, + dep_libdl, + dep_threads, + dep_frdp, + dep_frdp_server, +] + +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 + +libexec_weston = shared_library( + 'exec_weston', + sources: srcs_weston, + include_directories: common_inc, + dependencies: deps_weston, + install_dir: dir_module_weston, + install: true, + version: '0.0.0', + soversion: 0 +) +dep_libexec_weston = declare_dependency( + link_with: libexec_weston, + include_directories: [ include_directories('.'), public_inc ], + dependencies: dep_libweston_public +) +exe_weston = executable( + 'weston', + 'executable.c', + include_directories: common_inc, + dependencies: dep_libexec_weston, + install_rpath: dir_module_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_libexec_weston, + dep_libshared, + dep_libweston_public, + dep_libweston_private_h, # XXX: https://gitlab.freedesktop.org/wayland/weston/issues/292 + dep_wayland_client, + ] + plugin_screenshare = shared_library( + 'screen-share', + srcs_screenshare, + include_directories: common_inc, + dependencies: deps_screenshare, + name_prefix: '', + install: true, + install_dir: dir_module_weston, + install_rpath: '$ORIGIN' + ) + 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: common_inc, + dependencies: [ dep_libexec_weston, dep_libweston_public, dep_lcms2 ], + name_prefix: '', + install: true, + install_dir: dir_module_weston, + install_rpath: '$ORIGIN' + ) + 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_public, dep_colord, dep_lcms2 ] + + 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: common_inc, + 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: common_inc, + dependencies: [ dep_libweston_public, dep_libsystemd ], + name_prefix: '', + install: true, + install_dir: dir_module_weston + ) + env_modmap += 'systemd-notify.so=@0@;'.format(plugin_systemd_notify.full_path()) +endif + +if get_option('wslgd') + plugin_wslgd_notify = shared_library( + 'wslgd-notify', + 'wslgd-notify.c', + include_directories: common_inc, + dependencies: [ dep_libweston_public ], + name_prefix: '', + install: true, + install_dir: dir_module_weston + ) + env_modmap += 'wslgd-notify.so=@0@;'.format(plugin_wslgd_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 +) diff --git a/compositor/rdpaudio.c b/compositor/rdpaudio.c new file mode 100644 index 000000000..bfb7638a2 --- /dev/null +++ b/compositor/rdpaudio.c @@ -0,0 +1,799 @@ +/* + * Copyright © 2020 Microsoft + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rdpaudio.h" +#include +#include + +static AUDIO_FORMAT rdp_audio_supported_audio_formats[] = { + { WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0, NULL }, + }; + +#define AUDIO_LATENCY 5 +#define AUDIO_FRAMES_PER_RDP_PACKET (44100 * AUDIO_LATENCY / 1000) + +#define RDP_SINK_INTERFACE_VERSION 1 + +#define RDP_AUDIO_CMD_VERSION 0 +#define RDP_AUDIO_CMD_TRANSFER 1 +#define RDP_AUDIO_CMD_GET_LATENCY 2 +#define RDP_AUDIO_CMD_RESET_LATENCY 3 + +typedef struct _rdp_audio_cmd_header { + uint32_t cmd; + union { + uint32_t version; + struct { + uint32_t bytes; + uint64_t timestamp; + } transfer; + uint64_t reserved[8]; + }; +} rdp_audio_cmd_header; + +static char* +AUDIO_FORMAT_to_String(UINT16 format) +{ + switch (format) { + case WAVE_FORMAT_UNKNOWN: + return "WAVE_FORMAT_UNKNOWN"; + case WAVE_FORMAT_PCM: + return "WAVE_FORMAT_PCM"; + case WAVE_FORMAT_ADPCM: + return "WAVE_FORMAT_ADPCM"; + case WAVE_FORMAT_IEEE_FLOAT: + return "WAVE_FORMAT_IEEE_FLOAT"; + case WAVE_FORMAT_VSELP: + return "WAVE_FORMAT_VSELP"; + case WAVE_FORMAT_IBM_CVSD: + return "WAVE_FORMAT_IBM_CVSD"; + case WAVE_FORMAT_ALAW: + return "WAVE_FORMAT_ALAW"; + case WAVE_FORMAT_MULAW: + return "WAVE_FORMAT_MULAW"; + case WAVE_FORMAT_OKI_ADPCM: + return "WAVE_FORMAT_OKI_ADPCM"; + case WAVE_FORMAT_DVI_ADPCM: + return "WAVE_FORMAT_DVI_ADPCM"; + case WAVE_FORMAT_MEDIASPACE_ADPCM: + return "WAVE_FORMAT_MEDIASPACE_ADPCM"; + case WAVE_FORMAT_SIERRA_ADPCM: + return "WAVE_FORMAT_SIERRA_ADPCM"; + case WAVE_FORMAT_G723_ADPCM: + return "WAVE_FORMAT_G723_ADPCM"; + case WAVE_FORMAT_DIGISTD: + return "WAVE_FORMAT_DIGISTD"; + case WAVE_FORMAT_DIGIFIX: + return "WAVE_FORMAT_DIGIFIX"; + case WAVE_FORMAT_DIALOGIC_OKI_ADPCM: + return "WAVE_FORMAT_DIALOGIC_OKI_ADPCM"; + case WAVE_FORMAT_MEDIAVISION_ADPCM: + return "WAVE_FORMAT_MEDIAVISION_ADPCM"; + case WAVE_FORMAT_CU_CODEC: + return "WAVE_FORMAT_CU_CODEC"; + case WAVE_FORMAT_YAMAHA_ADPCM: + return "WAVE_FORMAT_YAMAHA_ADPCM"; + case WAVE_FORMAT_SONARC: + return "WAVE_FORMAT_SONARC"; + case WAVE_FORMAT_DSPGROUP_TRUESPEECH: + return "WAVE_FORMAT_DSPGROUP_TRUESPEECH"; + case WAVE_FORMAT_ECHOSC1: + return "WAVE_FORMAT_ECHOSC1"; + case WAVE_FORMAT_AUDIOFILE_AF36: + return "WAVE_FORMAT_AUDIOFILE_AF36"; + case WAVE_FORMAT_APTX: + return "WAVE_FORMAT_APTX"; + case WAVE_FORMAT_AUDIOFILE_AF10: + return "WAVE_FORMAT_AUDIOFILE_AF10"; + case WAVE_FORMAT_PROSODY_1612: + return "WAVE_FORMAT_PROSODY_1612"; + case WAVE_FORMAT_DOLBY_AC2: + return "WAVE_FORMAT_DOLBY_AC2"; + case WAVE_FORMAT_GSM610: + return "WAVE_FORMAT_GSM610"; + case WAVE_FORMAT_MSNAUDIO: + return "WAVE_FORMAT_MSNAUDIO"; + case WAVE_FORMAT_ANTEX_ADPCME: + return "WAVE_FORMAT_ANTEX_ADPCME"; + case WAVE_FORMAT_CONTROL_RES_VQLPC: + return "WAVE_FORMAT_CONTROL_RES_VQLPC"; + case WAVE_FORMAT_DIGIREAL: + return "WAVE_FORMAT_DIGIREAL"; + case WAVE_FORMAT_DIGIADPCM: + return "WAVE_FORMAT_DIGIADPCM"; + case WAVE_FORMAT_CONTROL_RES_CR10: + return "WAVE_FORMAT_CONTROL_RES_CR10"; + case WAVE_FORMAT_NMS_VBXADPCM: + return "WAVE_FORMAT_NMS_VBXADPCM"; + case WAVE_FORMAT_ROLAND_RDAC: + return "WAVE_FORMAT_ROLAND_RDAC"; + case WAVE_FORMAT_ECHOSC3: + return "WAVE_FORMAT_ECHOSC3"; + case WAVE_FORMAT_ROCKWELL_ADPCM: + return "WAVE_FORMAT_ROCKWELL_ADPCM"; + case WAVE_FORMAT_ROCKWELL_DIGITALK: + return "WAVE_FORMAT_ROCKWELL_DIGITALK"; + case WAVE_FORMAT_XEBEC: + return "WAVE_FORMAT_XEBEC"; + case WAVE_FORMAT_G721_ADPCM: + return "WAVE_FORMAT_G721_ADPCM"; + case WAVE_FORMAT_G728_CELP: + return "WAVE_FORMAT_G728_CELP"; + case WAVE_FORMAT_MSG723: + return "WAVE_FORMAT_MSG723"; + case WAVE_FORMAT_MPEG: + return "WAVE_FORMAT_MPEG"; + case WAVE_FORMAT_RT24: + return "WAVE_FORMAT_RT24"; + case WAVE_FORMAT_PAC: + return "WAVE_FORMAT_PAC"; + case WAVE_FORMAT_MPEGLAYER3: + return "WAVE_FORMAT_MPEGLAYER3"; + case WAVE_FORMAT_LUCENT_G723: + return "WAVE_FORMAT_LUCENT_G723"; + case WAVE_FORMAT_CIRRUS: + return "WAVE_FORMAT_CIRRUS"; + case WAVE_FORMAT_ESPCM: + return "WAVE_FORMAT_ESPCM"; + case WAVE_FORMAT_VOXWARE: + return "WAVE_FORMAT_VOXWARE"; + case WAVE_FORMAT_CANOPUS_ATRAC: + return "WAVE_FORMAT_CANOPUS_ATRAC"; + case WAVE_FORMAT_G726_ADPCM: + return "WAVE_FORMAT_G726_ADPCM"; + case WAVE_FORMAT_G722_ADPCM: + return "WAVE_FORMAT_G722_ADPCM"; + case WAVE_FORMAT_DSAT: + return "WAVE_FORMAT_DSAT"; + case WAVE_FORMAT_DSAT_DISPLAY: + return "WAVE_FORMAT_DSAT_DISPLAY"; + case WAVE_FORMAT_VOXWARE_BYTE_ALIGNED: + return "WAVE_FORMAT_VOXWARE_BYTE_ALIGNED"; + case WAVE_FORMAT_VOXWARE_AC8: + return "WAVE_FORMAT_VOXWARE_AC8"; + case WAVE_FORMAT_VOXWARE_AC10: + return "WAVE_FORMAT_VOXWARE_AC10"; + case WAVE_FORMAT_VOXWARE_AC16: + return "WAVE_FORMAT_VOXWARE_AC16"; + case WAVE_FORMAT_VOXWARE_AC20: + return "WAVE_FORMAT_VOXWARE_AC20"; + case WAVE_FORMAT_VOXWARE_RT24: + return "WAVE_FORMAT_VOXWARE_RT24"; + case WAVE_FORMAT_VOXWARE_RT29: + return "WAVE_FORMAT_VOXWARE_RT29"; + case WAVE_FORMAT_VOXWARE_RT29HW: + return "WAVE_FORMAT_VOXWARE_RT29HW"; + case WAVE_FORMAT_VOXWARE_VR12: + return "WAVE_FORMAT_VOXWARE_VR12"; + case WAVE_FORMAT_VOXWARE_VR18: + return "WAVE_FORMAT_VOXWARE_VR18"; + case WAVE_FORMAT_VOXWARE_TQ40: + return "WAVE_FORMAT_VOXWARE_TQ40"; + case WAVE_FORMAT_SOFTSOUND: + return "WAVE_FORMAT_SOFTSOUND"; + case WAVE_FORMAT_VOXWARE_TQ60: + return "WAVE_FORMAT_VOXWARE_TQ60"; + case WAVE_FORMAT_MSRT24: + return "WAVE_FORMAT_MSRT24"; + case WAVE_FORMAT_G729A: + return "WAVE_FORMAT_G729A"; + case WAVE_FORMAT_MVI_MV12: + return "WAVE_FORMAT_MVI_MV12"; + case WAVE_FORMAT_DF_G726: + return "WAVE_FORMAT_DF_G726"; + case WAVE_FORMAT_DF_GSM610: + return "WAVE_FORMAT_DF_GSM610"; + case WAVE_FORMAT_ISIAUDIO: + return "WAVE_FORMAT_ISIAUDIO"; + case WAVE_FORMAT_ONLIVE: + return "WAVE_FORMAT_ONLIVE"; + case WAVE_FORMAT_SBC24: + return "WAVE_FORMAT_SBC24"; + case WAVE_FORMAT_DOLBY_AC3_SPDIF: + return "WAVE_FORMAT_DOLBY_AC3_SPDIF"; + case WAVE_FORMAT_ZYXEL_ADPCM: + return "WAVE_FORMAT_ZYXEL_ADPCM"; + case WAVE_FORMAT_PHILIPS_LPCBB: + return "WAVE_FORMAT_PHILIPS_LPCBB"; + case WAVE_FORMAT_PACKED: + return "WAVE_FORMAT_PACKED"; + case WAVE_FORMAT_RHETOREX_ADPCM: + return "WAVE_FORMAT_RHETOREX_ADPCM"; + case WAVE_FORMAT_IRAT: + return "WAVE_FORMAT_IRAT"; + case WAVE_FORMAT_VIVO_G723: + return "WAVE_FORMAT_VIVO_G723"; + case WAVE_FORMAT_VIVO_SIREN: + return "WAVE_FORMAT_VIVO_SIREN"; + case WAVE_FORMAT_DIGITAL_G723: + return "WAVE_FORMAT_DIGITAL_G723"; + case WAVE_FORMAT_WMAUDIO2: + return "WAVE_FORMAT_WMAUDIO2"; + case WAVE_FORMAT_WMAUDIO3: + return "WAVE_FORMAT_WMAUDIO3"; + case WAVE_FORMAT_WMAUDIO_LOSSLESS: + return "WAVE_FORMAT_WMAUDIO_LOSSLESS"; + case WAVE_FORMAT_CREATIVE_ADPCM: + return "WAVE_FORMAT_CREATIVE_ADPCM"; + case WAVE_FORMAT_CREATIVE_FASTSPEECH8: + return "WAVE_FORMAT_CREATIVE_FASTSPEECH8"; + case WAVE_FORMAT_CREATIVE_FASTSPEECH10: + return "WAVE_FORMAT_CREATIVE_FASTSPEECH10"; + case WAVE_FORMAT_QUARTERDECK: + return "WAVE_FORMAT_QUARTERDECK"; + case WAVE_FORMAT_FM_TOWNS_SND: + return "WAVE_FORMAT_FM_TOWNS_SND"; + case WAVE_FORMAT_BTV_DIGITAL: + return "WAVE_FORMAT_BTV_DIGITAL"; + case WAVE_FORMAT_VME_VMPCM: + return "WAVE_FORMAT_VME_VMPCM"; + case WAVE_FORMAT_OLIGSM: + return "WAVE_FORMAT_OLIGSM"; + case WAVE_FORMAT_OLIADPCM: + return "WAVE_FORMAT_OLIADPCM"; + case WAVE_FORMAT_OLICELP: + return "WAVE_FORMAT_OLICELP"; + case WAVE_FORMAT_OLISBC: + return "WAVE_FORMAT_OLISBC"; + case WAVE_FORMAT_OLIOPR: + return "WAVE_FORMAT_OLIOPR"; + case WAVE_FORMAT_LH_CODEC: + return "WAVE_FORMAT_LH_CODEC"; + case WAVE_FORMAT_NORRIS: + return "WAVE_FORMAT_NORRIS"; + case WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS: + return "WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS"; + case WAVE_FORMAT_DVM: + return "WAVE_FORMAT_DVM"; + case WAVE_FORMAT_AAC_MS: + return "WAVE_FORMAT_AAC_MS"; + } + + return "WAVE_FORMAT_UNKNOWN"; +} + +static int +rdp_audio_setup_listener(void) +{ + char *sink_socket_path; + int fd; + struct sockaddr_un s; + int bytes; + int error; + + fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + weston_log("Couldn't create listener socket.\n"); + return -1; + } + + sink_socket_path = getenv("PULSE_AUDIO_RDP_SINK"); + if (sink_socket_path == NULL || sink_socket_path[0] == '\0') { + close(fd); + weston_log("Environment variable PULSE_AUDIO_RDP_SINK not set.\n"); + return -1; + } + + memset(&s, 0, sizeof(s)); + s.sun_family = AF_UNIX; + bytes = sizeof(s.sun_path) - 1; + snprintf(s.sun_path, bytes, "%s", sink_socket_path); + + remove(s.sun_path); + + weston_log("Pulse Audio Sink listener socket on %s\n", s.sun_path); + error = bind(fd, (struct sockaddr *)&s, sizeof(struct sockaddr_un)); + if (error != 0) { + close(fd); + weston_log("Failed to bind to listener socket (%d).\n", error); + return -1; + } + + listen(fd, 100); + return fd; +} + +static UINT64 +rdp_audio_timestamp() +{ + struct timespec time; + clock_gettime(CLOCK_REALTIME, &time); + return time.tv_sec * 1000000 + time.tv_nsec / 1000; +} + +static UINT +rdp_audio_client_confirm_block( + RdpsndServerContext* context, + BYTE confirmBlockNum, + UINT16 wtimestamp) +{ + struct audio_out_private *priv = context->data; + + if (priv->blockInfo[confirmBlockNum].ackReceivedTime != 0) { + assert(priv->blockInfo[confirmBlockNum].ackPlayedTime == 0); + priv->blockInfo[confirmBlockNum].ackPlayedTime = rdp_audio_timestamp(); + + /* + * Sum up all of the latency, we'll compute an average for the last period + * requested by the sink. + */ + if (priv->nextValidBlock == -1 || priv->nextValidBlock == confirmBlockNum) { + priv->nextValidBlock = -1; + + priv->accumulatedRenderedLatency += + priv->blockInfo[confirmBlockNum].ackPlayedTime - + priv->blockInfo[confirmBlockNum].submissionTime; + priv->accumulatedRenderedLatencyCount++; + } + + uint64_t one = 1; + if (write(priv->audioSem, &one, sizeof(one)) != sizeof(uint64_t)) { + weston_log("RDP Audio error at confirm_block while writing to audioSem (%s)\n", strerror(errno)); + return ERROR_INTERNAL_ERROR; + } + + } else { + priv->blockInfo[confirmBlockNum].ackReceivedTime = rdp_audio_timestamp(); + + priv->accumulatedNetworkLatency += + priv->blockInfo[confirmBlockNum].ackReceivedTime - + priv->blockInfo[confirmBlockNum].submissionTime; + priv->accumulatedNetworkLatencyCount++; + } + + return 0; +} + +static int +rdp_audio_handle_version( + struct audio_out_private *priv, + UINT PAVersion) +{ + uint32_t version = RDP_SINK_INTERFACE_VERSION; + ssize_t sizeSent; + + priv->PAVersion = PAVersion; + + weston_log("RDP Sink version (%d - %d)\n", PAVersion, version); + + sizeSent = send(priv->pulseAudioSinkFd, &version, + sizeof(version), MSG_DONTWAIT); + if (sizeSent != sizeof(version)) { + weston_log("RDP audio error responding to version request sent:%ld. %s\n", + sizeSent, strerror(errno)); + return -1; + } + + return 0; +} + +static int +rdp_audio_handle_transfer( + struct audio_out_private *priv, + UINT bytesLeft, + UINT64 timestamp) +{ + int nbFrames = bytesLeft / priv->bytesPerFrame; + UINT bytesRead = 0; + ssize_t sizeRead = 0; + + if (bytesLeft > priv->audioBufferSize) { + if(priv->audioBuffer) + free(priv->audioBuffer); + + priv->audioBuffer = zalloc(bytesLeft); + if (!priv->audioBuffer) { + weston_log("RDP Audio error zalloc(%d) failed.\n", bytesLeft); + return -1; + } + priv->audioBufferSize = bytesLeft; + } + + assert((bytesLeft % priv->bytesPerFrame) == 0); + + /* + * Read the expected amount of data over the sink before sending it to RDP + */ + while (bytesLeft > 0) { + sizeRead = read(priv->pulseAudioSinkFd, + priv->audioBuffer + bytesRead, bytesLeft); + if (sizeRead <= 0) { + weston_log("RDP Audio error while reading data from sink socket sizeRead:%ld. %s\n", sizeRead, strerror(errno)); + return -1; + } + bytesRead += sizeRead; + bytesLeft -= sizeRead; + } + + BYTE* audioBuffer = priv->audioBuffer; + while (nbFrames > 0) { + /* + * Ensure we don't overrun our audio buffers. + * + * SendSamples may not submit audio every time, it may accumulate audio and + * submit on subsequent call. We've sent latency such that it should never + * submit more than one packet of audio over the RDP channel for one + * of our incoming audio packet from pulse. + */ + uint64_t dummy; + if (read(priv->audioSem, &dummy, sizeof(dummy)) != sizeof(uint64_t)) { + weston_log("RDP Audio error at handle_transfer while reading from audioSem (%s)\n", strerror(errno)); + return -1; + } + + /* + * Setup tracking of all block sent by RDP so we can compute latency later + * when those block gets acknowledge by the client. + * + * Set 0 to timestamp to disable A/V sync at client side. + */ + BYTE block_no = priv->rdpsnd_server_context->block_no; + priv->blockInfo[block_no].submissionTime = timestamp; + priv->blockInfo[block_no].ackReceivedTime = 0; + priv->blockInfo[block_no].ackPlayedTime = 0; + if (priv->rdpsnd_server_context->SendSamples(priv->rdpsnd_server_context, + audioBuffer, + MIN(nbFrames, AUDIO_FRAMES_PER_RDP_PACKET), + 0) != 0) { + weston_log("RDP Audio error while SendSamples\n"); + return -1; + } + + if (block_no == priv->rdpsnd_server_context->block_no) { + /* + * Didn't submit any audio this time around, adjust our semaphore. + */ + uint64_t one = 1; + if (write(priv->audioSem, &one, sizeof(one)) != sizeof(uint64_t)) { + weston_log("RDP Audio error at handle_transfer while writing to audioSem (%s)\n", strerror(errno)); + return -1; + } + } else { + /* + * There shouldn't be more than one packet of audio sent by RDP. + */ + assert((block_no > priv->rdpsnd_server_context->block_no) || (block_no+1) == priv->rdpsnd_server_context->block_no); + assert((block_no < priv->rdpsnd_server_context->block_no) || (block_no==255 && priv->rdpsnd_server_context->block_no==0)); + } + + audioBuffer += AUDIO_FRAMES_PER_RDP_PACKET * priv->bytesPerFrame; + nbFrames -= AUDIO_FRAMES_PER_RDP_PACKET; + } + + return 0; +} + +static int +rdp_audio_handle_get_latency(struct audio_out_private *priv) +{ + UINT networkLatency; + UINT renderedLatency; + ssize_t sizeSent; + + if (priv->accumulatedNetworkLatencyCount > 0) { + networkLatency = priv->accumulatedNetworkLatency / priv->accumulatedNetworkLatencyCount; + priv->lastNetworkLatency = networkLatency; + priv->accumulatedNetworkLatency = 0; + priv->accumulatedNetworkLatencyCount = 0; + } else { + networkLatency = priv->lastNetworkLatency; + } + + if (priv->accumulatedRenderedLatencyCount > 0) { + renderedLatency = priv->accumulatedRenderedLatency / priv->accumulatedRenderedLatencyCount; + priv->lastRenderedLatency = renderedLatency; + priv->accumulatedRenderedLatency = 0; + priv->accumulatedRenderedLatencyCount = 0; + } else { + renderedLatency = priv->lastRenderedLatency; + } + + if (renderedLatency > networkLatency) + renderedLatency -= networkLatency; + + sizeSent = send(priv->pulseAudioSinkFd, &renderedLatency, + sizeof(renderedLatency), MSG_DONTWAIT); + if (sizeSent != sizeof(renderedLatency)) { + weston_log("RDP audio error responding to latency request sent:%ld. %s\n", + sizeSent, strerror(errno)); + return -1; + } + + return 0; +} + +static void* +rdp_audio_pulse_audio_sink_thread(void *context) +{ + struct audio_out_private *priv = context; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + + assert(priv->pulseAudioSinkListenerFd != 0); + + for (;;) { + rdp_audio_debug(priv, "Audio sink thread: Listening for audio connection.\n"); + + if (priv->audioExitSignal) { + rdp_audio_debug(priv, "Audio sink thread is asked to exit (accept loop)\n"); + break; + } + + /* + * Wait for a connection on our listening socket + */ + assert(priv->pulseAudioSinkFd < 0); + priv->pulseAudioSinkFd = accept(priv->pulseAudioSinkListenerFd, + NULL, NULL); + if (priv->pulseAudioSinkFd < 0) { + weston_log("Audio sink thread: Listener connection error (%s)\n", strerror(errno)); + continue; + } else { + rdp_audio_debug(priv, "Audio sink thread: connection successful on socket (%d).\n", + priv->pulseAudioSinkFd); + } + + /* + * Read audio from the socket and stream to the RDP Client. + */ + for (;;) { + rdp_audio_cmd_header header; + ssize_t sizeRead; + + sizeRead = read(priv->pulseAudioSinkFd, &header, sizeof(header)); + /* pulseaudio RDP sink always send sizeof(header) regardless command type. */ + if (sizeRead != sizeof(header)) { + weston_log("Audio sink thread: error while reading from sink socket sizeRead:%ld. %s\n", + sizeRead, strerror(errno)); + break; + } else if (header.cmd == RDP_AUDIO_CMD_VERSION) { + rdp_audio_debug(priv, "Audio sink command RDP_AUDIO_CMD_VERSION: %d\n", header.version); + if (rdp_audio_handle_version(priv, header.version) < 0) + break; + } else if (header.cmd == RDP_AUDIO_CMD_TRANSFER) { + rdp_audio_debug(priv, "Audio sink command RDP_AUDIO_CMD_TRANSFER: %d\n", header.transfer.bytes); + if (rdp_audio_handle_transfer(priv, header.transfer.bytes, header.transfer.timestamp) < 0) + break; + } else if (header.cmd == RDP_AUDIO_CMD_GET_LATENCY) { + rdp_audio_debug(priv, "Audio sink command RDP_AUDIO_CMD_GET_LATENCY\n"); + if (rdp_audio_handle_get_latency(priv) < 0) + break; + } else if (header.cmd == RDP_AUDIO_CMD_RESET_LATENCY) { + rdp_audio_debug(priv, "Audio sink command RDP_AUDIO_CMD_RESET_LATENCY\n"); + priv->nextValidBlock = priv->rdpsnd_server_context->block_no; + priv->lastNetworkLatency = 0; + priv->accumulatedNetworkLatency = 0; + priv->accumulatedNetworkLatencyCount = 0; + priv->lastRenderedLatency = 0; + priv->accumulatedRenderedLatency = 0; + priv->accumulatedRenderedLatencyCount = 0; + } else { + weston_log("Audio sink thread: unknown command from sink.\n"); + break; + } + } + + close(priv->pulseAudioSinkFd); + priv->pulseAudioSinkFd = -1; + } + + assert(priv->pulseAudioSinkFd < 0); + + return NULL; +} + +static void +rdp_audio_client_activated(RdpsndServerContext* context) +{ + struct audio_out_private *priv = context->data; + int format = -1; + int i, j; + + rdp_audio_debug(priv, "rdp_audio_server_activated: %d audio formats supported.\n", + context->num_client_formats); + + for (i = 0; i < context->num_client_formats; i++) { + rdp_audio_debug(priv, "\t[%d] - Format(%s) - Bits(%d), Channels(%d), Frequency(%d)\n", + i, + AUDIO_FORMAT_to_String(context->client_formats[i].wFormatTag), + context->client_formats[i].wBitsPerSample, + context->client_formats[i].nChannels, + context->client_formats[i].nSamplesPerSec); + + for (j = 0; j < (int)context->num_server_formats; j++) { + if ((context->client_formats[i].wFormatTag == context->server_formats[j].wFormatTag) && + (context->client_formats[i].nChannels == context->server_formats[j].nChannels) && + (context->client_formats[i].nSamplesPerSec == context->server_formats[j].nSamplesPerSec)) { + rdp_audio_debug(priv, "RDPAudio - Agreed on format %d.\n", i); + format = i; + break; + } + } + } + + if (format != -1) { + priv->nextValidBlock = -1; + priv->bytesPerFrame = (context->client_formats[format].wBitsPerSample / 8) * context->client_formats[format].nChannels; + context->latency = AUDIO_LATENCY; + + rdp_audio_debug(priv, "rdp_audio_server_activated: bytesPerFrame:%d, latency:%d\n", + priv->bytesPerFrame, context->latency); + + context->SelectFormat(context, format); + context->SetVolume(context, 0x7FFF, 0x7FFF); + + priv->pulseAudioSinkListenerFd = rdp_audio_setup_listener(); + if (priv->pulseAudioSinkListenerFd < 0) { + weston_log("RDPAudio - Failed to create listener socket\n"); + } else if (pthread_create(&priv->pulseAudioSinkThread, NULL, rdp_audio_pulse_audio_sink_thread, (void*)priv) < 0) { + weston_log("RDPAudio - Failed to start Pulse Audio Sink Thread. No audio will be available.\n"); + } + } else { + weston_log("RDPAudio - No agreeded format.\n"); + } +} + +void * +rdp_audio_out_init(struct weston_compositor *c, HANDLE vcm) +{ + struct audio_out_private *priv; + char *s; + + priv = xzalloc(sizeof *priv); + priv->rdpsnd_server_context = rdpsnd_server_context_new(vcm); + if (!priv->rdpsnd_server_context) { + weston_log("RDPAudio - Couldn't initialize audio virtual channel.\n"); + return NULL; + } + + priv->debug = weston_compositor_add_log_scope(c, "rdp-audio", + "Debug messages for RDP audio output\n", + NULL, NULL, NULL); + + priv->audioExitSignal = FALSE; + priv->pulseAudioSinkThread = 0; + priv->pulseAudioSinkListenerFd = -1; + priv->pulseAudioSinkFd = -1; + priv->audioBuffer = NULL; + + priv->audioSem = eventfd(256, EFD_SEMAPHORE | EFD_CLOEXEC); + if (!priv->audioSem) { + weston_log("RDPAudio - Couldn't initialize event semaphore.\n"); + goto Error_Exit; + } + + /* this will be freed by FreeRDP at rdpsnd_server_context_free. */ + AUDIO_FORMAT *audio_formats = malloc(sizeof rdp_audio_supported_audio_formats); + if (!audio_formats) { + weston_log("RDPAudio - Couldn't allocate memory for audio formats.\n"); + goto Error_Exit; + } + memcpy(audio_formats, rdp_audio_supported_audio_formats, sizeof rdp_audio_supported_audio_formats); + + priv->rdpsnd_server_context->data = (void*)priv; + priv->rdpsnd_server_context->Activated = rdp_audio_client_activated; + priv->rdpsnd_server_context->ConfirmBlock = rdp_audio_client_confirm_block; + priv->rdpsnd_server_context->num_server_formats = ARRAYSIZE(rdp_audio_supported_audio_formats); + priv->rdpsnd_server_context->server_formats = audio_formats; + priv->rdpsnd_server_context->src_format = &rdp_audio_supported_audio_formats[0]; +#if HAVE_RDPSND_DYNAMIC_VIRTUAL_CHANNEL + priv->rdpsnd_server_context->use_dynamic_virtual_channel = TRUE; + s = getenv("WESTON_RDP_DISABLE_AUDIO_PLAYBACK_DYNAMIC_VIRTUAL_CHANNEL"); + if (s) { + if (strcmp(s, "true") == 0) { + priv->rdpsnd_server_context->use_dynamic_virtual_channel = FALSE; + weston_log("RDPAudio - force static channel.\n"); + } + } +#endif // HAVE_RDPAUDIO_DYNAMIC_VIRTUAL_CHANNEL + + /* Calling Initialize does Start as well */ + if (priv->rdpsnd_server_context->Initialize(priv->rdpsnd_server_context, TRUE) != 0) + goto Error_Exit; + + return priv; + +Error_Exit: + if (priv->debug) + weston_log_scope_destroy(priv->debug); + + if (priv->audioSem != -1) { + close(priv->audioSem); + priv->audioSem = -1; + } + + if (priv->rdpsnd_server_context) { + rdpsnd_server_context_free(priv->rdpsnd_server_context); + priv->rdpsnd_server_context = NULL; + } + + free(priv); + return NULL; +} + +void +rdp_audio_out_destroy(void *audio_out_private) +{ + struct audio_out_private *priv = audio_out_private; + + if (priv->rdpsnd_server_context) { + + if (priv->pulseAudioSinkThread) { + priv->audioExitSignal = TRUE; + shutdown(priv->pulseAudioSinkListenerFd, SHUT_RDWR); + shutdown(priv->pulseAudioSinkFd, SHUT_RDWR); + pthread_cancel(priv->pulseAudioSinkThread); + pthread_join(priv->pulseAudioSinkThread, NULL); + + if (priv->pulseAudioSinkListenerFd != -1) { + close(priv->pulseAudioSinkListenerFd); + priv->pulseAudioSinkListenerFd = -1; + } + + if (priv->pulseAudioSinkFd != -1) { + close(priv->pulseAudioSinkFd); + priv->pulseAudioSinkFd = -1; + } + + if (priv->audioBuffer) { + free(priv->audioBuffer); + priv->audioBuffer = NULL; + } + + priv->pulseAudioSinkThread = 0; + } + + assert(priv->pulseAudioSinkListenerFd < 0); + assert(priv->pulseAudioSinkFd < 0); + assert(priv->audioBuffer == NULL); + + priv->rdpsnd_server_context->Close(priv->rdpsnd_server_context); + priv->rdpsnd_server_context->Stop(priv->rdpsnd_server_context); + + if (priv->audioSem != -1) { + close(priv->audioSem); + priv->audioSem = -1; + } + + rdpsnd_server_context_free(priv->rdpsnd_server_context); + priv->rdpsnd_server_context = NULL; + } + free(priv); +} diff --git a/compositor/rdpaudio.h b/compositor/rdpaudio.h new file mode 100644 index 000000000..c09fc271d --- /dev/null +++ b/compositor/rdpaudio.h @@ -0,0 +1,90 @@ +/* + * Copyright © 2022 Microsoft + * + * 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 RDP_AUDIO_H +#define RDP_AUDIO_H + +#include +#include +#include +#include + +#define rdp_audio_debug(p, ...) \ + weston_log_scope_printf((p)->debug, __VA_ARGS__) + +typedef struct _rdp_audio_block_info { + UINT64 submissionTime; + UINT64 ackReceivedTime; + UINT64 ackPlayedTime; +} rdp_audio_block_info; + +struct audio_out_private { + RdpsndServerContext* rdpsnd_server_context; + struct weston_log_scope *debug; + BOOL audioExitSignal; + int pulseAudioSinkListenerFd; + int pulseAudioSinkFd; + pthread_t pulseAudioSinkThread; + int bytesPerFrame; + UINT audioBufferSize; + BYTE* audioBuffer; + BYTE lastBlockSent; + UINT64 lastNetworkLatency; + UINT64 accumulatedNetworkLatency; + UINT accumulatedNetworkLatencyCount; + UINT64 lastRenderedLatency; + UINT64 accumulatedRenderedLatency; + UINT accumulatedRenderedLatencyCount; + rdp_audio_block_info blockInfo[256]; + int nextValidBlock; + UINT PAVersion; + int audioSem; +}; + +struct audio_in_private { + audin_server_context* audin_server_context; + struct weston_log_scope *debug; + BOOL audioInExitSignal; + int pulseAudioSourceListenerFd; + int pulseAudioSourceFd; + int closeAudioSourceFd; + pthread_t pulseAudioSourceThread; + BOOL isAudioInStreamOpened; +}; + +void * +rdp_audio_out_init(struct weston_compositor *c, HANDLE vcm); + +void +rdp_audio_out_destroy(void *audio_out_private); + +void * +rdp_audio_in_init(struct weston_compositor *c, HANDLE vcm); + +void +rdp_audio_in_destroy(void *audio_in_private); + + +#endif diff --git a/compositor/rdpaudioin.c b/compositor/rdpaudioin.c new file mode 100644 index 000000000..af367705f --- /dev/null +++ b/compositor/rdpaudioin.c @@ -0,0 +1,580 @@ +/* + * Copyright © 2020 Microsoft + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rdpaudio.h" +#include +#include + +static AUDIO_FORMAT rdp_audioin_supported_audio_formats[] = { + { WAVE_FORMAT_PCM, 1, 44100, 88200, 2, 16, 0, NULL }, + }; + +static char* +AUDIO_FORMAT_to_String(UINT16 format) +{ + switch (format) { + case WAVE_FORMAT_UNKNOWN: + return "WAVE_FORMAT_UNKNOWN"; + case WAVE_FORMAT_PCM: + return "WAVE_FORMAT_PCM"; + case WAVE_FORMAT_ADPCM: + return "WAVE_FORMAT_ADPCM"; + case WAVE_FORMAT_IEEE_FLOAT: + return "WAVE_FORMAT_IEEE_FLOAT"; + case WAVE_FORMAT_VSELP: + return "WAVE_FORMAT_VSELP"; + case WAVE_FORMAT_IBM_CVSD: + return "WAVE_FORMAT_IBM_CVSD"; + case WAVE_FORMAT_ALAW: + return "WAVE_FORMAT_ALAW"; + case WAVE_FORMAT_MULAW: + return "WAVE_FORMAT_MULAW"; + case WAVE_FORMAT_OKI_ADPCM: + return "WAVE_FORMAT_OKI_ADPCM"; + case WAVE_FORMAT_DVI_ADPCM: + return "WAVE_FORMAT_DVI_ADPCM"; + case WAVE_FORMAT_MEDIASPACE_ADPCM: + return "WAVE_FORMAT_MEDIASPACE_ADPCM"; + case WAVE_FORMAT_SIERRA_ADPCM: + return "WAVE_FORMAT_SIERRA_ADPCM"; + case WAVE_FORMAT_G723_ADPCM: + return "WAVE_FORMAT_G723_ADPCM"; + case WAVE_FORMAT_DIGISTD: + return "WAVE_FORMAT_DIGISTD"; + case WAVE_FORMAT_DIGIFIX: + return "WAVE_FORMAT_DIGIFIX"; + case WAVE_FORMAT_DIALOGIC_OKI_ADPCM: + return "WAVE_FORMAT_DIALOGIC_OKI_ADPCM"; + case WAVE_FORMAT_MEDIAVISION_ADPCM: + return "WAVE_FORMAT_MEDIAVISION_ADPCM"; + case WAVE_FORMAT_CU_CODEC: + return "WAVE_FORMAT_CU_CODEC"; + case WAVE_FORMAT_YAMAHA_ADPCM: + return "WAVE_FORMAT_YAMAHA_ADPCM"; + case WAVE_FORMAT_SONARC: + return "WAVE_FORMAT_SONARC"; + case WAVE_FORMAT_DSPGROUP_TRUESPEECH: + return "WAVE_FORMAT_DSPGROUP_TRUESPEECH"; + case WAVE_FORMAT_ECHOSC1: + return "WAVE_FORMAT_ECHOSC1"; + case WAVE_FORMAT_AUDIOFILE_AF36: + return "WAVE_FORMAT_AUDIOFILE_AF36"; + case WAVE_FORMAT_APTX: + return "WAVE_FORMAT_APTX"; + case WAVE_FORMAT_AUDIOFILE_AF10: + return "WAVE_FORMAT_AUDIOFILE_AF10"; + case WAVE_FORMAT_PROSODY_1612: + return "WAVE_FORMAT_PROSODY_1612"; + case WAVE_FORMAT_DOLBY_AC2: + return "WAVE_FORMAT_DOLBY_AC2"; + case WAVE_FORMAT_GSM610: + return "WAVE_FORMAT_GSM610"; + case WAVE_FORMAT_MSNAUDIO: + return "WAVE_FORMAT_MSNAUDIO"; + case WAVE_FORMAT_ANTEX_ADPCME: + return "WAVE_FORMAT_ANTEX_ADPCME"; + case WAVE_FORMAT_CONTROL_RES_VQLPC: + return "WAVE_FORMAT_CONTROL_RES_VQLPC"; + case WAVE_FORMAT_DIGIREAL: + return "WAVE_FORMAT_DIGIREAL"; + case WAVE_FORMAT_DIGIADPCM: + return "WAVE_FORMAT_DIGIADPCM"; + case WAVE_FORMAT_CONTROL_RES_CR10: + return "WAVE_FORMAT_CONTROL_RES_CR10"; + case WAVE_FORMAT_NMS_VBXADPCM: + return "WAVE_FORMAT_NMS_VBXADPCM"; + case WAVE_FORMAT_ROLAND_RDAC: + return "WAVE_FORMAT_ROLAND_RDAC"; + case WAVE_FORMAT_ECHOSC3: + return "WAVE_FORMAT_ECHOSC3"; + case WAVE_FORMAT_ROCKWELL_ADPCM: + return "WAVE_FORMAT_ROCKWELL_ADPCM"; + case WAVE_FORMAT_ROCKWELL_DIGITALK: + return "WAVE_FORMAT_ROCKWELL_DIGITALK"; + case WAVE_FORMAT_XEBEC: + return "WAVE_FORMAT_XEBEC"; + case WAVE_FORMAT_G721_ADPCM: + return "WAVE_FORMAT_G721_ADPCM"; + case WAVE_FORMAT_G728_CELP: + return "WAVE_FORMAT_G728_CELP"; + case WAVE_FORMAT_MSG723: + return "WAVE_FORMAT_MSG723"; + case WAVE_FORMAT_MPEG: + return "WAVE_FORMAT_MPEG"; + case WAVE_FORMAT_RT24: + return "WAVE_FORMAT_RT24"; + case WAVE_FORMAT_PAC: + return "WAVE_FORMAT_PAC"; + case WAVE_FORMAT_MPEGLAYER3: + return "WAVE_FORMAT_MPEGLAYER3"; + case WAVE_FORMAT_LUCENT_G723: + return "WAVE_FORMAT_LUCENT_G723"; + case WAVE_FORMAT_CIRRUS: + return "WAVE_FORMAT_CIRRUS"; + case WAVE_FORMAT_ESPCM: + return "WAVE_FORMAT_ESPCM"; + case WAVE_FORMAT_VOXWARE: + return "WAVE_FORMAT_VOXWARE"; + case WAVE_FORMAT_CANOPUS_ATRAC: + return "WAVE_FORMAT_CANOPUS_ATRAC"; + case WAVE_FORMAT_G726_ADPCM: + return "WAVE_FORMAT_G726_ADPCM"; + case WAVE_FORMAT_G722_ADPCM: + return "WAVE_FORMAT_G722_ADPCM"; + case WAVE_FORMAT_DSAT: + return "WAVE_FORMAT_DSAT"; + case WAVE_FORMAT_DSAT_DISPLAY: + return "WAVE_FORMAT_DSAT_DISPLAY"; + case WAVE_FORMAT_VOXWARE_BYTE_ALIGNED: + return "WAVE_FORMAT_VOXWARE_BYTE_ALIGNED"; + case WAVE_FORMAT_VOXWARE_AC8: + return "WAVE_FORMAT_VOXWARE_AC8"; + case WAVE_FORMAT_VOXWARE_AC10: + return "WAVE_FORMAT_VOXWARE_AC10"; + case WAVE_FORMAT_VOXWARE_AC16: + return "WAVE_FORMAT_VOXWARE_AC16"; + case WAVE_FORMAT_VOXWARE_AC20: + return "WAVE_FORMAT_VOXWARE_AC20"; + case WAVE_FORMAT_VOXWARE_RT24: + return "WAVE_FORMAT_VOXWARE_RT24"; + case WAVE_FORMAT_VOXWARE_RT29: + return "WAVE_FORMAT_VOXWARE_RT29"; + case WAVE_FORMAT_VOXWARE_RT29HW: + return "WAVE_FORMAT_VOXWARE_RT29HW"; + case WAVE_FORMAT_VOXWARE_VR12: + return "WAVE_FORMAT_VOXWARE_VR12"; + case WAVE_FORMAT_VOXWARE_VR18: + return "WAVE_FORMAT_VOXWARE_VR18"; + case WAVE_FORMAT_VOXWARE_TQ40: + return "WAVE_FORMAT_VOXWARE_TQ40"; + case WAVE_FORMAT_SOFTSOUND: + return "WAVE_FORMAT_SOFTSOUND"; + case WAVE_FORMAT_VOXWARE_TQ60: + return "WAVE_FORMAT_VOXWARE_TQ60"; + case WAVE_FORMAT_MSRT24: + return "WAVE_FORMAT_MSRT24"; + case WAVE_FORMAT_G729A: + return "WAVE_FORMAT_G729A"; + case WAVE_FORMAT_MVI_MV12: + return "WAVE_FORMAT_MVI_MV12"; + case WAVE_FORMAT_DF_G726: + return "WAVE_FORMAT_DF_G726"; + case WAVE_FORMAT_DF_GSM610: + return "WAVE_FORMAT_DF_GSM610"; + case WAVE_FORMAT_ISIAUDIO: + return "WAVE_FORMAT_ISIAUDIO"; + case WAVE_FORMAT_ONLIVE: + return "WAVE_FORMAT_ONLIVE"; + case WAVE_FORMAT_SBC24: + return "WAVE_FORMAT_SBC24"; + case WAVE_FORMAT_DOLBY_AC3_SPDIF: + return "WAVE_FORMAT_DOLBY_AC3_SPDIF"; + case WAVE_FORMAT_ZYXEL_ADPCM: + return "WAVE_FORMAT_ZYXEL_ADPCM"; + case WAVE_FORMAT_PHILIPS_LPCBB: + return "WAVE_FORMAT_PHILIPS_LPCBB"; + case WAVE_FORMAT_PACKED: + return "WAVE_FORMAT_PACKED"; + case WAVE_FORMAT_RHETOREX_ADPCM: + return "WAVE_FORMAT_RHETOREX_ADPCM"; + case WAVE_FORMAT_IRAT: + return "WAVE_FORMAT_IRAT"; + case WAVE_FORMAT_VIVO_G723: + return "WAVE_FORMAT_VIVO_G723"; + case WAVE_FORMAT_VIVO_SIREN: + return "WAVE_FORMAT_VIVO_SIREN"; + case WAVE_FORMAT_DIGITAL_G723: + return "WAVE_FORMAT_DIGITAL_G723"; + case WAVE_FORMAT_WMAUDIO2: + return "WAVE_FORMAT_WMAUDIO2"; + case WAVE_FORMAT_WMAUDIO3: + return "WAVE_FORMAT_WMAUDIO3"; + case WAVE_FORMAT_WMAUDIO_LOSSLESS: + return "WAVE_FORMAT_WMAUDIO_LOSSLESS"; + case WAVE_FORMAT_CREATIVE_ADPCM: + return "WAVE_FORMAT_CREATIVE_ADPCM"; + case WAVE_FORMAT_CREATIVE_FASTSPEECH8: + return "WAVE_FORMAT_CREATIVE_FASTSPEECH8"; + case WAVE_FORMAT_CREATIVE_FASTSPEECH10: + return "WAVE_FORMAT_CREATIVE_FASTSPEECH10"; + case WAVE_FORMAT_QUARTERDECK: + return "WAVE_FORMAT_QUARTERDECK"; + case WAVE_FORMAT_FM_TOWNS_SND: + return "WAVE_FORMAT_FM_TOWNS_SND"; + case WAVE_FORMAT_BTV_DIGITAL: + return "WAVE_FORMAT_BTV_DIGITAL"; + case WAVE_FORMAT_VME_VMPCM: + return "WAVE_FORMAT_VME_VMPCM"; + case WAVE_FORMAT_OLIGSM: + return "WAVE_FORMAT_OLIGSM"; + case WAVE_FORMAT_OLIADPCM: + return "WAVE_FORMAT_OLIADPCM"; + case WAVE_FORMAT_OLICELP: + return "WAVE_FORMAT_OLICELP"; + case WAVE_FORMAT_OLISBC: + return "WAVE_FORMAT_OLISBC"; + case WAVE_FORMAT_OLIOPR: + return "WAVE_FORMAT_OLIOPR"; + case WAVE_FORMAT_LH_CODEC: + return "WAVE_FORMAT_LH_CODEC"; + case WAVE_FORMAT_NORRIS: + return "WAVE_FORMAT_NORRIS"; + case WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS: + return "WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS"; + case WAVE_FORMAT_DVM: + return "WAVE_FORMAT_DVM"; + case WAVE_FORMAT_AAC_MS: + return "WAVE_FORMAT_AAC_MS"; + } + + return "WAVE_FORMAT_UNKNOWN"; +} + +static int +rdp_audioin_setup_listener(struct audio_in_private *priv) +{ + char *source_socket_path; + int fd; + struct sockaddr_un s; + int bytes; + int error; + + fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + weston_log("Couldn't create audioin listener socket.\n"); + return -1; + } + + source_socket_path = getenv("PULSE_AUDIO_RDP_SOURCE"); + if (source_socket_path == NULL || source_socket_path[0] == '\0') { + close(fd); + weston_log("Environment variable PULSE_AUDIO_RDP_SOURCE not set.\n"); + return -1; + } + + memset(&s, 0, sizeof(s)); + s.sun_family = AF_UNIX; + bytes = sizeof(s.sun_path) - 1; + snprintf(s.sun_path, bytes, "%s", source_socket_path); + + remove(s.sun_path); + + rdp_audio_debug(priv, "Pulse Audio source listener socket on %s\n", s.sun_path); + error = bind(fd, (struct sockaddr *)&s, sizeof(struct sockaddr_un)); + if (error != 0) { + close(fd); + weston_log("Failed to bind to listener socket for audioin (%d).\n", error); + return -1; + } + + listen(fd, 100); + return fd; +} + +static UINT +rdp_audioin_client_opening(audin_server_context* context) +{ + struct audio_in_private *priv = context->data; + int format = -1; + int i, j; + + rdp_audio_debug(priv, "RDP Audio Open: %d audio formats supported.\n", + (int)context->num_client_formats); + + for (i = 0; i < (int)context->num_client_formats; i++) { + rdp_audio_debug(priv, "\t[%d] - Format(%s) - Bits(%d), Channels(%d), Frequency(%d)\n", + i, + AUDIO_FORMAT_to_String(context->client_formats[i].wFormatTag), + context->client_formats[i].wBitsPerSample, + context->client_formats[i].nChannels, + context->client_formats[i].nSamplesPerSec); + + for (j = 0; j < (int)context->num_server_formats; j++) { + if ((context->client_formats[i].wFormatTag == context->server_formats[j].wFormatTag) && + (context->client_formats[i].nChannels == context->server_formats[j].nChannels) && + (context->client_formats[i].nSamplesPerSec == context->server_formats[j].nSamplesPerSec)) { + rdp_audio_debug(priv, "RDPAudioIn - Agreed on format %d.\n", i); + format = i; + break; + } + } + } + + if (format == -1) { + weston_log("RDPAudioIn - No agreeded format.\n"); + return ERROR_INVALID_DATA; + } + + context->SelectFormat(context, format); + priv->isAudioInStreamOpened = TRUE; + + return 0; +} + +static UINT +rdp_audioin_client_open_result( + audin_server_context* context, + UINT32 result) +{ + struct audio_in_private *priv = context->data; + + rdp_audio_debug(priv, "RDP AudioIn Open Result (%d)\n", result); + return 0; +} + +static UINT +rdp_audioin_client_receive_samples( + audin_server_context* context, + const AUDIO_FORMAT* format, + wStream* buf, + size_t nframes) +{ + struct audio_in_private *priv = context->data; + + if (!priv->isAudioInStreamOpened || priv->pulseAudioSourceFd == -1) { + weston_log("RDPAudioIn - audio stream is not opened.\n"); + return 0; + } + + if (nframes) { + assert(format->wFormatTag == WAVE_FORMAT_PCM); + assert(format->nChannels == 1); + assert(format->nSamplesPerSec == 44100); + assert(format->wBitsPerSample == 16); + assert(buf != NULL); + + int bytes = nframes * format->wBitsPerSample / 8; + int sent = send(priv->pulseAudioSourceFd, buf->buffer, bytes, 0); + if (sent != bytes) { + rdp_audio_debug(priv, "RDP AudioIn source send failed (sent:%d, bytes:%d) %s\n", + sent, bytes, strerror(errno)); + + /* Unblock worker thread to close pipe to pulseaudio */ + uint64_t one=1; + if (write(priv->closeAudioSourceFd, &one, sizeof(one)) != sizeof(uint64_t)) { + weston_log("RDP AudioIn error at receive_samples while writing to closeAudioSourceFd (%s)\n", strerror(errno)); + return ERROR_INTERNAL_ERROR; + } + + if (sent <= 0) { + /* return error to FreeRDP as failed to send samples to pulseaudio. */ + return ERROR_INTERNAL_ERROR; + } + } + } + + return 0; +} + +static void* +rdp_audioin_source_thread(void *context) +{ + struct audio_in_private *priv = context; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + + assert(priv->closeAudioSourceFd != -1); + assert(priv->pulseAudioSourceListenerFd != -1); + + for (;;) { + rdp_audio_debug(priv, "AudioIn source_thread: Listening for audio in connection.\n"); + + if (priv->audioInExitSignal) { + rdp_audio_debug(priv, "AudioIn source_thread is asked to exit (accept loop)\n"); + break; + } + + /* + * Wait for a connection on our listening socket + */ + priv->pulseAudioSourceFd = accept(priv->pulseAudioSourceListenerFd, NULL, NULL); + if (priv->pulseAudioSourceFd < 0) { + weston_log("AudioIn source thread: Listener connection error (%s)\n", strerror(errno)); + continue; + } else { + rdp_audio_debug(priv, "AudioIn connection successful on socket (%d).\n", priv->pulseAudioSourceFd); + if (priv->audin_server_context->Open(priv->audin_server_context)) { + rdp_audio_debug(priv, "RDP AudioIn opened.\n"); + /* + * Wait for the connection to be closed + */ + uint64_t dummy; + if (read(priv->closeAudioSourceFd, &dummy, sizeof(dummy)) != sizeof(uint64_t)) { + weston_log("RDP AudioIn wait on eventfd failed. thread exiting. %s\n", strerror(errno)); + break; + } + priv->audin_server_context->Close(priv->audin_server_context); + rdp_audio_debug(priv, "RDP AudioIn closed.\n"); + } else { + weston_log("Failed to open audio in connection with RDP client.\n"); + } + + close(priv->pulseAudioSourceFd); + priv->pulseAudioSourceFd = -1; + } + } + + if (priv->audin_server_context->IsOpen(priv->audin_server_context)) + priv->audin_server_context->Close(priv->audin_server_context); + + if (priv->pulseAudioSourceFd != -1) { + close(priv->pulseAudioSourceFd); + priv->pulseAudioSourceFd = -1; + } + + return NULL; +} + +void * +rdp_audio_in_init(struct weston_compositor *c, HANDLE vcm) +{ + struct audio_in_private *priv; + + priv = xzalloc(sizeof *priv); + priv->audin_server_context = audin_server_context_new(vcm); + if (!priv->audin_server_context) { + weston_log("RDPAudioIn - Couldn't initialize audio virtual channel.\n"); + return NULL; + } + priv->debug = weston_compositor_add_log_scope(c, "rdp-audio-in", + "Debug messages for RDP audio input\n", + NULL, NULL, NULL); + + priv->audioInExitSignal = FALSE; + priv->pulseAudioSourceThread = 0; + priv->pulseAudioSourceListenerFd = -1; + priv->pulseAudioSourceFd = -1; + priv->closeAudioSourceFd = -1; + + // this will be freed by FreeRDP at audin_server_context_free. + AUDIO_FORMAT *audio_formats = malloc(sizeof rdp_audioin_supported_audio_formats); + if (!audio_formats) { + weston_log("RDPAudioIn - Couldn't allocate memory for audio formats.\n"); + goto Error_Exit; + } + memcpy(audio_formats, rdp_audioin_supported_audio_formats, sizeof rdp_audioin_supported_audio_formats); + + priv->audin_server_context->data = (void*)priv; + priv->audin_server_context->Opening = rdp_audioin_client_opening; + priv->audin_server_context->OpenResult = rdp_audioin_client_open_result; + priv->audin_server_context->ReceiveSamples = rdp_audioin_client_receive_samples; + priv->audin_server_context->num_server_formats = ARRAYSIZE(rdp_audioin_supported_audio_formats); + priv->audin_server_context->server_formats = audio_formats; + priv->audin_server_context->dst_format = &rdp_audioin_supported_audio_formats[0]; + priv->audin_server_context->frames_per_packet = rdp_audioin_supported_audio_formats[0].nSamplesPerSec / 100; // 10ms per packet + + priv->closeAudioSourceFd = eventfd(0, EFD_CLOEXEC); + if (priv->closeAudioSourceFd < 0) { + weston_log("RDPAudioIn - Couldn't initialize eventfd.\n"); + goto Error_Exit; + } + + priv->pulseAudioSourceListenerFd = rdp_audioin_setup_listener(priv); + if (priv->pulseAudioSourceListenerFd < 0) { + weston_log("RDPAudioIn - rdp_audioin_setup_listener failed.\n"); + goto Error_Exit; + } + + if (pthread_create(&priv->pulseAudioSourceThread, NULL, rdp_audioin_source_thread, (void*)priv) < 0) { + weston_log("RDPAudioIn - Failed to start Pulse Audio Source Thread. No audio in will be available.\n"); + goto Error_Exit; + } + + return priv; + +Error_Exit: + if (priv->debug) + weston_log_scope_destroy(priv->debug); + + if (priv->pulseAudioSourceListenerFd != -1) { + close(priv->pulseAudioSourceListenerFd); + priv->pulseAudioSourceListenerFd = -1; + } + + if (priv->closeAudioSourceFd != -1) { + close(priv->closeAudioSourceFd); + priv->closeAudioSourceFd = -1; + } + + if (priv->audin_server_context) { + audin_server_context_free(priv->audin_server_context); + priv->audin_server_context = NULL; + } + free(priv); + + return NULL; // Continue without audio +} + +void +rdp_audio_in_destroy(void *audio_in_private) +{ + struct audio_in_private *priv = audio_in_private; + if (priv->audin_server_context) { + + if (priv->pulseAudioSourceThread) { + priv->audioInExitSignal = TRUE; + shutdown(priv->pulseAudioSourceListenerFd, SHUT_RDWR); + shutdown(priv->closeAudioSourceFd, SHUT_RDWR); + pthread_cancel(priv->pulseAudioSourceThread); + pthread_join(priv->pulseAudioSourceThread, NULL); + + if (priv->pulseAudioSourceListenerFd != -1) { + close(priv->pulseAudioSourceListenerFd); + priv->pulseAudioSourceListenerFd = -1; + } + + if (priv->closeAudioSourceFd != -1) { + close(priv->closeAudioSourceFd); + priv->closeAudioSourceFd = -1; + } + + priv->pulseAudioSourceThread = 0; + } + + assert(priv->pulseAudioSourceListenerFd < 0); + assert(priv->closeAudioSourceFd < 0); + + assert(!priv->audin_server_context->IsOpen(priv->audin_server_context)); + audin_server_context_free(priv->audin_server_context); + priv->audin_server_context = NULL; + } + free(priv); +} diff --git a/compositor/rdpdisp.c b/compositor/rdpdisp.c new file mode 100644 index 000000000..ce5d7a963 --- /dev/null +++ b/compositor/rdpdisp.c @@ -0,0 +1,419 @@ +/* + * Copyright © 2020 Microsoft + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "config.h" + +#include +#include + +#include "weston.h" +#include "rdpdisp.h" +#include + + +static bool +is_line_intersected(int l1, int l2, int r1, int r2) +{ + int l = l1 > r1 ? l1 : r1; + int r = l2 < r2 ? l2 : r2; + return (l < r); +} + +static int +compare_monitors_x(const void *p1, const void *p2) +{ + const struct weston_rdp_output_api *api; + const struct weston_head *whl = *(const struct weston_head **)p1; + const struct weston_head *whr = *(const struct weston_head **)p2; + rdpMonitor *l, *r; + + api = weston_rdp_output_get_api(whr->compositor); + l = api->head_get_rdpmonitor(whl); + r = api->head_get_rdpmonitor(whr); + + return l->x > r->x; +} + +static int +compare_monitors_y(const void *p1, const void *p2) +{ + const struct weston_rdp_output_api *api; + const struct weston_head *whl = *(const struct weston_head **)p1; + const struct weston_head *whr = *(const struct weston_head **)p2; + rdpMonitor *l, *r; + + api = weston_rdp_output_get_api(whr->compositor); + l = api->head_get_rdpmonitor(whl); + r = api->head_get_rdpmonitor(whr); + + return l->y > r->y; +} + +static float +disp_get_client_scale_from_monitor(struct weston_compositor *ec, const rdpMonitor *config) +{ + struct wet_rdp_params *rdp_params = wet_get_rdp_params(ec); + + if (config->attributes.desktopScaleFactor == 0.0) + return 1.0f; + + if (rdp_params->enable_hi_dpi_support) { + if (rdp_params->debug_desktop_scaling_factor) + return (float)rdp_params->debug_desktop_scaling_factor / 100.f; + else if (rdp_params->enable_fractional_hi_dpi_support) + return (float)config->attributes.desktopScaleFactor / 100.0f; + else if (rdp_params->enable_fractional_hi_dpi_roundup) + return (float)(int)((config->attributes.desktopScaleFactor + 50) / 100); + else + return (float)(int)(config->attributes.desktopScaleFactor / 100); + } else { + return 1.0f; + } +} + +static int +disp_get_output_scale_from_monitor(struct weston_compositor *ec, const rdpMonitor *config) +{ + return (int) disp_get_client_scale_from_monitor(ec, config); +} + +static rdpMonitor * +get_first_head_config(struct weston_compositor *ec) +{ + const struct weston_rdp_output_api *api = weston_rdp_output_get_api(ec); + struct weston_head *head; + + wl_list_for_each(head, &ec->head_list, compositor_link) + return api->head_get_rdpmonitor(head); + + return NULL; +} + +static void +sort_head_list(struct weston_compositor *ec, int (*compar)(const void *, const void *)) +{ + int count = wl_list_length(&ec->head_list); + struct weston_head *head_array[count]; + struct weston_head *iter, *tmp; + int i = 0; + + wl_list_for_each_safe(iter, tmp, &ec->head_list, compositor_link) { + head_array[i++] = iter; + wl_list_remove(&iter->compositor_link); + } + + qsort(head_array, count, sizeof(struct weston_head *), compar); + + wl_list_init(&ec->head_list); + for (i = 0; i < count; i++) + wl_list_insert(ec->head_list.prev, &head_array[i]->compositor_link); +} + +void +disp_monitor_validate_and_compute_layout(struct weston_compositor *ec) +{ + const struct weston_rdp_output_api *api = weston_rdp_output_get_api(ec); + struct weston_head *iter; + bool isConnected_H = false; + bool isConnected_V = false; + bool isScalingUsed = false; + bool isScalingSupported = true; + int upperLeftX = 0; + int upperLeftY = 0; + int i; + int count = wl_list_length(&ec->head_list); + pixman_rectangle32_t rectWeston[count]; + + wl_list_for_each(iter, &ec->head_list, compositor_link) { + rdpMonitor *head = api->head_get_rdpmonitor(iter); + float client_scale = disp_get_client_scale_from_monitor(ec, head); + + /* check if any monitor has scaling enabled */ + if (client_scale != 1.0f) + isScalingUsed = true; + + /* find upper-left corner of combined monitors in client space */ + if (upperLeftX > head->x) + upperLeftX = head->x; + if (upperLeftY > head->y) + upperLeftY = head->y; + } + assert(upperLeftX <= 0); + assert(upperLeftY <= 0); + weston_log("Client desktop upper left coordinate (%d,%d)\n", upperLeftX, upperLeftY); + + count = wl_list_length(&ec->head_list); + + if (count > 1) { + rdpMonitor *head, *last; + int32_t offsetFromOriginClient; + + /* first, sort monitors horizontally */ + sort_head_list(ec, compare_monitors_x); + head = get_first_head_config(ec); + last = head; + assert(upperLeftX == head->x); + + /* check if monitors are horizontally connected each other */ + offsetFromOriginClient = head->x + head->width; + i = 0; + wl_list_for_each(iter, &ec->head_list, compositor_link) { + rdpMonitor *cur = api->head_get_rdpmonitor(iter); + + i++; + if (i == 1) + continue; + + if (offsetFromOriginClient != cur->x) { + weston_log("\tRDP client reported monitors not horizontally connected each other at %d (x check)\n", i); + break; + } + offsetFromOriginClient += cur->width; + + if (!is_line_intersected(last->y, + last->y + last->height, + cur->y, + cur->y + cur->height)) { + weston_log("\tRDP client reported monitors not horizontally connected each other at %d (y check)\n\n", i); + break; + } + last = cur; + } + if (i == count) { + weston_log("\tAll monitors are horizontally placed\n"); + isConnected_H = true; + } else { + rdpMonitor *head, *last; + /* next, trying sort monitors vertically */ + sort_head_list(ec, compare_monitors_y); + head = get_first_head_config(ec); + last = head; + assert(upperLeftY == head->y); + + /* make sure monitors are horizontally connected each other */ + offsetFromOriginClient = head->y + head->height; + i = 0; + wl_list_for_each(iter, &ec->head_list, compositor_link) { + rdpMonitor *cur = api->head_get_rdpmonitor(iter); + + i++; + if (i == 1) + continue; + + if (offsetFromOriginClient != cur->y) { + weston_log("\tRDP client reported monitors not vertically connected each other at %d (y check)\n", i); + break; + } + offsetFromOriginClient += cur->height; + + if (!is_line_intersected(last->x, + last->x + last->width, + cur->x, + cur->x + cur->width)) { + weston_log("\tRDP client reported monitors not horizontally connected each other at %d (x check)\n\n", i); + break; + } + last = cur; + } + + if (i == count) { + weston_log("\tAll monitors are vertically placed\n"); + isConnected_V = true; + } + } + } else { + isConnected_H = true; + } + + if (isScalingUsed && (!isConnected_H && !isConnected_V)) { + /* scaling can't be supported in complex monitor placement */ + weston_log("\nWARNING\nWARNING\nWARNING: Scaling is used, but can't be supported in complex monitor placement\nWARNING\nWARNING\n"); + isScalingSupported = false; + } + + if (isScalingUsed && isScalingSupported) { + uint32_t offsetFromOriginWeston = 0; + i = 0; + + wl_list_for_each(iter, &ec->head_list, compositor_link) { + rdpMonitor *head = api->head_get_rdpmonitor(iter); + int scale = disp_get_output_scale_from_monitor(ec, head); + + rectWeston[i].width = head->width / scale; + rectWeston[i].height = head->height / scale; + if (isConnected_H) { + assert(isConnected_V == false); + rectWeston[i].x = offsetFromOriginWeston; + rectWeston[i].y = abs((upperLeftY - head->y) / scale); + offsetFromOriginWeston += rectWeston[i].width; + } else { + assert(isConnected_V == true); + rectWeston[i].x = abs((upperLeftX - head->x) / scale); + rectWeston[i].y = offsetFromOriginWeston; + offsetFromOriginWeston += rectWeston[i].height; + } + assert(rectWeston[i].x >= 0); + assert(rectWeston[i].y >= 0); + i++; + } + } else { + i = 0; + + /* no scaling is used or monitor placement is too complex to scale in weston space, fallback to 1.0f */ + wl_list_for_each(iter, &ec->head_list, compositor_link) { + rdpMonitor *head = api->head_get_rdpmonitor(iter); + + rectWeston[i].width = head->width; + rectWeston[i].height = head->height; + rectWeston[i].x = head->x + abs(upperLeftX); + rectWeston[i].y = head->y + abs(upperLeftY); + head->attributes.desktopScaleFactor = 0.0; + assert(rectWeston[i].x >= 0); + assert(rectWeston[i].y >= 0); + i++; + } + } + + weston_log("%s:---OUTPUT---\n", __func__); + i = 0; + wl_list_for_each(iter, &ec->head_list, compositor_link) { + rdpMonitor *head = api->head_get_rdpmonitor(iter); + float client_scale = disp_get_client_scale_from_monitor(ec, head); + int scale = disp_get_output_scale_from_monitor(ec, head); + + weston_log(" rdpMonitor[%d]: x:%d, y:%d, width:%d, height:%d, is_primary:%d\n", + i, head->x, head->y, + head->width, head->height, + head->is_primary); + weston_log(" rdpMonitor[%d]: weston x:%d, y:%d, width:%d, height:%d\n", + i, rectWeston[i].x, rectWeston[i].y, + rectWeston[i].width, rectWeston[i].height); + weston_log(" rdpMonitor[%d]: physicalWidth:%d, physicalHeight:%d, orientation:%d\n", + i, head->attributes.physicalWidth, + head->attributes.physicalHeight, + head->attributes.orientation); + weston_log(" rdpMonitor[%d]: desktopScaleFactor:%d, deviceScaleFactor:%d\n", + i, head->attributes.desktopScaleFactor, + head->attributes.deviceScaleFactor); + weston_log(" rdpMonitor[%d]: scale:%d, clientScale:%3.2f\n", + i, scale, client_scale); + i++; + } + + i = 0; + wl_list_for_each(iter, &ec->head_list, compositor_link) { + rdpMonitor *current = api->head_get_rdpmonitor(iter); + struct weston_output *output = iter->output; + float client_scale = disp_get_client_scale_from_monitor(ec, current); + int scale = disp_get_output_scale_from_monitor(ec, current); + struct wet_rdp_params *rdp_params = wet_get_rdp_params(ec); + int force_width = rdp_params->default_width; + int force_height = rdp_params->default_height; + struct weston_mode new_mode = {}; + + assert(output); + + if (!output->enabled) { + int width = force_width; + int height = force_height; + + width = width ? width : current->width; + height = height ? height : current->height; + + /* At startup the backend creates a 0,0 request + * If this wasn't overridden by config, just + * set it to 640 x 480. + */ + width = width ? width : 640; + height = height ? height : 480; + + new_mode.width = width; + new_mode.height = height; + api->output_set_mode(iter->output, &new_mode); + } else if (force_width && force_height) { + /* If we had command line parameters, we want to forcibly + * set any outputs changed by the backend matching code + * back to the forced settings. + */ + new_mode.width = force_width; + new_mode.height = force_height; + api->output_set_mode(iter->output, &new_mode); + } + weston_log("Head mode change:%s NEW width:%d, height:%d, scale:%d, clientScale:%f\n", + output->name, current->width, + current->height, + scale, + client_scale); + if (output->scale != scale) { + bool was_enabled = false; + + if (output->enabled) { + weston_output_disable(output); + was_enabled = true; + } + output->scale = 0; /* reset scale first, otherwise assert */ + weston_output_set_scale(output, scale); + if (was_enabled) + weston_output_enable(output); + } + + /* Notify clients for updated resolution/scale. */ + weston_output_set_transform(output, WL_OUTPUT_TRANSFORM_NORMAL); + + /* move output to final location */ + weston_log("move head/output %s (%d,%d) -> (%d,%d)\n", + iter->name, + iter->output->x, + iter->output->y, + rectWeston[i].x, + rectWeston[i].y); + /* Notify clients for updated output position. */ + weston_output_move(iter->output, + rectWeston[i].x, + rectWeston[i].y); + i++; + } + + /* make sure head list is not empty */ + assert(!wl_list_empty(&ec->head_list)); + + BOOL is_primary_found = FALSE; + i = 0; + wl_list_for_each(iter, &ec->head_list, compositor_link) { + rdpMonitor *current = api->head_get_rdpmonitor(iter); + + if (current->is_primary) { + weston_log("client origin (0,0) is (%d,%d) in Weston space\n", + rectWeston[i].x, + rectWeston[i].y); + /* primary must be at (0,0) in client space */ + assert(current->x == 0); + assert(current->y == 0); + /* there must be only one primary */ + assert(is_primary_found == FALSE); + is_primary_found = TRUE; + } + i++; + } +} diff --git a/compositor/rdpdisp.h b/compositor/rdpdisp.h new file mode 100644 index 000000000..6e93fc2fc --- /dev/null +++ b/compositor/rdpdisp.h @@ -0,0 +1,41 @@ +/* + * Copyright © 2020 Microsoft + * + * 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 RDP_DISP_H +#define RDP_DISP_H + +struct wet_rdp_params { + bool enable_hi_dpi_support; + bool enable_fractional_hi_dpi_support; + bool enable_fractional_hi_dpi_roundup; + int debug_desktop_scaling_factor; + int default_width; + int default_height; +}; + +void +disp_monitor_validate_and_compute_layout(struct weston_compositor *ec); + +#endif diff --git a/compositor/screen-share.c b/compositor/screen-share.c index a6f82b19c..8c37452da 100644 --- a/compositor/screen-share.c +++ b/compositor/screen-share.c @@ -40,10 +40,13 @@ #include -#include "compositor.h" +#include +#include "backend.h" +#include "libweston-internal.h" #include "weston.h" #include "shared/helpers.h" #include "shared/os-compatibility.h" +#include "shared/timespec-util.h" #include "fullscreen-shell-unstable-v1-client-protocol.h" struct shared_output { @@ -84,6 +87,7 @@ struct ss_seat { struct weston_seat base; struct shared_output *output; struct wl_list link; + uint32_t id; struct { struct wl_seat *seat; @@ -110,6 +114,9 @@ struct ss_shm_buffer { struct screen_share { struct weston_compositor *compositor; + /* XXX: missing compositor destroy listener + * https://gitlab.freedesktop.org/wayland/weston/issues/298 + */ char *command; }; @@ -140,11 +147,14 @@ ss_seat_handle_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) { struct ss_seat *seat = data; + struct timespec ts; + + timespec_from_msec(&ts, time); /* No transformation of input position is required here because we are * always receiving the input in the same coordinates as the output. */ - notify_motion_absolute(&seat->base, time, + notify_motion_absolute(&seat->base, &ts, wl_fixed_to_double(x), wl_fixed_to_double(y)); notify_pointer_frame(&seat->base); } @@ -155,8 +165,11 @@ ss_seat_handle_button(void *data, struct wl_pointer *pointer, uint32_t state) { struct ss_seat *seat = data; + struct timespec ts; - notify_button(&seat->base, time, button, state); + timespec_from_msec(&ts, time); + + notify_button(&seat->base, &ts, button, state); notify_pointer_frame(&seat->base); } @@ -166,12 +179,15 @@ ss_seat_handle_axis(void *data, struct wl_pointer *pointer, { struct ss_seat *seat = data; struct weston_pointer_axis_event weston_event; + struct timespec ts; weston_event.axis = axis; weston_event.value = wl_fixed_to_double(value); weston_event.has_discrete = false; - notify_axis(&seat->base, time, &weston_event); + timespec_from_msec(&ts, time); + + notify_axis(&seat->base, &ts, &weston_event); notify_pointer_frame(&seat->base); } @@ -197,7 +213,7 @@ ss_seat_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, if (format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); if (map_str == MAP_FAILED) { - weston_log("mmap failed: %m\n"); + weston_log("mmap failed: %s\n", strerror(errno)); goto error; } @@ -267,9 +283,11 @@ ss_seat_handle_key(void *data, struct wl_keyboard *keyboard, uint32_t key, uint32_t state) { struct ss_seat *seat = data; + struct timespec ts; + timespec_from_msec(&ts, time); seat->key_serial = serial; - notify_key(&seat->base, time, key, + notify_key(&seat->base, &ts, key, state ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED, seat->keyboard_state_update); @@ -352,6 +370,7 @@ ss_seat_create(struct shared_output *so, uint32_t id) weston_seat_init(&seat->base, so->output->compositor, "default"); seat->output = so; + seat->id = id; seat->parent.seat = wl_registry_bind(so->parent.registry, id, &wl_seat_interface, 1); wl_list_insert(so->seat_list.prev, &seat->link); @@ -450,13 +469,13 @@ shared_output_get_shm_buffer(struct shared_output *so) fd = os_create_anonymous_file(height * stride); if (fd < 0) { - weston_log("os_create_anonymous_file: %m\n"); + weston_log("os_create_anonymous_file: %s\n", strerror(errno)); return NULL; } data = mmap(NULL, height * stride, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { - weston_log("mmap: %m\n"); + weston_log("mmap: %s\n", strerror(errno)); goto out_close; } @@ -532,8 +551,8 @@ output_compute_transform(struct weston_output *output, break; case WL_OUTPUT_TRANSFORM_90: case WL_OUTPUT_TRANSFORM_FLIPPED_90: - pixman_transform_rotate(transform, NULL, 0, pixman_fixed_1); - pixman_transform_translate(transform, NULL, fh, 0); + pixman_transform_rotate(transform, NULL, 0, -pixman_fixed_1); + pixman_transform_translate(transform, NULL, 0, fw); break; case WL_OUTPUT_TRANSFORM_180: case WL_OUTPUT_TRANSFORM_FLIPPED_180: @@ -542,8 +561,8 @@ output_compute_transform(struct weston_output *output, break; case WL_OUTPUT_TRANSFORM_270: case WL_OUTPUT_TRANSFORM_FLIPPED_270: - pixman_transform_rotate(transform, NULL, 0, -pixman_fixed_1); - pixman_transform_translate(transform, NULL, 0, fw); + pixman_transform_rotate(transform, NULL, 0, pixman_fixed_1); + pixman_transform_translate(transform, NULL, fh, 0); break; } @@ -722,6 +741,12 @@ static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + struct shared_output *so = data; + struct ss_seat *seat, *next; + + wl_list_for_each_safe(seat, next, &so->seat_list, link) + if (seat->id == name) + ss_seat_destroy(seat); } static const struct wl_registry_listener registry_listener = { @@ -794,27 +819,13 @@ shared_output_repainted(struct wl_listener *listener, void *data) struct shared_output *so = container_of(listener, struct shared_output, frame_listener); pixman_region32_t damage; + pixman_region32_t *current_damage = data; struct ss_shm_buffer *sb; int32_t x, y, width, height, stride; - int i, nrects, do_yflip; + int i, nrects, do_yflip, y_orig; pixman_box32_t *r; - uint32_t *cache_data; - - /* Damage in output coordinates */ - pixman_region32_init(&damage); - pixman_region32_intersect(&damage, &so->output->region, - &so->output->previous_damage); - pixman_region32_translate(&damage, -so->output->x, -so->output->y); - - /* Apply damage to all buffers */ - wl_list_for_each(sb, &so->shm.buffers, link) - pixman_region32_union(&sb->damage, &sb->damage, &damage); - - /* Transform to buffer coordinates */ - weston_transformed_region(so->output->width, so->output->height, - so->output->transform, - so->output->current_scale, - &damage, &damage); + pixman_image_t *damaged_image; + pixman_transform_t transform; width = so->output->current_mode->width; height = so->output->current_mode->height; @@ -830,23 +841,32 @@ shared_output_repainted(struct wl_listener *listener, void *data) pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, NULL, stride); - if (!so->cache_image) { - shared_output_destroy(so); - return; - } + if (!so->cache_image) + goto err_shared_output; - pixman_region32_fini(&damage); pixman_region32_init_rect(&damage, 0, 0, width, height); + } else { + /* Damage in output coordinates */ + pixman_region32_init(&damage); + pixman_region32_intersect(&damage, &so->output->region, current_damage); + pixman_region32_translate(&damage, -so->output->x, -so->output->y); } - if (shared_output_ensure_tmp_data(so, &damage) < 0) { - shared_output_destroy(so); - return; - } + /* Apply damage to all buffers */ + wl_list_for_each(sb, &so->shm.buffers, link) + pixman_region32_union(&sb->damage, &sb->damage, &damage); + + /* Transform to buffer coordinates */ + weston_transformed_region(so->output->width, so->output->height, + so->output->transform, + so->output->current_scale, + &damage, &damage); + + if (shared_output_ensure_tmp_data(so, &damage) < 0) + goto err_pixman_init; do_yflip = !!(so->output->compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP); - cache_data = pixman_image_get_data(so->cache_image); r = pixman_region32_rectangles(&damage, &nrects); for (i = 0; i < nrects; ++i) { x = r[i].x1; @@ -854,29 +874,56 @@ shared_output_repainted(struct wl_listener *listener, void *data) width = r[i].x2 - r[i].x1; height = r[i].y2 - r[i].y1; + if (do_yflip) + y_orig = so->output->current_mode->height - r[i].y2; + else + y_orig = y; + + so->output->compositor->renderer->read_pixels( + so->output, PIXMAN_a8r8g8b8, so->tmp_data, + x, y_orig, width, height); + + damaged_image = pixman_image_create_bits(PIXMAN_a8r8g8b8, + width, height, + so->tmp_data, + (PIXMAN_FORMAT_BPP(PIXMAN_a8r8g8b8) / 8) * width); + if (!damaged_image) + goto err_pixman_init; + if (do_yflip) { - so->output->compositor->renderer->read_pixels( - so->output, PIXMAN_a8r8g8b8, so->tmp_data, - x, so->output->current_mode->height - r[i].y2, - width, height); - - pixman_blt(so->tmp_data, cache_data, -width, stride, - 32, 32, 0, 1 - height, x, y, width, height); - } else { - so->output->compositor->renderer->read_pixels( - so->output, PIXMAN_a8r8g8b8, so->tmp_data, - x, y, width, height); - - pixman_blt(so->tmp_data, cache_data, width, stride, - 32, 32, 0, 0, x, y, width, height); + pixman_transform_init_scale(&transform, + pixman_fixed_1, + pixman_fixed_minus_1); + + pixman_transform_translate(&transform, NULL, + 0, + pixman_int_to_fixed(height)); + + pixman_image_set_transform(damaged_image, &transform); } - } - pixman_region32_fini(&damage); + pixman_image_composite32(PIXMAN_OP_SRC, + damaged_image, + NULL, + so->cache_image, + 0, 0, + 0, 0, + x, y, + width, height); + pixman_image_unref(damaged_image); + } so->cache_dirty = 1; + pixman_region32_fini(&damage); shared_output_update(so); + + return; + +err_pixman_init: + pixman_region32_fini(&damage); +err_shared_output: + shared_output_destroy(so); } static struct shared_output * @@ -928,7 +975,7 @@ shared_output_create(struct weston_output *output, int parent_fd) so->parent.surface = wl_compositor_create_surface(so->parent.compositor); if (!so->parent.surface) { - weston_log("Screen share failed: %m\n"); + weston_log("Screen share failed: %s\n", strerror(errno)); goto err_display; } @@ -938,7 +985,7 @@ shared_output_create(struct weston_output *output, int parent_fd) so->parent.output, output->current_mode->refresh); if (!so->parent.mode_feedback) { - weston_log("Screen share failed: %m\n"); + weston_log("Screen share failed: %s\n", strerror(errno)); goto err_display; } zwp_fullscreen_shell_mode_feedback_v1_add_listener(so->parent.mode_feedback, @@ -952,7 +999,7 @@ shared_output_create(struct weston_output *output, int parent_fd) wl_event_loop_add_fd(loop, epoll_fd, WL_EVENT_READABLE, shared_output_handle_event, so); if (!so->event_source) { - weston_log("Screen share failed: %m\n"); + weston_log("Screen share failed: %s\n", strerror(errno)); goto err_display; } @@ -966,7 +1013,7 @@ shared_output_create(struct weston_output *output, int parent_fd) so->frame_listener.notify = shared_output_repainted; wl_signal_add(&output->frame_signal, &so->frame_listener); - output->disable_planes++; + weston_output_disable_planes_incr(output); weston_output_damage(output); return so; @@ -987,7 +1034,7 @@ shared_output_destroy(struct shared_output *so) { struct ss_shm_buffer *buffer, *bnext; - so->output->disable_planes--; + weston_output_disable_planes_decr(so->output); wl_list_for_each_safe(buffer, bnext, &so->shm.buffers, link) ss_shm_buffer_destroy(buffer); @@ -1021,7 +1068,8 @@ weston_output_share(struct weston_output *output, const char* command) }; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) { - weston_log("weston_output_share: socketpair failed: %m\n"); + weston_log("weston_output_share: socketpair failed: %s\n", + strerror(errno)); return NULL; } @@ -1030,7 +1078,8 @@ weston_output_share(struct weston_output *output, const char* command) if (pid == -1) { close(sv[0]); close(sv[1]); - weston_log("weston_output_share: fork failed: %m\n"); + weston_log("weston_output_share: fork failed: %s\n", + strerror(errno)); return NULL; } @@ -1042,13 +1091,15 @@ weston_output_share(struct weston_output *output, const char* command) /* Launch clients as the user. Do not launch clients with * wrong euid. */ if (seteuid(getuid()) == -1) { - weston_log("weston_output_share: setuid failed: %m\n"); + weston_log("weston_output_share: setuid failed: %s\n", + strerror(errno)); abort(); } sv[1] = dup(sv[1]); if (sv[1] == -1) { - weston_log("weston_output_share: dup failed: %m\n"); + weston_log("weston_output_share: dup failed: %s\n", + strerror(errno)); abort(); } @@ -1056,7 +1107,8 @@ weston_output_share(struct weston_output *output, const char* command) setenv("WAYLAND_SERVER_SOCKET", str, 1); execv(argv[0], argv); - weston_log("weston_output_share: exec failed: %m\n"); + weston_log("weston_output_share: exec failed: %s\n", + strerror(errno)); abort(); } else { close(sv[1]); @@ -1082,8 +1134,8 @@ weston_output_find(struct weston_compositor *c, int32_t x, int32_t y) } static void -share_output_binding(struct weston_keyboard *keyboard, uint32_t time, uint32_t key, - void *data) +share_output_binding(struct weston_keyboard *keyboard, + const struct timespec *time, uint32_t key, void *data) { struct weston_output *output; struct weston_pointer *pointer; @@ -1111,7 +1163,7 @@ wet_module_init(struct weston_compositor *compositor, int *argc, char *argv[]) { struct screen_share *ss; - struct weston_config *config = wet_get_config(compositor); + struct weston_config *config; struct weston_config_section *section; ss = zalloc(sizeof *ss); @@ -1119,6 +1171,8 @@ wet_module_init(struct weston_compositor *compositor, return -1; ss->compositor = compositor; + config = wet_get_config(compositor); + section = weston_config_get_section(config, "screen-share", NULL, NULL); weston_config_section_get_string(section, "command", &ss->command, ""); diff --git a/compositor/systemd-notify.c b/compositor/systemd-notify.c index e53a9d287..61196d818 100644 --- a/compositor/systemd-notify.c +++ b/compositor/systemd-notify.c @@ -32,8 +32,8 @@ #include "shared/helpers.h" #include "shared/string-helpers.h" -#include "shared/zalloc.h" -#include "compositor.h" +#include +#include #include "weston.h" struct systemd_notifier { @@ -127,10 +127,12 @@ wet_module_init(struct weston_compositor *compositor, if (notifier == NULL) return -1; - notifier->compositor_destroy_listener.notify = - weston_compositor_destroy_listener; - wl_signal_add(&compositor->destroy_signal, - ¬ifier->compositor_destroy_listener); + if (!weston_compositor_add_destroy_listener_once(compositor, + ¬ifier->compositor_destroy_listener, + weston_compositor_destroy_listener)) { + free(notifier); + return 0; + } if (add_systemd_sockets(compositor) < 0) return -1; diff --git a/libweston/timeline-object.h b/compositor/testsuite-util.c similarity index 55% rename from libweston/timeline-object.h rename to compositor/testsuite-util.c index 943f979c1..34882d1fc 100644 --- a/libweston/timeline-object.h +++ b/compositor/testsuite-util.c @@ -1,6 +1,5 @@ /* - * Copyright © 2014 Pekka Paalanen - * Copyright © 2014 Collabora, Ltd. + * 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 @@ -24,32 +23,40 @@ * SOFTWARE. */ -#ifndef WESTON_TIMELINE_OBJECT_H -#define WESTON_TIMELINE_OBJECT_H +#include "config.h" -/* - * This struct can be embedded in objects related to timeline output. - * It must be initialized to all-zero. Afterwards, the timeline code - * will handle it alone. No clean-up is necessary. - */ -struct weston_timeline_object { - /* - * Timeline series gets bumped every time a new log is opened. - * This triggers id allocation and object info emission. - * 0 is an invalid series value. - */ - unsigned series; +#include +#include "weston.h" - /* Object id in the timeline JSON output. 0 is invalid. */ - unsigned id; +static struct wet_testsuite_data *wet_testsuite_data_global; - /* - * If non-zero, forces a re-emission of object description. - * Should be set to non-zero, when changing long-lived - * object state that is not emitted on normal timeline - * events. - */ - unsigned force_refresh; -}; +/** Set global test suite data + * + * \param data Custom test suite data. + * + * The type struct wet_testsuite_data is free to be defined by any test suite + * in any way they want. This function stores a single pointer to that data + * in a global variable. + * + * The data is expected to be fetched from a test suite specific plugin that + * knows how to interpret it. + * + * \sa wet_testsuite_data_get + */ +WL_EXPORT void +wet_testsuite_data_set(struct wet_testsuite_data *data) +{ + wet_testsuite_data_global = data; +} -#endif /* WESTON_TIMELINE_OBJECT_H */ +/** Get global test suite data + * + * \return Custom test suite data. + * + * Returns the value last set with wet_testsuite_data_set(). + */ +WL_EXPORT struct wet_testsuite_data * +wet_testsuite_data_get(void) +{ + return wet_testsuite_data_global; +} diff --git a/compositor/text-backend.c b/compositor/text-backend.c index bf5c45cc9..722dcb2c1 100644 --- a/compositor/text-backend.c +++ b/compositor/text-backend.c @@ -33,11 +33,12 @@ #include #include -#include "compositor.h" +#include #include "weston.h" #include "text-input-unstable-v1-server-protocol.h" #include "input-method-unstable-v1-server-protocol.h" #include "shared/helpers.h" +#include "shared/timespec-util.h" struct text_input_manager; struct input_method; @@ -64,7 +65,7 @@ struct text_input_manager { struct wl_global *text_input_manager_global; struct wl_listener destroy_listener; - struct text_input *current_panel; + struct text_input *current_text_input; struct weston_compositor *ec; }; @@ -105,7 +106,7 @@ struct text_backend { struct wl_client *client; unsigned deathcount; - uint32_t deathstamp; + struct timespec deathstamp; } input_method; struct wl_listener client_listener; @@ -140,11 +141,15 @@ deactivate_input_method(struct input_method *input_method) input_method->context = NULL; if (wl_list_empty(&text_input->input_methods) && - text_input->input_panel_visible) { + text_input->input_panel_visible && + text_input->manager->current_text_input == text_input) { wl_signal_emit(&ec->hide_input_panel_signal, ec); text_input->input_panel_visible = false; - text_input->manager->current_panel = NULL; } + + if (text_input->manager->current_text_input == text_input) + text_input->manager->current_text_input = NULL; + zwp_text_input_v1_send_leave(text_input->resource); } @@ -188,10 +193,14 @@ text_input_activate(struct wl_client *client, { struct text_input *text_input = wl_resource_get_user_data(resource); struct weston_seat *weston_seat = wl_resource_get_user_data(seat); - struct input_method *input_method = weston_seat->input_method; + struct input_method *input_method; struct weston_compositor *ec = text_input->ec; struct text_input *current; + if (!weston_seat) + return; + + input_method = weston_seat->input_method; if (input_method->input == text_input) return; @@ -206,12 +215,11 @@ text_input_activate(struct wl_client *client, input_method_context_create(text_input, input_method); - current = text_input->manager->current_panel; + current = text_input->manager->current_text_input; if (current && current != text_input) { current->input_panel_visible = false; wl_signal_emit(&ec->hide_input_panel_signal, ec); - text_input->manager->current_panel = NULL; } if (text_input->input_panel_visible) { @@ -219,8 +227,8 @@ text_input_activate(struct wl_client *client, text_input->surface); wl_signal_emit(&ec->update_input_panel_signal, &text_input->cursor_rectangle); - text_input->manager->current_panel = text_input; } + text_input->manager->current_text_input = text_input; zwp_text_input_v1_send_enter(text_input->resource, text_input->surface->resource); @@ -233,7 +241,7 @@ text_input_deactivate(struct wl_client *client, { struct weston_seat *weston_seat = wl_resource_get_user_data(seat); - if (weston_seat->input_method->input) + if (weston_seat && weston_seat->input_method->input) deactivate_input_method(weston_seat->input_method); } @@ -335,7 +343,8 @@ text_input_show_input_panel(struct wl_client *client, text_input->input_panel_visible = true; - if (!wl_list_empty(&text_input->input_methods)) { + if (!wl_list_empty(&text_input->input_methods) && + text_input == text_input->manager->current_text_input) { wl_signal_emit(&ec->show_input_panel_signal, text_input->surface); wl_signal_emit(&ec->update_input_panel_signal, @@ -353,10 +362,8 @@ text_input_hide_input_panel(struct wl_client *client, text_input->input_panel_visible = false; if (!wl_list_empty(&text_input->input_methods) && - text_input == text_input->manager->current_panel) { - text_input->manager->current_panel = NULL; + text_input == text_input->manager->current_text_input) wl_signal_emit(&ec->hide_input_panel_signal, ec); - } } static void @@ -445,6 +452,7 @@ text_input_manager_notifier_destroy(struct wl_listener *listener, void *data) struct text_input_manager, destroy_listener); + wl_list_remove(&text_input_manager->destroy_listener.link); wl_global_destroy(text_input_manager->text_input_manager_global); free(text_input_manager); @@ -607,11 +615,13 @@ unbind_keyboard(struct wl_resource *resource) static void input_method_context_grab_key(struct weston_keyboard_grab *grab, - uint32_t time, uint32_t key, uint32_t state_w) + 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; if (!keyboard->input_method_resource) return; @@ -619,8 +629,9 @@ input_method_context_grab_key(struct weston_keyboard_grab *grab, display = wl_client_get_display( wl_resource_get_client(keyboard->input_method_resource)); serial = wl_display_next_serial(display); + msecs = timespec_to_msec(time); wl_keyboard_send_key(keyboard->input_method_resource, - serial, time, key, state_w); + serial, msecs, key, state_w); } static void @@ -669,9 +680,7 @@ input_method_context_grab_keyboard(struct wl_client *client, context->keyboard = cr; - wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, - keyboard->xkb_info->keymap_fd, - keyboard->xkb_info->keymap_size); + weston_keyboard_send_keymap(keyboard, cr); if (keyboard->grab != &keyboard->default_grab) { weston_keyboard_end_grab(keyboard); @@ -693,8 +702,11 @@ input_method_context_key(struct wl_client *client, struct weston_seat *seat = context->input_method->seat; struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); struct weston_keyboard_grab *default_grab = &keyboard->default_grab; + struct timespec ts; - default_grab->interface->key(default_grab, time, key, state_w); + timespec_from_msec(&ts, time); + + default_grab->interface->key(default_grab, &ts, key, state_w); } static void @@ -931,11 +943,14 @@ static void launch_input_method(struct text_backend *text_backend); static void respawn_input_method_process(struct text_backend *text_backend) { - uint32_t time; + struct timespec time; + int64_t tdiff; /* if input_method dies more than 5 times in 10 seconds, give up */ - time = weston_compositor_get_time(); - if (time - text_backend->input_method.deathstamp > 10000) { + weston_compositor_get_time(&time); + tdiff = timespec_sub_to_msec(&time, + &text_backend->input_method.deathstamp); + if (tdiff > 10000) { text_backend->input_method.deathstamp = time; text_backend->input_method.deathcount = 0; } @@ -1031,14 +1046,10 @@ text_backend_configuration(struct text_backend *text_backend) struct weston_config *config = wet_get_config(text_backend->compositor); struct weston_config_section *section; char *client; - int ret; section = weston_config_get_section(config, "input-method", NULL, NULL); - ret = asprintf(&client, "%s/weston-keyboard", - weston_config_get_libexec_dir()); - if (ret < 0) - client = NULL; + client = wet_get_libexec_path("weston-keyboard"); weston_config_section_get_string(section, "path", &text_backend->input_method.path, client); @@ -1048,6 +1059,8 @@ text_backend_configuration(struct text_backend *text_backend) WL_EXPORT void text_backend_destroy(struct text_backend *text_backend) { + wl_list_remove(&text_backend->seat_created_listener.link); + if (text_backend->input_method.client) { /* disable respawn */ wl_list_remove(&text_backend->client_listener.link); diff --git a/compositor/weston-screenshooter.c b/compositor/weston-screenshooter.c index 9999909e1..ed354b0c1 100644 --- a/compositor/weston-screenshooter.c +++ b/compositor/weston-screenshooter.c @@ -28,10 +28,11 @@ #include #include -#include "compositor.h" +#include #include "weston.h" #include "weston-screenshooter-server-protocol.h" #include "shared/helpers.h" +#include struct screenshooter { struct weston_compositor *ec; @@ -66,7 +67,7 @@ screenshooter_shoot(struct wl_client *client, struct wl_resource *buffer_resource) { struct weston_output *output = - wl_resource_get_user_data(output_resource); + weston_head_from_resource(output_resource)->output; struct weston_buffer *buffer = weston_buffer_from_resource(buffer_resource); @@ -88,13 +89,20 @@ bind_shooter(struct wl_client *client, { 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 (client != shooter->client) { + if (!debug_enabled && !shooter->client) { wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, - "screenshooter failed: permission denied"); + "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; } @@ -112,17 +120,15 @@ screenshooter_sigchld(struct weston_process *process, int status) } static void -screenshooter_binding(struct weston_keyboard *keyboard, uint32_t time, - uint32_t key, void *data) +screenshooter_binding(struct weston_keyboard *keyboard, + const struct timespec *time, uint32_t key, void *data) { struct screenshooter *shooter = data; char *screenshooter_exe; - int ret; - ret = asprintf(&screenshooter_exe, "%s/%s", - weston_config_get_libexec_dir(), - "/weston-screenshooter"); - if (ret < 0) { + + screenshooter_exe = wet_get_bindir_path("weston-screenshooter"); + if (!screenshooter_exe) { weston_log("Could not construct screenshooter path.\n"); return; } @@ -135,7 +141,7 @@ screenshooter_binding(struct weston_keyboard *keyboard, uint32_t time, } static void -recorder_binding(struct weston_keyboard *keyboard, uint32_t time, +recorder_binding(struct weston_keyboard *keyboard, const struct timespec *time, uint32_t key, void *data) { struct weston_compositor *ec = keyboard->seat->compositor; @@ -164,6 +170,8 @@ 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); } diff --git a/compositor/weston.h b/compositor/weston.h index 5708aca40..b38c99465 100644 --- a/compositor/weston.h +++ b/compositor/weston.h @@ -30,7 +30,8 @@ extern "C" { #endif -#include +#include +#include void screenshooter_create(struct weston_compositor *ec); @@ -60,6 +61,9 @@ weston_watch_process(struct weston_process *process); struct weston_config * wet_get_config(struct weston_compositor *compositor); +struct wet_rdp_params * +wet_get_rdp_params(struct weston_compositor *); + void * wet_load_module_entrypoint(const char *name, const char *entrypoint); @@ -77,6 +81,12 @@ int module_init(struct weston_compositor *compositor, int *argc, char *argv[]); +char * +wet_get_libexec_path(const char *name); + +char * +wet_get_bindir_path(const char *name); + int wet_load_xwayland(struct weston_compositor *comp); @@ -88,6 +98,21 @@ text_backend_init(struct weston_compositor *ec); void text_backend_destroy(struct text_backend *text_backend); +int +wet_main(int argc, char *argv[]); + + +/* test suite utilities */ + +/** Opaque type for a test suite to define. */ +struct wet_testsuite_data; + +void +wet_testsuite_data_set(struct wet_testsuite_data *data); + +struct wet_testsuite_data * +wet_testsuite_data_get(void); + #ifdef __cplusplus } #endif diff --git a/compositor/weston.pc.in b/compositor/weston.pc.in deleted file mode 100644 index 6890a77cb..000000000 --- a/compositor/weston.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ -libexecdir=@libexecdir@ -pkglibexecdir=${libexecdir}/@PACKAGE@ - -Name: Weston Plugin API -Description: Header files for Weston plugin development -Version: @WESTON_VERSION@ -Requires.private: libweston-@LIBWESTON_MAJOR@ -Cflags: -I${includedir}/weston diff --git a/compositor/wslgd-notify.c b/compositor/wslgd-notify.c new file mode 100644 index 000000000..61a2eccfd --- /dev/null +++ b/compositor/wslgd-notify.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022 Microsoft. All rights reservied. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "shared/helpers.h" +#include "shared/string-helpers.h" +#include +#include +#include "weston.h" + +WL_EXPORT int +wet_module_init(struct weston_compositor *compositor, + int *argc, char *argv[]) +{ + struct sockaddr_un addr = {}; + socklen_t size, name_size; + char *socket_path = getenv("WSLGD_NOTIFY_SOCKET"); + if (!socket_path) { + weston_log("%s: socket path is not specified\n", __func__); + return 0; + } + + int socket_fd = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + if (socket_fd < 0) { + weston_log("%s: socket failed\n", __func__); + return -1; + } + + addr.sun_family = AF_LOCAL; + name_size = snprintf(addr.sun_path, sizeof addr.sun_path, + "%s", socket_path) + 1; + size = offsetof(struct sockaddr_un, sun_path) + name_size; + + int fd = connect(socket_fd, &addr, size); + if (fd < 0) { + weston_log("%s: connect(%s) failed %s\n", __func__, addr.sun_path, strerror(errno)); + goto close_socket_fd; + } + + weston_log("%s: socket connected\n", __FILE__); + + close(fd); + +close_socket_fd: + close(socket_fd); + + return 0; +} diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 9881cd9c1..042c202b1 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -27,11 +27,14 @@ #include "config.h" #include +#include +#include #include +#include -#include "compositor.h" +#include #include "compositor/weston.h" -#include "xwayland/xwayland-api.h" +#include #include "shared/helpers.h" struct wet_xwayland { @@ -63,9 +66,10 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd { struct wet_xwayland *wxw = user_data; pid_t pid; - char s[8], abstract_fd_str[8], unix_fd_str[8], wm_fd_str[8]; + char s[12], abstract_fd_str[12], unix_fd_str[12], wm_fd_str[12]; int sv[2], wm[2], fd; char *xserver = NULL; + bool disable_ac = false; struct weston_config *config = wet_get_config(wxw->compositor); struct weston_config_section *section; @@ -82,6 +86,7 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd pid = fork(); switch (pid) { case 0: + setsid(); /* SOCK_CLOEXEC closes both ends, so we need to unset * the flag on the client fd. */ fd = dup(sv[1]); @@ -90,10 +95,13 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd 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); + if (abstract_fd) { + 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; @@ -108,6 +116,9 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd weston_config_section_get_string(section, "path", &xserver, XSERVER_PATH); + weston_config_section_get_bool(section, "disable_access_control", + &disable_ac, false); + /* Ignore SIGUSR1 in the child, which will make the X * server send SIGUSR1 to the parent (weston) when * it's done with initialization. During @@ -117,20 +128,43 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd * it's done with that. */ signal(SIGUSR1, SIG_IGN); - if (execl(xserver, - xserver, - display, - "-rootless", - "-listen", abstract_fd_str, - "-listen", unix_fd_str, - "-wm", wm_fd_str, - "-terminate", - NULL) < 0) - weston_log("exec of '%s %s -rootless " - "-listen %s -listen %s -wm %s " - "-terminate' failed: %m\n", - xserver, display, - abstract_fd_str, unix_fd_str, wm_fd_str); + // Build our parameters + #define ARGS_COUNT 13 + const char *argv[ARGS_COUNT] = { + xserver, // 0 + display, // 1 + "-rootless", // 2 + "-core", // 3 + "-listen", unix_fd_str, // 4, 5 + "-wm", wm_fd_str, // 6, 7 + "-terminate", // 8 + NULL, NULL, // 9, 10 (-listen, abstract_fd_str) + NULL, // 11 (-ac) + NULL // 12 + }; + + int argc = 9; + if (abstract_fd) { + argv[argc++] = "-listen"; + argv[argc++] = abstract_fd_str; + } else { + argv[argc++] = "-nolisten"; + argv[argc++] = "local"; + } + + if (disable_ac) { + argv[argc++] = "-ac"; + } + assert(argc <= ARGS_COUNT); + + if(execv(xserver, (char* const*)argv) < 0) { + weston_log("%s ", argv[0]); + for (int i=0; i libweston, wrong - AC_MSG_ERROR([Weston version is greater than libweston.]) - ]) - ]) - -# Check for programs -AC_PROG_CC -AC_PROG_SED - -# Initialize libtool -LT_PREREQ([2.2]) -LT_INIT([disable-static]) - -AC_ARG_VAR([WESTON_NATIVE_BACKEND], - [Set the native backend to use, if Weston is not running under Wayland nor X11. @<:@default=drm-backend.so@:>@]) -AC_ARG_VAR([WESTON_SHELL_CLIENT], - [Set the default desktop shell client to load if none is specified in weston.ini. @<:@default=weston-desktop-shell@:>@]) - -PKG_PROG_PKG_CONFIG() - -WESTON_SEARCH_LIBS([DLOPEN], [dl], [dlopen]) - -# In old glibc versions (< 2.17) clock_gettime() and clock_getres() are in librt -WESTON_SEARCH_LIBS([CLOCK_GETTIME], [rt], [clock_gettime]) -WESTON_SEARCH_LIBS([CLOCK_GETRES], [rt], [clock_getres]) - -AC_CHECK_DECL(SFD_CLOEXEC,[], - [AC_MSG_ERROR("SFD_CLOEXEC is needed to compile weston")], - [[#include ]]) -AC_CHECK_DECL(TFD_CLOEXEC,[], - [AC_MSG_ERROR("TFD_CLOEXEC is needed to compile weston")], - [[#include ]]) -AC_CHECK_DECL(CLOCK_MONOTONIC,[], - [AC_MSG_ERROR("CLOCK_MONOTONIC is needed to compile weston")], - [[#include ]]) -AC_CHECK_HEADERS([execinfo.h]) - -AC_CHECK_FUNCS([mkostemp strchrnul initgroups posix_fallocate]) - -COMPOSITOR_MODULES="wayland-server >= $WAYLAND_PREREQ_VERSION pixman-1 >= 0.25.2" - -AC_CONFIG_FILES([doc/doxygen/tools.doxygen doc/doxygen/tooldev.doxygen]) - -AC_ARG_ENABLE(devdocs, - AS_HELP_STRING([--disable-devdocs], - [do not enable building of developer documentation]),, - enable_devdocs=auto) -if test "x$enable_devdocs" != "xno"; then - AC_CHECK_PROGS([DOXYGEN], [doxygen]) - if test "x$DOXYGEN" = "x" -a "x$enable_devdocs" = "xyes"; then - AC_MSG_ERROR([Developer documentation explicitly requested, but Doxygen couldn't be found]) - fi - if test "x$DOXYGEN" != "x"; then - enable_devdocs=yes - else - enable_devdocs=no - fi -fi -AM_CONDITIONAL(ENABLE_DEVDOCS, test "x$enable_devdocs" = "xyes") - -AC_ARG_ENABLE(egl, [ --disable-egl],, - enable_egl=yes) -AM_CONDITIONAL(ENABLE_EGL, test x$enable_egl = xyes) -if test x$enable_egl = xyes; then - AC_DEFINE([ENABLE_EGL], [1], [Build Weston with EGL support]) - PKG_CHECK_MODULES(EGL, [egl glesv2]) - PKG_CHECK_MODULES([EGL_TESTS], [egl glesv2 wayland-client wayland-egl]) - PKG_CHECK_MODULES([GL_RENDERER], [libdrm]) -fi - -AC_ARG_ENABLE(xkbcommon, - AS_HELP_STRING([--disable-xkbcommon], [Disable libxkbcommon - support: This is only useful in environments - where you do not have a hardware keyboard. If - libxkbcommon support is disabled clients will not - be sent a keymap and must know how to interpret - the keycode sent for any key event.]),, - enable_xkbcommon=yes) -if test x$enable_xkbcommon = xyes; then - AC_DEFINE(ENABLE_XKBCOMMON, [1], [Build Weston with libxkbcommon support]) - COMPOSITOR_MODULES="$COMPOSITOR_MODULES xkbcommon >= 0.3.0" - PKG_CHECK_MODULES(XKBCOMMON_COMPOSE, [xkbcommon >= 0.5.0], - [AC_DEFINE(HAVE_XKBCOMMON_COMPOSE, 1, - [Define if xkbcommon is 0.5.0 or newer])], - true) -fi - -AC_ARG_ENABLE(setuid-install, [ --enable-setuid-install],, - enable_setuid_install=yes) -AM_CONDITIONAL(ENABLE_SETUID_INSTALL, test x$enable_setuid_install = xyes) - - -AC_ARG_ENABLE(xwayland, [ --enable-xwayland],, - enable_xwayland=yes) -AC_ARG_ENABLE(xwayland-test, [ --enable-xwayland-test],, - enable_xwayland_test=yes) -AM_CONDITIONAL(ENABLE_XWAYLAND, test x$enable_xwayland = xyes) -AM_CONDITIONAL(ENABLE_XWAYLAND_TEST, test x$enable_xwayland = xyes -a x$enable_xwayland_test = xyes) -if test x$enable_xwayland = xyes; then - PKG_CHECK_MODULES([XWAYLAND], xcb xcb-xfixes xcb-composite xcursor cairo-xcb) - AC_DEFINE([BUILD_XWAYLAND], [1], [Build the X server launcher]) - - AC_ARG_WITH(xserver-path, AS_HELP_STRING([--with-xserver-path=PATH], - [Path to X server]), [XSERVER_PATH="$withval"], - [XSERVER_PATH="/usr/bin/Xwayland"]) - AC_SUBST([XSERVER_PATH]) - if test x$enable_xwayland_test = xyes; then - PKG_CHECK_MODULES([XWAYLAND_TEST], x11) - fi -fi - -PKG_CHECK_MODULES(LIBDRM, [libdrm], - [AC_DEFINE(HAVE_LIBDRM, 1, [Define if libdrm is available]) have_libdrm=yes], have_libdrm=no) - -AC_ARG_ENABLE(x11-compositor, [ --enable-x11-compositor],, - enable_x11_compositor=yes) -AM_CONDITIONAL(ENABLE_X11_COMPOSITOR, test x$enable_x11_compositor = xyes) -have_xcb_xkb=no -if test x$enable_x11_compositor = xyes; then - PKG_CHECK_MODULES([XCB], xcb >= 1.8) - X11_COMPOSITOR_MODULES="x11 x11-xcb xcb-shm" - - PKG_CHECK_MODULES(X11_COMPOSITOR_XKB, [xcb-xkb >= 1.9], - [have_xcb_xkb="yes"], [have_xcb_xkb="no"]) - if test "x$have_xcb_xkb" = xyes; then - X11_COMPOSITOR_MODULES="$X11_COMPOSITOR_MODULES xcb-xkb" - AC_DEFINE([HAVE_XCB_XKB], [1], [libxcb supports XKB protocol]) - fi - - PKG_CHECK_MODULES(X11_COMPOSITOR, [$X11_COMPOSITOR_MODULES]) - AC_DEFINE([BUILD_X11_COMPOSITOR], [1], [Build the X11 compositor]) -fi - - -AC_ARG_ENABLE(drm-compositor, [ --enable-drm-compositor],, - enable_drm_compositor=yes) -AM_CONDITIONAL(ENABLE_DRM_COMPOSITOR, test x$enable_drm_compositor = xyes) -if test x$enable_drm_compositor = xyes; then - AC_DEFINE([BUILD_DRM_COMPOSITOR], [1], [Build the DRM compositor]) - PKG_CHECK_MODULES(DRM_COMPOSITOR, [libudev >= 136 libdrm >= 2.4.30 gbm mtdev >= 1.1.0]) - PKG_CHECK_MODULES(DRM_COMPOSITOR_GBM, [gbm >= 10.2], - [AC_DEFINE([HAVE_GBM_FD_IMPORT], 1, [gbm supports dmabuf import])], - [AC_MSG_WARN([gbm does not support dmabuf import, will omit that capability])]) -fi - - -PKG_CHECK_MODULES(LIBINPUT_BACKEND, [libinput >= 0.8.0]) -PKG_CHECK_MODULES(COMPOSITOR, [$COMPOSITOR_MODULES]) - -PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.7], - [ac_wayland_protocols_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`]) -AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, $ac_wayland_protocols_pkgdatadir) - -AC_ARG_ENABLE(wayland-compositor, [ --enable-wayland-compositor],, - enable_wayland_compositor=yes) -AM_CONDITIONAL(ENABLE_WAYLAND_COMPOSITOR, - test x$enable_wayland_compositor = xyes) -if test x$enable_wayland_compositor = xyes; then - AC_DEFINE([BUILD_WAYLAND_COMPOSITOR], [1], - [Build the Wayland (nested) compositor]) - PKG_CHECK_MODULES(WAYLAND_COMPOSITOR, [wayland-client >= $WAYLAND_PREREQ_VERSION wayland-cursor]) - if test x$enable_egl = xyes; then - PKG_CHECK_MODULES(WAYLAND_COMPOSITOR_EGL, [wayland-egl]) - fi -fi - - -AC_ARG_ENABLE(headless-compositor, [ --enable-headless-compositor],, - enable_headless_compositor=yes) -AM_CONDITIONAL(ENABLE_HEADLESS_COMPOSITOR, - test x$enable_headless_compositor = xyes) -if test x$enable_headless_compositor = xyes; then - AC_DEFINE([BUILD_HEADLESS_COMPOSITOR], [1], [Build the headless compositor]) -fi - - -AC_ARG_ENABLE([fbdev-compositor], [ --enable-fbdev-compositor],, - enable_fbdev_compositor=yes) -AM_CONDITIONAL([ENABLE_FBDEV_COMPOSITOR], - [test x$enable_fbdev_compositor = xyes]) -AS_IF([test x$enable_fbdev_compositor = xyes], [ - AC_DEFINE([BUILD_FBDEV_COMPOSITOR], [1], [Build the fbdev compositor]) - PKG_CHECK_MODULES([FBDEV_COMPOSITOR], [libudev >= 136 mtdev >= 1.1.0]) -]) - -AC_ARG_ENABLE([rdp-compositor], [ --enable-rdp-compositor],, - enable_rdp_compositor=no) -AM_CONDITIONAL([ENABLE_RDP_COMPOSITOR], - [test x$enable_rdp_compositor = xyes]) -if test x$enable_rdp_compositor = xyes; then - AC_DEFINE([BUILD_RDP_COMPOSITOR], [1], [Build the RDP compositor]) - PKG_CHECK_MODULES(RDP_COMPOSITOR, [freerdp2 >= 2.0.0], - [], - [PKG_CHECK_MODULES(RDP_COMPOSITOR, [freerdp >= 1.1.0],[])] - ) - - SAVED_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $RDP_COMPOSITOR_CFLAGS" - AC_CHECK_HEADERS([freerdp/version.h]) - CPPFLAGS="$SAVED_CPPFLAGS" -fi - -AC_ARG_ENABLE([screen-sharing], [ --enable-screen-sharing],, - enable_screen_sharing=no) -AM_CONDITIONAL([ENABLE_SCREEN_SHARING], - [test x$enable_screen_sharing = xyes]) -if test x$enable_screen_sharing = xyes; then - PKG_CHECK_MODULES(SCREEN_SHARE, [wayland-client]) - - if test x$enable_rdp_compositor != xyes; then - AC_MSG_WARN([The screen-share.so module requires the RDP backend.]) - fi -fi - -AC_ARG_WITH(cairo, - AS_HELP_STRING([--with-cairo=@<:@image|gl|glesv2@:>@] - [Which Cairo renderer to use for the clients]), - [],[with_cairo="image"]) - -if test "x$with_cairo" = "ximage"; then - cairo_modules="cairo" -else -if test "x$with_cairo" = "xgl"; then - cairo_modules="cairo-gl" - AC_MSG_WARN([The --with-cairo=gl option can cause increased resource usage and potential instability, and thus is not recommended. It is needed only for a few special demo programs.]) -else -if test "x$with_cairo" = "xglesv2"; then - cairo_modules="cairo-glesv2" - AC_MSG_WARN([The --with-cairo=gles2 option can cause increased resource usage and potential instability, and thus is not recommended. It is needed only for a few special demo programs.]) -else - AC_ERROR([Unknown cairo renderer requested]) -fi -fi -fi - -# Included for legacy compat -AC_ARG_WITH(cairo-glesv2, - AS_HELP_STRING([--with-cairo-glesv2], - [Use GLESv2 cairo])) -if test "x$with_cairo_glesv2" = "xyes"; then - cairo_modules="cairo-glesv2" - with_cairo="glesv2" -fi - -if test "x$cairo_modules" = "xcairo-glesv2"; then -AC_DEFINE([USE_CAIRO_GLESV2], [1], [Use the GLESv2 GL cairo backend]) -fi - -PKG_CHECK_MODULES(PIXMAN, [pixman-1]) -PKG_CHECK_MODULES(PNG, [libpng]) - -AC_ARG_WITH([jpeg], - AS_HELP_STRING([--without-jpeg], - [Use jpeglib for JPEG decoding support [default=auto]])) -AS_IF([test "x$with_jpeg" != "xno"], - [WESTON_SEARCH_LIBS([JPEG], [jpeg], [jpeg_CreateDecompress], [have_jpeglib=yes], [have_jpeglib=no])], - [have_jpeglib=no]) -AS_IF([test "x$have_jpeglib" = "xyes"], - [AC_DEFINE([HAVE_JPEG], [1], [Have jpeglib])], - [AS_IF([test "x$with_jpeg" = "xyes"], - [AC_MSG_ERROR([JPEG support explicitly requested, but jpeglib couldn't be found])])]) - -AC_ARG_WITH([webp], - AS_HELP_STRING([--without-webp], - [Use libwebp for WebP decoding support [default=auto]])) -AS_IF([test "x$with_webp" != "xno"], - [PKG_CHECK_MODULES(WEBP, [libwebp], [have_webp=yes], [have_webp=no])], - [have_webp=no]) -AS_IF([test "x$have_webp" = "xyes"], - [AC_DEFINE([HAVE_WEBP], [1], [Have webp])], - [AS_IF([test "x$with_webp" = "xyes"], - [AC_MSG_ERROR([WebP support explicitly requested, but libwebp couldn't be found])])]) - -AC_ARG_ENABLE(vaapi-recorder, [ --enable-vaapi-recorder],, - enable_vaapi_recorder=auto) -have_libva=no -if test x$enable_vaapi_recorder != xno; then - PKG_CHECK_MODULES(LIBVA, [libva >= 0.34.0 libva-drm >= 0.34.0], - [have_libva=yes], [have_libva=no]) - if test "x$have_libva" = "xno" -a "x$enable_vaapi_recorder" = "xyes"; then - AC_MSG_ERROR([vaapi-recorder explicitly enabled, but libva couldn't be found]) - fi - AS_IF([test "x$have_libva" = "xyes"], - [AC_DEFINE([BUILD_VAAPI_RECORDER], [1], [Build the vaapi recorder])]) -fi -AM_CONDITIONAL(ENABLE_VAAPI_RECORDER, test "x$have_libva" = xyes) - -PKG_CHECK_MODULES(CAIRO, [cairo]) - -PKG_CHECK_MODULES(TEST_CLIENT, [wayland-client >= $WAYLAND_PREREQ_VERSION pixman-1]) - -AC_ARG_ENABLE(simple-clients, - AS_HELP_STRING([--disable-simple-clients], - [do not build the simple wl_shm clients]),, - enable_simple_clients=yes) -AM_CONDITIONAL(BUILD_SIMPLE_CLIENTS, test "x$enable_simple_clients" = "xyes") -if test x$enable_simple_clients = xyes; then - PKG_CHECK_MODULES(SIMPLE_CLIENT, [wayland-client]) -fi - -AC_ARG_ENABLE(simple-egl-clients, - AS_HELP_STRING([--disable-simple-egl-clients], - [do not build the simple EGL clients]),, - enable_simple_egl_clients="$enable_egl") -AM_CONDITIONAL(BUILD_SIMPLE_EGL_CLIENTS, test "x$enable_simple_egl_clients" = "xyes") -if test x$enable_simple_egl_clients = xyes; then - PKG_CHECK_MODULES(SIMPLE_EGL_CLIENT, - [egl glesv2 wayland-client wayland-egl wayland-cursor]) -fi - -AC_ARG_ENABLE(simple-dmabuf-intel-client, - AS_HELP_STRING([--disable-simple-dmabuf-intel-client], - [do not build the simple dmabuf intel client]),, - enable_simple_dmabuf_intel_client="auto") -if ! test "x$enable_simple_dmabuf_intel_client" = "xno"; then - PKG_CHECK_MODULES(SIMPLE_DMABUF_INTEL_CLIENT, [wayland-client libdrm libdrm_intel], - have_simple_dmabuf_intel_client=yes, have_simple_dmabuf_intel_client=no) - if test "x$have_simple_dmabuf_intel_client" = "xno" -a "x$enable_simple_dmabuf_intel_client" = "xyes"; then - AC_MSG_ERROR([Intel dmabuf client explicitly enabled, but libdrm_intel couldn't be found]) - fi - enable_simple_dmabuf_intel_client="$have_simple_dmabuf_intel_client" -fi -AM_CONDITIONAL(BUILD_SIMPLE_DMABUF_INTEL_CLIENT, test "x$enable_simple_dmabuf_intel_client" = "xyes") - -AC_ARG_ENABLE(simple-dmabuf-v4l-client, - AS_HELP_STRING([--disable-simple-dmabuf-v4l-client], - [do not build the simple dmabuf v4l client]),, - enable_simple_dmabuf_v4l_client="auto") -if ! test "x$enable_simple_dmabuf_v4l_client" = "xno"; then - PKG_CHECK_MODULES(SIMPLE_DMABUF_V4L_CLIENT, [wayland-client libdrm], - have_simple_dmabuf_v4l_client=yes, have_simple_dmabuf_v4l_client=no) - if test "x$have_simple_dmabuf_v4l_client" = "xno" -a "x$enable_simple_dmabuf_v4l_client" = "xyes"; then - AC_MSG_ERROR([V4L dmabuf client explicitly enabled, but libdrm couldn't be found]) - fi - enable_simple_dmabuf_v4l_client="$have_simple_dmabuf_v4l_client" -fi -AM_CONDITIONAL(BUILD_SIMPLE_DMABUF_V4L_CLIENT, test "x$enable_simple_dmabuf_v4l_client" = "xyes") - -AC_ARG_ENABLE(clients, [ --enable-clients],, enable_clients=yes) -AM_CONDITIONAL(BUILD_CLIENTS, test x$enable_clients = xyes) -have_cairo_egl=no -if test x$enable_clients = xyes; then - AC_DEFINE([BUILD_CLIENTS], [1], [Build the Wayland clients]) - - PKG_CHECK_MODULES(CLIENT, [wayland-client >= $WAYLAND_PREREQ_VERSION cairo >= 1.10.0 xkbcommon wayland-cursor]) - PKG_CHECK_MODULES(SERVER, [wayland-server]) - PKG_CHECK_MODULES(WESTON_INFO, [wayland-client >= $WAYLAND_PREREQ_VERSION]) - - # Only check for cairo-egl if a GL or GLES renderer requested - AS_IF([test "x$cairo_modules" = "xcairo-gl" -o "x$cairo_modules" = "xcairo-glesv2"], [ - PKG_CHECK_MODULES(CAIRO_EGL, [wayland-egl egl cairo-egl >= 1.11.3 $cairo_modules], - [have_cairo_egl=yes], [have_cairo_egl=no]) - AS_IF([test "x$have_cairo_egl" = "xyes"], - [AC_DEFINE([HAVE_CAIRO_EGL], [1], [Have cairo-egl])], - [AC_ERROR([cairo-egl not used because $CAIRO_EGL_PKG_ERRORS])])], - [have_cairo_egl=no]) - - PKG_CHECK_MODULES(PANGO, [pangocairo pango glib-2.0 >= 2.36], [have_pango=yes], [have_pango=no]) -fi - -AC_ARG_ENABLE(resize-optimization, - AS_HELP_STRING([--disable-resize-optimization], - [disable resize optimization allocating a big buffer in toytoolkit]),, - enable_resize_optimization=yes) -AS_IF([test "x$enable_resize_optimization" = "xyes"], - [AC_DEFINE([USE_RESIZE_POOL], [1], [Use resize memory pool as a performance optimization])]) - -AC_ARG_ENABLE(weston-launch, [ --enable-weston-launch],, enable_weston_launch=yes) -AM_CONDITIONAL(BUILD_WESTON_LAUNCH, test x$enable_weston_launch = xyes) -if test x$enable_weston_launch = xyes; then - WESTON_SEARCH_LIBS([PAM], [pam], [pam_open_session], [have_pam=yes], [have_pam=no]) - if test x$have_pam = xno; then - AC_ERROR([weston-launch requires pam]) - fi -fi - -AM_CONDITIONAL(HAVE_PANGO, test "x$have_pango" = "xyes") - -AM_CONDITIONAL(HAVE_CAIRO_GLESV2, - [test "x$have_cairo_egl" = "xyes" -a "x$cairo_modules" = "xcairo-glesv2" -a "x$enable_egl" = "xyes"]) - -AM_CONDITIONAL(BUILD_FULL_GL_CLIENTS, - test x$cairo_modules = "xcairo-gl" -a "x$have_cairo_egl" = "xyes" -a "x$enable_egl" = "xyes") - -AM_CONDITIONAL(BUILD_SUBSURFACES_CLIENT, - [test '(' "x$have_cairo_egl" != "xyes" -o "x$cairo_modules" = "xcairo-glesv2" ')' -a "x$enable_simple_egl_clients" = "xyes"]) - -AM_CONDITIONAL(ENABLE_DESKTOP_SHELL, true) - -AC_ARG_ENABLE(fullscreen-shell, - AS_HELP_STRING([--disable-fullscreen-shell], - [do not build fullscreen-shell server plugin]),, - enable_fullscreen_shell=yes) -AM_CONDITIONAL(ENABLE_FULLSCREEN_SHELL, - test "x$enable_fullscreen_shell" = "xyes") - -# CMS modules -AC_ARG_ENABLE(colord, - AS_HELP_STRING([--disable-colord], - [do not build colord CMS support]),, - enable_colord=auto) -have_colord=no -if test "x$enable_colord" != "xno"; then - PKG_CHECK_MODULES(COLORD, - colord >= 0.1.27, - have_colord=yes, - have_colord=no) - if test "x$have_colord" = "xno" -a "x$enable_colord" = "xyes"; then - AC_MSG_ERROR([colord support explicitly requested, but colord couldn't be found]) - fi - if test "x$have_colord" = "xyes"; then - enable_colord=yes - fi -fi -AM_CONDITIONAL(ENABLE_COLORD, test "x$enable_colord" = "xyes") - -# dbus support -AC_ARG_ENABLE(dbus, - AS_HELP_STRING([--disable-dbus], - [do not build with dbus support]),, - enable_dbus=auto) -if test "x$enable_dbus" != "xno"; then - PKG_CHECK_MODULES(DBUS, - dbus-1 >= 1.6, - have_dbus=yes, - have_dbus=no) - if test "x$have_dbus" = "xno" -a "x$enable_dbus" = "xyes"; then - AC_MSG_ERROR([dbus support explicitly requested, but libdbus couldn't be found]) - fi - if test "x$have_dbus" = "xyes"; then - enable_dbus=yes - AC_DEFINE([HAVE_DBUS], [1], [Build with dbus support]) - else - enable_dbus=no - fi -fi -AM_CONDITIONAL(ENABLE_DBUS, test "x$enable_dbus" = "xyes") - -# systemd-login support -AC_ARG_ENABLE(systemd-login, - AS_HELP_STRING([--enable-systemd-login], - [Enable logind support]),, - enable_systemd_login=auto) -if test x$enable_systemd_login != xno -a x$have_dbus != xno; then - PKG_CHECK_MODULES(SYSTEMD_LOGIN, - [libsystemd >= 209], - [have_systemd_login_209=yes;have_systemd_login=yes], - [have_systemd_login_209=no;have_systemd_login=no]) - - # Older versions of systemd package systemd-login separately. Fall back on that - AS_IF([test x$have_systemd_login != xyes],[ - PKG_CHECK_MODULES(SYSTEMD_LOGIN, - [libsystemd-login >= 198], - [have_systemd_login=yes], - [have_systemd_login=no]) - ]) -else - have_systemd_login=no -fi - -if test "x$have_systemd_login" = "xno" -a "x$enable_systemd_login" = "xyes"; then - AC_MSG_ERROR([systemd-login support explicitly enabled, but can't find libsystemd>=209, libsystemd-login or dbus]) -fi - -AS_IF([test "x$have_systemd_login" = "xyes"], - [AC_DEFINE([HAVE_SYSTEMD_LOGIN], [1], [Have systemd-login])]) -AM_CONDITIONAL(HAVE_SYSTEMD_LOGIN, test "x$have_systemd_login" = "xyes") - -AS_IF([test "x$have_systemd_login_209" = "xyes"], - [AC_DEFINE([HAVE_SYSTEMD_LOGIN_209], [1], [Have systemd-login >= 209])]) - - -# Note that other features might want libxml2, or this feature might use -# alternative xml libraries at some point. Therefore the feature and -# pre-requisite concepts are split. -AC_ARG_ENABLE(junit_xml, - AS_HELP_STRING([--disable-junit-xml], - [do not build with JUnit XML output]),, - enable_junit_xml=auto) -if test "x$enable_junit_xml" != "xno"; then - PKG_CHECK_MODULES(LIBXML2, - [libxml-2.0 >= 2.6], - have_libxml2=yes, - have_libxml2=no) - if test "x$have_libxml2" = "xno" -a "x$enable_junit_xml" = "xyes"; then - AC_MSG_ERROR([JUnit XML support explicitly requested, but libxml2 couldn't be found]) - fi - if test "x$have_libxml2" = "xyes"; then - enable_junit_xml=yes - AC_DEFINE(ENABLE_JUNIT_XML, [1], [Build Weston with JUnit output support]) - else - enable_junit_xml=no - fi -fi -AM_CONDITIONAL(ENABLE_JUNIT_XML, test "x$enable_junit_xml" = "xyes") - -# ivi-shell support -AC_ARG_ENABLE(ivi-shell, - AS_HELP_STRING([--disable-ivi-shell], - [do not build ivi-shell server plugin and client]),, - enable_ivi_shell=yes) -AM_CONDITIONAL(ENABLE_IVI_SHELL, test "x$enable_ivi_shell" = "xyes") - -AC_ARG_ENABLE(wcap-tools, [ --disable-wcap-tools],, enable_wcap_tools=yes) -AM_CONDITIONAL(BUILD_WCAP_TOOLS, test x$enable_wcap_tools = xyes) -if test x$enable_wcap_tools = xyes; then - AC_DEFINE([BUILD_WCAP_TOOLS], [1], [Build the wcap tools]) - PKG_CHECK_MODULES(WCAP, [cairo]) - WCAP_LIBS="$WCAP_LIBS -lm" -fi - -PKG_CHECK_MODULES(SETBACKLIGHT, [libudev libdrm], enable_setbacklight=yes, enable_setbacklight=no) -AM_CONDITIONAL(BUILD_SETBACKLIGHT, test "x$enable_setbacklight" = "xyes") - -if test "x$GCC" = "xyes"; then - GCC_CFLAGS="-Wall -Wextra -Wno-unused-parameter \ - -Wno-shift-negative-value -Wno-missing-field-initializers \ - -g -fvisibility=hidden \ - -Wstrict-prototypes -Wmissing-prototypes -Wsign-compare" -fi -AC_SUBST(GCC_CFLAGS) - -AC_ARG_ENABLE(libunwind, - AS_HELP_STRING([--disable-libunwind], - [Disable libunwind usage for backtraces]),, - enable_libunwind=auto) -AM_CONDITIONAL(HAVE_LIBUNWIND, [test "x$enable_libunwind" = xyes]) -have_libunwind=no -if test "x$enable_libunwind" != "xno"; then - PKG_CHECK_MODULES(LIBUNWIND, - libunwind, - have_libunwind=yes, - have_libunwind=no) - if test "x$have_libunwind" = "xno" -a "x$enable_libunwind" = "xyes"; then - AC_MSG_ERROR([libunwind support explicitly requested, but libunwind couldn't be found]) - fi - if test "x$have_libunwind" = "xyes"; then - enable_libunwind=yes - AC_DEFINE(HAVE_LIBUNWIND, 1, [Have libunwind support]) - fi -fi - - -if test "x$WESTON_NATIVE_BACKEND" = "x"; then - WESTON_NATIVE_BACKEND="drm-backend.so" -fi -AC_MSG_NOTICE([Weston's native backend: $WESTON_NATIVE_BACKEND]) -AC_DEFINE_UNQUOTED([WESTON_NATIVE_BACKEND], ["$WESTON_NATIVE_BACKEND"], - [The default backend to load, if not wayland nor x11.]) - -if test "x$WESTON_SHELL_CLIENT" = "x"; then - WESTON_SHELL_CLIENT="weston-desktop-shell" -fi -AC_MSG_NOTICE([Weston's default desktop shell client: $WESTON_SHELL_CLIENT]) -AC_DEFINE_UNQUOTED([WESTON_SHELL_CLIENT], ["$WESTON_SHELL_CLIENT"], - [The default desktop shell client to load.]) - -AC_ARG_ENABLE(demo-clients-install, - AS_HELP_STRING([--enable-demo-clients-install], - [Install demo clients built with weston]),, - enable_demo_clients_install=no) -AM_CONDITIONAL(INSTALL_DEMO_CLIENTS, [test "x$enable_demo_clients_install" = "xyes"]) - -AC_ARG_ENABLE(lcms, - AS_HELP_STRING([--disable-lcms], - [Disable lcms support]),, - enable_lcms=auto) -have_lcms=no -if test "x$enable_lcms" != "xno"; then - PKG_CHECK_MODULES(LCMS, - lcms2, - have_lcms=yes, - have_lcms=no) - if test "x$have_lcms" = "xno" -a "x$enable_lcms" = "xyes"; then - AC_MSG_ERROR([lcms support explicitly requested, but lcms couldn't be found]) - fi - if test "x$have_lcms" = "xyes"; then - enable_lcms=yes - AC_DEFINE(HAVE_LCMS, 1, [Have lcms support]) - fi -fi -AM_CONDITIONAL(HAVE_LCMS, [test "x$enable_lcms" = xyes]) - -AC_PATH_PROG([wayland_scanner], [wayland-scanner]) -if test x$wayland_scanner = x; then - PKG_CHECK_MODULES(WAYLAND_SCANNER, [wayland-scanner]) - wayland_scanner=`$PKG_CONFIG --variable=wayland_scanner wayland-scanner` -fi - -AC_ARG_ENABLE(systemd_notify, - AS_HELP_STRING([--enable-systemd-notify], - [Enables systemd notifications to - notify systemd about weston state - and update watchdog. - Also sockets provided by systemd - in case of socket-base activation - are added to wayland display]),, - enable_systemd_notify=no) -AM_CONDITIONAL(SYSTEMD_NOTIFY_SUPPORT, test x$enable_systemd_notify = xyes) -if test "x$enable_systemd_notify" = "xyes"; then - AC_DEFINE([SYSTEMD_NOTIFY_SUPPORT], [1], [Build the systemd sd_notify support]) - PKG_CHECK_MODULES(SYSTEMD_DAEMON, [libsystemd]) -fi - -AC_CONFIG_FILES([Makefile libweston/version.h compositor/weston.pc]) - -# AC_CONFIG_FILES needs the full name when running autoconf, so we need to use -# libweston_abi_version here, and outside [] because of m4 quoting rules -AC_CONFIG_FILES([libweston/libweston-]libweston_major_version[.pc:libweston/libweston.pc.in]) -AC_CONFIG_FILES([libweston-desktop/libweston-desktop-]libweston_major_version[.pc:libweston-desktop/libweston-desktop.pc.in]) - -AM_CONDITIONAL([HAVE_GIT_REPO], [test -f $srcdir/.git/logs/HEAD]) - -AC_OUTPUT - -AC_MSG_RESULT([ - Native Backend ${WESTON_NATIVE_BACKEND} - setuid Install ${enable_setuid_install} - - Cairo Renderer ${with_cairo} - EGL ${enable_egl} - libxkbcommon ${enable_xkbcommon} - xcb_xkb ${have_xcb_xkb} - XWayland ${enable_xwayland} - dbus ${enable_dbus} - - ivi-shell ${enable_ivi_shell} - - Build wcap utility ${enable_wcap_tools} - Build Fullscreen Shell ${enable_fullscreen_shell} - Enable developer documentation ${enable_devdocs} - - weston-launch utility ${enable_weston_launch} - systemd-login support ${have_systemd_login} - systemd notify support ${enable_systemd_notify} - - DRM Compositor ${enable_drm_compositor} - X11 Compositor ${enable_x11_compositor} - Wayland Compositor ${enable_wayland_compositor} - Headless Compositor ${enable_headless_compositor} - FBDEV Compositor ${enable_fbdev_compositor} - RDP Compositor ${enable_rdp_compositor} - Screen Sharing ${enable_screen_sharing} - JUnit XML output ${enable_junit_xml} - - Build Clients ${enable_clients} - Build EGL Clients ${have_cairo_egl} - Build Simple Clients ${enable_simple_clients} - Build Simple EGL Clients ${enable_simple_egl_clients} - - Install Demo Clients ${enable_demo_clients_install} - - Colord Support ${have_colord} - LCMS2 Support ${have_lcms} - libjpeg Support ${have_jpeglib} - libwebp Support ${have_webp} - libunwind Support ${have_libunwind} - VA H.264 encoding Support ${have_libva} -]) diff --git a/data/background.png b/data/background.png index 60c317c94..67caee38c 100644 Binary files a/data/background.png and b/data/background.png differ diff --git a/data/border.png b/data/border.png index 6a3a8f9b2..3f9388bfa 100644 Binary files a/data/border.png and b/data/border.png differ diff --git a/data/fullscreen.png b/data/fullscreen.png index dc5a302d5..dd4944b15 100644 Binary files a/data/fullscreen.png and b/data/fullscreen.png differ diff --git a/data/home.png b/data/home.png index af9f60372..b7ff0a554 100644 Binary files a/data/home.png and b/data/home.png differ diff --git a/data/icon_editor.png b/data/icon_editor.png index 19aa494c5..d1324ad97 100644 Binary files a/data/icon_editor.png and b/data/icon_editor.png differ diff --git a/data/icon_flower.png b/data/icon_flower.png index 661935278..1e80f2c8c 100644 Binary files a/data/icon_flower.png and b/data/icon_flower.png differ diff --git a/data/icon_ivi_clickdot.png b/data/icon_ivi_clickdot.png index 711bcfcbd..392ca95a6 100644 Binary files a/data/icon_ivi_clickdot.png and b/data/icon_ivi_clickdot.png differ diff --git a/data/icon_ivi_flower.png b/data/icon_ivi_flower.png index 0a71479c6..7d3231566 100644 Binary files a/data/icon_ivi_flower.png and b/data/icon_ivi_flower.png differ diff --git a/data/icon_ivi_simple-egl.png b/data/icon_ivi_simple-egl.png index 025395033..dedc8eecd 100644 Binary files a/data/icon_ivi_simple-egl.png and b/data/icon_ivi_simple-egl.png differ diff --git a/data/icon_ivi_simple-shm.png b/data/icon_ivi_simple-shm.png index 6aacaaa87..85676f29f 100644 Binary files a/data/icon_ivi_simple-shm.png and b/data/icon_ivi_simple-shm.png differ diff --git a/data/icon_ivi_smoke.png b/data/icon_ivi_smoke.png index 0dbe20ea0..aead31d3b 100644 Binary files a/data/icon_ivi_smoke.png and b/data/icon_ivi_smoke.png differ diff --git a/data/icon_terminal.png b/data/icon_terminal.png index 93ac8ff43..4c4c3b694 100644 Binary files a/data/icon_terminal.png and b/data/icon_terminal.png differ diff --git a/data/icon_window.png b/data/icon_window.png index 2587ca347..2a07cc161 100644 Binary files a/data/icon_window.png and b/data/icon_window.png differ diff --git a/data/meson.build b/data/meson.build new file mode 100644 index 000000000..16f172fec --- /dev/null +++ b/data/meson.build @@ -0,0 +1,29 @@ +install_data( + [ + 'background.png', + 'border.png', + 'fullscreen.png', + 'home.png', + 'icon_editor.png', + 'icon_flower.png', + 'icon_ivi_clickdot.png', + 'icon_ivi_flower.png', + 'icon_ivi_simple-egl.png', + 'icon_ivi_simple-shm.png', + 'icon_ivi_smoke.png', + 'icon_terminal.png', + 'icon_window.png', + 'panel.png', + 'pattern.png', + 'random.png', + 'sidebyside.png', + 'sign_close.png', + 'sign_maximize.png', + 'sign_minimize.png', + 'terminal.png', + 'tiling.png', + 'wayland.png', + 'wayland.svg', + ], + install_dir: join_paths(dir_data, 'weston') +) diff --git a/data/panel.png b/data/panel.png index 0fd72b87f..f0f46b2b9 100644 Binary files a/data/panel.png and b/data/panel.png differ diff --git a/data/pattern.png b/data/pattern.png index 5ac8986d6..27dddc4ba 100644 Binary files a/data/pattern.png and b/data/pattern.png differ diff --git a/data/random.png b/data/random.png index 7640aa052..2ed2a1c56 100644 Binary files a/data/random.png and b/data/random.png differ diff --git a/data/sidebyside.png b/data/sidebyside.png index 98ea7803b..f5b6f5363 100644 Binary files a/data/sidebyside.png and b/data/sidebyside.png differ diff --git a/data/sign_close.png b/data/sign_close.png index 741ea0b05..56182a3c4 100644 Binary files a/data/sign_close.png and b/data/sign_close.png differ diff --git a/data/sign_maximize.png b/data/sign_maximize.png index 2443a4c03..ae9e86abe 100644 Binary files a/data/sign_maximize.png and b/data/sign_maximize.png differ diff --git a/data/sign_minimize.png b/data/sign_minimize.png index f4f3d9d23..fd3a64aaf 100644 Binary files a/data/sign_minimize.png and b/data/sign_minimize.png differ diff --git a/data/terminal.png b/data/terminal.png index 3c02dd210..629575344 100644 Binary files a/data/terminal.png and b/data/terminal.png differ diff --git a/data/tiling.png b/data/tiling.png index dd3e02c0f..3d711dc07 100644 Binary files a/data/tiling.png and b/data/tiling.png differ diff --git a/data/wayland.png b/data/wayland.png index 1b7363e0b..fe3525ed4 100644 Binary files a/data/wayland.png and b/data/wayland.png differ diff --git a/desktop-shell/exposay.c b/desktop-shell/exposay.c index b11a7f794..7e9a324c4 100644 --- a/desktop-shell/exposay.c +++ b/desktop-shell/exposay.c @@ -207,9 +207,50 @@ handle_view_destroy(struct wl_listener *listener, void *data) exposay_surface_destroy(esurface); } -/* Pretty lame layout for now; just tries to make a square. Should take +/* Compute each surface size and then inner pad (10% of surface size). + * After that, it's necessary to recompute surface size (90% of its + * original size). Also, each surface can't be bigger than half the + * exposay area width and height. + */ +static void +exposay_surface_and_inner_pad_size(pixman_rectangle32_t exposay_area, struct exposay_output *eoutput) +{ + if (exposay_area.height < exposay_area.width) + eoutput->surface_size = exposay_area.height / eoutput->grid_size; + else + eoutput->surface_size = exposay_area.width / eoutput->grid_size; + + eoutput->padding_inner = eoutput->surface_size / 10; + eoutput->surface_size -= eoutput->padding_inner; + + if ((uint32_t)eoutput->surface_size > (exposay_area.width / 2)) + eoutput->surface_size = exposay_area.width / 2; + if ((uint32_t)eoutput->surface_size > (exposay_area.height / 2)) + eoutput->surface_size = exposay_area.height / 2; +} + +/* Compute the exposay top/left margin in order to centralize it */ +static void +exposay_margin_size(struct desktop_shell *shell, pixman_rectangle32_t exposay_area, + int row_size, int column_size, int *left_margin, int *top_margin) +{ + (*left_margin) = exposay_area.x + (exposay_area.width - row_size) / 2; + (*top_margin) = exposay_area.y + (exposay_area.height - column_size) / 2; +} + +/* 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. */ + * addition and removal and adjust layout/animate accordingly. + * + * 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 maximise the area made available to surfaces + * after this. Also, add an inner padding between surfaces that varies + * with the surface size (10% of its size). + * + * If we can't make a square grid, add one extra row at the bottom which + * will have a smaller number of columns. + */ static enum exposay_layout_state exposay_layout(struct desktop_shell *shell, struct shell_output *shell_output) { @@ -218,9 +259,11 @@ exposay_layout(struct desktop_shell *shell, struct shell_output *shell_output) struct exposay_output *eoutput = &shell_output->eoutput; struct weston_view *view; struct exposay_surface *esurface, *highlight = NULL; - int w, h; + pixman_rectangle32_t exposay_area; + int pad, row_size, column_size, left_margin, top_margin; + int last_row_size, last_row_margin_increase; + int populated_rows; int i; - int last_row_removed = 0; eoutput->num_surfaces = 0; wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) { @@ -233,53 +276,45 @@ exposay_layout(struct desktop_shell *shell, struct shell_output *shell_output) 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?! - */ + /* Get exposay area and position, taking into account + * the shell panel position and size */ + get_output_work_area(shell, output, &exposay_area); + + /* Compute grid size */ 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; + /* Compute each surface size and the inner padding between them */ + exposay_surface_and_inner_pad_size(exposay_area, eoutput); + + /* Compute each row/column size */ + pad = eoutput->surface_size + eoutput->padding_inner; + row_size = (pad * eoutput->grid_size) - eoutput->padding_inner; + /* We may have empty rows that should be desconsidered to compute + * column size */ + populated_rows = ceil(eoutput->num_surfaces / (float) eoutput->grid_size); + column_size = (pad * populated_rows) - eoutput->padding_inner; + + /* The last row size can be different, since it may have less surfaces + * than the grid size. Also, its margin may be increased to centralize + * its surfaces, in the case where we don't have a perfect grid. */ + last_row_size = ((eoutput->num_surfaces % eoutput->grid_size) * pad) - eoutput->padding_inner; + if (eoutput->num_surfaces % eoutput->grid_size) + last_row_margin_increase = (row_size - last_row_size) / 2; + else + last_row_margin_increase = 0; - 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; + /* Compute a top/left margin to centralize the exposay */ + exposay_margin_size(shell, exposay_area, row_size, column_size, &left_margin, &top_margin); 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; @@ -301,13 +336,13 @@ exposay_layout(struct desktop_shell *shell, struct shell_output *shell_output) 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; + esurface->x = left_margin + (pad * esurface->column); + esurface->y = top_margin + (pad * esurface->row); - if (esurface->row == eoutput->grid_size - 1) - esurface->x += (eoutput->surface_size + eoutput->padding_inner) * last_row_removed / 2; + /* If this is the last row, increase left margin (it sums 0 if + * we have a perfect square) to centralize the surfaces */ + if (eoutput->num_surfaces / eoutput->grid_size == esurface->row) + esurface->x += last_row_margin_increase; if (view->surface->width > view->surface->height) esurface->scale = eoutput->surface_size / (float) view->surface->width; @@ -316,6 +351,13 @@ exposay_layout(struct desktop_shell *shell, struct shell_output *shell_output) esurface->width = view->surface->width * esurface->scale; esurface->height = view->surface->height * esurface->scale; + /* Surfaces are usually rectangular, but their exposay surfaces + * are square. centralize them in their own square */ + if (esurface->width > esurface->height) + esurface->y += (esurface->width - esurface->height) / 2; + else + esurface->x += (esurface->height - esurface->width) / 2; + if (shell->exposay.focus_current == esurface->view) highlight = esurface; @@ -349,7 +391,8 @@ exposay_focus(struct weston_pointer_grab *grab) } static void -exposay_motion(struct weston_pointer_grab *grab, uint32_t time, +exposay_motion(struct weston_pointer_grab *grab, + const struct timespec *time, struct weston_pointer_motion_event *event) { struct desktop_shell *shell = @@ -363,8 +406,8 @@ exposay_motion(struct weston_pointer_grab *grab, uint32_t time, } static void -exposay_button(struct weston_pointer_grab *grab, uint32_t time, uint32_t button, - uint32_t state_w) +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); @@ -389,7 +432,8 @@ exposay_button(struct weston_pointer_grab *grab, uint32_t time, uint32_t button, static void exposay_axis(struct weston_pointer_grab *grab, - uint32_t time, struct weston_pointer_axis_event *event) + const struct timespec *time, + struct weston_pointer_axis_event *event) { } @@ -440,8 +484,8 @@ exposay_maybe_move(struct desktop_shell *shell, int row, int column) } static void -exposay_key(struct weston_keyboard_grab *grab, uint32_t time, uint32_t key, - uint32_t state_w) +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 = diff --git a/desktop-shell/input-panel.c b/desktop-shell/input-panel.c index 40a4092e4..8292f20a6 100644 --- a/desktop-shell/input-panel.c +++ b/desktop-shell/input-panel.c @@ -270,11 +270,13 @@ 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; wl_list_insert(&shell->input_panel.surfaces, &input_panel_surface->link); - input_panel_surface->output = wl_resource_get_user_data(output_resource); + head = weston_head_from_resource(output_resource); + input_panel_surface->output = head->output; input_panel_surface->panel = 0; } diff --git a/desktop-shell/meson.build b/desktop-shell/meson.build new file mode 100644 index 000000000..c67bfd600 --- /dev/null +++ b/desktop-shell/meson.build @@ -0,0 +1,31 @@ +if get_option('shell-desktop') + config_h.set_quoted('WESTON_SHELL_CLIENT', get_option('desktop-shell-client-default')) + + srcs_shell_desktop = [ + 'shell.c', + 'exposay.c', + 'input-panel.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_lib_desktop, + dep_libweston_public, + ] + plugin_shell_desktop = shared_library( + 'desktop-shell', + srcs_shell_desktop, + include_directories: common_inc, + dependencies: deps_shell_desktop, + name_prefix: '', + install: true, + install_dir: dir_module_weston, + install_rpath: '$ORIGIN' + ) + env_modmap += 'desktop-shell.so=@0@;'.format(plugin_shell_desktop.full_path()) +endif diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index f80088f76..e102ee7e9 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -39,17 +39,14 @@ #include "shell.h" #include "compositor/weston.h" #include "weston-desktop-shell-server-protocol.h" -#include "shared/config-parser.h" +#include #include "shared/helpers.h" -#include "libweston-desktop/libweston-desktop.h" +#include "shared/timespec-util.h" +#include #define DEFAULT_NUM_WORKSPACES 1 #define DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH 200 -#ifndef static_assert -#define static_assert(cond, msg) -#endif - struct focus_state { struct desktop_shell *shell; struct weston_seat *seat; @@ -107,6 +104,9 @@ struct shell_surface { struct desktop_shell *shell; + struct wl_list children_list; + struct wl_list children_link; + int32_t saved_x, saved_y; bool saved_position_valid; bool saved_rotation_valid; @@ -127,6 +127,7 @@ struct shell_surface { struct weston_output *fullscreen_output; struct weston_output *output; + struct wl_listener output_destroy_listener; struct surface_state { bool fullscreen; @@ -202,6 +203,9 @@ surface_rotate(struct shell_surface *surface, struct weston_pointer *pointer); static void shell_fade_startup(struct desktop_shell *shell); +static void +shell_fade(struct desktop_shell *shell, enum fade_type type); + static struct shell_seat * get_shell_seat(struct weston_seat *seat); @@ -330,13 +334,22 @@ get_output_panel_size(struct desktop_shell *shell, /* the correct view wasn't found */ } -static void +void get_output_work_area(struct desktop_shell *shell, struct weston_output *output, pixman_rectangle32_t *area) { int32_t panel_width = 0, panel_height = 0; + if (!output) { + area->x = 0; + area->y = 0; + area->width = 0; + area->height = 0; + + return; + } + area->x = output->x; area->y = output->y; @@ -345,12 +358,14 @@ get_output_work_area(struct desktop_shell *shell, case WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP: default: area->y += panel_height; + /* fallthrough */ case WESTON_DESKTOP_SHELL_PANEL_POSITION_BOTTOM: area->width = output->width; area->height = output->height - panel_height; break; case WESTON_DESKTOP_SHELL_PANEL_POSITION_LEFT: area->x += panel_width; + /* fallthrough */ case WESTON_DESKTOP_SHELL_PANEL_POSITION_RIGHT: area->width = output->width - panel_width; area->height = output->height; @@ -452,17 +467,12 @@ shell_configuration(struct desktop_shell *shell) { struct weston_config_section *section; char *s, *client; - int ret; - int allow_zap; + bool allow_zap; section = weston_config_get_section(wet_get_config(shell->compositor), "shell", NULL, NULL); - ret = asprintf(&client, "%s/%s", weston_config_get_libexec_dir(), - WESTON_SHELL_CLIENT); - if (ret < 0) - client = NULL; - weston_config_section_get_string(section, - "client", &s, client); + client = wet_get_libexec_path(WESTON_SHELL_CLIENT); + weston_config_section_get_string(section, "client", &s, client); free(client); shell->client = s; @@ -514,7 +524,7 @@ static int focus_surface_get_label(struct weston_surface *surface, char *buf, size_t len) { return snprintf(buf, len, "focus highlight effect for output %s", - surface->output->name); + (surface->output ? surface->output->name : "NULL")); } /* no-op func for checking focus surface */ @@ -574,7 +584,7 @@ create_focus_surface(struct weston_compositor *ec, free(fsurf); return NULL; } - fsurf->view->output = output; + weston_view_set_output(fsurf->view, output); fsurf->view->is_mapped = true; weston_surface_set_size(surface, output->width, output->height); @@ -656,6 +666,10 @@ focus_state_surface_destroy(struct wl_listener *listener, void *data) next = get_default_view(main_surface); if (next) { + if (state->keyboard_focus) { + wl_list_remove(&state->surface_destroy_listener.link); + wl_list_init(&state->surface_destroy_listener.link); + } state->keyboard_focus = NULL; activate(state->shell, next, state->seat, WESTON_ACTIVATE_FLAG_CONFIGURE); @@ -954,19 +968,32 @@ get_output_height(struct weston_output *output) return abs(output->region.extents.y1 - output->region.extents.y2); } -static void -view_translate(struct workspace *ws, struct weston_view *view, double d) +static struct weston_transform * +view_get_transform(struct weston_view *view) { - struct weston_transform *transform; + struct focus_surface *fsurf = NULL; + struct shell_surface *shsurf = NULL; if (is_focus_view(view)) { - struct focus_surface *fsurf = get_focus_surface(view->surface); - transform = &fsurf->workspace_transform; - } else { - struct shell_surface *shsurf = get_shell_surface(view->surface); - transform = &shsurf->workspace_transform; + fsurf = get_focus_surface(view->surface); + return &fsurf->workspace_transform; } + shsurf = get_shell_surface(view->surface); + if (shsurf) + return &shsurf->workspace_transform; + + return NULL; +} + +static void +view_translate(struct workspace *ws, struct weston_view *view, double d) +{ + struct weston_transform *transform = view_get_transform(view); + + if (!transform) + return; + if (wl_list_empty(&transform->link)) wl_list_insert(view->geometry.transformation_list.prev, &transform->link); @@ -1022,7 +1049,7 @@ reverse_workspace_change_animation(struct desktop_shell *shell, shell->workspaces.anim_to = to; shell->workspaces.anim_from = from; shell->workspaces.anim_dir = -1 * shell->workspaces.anim_dir; - shell->workspaces.anim_timestamp = 0; + shell->workspaces.anim_timestamp = (struct timespec) { 0 }; weston_layer_set_position(&to->layer, WESTON_LAYER_POSITION_NORMAL); weston_layer_set_position(&from->layer, WESTON_LAYER_POSITION_NORMAL - 1); @@ -1037,13 +1064,9 @@ workspace_deactivate_transforms(struct workspace *ws) struct weston_transform *transform; wl_list_for_each(view, &ws->layer.view_list.link, layer_link.link) { - if (is_focus_view(view)) { - struct focus_surface *fsurf = get_focus_surface(view->surface); - transform = &fsurf->workspace_transform; - } else { - struct shell_surface *shsurf = get_shell_surface(view->surface); - transform = &shsurf->workspace_transform; - } + transform = view_get_transform(view); + if (!transform) + continue; if (!wl_list_empty(&transform->link)) { wl_list_remove(&transform->link); @@ -1079,14 +1102,15 @@ finish_workspace_change_animation(struct desktop_shell *shell, static void animate_workspace_change_frame(struct weston_animation *animation, - struct weston_output *output, uint32_t msecs) + struct weston_output *output, + const struct timespec *time) { struct desktop_shell *shell = container_of(animation, struct desktop_shell, workspaces.animation); struct workspace *from = shell->workspaces.anim_from; struct workspace *to = shell->workspaces.anim_to; - uint32_t t; + int64_t t; double x, y; if (workspace_is_empty(from) && workspace_is_empty(to)) { @@ -1094,19 +1118,19 @@ animate_workspace_change_frame(struct weston_animation *animation, return; } - if (shell->workspaces.anim_timestamp == 0) { + if (timespec_is_zero(&shell->workspaces.anim_timestamp)) { if (shell->workspaces.anim_current == 0.0) - shell->workspaces.anim_timestamp = msecs; + shell->workspaces.anim_timestamp = *time; else - shell->workspaces.anim_timestamp = - msecs - - /* Invers of movement function 'y' below. */ - (asin(1.0 - shell->workspaces.anim_current) * - DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH * - M_2_PI); + timespec_add_msec(&shell->workspaces.anim_timestamp, + time, + /* Inverse of movement function 'y' below. */ + -(asin(1.0 - shell->workspaces.anim_current) * + DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH * + M_2_PI)); } - t = msecs - shell->workspaces.anim_timestamp; + t = timespec_sub_to_msec(time, &shell->workspaces.anim_timestamp); /* * x = [0, π/2] @@ -1149,7 +1173,7 @@ animate_workspace_change(struct desktop_shell *shell, shell->workspaces.anim_from = from; shell->workspaces.anim_to = to; shell->workspaces.anim_current = 0.0; - shell->workspaces.anim_timestamp = 0; + shell->workspaces.anim_timestamp = (struct timespec) { 0 }; output = container_of(shell->compositor->output_list.next, struct weston_output, link); @@ -1324,13 +1348,15 @@ take_surface_to_workspace_by_seat(struct desktop_shell *shell, } static void -touch_move_grab_down(struct weston_touch_grab *grab, uint32_t time, +touch_move_grab_down(struct weston_touch_grab *grab, + const struct timespec *time, int touch_id, wl_fixed_t x, wl_fixed_t y) { } static void -touch_move_grab_up(struct weston_touch_grab *grab, uint32_t time, int touch_id) +touch_move_grab_up(struct weston_touch_grab *grab, const struct timespec *time, + int touch_id) { struct weston_touch_move_grab *move = (struct weston_touch_move_grab *) container_of( @@ -1346,8 +1372,9 @@ touch_move_grab_up(struct weston_touch_grab *grab, uint32_t time, int touch_id) } static void -touch_move_grab_motion(struct weston_touch_grab *grab, uint32_t time, - int touch_id, wl_fixed_t x, wl_fixed_t y) +touch_move_grab_motion(struct weston_touch_grab *grab, + const struct timespec *time, int touch_id, + wl_fixed_t x, wl_fixed_t y) { struct weston_touch_move_grab *move = (struct weston_touch_move_grab *) grab; struct shell_surface *shsurf = move->base.shsurf; @@ -1424,7 +1451,8 @@ noop_grab_focus(struct weston_pointer_grab *grab) static void noop_grab_axis(struct weston_pointer_grab *grab, - uint32_t time, struct weston_pointer_axis_event *event) + const struct timespec *time, + struct weston_pointer_axis_event *event) { } @@ -1475,7 +1503,8 @@ constrain_position(struct weston_move_grab *move, int *cx, int *cy) } static void -move_grab_motion(struct weston_pointer_grab *grab, uint32_t time, +move_grab_motion(struct weston_pointer_grab *grab, + const struct timespec *time, struct weston_pointer_motion_event *event) { struct weston_move_grab *move = (struct weston_move_grab *) grab; @@ -1499,7 +1528,7 @@ move_grab_motion(struct weston_pointer_grab *grab, uint32_t time, static void move_grab_button(struct weston_pointer_grab *grab, - uint32_t time, uint32_t button, uint32_t state_w) + const struct timespec *time, uint32_t button, uint32_t state_w) { struct shell_grab *shell_grab = container_of(grab, struct shell_grab, grab); @@ -1570,7 +1599,8 @@ struct weston_resize_grab { }; static void -resize_grab_motion(struct weston_pointer_grab *grab, uint32_t time, +resize_grab_motion(struct weston_pointer_grab *grab, + const struct timespec *time, struct weston_pointer_motion_event *event) { struct weston_resize_grab *resize = (struct weston_resize_grab *) grab; @@ -1618,14 +1648,15 @@ resize_grab_motion(struct weston_pointer_grab *grab, uint32_t time, width = max_size.width; if (height < min_size.height) height = min_size.height; - else if (max_size.width > 0 && width > max_size.width) - width = max_size.width; + else if (max_size.height > 0 && height > max_size.height) + height = max_size.height; weston_desktop_surface_set_size(shsurf->desktop_surface, width, height); } static void resize_grab_button(struct weston_pointer_grab *grab, - uint32_t time, uint32_t button, uint32_t state_w) + const struct timespec *time, + uint32_t button, uint32_t state_w) { struct weston_resize_grab *resize = (struct weston_resize_grab *) grab; struct weston_pointer *pointer = grab->pointer; @@ -1633,6 +1664,13 @@ resize_grab_button(struct weston_pointer_grab *grab, if (pointer->button_count == 0 && state == WL_POINTER_BUTTON_STATE_RELEASED) { + if (resize->base.shsurf != NULL) { + struct weston_desktop_surface *desktop_surface = + resize->base.shsurf->desktop_surface; + weston_desktop_surface_set_resizing(desktop_surface, + false); + } + shell_grab_end(&resize->base); free(grab); } @@ -1643,6 +1681,12 @@ resize_grab_cancel(struct weston_pointer_grab *grab) { struct weston_resize_grab *resize = (struct weston_resize_grab *) grab; + if (resize->base.shsurf != NULL) { + struct weston_desktop_surface *desktop_surface = + resize->base.shsurf->desktop_surface; + weston_desktop_surface_set_resizing(desktop_surface, false); + } + shell_grab_end(&resize->base); free(grab); } @@ -1726,6 +1770,7 @@ surface_resize(struct shell_surface *shsurf, resize->height = geometry.height; shsurf->resize_edges = edges; + weston_desktop_surface_set_resizing(shsurf->desktop_surface, true); shell_grab_start(&resize->base, &resize_grab_interface, shsurf, pointer, edges); @@ -1753,7 +1798,8 @@ busy_cursor_grab_focus(struct weston_pointer_grab *base) } static void -busy_cursor_grab_motion(struct weston_pointer_grab *grab, uint32_t time, +busy_cursor_grab_motion(struct weston_pointer_grab *grab, + const struct timespec *time, struct weston_pointer_motion_event *event) { weston_pointer_move(grab->pointer, event); @@ -1761,7 +1807,8 @@ busy_cursor_grab_motion(struct weston_pointer_grab *grab, uint32_t time, static void busy_cursor_grab_button(struct weston_pointer_grab *base, - uint32_t time, uint32_t button, uint32_t state) + const struct timespec *time, + uint32_t button, uint32_t state) { struct shell_grab *grab = (struct shell_grab *) base; struct shell_surface *shsurf = grab->shsurf; @@ -1847,7 +1894,7 @@ handle_keyboard_focus(struct wl_listener *listener, void *data) shell_surface_lose_keyboard_focus(shsurf); } - seat->focused_surface = keyboard->focus; + seat->focused_surface = weston_surface_get_main_surface(keyboard->focus); if (seat->focused_surface) { struct shell_surface *shsurf = get_shell_surface(seat->focused_surface); @@ -1910,6 +1957,17 @@ shell_surface_update_layer(struct shell_surface *shsurf) shell_surface_update_child_surface_layers(shsurf); } +static void +notify_output_destroy(struct wl_listener *listener, void *data) +{ + struct shell_surface *shsurf = + container_of(listener, + struct shell_surface, output_destroy_listener); + + shsurf->output = NULL; + shsurf->output_destroy_listener.notify = NULL; +} + static void shell_surface_set_output(struct shell_surface *shsurf, struct weston_output *output) @@ -1925,6 +1983,18 @@ shell_surface_set_output(struct shell_surface *shsurf, shsurf->output = es->output; else shsurf->output = get_default_output(es->compositor); + + if (shsurf->output_destroy_listener.notify) { + wl_list_remove(&shsurf->output_destroy_listener.link); + shsurf->output_destroy_listener.notify = NULL; + } + + if (!shsurf->output) + return; + + shsurf->output_destroy_listener.notify = notify_output_destroy; + wl_signal_add(&shsurf->output->destroy_signal, + &shsurf->output_destroy_listener); } static void @@ -1963,7 +2033,7 @@ unset_maximized(struct shell_surface *shsurf) weston_desktop_surface_get_surface(shsurf->desktop_surface); /* undo all maximized things here */ - shsurf->output = get_default_output(surface->compositor); + shell_surface_set_output(shsurf, get_default_output(surface->compositor)); if (shsurf->saved_position_valid) weston_view_set_position(shsurf->view, @@ -2120,6 +2190,13 @@ shell_configure_fullscreen(struct shell_surface *shsurf) weston_layer_entry_insert(&shsurf->shell->fullscreen_layer.view_list, &shsurf->view->layer_link); + if (!shsurf->fullscreen_output) { + /* If there is no output, there's not much we can do. + * Position the window somewhere, whatever. */ + weston_view_set_position(shsurf->view, 0, 0); + return; + } + shell_ensure_fullscreen_black_view(shsurf); surface_subsurfaces_boundingbox(surface, &surf_x, &surf_y, @@ -2149,7 +2226,7 @@ get_focused_output(struct weston_compositor *compositor) /* Priority has touch focus, then pointer and * then keyboard focus. We should probably have - * three for loops and check frist for touch, + * three for loops and check first for touch, * then for pointer, etc. but unless somebody has some * objections, I think this is sufficient. */ if (touch && touch->focus) @@ -2256,6 +2333,12 @@ fade_out_done_idle_cb(void *data) struct shell_surface *shsurf = data; weston_surface_destroy(shsurf->view->surface); + + if (shsurf->output_destroy_listener.notify) { + wl_list_remove(&shsurf->output_destroy_listener.link); + shsurf->output_destroy_listener.notify = NULL; + } + free(shsurf); } @@ -2268,7 +2351,7 @@ fade_out_done(struct weston_view_animation *animation, void *data) loop = wl_display_get_event_loop(shsurf->shell->compositor->wl_display); if (weston_view_is_mapped(shsurf->view)) { - shsurf->view->is_mapped = false; + weston_view_unmap(shsurf->view); wl_event_loop_add_idle(loop, fade_out_done_idle_cb, shsurf); } } @@ -2325,7 +2408,8 @@ desktop_surface_added(struct weston_desktop_surface *desktop_surface, shsurf->fullscreen.black_view = NULL; wl_list_init(&shsurf->fullscreen.transform.link); - shsurf->output = get_default_output(shsurf->shell->compositor); + shell_surface_set_output( + shsurf, get_default_output(shsurf->shell->compositor)); wl_signal_init(&shsurf->destroy_signal); @@ -2335,6 +2419,13 @@ desktop_surface_added(struct weston_desktop_surface *desktop_surface, wl_list_init(&shsurf->workspace_transform.link); + /* + * initialize list as well as link. The latter allows to use + * wl_list_remove() even when this surface is not in another list. + */ + wl_list_init(&shsurf->children_list); + wl_list_init(&shsurf->children_link); + weston_desktop_surface_set_user_data(desktop_surface, shsurf); weston_desktop_surface_set_activated(desktop_surface, shsurf->focus_count > 0); @@ -2346,12 +2437,19 @@ desktop_surface_removed(struct weston_desktop_surface *desktop_surface, { struct shell_surface *shsurf = weston_desktop_surface_get_user_data(desktop_surface); + struct shell_surface *shsurf_child, *tmp; struct weston_surface *surface = weston_desktop_surface_get_surface(desktop_surface); if (!shsurf) return; + wl_list_for_each_safe(shsurf_child, tmp, &shsurf->children_list, children_link) { + wl_list_remove(&shsurf_child->children_link); + wl_list_init(&shsurf_child->children_link); + } + wl_list_remove(&shsurf->children_link); + wl_signal_emit(&shsurf->destroy_signal, shsurf); if (shsurf->fullscreen.black_view) @@ -2372,6 +2470,12 @@ desktop_surface_removed(struct weston_desktop_surface *desktop_surface, fade_out_done, shsurf); } else { weston_view_destroy(shsurf->view); + + if (shsurf->output_destroy_listener.notify) { + wl_list_remove(&shsurf->output_destroy_listener.link); + shsurf->output_destroy_listener.notify = NULL; + } + free(shsurf); } } @@ -2441,7 +2545,7 @@ map(struct desktop_shell *shell, struct shell_surface *shsurf, shsurf->view->is_mapped = true; if (shsurf->state.maximized) { surface->output = shsurf->output; - shsurf->view->output = shsurf->output; + weston_view_set_output(shsurf->view, shsurf->output); } if (!shell->locked) { @@ -2543,9 +2647,6 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, if (shsurf->resize_edges & WL_SHELL_SURFACE_RESIZE_TOP) sy = shsurf->last_height - surface->height; - shsurf->last_width = surface->width; - shsurf->last_height = surface->height; - weston_view_to_global_float(shsurf->view, 0, 0, &from_x, &from_y); weston_view_to_global_float(shsurf->view, sx, sy, &to_x, &to_y); x = shsurf->view->geometry.x + to_x - from_x; @@ -2554,6 +2655,9 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, weston_view_set_position(shsurf->view, x, y); } + shsurf->last_width = surface->width; + shsurf->last_height = surface->height; + /* XXX: would a fullscreen surface need the same handling? */ if (surface->output) { wl_list_for_each(view, &surface->views, surface_link) @@ -2561,6 +2665,19 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, } } +static void +get_maximized_size(struct shell_surface *shsurf, int32_t *width, int32_t *height) +{ + struct desktop_shell *shell; + pixman_rectangle32_t area; + + shell = shell_surface_get_shell(shsurf); + get_output_work_area(shell, shsurf->output, &area); + + *width = area.width; + *height = area.height; +} + static void set_fullscreen(struct shell_surface *shsurf, bool fullscreen, struct weston_output *output) @@ -2580,8 +2697,12 @@ set_fullscreen(struct shell_surface *shsurf, bool fullscreen, shell_surface_set_output(shsurf, output); shsurf->fullscreen_output = shsurf->output; - width = shsurf->output->width; - height = shsurf->output->height; + if (shsurf->output) { + width = shsurf->output->width; + height = shsurf->output->height; + } + } else if (weston_desktop_surface_get_maximized(desktop_surface)) { + get_maximized_size(shsurf, &width, &height); } weston_desktop_surface_set_fullscreen(desktop_surface, fullscreen); weston_desktop_surface_set_size(desktop_surface, width, height); @@ -2645,6 +2766,30 @@ desktop_surface_resize(struct weston_desktop_surface *desktop_surface, wl_resource_post_no_memory(resource); } +static void +desktop_surface_set_parent(struct weston_desktop_surface *desktop_surface, + struct weston_desktop_surface *parent, + void *shell) +{ + struct shell_surface *shsurf_parent; + struct shell_surface *shsurf = + weston_desktop_surface_get_user_data(desktop_surface); + + /* unlink any potential child */ + wl_list_remove(&shsurf->children_link); + + if (parent) { + shsurf_parent = weston_desktop_surface_get_user_data(parent); + if (shsurf_parent) + wl_list_insert(shsurf_parent->children_list.prev, + &shsurf->children_link); + else + wl_list_init(&shsurf->children_link); + } else { + wl_list_init(&shsurf->children_link); + } +} + static void desktop_surface_fullscreen_requested(struct weston_desktop_surface *desktop_surface, bool fullscreen, @@ -2666,8 +2811,6 @@ set_maximized(struct shell_surface *shsurf, bool maximized) if (maximized) { struct weston_output *output; - struct desktop_shell *shell; - pixman_rectangle32_t area; if (!weston_surface_is_mapped(surface)) output = get_focused_output(surface->compositor); @@ -2676,11 +2819,7 @@ set_maximized(struct shell_surface *shsurf, bool maximized) shell_surface_set_output(shsurf, output); - shell = shell_surface_get_shell(shsurf); - get_output_work_area(shell, shsurf->output, &area); - - width = area.width; - height = area.height; + get_maximized_size(shsurf, &width, &height); } weston_desktop_surface_set_maximized(desktop_surface, maximized); weston_desktop_surface_set_size(desktop_surface, width, height); @@ -2826,6 +2965,17 @@ desktop_surface_set_xwayland_position(struct weston_desktop_surface *surface, shsurf->xwayland.is_set = true; } +static void +desktop_surface_get_position(struct weston_desktop_surface *surface, + int32_t *x, int32_t *y, + void *shell_) +{ + struct shell_surface *shsurf = weston_desktop_surface_get_user_data(surface); + + *x = shsurf->view->geometry.x; + *y = shsurf->view->geometry.y; +} + static const struct weston_desktop_api shell_desktop_api = { .struct_size = sizeof(struct weston_desktop_api), .surface_added = desktop_surface_added, @@ -2833,25 +2983,27 @@ static const struct weston_desktop_api shell_desktop_api = { .committed = desktop_surface_committed, .move = desktop_surface_move, .resize = desktop_surface_resize, + .set_parent = desktop_surface_set_parent, .fullscreen_requested = desktop_surface_fullscreen_requested, .maximized_requested = desktop_surface_maximized_requested, .minimized_requested = desktop_surface_minimized_requested, .ping_timeout = desktop_surface_ping_timeout, .pong = desktop_surface_pong, .set_xwayland_position = desktop_surface_set_xwayland_position, + .get_position = desktop_surface_get_position, }; /* ************************ * * end of libweston-desktop * * ************************ */ -static void -shell_fade(struct desktop_shell *shell, enum fade_type type); - static void configure_static_view(struct weston_view *ev, struct weston_layer *layer, int x, int y) { struct weston_view *v, *next; + if (!ev->output) + return; + wl_list_for_each_safe(v, next, &layer->view_list.link, layer_link.link) { if (v->output == ev->output && v != ev) { weston_view_unmap(v); @@ -2889,7 +3041,7 @@ static int background_get_label(struct weston_surface *surface, char *buf, size_t len) { return snprintf(buf, len, "background for output %s", - surface->output->name); + (surface->output ? surface->output->name : "NULL")); } static void @@ -2910,6 +3062,7 @@ handle_background_surface_destroy(struct wl_listener *listener, void *data) container_of(listener, struct shell_output, background_surface_listener); weston_log("background surface gone\n"); + wl_list_remove(&output->background_surface_listener.link); output->background_surface = NULL; } @@ -2939,25 +3092,36 @@ desktop_shell_set_background(struct wl_client *client, surface->committed = background_committed; surface->committed_private = shell; weston_surface_set_label_func(surface, background_get_label); - surface->output = wl_resource_get_user_data(output_resource); - view->output = surface->output; - weston_desktop_shell_send_configure(resource, 0, - surface_resource, - surface->output->width, - surface->output->height); + surface->output = weston_head_from_resource(output_resource)->output; + weston_view_set_output(view, surface->output); sh_output = find_shell_output_from_weston_output(shell, surface->output); - sh_output->background_surface = surface; + if (sh_output->background_surface) { + /* The output already has a background, tell our helper + * there is no need for another one. */ + weston_desktop_shell_send_configure(resource, 0, + surface_resource, + 0, 0); + } else { + weston_desktop_shell_send_configure(resource, 0, + surface_resource, + surface->output->width, + surface->output->height); + + sh_output->background_surface = surface; - sh_output->background_surface_listener.notify = handle_background_surface_destroy; - wl_signal_add(&surface->destroy_signal, &sh_output->background_surface_listener); + sh_output->background_surface_listener.notify = + handle_background_surface_destroy; + wl_signal_add(&surface->destroy_signal, + &sh_output->background_surface_listener); + } } static int panel_get_label(struct weston_surface *surface, char *buf, size_t len) { return snprintf(buf, len, "panel for output %s", - surface->output->name); + (surface->output ? surface->output->name : "NULL")); } static void @@ -2994,6 +3158,7 @@ handle_panel_surface_destroy(struct wl_listener *listener, void *data) container_of(listener, struct shell_output, panel_surface_listener); weston_log("panel surface gone\n"); + wl_list_remove(&output->panel_surface_listener.link); output->panel_surface = NULL; } @@ -3024,18 +3189,27 @@ desktop_shell_set_panel(struct wl_client *client, surface->committed = panel_committed; surface->committed_private = shell; weston_surface_set_label_func(surface, panel_get_label); - surface->output = wl_resource_get_user_data(output_resource); - view->output = surface->output; - weston_desktop_shell_send_configure(resource, 0, - surface_resource, - surface->output->width, - surface->output->height); + surface->output = weston_head_from_resource(output_resource)->output; + weston_view_set_output(view, surface->output); sh_output = find_shell_output_from_weston_output(shell, surface->output); - sh_output->panel_surface = surface; + if (sh_output->panel_surface) { + /* The output already has a panel, tell our helper + * there is no need for another one. */ + weston_desktop_shell_send_configure(resource, 0, + surface_resource, + 0, 0); + } else { + weston_desktop_shell_send_configure(resource, 0, + surface_resource, + surface->output->width, + surface->output->height); + + sh_output->panel_surface = surface; - sh_output->panel_surface_listener.notify = handle_panel_surface_destroy; - wl_signal_add(&surface->destroy_signal, &sh_output->panel_surface_listener); + sh_output->panel_surface_listener.notify = handle_panel_surface_destroy; + wl_signal_add(&surface->destroy_signal, &sh_output->panel_surface_listener); + } } static int @@ -3189,7 +3363,7 @@ static const struct weston_desktop_shell_interface desktop_shell_implementation }; static void -move_binding(struct weston_pointer *pointer, uint32_t time, +move_binding(struct weston_pointer *pointer, const struct timespec *time, uint32_t button, void *data) { struct weston_surface *focus; @@ -3215,7 +3389,7 @@ move_binding(struct weston_pointer *pointer, uint32_t time, } static void -maximize_binding(struct weston_keyboard *keyboard, uint32_t time, +maximize_binding(struct weston_keyboard *keyboard, const struct timespec *time, uint32_t button, void *data) { struct weston_surface *focus = keyboard->focus; @@ -3234,8 +3408,8 @@ maximize_binding(struct weston_keyboard *keyboard, uint32_t time, } static void -fullscreen_binding(struct weston_keyboard *keyboard, uint32_t time, - uint32_t button, void *data) +fullscreen_binding(struct weston_keyboard *keyboard, + const struct timespec *time, uint32_t button, void *data) { struct weston_surface *focus = keyboard->focus; struct weston_surface *surface; @@ -3257,7 +3431,7 @@ fullscreen_binding(struct weston_keyboard *keyboard, uint32_t time, } static void -touch_move_binding(struct weston_touch *touch, uint32_t time, void *data) +touch_move_binding(struct weston_touch *touch, const struct timespec *time, void *data) { struct weston_surface *focus; struct weston_surface *surface; @@ -3281,7 +3455,7 @@ touch_move_binding(struct weston_touch *touch, uint32_t time, void *data) } static void -resize_binding(struct weston_pointer *pointer, uint32_t time, +resize_binding(struct weston_pointer *pointer, const struct timespec *time, uint32_t button, void *data) { struct weston_surface *focus; @@ -3328,7 +3502,8 @@ resize_binding(struct weston_pointer *pointer, uint32_t time, } static void -surface_opacity_binding(struct weston_pointer *pointer, uint32_t time, +surface_opacity_binding(struct weston_pointer *pointer, + const struct timespec *time, struct weston_pointer_axis_event *event, void *data) { @@ -3358,8 +3533,8 @@ surface_opacity_binding(struct weston_pointer *pointer, uint32_t time, } static void -do_zoom(struct weston_seat *seat, uint32_t time, uint32_t key, uint32_t axis, - double value) +do_zoom(struct weston_seat *seat, const struct timespec *time, uint32_t key, + uint32_t axis, double value) { struct weston_compositor *compositor = seat->compositor; struct weston_pointer *pointer = weston_seat_get_pointer(seat); @@ -3408,7 +3583,7 @@ do_zoom(struct weston_seat *seat, uint32_t time, uint32_t key, uint32_t axis, } static void -zoom_axis_binding(struct weston_pointer *pointer, uint32_t time, +zoom_axis_binding(struct weston_pointer *pointer, const struct timespec *time, struct weston_pointer_axis_event *event, void *data) { @@ -3416,31 +3591,31 @@ zoom_axis_binding(struct weston_pointer *pointer, uint32_t time, } static void -zoom_key_binding(struct weston_keyboard *keyboard, uint32_t time, +zoom_key_binding(struct weston_keyboard *keyboard, const struct timespec *time, uint32_t key, void *data) { do_zoom(keyboard->seat, time, key, 0, 0); } static void -terminate_binding(struct weston_keyboard *keyboard, uint32_t time, +terminate_binding(struct weston_keyboard *keyboard, const struct timespec *time, uint32_t key, void *data) { struct weston_compositor *compositor = data; - wl_display_terminate(compositor->wl_display); + weston_compositor_exit(compositor); } static void -rotate_grab_motion(struct weston_pointer_grab *grab, uint32_t time, +rotate_grab_motion(struct weston_pointer_grab *grab, + const struct timespec *time, struct weston_pointer_motion_event *event) { struct rotate_grab *rotate = container_of(grab, struct rotate_grab, base.grab); struct weston_pointer *pointer = grab->pointer; struct shell_surface *shsurf = rotate->base.shsurf; - struct weston_surface *surface = - weston_desktop_surface_get_surface(shsurf->desktop_surface); + struct weston_surface *surface; float cx, cy, dx, dy, cposx, cposy, dposx, dposy, r; weston_pointer_move(pointer, event); @@ -3448,6 +3623,8 @@ rotate_grab_motion(struct weston_pointer_grab *grab, uint32_t time, if (!shsurf) return; + surface = weston_desktop_surface_get_surface(shsurf->desktop_surface); + cx = 0.5f * surface->width; cy = 0.5f * surface->height; @@ -3500,7 +3677,8 @@ rotate_grab_motion(struct weston_pointer_grab *grab, uint32_t time, static void rotate_grab_button(struct weston_pointer_grab *grab, - uint32_t time, uint32_t button, uint32_t state_w) + const struct timespec *time, + uint32_t button, uint32_t state_w) { struct rotate_grab *rotate = container_of(grab, struct rotate_grab, base.grab); @@ -3578,8 +3756,8 @@ surface_rotate(struct shell_surface *shsurf, struct weston_pointer *pointer) } static void -rotate_binding(struct weston_pointer *pointer, uint32_t time, uint32_t button, - void *data) +rotate_binding(struct weston_pointer *pointer, const struct timespec *time, + uint32_t button, void *data) { struct weston_surface *focus; struct weston_surface *base_surface; @@ -3655,6 +3833,18 @@ lower_fullscreen_layer(struct desktop_shell *shell, } } +static struct shell_surface *get_last_child(struct shell_surface *shsurf) +{ + struct shell_surface *shsurf_child; + + wl_list_for_each_reverse(shsurf_child, &shsurf->children_list, children_link) { + if (weston_view_is_mapped(shsurf_child->view)) + return shsurf_child; + } + + return NULL; +} + void activate(struct desktop_shell *shell, struct weston_view *view, struct weston_seat *seat, uint32_t flags) @@ -3664,15 +3854,23 @@ activate(struct desktop_shell *shell, struct weston_view *view, struct focus_state *state; struct workspace *ws; struct weston_surface *old_es; - struct shell_surface *shsurf; + struct shell_surface *shsurf, *shsurf_child; main_surface = weston_surface_get_main_surface(es); shsurf = get_shell_surface(main_surface); assert(shsurf); + shsurf_child = get_last_child(shsurf); + if (shsurf_child) { + /* Activate last xdg child instead of parent. */ + activate(shell, shsurf_child->view, seat, flags); + return; + } + /* Only demote fullscreen surfaces on the output of activated shsurf. * Leave fullscreen surfaces on unrelated outputs alone. */ - lower_fullscreen_layer(shell, shsurf->output); + if (shsurf->output) + lower_fullscreen_layer(shell, shsurf->output); weston_view_activate(view, seat, flags); @@ -3739,7 +3937,8 @@ activate_binding(struct weston_seat *seat, } static void -click_to_activate_binding(struct weston_pointer *pointer, uint32_t time, +click_to_activate_binding(struct weston_pointer *pointer, + const struct timespec *time, uint32_t button, void *data) { if (pointer->grab != &pointer->default_grab) @@ -3753,7 +3952,8 @@ click_to_activate_binding(struct weston_pointer *pointer, uint32_t time, } static void -touch_to_activate_binding(struct weston_touch *touch, uint32_t time, +touch_to_activate_binding(struct weston_touch *touch, + const struct timespec *time, void *data) { if (touch->grab != &touch->default_grab) @@ -3843,16 +4043,16 @@ unlock(struct desktop_shell *shell) } static void -shell_fade_done(struct weston_view_animation *animation, void *data) +shell_fade_done_for_output(struct weston_view_animation *animation, void *data) { - struct desktop_shell *shell = data; - - shell->fade.animation = NULL; + struct shell_output *shell_output = data; + struct desktop_shell *shell = shell_output->shell; - switch (shell->fade.type) { + shell_output->fade.animation = NULL; + switch (shell_output->fade.type) { case FADE_IN: - weston_surface_destroy(shell->fade.view->surface); - shell->fade.view = NULL; + weston_surface_destroy(shell_output->fade.view->surface); + shell_output->fade.view = NULL; break; case FADE_OUT: lock(shell); @@ -3863,7 +4063,7 @@ shell_fade_done(struct weston_view_animation *animation, void *data) } static struct weston_view * -shell_fade_create_surface(struct desktop_shell *shell) +shell_fade_create_surface_for_output(struct desktop_shell *shell, struct shell_output *shell_output) { struct weston_compositor *compositor = shell->compositor; struct weston_surface *surface; @@ -3879,8 +4079,8 @@ shell_fade_create_surface(struct desktop_shell *shell) return NULL; } - weston_surface_set_size(surface, 8192, 8192); - weston_view_set_position(view, 0, 0); + weston_surface_set_size(surface, shell_output->output->width, shell_output->output->height); + weston_view_set_position(view, shell_output->output->x, shell_output->output->y); weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1.0); weston_layer_entry_insert(&compositor->fade_layer.view_list, &view->layer_link); @@ -3895,6 +4095,7 @@ static void shell_fade(struct desktop_shell *shell, enum fade_type type) { float tint; + struct shell_output *shell_output; switch (type) { case FADE_IN: @@ -3908,32 +4109,35 @@ shell_fade(struct desktop_shell *shell, enum fade_type type) return; } - shell->fade.type = type; + /* Create a separate fade surface for each output */ + wl_list_for_each(shell_output, &shell->output_list, link) { + shell_output->fade.type = type; - if (shell->fade.view == NULL) { - shell->fade.view = shell_fade_create_surface(shell); - if (!shell->fade.view) - return; + if (shell_output->fade.view == NULL) { + shell_output->fade.view = shell_fade_create_surface_for_output(shell, shell_output); + if (!shell_output->fade.view) + continue; - shell->fade.view->alpha = 1.0 - tint; - weston_view_update_transform(shell->fade.view); - } + shell_output->fade.view->alpha = 1.0 - tint; + weston_view_update_transform(shell_output->fade.view); + } - if (shell->fade.view->output == NULL) { - /* If the black view gets a NULL output, we lost the - * last output and we'll just cancel the fade. This - * happens when you close the last window under the - * X11 or Wayland backends. */ - shell->locked = false; - weston_surface_destroy(shell->fade.view->surface); - shell->fade.view = NULL; - } else if (shell->fade.animation) { - weston_fade_update(shell->fade.animation, tint); - } else { - shell->fade.animation = - weston_fade_run(shell->fade.view, - 1.0 - tint, tint, 300.0, - shell_fade_done, shell); + if (shell_output->fade.view->output == NULL) { + /* If the black view gets a NULL output, we lost the + * last output and we'll just cancel the fade. This + * happens when you close the last window under the + * X11 or Wayland backends. */ + shell->locked = false; + weston_surface_destroy(shell_output->fade.view->surface); + shell_output->fade.view = NULL; + } else if (shell_output->fade.animation) { + weston_fade_update(shell_output->fade.animation, tint); + } else { + shell_output->fade.animation = + weston_fade_run(shell_output->fade.view, + 1.0 - tint, tint, 300.0, + shell_fade_done_for_output, shell_output); + } } } @@ -3941,6 +4145,7 @@ static void do_shell_fade_startup(void *data) { struct desktop_shell *shell = data; + struct shell_output *shell_output; if (shell->startup_animation_type == ANIMATION_FADE) { shell_fade(shell, FADE_IN); @@ -3948,8 +4153,10 @@ do_shell_fade_startup(void *data) weston_log("desktop shell: " "unexpected fade-in animation type %d\n", shell->startup_animation_type); - weston_surface_destroy(shell->fade.view->surface); - shell->fade.view = NULL; + wl_list_for_each(shell_output, &shell->output_list, link) { + weston_surface_destroy(shell_output->fade.view->surface); + shell_output->fade.view = NULL; + } } } @@ -3957,15 +4164,22 @@ static void shell_fade_startup(struct desktop_shell *shell) { struct wl_event_loop *loop; + struct shell_output *shell_output; + bool has_fade = false; - if (!shell->fade.startup_timer) - return; + wl_list_for_each(shell_output, &shell->output_list, link) { + if (!shell_output->fade.startup_timer) + continue; - wl_event_source_remove(shell->fade.startup_timer); - shell->fade.startup_timer = NULL; + wl_event_source_remove(shell_output->fade.startup_timer); + shell_output->fade.startup_timer = NULL; + has_fade = true; + } - loop = wl_display_get_event_loop(shell->compositor->wl_display); - wl_event_loop_add_idle(loop, do_shell_fade_startup, shell); + if (has_fade) { + loop = wl_display_get_event_loop(shell->compositor->wl_display); + wl_event_loop_add_idle(loop, do_shell_fade_startup, shell); + } } static int @@ -3986,27 +4200,30 @@ shell_fade_init(struct desktop_shell *shell) */ struct wl_event_loop *loop; - - if (shell->fade.view != NULL) { - weston_log("%s: warning: fade surface already exists\n", - __func__); - return; - } + struct shell_output *shell_output; if (shell->startup_animation_type == ANIMATION_NONE) return; - shell->fade.view = shell_fade_create_surface(shell); - if (!shell->fade.view) - return; + wl_list_for_each(shell_output, &shell->output_list, link) { + if (shell_output->fade.view != NULL) { + weston_log("%s: warning: fade surface already exists\n", + __func__); + continue; + } + + shell_output->fade.view = shell_fade_create_surface_for_output(shell, shell_output); + if (!shell_output->fade.view) + continue; - weston_view_update_transform(shell->fade.view); - weston_surface_damage(shell->fade.view->surface); + weston_view_update_transform(shell_output->fade.view); + weston_surface_damage(shell_output->fade.view->surface); - loop = wl_display_get_event_loop(shell->compositor->wl_display); - shell->fade.startup_timer = - wl_event_loop_add_timer(loop, fade_startup_timeout, shell); - wl_event_source_timer_update(shell->fade.startup_timer, 15000); + loop = wl_display_get_event_loop(shell->compositor->wl_display); + shell_output->fade.startup_timer = + wl_event_loop_add_timer(loop, fade_startup_timeout, shell); + wl_event_source_timer_update(shell_output->fade.startup_timer, 15000); + } } static void @@ -4021,7 +4238,7 @@ idle_handler(struct wl_listener *listener, void *data) weston_seat_break_desktop_grabs(seat); shell_fade(shell, FADE_OUT); - /* lock() is called from shell_fade_done() */ + /* lock() is called from shell_fade_done_for_output() */ } static void @@ -4068,6 +4285,11 @@ center_on_output(struct weston_view *view, struct weston_output *output) int32_t surf_x, surf_y, width, height; float x, y; + if (!output) { + weston_view_set_position(view, 0, 0); + return; + } + surface_subsurfaces_boundingbox(view->surface, &surf_x, &surf_y, &width, &height); x = output->x + (output->width - width) / 2 - surf_x / 2; @@ -4154,7 +4376,8 @@ check_desktop_shell_crash_too_early(struct desktop_shell *shell) weston_log("Error: %s apparently cannot run at all.\n", shell->client); weston_log_continue(STAMP_SPACE "Quitting..."); - wl_display_terminate(shell->compositor->wl_display); + weston_compositor_exit_with_code(shell->compositor, + EXIT_FAILURE); return true; } @@ -4167,11 +4390,11 @@ static void launch_desktop_shell_process(void *data); static void respawn_desktop_shell_process(struct desktop_shell *shell) { - uint32_t time; + struct timespec time; /* if desktop-shell dies more than 5 times in 30 seconds, give up */ - time = weston_compositor_get_time(); - if (time - shell->child.deathstamp > 30000) { + weston_compositor_get_time(&time); + if (timespec_sub_to_msec(&time, &shell->child.deathstamp) > 30000) { shell->child.deathstamp = time; shell->child.deathcount = 0; } @@ -4379,7 +4602,7 @@ switcher_destroy(struct switcher *switcher) static void switcher_key(struct weston_keyboard_grab *grab, - uint32_t time, uint32_t key, uint32_t state_w) + const struct timespec *time, uint32_t key, uint32_t state_w) { struct switcher *switcher = container_of(grab, struct switcher, grab); enum wl_keyboard_key_state state = state_w; @@ -4415,13 +4638,16 @@ static const struct weston_keyboard_grab_interface switcher_grab = { }; static void -switcher_binding(struct weston_keyboard *keyboard, uint32_t time, +switcher_binding(struct weston_keyboard *keyboard, const struct timespec *time, uint32_t key, void *data) { struct desktop_shell *shell = data; struct switcher *switcher; switcher = malloc(sizeof *switcher); + if (!switcher) + return; + switcher->shell = shell; switcher->current = NULL; switcher->listener.notify = switcher_handle_view_destroy; @@ -4436,7 +4662,7 @@ switcher_binding(struct weston_keyboard *keyboard, uint32_t time, } static void -backlight_binding(struct weston_keyboard *keyboard, uint32_t time, +backlight_binding(struct weston_keyboard *keyboard, const struct timespec *time, uint32_t key, void *data) { struct weston_compositor *compositor = data; @@ -4469,8 +4695,8 @@ backlight_binding(struct weston_keyboard *keyboard, uint32_t time, } static void -force_kill_binding(struct weston_keyboard *keyboard, uint32_t time, - uint32_t key, void *data) +force_kill_binding(struct weston_keyboard *keyboard, + const struct timespec *time, uint32_t key, void *data) { struct weston_surface *focus_surface; struct wl_client *client; @@ -4496,8 +4722,8 @@ force_kill_binding(struct weston_keyboard *keyboard, uint32_t time, } static void -workspace_up_binding(struct weston_keyboard *keyboard, uint32_t time, - uint32_t key, void *data) +workspace_up_binding(struct weston_keyboard *keyboard, + const struct timespec *time, uint32_t key, void *data) { struct desktop_shell *shell = data; unsigned int new_index = shell->workspaces.current; @@ -4511,8 +4737,8 @@ workspace_up_binding(struct weston_keyboard *keyboard, uint32_t time, } static void -workspace_down_binding(struct weston_keyboard *keyboard, uint32_t time, - uint32_t key, void *data) +workspace_down_binding(struct weston_keyboard *keyboard, + const struct timespec *time, uint32_t key, void *data) { struct desktop_shell *shell = data; unsigned int new_index = shell->workspaces.current; @@ -4526,8 +4752,8 @@ workspace_down_binding(struct weston_keyboard *keyboard, uint32_t time, } static void -workspace_f_binding(struct weston_keyboard *keyboard, uint32_t time, - uint32_t key, void *data) +workspace_f_binding(struct weston_keyboard *keyboard, + const struct timespec *time, uint32_t key, void *data) { struct desktop_shell *shell = data; unsigned int new_index; @@ -4543,7 +4769,8 @@ workspace_f_binding(struct weston_keyboard *keyboard, uint32_t time, static void workspace_move_surface_up_binding(struct weston_keyboard *keyboard, - uint32_t time, uint32_t key, void *data) + const struct timespec *time, uint32_t key, + void *data) { struct desktop_shell *shell = data; unsigned int new_index = shell->workspaces.current; @@ -4559,7 +4786,8 @@ workspace_move_surface_up_binding(struct weston_keyboard *keyboard, static void workspace_move_surface_down_binding(struct weston_keyboard *keyboard, - uint32_t time, uint32_t key, void *data) + const struct timespec *time, uint32_t key, + void *data) { struct desktop_shell *shell = data; unsigned int new_index = shell->workspaces.current; @@ -4574,7 +4802,7 @@ workspace_move_surface_down_binding(struct weston_keyboard *keyboard, } static void -shell_reposition_view_on_output_destroy(struct weston_view *view) +shell_reposition_view_on_output_change(struct weston_view *view) { struct weston_output *output, *first_output; struct weston_compositor *ec = view->surface->compositor; @@ -4582,6 +4810,9 @@ shell_reposition_view_on_output_destroy(struct weston_view *view) float x, y; int visible; + if (wl_list_empty(&ec->output_list)) + return; + x = view->geometry.x; y = view->geometry.y; @@ -4636,19 +4867,15 @@ shell_for_each_layer(struct desktop_shell *shell, } static void -shell_output_destroy_move_layer(struct desktop_shell *shell, +shell_output_changed_move_layer(struct desktop_shell *shell, struct weston_layer *layer, void *data) { - struct weston_output *output = data; struct weston_view *view; - wl_list_for_each(view, &layer->view_list.link, layer_link.link) { - if (view->output != output) - continue; + wl_list_for_each(view, &layer->view_list.link, layer_link.link) + shell_reposition_view_on_output_change(view); - shell_reposition_view_on_output_destroy(view); - } } static void @@ -4656,13 +4883,14 @@ handle_output_destroy(struct wl_listener *listener, void *data) { struct shell_output *output_listener = container_of(listener, struct shell_output, destroy_listener); - struct weston_output *output = output_listener->output; struct desktop_shell *shell = output_listener->shell; - shell_for_each_layer(shell, shell_output_destroy_move_layer, output); + shell_for_each_layer(shell, shell_output_changed_move_layer, NULL); - wl_list_remove(&output_listener->panel_surface_listener.link); - wl_list_remove(&output_listener->background_surface_listener.link); + if (output_listener->panel_surface) + wl_list_remove(&output_listener->panel_surface_listener.link); + if (output_listener->background_surface) + wl_list_remove(&output_listener->background_surface_listener.link); wl_list_remove(&output_listener->destroy_listener.link); wl_list_remove(&output_listener->link); free(output_listener); @@ -4708,10 +4936,13 @@ create_shell_output(struct desktop_shell *shell, shell_output->output = output; shell_output->shell = shell; shell_output->destroy_listener.notify = handle_output_destroy; - wl_list_init(&shell_output->panel_surface_listener.link); wl_signal_add(&output->destroy_signal, &shell_output->destroy_listener); wl_list_insert(shell->output_list.prev, &shell_output->link); + + if (wl_list_length(&shell->output_list) == 1) + shell_for_each_layer(shell, + shell_output_changed_move_layer, NULL); } static void @@ -4788,6 +5019,7 @@ shell_destroy(struct wl_listener *listener, void *data) wl_client_destroy(shell->child.client); } + wl_list_remove(&shell->destroy_listener.link); wl_list_remove(&shell->idle_listener.link); wl_list_remove(&shell->wake_listener.link); wl_list_remove(&shell->transform_listener.link); @@ -4834,12 +5066,6 @@ shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell) weston_compositor_add_touch_binding(ec, 0, touch_to_activate_binding, shell); - weston_compositor_add_axis_binding(ec, WL_POINTER_AXIS_VERTICAL_SCROLL, - MODIFIER_SUPER | MODIFIER_ALT, - surface_opacity_binding, NULL); - weston_compositor_add_axis_binding(ec, WL_POINTER_AXIS_VERTICAL_SCROLL, - MODIFIER_SUPER, zoom_axis_binding, - NULL); weston_compositor_add_key_binding(ec, KEY_BRIGHTNESSDOWN, 0, backlight_binding, ec); weston_compositor_add_key_binding(ec, KEY_BRIGHTNESSUP, 0, @@ -4854,6 +5080,16 @@ shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell) if (!mod) return; + /* This binding is not configurable, but is only enabled if there is a + * valid binding modifier. */ + weston_compositor_add_axis_binding(ec, WL_POINTER_AXIS_VERTICAL_SCROLL, + MODIFIER_SUPER | MODIFIER_ALT, + surface_opacity_binding, NULL); + + weston_compositor_add_axis_binding(ec, WL_POINTER_AXIS_VERTICAL_SCROLL, + mod, zoom_axis_binding, + NULL); + weston_compositor_add_key_binding(ec, KEY_PAGEUP, mod, zoom_key_binding, NULL); weston_compositor_add_key_binding(ec, KEY_PAGEDOWN, mod, @@ -4932,8 +5168,13 @@ wet_shell_init(struct weston_compositor *ec, shell->compositor = ec; - shell->destroy_listener.notify = shell_destroy; - wl_signal_add(&ec->destroy_signal, &shell->destroy_listener); + if (!weston_compositor_add_destroy_listener_once(ec, + &shell->destroy_listener, + shell_destroy)) { + free(shell); + return 0; + } + shell->idle_listener.notify = idle_handler; wl_signal_add(&ec->idle_signal, &shell->idle_listener); shell->wake_listener.notify = wake_handler; @@ -4995,7 +5236,7 @@ wet_shell_init(struct weston_compositor *ec, shell, bind_desktop_shell) == NULL) return -1; - shell->child.deathstamp = weston_compositor_get_time(); + weston_compositor_get_time(&shell->child.deathstamp); shell->panel_position = WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP; diff --git a/desktop-shell/shell.h b/desktop-shell/shell.h index a1cea7505..9f0b6abe9 100644 --- a/desktop-shell/shell.h +++ b/desktop-shell/shell.h @@ -27,8 +27,8 @@ #include #include -#include "compositor.h" -#include "xwayland/xwayland-api.h" +#include +#include #include "weston-desktop-shell-server-protocol.h" @@ -62,9 +62,6 @@ struct exposay_output { int num_surfaces; int grid_size; int surface_size; - - int hpadding_outer; - int vpadding_outer; int padding_inner; }; @@ -122,6 +119,13 @@ struct shell_output { struct weston_surface *background_surface; 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; @@ -154,7 +158,7 @@ struct desktop_shell { struct wl_listener client_destroy_listener; unsigned deathcount; - uint32_t deathstamp; + struct timespec deathstamp; } child; bool locked; @@ -181,7 +185,7 @@ struct desktop_shell { struct weston_animation animation; struct wl_list anim_sticky_list; int anim_dir; - uint32_t anim_timestamp; + struct timespec anim_timestamp; double anim_current; struct workspace *anim_from; struct workspace *anim_to; @@ -192,13 +196,6 @@ struct desktop_shell { struct wl_list surfaces; } input_panel; - struct { - struct weston_view *view; - struct weston_view_animation *animation; - enum fade_type type; - struct wl_event_source *startup_timer; - } fade; - struct exposay exposay; bool allow_zap; @@ -235,6 +232,11 @@ 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 weston_output *output, + pixman_rectangle32_t *area); + void lower_fullscreen_layer(struct desktop_shell *shell, struct weston_output *lowering_output); diff --git a/doc/scripts/calibration-helper.bash b/doc/scripts/calibration-helper.bash new file mode 100755 index 000000000..72effe3a8 --- /dev/null +++ b/doc/scripts/calibration-helper.bash @@ -0,0 +1,66 @@ +#!/bin/bash + +# Copyright 2018 Collabora, Ltd. +# Copyright 2018 General Electric Company +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice (including the +# next paragraph) shall be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# This is an example script working as Weston's calibration helper. +# Its purpose is to permanently store the calibration matrix for the given +# touchscreen input device into a udev property. Since this script naturally +# runs as the user that runs Weston, it presumably cannot write directly into +# /etc. It is left for the administrator to set up appropriate files and +# permissions. + +# To use this script, one needs to edit weston.ini, in section [libinput], add: +# calibration_helper=/path/to/bin/calibration-helper.bash + +# exit immediately if any command fails +set -e + +# The arguments Weston gives us: +SYSPATH="$1" +MATRIX="$2 $3 $4 $5 $6 $7" + +# Pick something to recognize the right touch device with. +# Usually one would use something like a serial. +SERIAL=$(udevadm info "$SYSPATH" --query=property | \ + awk -- 'BEGIN { FS="=" } { if ($1 == "ID_SERIAL") { print $2; exit } }') + +# If cannot find a serial, tell the server to not use the new calibration. +[ -z "$SERIAL" ] && exit 1 + +# You'd have this write a file instead. +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 +# This lets Weston get the new calibration if you unplug and replug the input +# device. Instead of writing a udev rule directly, you could have a udev rule +# with IMPORT{file}="/path/to/calibration", write +# "LIBINPUT_CALIBRATION_MATRIX=\"$MATRIX\"" into /path/to/calibration instead, +# and skip this reload step. + +# Make udev process the new rule by triggering a "change" event: +#udevadm trigger "$SYSPATH" +# If you were to restart Weston without rebooting, this lets it pick up the new +# calibration. diff --git a/doc/scripts/gdb/flight_rec.py b/doc/scripts/gdb/flight_rec.py new file mode 100755 index 000000000..9872f17fd --- /dev/null +++ b/doc/scripts/gdb/flight_rec.py @@ -0,0 +1,103 @@ +# +# 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, " + "Overlaped: {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() diff --git a/doc/scripts/remoting-client-receive.bash b/doc/scripts/remoting-client-receive.bash new file mode 100755 index 000000000..b518689a6 --- /dev/null +++ b/doc/scripts/remoting-client-receive.bash @@ -0,0 +1,38 @@ +#!/bin/bash + +# Copyright © 2018 Renesas Electronics Corp. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice (including the +# next paragraph) shall be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Authors: IGEL Co., Ltd. + +# By using this script, client can receive remoted output via gstreamer. +# Usage: +# remoting-client-receive.bash + +gst-launch-1.0 rtpbin name=rtpbin \ + udpsrc caps="application/x-rtp,media=(string)video,clock-rate=(int)90000,encoding-name=JPEG,payload=26" port=$1 ! \ + rtpbin.recv_rtp_sink_0 \ + rtpbin. ! rtpjpegdepay ! jpegdec ! autovideosink \ + udpsrc port=$(($1 + 1)) ! rtpbin.recv_rtcp_sink_0 \ + rtpbin.send_rtcp_src_0 ! \ + udpsink port=$(($1 + 2)) sync=false async=false diff --git a/doc/sphinx/conf.py.in b/doc/sphinx/conf.py.in new file mode 100644 index 000000000..ab8f9bf11 --- /dev/null +++ b/doc/sphinx/conf.py.in @@ -0,0 +1,204 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +import sphinx + +sys.path.append(os.path.abspath('sphinxext')) + +# -- Project information ----------------------------------------------------- + +project = u'weston' +copyright = u'2019, Weston community' +author = u'Weston community ' + + +# The short X.Y version +version = u'' +# The full version, including alpha/beta/rc tags +release = u'@VERSION@' + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +needs_sphinx = '2.1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.intersphinx', + 'sphinx.ext.autosectionlabel', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.mathjax', + 'sphinx.ext.ifconfig', + 'sphinx.ext.viewcode', + 'breathe', +] + +breathe_projects = { "weston": "@BUILD_ROOT@/xml/" } +breathe_default_members = ('members', 'undoc-members') +breathe_default_project = "weston" + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +source_suffix = ['.rst' ] + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + +# The name of the Pygments (syntax highlighting) style to use. +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 +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = 'weston' + + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'weston.tex', u'Weston Documentation', + u'Weston community', 'manual'), +] + + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'weston', u'Weston Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'weston', u'Wweston Documentation', + author, 'Weston community', 'Weston Documentation' + 'Miscellaneous'), +] + + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + + +# -- Extension configuration ------------------------------------------------- + +# -- Options for intersphinx extension --------------------------------------- + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'https://docs.python.org/3': None} + +# -- Options for todo extension ---------------------------------------------- + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True diff --git a/doc/sphinx/doxygen.ini.in b/doc/sphinx/doxygen.ini.in new file mode 100644 index 000000000..cfeba0905 --- /dev/null +++ b/doc/sphinx/doxygen.ini.in @@ -0,0 +1,2480 @@ +# Doxyfile 1.8.13 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "libweston" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = @OUTPUT_DIR@ + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = "rst=\verbatim embed:rst" +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 +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +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 +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# 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@/tests + +# 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 +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: 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 +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = NO + +# The HTML_OUTPUT tag is used to specify where the HTML 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: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# 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). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +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. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +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 +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /