first commit
|
|
@ -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
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
*.swp
|
||||
.*.sw?
|
||||
.sw?
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
*~
|
||||
ctags
|
||||
cscope.out
|
||||
TAGS
|
||||
|
||||
00*.patch
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
Copyright © 2008-2012 Kristian Høgsberg
|
||||
Copyright © 2010-2012 Intel Corporation
|
||||
Copyright © 2010-2011 Benjamin Franzke
|
||||
Copyright © 2011-2012 Collabora, Ltd.
|
||||
Copyright © 2010 Red Hat <mjg@redhat.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
The above is the version of the MIT "Expat" License used by X.org:
|
||||
|
||||
http://cgit.freedesktop.org/xorg/xserver/tree/COPYING
|
||||
|
|
@ -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.
|
||||
|
|
@ -0,0 +1,360 @@
|
|||
Weston
|
||||
======
|
||||
|
||||

|
||||
|
||||
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
|
||||
correctly implementing core Wayland protocols and interfacing with
|
||||
input and output systems, so that people who just want to write a new
|
||||
"Wayland window manager" (WM) or a small desktop environment (DE) can
|
||||
focus on the WM part.
|
||||
|
||||
Libweston was first introduced in Weston 1.12, and is expected to
|
||||
continue evolving through many Weston releases before it achieves a
|
||||
stable API and feature completeness.
|
||||
|
||||
Libweston's primary purpose is exporting an API for creating Wayland
|
||||
compositors. Libweston's secondary purpose is to export the weston_config API
|
||||
so that third party plugins and helper programs can read `weston.ini` if they
|
||||
want to. However, these two scopes are orthogonal and independent. At no point
|
||||
will the compositor functionality use or depend on the weston_config
|
||||
functionality.
|
||||
|
||||
|
||||
API/ABI (in)stability and parallel installability
|
||||
-------------------------------------------------
|
||||
|
||||
As libweston's API surface is huge, it is impossible to get it right
|
||||
in one go. Therefore developers reserve the right to break the API/ABI and bump
|
||||
the major version to signify that. For git snapshots of the master branch, the
|
||||
API/ABI can break any time without warning.
|
||||
|
||||
Libweston major can be bumped only once during a development cycle. This should
|
||||
happen on the first patch that breaks the API or ABI. Further breaks before the
|
||||
next Weston major.0.0 release do not cause a bump. This means that libweston
|
||||
API and ABI are allowed to break also after an alpha release, up to the final
|
||||
release. However, breaks after alpha should be judged by the usual practices
|
||||
for allowing minor features, fixes only, or critical fixes only.
|
||||
|
||||
To make things tolerable for libweston users despite API/ABI breakages,
|
||||
different libweston major versions are designed to be perfectly
|
||||
parallel-installable. This way external projects can easily depend on a
|
||||
particular API/ABI-version. Thus they do not have to fight over which
|
||||
ABI-version is installed in a user's system. This allows a user to install many
|
||||
different compositors each requiring a different libweston ABI-version without
|
||||
tricks or conflicts.
|
||||
|
||||
Note, that versions of Weston itself will not be parallel-installable,
|
||||
only libweston is.
|
||||
|
||||
For more information about parallel installability, see
|
||||
http://ometer.com/parallel.html
|
||||
|
||||
|
||||
Versioning scheme
|
||||
-----------------
|
||||
|
||||
In order to provide consistent, easy to use versioning, libweston
|
||||
follows the rules in the Apache Portable Runtime Project
|
||||
http://apr.apache.org/versioning.html.
|
||||
|
||||
The document provides the full details, with the gist summed below:
|
||||
- Major - backward incompatible changes.
|
||||
- Minor - new backward compatible features.
|
||||
- Patch - internal (implementation specific) fixes.
|
||||
|
||||
Weston and libweston have separate version numbers in meson.build. All
|
||||
releases are made by the Weston version number. Libweston version number
|
||||
matches the Weston version number in all releases except maybe pre-releases.
|
||||
Pre-releases have the Weston micro version 91 or greater.
|
||||
|
||||
A pre-release is allowed to install a libweston version greater than the Weston
|
||||
version in case libweston major was bumped. In that case, the libweston version
|
||||
must be Weston major + 1.
|
||||
|
||||
Pkg-config files are named after libweston major, but carry the Weston version
|
||||
number. This means that Weston pre-release 2.1.91 may install libweston-3.pc
|
||||
for the future libweston 3.0.0, but the .pc file says the version is still
|
||||
2.1.91. When a libweston user wants to depend on the fully stable API and ABI
|
||||
of a libweston major, he should use (e.g. for major 3):
|
||||
|
||||
PKG_CHECK_MODULES(LIBWESTON, [libweston-3 >= 3.0.0])
|
||||
|
||||
Depending only on libweston-3 without a specific version number still allows
|
||||
pre-releases which might have different API or ABI.
|
||||
|
||||
|
||||
Forward compatibility
|
||||
---------------------
|
||||
|
||||
Inspired by ATK, Qt and KDE programs/libraries, libjpeg-turbo, GDK,
|
||||
NetworkManager, js17, lz4 and many others, libweston uses a macro to restrict
|
||||
the API visible to the developer - REQUIRE_LIBWESTON_API_VERSION.
|
||||
|
||||
Note that different projects focus on different aspects - upper and/or lower
|
||||
version check, default to visible/hidden old/new symbols and so on.
|
||||
|
||||
libweston aims to guard all newly introduced API, in order to prevent subtle
|
||||
breaks that a simple recompile (against a newer version) might cause.
|
||||
|
||||
The macro is of the format 0x$MAJOR$MINOR and does not include PATCH version.
|
||||
As mentioned in the Versioning scheme section, the latter does not reflect any
|
||||
user visible API changes, thus should be not considered part of the API version.
|
||||
|
||||
All new symbols should be guarded by the macro like the example given below:
|
||||
|
||||
~~~~
|
||||
#if REQUIRE_LIBWESTON_API_VERSION >= 0x0101
|
||||
|
||||
bool
|
||||
weston_ham_sandwich(void);
|
||||
|
||||
#endif
|
||||
~~~~
|
||||
|
||||
In order to use the said symbol, the one will have a similar code in their
|
||||
configure.ac:
|
||||
|
||||
~~~~
|
||||
PKG_CHECK_MODULES(LIBWESTON, [libweston-1 >= 1.1])
|
||||
AC_DEFINE(REQUIRE_LIBWESTON_API_VERSION, [0x0101])
|
||||
~~~~
|
||||
|
||||
If the user is _not_ interested in forward compatibility, they can use 0xffff
|
||||
or similar high value. Yet doing so is not recommended.
|
||||
|
||||
|
||||
Libweston design goals
|
||||
----------------------
|
||||
|
||||
The high-level goal of libweston is to decouple the compositor from
|
||||
the shell implementation (what used to be shell plugins).
|
||||
|
||||
Thus, instead of launching 'weston' with various arguments to choose the
|
||||
shell, one would launch the shell itself, e.g. 'weston-desktop',
|
||||
'weston-ivi', 'orbital', etc. The main executable (the hosting program)
|
||||
will implement the shell, while libweston will be used for a fundamental
|
||||
compositor implementation.
|
||||
|
||||
Libweston is also intended for use by other project developers who want
|
||||
to create new "Wayland WMs".
|
||||
|
||||
Details:
|
||||
|
||||
- All configuration and user interfaces will be outside of libweston.
|
||||
This includes command line parsing, configuration files, and runtime
|
||||
(graphical) UI.
|
||||
|
||||
- The hosting program (main executable) will be in full control of all
|
||||
libweston options. Libweston should not have user settable options
|
||||
that would work behind the hosting program's back, except perhaps
|
||||
debugging features and such.
|
||||
|
||||
- Signal handling will be outside of libweston.
|
||||
|
||||
- Child process execution and management will be outside of libweston.
|
||||
|
||||
- The different backends (drm, fbdev, x11, etc) will be an internal
|
||||
detail of libweston. Libweston will not support third party
|
||||
backends. However, hosting programs need to handle
|
||||
backend-specific configuration due to differences in behaviour and
|
||||
available features.
|
||||
|
||||
- Renderers will be libweston internal details too, though again the
|
||||
hosting program may affect the choice of renderer if the backend
|
||||
allows, and maybe set renderer-specific options.
|
||||
|
||||
- plugin design ???
|
||||
|
||||
- xwayland ???
|
||||
|
||||
- weston-launch is still with libweston even though it can only launch
|
||||
Weston and nothing else. We would like to allow it to launch any compositor,
|
||||
but since it gives by design root access to input devices and DRM, how can
|
||||
we restrict it to intended programs?
|
||||
|
||||
There are still many more details to be decided.
|
||||
|
||||
|
||||
For packagers
|
||||
-------------
|
||||
|
||||
Always build Weston with --with-cairo=image.
|
||||
|
||||
The Weston project is (will be) intended to be split into several
|
||||
binary packages, each with its own dependencies. The maximal split
|
||||
would be roughly like this:
|
||||
|
||||
- libweston (minimal dependencies):
|
||||
+ headless backend
|
||||
+ wayland backend
|
||||
|
||||
- gl-renderer (depends on GL libs etc.)
|
||||
|
||||
- drm-backend (depends on libdrm, libgbm, libudev, libinput, ...)
|
||||
|
||||
- x11-backend (depends of X11/xcb libs)
|
||||
|
||||
- xwayland (depends on X11/xcb libs)
|
||||
|
||||
- fbdev-backend (depends on libudev...)
|
||||
|
||||
- rdp-backend (depends on freerdp)
|
||||
|
||||
- weston (the executable, not parallel-installable):
|
||||
+ desktop shell
|
||||
+ ivi-shell
|
||||
+ fullscreen shell
|
||||
+ weston-info (deprecated), weston-terminal, etc. we install by default
|
||||
+ screen-share
|
||||
|
||||
- weston demos (not parallel-installable)
|
||||
+ weston-simple-* programs
|
||||
+ possibly all the programs we build but do not install by
|
||||
default
|
||||
|
||||
- and possibly more...
|
||||
|
||||
Everything should be parallel-installable across libweston major
|
||||
ABI-versions (libweston-1.so, libweston-2.so, etc.), except those
|
||||
explicitly mentioned.
|
||||
|
||||
Weston's build may not sanely allow this yet, but this is the
|
||||
intention.
|
||||
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* Copyright © 2012 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cairo.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "window.h"
|
||||
#include "shared/helpers.h"
|
||||
#include <libweston/matrix.h>
|
||||
|
||||
/* Our points for the calibration must be not be on a line */
|
||||
static const struct {
|
||||
float x_ratio, y_ratio;
|
||||
} test_ratios[] = {
|
||||
{ 0.20, 0.40 },
|
||||
{ 0.80, 0.60 },
|
||||
{ 0.40, 0.80 }
|
||||
};
|
||||
|
||||
struct calibrator {
|
||||
struct tests {
|
||||
int32_t drawn_x, drawn_y;
|
||||
int32_t clicked_x, clicked_y;
|
||||
} tests[ARRAY_LENGTH(test_ratios)];
|
||||
int current_test;
|
||||
|
||||
struct display *display;
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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 void
|
||||
finish_calibration (struct calibrator *calibrator)
|
||||
{
|
||||
struct weston_matrix m;
|
||||
struct weston_matrix inverse;
|
||||
struct weston_vector x_calib, y_calib;
|
||||
int i;
|
||||
|
||||
|
||||
/*
|
||||
* x1 y1 1 0
|
||||
* x2 y2 1 0
|
||||
* x3 y3 1 0
|
||||
* 0 0 0 1
|
||||
*/
|
||||
memset(&m, 0, sizeof(m));
|
||||
for (i = 0; i < (int)ARRAY_LENGTH(test_ratios); i++) {
|
||||
m.d[i] = calibrator->tests[i].clicked_x;
|
||||
m.d[i + 4] = calibrator->tests[i].clicked_y;
|
||||
m.d[i + 8] = 1;
|
||||
}
|
||||
m.d[15] = 1;
|
||||
|
||||
weston_matrix_invert(&inverse, &m);
|
||||
|
||||
memset(&x_calib, 0, sizeof(x_calib));
|
||||
memset(&y_calib, 0, sizeof(y_calib));
|
||||
|
||||
for (i = 0; i < (int)ARRAY_LENGTH(test_ratios); i++) {
|
||||
x_calib.f[i] = calibrator->tests[i].drawn_x;
|
||||
y_calib.f[i] = calibrator->tests[i].drawn_y;
|
||||
}
|
||||
|
||||
/* Multiples into the vector */
|
||||
weston_matrix_transform(&inverse, &x_calib);
|
||||
weston_matrix_transform(&inverse, &y_calib);
|
||||
|
||||
printf ("Calibration values: %f %f %f %f %f %f\n",
|
||||
x_calib.f[0], x_calib.f[1], x_calib.f[2],
|
||||
y_calib.f[0], y_calib.f[1], y_calib.f[2]);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
button_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t button,
|
||||
enum wl_pointer_button_state state, void *data)
|
||||
{
|
||||
struct calibrator *calibrator = data;
|
||||
int32_t x, y;
|
||||
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED && button == BTN_LEFT) {
|
||||
input_get_position(input, &x, &y);
|
||||
calibrator->tests[calibrator->current_test].clicked_x = x;
|
||||
calibrator->tests[calibrator->current_test].clicked_y = y;
|
||||
|
||||
calibrator->current_test--;
|
||||
if (calibrator->current_test < 0)
|
||||
finish_calibration(calibrator);
|
||||
}
|
||||
|
||||
widget_schedule_redraw(widget);
|
||||
}
|
||||
|
||||
static void
|
||||
touch_handler(struct widget *widget, struct input *input, uint32_t serial,
|
||||
uint32_t time, int32_t id, float x, float y, void *data)
|
||||
{
|
||||
struct calibrator *calibrator = data;
|
||||
|
||||
calibrator->tests[calibrator->current_test].clicked_x = x;
|
||||
calibrator->tests[calibrator->current_test].clicked_y = y;
|
||||
calibrator->current_test--;
|
||||
|
||||
if (calibrator->current_test < 0)
|
||||
finish_calibration(calibrator);
|
||||
|
||||
widget_schedule_redraw(widget);
|
||||
}
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct calibrator *calibrator = data;
|
||||
struct rectangle allocation;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
int32_t drawn_x, drawn_y;
|
||||
|
||||
widget_get_allocation(calibrator->widget, &allocation);
|
||||
surface = window_get_surface(calibrator->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);
|
||||
|
||||
drawn_x = test_ratios[calibrator->current_test].x_ratio * allocation.width;
|
||||
drawn_y = test_ratios[calibrator->current_test].y_ratio * allocation.height;
|
||||
|
||||
calibrator->tests[calibrator->current_test].drawn_x = drawn_x;
|
||||
calibrator->tests[calibrator->current_test].drawn_y = drawn_y;
|
||||
|
||||
cairo_translate(cr, drawn_x, drawn_y);
|
||||
cairo_set_line_width(cr, 2.0);
|
||||
cairo_set_source_rgb(cr, 1.0, 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);
|
||||
|
||||
cairo_destroy(cr);
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
static struct calibrator *
|
||||
calibrator_create(struct display *display, bool enable_button)
|
||||
{
|
||||
struct calibrator *calibrator;
|
||||
|
||||
calibrator = malloc(sizeof *calibrator);
|
||||
if (calibrator == NULL)
|
||||
return NULL;
|
||||
|
||||
calibrator->window = window_create(display);
|
||||
calibrator->widget = window_add_widget(calibrator->window, calibrator);
|
||||
window_set_title(calibrator->window, "Wayland calibrator");
|
||||
calibrator->display = display;
|
||||
|
||||
calibrator->current_test = ARRAY_LENGTH(test_ratios) - 1;
|
||||
|
||||
if (enable_button)
|
||||
widget_set_button_handler(calibrator->widget, button_handler);
|
||||
widget_set_touch_down_handler(calibrator->widget, touch_handler);
|
||||
widget_set_redraw_handler(calibrator->widget, redraw_handler);
|
||||
|
||||
window_set_fullscreen(calibrator->window, 1);
|
||||
|
||||
return calibrator;
|
||||
}
|
||||
|
||||
static void
|
||||
calibrator_destroy(struct calibrator *calibrator)
|
||||
{
|
||||
widget_destroy(calibrator->widget);
|
||||
window_destroy(calibrator->window);
|
||||
free(calibrator);
|
||||
}
|
||||
|
||||
static void
|
||||
help(const char *name)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [args...]\n", name);
|
||||
fprintf(stderr, " -m, --enable-mouse Enable mouse for testing the touchscreen\n");
|
||||
fprintf(stderr, " -h, --help Display this help message\n");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct display *display;
|
||||
struct calibrator *calibrator;
|
||||
int c;
|
||||
bool enable_mouse = 0;
|
||||
struct option opts[] = {
|
||||
{ "enable-mouse", no_argument, NULL, 'm' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ 0, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
while ((c = getopt_long(argc, argv, "mh", opts, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'm':
|
||||
enable_mouse = 1;
|
||||
break;
|
||||
case 'h':
|
||||
help(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
display = display_create(&argc, argv);
|
||||
|
||||
if (display == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
calibrator = calibrator_create(display, enable_mouse);
|
||||
|
||||
if (!calibrator)
|
||||
return -1;
|
||||
|
||||
display_run(display);
|
||||
|
||||
calibrator_destroy(calibrator);
|
||||
display_destroy(display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
* Copyright © 2010 Intel Corporation
|
||||
* Copyright © 2012 Collabora, Ltd.
|
||||
* Copyright © 2012 Jonas Ådahl
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cairo.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "window.h"
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/xalloc.h"
|
||||
|
||||
struct clickdot {
|
||||
struct display *display;
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
|
||||
cairo_surface_t *buffer;
|
||||
|
||||
struct {
|
||||
int32_t x, y;
|
||||
} dot;
|
||||
|
||||
struct {
|
||||
int32_t x, y;
|
||||
int32_t old_x, old_y;
|
||||
} line;
|
||||
|
||||
int reset;
|
||||
|
||||
struct input *cursor_timeout_input;
|
||||
struct toytimer cursor_timeout;
|
||||
};
|
||||
|
||||
static void
|
||||
draw_line(struct clickdot *clickdot, cairo_t *cr,
|
||||
struct rectangle *allocation)
|
||||
{
|
||||
cairo_t *bcr;
|
||||
cairo_surface_t *tmp_buffer = NULL;
|
||||
|
||||
if (clickdot->reset) {
|
||||
tmp_buffer = clickdot->buffer;
|
||||
clickdot->buffer = NULL;
|
||||
clickdot->line.x = -1;
|
||||
clickdot->line.y = -1;
|
||||
clickdot->line.old_x = -1;
|
||||
clickdot->line.old_y = -1;
|
||||
clickdot->reset = 0;
|
||||
}
|
||||
|
||||
if (clickdot->buffer == NULL) {
|
||||
clickdot->buffer =
|
||||
cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
|
||||
allocation->width,
|
||||
allocation->height);
|
||||
bcr = cairo_create(clickdot->buffer);
|
||||
cairo_set_source_rgba(bcr, 0, 0, 0, 0);
|
||||
cairo_rectangle(bcr,
|
||||
0, 0,
|
||||
allocation->width, allocation->height);
|
||||
cairo_fill(bcr);
|
||||
}
|
||||
else
|
||||
bcr = cairo_create(clickdot->buffer);
|
||||
|
||||
if (tmp_buffer) {
|
||||
cairo_set_source_surface(bcr, tmp_buffer, 0, 0);
|
||||
cairo_rectangle(bcr, 0, 0,
|
||||
allocation->width, allocation->height);
|
||||
cairo_clip(bcr);
|
||||
cairo_paint(bcr);
|
||||
|
||||
cairo_surface_destroy(tmp_buffer);
|
||||
}
|
||||
|
||||
if (clickdot->line.x != -1 && clickdot->line.y != -1) {
|
||||
if (clickdot->line.old_x != -1 &&
|
||||
clickdot->line.old_y != -1) {
|
||||
cairo_set_line_width(bcr, 2.0);
|
||||
cairo_set_source_rgb(bcr, 1, 1, 1);
|
||||
cairo_translate(bcr,
|
||||
-allocation->x, -allocation->y);
|
||||
|
||||
cairo_move_to(bcr,
|
||||
clickdot->line.old_x,
|
||||
clickdot->line.old_y);
|
||||
cairo_line_to(bcr,
|
||||
clickdot->line.x,
|
||||
clickdot->line.y);
|
||||
|
||||
cairo_stroke(bcr);
|
||||
}
|
||||
|
||||
clickdot->line.old_x = clickdot->line.x;
|
||||
clickdot->line.old_y = clickdot->line.y;
|
||||
}
|
||||
cairo_destroy(bcr);
|
||||
|
||||
cairo_set_source_surface(cr, clickdot->buffer,
|
||||
allocation->x, allocation->y);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
|
||||
cairo_rectangle(cr,
|
||||
allocation->x, allocation->y,
|
||||
allocation->width, allocation->height);
|
||||
cairo_clip(cr);
|
||||
cairo_paint(cr);
|
||||
}
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
static const double r = 10.0;
|
||||
struct clickdot *clickdot = data;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
struct rectangle allocation;
|
||||
|
||||
widget_get_allocation(clickdot->widget, &allocation);
|
||||
|
||||
surface = window_get_surface(clickdot->window);
|
||||
|
||||
cr = cairo_create(surface);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_rectangle(cr,
|
||||
allocation.x,
|
||||
allocation.y,
|
||||
allocation.width,
|
||||
allocation.height);
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
|
||||
cairo_fill(cr);
|
||||
|
||||
draw_line(clickdot, cr, &allocation);
|
||||
|
||||
cairo_translate(cr, clickdot->dot.x + 0.5, clickdot->dot.y + 0.5);
|
||||
cairo_set_line_width(cr, 1.0);
|
||||
cairo_set_source_rgb(cr, 0.1, 0.9, 0.9);
|
||||
cairo_move_to(cr, 0.0, -r);
|
||||
cairo_line_to(cr, 0.0, r);
|
||||
cairo_move_to(cr, -r, 0.0);
|
||||
cairo_line_to(cr, r, 0.0);
|
||||
cairo_arc(cr, 0.0, 0.0, r, 0.0, 2.0 * M_PI);
|
||||
cairo_stroke(cr);
|
||||
|
||||
cairo_destroy(cr);
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_focus_handler(struct window *window,
|
||||
struct input *device, void *data)
|
||||
{
|
||||
struct clickdot *clickdot = data;
|
||||
|
||||
window_schedule_redraw(clickdot->window);
|
||||
}
|
||||
|
||||
static void
|
||||
key_handler(struct window *window, struct input *input, uint32_t time,
|
||||
uint32_t key, uint32_t sym,
|
||||
enum wl_keyboard_key_state state, void *data)
|
||||
{
|
||||
struct clickdot *clickdot = data;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
|
||||
return;
|
||||
|
||||
switch (sym) {
|
||||
case XKB_KEY_Escape:
|
||||
display_exit(clickdot->display);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
button_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t button,
|
||||
enum wl_pointer_button_state state, void *data)
|
||||
{
|
||||
struct clickdot *clickdot = data;
|
||||
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED && button == BTN_LEFT)
|
||||
input_get_position(input, &clickdot->dot.x, &clickdot->dot.y);
|
||||
|
||||
widget_schedule_redraw(widget);
|
||||
}
|
||||
|
||||
static void
|
||||
cursor_timeout_reset(struct clickdot *clickdot)
|
||||
{
|
||||
toytimer_arm_once_usec(&clickdot->cursor_timeout, 500 * 1000);
|
||||
}
|
||||
|
||||
static int
|
||||
motion_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
float x, float y, void *data)
|
||||
{
|
||||
struct clickdot *clickdot = data;
|
||||
clickdot->line.x = x;
|
||||
clickdot->line.y = y;
|
||||
|
||||
window_schedule_redraw(clickdot->window);
|
||||
|
||||
cursor_timeout_reset(clickdot);
|
||||
clickdot->cursor_timeout_input = input;
|
||||
|
||||
return CURSOR_BLANK;
|
||||
}
|
||||
|
||||
static void
|
||||
resize_handler(struct widget *widget,
|
||||
int32_t width, int32_t height,
|
||||
void *data)
|
||||
{
|
||||
struct clickdot *clickdot = data;
|
||||
|
||||
clickdot->reset = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
leave_handler(struct widget *widget,
|
||||
struct input *input, void *data)
|
||||
{
|
||||
struct clickdot *clickdot = data;
|
||||
|
||||
clickdot->reset = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
cursor_timeout_func(struct toytimer *tt)
|
||||
{
|
||||
struct clickdot *clickdot =
|
||||
container_of(tt, struct clickdot, cursor_timeout);
|
||||
|
||||
input_set_pointer_image(clickdot->cursor_timeout_input,
|
||||
CURSOR_LEFT_PTR);
|
||||
}
|
||||
|
||||
static struct clickdot *
|
||||
clickdot_create(struct display *display)
|
||||
{
|
||||
struct clickdot *clickdot;
|
||||
|
||||
clickdot = xzalloc(sizeof *clickdot);
|
||||
clickdot->window = window_create(display);
|
||||
clickdot->widget = window_frame_create(clickdot->window, clickdot);
|
||||
window_set_title(clickdot->window, "Wayland ClickDot");
|
||||
clickdot->display = display;
|
||||
clickdot->buffer = NULL;
|
||||
|
||||
window_set_key_handler(clickdot->window, key_handler);
|
||||
window_set_user_data(clickdot->window, clickdot);
|
||||
window_set_keyboard_focus_handler(clickdot->window,
|
||||
keyboard_focus_handler);
|
||||
|
||||
widget_set_redraw_handler(clickdot->widget, redraw_handler);
|
||||
widget_set_button_handler(clickdot->widget, button_handler);
|
||||
widget_set_motion_handler(clickdot->widget, motion_handler);
|
||||
widget_set_resize_handler(clickdot->widget, resize_handler);
|
||||
widget_set_leave_handler(clickdot->widget, leave_handler);
|
||||
|
||||
widget_schedule_resize(clickdot->widget, 500, 400);
|
||||
clickdot->dot.x = 250;
|
||||
clickdot->dot.y = 200;
|
||||
clickdot->line.x = -1;
|
||||
clickdot->line.y = -1;
|
||||
clickdot->line.old_x = -1;
|
||||
clickdot->line.old_y = -1;
|
||||
clickdot->reset = 0;
|
||||
|
||||
toytimer_init(&clickdot->cursor_timeout, CLOCK_MONOTONIC,
|
||||
display, cursor_timeout_func);
|
||||
|
||||
return clickdot;
|
||||
}
|
||||
|
||||
static void
|
||||
clickdot_destroy(struct clickdot *clickdot)
|
||||
{
|
||||
toytimer_fini(&clickdot->cursor_timeout);
|
||||
if (clickdot->buffer)
|
||||
cairo_surface_destroy(clickdot->buffer);
|
||||
widget_destroy(clickdot->widget);
|
||||
window_destroy(clickdot->window);
|
||||
free(clickdot);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct display *display;
|
||||
struct clickdot *clickdot;
|
||||
|
||||
display = display_create(&argc, argv);
|
||||
if (display == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
clickdot = clickdot_create(display);
|
||||
|
||||
display_run(display);
|
||||
|
||||
clickdot_destroy(clickdot);
|
||||
display_destroy(display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,637 @@
|
|||
/*
|
||||
* Copyright © 2012 Collabora, Ltd.
|
||||
* Copyright © 2012 Rob Clark
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* cliptest: for debugging calculate_edges() function.
|
||||
* controls:
|
||||
* clip box position: mouse left drag, keys: w a s d
|
||||
* clip box size: mouse right drag, keys: i j k l
|
||||
* surface orientation: mouse wheel, keys: n m
|
||||
* surface transform disable key: r
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <pixman.h>
|
||||
#include <cairo.h>
|
||||
#include <float.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "libweston/vertex-clipping.h"
|
||||
#include "shared/xalloc.h"
|
||||
#include "window.h"
|
||||
|
||||
typedef float GLfloat;
|
||||
|
||||
struct geometry {
|
||||
pixman_box32_t clip;
|
||||
|
||||
pixman_box32_t surf;
|
||||
float s; /* sin phi */
|
||||
float c; /* cos phi */
|
||||
float phi;
|
||||
};
|
||||
|
||||
struct weston_view {
|
||||
struct {
|
||||
int enabled;
|
||||
} transform;
|
||||
|
||||
struct geometry *geometry;
|
||||
};
|
||||
|
||||
static void
|
||||
weston_view_to_global_float(struct weston_view *view,
|
||||
float sx, float sy, float *x, float *y)
|
||||
{
|
||||
struct geometry *g = view->geometry;
|
||||
|
||||
/* pure rotation around origin by sine and cosine */
|
||||
*x = g->c * sx + g->s * sy;
|
||||
*y = -g->s * sx + g->c * sy;
|
||||
}
|
||||
|
||||
/* ---------------------- copied begins -----------------------*/
|
||||
/* Keep this in sync with what is in gl-renderer.c! */
|
||||
|
||||
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#define min(a, b) (((a) > (b)) ? (b) : (a))
|
||||
|
||||
/*
|
||||
* Compute the boundary vertices of the intersection of the global coordinate
|
||||
* aligned rectangle 'rect', and an arbitrary quadrilateral produced from
|
||||
* 'surf_rect' when transformed from surface coordinates into global coordinates.
|
||||
* The vertices are written to 'ex' and 'ey', and the return value is the
|
||||
* number of vertices. Vertices are produced in clockwise winding order.
|
||||
* Guarantees to produce either zero vertices, or 3-8 vertices with non-zero
|
||||
* polygon area.
|
||||
*/
|
||||
static int
|
||||
calculate_edges(struct weston_view *ev, pixman_box32_t *rect,
|
||||
pixman_box32_t *surf_rect, GLfloat *ex, GLfloat *ey)
|
||||
{
|
||||
|
||||
struct clip_context ctx;
|
||||
int i, n;
|
||||
GLfloat min_x, max_x, min_y, max_y;
|
||||
struct polygon8 surf = {
|
||||
{ surf_rect->x1, surf_rect->x2, surf_rect->x2, surf_rect->x1 },
|
||||
{ surf_rect->y1, surf_rect->y1, surf_rect->y2, surf_rect->y2 },
|
||||
4
|
||||
};
|
||||
|
||||
ctx.clip.x1 = rect->x1;
|
||||
ctx.clip.y1 = rect->y1;
|
||||
ctx.clip.x2 = rect->x2;
|
||||
ctx.clip.y2 = rect->y2;
|
||||
|
||||
/* transform surface to screen space: */
|
||||
for (i = 0; i < surf.n; i++)
|
||||
weston_view_to_global_float(ev, surf.x[i], surf.y[i],
|
||||
&surf.x[i], &surf.y[i]);
|
||||
|
||||
/* find bounding box: */
|
||||
min_x = max_x = surf.x[0];
|
||||
min_y = max_y = surf.y[0];
|
||||
|
||||
for (i = 1; i < surf.n; i++) {
|
||||
min_x = min(min_x, surf.x[i]);
|
||||
max_x = max(max_x, surf.x[i]);
|
||||
min_y = min(min_y, surf.y[i]);
|
||||
max_y = max(max_y, surf.y[i]);
|
||||
}
|
||||
|
||||
/* First, simple bounding box check to discard early transformed
|
||||
* surface rects that do not intersect with the clip region:
|
||||
*/
|
||||
if ((min_x >= ctx.clip.x2) || (max_x <= ctx.clip.x1) ||
|
||||
(min_y >= ctx.clip.y2) || (max_y <= ctx.clip.y1))
|
||||
return 0;
|
||||
|
||||
/* Simple case, bounding box edges are parallel to surface edges,
|
||||
* there will be only four edges. We just need to clip the surface
|
||||
* vertices to the clip rect bounds:
|
||||
*/
|
||||
if (!ev->transform.enabled)
|
||||
return clip_simple(&ctx, &surf, ex, ey);
|
||||
|
||||
/* Transformed case: use a general polygon clipping algorithm to
|
||||
* clip the surface rectangle with each side of 'rect'.
|
||||
* The algorithm is Sutherland-Hodgman, as explained in
|
||||
* http://www.codeguru.com/cpp/misc/misc/graphics/article.php/c8965/Polygon-Clipping.htm
|
||||
* but without looking at any of that code.
|
||||
*/
|
||||
n = clip_transformed(&ctx, &surf, ex, ey);
|
||||
|
||||
if (n < 3)
|
||||
return 0;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------- copied ends -----------------------*/
|
||||
|
||||
static void
|
||||
geometry_set_phi(struct geometry *g, float phi)
|
||||
{
|
||||
g->phi = phi;
|
||||
g->s = sin(phi);
|
||||
g->c = cos(phi);
|
||||
}
|
||||
|
||||
static void
|
||||
geometry_init(struct geometry *g)
|
||||
{
|
||||
g->clip.x1 = -50;
|
||||
g->clip.y1 = -50;
|
||||
g->clip.x2 = -10;
|
||||
g->clip.y2 = -10;
|
||||
|
||||
g->surf.x1 = -20;
|
||||
g->surf.y1 = -20;
|
||||
g->surf.x2 = 20;
|
||||
g->surf.y2 = 20;
|
||||
|
||||
geometry_set_phi(g, 0.0);
|
||||
}
|
||||
|
||||
struct ui_state {
|
||||
uint32_t button;
|
||||
int down;
|
||||
|
||||
int down_pos[2];
|
||||
struct geometry geometry;
|
||||
};
|
||||
|
||||
struct cliptest {
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
struct display *display;
|
||||
int fullscreen;
|
||||
|
||||
struct ui_state ui;
|
||||
|
||||
struct geometry geometry;
|
||||
struct weston_view view;
|
||||
};
|
||||
|
||||
static void
|
||||
draw_polygon_closed(cairo_t *cr, GLfloat *x, GLfloat *y, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
cairo_move_to(cr, x[0], y[0]);
|
||||
for (i = 1; i < n; i++)
|
||||
cairo_line_to(cr, x[i], y[i]);
|
||||
cairo_line_to(cr, x[0], y[0]);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_polygon_labels(cairo_t *cr, GLfloat *x, GLfloat *y, int n)
|
||||
{
|
||||
char str[16];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
snprintf(str, 16, "%d", i);
|
||||
cairo_move_to(cr, x[i], y[i]);
|
||||
cairo_show_text(cr, str);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
draw_coordinates(cairo_t *cr, double ox, double oy, GLfloat *x, GLfloat *y, int n)
|
||||
{
|
||||
char str[64];
|
||||
int i;
|
||||
cairo_font_extents_t ext;
|
||||
|
||||
cairo_font_extents(cr, &ext);
|
||||
for (i = 0; i < n; i++) {
|
||||
snprintf(str, 64, "%d: %14.9f, %14.9f", i, x[i], y[i]);
|
||||
cairo_move_to(cr, ox, oy + ext.height * (i + 1));
|
||||
cairo_show_text(cr, str);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
draw_box(cairo_t *cr, pixman_box32_t *box, struct weston_view *view)
|
||||
{
|
||||
GLfloat x[4], y[4];
|
||||
|
||||
if (view) {
|
||||
weston_view_to_global_float(view, box->x1, box->y1, &x[0], &y[0]);
|
||||
weston_view_to_global_float(view, box->x2, box->y1, &x[1], &y[1]);
|
||||
weston_view_to_global_float(view, box->x2, box->y2, &x[2], &y[2]);
|
||||
weston_view_to_global_float(view, box->x1, box->y2, &x[3], &y[3]);
|
||||
} else {
|
||||
x[0] = box->x1; y[0] = box->y1;
|
||||
x[1] = box->x2; y[1] = box->y1;
|
||||
x[2] = box->x2; y[2] = box->y2;
|
||||
x[3] = box->x1; y[3] = box->y2;
|
||||
}
|
||||
|
||||
draw_polygon_closed(cr, x, y, 4);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_geometry(cairo_t *cr, struct weston_view *view,
|
||||
GLfloat *ex, GLfloat *ey, int n)
|
||||
{
|
||||
struct geometry *g = view->geometry;
|
||||
float cx, cy;
|
||||
|
||||
draw_box(cr, &g->surf, view);
|
||||
cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.4);
|
||||
cairo_fill(cr);
|
||||
weston_view_to_global_float(view, g->surf.x1 - 4, g->surf.y1 - 4, &cx, &cy);
|
||||
cairo_arc(cr, cx, cy, 1.5, 0.0, 2.0 * M_PI);
|
||||
if (view->transform.enabled == 0)
|
||||
cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.8);
|
||||
cairo_fill(cr);
|
||||
|
||||
draw_box(cr, &g->clip, NULL);
|
||||
cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 0.4);
|
||||
cairo_fill(cr);
|
||||
|
||||
if (n) {
|
||||
draw_polygon_closed(cr, ex, ey, n);
|
||||
cairo_set_source_rgb(cr, 0.0, 1.0, 0.0);
|
||||
cairo_stroke(cr);
|
||||
|
||||
cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 0.5);
|
||||
draw_polygon_labels(cr, ex, ey, n);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct cliptest *cliptest = data;
|
||||
struct geometry *g = cliptest->view.geometry;
|
||||
struct rectangle allocation;
|
||||
cairo_t *cr;
|
||||
cairo_surface_t *surface;
|
||||
GLfloat ex[8];
|
||||
GLfloat ey[8];
|
||||
int n;
|
||||
|
||||
n = calculate_edges(&cliptest->view, &g->clip, &g->surf, ex, ey);
|
||||
|
||||
widget_get_allocation(cliptest->widget, &allocation);
|
||||
|
||||
surface = window_get_surface(cliptest->window);
|
||||
cr = cairo_create(surface);
|
||||
widget_get_allocation(cliptest->widget, &allocation);
|
||||
cairo_rectangle(cr, allocation.x, allocation.y,
|
||||
allocation.width, allocation.height);
|
||||
cairo_clip(cr);
|
||||
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 1);
|
||||
cairo_paint(cr);
|
||||
|
||||
cairo_translate(cr, allocation.x, allocation.y);
|
||||
cairo_set_line_width(cr, 1.0);
|
||||
cairo_move_to(cr, allocation.width / 2.0, 0.0);
|
||||
cairo_line_to(cr, allocation.width / 2.0, allocation.height);
|
||||
cairo_move_to(cr, 0.0, allocation.height / 2.0);
|
||||
cairo_line_to(cr, allocation.width, allocation.height / 2.0);
|
||||
cairo_set_source_rgba(cr, 0.5, 0.5, 0.5, 1.0);
|
||||
cairo_stroke(cr);
|
||||
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
|
||||
cairo_push_group(cr);
|
||||
cairo_translate(cr, allocation.width / 2.0,
|
||||
allocation.height / 2.0);
|
||||
cairo_scale(cr, 4.0, 4.0);
|
||||
cairo_set_line_width(cr, 0.5);
|
||||
cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL);
|
||||
cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
|
||||
CAIRO_FONT_WEIGHT_BOLD);
|
||||
cairo_set_font_size(cr, 5.0);
|
||||
draw_geometry(cr, &cliptest->view, ex, ey, n);
|
||||
cairo_pop_group_to_source(cr);
|
||||
cairo_paint(cr);
|
||||
|
||||
cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0);
|
||||
cairo_select_font_face(cr, "monospace", CAIRO_FONT_SLANT_NORMAL,
|
||||
CAIRO_FONT_WEIGHT_NORMAL);
|
||||
cairo_set_font_size(cr, 12.0);
|
||||
draw_coordinates(cr, 10.0, 10.0, ex, ey, n);
|
||||
|
||||
cairo_destroy(cr);
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
static int
|
||||
motion_handler(struct widget *widget, struct input *input,
|
||||
uint32_t time, float x, float y, void *data)
|
||||
{
|
||||
struct cliptest *cliptest = data;
|
||||
struct ui_state *ui = &cliptest->ui;
|
||||
struct geometry *ref = &ui->geometry;
|
||||
struct geometry *geom = &cliptest->geometry;
|
||||
float dx, dy;
|
||||
|
||||
if (!ui->down)
|
||||
return CURSOR_LEFT_PTR;
|
||||
|
||||
dx = (x - ui->down_pos[0]) * 0.25;
|
||||
dy = (y - ui->down_pos[1]) * 0.25;
|
||||
|
||||
switch (ui->button) {
|
||||
case BTN_LEFT:
|
||||
geom->clip.x1 = ref->clip.x1 + dx;
|
||||
geom->clip.y1 = ref->clip.y1 + dy;
|
||||
/* fall through */
|
||||
case BTN_RIGHT:
|
||||
geom->clip.x2 = ref->clip.x2 + dx;
|
||||
geom->clip.y2 = ref->clip.y2 + dy;
|
||||
break;
|
||||
default:
|
||||
return CURSOR_LEFT_PTR;
|
||||
}
|
||||
|
||||
widget_schedule_redraw(cliptest->widget);
|
||||
return CURSOR_BLANK;
|
||||
}
|
||||
|
||||
static void
|
||||
button_handler(struct widget *widget, struct input *input,
|
||||
uint32_t time, uint32_t button,
|
||||
enum wl_pointer_button_state state, void *data)
|
||||
{
|
||||
struct cliptest *cliptest = data;
|
||||
struct ui_state *ui = &cliptest->ui;
|
||||
|
||||
ui->button = button;
|
||||
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
ui->down = 1;
|
||||
input_get_position(input, &ui->down_pos[0], &ui->down_pos[1]);
|
||||
} else {
|
||||
ui->down = 0;
|
||||
ui->geometry = cliptest->geometry;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
axis_handler(struct widget *widget, struct input *input, uint32_t time,
|
||||
uint32_t axis, wl_fixed_t value, void *data)
|
||||
{
|
||||
struct cliptest *cliptest = data;
|
||||
struct geometry *geom = &cliptest->geometry;
|
||||
|
||||
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
|
||||
return;
|
||||
|
||||
geometry_set_phi(geom, geom->phi +
|
||||
(M_PI / 12.0) * wl_fixed_to_double(value));
|
||||
cliptest->view.transform.enabled = 1;
|
||||
|
||||
widget_schedule_redraw(cliptest->widget);
|
||||
}
|
||||
|
||||
static void
|
||||
key_handler(struct window *window, struct input *input, uint32_t time,
|
||||
uint32_t key, uint32_t sym,
|
||||
enum wl_keyboard_key_state state, void *data)
|
||||
{
|
||||
struct cliptest *cliptest = data;
|
||||
struct geometry *g = &cliptest->geometry;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
|
||||
return;
|
||||
|
||||
switch (sym) {
|
||||
case XKB_KEY_Escape:
|
||||
display_exit(cliptest->display);
|
||||
return;
|
||||
case XKB_KEY_w:
|
||||
g->clip.y1 -= 1;
|
||||
g->clip.y2 -= 1;
|
||||
break;
|
||||
case XKB_KEY_a:
|
||||
g->clip.x1 -= 1;
|
||||
g->clip.x2 -= 1;
|
||||
break;
|
||||
case XKB_KEY_s:
|
||||
g->clip.y1 += 1;
|
||||
g->clip.y2 += 1;
|
||||
break;
|
||||
case XKB_KEY_d:
|
||||
g->clip.x1 += 1;
|
||||
g->clip.x2 += 1;
|
||||
break;
|
||||
case XKB_KEY_i:
|
||||
g->clip.y2 -= 1;
|
||||
break;
|
||||
case XKB_KEY_j:
|
||||
g->clip.x2 -= 1;
|
||||
break;
|
||||
case XKB_KEY_k:
|
||||
g->clip.y2 += 1;
|
||||
break;
|
||||
case XKB_KEY_l:
|
||||
g->clip.x2 += 1;
|
||||
break;
|
||||
case XKB_KEY_n:
|
||||
geometry_set_phi(g, g->phi + (M_PI / 24.0));
|
||||
cliptest->view.transform.enabled = 1;
|
||||
break;
|
||||
case XKB_KEY_m:
|
||||
geometry_set_phi(g, g->phi - (M_PI / 24.0));
|
||||
cliptest->view.transform.enabled = 1;
|
||||
break;
|
||||
case XKB_KEY_r:
|
||||
geometry_set_phi(g, 0.0);
|
||||
cliptest->view.transform.enabled = 0;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
widget_schedule_redraw(cliptest->widget);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_focus_handler(struct window *window,
|
||||
struct input *device, void *data)
|
||||
{
|
||||
struct cliptest *cliptest = data;
|
||||
|
||||
window_schedule_redraw(cliptest->window);
|
||||
}
|
||||
|
||||
static void
|
||||
fullscreen_handler(struct window *window, void *data)
|
||||
{
|
||||
struct cliptest *cliptest = data;
|
||||
|
||||
cliptest->fullscreen ^= 1;
|
||||
window_set_fullscreen(window, cliptest->fullscreen);
|
||||
}
|
||||
|
||||
static struct cliptest *
|
||||
cliptest_create(struct display *display)
|
||||
{
|
||||
struct cliptest *cliptest;
|
||||
|
||||
cliptest = xzalloc(sizeof *cliptest);
|
||||
cliptest->view.geometry = &cliptest->geometry;
|
||||
cliptest->view.transform.enabled = 0;
|
||||
geometry_init(&cliptest->geometry);
|
||||
geometry_init(&cliptest->ui.geometry);
|
||||
|
||||
cliptest->window = window_create(display);
|
||||
cliptest->widget = window_frame_create(cliptest->window, cliptest);
|
||||
window_set_title(cliptest->window, "cliptest");
|
||||
cliptest->display = display;
|
||||
|
||||
window_set_user_data(cliptest->window, cliptest);
|
||||
widget_set_redraw_handler(cliptest->widget, redraw_handler);
|
||||
widget_set_button_handler(cliptest->widget, button_handler);
|
||||
widget_set_motion_handler(cliptest->widget, motion_handler);
|
||||
widget_set_axis_handler(cliptest->widget, axis_handler);
|
||||
|
||||
window_set_keyboard_focus_handler(cliptest->window,
|
||||
keyboard_focus_handler);
|
||||
window_set_key_handler(cliptest->window, key_handler);
|
||||
window_set_fullscreen_handler(cliptest->window, fullscreen_handler);
|
||||
|
||||
/* set minimum size */
|
||||
widget_schedule_resize(cliptest->widget, 200, 100);
|
||||
|
||||
/* set current size */
|
||||
widget_schedule_resize(cliptest->widget, 500, 400);
|
||||
|
||||
return cliptest;
|
||||
}
|
||||
|
||||
static struct timespec begin_time;
|
||||
|
||||
static void
|
||||
reset_timer(void)
|
||||
{
|
||||
clock_gettime(CLOCK_MONOTONIC, &begin_time);
|
||||
}
|
||||
|
||||
static double
|
||||
read_timer(void)
|
||||
{
|
||||
struct timespec t;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
return (double)(t.tv_sec - begin_time.tv_sec) +
|
||||
1e-9 * (t.tv_nsec - begin_time.tv_nsec);
|
||||
}
|
||||
|
||||
static int
|
||||
benchmark(void)
|
||||
{
|
||||
struct weston_view view;
|
||||
struct geometry geom;
|
||||
GLfloat ex[8], ey[8];
|
||||
int i;
|
||||
double t;
|
||||
const int N = 1000000;
|
||||
|
||||
geom.clip.x1 = -19;
|
||||
geom.clip.y1 = -19;
|
||||
geom.clip.x2 = 19;
|
||||
geom.clip.y2 = 19;
|
||||
|
||||
geom.surf.x1 = -20;
|
||||
geom.surf.y1 = -20;
|
||||
geom.surf.x2 = 20;
|
||||
geom.surf.y2 = 20;
|
||||
|
||||
geometry_set_phi(&geom, 0.0);
|
||||
|
||||
view.transform.enabled = 1;
|
||||
view.geometry = &geom;
|
||||
|
||||
reset_timer();
|
||||
for (i = 0; i < N; i++) {
|
||||
geometry_set_phi(&geom, (float)i / 360.0f);
|
||||
calculate_edges(&view, &geom.clip, &geom.surf, ex, ey);
|
||||
}
|
||||
t = read_timer();
|
||||
|
||||
printf("%d calls took %g s, average %g us/call\n", N, t, t / N * 1e6);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
cliptest_destroy(struct cliptest *cliptest)
|
||||
{
|
||||
widget_destroy(cliptest->widget);
|
||||
window_destroy(cliptest->window);
|
||||
free(cliptest);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct display *d;
|
||||
struct cliptest *cliptest;
|
||||
|
||||
if (argc > 1) {
|
||||
if (argc == 2 && !strcmp(argv[1], "-b"))
|
||||
return benchmark();
|
||||
printf("Usage: %s [OPTIONS]\n -b run benchmark\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
d = display_create(&argc, argv);
|
||||
if (d == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cliptest = cliptest_create(d);
|
||||
display_run(d);
|
||||
|
||||
cliptest_destroy(cliptest);
|
||||
display_destroy(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* Copyright © 2010 Intel Corporation
|
||||
* Copyright © 2012 Collabora, Ltd.
|
||||
* Copyright © 2012 Jonas Ådahl
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cairo.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "window.h"
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/xalloc.h"
|
||||
|
||||
#define NUM_COMPLEX_REGION_RECTS 9
|
||||
|
||||
static bool option_complex_confine_region;
|
||||
static bool option_help;
|
||||
|
||||
struct confine {
|
||||
struct display *display;
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
|
||||
cairo_surface_t *buffer;
|
||||
|
||||
struct {
|
||||
int32_t x, y;
|
||||
int32_t old_x, old_y;
|
||||
} line;
|
||||
|
||||
int reset;
|
||||
|
||||
struct input *cursor_timeout_input;
|
||||
struct toytimer cursor_timeout;
|
||||
|
||||
bool pointer_confined;
|
||||
|
||||
bool complex_confine_region_enabled;
|
||||
bool complex_confine_region_dirty;
|
||||
struct rectangle complex_confine_region[NUM_COMPLEX_REGION_RECTS];
|
||||
};
|
||||
|
||||
static void
|
||||
draw_line(struct confine *confine, cairo_t *cr,
|
||||
struct rectangle *allocation)
|
||||
{
|
||||
cairo_t *bcr;
|
||||
cairo_surface_t *tmp_buffer = NULL;
|
||||
|
||||
if (confine->reset) {
|
||||
tmp_buffer = confine->buffer;
|
||||
confine->buffer = NULL;
|
||||
confine->line.x = -1;
|
||||
confine->line.y = -1;
|
||||
confine->line.old_x = -1;
|
||||
confine->line.old_y = -1;
|
||||
confine->reset = 0;
|
||||
}
|
||||
|
||||
if (confine->buffer == NULL) {
|
||||
confine->buffer =
|
||||
cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
|
||||
allocation->width,
|
||||
allocation->height);
|
||||
bcr = cairo_create(confine->buffer);
|
||||
cairo_set_source_rgba(bcr, 0, 0, 0, 0);
|
||||
cairo_rectangle(bcr,
|
||||
0, 0,
|
||||
allocation->width, allocation->height);
|
||||
cairo_fill(bcr);
|
||||
}
|
||||
else
|
||||
bcr = cairo_create(confine->buffer);
|
||||
|
||||
if (tmp_buffer) {
|
||||
cairo_set_source_surface(bcr, tmp_buffer, 0, 0);
|
||||
cairo_rectangle(bcr, 0, 0,
|
||||
allocation->width, allocation->height);
|
||||
cairo_clip(bcr);
|
||||
cairo_paint(bcr);
|
||||
|
||||
cairo_surface_destroy(tmp_buffer);
|
||||
}
|
||||
|
||||
if (confine->line.x != -1 && confine->line.y != -1) {
|
||||
if (confine->line.old_x != -1 &&
|
||||
confine->line.old_y != -1) {
|
||||
cairo_set_line_width(bcr, 2.0);
|
||||
cairo_set_source_rgb(bcr, 1, 1, 1);
|
||||
cairo_translate(bcr,
|
||||
-allocation->x, -allocation->y);
|
||||
|
||||
cairo_move_to(bcr,
|
||||
confine->line.old_x,
|
||||
confine->line.old_y);
|
||||
cairo_line_to(bcr,
|
||||
confine->line.x,
|
||||
confine->line.y);
|
||||
|
||||
cairo_stroke(bcr);
|
||||
}
|
||||
|
||||
confine->line.old_x = confine->line.x;
|
||||
confine->line.old_y = confine->line.y;
|
||||
}
|
||||
cairo_destroy(bcr);
|
||||
|
||||
cairo_set_source_surface(cr, confine->buffer,
|
||||
allocation->x, allocation->y);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
|
||||
cairo_rectangle(cr,
|
||||
allocation->x, allocation->y,
|
||||
allocation->width, allocation->height);
|
||||
cairo_clip(cr);
|
||||
cairo_paint(cr);
|
||||
}
|
||||
|
||||
static void
|
||||
calculate_complex_confine_region(struct confine *confine)
|
||||
{
|
||||
struct rectangle allocation;
|
||||
int32_t x, y, w, h;
|
||||
struct rectangle *rs = confine->complex_confine_region;
|
||||
|
||||
if (!confine->complex_confine_region_dirty)
|
||||
return;
|
||||
|
||||
widget_get_allocation(confine->widget, &allocation);
|
||||
x = allocation.x;
|
||||
y = allocation.y;
|
||||
w = allocation.width;
|
||||
h = allocation.height;
|
||||
|
||||
/*
|
||||
* The code below constructs a region made up of rectangles that
|
||||
* is then used to set up both an illustrative shaded region in the
|
||||
* widget and a confine region used when confining the pointer.
|
||||
*/
|
||||
|
||||
rs[0].x = x + (int)round(w * 0.05);
|
||||
rs[0].y = y + (int)round(h * 0.15);
|
||||
rs[0].width = (int)round(w * 0.35);
|
||||
rs[0].height = (int)round(h * 0.7);
|
||||
|
||||
rs[1].x = rs[0].x + rs[0].width;
|
||||
rs[1].y = y + (int)round(h * 0.45);
|
||||
rs[1].width = (int)round(w * 0.09);
|
||||
rs[1].height = (int)round(h * 0.1);
|
||||
|
||||
rs[2].x = rs[1].x + rs[1].width;
|
||||
rs[2].y = y + (int)round(h * 0.48);
|
||||
rs[2].width = (int)round(w * 0.02);
|
||||
rs[2].height = (int)round(h * 0.04);
|
||||
|
||||
rs[3].x = rs[2].x + rs[2].width;
|
||||
rs[3].y = y + (int)round(h * 0.45);
|
||||
rs[3].width = (int)round(w * 0.09);
|
||||
rs[3].height = (int)round(h * 0.1);
|
||||
|
||||
rs[4].x = rs[3].x + rs[3].width;
|
||||
rs[4].y = y + (int)round(h * 0.15);
|
||||
rs[4].width = (int)round(w * 0.35);
|
||||
rs[4].height = (int)round(h * 0.7);
|
||||
|
||||
rs[5].x = x + (int)round(w * 0.05);
|
||||
rs[5].y = y + (int)round(h * 0.05);
|
||||
rs[5].width = rs[0].width + rs[1].width + rs[2].width +
|
||||
rs[3].width + rs[4].width;
|
||||
rs[5].height = (int)round(h * 0.10);
|
||||
|
||||
rs[6].x = x + (int)round(w * 0.1);
|
||||
rs[6].y = rs[4].y + rs[4].height + (int)round(h * 0.02);
|
||||
rs[6].width = (int)round(w * 0.8);
|
||||
rs[6].height = (int)round(h * 0.03);
|
||||
|
||||
rs[7].x = x + (int)round(w * 0.05);
|
||||
rs[7].y = rs[6].y + rs[6].height;
|
||||
rs[7].width = (int)round(w * 0.9);
|
||||
rs[7].height = (int)round(h * 0.03);
|
||||
|
||||
rs[8].x = x + (int)round(w * 0.1);
|
||||
rs[8].y = rs[7].y + rs[7].height;
|
||||
rs[8].width = (int)round(w * 0.8);
|
||||
rs[8].height = (int)round(h * 0.03);
|
||||
|
||||
confine->complex_confine_region_dirty = false;
|
||||
}
|
||||
|
||||
static void
|
||||
draw_complex_confine_region_mask(struct confine *confine, cairo_t *cr)
|
||||
{
|
||||
int i;
|
||||
|
||||
calculate_complex_confine_region(confine);
|
||||
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
|
||||
for (i = 0; i < NUM_COMPLEX_REGION_RECTS; i++) {
|
||||
cairo_rectangle(cr,
|
||||
confine->complex_confine_region[i].x,
|
||||
confine->complex_confine_region[i].y,
|
||||
confine->complex_confine_region[i].width,
|
||||
confine->complex_confine_region[i].height);
|
||||
cairo_set_source_rgba(cr, 0.14, 0.14, 0.14, 0.9);
|
||||
cairo_fill(cr);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct confine *confine = data;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
struct rectangle allocation;
|
||||
|
||||
widget_get_allocation(confine->widget, &allocation);
|
||||
|
||||
surface = window_get_surface(confine->window);
|
||||
|
||||
cr = cairo_create(surface);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_rectangle(cr,
|
||||
allocation.x,
|
||||
allocation.y,
|
||||
allocation.width,
|
||||
allocation.height);
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
|
||||
cairo_fill(cr);
|
||||
|
||||
if (confine->complex_confine_region_enabled) {
|
||||
draw_complex_confine_region_mask(confine, cr);
|
||||
}
|
||||
|
||||
draw_line(confine, cr, &allocation);
|
||||
|
||||
cairo_destroy(cr);
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_focus_handler(struct window *window,
|
||||
struct input *device, void *data)
|
||||
{
|
||||
struct confine *confine = data;
|
||||
|
||||
window_schedule_redraw(confine->window);
|
||||
}
|
||||
|
||||
static void
|
||||
key_handler(struct window *window, struct input *input, uint32_t time,
|
||||
uint32_t key, uint32_t sym,
|
||||
enum wl_keyboard_key_state state, void *data)
|
||||
{
|
||||
struct confine *confine = data;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
|
||||
return;
|
||||
|
||||
switch (sym) {
|
||||
case XKB_KEY_Escape:
|
||||
display_exit(confine->display);
|
||||
break;
|
||||
case XKB_KEY_BackSpace:
|
||||
cairo_surface_destroy(confine->buffer);
|
||||
confine->buffer = NULL;
|
||||
window_schedule_redraw(confine->window);
|
||||
break;
|
||||
case XKB_KEY_m:
|
||||
window_set_maximized(confine->window,
|
||||
!window_is_maximized(window));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
toggle_pointer_confine(struct confine *confine, struct input *input)
|
||||
{
|
||||
if (confine->pointer_confined) {
|
||||
window_unconfine_pointer(confine->window);
|
||||
} else if (confine->complex_confine_region_enabled) {
|
||||
calculate_complex_confine_region(confine);
|
||||
window_confine_pointer_to_rectangles(
|
||||
confine->window,
|
||||
input,
|
||||
confine->complex_confine_region,
|
||||
NUM_COMPLEX_REGION_RECTS);
|
||||
|
||||
} else {
|
||||
window_confine_pointer_to_widget(confine->window,
|
||||
confine->widget,
|
||||
input);
|
||||
}
|
||||
|
||||
confine->pointer_confined = !confine->pointer_confined;
|
||||
}
|
||||
|
||||
static void
|
||||
button_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t button,
|
||||
enum wl_pointer_button_state state, void *data)
|
||||
{
|
||||
struct confine *confine = data;
|
||||
bool is_pressed = state == WL_POINTER_BUTTON_STATE_PRESSED;
|
||||
|
||||
if (is_pressed && button == BTN_LEFT)
|
||||
toggle_pointer_confine(confine, input);
|
||||
widget_schedule_redraw(widget);
|
||||
}
|
||||
|
||||
static void
|
||||
cursor_timeout_reset(struct confine *confine)
|
||||
{
|
||||
toytimer_arm_once_usec(&confine->cursor_timeout, 500 * 1000);
|
||||
}
|
||||
|
||||
static int
|
||||
motion_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
float x, float y, void *data)
|
||||
{
|
||||
struct confine *confine = data;
|
||||
confine->line.x = x;
|
||||
confine->line.y = y;
|
||||
|
||||
window_schedule_redraw(confine->window);
|
||||
|
||||
cursor_timeout_reset(confine);
|
||||
confine->cursor_timeout_input = input;
|
||||
|
||||
return CURSOR_BLANK;
|
||||
}
|
||||
|
||||
static void
|
||||
resize_handler(struct widget *widget,
|
||||
int32_t width, int32_t height,
|
||||
void *data)
|
||||
{
|
||||
struct confine *confine = data;
|
||||
|
||||
confine->reset = 1;
|
||||
|
||||
if (confine->complex_confine_region_enabled) {
|
||||
confine->complex_confine_region_dirty = true;
|
||||
|
||||
if (confine->pointer_confined) {
|
||||
calculate_complex_confine_region(confine);
|
||||
window_update_confine_rectangles(
|
||||
confine->window,
|
||||
confine->complex_confine_region,
|
||||
NUM_COMPLEX_REGION_RECTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
leave_handler(struct widget *widget,
|
||||
struct input *input, void *data)
|
||||
{
|
||||
struct confine *confine = data;
|
||||
|
||||
confine->reset = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
cursor_timeout_func(struct toytimer *tt)
|
||||
{
|
||||
struct confine *confine =
|
||||
container_of(tt, struct confine, cursor_timeout);
|
||||
|
||||
input_set_pointer_image(confine->cursor_timeout_input,
|
||||
CURSOR_LEFT_PTR);
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_unconfined(struct window *window, struct input *input, void *data)
|
||||
{
|
||||
struct confine *confine = data;
|
||||
|
||||
confine->pointer_confined = false;
|
||||
}
|
||||
|
||||
static struct confine *
|
||||
confine_create(struct display *display)
|
||||
{
|
||||
struct confine *confine;
|
||||
|
||||
confine = xzalloc(sizeof *confine);
|
||||
confine->window = window_create(display);
|
||||
confine->widget = window_frame_create(confine->window, confine);
|
||||
window_set_title(confine->window, "Wayland Confine");
|
||||
confine->display = display;
|
||||
confine->buffer = NULL;
|
||||
|
||||
window_set_key_handler(confine->window, key_handler);
|
||||
window_set_user_data(confine->window, confine);
|
||||
window_set_keyboard_focus_handler(confine->window,
|
||||
keyboard_focus_handler);
|
||||
window_set_pointer_confined_handler(confine->window,
|
||||
NULL,
|
||||
pointer_unconfined);
|
||||
|
||||
widget_set_redraw_handler(confine->widget, redraw_handler);
|
||||
widget_set_button_handler(confine->widget, button_handler);
|
||||
widget_set_motion_handler(confine->widget, motion_handler);
|
||||
widget_set_resize_handler(confine->widget, resize_handler);
|
||||
widget_set_leave_handler(confine->widget, leave_handler);
|
||||
|
||||
widget_schedule_resize(confine->widget, 500, 400);
|
||||
confine->line.x = -1;
|
||||
confine->line.y = -1;
|
||||
confine->line.old_x = -1;
|
||||
confine->line.old_y = -1;
|
||||
confine->reset = 0;
|
||||
|
||||
toytimer_init(&confine->cursor_timeout, CLOCK_MONOTONIC,
|
||||
display, cursor_timeout_func);
|
||||
|
||||
return confine;
|
||||
}
|
||||
|
||||
static void
|
||||
confine_destroy(struct confine *confine)
|
||||
{
|
||||
toytimer_fini(&confine->cursor_timeout);
|
||||
if (confine->buffer)
|
||||
cairo_surface_destroy(confine->buffer);
|
||||
widget_destroy(confine->widget);
|
||||
window_destroy(confine->window);
|
||||
free(confine);
|
||||
}
|
||||
|
||||
static const struct weston_option confine_options[] = {
|
||||
{ WESTON_OPTION_BOOLEAN, "complex-confine-region", 0, &option_complex_confine_region },
|
||||
{ WESTON_OPTION_BOOLEAN, "help", 0, &option_help },
|
||||
};
|
||||
|
||||
static void
|
||||
print_help(const char *argv0)
|
||||
{
|
||||
printf("Usage: %s [--complex-confine-region]\n", argv0);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct display *display;
|
||||
struct confine *confine;
|
||||
|
||||
if (parse_options(confine_options,
|
||||
ARRAY_LENGTH(confine_options),
|
||||
&argc, argv) > 1 ||
|
||||
option_help) {
|
||||
print_help(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
display = display_create(&argc, argv);
|
||||
if (display == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
confine = confine_create(display);
|
||||
|
||||
if (option_complex_confine_region) {
|
||||
confine->complex_confine_region_dirty = true;
|
||||
confine->complex_confine_region_enabled = true;
|
||||
}
|
||||
|
||||
display_run(display);
|
||||
|
||||
confine_destroy(confine);
|
||||
display_destroy(display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -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 <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <cairo.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <wayland-client.h>
|
||||
#include "weston-content-protection-client-protocol.h"
|
||||
#include "window.h"
|
||||
#include <wayland-client-protocol.h>
|
||||
|
||||
#define WIDTH 500
|
||||
#define HEIGHT 400
|
||||
#define FRAME_H 18
|
||||
#define FRAME_W 5
|
||||
#define BUTTON_WIDTH 65
|
||||
#define BUTTON_HEIGHT 20
|
||||
|
||||
enum protection_mode {
|
||||
RELAXED,
|
||||
ENFORCED
|
||||
};
|
||||
|
||||
struct protected_content_player {
|
||||
struct weston_content_protection *protection;
|
||||
struct weston_protected_surface *psurface;
|
||||
struct display *display;
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
struct button_t *b0, *b1, *off, *enforced, *relaxed;
|
||||
int width, height, x, y;
|
||||
enum weston_protected_surface_type protection_type;
|
||||
enum protection_mode mode;
|
||||
};
|
||||
|
||||
struct button_t {
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
struct protected_content_player *pc_player;
|
||||
const char *name;
|
||||
};
|
||||
/**
|
||||
* An event to tell the client that there is a change in protection status
|
||||
*
|
||||
* This event is sent whenever there is a change in content
|
||||
* protection. The content protection status can be ON or OFF. ON
|
||||
* in case of the desired protection type is accepted on all
|
||||
* connectors, and Off in case of any of the connector
|
||||
* content-protection property is changed from "enabled"
|
||||
*/
|
||||
static void
|
||||
handle_status_changed(void *data, struct weston_protected_surface *psurface,
|
||||
uint32_t status)
|
||||
{
|
||||
struct protected_content_player *pc_player = data;
|
||||
enum weston_protected_surface_type event_status = status;
|
||||
|
||||
switch (event_status) {
|
||||
case WESTON_PROTECTED_SURFACE_TYPE_HDCP_0:
|
||||
pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_HDCP_0;
|
||||
break;
|
||||
case WESTON_PROTECTED_SURFACE_TYPE_HDCP_1:
|
||||
pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_HDCP_1;
|
||||
break;
|
||||
case WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED:
|
||||
default:
|
||||
pc_player->protection_type = WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED;
|
||||
}
|
||||
window_schedule_redraw(pc_player->window);
|
||||
}
|
||||
|
||||
static const struct weston_protected_surface_listener pc_player_listener = {
|
||||
handle_status_changed,
|
||||
};
|
||||
|
||||
static void
|
||||
draw_content(cairo_surface_t *surface, int x, int y, int width, int height,
|
||||
enum weston_protected_surface_type type, enum protection_mode mode)
|
||||
{
|
||||
cairo_t *cr;
|
||||
cairo_text_extents_t extents;
|
||||
const char *content_text;
|
||||
const char *mode_text;
|
||||
|
||||
cr = cairo_create(surface);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_rectangle(cr, x, y, width, height);
|
||||
if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_0)
|
||||
cairo_set_source_rgba(cr, 0, 1.0, 0, 1.0);
|
||||
else if (type == WESTON_PROTECTED_SURFACE_TYPE_HDCP_1)
|
||||
cairo_set_source_rgba(cr, 0, 0, 1.0, 1.0);
|
||||
else
|
||||
cairo_set_source_rgba(cr, 1.0, 0, 0, 1.0);
|
||||
cairo_fill(cr);
|
||||
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 1.0);
|
||||
cairo_select_font_face(cr, "Sans", 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,867 @@
|
|||
/*
|
||||
* Copyright © 2010 Kristian Høgsberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <sys/time.h>
|
||||
#include <cairo.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-cursor.h>
|
||||
|
||||
#include "window.h"
|
||||
#include "shared/cairo-util.h"
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/xalloc.h"
|
||||
|
||||
struct dnd_drag;
|
||||
|
||||
struct pointer {
|
||||
struct input *input;
|
||||
bool dragging;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct dnd {
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
struct display *display;
|
||||
uint32_t key;
|
||||
struct item *items[16];
|
||||
int self_only;
|
||||
struct dnd_drag *current_drag;
|
||||
struct wl_list pointers;
|
||||
};
|
||||
|
||||
struct dnd_drag {
|
||||
cairo_surface_t *translucent;
|
||||
cairo_surface_t *opaque;
|
||||
int hotspot_x, hotspot_y;
|
||||
struct dnd *dnd;
|
||||
struct input *input;
|
||||
uint32_t time;
|
||||
struct item *item;
|
||||
int x_offset, y_offset;
|
||||
int width, height;
|
||||
uint32_t dnd_action;
|
||||
const char *mime_type;
|
||||
|
||||
struct wl_surface *drag_surface;
|
||||
struct wl_data_source *data_source;
|
||||
};
|
||||
|
||||
struct item {
|
||||
cairo_surface_t *surface;
|
||||
int seed;
|
||||
int x, y;
|
||||
};
|
||||
|
||||
struct dnd_flower_message {
|
||||
int seed, x_offset, y_offset;
|
||||
};
|
||||
|
||||
|
||||
static const int item_width = 64;
|
||||
static const int item_height = 64;
|
||||
static const int item_padding = 16;
|
||||
|
||||
static const char flower_mime_type[] = "application/x-wayland-dnd-flower";
|
||||
static const char text_mime_type[] = "text/plain;charset=utf-8";
|
||||
|
||||
static struct item *
|
||||
item_create(struct display *display, int x, int y, int seed)
|
||||
{
|
||||
struct item *item;
|
||||
struct timeval tv;
|
||||
|
||||
item = malloc(sizeof *item);
|
||||
if (item == NULL)
|
||||
return NULL;
|
||||
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
item->seed = seed ? seed : tv.tv_usec;
|
||||
srandom(item->seed);
|
||||
|
||||
const int petal_count = 3 + random() % 5;
|
||||
const double r1 = 20 + random() % 10;
|
||||
const double r2 = 5 + random() % 12;
|
||||
const double u = (10 + random() % 90) / 100.0;
|
||||
const double v = (random() % 90) / 100.0;
|
||||
|
||||
cairo_t *cr;
|
||||
int i;
|
||||
double t, dt = 2 * M_PI / (petal_count * 2);
|
||||
double x1, y1, x2, y2, x3, y3;
|
||||
struct rectangle rect;
|
||||
|
||||
|
||||
rect.width = item_width;
|
||||
rect.height = item_height;
|
||||
item->surface =
|
||||
display_create_surface(display, NULL, &rect, SURFACE_SHM);
|
||||
|
||||
item->x = x;
|
||||
item->y = y;
|
||||
|
||||
cr = cairo_create(item->surface);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 0);
|
||||
cairo_paint(cr);
|
||||
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
|
||||
cairo_translate(cr, item_width / 2, item_height / 2);
|
||||
t = random();
|
||||
cairo_move_to(cr, cos(t) * r1, sin(t) * r1);
|
||||
for (i = 0; i < petal_count; i++, t += dt * 2) {
|
||||
x1 = cos(t) * r1;
|
||||
y1 = sin(t) * r1;
|
||||
x2 = cos(t + dt) * r2;
|
||||
y2 = sin(t + dt) * r2;
|
||||
x3 = cos(t + 2 * dt) * r1;
|
||||
y3 = sin(t + 2 * dt) * r1;
|
||||
|
||||
cairo_curve_to(cr,
|
||||
x1 - y1 * u, y1 + x1 * u,
|
||||
x2 + y2 * v, y2 - x2 * v,
|
||||
x2, y2);
|
||||
|
||||
cairo_curve_to(cr,
|
||||
x2 - y2 * v, y2 + x2 * v,
|
||||
x3 + y3 * u, y3 - x3 * u,
|
||||
x3, y3);
|
||||
}
|
||||
|
||||
cairo_close_path(cr);
|
||||
|
||||
cairo_set_source_rgba(cr,
|
||||
0.5 + (random() % 50) / 49.0,
|
||||
0.5 + (random() % 50) / 49.0,
|
||||
0.5 + (random() % 50) / 49.0,
|
||||
0.5 + (random() % 100) / 99.0);
|
||||
|
||||
cairo_fill_preserve(cr);
|
||||
|
||||
cairo_set_line_width(cr, 1);
|
||||
cairo_set_source_rgba(cr,
|
||||
0.5 + (random() % 50) / 49.0,
|
||||
0.5 + (random() % 50) / 49.0,
|
||||
0.5 + (random() % 50) / 49.0,
|
||||
0.5 + (random() % 100) / 99.0);
|
||||
cairo_stroke(cr);
|
||||
|
||||
cairo_destroy(cr);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static void
|
||||
dnd_redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct dnd *dnd = data;
|
||||
struct rectangle allocation;
|
||||
cairo_t *cr;
|
||||
cairo_surface_t *surface, *item_surface;
|
||||
unsigned int i;
|
||||
|
||||
surface = window_get_surface(dnd->window);
|
||||
cr = cairo_create(surface);
|
||||
widget_get_allocation(dnd->widget, &allocation);
|
||||
cairo_rectangle(cr, allocation.x, allocation.y,
|
||||
allocation.width, allocation.height);
|
||||
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
|
||||
cairo_fill(cr);
|
||||
|
||||
cairo_rectangle(cr, allocation.x, allocation.y,
|
||||
allocation.width, allocation.height);
|
||||
cairo_clip(cr);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
|
||||
for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
|
||||
if (!dnd->items[i])
|
||||
continue;
|
||||
|
||||
if (dnd->current_drag && dnd->items[i] == dnd->current_drag->item)
|
||||
item_surface = dnd->current_drag->translucent;
|
||||
else
|
||||
item_surface = dnd->items[i]->surface;
|
||||
|
||||
cairo_set_source_surface(cr, item_surface,
|
||||
dnd->items[i]->x + allocation.x,
|
||||
dnd->items[i]->y + allocation.y);
|
||||
cairo_paint(cr);
|
||||
}
|
||||
|
||||
cairo_destroy(cr);
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_focus_handler(struct window *window,
|
||||
struct input *device, void *data)
|
||||
{
|
||||
struct dnd *dnd = data;
|
||||
|
||||
window_schedule_redraw(dnd->window);
|
||||
}
|
||||
|
||||
static int
|
||||
dnd_add_item(struct dnd *dnd, struct item *item)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
|
||||
if (dnd->items[i] == 0) {
|
||||
dnd->items[i] = item;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct item *
|
||||
dnd_get_item(struct dnd *dnd, int32_t x, int32_t y)
|
||||
{
|
||||
struct item *item;
|
||||
struct rectangle allocation;
|
||||
unsigned int i;
|
||||
|
||||
widget_get_allocation(dnd->widget, &allocation);
|
||||
|
||||
x -= allocation.x;
|
||||
y -= allocation.y;
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
|
||||
item = dnd->items[i];
|
||||
if (item &&
|
||||
item->x <= x && x < item->x + item_width &&
|
||||
item->y <= y && y < item->y + item_height)
|
||||
return item;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
lookup_dnd_cursor(uint32_t dnd_action)
|
||||
{
|
||||
if (dnd_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
|
||||
return CURSOR_DND_MOVE;
|
||||
else if (dnd_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
|
||||
return CURSOR_DND_COPY;
|
||||
|
||||
return CURSOR_DND_FORBIDDEN;
|
||||
}
|
||||
|
||||
static void
|
||||
dnd_drag_update_cursor(struct dnd_drag *dnd_drag)
|
||||
{
|
||||
int cursor;
|
||||
|
||||
if (dnd_drag->mime_type == NULL)
|
||||
cursor = CURSOR_DND_FORBIDDEN;
|
||||
else
|
||||
cursor = lookup_dnd_cursor(dnd_drag->dnd_action);
|
||||
|
||||
input_set_pointer_image(dnd_drag->input, cursor);
|
||||
}
|
||||
|
||||
static void
|
||||
dnd_drag_update_surface(struct dnd_drag *dnd_drag)
|
||||
{
|
||||
struct dnd *dnd = dnd_drag->dnd;
|
||||
cairo_surface_t *surface;
|
||||
struct wl_buffer *buffer;
|
||||
|
||||
if (dnd_drag->mime_type && dnd_drag->dnd_action)
|
||||
surface = dnd_drag->opaque;
|
||||
else
|
||||
surface = dnd_drag->translucent;
|
||||
|
||||
buffer = display_get_buffer_for_surface(dnd->display, surface);
|
||||
wl_surface_attach(dnd_drag->drag_surface, buffer, 0, 0);
|
||||
wl_surface_damage(dnd_drag->drag_surface, 0, 0,
|
||||
dnd_drag->width, dnd_drag->height);
|
||||
wl_surface_commit(dnd_drag->drag_surface);
|
||||
}
|
||||
|
||||
static void
|
||||
data_source_target(void *data,
|
||||
struct wl_data_source *source, const char *mime_type)
|
||||
{
|
||||
struct dnd_drag *dnd_drag = data;
|
||||
|
||||
dnd_drag->mime_type = mime_type;
|
||||
dnd_drag_update_surface(dnd_drag);
|
||||
dnd_drag_update_cursor(dnd_drag);
|
||||
}
|
||||
|
||||
static void
|
||||
data_source_send(void *data, struct wl_data_source *source,
|
||||
const char *mime_type, int32_t fd)
|
||||
{
|
||||
struct dnd_flower_message dnd_flower_message;
|
||||
struct dnd_drag *dnd_drag = data;
|
||||
char buffer[128];
|
||||
int n;
|
||||
|
||||
if (strcmp(mime_type, flower_mime_type) == 0) {
|
||||
dnd_flower_message.seed = dnd_drag->item->seed;
|
||||
dnd_flower_message.x_offset = dnd_drag->x_offset;
|
||||
dnd_flower_message.y_offset = dnd_drag->y_offset;
|
||||
|
||||
if (write(fd, &dnd_flower_message,
|
||||
sizeof dnd_flower_message) < 0)
|
||||
abort();
|
||||
} else if (strcmp(mime_type, text_mime_type) == 0) {
|
||||
n = snprintf(buffer, sizeof buffer, "seed=%d x=%d y=%d\n",
|
||||
dnd_drag->item->seed,
|
||||
dnd_drag->x_offset,
|
||||
dnd_drag->y_offset);
|
||||
|
||||
if (write(fd, buffer, n) < 0)
|
||||
abort();
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void
|
||||
dnd_drag_destroy(struct dnd_drag *dnd_drag, bool delete_item)
|
||||
{
|
||||
struct dnd *dnd = dnd_drag->dnd;
|
||||
unsigned int i;
|
||||
|
||||
wl_data_source_destroy(dnd_drag->data_source);
|
||||
|
||||
if (delete_item) {
|
||||
for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
|
||||
if (dnd_drag->item == dnd->items[i]) {
|
||||
dnd->items[i] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Destroy the item that has been dragged out */
|
||||
cairo_surface_destroy(dnd_drag->item->surface);
|
||||
free(dnd_drag->item);
|
||||
}
|
||||
|
||||
dnd->current_drag = NULL;
|
||||
|
||||
wl_surface_destroy(dnd_drag->drag_surface);
|
||||
|
||||
cairo_surface_destroy(dnd_drag->translucent);
|
||||
cairo_surface_destroy(dnd_drag->opaque);
|
||||
free(dnd_drag);
|
||||
}
|
||||
|
||||
static void
|
||||
data_source_cancelled(void *data, struct wl_data_source *source)
|
||||
{
|
||||
struct dnd_drag *dnd_drag = data;
|
||||
struct dnd *dnd = dnd_drag->dnd;
|
||||
|
||||
/* The 'cancelled' event means that the source is no longer in
|
||||
* use by the drag (or current selection). We need to clean
|
||||
* up the drag object created and the local state. */
|
||||
dnd_drag_destroy(dnd_drag, false);
|
||||
window_schedule_redraw(dnd->window);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct dnd_drag *dnd_drag = data;
|
||||
struct dnd *dnd = dnd_drag->dnd;
|
||||
bool delete_item;
|
||||
|
||||
delete_item =
|
||||
dnd_drag->dnd_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
|
||||
|
||||
/* The operation is already finished, we can destroy all
|
||||
* related data.
|
||||
*/
|
||||
dnd_drag_destroy(dnd_drag, delete_item);
|
||||
window_schedule_redraw(dnd->window);
|
||||
}
|
||||
|
||||
static void
|
||||
data_source_action(void *data, struct wl_data_source *source, uint32_t dnd_action)
|
||||
{
|
||||
struct dnd_drag *dnd_drag = data;
|
||||
|
||||
dnd_drag->dnd_action = dnd_action;
|
||||
dnd_drag_update_surface(dnd_drag);
|
||||
dnd_drag_update_cursor(dnd_drag);
|
||||
}
|
||||
|
||||
static const struct wl_data_source_listener data_source_listener = {
|
||||
data_source_target,
|
||||
data_source_send,
|
||||
data_source_cancelled,
|
||||
data_source_dnd_drop_performed,
|
||||
data_source_dnd_finished,
|
||||
data_source_action,
|
||||
};
|
||||
|
||||
static cairo_surface_t *
|
||||
create_drag_icon(struct dnd_drag *dnd_drag,
|
||||
struct item *item, int32_t x, int32_t y, double opacity)
|
||||
{
|
||||
struct dnd *dnd = dnd_drag->dnd;
|
||||
cairo_surface_t *surface;
|
||||
struct rectangle rectangle;
|
||||
cairo_pattern_t *pattern;
|
||||
cairo_t *cr;
|
||||
|
||||
rectangle.width = item_width;
|
||||
rectangle.height = item_height;
|
||||
surface = display_create_surface(dnd->display, NULL, &rectangle,
|
||||
SURFACE_SHM);
|
||||
|
||||
cr = cairo_create(surface);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_surface(cr, item->surface, 0, 0);
|
||||
pattern = cairo_pattern_create_rgba(0, 0, 0, opacity);
|
||||
cairo_mask(cr, pattern);
|
||||
cairo_pattern_destroy(pattern);
|
||||
|
||||
cairo_destroy(cr);
|
||||
|
||||
dnd_drag->hotspot_x = x - item->x;
|
||||
dnd_drag->hotspot_y = y - item->y;
|
||||
dnd_drag->width = rectangle.width;
|
||||
dnd_drag->height = rectangle.height;
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
static int
|
||||
create_drag_source(struct dnd *dnd,
|
||||
struct input *input, uint32_t time,
|
||||
int32_t x, int32_t y)
|
||||
{
|
||||
struct item *item;
|
||||
struct rectangle allocation;
|
||||
struct dnd_drag *dnd_drag;
|
||||
struct display *display;
|
||||
struct wl_compositor *compositor;
|
||||
struct wl_buffer *buffer;
|
||||
unsigned int i;
|
||||
uint32_t serial;
|
||||
cairo_surface_t *icon;
|
||||
uint32_t actions;
|
||||
|
||||
widget_get_allocation(dnd->widget, &allocation);
|
||||
item = dnd_get_item(dnd, x, y);
|
||||
x -= allocation.x;
|
||||
y -= allocation.y;
|
||||
|
||||
if (item) {
|
||||
dnd_drag = xmalloc(sizeof *dnd_drag);
|
||||
dnd_drag->dnd = dnd;
|
||||
dnd_drag->input = input;
|
||||
dnd_drag->time = time;
|
||||
dnd_drag->item = item;
|
||||
dnd_drag->x_offset = x - item->x;
|
||||
dnd_drag->y_offset = y - item->y;
|
||||
dnd_drag->dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
|
||||
dnd_drag->mime_type = NULL;
|
||||
|
||||
actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE |
|
||||
WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
|
||||
|
||||
display = window_get_display(dnd->window);
|
||||
compositor = display_get_compositor(display);
|
||||
serial = display_get_serial(display);
|
||||
dnd_drag->drag_surface =
|
||||
wl_compositor_create_surface(compositor);
|
||||
|
||||
if (display_get_data_device_manager_version(display) <
|
||||
WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) {
|
||||
/* Data sources version < 3 will not get action
|
||||
* nor dnd_finished events, as we can't honor
|
||||
* the "move" action at the time of finishing
|
||||
* drag-and-drop, do it preemptively here.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
|
||||
if (item == dnd->items[i]){
|
||||
dnd->items[i] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dnd->self_only) {
|
||||
dnd_drag->data_source = NULL;
|
||||
} 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);
|
||||
wl_data_source_offer(dnd_drag->data_source,
|
||||
flower_mime_type);
|
||||
wl_data_source_offer(dnd_drag->data_source,
|
||||
text_mime_type);
|
||||
}
|
||||
|
||||
if (display_get_data_device_manager_version(display) >=
|
||||
WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) {
|
||||
wl_data_source_set_actions(dnd_drag->data_source, actions);
|
||||
}
|
||||
|
||||
wl_data_device_start_drag(input_get_data_device(input),
|
||||
dnd_drag->data_source,
|
||||
window_get_wl_surface(dnd->window),
|
||||
dnd_drag->drag_surface,
|
||||
serial);
|
||||
|
||||
dnd_drag->opaque =
|
||||
create_drag_icon(dnd_drag, item, x, y, 1);
|
||||
dnd_drag->translucent =
|
||||
create_drag_icon(dnd_drag, item, x, y, 0.2);
|
||||
|
||||
if (dnd->self_only)
|
||||
icon = dnd_drag->opaque;
|
||||
else
|
||||
icon = dnd_drag->translucent;
|
||||
|
||||
buffer = display_get_buffer_for_surface(dnd->display, icon);
|
||||
wl_surface_attach(dnd_drag->drag_surface, buffer,
|
||||
-dnd_drag->hotspot_x, -dnd_drag->hotspot_y);
|
||||
wl_surface_damage(dnd_drag->drag_surface, 0, 0,
|
||||
dnd_drag->width, dnd_drag->height);
|
||||
wl_surface_commit(dnd_drag->drag_surface);
|
||||
|
||||
dnd->current_drag = dnd_drag;
|
||||
window_schedule_redraw(dnd->window);
|
||||
|
||||
return 0;
|
||||
} else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
lookup_cursor(struct dnd *dnd, int x, int y)
|
||||
{
|
||||
struct item *item;
|
||||
|
||||
item = dnd_get_item(dnd, x, y);
|
||||
if (item)
|
||||
return CURSOR_HAND1;
|
||||
else
|
||||
return CURSOR_LEFT_PTR;
|
||||
}
|
||||
|
||||
/* Update all the mouse pointers in the window appropriately.
|
||||
* Optionally, skip one (which will be the current pointer just
|
||||
* about to start a drag). This is done here to save a scan
|
||||
* through the pointer list.
|
||||
*/
|
||||
static void
|
||||
update_pointer_images_except(struct dnd *dnd, struct input *except)
|
||||
{
|
||||
struct pointer *pointer;
|
||||
int32_t x, y;
|
||||
|
||||
wl_list_for_each(pointer, &dnd->pointers, link) {
|
||||
if (pointer->input == except) {
|
||||
pointer->dragging = true;
|
||||
continue;
|
||||
}
|
||||
input_get_position(pointer->input, &x, &y);
|
||||
input_set_pointer_image(pointer->input,
|
||||
lookup_cursor(dnd, x, y));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dnd_button_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t button, enum wl_pointer_button_state state,
|
||||
void *data)
|
||||
{
|
||||
struct dnd *dnd = data;
|
||||
int32_t x, y;
|
||||
|
||||
input_get_position(input, &x, &y);
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
input_ungrab(input);
|
||||
if (create_drag_source(dnd, input, time, x, y) == 0) {
|
||||
input_set_pointer_image(input, CURSOR_DRAGGING);
|
||||
update_pointer_images_except(dnd, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dnd_touch_down_handler(struct widget *widget,
|
||||
struct input *input, uint32_t serial,
|
||||
uint32_t time, int32_t id,
|
||||
float x, float y, void *data)
|
||||
{
|
||||
struct dnd *dnd = data;
|
||||
int32_t int_x, int_y;
|
||||
|
||||
if (id > 0)
|
||||
return;
|
||||
|
||||
int_x = (int32_t)x;
|
||||
int_y = (int32_t)y;
|
||||
if (create_drag_source(dnd, input, time, int_x, int_y) == 0)
|
||||
touch_grab(input, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
dnd_enter_handler(struct widget *widget,
|
||||
struct input *input, float x, float y, void *data)
|
||||
{
|
||||
struct dnd *dnd = data;
|
||||
struct pointer *new_pointer = malloc(sizeof *new_pointer);
|
||||
|
||||
if (new_pointer) {
|
||||
new_pointer->input = input;
|
||||
new_pointer->dragging = false;
|
||||
wl_list_insert(dnd->pointers.prev, &new_pointer->link);
|
||||
}
|
||||
|
||||
return lookup_cursor(dnd, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
dnd_leave_handler(struct widget *widget,
|
||||
struct input *input, void *data)
|
||||
{
|
||||
struct dnd *dnd = data;
|
||||
struct pointer *pointer, *tmp;
|
||||
|
||||
wl_list_for_each_safe(pointer, tmp, &dnd->pointers, link)
|
||||
if (pointer->input == input) {
|
||||
wl_list_remove(&pointer->link);
|
||||
free(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
dnd_motion_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
float x, float y, void *data)
|
||||
{
|
||||
struct dnd *dnd = data;
|
||||
struct pointer *pointer;
|
||||
|
||||
wl_list_for_each(pointer, &dnd->pointers, link)
|
||||
if (pointer->input == input) {
|
||||
if (pointer->dragging)
|
||||
return CURSOR_DRAGGING;
|
||||
break;
|
||||
}
|
||||
|
||||
return lookup_cursor(data, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
dnd_data_handler(struct window *window,
|
||||
struct input *input,
|
||||
float x, float y, const char **types, void *data)
|
||||
{
|
||||
struct dnd *dnd = data;
|
||||
int i, has_flower = 0;
|
||||
|
||||
if (!types)
|
||||
return;
|
||||
for (i = 0; types[i]; i++)
|
||||
if (strcmp(types[i], flower_mime_type) == 0)
|
||||
has_flower = 1;
|
||||
|
||||
if (dnd_get_item(dnd, x, y) || dnd->self_only || !has_flower) {
|
||||
input_accept(input, NULL);
|
||||
} else {
|
||||
input_accept(input, flower_mime_type);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dnd_receive_func(void *data, size_t len, int32_t x, int32_t y, void *user_data)
|
||||
{
|
||||
struct dnd *dnd = user_data;
|
||||
struct dnd_flower_message *message = data;
|
||||
struct item *item;
|
||||
struct rectangle allocation;
|
||||
|
||||
if (len == 0) {
|
||||
return;
|
||||
} else if (len != sizeof *message) {
|
||||
fprintf(stderr, "odd message length %zu, expected %zu\n",
|
||||
len, sizeof *message);
|
||||
return;
|
||||
}
|
||||
|
||||
widget_get_allocation(dnd->widget, &allocation);
|
||||
item = item_create(dnd->display,
|
||||
x - message->x_offset - allocation.x,
|
||||
y - message->y_offset - allocation.y,
|
||||
message->seed);
|
||||
|
||||
dnd_add_item(dnd, item);
|
||||
update_pointer_images_except(dnd, NULL);
|
||||
window_schedule_redraw(dnd->window);
|
||||
}
|
||||
|
||||
static void
|
||||
dnd_drop_handler(struct window *window, struct input *input,
|
||||
int32_t x, int32_t y, void *data)
|
||||
{
|
||||
struct dnd *dnd = data;
|
||||
struct dnd_flower_message message;
|
||||
|
||||
if (dnd_get_item(dnd, x, y)) {
|
||||
fprintf(stderr, "got 'drop', but no target\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dnd->self_only) {
|
||||
input_receive_drag_data(input,
|
||||
flower_mime_type,
|
||||
dnd_receive_func, dnd);
|
||||
} else if (dnd->current_drag) {
|
||||
message.seed = dnd->current_drag->item->seed;
|
||||
message.x_offset = dnd->current_drag->x_offset;
|
||||
message.y_offset = dnd->current_drag->y_offset;
|
||||
dnd_receive_func(&message, sizeof message, x, y, dnd);
|
||||
} else {
|
||||
fprintf(stderr, "ignoring drop from another client\n");
|
||||
}
|
||||
}
|
||||
|
||||
static struct dnd *
|
||||
dnd_create(struct display *display)
|
||||
{
|
||||
struct dnd *dnd;
|
||||
int x, y;
|
||||
int32_t width, height;
|
||||
unsigned int i;
|
||||
|
||||
dnd = xzalloc(sizeof *dnd);
|
||||
dnd->window = window_create(display);
|
||||
dnd->widget = window_frame_create(dnd->window, dnd);
|
||||
window_set_title(dnd->window, "Wayland Drag and Drop Demo");
|
||||
|
||||
dnd->display = display;
|
||||
dnd->key = 100;
|
||||
|
||||
wl_list_init(&dnd->pointers);
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
|
||||
x = (i % 4) * (item_width + item_padding) + item_padding;
|
||||
y = (i / 4) * (item_height + item_padding) + item_padding;
|
||||
if ((i ^ (i >> 2)) & 1)
|
||||
dnd->items[i] = item_create(display, x, y, 0);
|
||||
else
|
||||
dnd->items[i] = NULL;
|
||||
}
|
||||
|
||||
window_set_user_data(dnd->window, dnd);
|
||||
window_set_keyboard_focus_handler(dnd->window,
|
||||
keyboard_focus_handler);
|
||||
window_set_data_handler(dnd->window, dnd_data_handler);
|
||||
window_set_drop_handler(dnd->window, dnd_drop_handler);
|
||||
|
||||
widget_set_redraw_handler(dnd->widget, dnd_redraw_handler);
|
||||
widget_set_enter_handler(dnd->widget, dnd_enter_handler);
|
||||
widget_set_leave_handler(dnd->widget, dnd_leave_handler);
|
||||
widget_set_motion_handler(dnd->widget, dnd_motion_handler);
|
||||
widget_set_button_handler(dnd->widget, dnd_button_handler);
|
||||
widget_set_touch_down_handler(dnd->widget, dnd_touch_down_handler);
|
||||
|
||||
width = 4 * (item_width + item_padding) + item_padding;
|
||||
height = 4 * (item_height + item_padding) + item_padding;
|
||||
|
||||
window_frame_set_child_size(dnd->widget, width, height);
|
||||
|
||||
return dnd;
|
||||
}
|
||||
|
||||
static void
|
||||
dnd_destroy(struct dnd *dnd)
|
||||
{
|
||||
widget_destroy(dnd->widget);
|
||||
window_destroy(dnd->window);
|
||||
free(dnd);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct display *d;
|
||||
struct dnd *dnd;
|
||||
int self_only = 0;
|
||||
|
||||
if (argc == 2 && !strcmp(argv[1], "--self-only"))
|
||||
self_only = 1;
|
||||
else if (argc > 1) {
|
||||
printf("Usage: %s [OPTIONS]\n --self-only\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
d = display_create(&argc, argv);
|
||||
if (d == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
dnd = dnd_create(d);
|
||||
if (self_only)
|
||||
dnd->self_only = 1;
|
||||
|
||||
display_run(d);
|
||||
|
||||
dnd_destroy(dnd);
|
||||
display_destroy(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,541 @@
|
|||
/*
|
||||
* Copyright © 2011 Tim Wiederhake
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file eventdemo.c
|
||||
* \brief Demonstrate the use of Wayland's toytoolkit.
|
||||
*
|
||||
* Heavily commented demo program that can report all events that are
|
||||
* dispatched to the window. For other functionality, eg. opengl/egl,
|
||||
* drag and drop, etc. have a look at the other demos.
|
||||
* \author Tim Wiederhake
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <cairo.h>
|
||||
|
||||
#include "shared/helpers.h"
|
||||
#include "window.h"
|
||||
|
||||
/** window title */
|
||||
static char *title = "EventDemo";
|
||||
|
||||
/** window width */
|
||||
static int width = 500;
|
||||
|
||||
/** window height */
|
||||
static int height = 400;
|
||||
|
||||
/** set if window has no borders */
|
||||
static bool noborder = false;
|
||||
|
||||
/** if non-zero, maximum window width */
|
||||
static int width_max = 0;
|
||||
|
||||
/** if non-zero, maximum window height */
|
||||
static int height_max = 0;
|
||||
|
||||
/** set to log redrawing */
|
||||
static bool log_redraw = false;
|
||||
|
||||
/** set to log resizing */
|
||||
static bool log_resize = false;
|
||||
|
||||
/** set to log keyboard focus */
|
||||
static bool log_focus = false;
|
||||
|
||||
/** set to log key events */
|
||||
static bool log_key = false;
|
||||
|
||||
/** set to log button events */
|
||||
static bool log_button = false;
|
||||
|
||||
/** set to log axis events */
|
||||
static bool log_axis = false;
|
||||
|
||||
/** set to log motion events */
|
||||
static bool log_motion = false;
|
||||
|
||||
/**
|
||||
* \struct eventdemo
|
||||
* \brief Holds all data the program needs per window
|
||||
*
|
||||
* In this demo the struct holds the position of a
|
||||
* red rectangle that is drawn in the window's area.
|
||||
*/
|
||||
struct eventdemo {
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
struct display *display;
|
||||
|
||||
int x, y, w, h;
|
||||
|
||||
bool print_pointer_frame;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief CALLBACK function, Wayland requests the window to redraw.
|
||||
* \param widget widget to be redrawn
|
||||
* \param data user data associated to the window
|
||||
*
|
||||
* Draws a red rectangle as demonstration of per-window data.
|
||||
*/
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct eventdemo *e = data;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
struct rectangle rect;
|
||||
|
||||
if (log_redraw)
|
||||
printf("redraw\n");
|
||||
|
||||
widget_get_allocation(e->widget, &rect);
|
||||
surface = window_get_surface(e->window);
|
||||
|
||||
cr = cairo_create(surface);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
|
||||
cairo_rectangle(cr, rect.x, rect.y, rect.width, rect.height);
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
|
||||
cairo_fill(cr);
|
||||
|
||||
cairo_rectangle(cr, e->x, e->y, e->w, e->h);
|
||||
cairo_set_source_rgba(cr, 1.0, 0, 0, 1);
|
||||
cairo_fill(cr);
|
||||
|
||||
cairo_destroy(cr);
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief CALLBACK function, Wayland requests the window to resize.
|
||||
* \param widget widget to be resized
|
||||
* \param width desired width
|
||||
* \param height desired height
|
||||
* \param data user data associated to the window
|
||||
*/
|
||||
|
||||
static void
|
||||
resize_handler(struct widget *widget,
|
||||
int32_t width, int32_t height, void *data)
|
||||
{
|
||||
struct eventdemo *e = data;
|
||||
if (log_resize)
|
||||
printf("resize width: %d, height: %d\n", width, height);
|
||||
|
||||
/* if a maximum width is set, constrain to it */
|
||||
if (width_max && width_max < width)
|
||||
width = width_max;
|
||||
|
||||
/* if a maximum height is set, constrain to it */
|
||||
if (height_max && height_max < height)
|
||||
height = height_max;
|
||||
|
||||
/* set the new window dimensions */
|
||||
widget_set_size(e->widget, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief CALLBACK function, Wayland informs about keyboard focus change
|
||||
* \param window window
|
||||
* \param device device that caused the focus change
|
||||
* \param data user data associated to the window
|
||||
*/
|
||||
static void
|
||||
keyboard_focus_handler(struct window *window,
|
||||
struct input *device, void *data)
|
||||
{
|
||||
int32_t x, y;
|
||||
struct eventdemo *e = data;
|
||||
|
||||
if (log_focus) {
|
||||
if (device) {
|
||||
input_get_position(device, &x, &y);
|
||||
printf("focus x: %d, y: %d\n", x, y);
|
||||
} else {
|
||||
printf("focus lost\n");
|
||||
}
|
||||
}
|
||||
|
||||
window_schedule_redraw(e->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 data user data associated to the window
|
||||
*/
|
||||
static void
|
||||
key_handler(struct window *window, struct input *input, uint32_t time,
|
||||
uint32_t key, uint32_t unicode, enum wl_keyboard_key_state state,
|
||||
void *data)
|
||||
{
|
||||
uint32_t modifiers = input_get_modifiers(input);
|
||||
|
||||
if (!log_key)
|
||||
return;
|
||||
|
||||
printf("key key: %u, unicode: %u, state: %s, modifiers: 0x%x\n",
|
||||
key, unicode,
|
||||
(state == WL_KEYBOARD_KEY_STATE_PRESSED) ? "pressed" :
|
||||
"released",
|
||||
modifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief CALLBACK function, Wayland informs about button event
|
||||
* \param widget widget
|
||||
* \param input input device that caused the button event
|
||||
* \param time time the event happened
|
||||
* \param button button
|
||||
* \param state pressed or released
|
||||
* \param data user data associated to the window
|
||||
*/
|
||||
static void
|
||||
button_handler(struct widget *widget, struct input *input, uint32_t time,
|
||||
uint32_t button, enum wl_pointer_button_state state, void *data)
|
||||
{
|
||||
struct eventdemo *e = data;
|
||||
int32_t x, y;
|
||||
|
||||
if (!log_button)
|
||||
return;
|
||||
|
||||
e->print_pointer_frame = true;
|
||||
|
||||
input_get_position(input, &x, &y);
|
||||
printf("button time: %u, button: %u, state: %s, x: %d, y: %d\n",
|
||||
time, button,
|
||||
(state == WL_POINTER_BUTTON_STATE_PRESSED) ? "pressed" :
|
||||
"released",
|
||||
x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief CALLBACK function, Wayland informs about axis event
|
||||
* \param widget widget
|
||||
* \param input input device that caused the axis event
|
||||
* \param time time the event happened
|
||||
* \param axis vertical or horizontal
|
||||
* \param value amount of scrolling
|
||||
* \param data user data associated to the widget
|
||||
*/
|
||||
static void
|
||||
axis_handler(struct widget *widget, struct input *input, uint32_t time,
|
||||
uint32_t axis, wl_fixed_t value, void *data)
|
||||
{
|
||||
struct eventdemo *e = data;
|
||||
|
||||
if (!log_axis)
|
||||
return;
|
||||
|
||||
e->print_pointer_frame = true;
|
||||
|
||||
printf("axis time: %u, axis: %s, value: %f\n",
|
||||
time,
|
||||
axis == WL_POINTER_AXIS_VERTICAL_SCROLL ? "vertical" :
|
||||
"horizontal",
|
||||
wl_fixed_to_double(value));
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_frame_handler(struct widget *widget, struct input *input, void *data)
|
||||
{
|
||||
struct eventdemo *e = data;
|
||||
|
||||
if (!e->print_pointer_frame)
|
||||
return;
|
||||
|
||||
printf("pointer frame\n");
|
||||
e->print_pointer_frame = false;
|
||||
}
|
||||
|
||||
static void
|
||||
axis_source_handler(struct widget *widget, struct input *input,
|
||||
uint32_t source, void *data)
|
||||
{
|
||||
const char *axis_source;
|
||||
struct eventdemo *e = data;
|
||||
|
||||
if (!log_axis)
|
||||
return;
|
||||
|
||||
e->print_pointer_frame = true;
|
||||
|
||||
switch (source) {
|
||||
case WL_POINTER_AXIS_SOURCE_WHEEL:
|
||||
axis_source = "wheel";
|
||||
break;
|
||||
case WL_POINTER_AXIS_SOURCE_FINGER:
|
||||
axis_source = "finger";
|
||||
break;
|
||||
case WL_POINTER_AXIS_SOURCE_CONTINUOUS:
|
||||
axis_source = "continuous";
|
||||
break;
|
||||
default:
|
||||
axis_source = "<invalid source value>";
|
||||
break;
|
||||
}
|
||||
|
||||
printf("axis source: %s\n", axis_source);
|
||||
}
|
||||
|
||||
static void
|
||||
axis_stop_handler(struct widget *widget, struct input *input,
|
||||
uint32_t time, uint32_t axis,
|
||||
void *data)
|
||||
{
|
||||
struct eventdemo *e = data;
|
||||
|
||||
if (!log_axis)
|
||||
return;
|
||||
|
||||
e->print_pointer_frame = true;
|
||||
printf("axis stop time: %u, axis: %s\n",
|
||||
time,
|
||||
axis == WL_POINTER_AXIS_VERTICAL_SCROLL ? "vertical" :
|
||||
"horizontal");
|
||||
}
|
||||
|
||||
static void
|
||||
axis_discrete_handler(struct widget *widget, struct input *input,
|
||||
uint32_t axis, int32_t discrete, void *data)
|
||||
{
|
||||
struct eventdemo *e = data;
|
||||
|
||||
if (!log_axis)
|
||||
return;
|
||||
|
||||
e->print_pointer_frame = true;
|
||||
printf("axis discrete axis: %u value: %d\n", axis, discrete);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief CALLBACK function, Waylands informs about pointer motion
|
||||
* \param widget widget
|
||||
* \param input input device that caused the motion event
|
||||
* \param time time the event happened
|
||||
* \param x absolute x position
|
||||
* \param y absolute y position
|
||||
* \param x x position relative to the window
|
||||
* \param y y position relative to the window
|
||||
* \param data user data associated to the window
|
||||
*
|
||||
* Demonstrates the use of different cursors
|
||||
*/
|
||||
static int
|
||||
motion_handler(struct widget *widget, struct input *input, uint32_t time,
|
||||
float x, float y, void *data)
|
||||
{
|
||||
struct eventdemo *e = data;
|
||||
|
||||
if (log_motion) {
|
||||
printf("motion time: %u, x: %f, y: %f\n", time, x, y);
|
||||
e->print_pointer_frame = true;
|
||||
}
|
||||
|
||||
if (x > e->x && x < e->x + e->w)
|
||||
if (y > e->y && y < e->y + e->h)
|
||||
return CURSOR_HAND1;
|
||||
|
||||
return CURSOR_LEFT_PTR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Create and initialise a new eventdemo window.
|
||||
* The returned eventdemo instance should be destroyed using \c eventdemo_destroy().
|
||||
* \param d associated display
|
||||
*/
|
||||
static struct eventdemo *
|
||||
eventdemo_create(struct display *d)
|
||||
{
|
||||
struct eventdemo *e;
|
||||
|
||||
e = zalloc(sizeof (struct eventdemo));
|
||||
if (e == NULL)
|
||||
return NULL;
|
||||
|
||||
e->window = window_create(d);
|
||||
|
||||
if (noborder) {
|
||||
/* Demonstrate how to create a borderless window.
|
||||
* Move windows with META + left mouse button.
|
||||
*/
|
||||
e->widget = window_add_widget(e->window, e);
|
||||
} else {
|
||||
e->widget = window_frame_create(e->window, e);
|
||||
window_set_title(e->window, title);
|
||||
}
|
||||
e->display = d;
|
||||
|
||||
/* The eventdemo window draws a red rectangle as a demonstration
|
||||
* of per-window data. The dimensions of that rectangle are set
|
||||
* here.
|
||||
*/
|
||||
e->x = width * 1.0 / 4.0;
|
||||
e->w = width * 2.0 / 4.0;
|
||||
e->y = height * 1.0 / 4.0;
|
||||
e->h = height * 2.0 / 4.0;
|
||||
|
||||
/* Connect the user data to the window */
|
||||
window_set_user_data(e->window, e);
|
||||
|
||||
/* Set the callback redraw handler for the window */
|
||||
widget_set_redraw_handler(e->widget, redraw_handler);
|
||||
|
||||
/* Set the callback resize handler for the window */
|
||||
widget_set_resize_handler(e->widget, resize_handler);
|
||||
|
||||
/* Set the callback focus handler for the window */
|
||||
window_set_keyboard_focus_handler(e->window,
|
||||
keyboard_focus_handler);
|
||||
|
||||
/* Set the callback key handler for the window */
|
||||
window_set_key_handler(e->window, key_handler);
|
||||
|
||||
/* Set the callback button handler for the window */
|
||||
widget_set_button_handler(e->widget, button_handler);
|
||||
|
||||
/* Set the callback motion handler for the window */
|
||||
widget_set_motion_handler(e->widget, motion_handler);
|
||||
|
||||
/* Set the callback pointer frame handler for the window */
|
||||
widget_set_pointer_frame_handler(e->widget, pointer_frame_handler);
|
||||
|
||||
/* Set the callback axis handler for the window */
|
||||
widget_set_axis_handlers(e->widget,
|
||||
axis_handler,
|
||||
axis_source_handler,
|
||||
axis_stop_handler,
|
||||
axis_discrete_handler);
|
||||
|
||||
/* Initial drawing of the window */
|
||||
window_schedule_resize(e->window, width, height);
|
||||
|
||||
return e;
|
||||
}
|
||||
/**
|
||||
* \brief Destroy eventdemo instance previously created by \c eventdemo_create().
|
||||
* \param eventdemo eventdemo instance to destroy
|
||||
*/
|
||||
static void eventdemo_destroy(struct eventdemo * eventdemo)
|
||||
{
|
||||
widget_destroy(eventdemo->widget);
|
||||
window_destroy(eventdemo->window);
|
||||
free(eventdemo);
|
||||
}
|
||||
/**
|
||||
* \brief command line options for eventdemo
|
||||
*/
|
||||
static const struct weston_option eventdemo_options[] = {
|
||||
{ WESTON_OPTION_STRING, "title", 0, &title },
|
||||
{ WESTON_OPTION_INTEGER, "width", 'w', &width },
|
||||
{ WESTON_OPTION_INTEGER, "height", 'h', &height },
|
||||
{ WESTON_OPTION_INTEGER, "max-width", 0, &width_max },
|
||||
{ WESTON_OPTION_INTEGER, "max-height", 0, &height_max },
|
||||
{ WESTON_OPTION_BOOLEAN, "no-border", 'b', &noborder },
|
||||
{ WESTON_OPTION_BOOLEAN, "log-redraw", 0, &log_redraw },
|
||||
{ WESTON_OPTION_BOOLEAN, "log-resize", 0, &log_resize },
|
||||
{ WESTON_OPTION_BOOLEAN, "log-focus", 0, &log_focus },
|
||||
{ WESTON_OPTION_BOOLEAN, "log-key", 0, &log_key },
|
||||
{ WESTON_OPTION_BOOLEAN, "log-button", 0, &log_button },
|
||||
{ WESTON_OPTION_BOOLEAN, "log-axis", 0, &log_axis },
|
||||
{ WESTON_OPTION_BOOLEAN, "log-motion", 0, &log_motion },
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Connects to the display, creates the window and hands over
|
||||
* to the main loop.
|
||||
*/
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct display *d;
|
||||
struct eventdemo *e;
|
||||
|
||||
if (parse_options(eventdemo_options,
|
||||
ARRAY_LENGTH(eventdemo_options), &argc, argv) > 1) {
|
||||
unsigned k;
|
||||
printf("Usage: %s [OPTIONS]\n\n", argv[0]);
|
||||
for (k = 0; k < ARRAY_LENGTH(eventdemo_options); k++) {
|
||||
const struct weston_option* p = &eventdemo_options[k];
|
||||
if (p->name) {
|
||||
printf(" --%s", p->name);
|
||||
if (p->type != WESTON_OPTION_BOOLEAN)
|
||||
printf("=VALUE");
|
||||
putchar('\n');
|
||||
}
|
||||
if (p->short_name) {
|
||||
printf(" -%c", p->short_name);
|
||||
if (p->type != WESTON_OPTION_BOOLEAN)
|
||||
printf("VALUE");
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!log_redraw && !log_resize && !log_focus && !log_key &&
|
||||
!log_button && !log_axis && !log_motion)
|
||||
log_redraw = log_resize = log_focus = log_key =
|
||||
log_button = log_axis = log_motion = true;
|
||||
|
||||
/* Connect to the display and have the arguments parsed */
|
||||
d = display_create(&argc, argv);
|
||||
if (d == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create new eventdemo window */
|
||||
e = eventdemo_create(d);
|
||||
if (e == NULL) {
|
||||
fprintf(stderr, "failed to create eventdemo: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
display_run(d);
|
||||
|
||||
/* Release resources */
|
||||
eventdemo_destroy(e);
|
||||
display_destroy(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* Copyright © 2008 Kristian Høgsberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <cairo.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <wayland-client.h>
|
||||
#include "window.h"
|
||||
|
||||
struct flower {
|
||||
struct display *display;
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
int width, height;
|
||||
};
|
||||
|
||||
static void
|
||||
set_random_color(cairo_t *cr)
|
||||
{
|
||||
cairo_set_source_rgba(cr,
|
||||
0.5 + (random() % 50) / 49.0,
|
||||
0.5 + (random() % 50) / 49.0,
|
||||
0.5 + (random() % 50) / 49.0,
|
||||
0.5 + (random() % 100) / 99.0);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
draw_stuff(cairo_surface_t *surface, int width, int height)
|
||||
{
|
||||
const int petal_count = 3 + random() % 5;
|
||||
const double r1 = 60 + random() % 35;
|
||||
const double r2 = 20 + random() % 40;
|
||||
const double u = (10 + random() % 90) / 100.0;
|
||||
const double v = (random() % 90) / 100.0;
|
||||
|
||||
cairo_t *cr;
|
||||
int i;
|
||||
double t, dt = 2 * M_PI / (petal_count * 2);
|
||||
double x1, y1, x2, y2, x3, y3;
|
||||
|
||||
cr = cairo_create(surface);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 0);
|
||||
cairo_paint(cr);
|
||||
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
|
||||
cairo_translate(cr, width / 2, height / 2);
|
||||
cairo_move_to(cr, cos(0) * r1, sin(0) * r1);
|
||||
for (t = 0, i = 0; i < petal_count; i++, t += dt * 2) {
|
||||
x1 = cos(t) * r1;
|
||||
y1 = sin(t) * r1;
|
||||
x2 = cos(t + dt) * r2;
|
||||
y2 = sin(t + dt) * r2;
|
||||
x3 = cos(t + 2 * dt) * r1;
|
||||
y3 = sin(t + 2 * dt) * r1;
|
||||
|
||||
cairo_curve_to(cr,
|
||||
x1 - y1 * u, y1 + x1 * u,
|
||||
x2 + y2 * v, y2 - x2 * v,
|
||||
x2, y2);
|
||||
|
||||
cairo_curve_to(cr,
|
||||
x2 - y2 * v, y2 + x2 * v,
|
||||
x3 + y3 * u, y3 - x3 * u,
|
||||
x3, y3);
|
||||
}
|
||||
|
||||
cairo_close_path(cr);
|
||||
set_random_color(cr);
|
||||
cairo_fill_preserve(cr);
|
||||
set_random_color(cr);
|
||||
cairo_stroke(cr);
|
||||
|
||||
cairo_destroy(cr);
|
||||
}
|
||||
|
||||
static void
|
||||
resize_handler(struct widget *widget,
|
||||
int32_t width, int32_t height, void *data)
|
||||
{
|
||||
struct flower *flower = data;
|
||||
|
||||
/* Don't resize me */
|
||||
widget_set_size(flower->widget, flower->width, flower->height);
|
||||
}
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct flower *flower = data;
|
||||
cairo_surface_t *surface;
|
||||
|
||||
surface = window_get_surface(flower->window);
|
||||
if (surface == NULL ||
|
||||
cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "failed to create cairo egl surface\n");
|
||||
return;
|
||||
}
|
||||
|
||||
draw_stuff(surface, flower->width, flower->height);
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
button_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t button, enum wl_pointer_button_state state, void *data)
|
||||
{
|
||||
struct flower *flower = data;
|
||||
|
||||
switch (button) {
|
||||
case BTN_LEFT:
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
window_move(flower->window, input,
|
||||
display_get_serial(flower->display));
|
||||
break;
|
||||
case BTN_MIDDLE:
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
widget_schedule_redraw(widget);
|
||||
break;
|
||||
case BTN_RIGHT:
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
window_show_frame_menu(flower->window, input, time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
touch_down_handler(struct widget *widget, struct input *input,
|
||||
uint32_t serial, uint32_t time, int32_t id,
|
||||
float x, float y, void *data)
|
||||
{
|
||||
struct flower *flower = data;
|
||||
window_move(flower->window, input, display_get_serial(flower->display));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct flower flower;
|
||||
struct display *d;
|
||||
struct timeval tv;
|
||||
|
||||
d = display_create(&argc, argv);
|
||||
if (d == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
srandom(tv.tv_usec);
|
||||
|
||||
flower.width = 200;
|
||||
flower.height = 200;
|
||||
flower.display = d;
|
||||
flower.window = window_create(d);
|
||||
flower.widget = window_add_widget(flower.window, &flower);
|
||||
window_set_title(flower.window, "Flower");
|
||||
|
||||
widget_set_resize_handler(flower.widget, resize_handler);
|
||||
widget_set_redraw_handler(flower.widget, redraw_handler);
|
||||
widget_set_button_handler(flower.widget, button_handler);
|
||||
widget_set_default_cursor(flower.widget, CURSOR_HAND1);
|
||||
widget_set_touch_down_handler(flower.widget, touch_down_handler);
|
||||
|
||||
window_schedule_resize(flower.window, flower.width, flower.height);
|
||||
|
||||
display_run(d);
|
||||
|
||||
widget_destroy(flower.widget);
|
||||
window_destroy(flower.window);
|
||||
display_destroy(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,581 @@
|
|||
/*
|
||||
* Copyright © 2008 Kristian Høgsberg
|
||||
* Copyright © 2012 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include <cairo.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <wayland-client.h>
|
||||
#include "window.h"
|
||||
#include "fullscreen-shell-unstable-v1-client-protocol.h"
|
||||
#include <libweston/zalloc.h>
|
||||
|
||||
struct fs_output {
|
||||
struct wl_list link;
|
||||
struct output *output;
|
||||
};
|
||||
|
||||
struct fullscreen {
|
||||
struct display *display;
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
struct zwp_fullscreen_shell_v1 *fshell;
|
||||
enum zwp_fullscreen_shell_v1_present_method present_method;
|
||||
int width, height;
|
||||
int fullscreen;
|
||||
float pointer_x, pointer_y;
|
||||
int draw_cursor;
|
||||
|
||||
struct wl_list output_list;
|
||||
struct fs_output *current_output;
|
||||
};
|
||||
|
||||
static void
|
||||
fullscreen_handler(struct window *window, void *data)
|
||||
{
|
||||
struct fullscreen *fullscreen = data;
|
||||
|
||||
fullscreen->fullscreen ^= 1;
|
||||
window_set_fullscreen(window, fullscreen->fullscreen);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_string(cairo_t *cr,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
char buffer[4096];
|
||||
char *p, *end;
|
||||
va_list argp;
|
||||
cairo_text_extents_t text_extents;
|
||||
cairo_font_extents_t font_extents;
|
||||
|
||||
cairo_save(cr);
|
||||
|
||||
cairo_select_font_face(cr, "sans",
|
||||
CAIRO_FONT_SLANT_NORMAL,
|
||||
CAIRO_FONT_WEIGHT_NORMAL);
|
||||
cairo_set_font_size(cr, 14);
|
||||
|
||||
cairo_font_extents (cr, &font_extents);
|
||||
|
||||
va_start(argp, fmt);
|
||||
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, argp);
|
||||
|
||||
p = buffer;
|
||||
while (*p) {
|
||||
end = strchr(p, '\n');
|
||||
if (end)
|
||||
*end = 0;
|
||||
|
||||
cairo_show_text(cr, p);
|
||||
cairo_text_extents (cr, p, &text_extents);
|
||||
cairo_rel_move_to (cr, -text_extents.x_advance, font_extents.height);
|
||||
|
||||
if (end)
|
||||
p = end + 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
va_end(argp);
|
||||
|
||||
cairo_restore(cr);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct fullscreen *fullscreen = data;
|
||||
struct rectangle allocation;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
int i;
|
||||
double x, y, border;
|
||||
const char *method_name[] = { "default", "center", "zoom", "zoom_crop", "stretch"};
|
||||
|
||||
surface = window_get_surface(fullscreen->window);
|
||||
if (surface == NULL ||
|
||||
cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "failed to create cairo egl surface\n");
|
||||
return;
|
||||
}
|
||||
|
||||
widget_get_allocation(fullscreen->widget, &allocation);
|
||||
|
||||
cr = widget_cairo_create(widget);
|
||||
|
||||
cairo_set_source_rgb(cr, 0, 0, 0);
|
||||
cairo_paint (cr);
|
||||
|
||||
cairo_set_source_rgb(cr, 0, 0, 1);
|
||||
cairo_set_line_width (cr, 10);
|
||||
cairo_rectangle(cr, 5, 5, allocation.width - 10, allocation.height - 10);
|
||||
cairo_stroke (cr);
|
||||
|
||||
cairo_move_to(cr,
|
||||
allocation.x + 15,
|
||||
allocation.y + 25);
|
||||
cairo_set_source_rgb(cr, 1, 1, 1);
|
||||
|
||||
if (fullscreen->fshell) {
|
||||
draw_string(cr,
|
||||
"Surface size: %d, %d\n"
|
||||
"Scale: %d, transform: %d\n"
|
||||
"Pointer: %f,%f\n"
|
||||
"Output: %s, present method: %s\n"
|
||||
"Keys: (s)cale, (t)ransform, si(z)e, (m)ethod,\n"
|
||||
" (o)utput, modes(w)itch, (q)uit\n",
|
||||
fullscreen->width, fullscreen->height,
|
||||
window_get_buffer_scale (fullscreen->window),
|
||||
window_get_buffer_transform (fullscreen->window),
|
||||
fullscreen->pointer_x, fullscreen->pointer_y,
|
||||
method_name[fullscreen->present_method],
|
||||
fullscreen->current_output ? output_get_model(fullscreen->current_output->output): "null");
|
||||
} else {
|
||||
draw_string(cr,
|
||||
"Surface size: %d, %d\n"
|
||||
"Scale: %d, transform: %d\n"
|
||||
"Pointer: %f,%f\n"
|
||||
"Fullscreen: %d\n"
|
||||
"Keys: (s)cale, (t)ransform, si(z)e, (f)ullscreen, (q)uit\n",
|
||||
fullscreen->width, fullscreen->height,
|
||||
window_get_buffer_scale (fullscreen->window),
|
||||
window_get_buffer_transform (fullscreen->window),
|
||||
fullscreen->pointer_x, fullscreen->pointer_y,
|
||||
fullscreen->fullscreen);
|
||||
}
|
||||
|
||||
y = 100;
|
||||
i = 0;
|
||||
while (y + 60 < fullscreen->height) {
|
||||
border = (i++ % 2 == 0) ? 1 : 0.5;
|
||||
|
||||
x = 50;
|
||||
cairo_set_line_width (cr, border);
|
||||
while (x + 70 < fullscreen->width) {
|
||||
if (window_has_focus(fullscreen->window) &&
|
||||
fullscreen->pointer_x >= x && fullscreen->pointer_x < x + 50 &&
|
||||
fullscreen->pointer_y >= y && fullscreen->pointer_y < y + 40) {
|
||||
cairo_set_source_rgb(cr, 1, 0, 0);
|
||||
cairo_rectangle(cr,
|
||||
x, y,
|
||||
50, 40);
|
||||
cairo_fill(cr);
|
||||
}
|
||||
cairo_set_source_rgb(cr, 0, 1, 0);
|
||||
cairo_rectangle(cr,
|
||||
x + border/2.0, y + border/2.0,
|
||||
50, 40);
|
||||
cairo_stroke(cr);
|
||||
x += 60;
|
||||
}
|
||||
|
||||
y += 50;
|
||||
}
|
||||
|
||||
if (window_has_focus(fullscreen->window) && fullscreen->draw_cursor) {
|
||||
cairo_set_source_rgb(cr, 1, 1, 1);
|
||||
cairo_set_line_width (cr, 8);
|
||||
cairo_move_to(cr,
|
||||
fullscreen->pointer_x - 12,
|
||||
fullscreen->pointer_y - 12);
|
||||
cairo_line_to(cr,
|
||||
fullscreen->pointer_x + 12,
|
||||
fullscreen->pointer_y + 12);
|
||||
cairo_stroke(cr);
|
||||
|
||||
cairo_move_to(cr,
|
||||
fullscreen->pointer_x + 12,
|
||||
fullscreen->pointer_y - 12);
|
||||
cairo_line_to(cr,
|
||||
fullscreen->pointer_x - 12,
|
||||
fullscreen->pointer_y + 12);
|
||||
cairo_stroke(cr);
|
||||
|
||||
cairo_set_source_rgb(cr, 0, 0, 0);
|
||||
cairo_set_line_width (cr, 4);
|
||||
cairo_move_to(cr,
|
||||
fullscreen->pointer_x - 10,
|
||||
fullscreen->pointer_y - 10);
|
||||
cairo_line_to(cr,
|
||||
fullscreen->pointer_x + 10,
|
||||
fullscreen->pointer_y + 10);
|
||||
cairo_stroke(cr);
|
||||
|
||||
cairo_move_to(cr,
|
||||
fullscreen->pointer_x + 10,
|
||||
fullscreen->pointer_y - 10);
|
||||
cairo_line_to(cr,
|
||||
fullscreen->pointer_x - 10,
|
||||
fullscreen->pointer_y + 10);
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
|
||||
cairo_destroy(cr);
|
||||
}
|
||||
|
||||
static void
|
||||
key_handler(struct window *window, struct input *input, uint32_t time,
|
||||
uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
|
||||
void *data)
|
||||
{
|
||||
struct fullscreen *fullscreen = data;
|
||||
int transform, scale;
|
||||
static int current_size = 0;
|
||||
struct fs_output *fsout;
|
||||
struct wl_output *wl_output;
|
||||
int widths[] = { 640, 320, 800, 400 };
|
||||
int heights[] = { 480, 240, 600, 300 };
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
|
||||
return;
|
||||
|
||||
switch (sym) {
|
||||
case XKB_KEY_t:
|
||||
transform = window_get_buffer_transform (window);
|
||||
transform = (transform + 1) % 8;
|
||||
window_set_buffer_transform(window, transform);
|
||||
window_schedule_redraw(window);
|
||||
break;
|
||||
|
||||
case XKB_KEY_s:
|
||||
scale = window_get_buffer_scale (window);
|
||||
if (scale == 1)
|
||||
scale = 2;
|
||||
else
|
||||
scale = 1;
|
||||
window_set_buffer_scale(window, scale);
|
||||
window_schedule_redraw(window);
|
||||
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];
|
||||
window_schedule_resize(fullscreen->window,
|
||||
fullscreen->width, fullscreen->height);
|
||||
break;
|
||||
|
||||
case XKB_KEY_m:
|
||||
if (!fullscreen->fshell)
|
||||
break;
|
||||
|
||||
wl_output = NULL;
|
||||
if (fullscreen->current_output)
|
||||
wl_output = output_get_wl_output(fullscreen->current_output->output);
|
||||
fullscreen->present_method = (fullscreen->present_method + 1) % 5;
|
||||
zwp_fullscreen_shell_v1_present_surface(fullscreen->fshell,
|
||||
window_get_wl_surface(fullscreen->window),
|
||||
fullscreen->present_method,
|
||||
wl_output);
|
||||
window_schedule_redraw(window);
|
||||
break;
|
||||
|
||||
case XKB_KEY_o:
|
||||
if (!fullscreen->fshell)
|
||||
break;
|
||||
|
||||
fsout = fullscreen->current_output;
|
||||
wl_output = fsout ? output_get_wl_output(fsout->output) : NULL;
|
||||
|
||||
/* Clear the current presentation */
|
||||
zwp_fullscreen_shell_v1_present_surface(fullscreen->fshell, NULL,
|
||||
0, wl_output);
|
||||
|
||||
if (fullscreen->current_output) {
|
||||
if (fullscreen->current_output->link.next == &fullscreen->output_list)
|
||||
fsout = NULL;
|
||||
else
|
||||
fsout = wl_container_of(fullscreen->current_output->link.next,
|
||||
fsout, link);
|
||||
} else {
|
||||
fsout = wl_container_of(fullscreen->output_list.next,
|
||||
fsout, link);
|
||||
}
|
||||
|
||||
fullscreen->current_output = fsout;
|
||||
wl_output = fsout ? output_get_wl_output(fsout->output) : NULL;
|
||||
zwp_fullscreen_shell_v1_present_surface(fullscreen->fshell,
|
||||
window_get_wl_surface(fullscreen->window),
|
||||
fullscreen->present_method,
|
||||
wl_output);
|
||||
window_schedule_redraw(window);
|
||||
break;
|
||||
|
||||
case XKB_KEY_w:
|
||||
if (!fullscreen->fshell || !fullscreen->current_output)
|
||||
break;
|
||||
|
||||
wl_output = NULL;
|
||||
if (fullscreen->current_output)
|
||||
wl_output = output_get_wl_output(fullscreen->current_output->output);
|
||||
zwp_fullscreen_shell_mode_feedback_v1_destroy(
|
||||
zwp_fullscreen_shell_v1_present_surface_for_mode(fullscreen->fshell,
|
||||
window_get_wl_surface(fullscreen->window),
|
||||
wl_output, 0));
|
||||
window_schedule_redraw(window);
|
||||
break;
|
||||
|
||||
case XKB_KEY_f:
|
||||
if (fullscreen->fshell)
|
||||
break;
|
||||
fullscreen->fullscreen ^= 1;
|
||||
window_set_fullscreen(window, fullscreen->fullscreen);
|
||||
break;
|
||||
|
||||
case XKB_KEY_q:
|
||||
exit (0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
motion_handler(struct widget *widget,
|
||||
struct input *input,
|
||||
uint32_t time,
|
||||
float x,
|
||||
float y, void *data)
|
||||
{
|
||||
struct fullscreen *fullscreen = data;
|
||||
|
||||
fullscreen->pointer_x = x;
|
||||
fullscreen->pointer_y = y;
|
||||
|
||||
widget_schedule_redraw(widget);
|
||||
|
||||
return fullscreen->draw_cursor ? CURSOR_BLANK : CURSOR_LEFT_PTR;
|
||||
}
|
||||
|
||||
static int
|
||||
enter_handler(struct widget *widget,
|
||||
struct input *input,
|
||||
float x, float y, void *data)
|
||||
{
|
||||
struct fullscreen *fullscreen = data;
|
||||
|
||||
fullscreen->pointer_x = x;
|
||||
fullscreen->pointer_y = y;
|
||||
|
||||
widget_schedule_redraw(widget);
|
||||
|
||||
return fullscreen->draw_cursor ? CURSOR_BLANK : CURSOR_LEFT_PTR;
|
||||
}
|
||||
|
||||
static void
|
||||
button_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t button, enum wl_pointer_button_state state, void *data)
|
||||
{
|
||||
struct fullscreen *fullscreen = data;
|
||||
|
||||
switch (button) {
|
||||
case BTN_LEFT:
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
window_move(fullscreen->window, input,
|
||||
display_get_serial(fullscreen->display));
|
||||
break;
|
||||
case BTN_RIGHT:
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
window_show_frame_menu(fullscreen->window, input, time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
touch_handler(struct widget *widget, struct input *input,
|
||||
uint32_t serial, uint32_t time, int32_t id,
|
||||
float x, float y, void *data)
|
||||
{
|
||||
struct fullscreen *fullscreen = data;
|
||||
window_move(fullscreen->window, input, display_get_serial(fullscreen->display));
|
||||
}
|
||||
|
||||
static void
|
||||
fshell_capability_handler(void *data, struct zwp_fullscreen_shell_v1 *fshell,
|
||||
uint32_t capability)
|
||||
{
|
||||
struct fullscreen *fullscreen = data;
|
||||
|
||||
switch (capability) {
|
||||
case ZWP_FULLSCREEN_SHELL_V1_CAPABILITY_CURSOR_PLANE:
|
||||
fullscreen->draw_cursor = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct zwp_fullscreen_shell_v1_listener fullscreen_shell_listener = {
|
||||
fshell_capability_handler
|
||||
};
|
||||
|
||||
static void
|
||||
usage(int error_code)
|
||||
{
|
||||
fprintf(stderr, "Usage: fullscreen [OPTIONS]\n\n"
|
||||
" -w <width>\tSet window width to <width>\n"
|
||||
" -h <height>\tSet window height to <height>\n"
|
||||
" --help\tShow this help text\n\n");
|
||||
|
||||
exit(error_code);
|
||||
}
|
||||
|
||||
static void
|
||||
output_handler(struct output *output, void *data)
|
||||
{
|
||||
struct fullscreen *fullscreen = data;
|
||||
struct fs_output *fsout;
|
||||
|
||||
/* If we've already seen this one, don't add it to the list */
|
||||
wl_list_for_each(fsout, &fullscreen->output_list, link)
|
||||
if (fsout->output == output)
|
||||
return;
|
||||
|
||||
fsout = zalloc(sizeof *fsout);
|
||||
if (fsout == NULL) {
|
||||
fprintf(stderr, "out of memory in output_handler\n");
|
||||
return;
|
||||
}
|
||||
fsout->output = output;
|
||||
wl_list_insert(&fullscreen->output_list, &fsout->link);
|
||||
}
|
||||
|
||||
static void
|
||||
global_handler(struct display *display, uint32_t id, const char *interface,
|
||||
uint32_t version, void *data)
|
||||
{
|
||||
struct fullscreen *fullscreen = data;
|
||||
|
||||
if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
|
||||
fullscreen->fshell = display_bind(display, id,
|
||||
&zwp_fullscreen_shell_v1_interface,
|
||||
1);
|
||||
zwp_fullscreen_shell_v1_add_listener(fullscreen->fshell,
|
||||
&fullscreen_shell_listener,
|
||||
fullscreen);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct fullscreen fullscreen;
|
||||
struct display *d;
|
||||
int i;
|
||||
|
||||
fullscreen.width = 640;
|
||||
fullscreen.height = 480;
|
||||
fullscreen.fullscreen = 0;
|
||||
fullscreen.present_method = ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT;
|
||||
wl_list_init(&fullscreen.output_list);
|
||||
fullscreen.current_output = NULL;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-w") == 0) {
|
||||
if (++i >= argc)
|
||||
usage(EXIT_FAILURE);
|
||||
|
||||
fullscreen.width = atol(argv[i]);
|
||||
} else if (strcmp(argv[i], "-h") == 0) {
|
||||
if (++i >= argc)
|
||||
usage(EXIT_FAILURE);
|
||||
|
||||
fullscreen.height = atol(argv[i]);
|
||||
} else if (strcmp(argv[i], "--help") == 0)
|
||||
usage(EXIT_SUCCESS);
|
||||
else
|
||||
usage(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
d = display_create(&argc, argv);
|
||||
if (d == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fullscreen.display = d;
|
||||
fullscreen.fshell = NULL;
|
||||
display_set_user_data(fullscreen.display, &fullscreen);
|
||||
display_set_global_handler(fullscreen.display, global_handler);
|
||||
display_set_output_configure_handler(fullscreen.display, output_handler);
|
||||
|
||||
if (fullscreen.fshell) {
|
||||
fullscreen.window = window_create_custom(d);
|
||||
zwp_fullscreen_shell_v1_present_surface(fullscreen.fshell,
|
||||
window_get_wl_surface(fullscreen.window),
|
||||
fullscreen.present_method,
|
||||
NULL);
|
||||
/* If we get the CURSOR_PLANE capability, we'll change this */
|
||||
fullscreen.draw_cursor = 1;
|
||||
} else {
|
||||
fullscreen.window = window_create(d);
|
||||
fullscreen.draw_cursor = 0;
|
||||
}
|
||||
|
||||
fullscreen.widget =
|
||||
window_add_widget(fullscreen.window, &fullscreen);
|
||||
|
||||
window_set_title(fullscreen.window, "Fullscreen");
|
||||
|
||||
widget_set_transparent(fullscreen.widget, 0);
|
||||
|
||||
widget_set_default_cursor(fullscreen.widget, CURSOR_LEFT_PTR);
|
||||
widget_set_redraw_handler(fullscreen.widget, redraw_handler);
|
||||
widget_set_button_handler(fullscreen.widget, button_handler);
|
||||
widget_set_motion_handler(fullscreen.widget, motion_handler);
|
||||
widget_set_enter_handler(fullscreen.widget, enter_handler);
|
||||
|
||||
widget_set_touch_down_handler(fullscreen.widget, touch_handler);
|
||||
|
||||
window_set_key_handler(fullscreen.window, key_handler);
|
||||
window_set_fullscreen_handler(fullscreen.window, fullscreen_handler);
|
||||
|
||||
window_set_user_data(fullscreen.window, &fullscreen);
|
||||
/* Hack to set minimum allocation so we can shrink later */
|
||||
window_schedule_resize(fullscreen.window,
|
||||
1, 1);
|
||||
window_schedule_resize(fullscreen.window,
|
||||
fullscreen.width, fullscreen.height);
|
||||
|
||||
display_run(d);
|
||||
|
||||
widget_destroy(fullscreen.widget);
|
||||
window_destroy(fullscreen.window);
|
||||
display_destroy(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,503 @@
|
|||
/*
|
||||
* Copyright © 2008 Kristian Høgsberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "window.h"
|
||||
|
||||
struct gears {
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
|
||||
struct display *d;
|
||||
|
||||
EGLDisplay display;
|
||||
EGLDisplay config;
|
||||
EGLContext context;
|
||||
GLfloat angle;
|
||||
|
||||
struct {
|
||||
GLfloat rotx;
|
||||
GLfloat roty;
|
||||
} view;
|
||||
|
||||
int button_down;
|
||||
int last_x, last_y;
|
||||
|
||||
GLint gear_list[3];
|
||||
int fullscreen;
|
||||
int frames;
|
||||
uint32_t last_fps;
|
||||
};
|
||||
|
||||
struct gear_template {
|
||||
GLfloat material[4];
|
||||
GLfloat inner_radius;
|
||||
GLfloat outer_radius;
|
||||
GLfloat width;
|
||||
GLint teeth;
|
||||
GLfloat tooth_depth;
|
||||
};
|
||||
|
||||
static const struct gear_template gear_templates[] = {
|
||||
{ { 0.8, 0.1, 0.0, 1.0 }, 1.0, 4.0, 1.0, 20, 0.7 },
|
||||
{ { 0.0, 0.8, 0.2, 1.0 }, 0.5, 2.0, 2.0, 10, 0.7 },
|
||||
{ { 0.2, 0.2, 1.0, 1.0 }, 1.3, 2.0, 0.5, 10, 0.7 },
|
||||
};
|
||||
|
||||
static GLfloat light_pos[4] = {5.0, 5.0, 10.0, 0.0};
|
||||
|
||||
static void die(const char *msg)
|
||||
{
|
||||
fprintf(stderr, "%s", msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void
|
||||
make_gear(const struct gear_template *t)
|
||||
{
|
||||
GLint i;
|
||||
GLfloat r0, r1, r2;
|
||||
GLfloat angle, da;
|
||||
GLfloat u, v, len;
|
||||
|
||||
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, t->material);
|
||||
|
||||
r0 = t->inner_radius;
|
||||
r1 = t->outer_radius - t->tooth_depth / 2.0;
|
||||
r2 = t->outer_radius + t->tooth_depth / 2.0;
|
||||
|
||||
da = 2.0 * M_PI / t->teeth / 4.0;
|
||||
|
||||
glShadeModel(GL_FLAT);
|
||||
|
||||
glNormal3f(0.0, 0.0, 1.0);
|
||||
|
||||
/* draw front face */
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
for (i = 0; i <= t->teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / t->teeth;
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
|
||||
if (i < t->teeth) {
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
|
||||
/* draw front sides of teeth */
|
||||
glBegin(GL_QUADS);
|
||||
da = 2.0 * M_PI / t->teeth / 4.0;
|
||||
for (i = 0; i < t->teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / t->teeth;
|
||||
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5);
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
glNormal3f(0.0, 0.0, -1.0);
|
||||
|
||||
/* draw back face */
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
for (i = 0; i <= t->teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / t->teeth;
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
|
||||
if (i < t->teeth) {
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
|
||||
/* draw back sides of teeth */
|
||||
glBegin(GL_QUADS);
|
||||
da = 2.0 * M_PI / t->teeth / 4.0;
|
||||
for (i = 0; i < t->teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / t->teeth;
|
||||
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5);
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
/* draw outward faces of teeth */
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
for (i = 0; i < t->teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / t->teeth;
|
||||
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
|
||||
u = r2 * cos(angle + da) - r1 * cos(angle);
|
||||
v = r2 * sin(angle + da) - r1 * sin(angle);
|
||||
len = sqrt(u * u + v * v);
|
||||
u /= len;
|
||||
v /= len;
|
||||
glNormal3f(v, -u, 0.0);
|
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5);
|
||||
glNormal3f(cos(angle), sin(angle), 0.0);
|
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5);
|
||||
u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
|
||||
v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
|
||||
glNormal3f(v, -u, 0.0);
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
|
||||
glNormal3f(cos(angle), sin(angle), 0.0);
|
||||
}
|
||||
|
||||
glVertex3f(r1 * cos(0), r1 * sin(0), t->width * 0.5);
|
||||
glVertex3f(r1 * cos(0), r1 * sin(0), -t->width * 0.5);
|
||||
|
||||
glEnd();
|
||||
|
||||
glShadeModel(GL_SMOOTH);
|
||||
|
||||
/* draw inside radius cylinder */
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
for (i = 0; i <= t->teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / t->teeth;
|
||||
glNormal3f(-cos(angle), -sin(angle), 0.0);
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
static void
|
||||
update_fps(struct gears *gears, uint32_t time)
|
||||
{
|
||||
long diff_ms;
|
||||
static bool first_call = true;
|
||||
|
||||
if (first_call) {
|
||||
gears->last_fps = time;
|
||||
first_call = false;
|
||||
} else
|
||||
gears->frames++;
|
||||
|
||||
diff_ms = time - gears->last_fps;
|
||||
|
||||
if (diff_ms > 5000) {
|
||||
float seconds = diff_ms / 1000.0;
|
||||
float fps = gears->frames / seconds;
|
||||
|
||||
printf("%d frames in %6.3f seconds = %6.3f FPS\n", gears->frames, seconds, fps);
|
||||
fflush(stdout);
|
||||
|
||||
gears->frames = 0;
|
||||
gears->last_fps = time;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
frame_callback(void *data, struct wl_callback *callback, uint32_t time)
|
||||
{
|
||||
struct gears *gears = data;
|
||||
|
||||
update_fps(gears, time);
|
||||
|
||||
gears->angle = (GLfloat) (time % 8192) * 360 / 8192.0;
|
||||
|
||||
window_schedule_redraw(gears->window);
|
||||
|
||||
if (callback)
|
||||
wl_callback_destroy(callback);
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener listener = {
|
||||
frame_callback
|
||||
};
|
||||
|
||||
static int
|
||||
motion_handler(struct widget *widget, struct input *input,
|
||||
uint32_t time, float x, float y, void *data)
|
||||
{
|
||||
struct gears *gears = data;
|
||||
int offset_x, offset_y;
|
||||
float step = 0.5;
|
||||
|
||||
if (gears->button_down) {
|
||||
offset_x = x - gears->last_x;
|
||||
offset_y = y - gears->last_y;
|
||||
gears->last_x = x;
|
||||
gears->last_y = y;
|
||||
gears->view.roty += offset_x * step;
|
||||
gears->view.rotx += offset_y * step;
|
||||
if (gears->view.roty >= 360)
|
||||
gears->view.roty = gears->view.roty - 360;
|
||||
if (gears->view.roty <= 0)
|
||||
gears->view.roty = gears->view.roty + 360;
|
||||
if (gears->view.rotx >= 360)
|
||||
gears->view.rotx = gears->view.rotx - 360;
|
||||
if (gears->view.rotx <= 0)
|
||||
gears->view.rotx = gears->view.rotx + 360;
|
||||
}
|
||||
|
||||
return CURSOR_LEFT_PTR;
|
||||
}
|
||||
|
||||
static void
|
||||
button_handler(struct widget *widget, struct input *input,
|
||||
uint32_t time, uint32_t button,
|
||||
enum wl_pointer_button_state state, void *data)
|
||||
{
|
||||
struct gears *gears = data;
|
||||
|
||||
if (button == BTN_LEFT) {
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
gears->button_down = 1;
|
||||
input_get_position(input,
|
||||
&gears->last_x, &gears->last_y);
|
||||
} else {
|
||||
gears->button_down = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct rectangle window_allocation;
|
||||
struct rectangle allocation;
|
||||
struct wl_callback *callback;
|
||||
struct gears *gears = data;
|
||||
|
||||
widget_get_allocation(gears->widget, &allocation);
|
||||
window_get_allocation(gears->window, &window_allocation);
|
||||
|
||||
if (display_acquire_window_surface(gears->d,
|
||||
gears->window,
|
||||
gears->context) < 0) {
|
||||
die("Unable to acquire window surface, "
|
||||
"compiled without cairo-egl?\n");
|
||||
}
|
||||
|
||||
glViewport(allocation.x,
|
||||
window_allocation.height - allocation.height - allocation.y,
|
||||
allocation.width, allocation.height);
|
||||
glScissor(allocation.x,
|
||||
window_allocation.height - allocation.height - allocation.y,
|
||||
allocation.width, allocation.height);
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glPushMatrix();
|
||||
|
||||
glTranslatef(0.0, 0.0, -50);
|
||||
|
||||
glRotatef(gears->view.rotx, 1.0, 0.0, 0.0);
|
||||
glRotatef(gears->view.roty, 0.0, 1.0, 0.0);
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(-3.0, -2.0, 0.0);
|
||||
glRotatef(gears->angle, 0.0, 0.0, 1.0);
|
||||
glCallList(gears->gear_list[0]);
|
||||
glPopMatrix();
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(3.1, -2.0, 0.0);
|
||||
glRotatef(-2.0 * gears->angle - 9.0, 0.0, 0.0, 1.0);
|
||||
glCallList(gears->gear_list[1]);
|
||||
glPopMatrix();
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(-3.1, 4.2, 0.0);
|
||||
glRotatef(-2.0 * gears->angle - 25.0, 0.0, 0.0, 1.0);
|
||||
glCallList(gears->gear_list[2]);
|
||||
glPopMatrix();
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glFlush();
|
||||
|
||||
display_release_window_surface(gears->d, gears->window);
|
||||
|
||||
callback = wl_surface_frame(window_get_wl_surface(gears->window));
|
||||
wl_callback_add_listener(callback, &listener, gears);
|
||||
}
|
||||
|
||||
static void
|
||||
resize_handler(struct widget *widget,
|
||||
int32_t width, int32_t height, void *data)
|
||||
{
|
||||
struct gears *gears = data;
|
||||
int32_t size, big, small;
|
||||
|
||||
/* Constrain child size to be square and at least 300x300 */
|
||||
if (width < height) {
|
||||
small = width;
|
||||
big = height;
|
||||
} else {
|
||||
small = height;
|
||||
big = width;
|
||||
}
|
||||
|
||||
if (gears->fullscreen)
|
||||
size = small;
|
||||
else
|
||||
size = big;
|
||||
|
||||
widget_set_size(gears->widget, size, size);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_focus_handler(struct window *window,
|
||||
struct input *device, void *data)
|
||||
{
|
||||
window_schedule_redraw(window);
|
||||
}
|
||||
|
||||
static void
|
||||
fullscreen_handler(struct window *window, void *data)
|
||||
{
|
||||
struct gears *gears = data;
|
||||
|
||||
gears->fullscreen ^= 1;
|
||||
window_set_fullscreen(window, gears->fullscreen);
|
||||
}
|
||||
|
||||
static struct gears *
|
||||
gears_create(struct display *display)
|
||||
{
|
||||
const int width = 450, height = 500;
|
||||
struct gears *gears;
|
||||
int i;
|
||||
|
||||
gears = zalloc(sizeof *gears);
|
||||
gears->d = display;
|
||||
gears->window = window_create(display);
|
||||
gears->widget = window_frame_create(gears->window, gears);
|
||||
window_set_title(gears->window, "Wayland Gears");
|
||||
|
||||
gears->display = display_get_egl_display(gears->d);
|
||||
if (gears->display == NULL)
|
||||
die("failed to create egl display\n");
|
||||
|
||||
eglBindAPI(EGL_OPENGL_API);
|
||||
|
||||
gears->config = display_get_argb_egl_config(gears->d);
|
||||
|
||||
gears->context = eglCreateContext(gears->display, gears->config,
|
||||
EGL_NO_CONTEXT, NULL);
|
||||
if (gears->context == NULL)
|
||||
die("failed to create context\n");
|
||||
|
||||
if (!eglMakeCurrent(gears->display, NULL, NULL, gears->context))
|
||||
die("failed to make context current\n");
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
gears->gear_list[i] = glGenLists(1);
|
||||
glNewList(gears->gear_list[i], GL_COMPILE);
|
||||
make_gear(&gear_templates[i]);
|
||||
glEndList();
|
||||
}
|
||||
|
||||
gears->button_down = 0;
|
||||
gears->last_x = 0;
|
||||
gears->last_y = 0;
|
||||
|
||||
gears->view.rotx = 20.0;
|
||||
gears->view.roty = 30.0;
|
||||
|
||||
printf("Warning: FPS count is limited by the wayland compositor or monitor refresh rate\n");
|
||||
|
||||
glEnable(GL_NORMALIZE);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 200.0);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_LIGHT0);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glClearColor(0, 0, 0, 0.92);
|
||||
|
||||
window_set_user_data(gears->window, gears);
|
||||
widget_set_resize_handler(gears->widget, resize_handler);
|
||||
widget_set_redraw_handler(gears->widget, redraw_handler);
|
||||
widget_set_button_handler(gears->widget, button_handler);
|
||||
widget_set_motion_handler(gears->widget, motion_handler);
|
||||
window_set_keyboard_focus_handler(gears->window,
|
||||
keyboard_focus_handler);
|
||||
window_set_fullscreen_handler(gears->window, fullscreen_handler);
|
||||
|
||||
window_schedule_resize(gears->window, width, height);
|
||||
|
||||
return gears;
|
||||
}
|
||||
|
||||
static void
|
||||
gears_destroy(struct gears *gears)
|
||||
{
|
||||
widget_destroy(gears->widget);
|
||||
window_destroy(gears->window);
|
||||
free(gears);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct display *d;
|
||||
struct gears *gears;
|
||||
|
||||
d = display_create(&argc, argv);
|
||||
if (d == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
gears = gears_create(d);
|
||||
display_run(d);
|
||||
|
||||
gears_destroy(gears);
|
||||
display_destroy(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,437 @@
|
|||
/*
|
||||
* Copyright © 2008 Kristian Høgsberg
|
||||
* Copyright © 2009 Chris Wilson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <cairo.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "window.h"
|
||||
#include "shared/cairo-util.h"
|
||||
|
||||
struct image {
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
struct display *display;
|
||||
char *filename;
|
||||
cairo_surface_t *image;
|
||||
int fullscreen;
|
||||
int *image_counter;
|
||||
int32_t width, height;
|
||||
|
||||
struct {
|
||||
double x;
|
||||
double y;
|
||||
} pointer;
|
||||
bool button_pressed;
|
||||
|
||||
bool initialized;
|
||||
cairo_matrix_t matrix;
|
||||
};
|
||||
|
||||
static double
|
||||
get_scale(struct image *image)
|
||||
{
|
||||
assert(image->matrix.xy == 0.0 &&
|
||||
image->matrix.yx == 0.0 &&
|
||||
image->matrix.xx == image->matrix.yy);
|
||||
return image->matrix.xx;
|
||||
}
|
||||
|
||||
static void
|
||||
clamp_view(struct image *image)
|
||||
{
|
||||
struct rectangle allocation;
|
||||
double scale = get_scale(image);
|
||||
double sw, sh;
|
||||
|
||||
sw = image->width * scale;
|
||||
sh = image->height * scale;
|
||||
widget_get_allocation(image->widget, &allocation);
|
||||
|
||||
if (sw < allocation.width) {
|
||||
image->matrix.x0 =
|
||||
(allocation.width - image->width * scale) / 2;
|
||||
} else {
|
||||
if (image->matrix.x0 > 0.0)
|
||||
image->matrix.x0 = 0.0;
|
||||
if (sw + image->matrix.x0 < allocation.width)
|
||||
image->matrix.x0 = allocation.width - sw;
|
||||
}
|
||||
|
||||
if (sh < allocation.height) {
|
||||
image->matrix.y0 =
|
||||
(allocation.height - image->height * scale) / 2;
|
||||
} else {
|
||||
if (image->matrix.y0 > 0.0)
|
||||
image->matrix.y0 = 0.0;
|
||||
if (sh + image->matrix.y0 < allocation.height)
|
||||
image->matrix.y0 = allocation.height - sh;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct image *image = data;
|
||||
struct rectangle allocation;
|
||||
cairo_t *cr;
|
||||
cairo_surface_t *surface;
|
||||
double width, height, doc_aspect, window_aspect, scale;
|
||||
cairo_matrix_t matrix;
|
||||
cairo_matrix_t translate;
|
||||
|
||||
surface = window_get_surface(image->window);
|
||||
cr = cairo_create(surface);
|
||||
widget_get_allocation(image->widget, &allocation);
|
||||
cairo_rectangle(cr, allocation.x, allocation.y,
|
||||
allocation.width, allocation.height);
|
||||
cairo_clip(cr);
|
||||
cairo_push_group(cr);
|
||||
cairo_translate(cr, allocation.x, allocation.y);
|
||||
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 1);
|
||||
cairo_paint(cr);
|
||||
|
||||
if (!image->initialized) {
|
||||
image->initialized = true;
|
||||
width = cairo_image_surface_get_width(image->image);
|
||||
height = cairo_image_surface_get_height(image->image);
|
||||
|
||||
doc_aspect = width / height;
|
||||
window_aspect = (double) allocation.width / allocation.height;
|
||||
if (doc_aspect < window_aspect)
|
||||
scale = allocation.height / height;
|
||||
else
|
||||
scale = allocation.width / width;
|
||||
|
||||
image->width = width;
|
||||
image->height = height;
|
||||
cairo_matrix_init_scale(&image->matrix, scale, scale);
|
||||
|
||||
clamp_view(image);
|
||||
}
|
||||
|
||||
matrix = image->matrix;
|
||||
cairo_matrix_init_translate(&translate, allocation.x, allocation.y);
|
||||
cairo_matrix_multiply(&matrix, &matrix, &translate);
|
||||
cairo_set_matrix(cr, &matrix);
|
||||
|
||||
cairo_set_source_surface(cr, image->image, 0, 0);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
|
||||
cairo_paint(cr);
|
||||
|
||||
cairo_pop_group_to_source(cr);
|
||||
cairo_paint(cr);
|
||||
cairo_destroy(cr);
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
resize_handler(struct widget *widget,
|
||||
int32_t width, int32_t height, void *data)
|
||||
{
|
||||
struct image *image = data;
|
||||
|
||||
clamp_view(image);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_focus_handler(struct window *window,
|
||||
struct input *device, void *data)
|
||||
{
|
||||
struct image *image = data;
|
||||
|
||||
window_schedule_redraw(image->window);
|
||||
}
|
||||
|
||||
static int
|
||||
enter_handler(struct widget *widget,
|
||||
struct input *input,
|
||||
float x, float y, void *data)
|
||||
{
|
||||
struct image *image = data;
|
||||
struct rectangle allocation;
|
||||
|
||||
widget_get_allocation(image->widget, &allocation);
|
||||
x -= allocation.x;
|
||||
y -= allocation.y;
|
||||
|
||||
image->pointer.x = x;
|
||||
image->pointer.y = y;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
move_viewport(struct image *image, double dx, double dy)
|
||||
{
|
||||
double scale = get_scale(image);
|
||||
|
||||
if (!image->initialized)
|
||||
return;
|
||||
|
||||
cairo_matrix_translate(&image->matrix, -dx/scale, -dy/scale);
|
||||
clamp_view(image);
|
||||
|
||||
window_schedule_redraw(image->window);
|
||||
}
|
||||
|
||||
static int
|
||||
motion_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
float x, float y, void *data)
|
||||
{
|
||||
struct image *image = data;
|
||||
struct rectangle allocation;
|
||||
|
||||
widget_get_allocation(image->widget, &allocation);
|
||||
x -= allocation.x;
|
||||
y -= allocation.y;
|
||||
|
||||
if (image->button_pressed)
|
||||
move_viewport(image, image->pointer.x - x,
|
||||
image->pointer.y - y);
|
||||
|
||||
image->pointer.x = x;
|
||||
image->pointer.y = y;
|
||||
|
||||
return image->button_pressed ? CURSOR_DRAGGING : CURSOR_LEFT_PTR;
|
||||
}
|
||||
|
||||
static void
|
||||
button_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t button,
|
||||
enum wl_pointer_button_state state,
|
||||
void *data)
|
||||
{
|
||||
struct image *image = data;
|
||||
|
||||
if (button == BTN_LEFT) {
|
||||
image->button_pressed =
|
||||
state == WL_POINTER_BUTTON_STATE_PRESSED;
|
||||
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
input_set_pointer_image(input, CURSOR_DRAGGING);
|
||||
else
|
||||
input_set_pointer_image(input, CURSOR_LEFT_PTR);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
zoom(struct image *image, double scale)
|
||||
{
|
||||
double x = image->pointer.x;
|
||||
double y = image->pointer.y;
|
||||
cairo_matrix_t scale_matrix;
|
||||
|
||||
if (!image->initialized)
|
||||
return;
|
||||
|
||||
if (get_scale(image) * scale > 20.0 ||
|
||||
get_scale(image) * scale < 0.02)
|
||||
return;
|
||||
|
||||
cairo_matrix_init_identity(&scale_matrix);
|
||||
cairo_matrix_translate(&scale_matrix, x, y);
|
||||
cairo_matrix_scale(&scale_matrix, scale, scale);
|
||||
cairo_matrix_translate(&scale_matrix, -x, -y);
|
||||
|
||||
cairo_matrix_multiply(&image->matrix, &image->matrix, &scale_matrix);
|
||||
clamp_view(image);
|
||||
}
|
||||
|
||||
static void
|
||||
key_handler(struct window *window, struct input *input, uint32_t time,
|
||||
uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
|
||||
void *data)
|
||||
{
|
||||
struct image *image = data;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
|
||||
return;
|
||||
|
||||
switch (sym) {
|
||||
case XKB_KEY_minus:
|
||||
zoom(image, 0.8);
|
||||
window_schedule_redraw(image->window);
|
||||
break;
|
||||
case XKB_KEY_equal:
|
||||
case XKB_KEY_plus:
|
||||
zoom(image, 1.2);
|
||||
window_schedule_redraw(image->window);
|
||||
break;
|
||||
case XKB_KEY_1:
|
||||
image->matrix.xx = 1.0;
|
||||
image->matrix.xy = 0.0;
|
||||
image->matrix.yx = 0.0;
|
||||
image->matrix.yy = 1.0;
|
||||
clamp_view(image);
|
||||
window_schedule_redraw(image->window);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
axis_handler(struct widget *widget, struct input *input, uint32_t time,
|
||||
uint32_t axis, wl_fixed_t value, void *data)
|
||||
{
|
||||
struct image *image = data;
|
||||
|
||||
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL &&
|
||||
input_get_modifiers(input) == MOD_CONTROL_MASK) {
|
||||
/* set zoom level to 2% per 10 axis units */
|
||||
zoom(image, (1.0 - wl_fixed_to_double(value) / 500.0));
|
||||
|
||||
window_schedule_redraw(image->window);
|
||||
} else if (input_get_modifiers(input) == 0) {
|
||||
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
|
||||
move_viewport(image, 0, wl_fixed_to_double(value));
|
||||
else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
|
||||
move_viewport(image, wl_fixed_to_double(value), 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fullscreen_handler(struct window *window, void *data)
|
||||
{
|
||||
struct image *image = data;
|
||||
|
||||
image->fullscreen ^= 1;
|
||||
window_set_fullscreen(window, image->fullscreen);
|
||||
}
|
||||
|
||||
static void
|
||||
close_handler(void *data)
|
||||
{
|
||||
struct image *image = data;
|
||||
|
||||
*image->image_counter -= 1;
|
||||
|
||||
if (*image->image_counter == 0)
|
||||
display_exit(image->display);
|
||||
|
||||
widget_destroy(image->widget);
|
||||
window_destroy(image->window);
|
||||
|
||||
free(image);
|
||||
}
|
||||
|
||||
static struct image *
|
||||
image_create(struct display *display, const char *filename,
|
||||
int *image_counter)
|
||||
{
|
||||
struct image *image;
|
||||
char *b, *copy, title[512];
|
||||
|
||||
image = zalloc(sizeof *image);
|
||||
if (image == NULL)
|
||||
return image;
|
||||
|
||||
copy = strdup(filename);
|
||||
b = basename(copy);
|
||||
snprintf(title, sizeof title, "Wayland Image - %s", b);
|
||||
free(copy);
|
||||
|
||||
image->filename = strdup(filename);
|
||||
image->image = load_cairo_surface(filename);
|
||||
|
||||
if (!image->image) {
|
||||
free(image->filename);
|
||||
free(image);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image->window = window_create(display);
|
||||
image->widget = window_frame_create(image->window, image);
|
||||
window_set_title(image->window, title);
|
||||
image->display = display;
|
||||
image->image_counter = image_counter;
|
||||
*image_counter += 1;
|
||||
image->initialized = false;
|
||||
|
||||
window_set_user_data(image->window, image);
|
||||
widget_set_redraw_handler(image->widget, redraw_handler);
|
||||
widget_set_resize_handler(image->widget, resize_handler);
|
||||
window_set_keyboard_focus_handler(image->window,
|
||||
keyboard_focus_handler);
|
||||
window_set_fullscreen_handler(image->window, fullscreen_handler);
|
||||
window_set_close_handler(image->window, close_handler);
|
||||
|
||||
widget_set_enter_handler(image->widget, enter_handler);
|
||||
widget_set_motion_handler(image->widget, motion_handler);
|
||||
widget_set_button_handler(image->widget, button_handler);
|
||||
widget_set_axis_handler(image->widget, axis_handler);
|
||||
window_set_key_handler(image->window, key_handler);
|
||||
widget_schedule_resize(image->widget, 500, 400);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct display *d;
|
||||
int i;
|
||||
int image_counter = 0;
|
||||
|
||||
if (argc <= 1 || argv[1][0]=='-') {
|
||||
printf("Usage: %s image...\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
d = display_create(&argc, argv);
|
||||
if (d == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
image_create(d, argv[i], &image_counter);
|
||||
|
||||
if (image_counter > 0)
|
||||
display_run(d);
|
||||
|
||||
display_destroy(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,591 @@
|
|||
/*
|
||||
* Copyright © 2011 Benjamin Franzke
|
||||
* Copyright © 2010, 2013 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <poll.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include "shared/os-compatibility.h"
|
||||
#include "shared/xalloc.h"
|
||||
#include <libweston/zalloc.h>
|
||||
|
||||
struct device {
|
||||
enum { KEYBOARD, POINTER } type;
|
||||
|
||||
int start_time;
|
||||
int end_time;
|
||||
struct wl_list link;
|
||||
|
||||
union {
|
||||
struct wl_keyboard *keyboard;
|
||||
struct wl_pointer *pointer;
|
||||
} p;
|
||||
};
|
||||
|
||||
struct display {
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
struct wl_shell *shell;
|
||||
struct wl_seat *seat;
|
||||
struct wl_shm *shm;
|
||||
uint32_t formats;
|
||||
struct wl_list devices;
|
||||
};
|
||||
|
||||
struct window {
|
||||
struct display *display;
|
||||
int width, height;
|
||||
struct wl_surface *surface;
|
||||
struct wl_shell_surface *shell_surface;
|
||||
};
|
||||
|
||||
static void
|
||||
buffer_release(void *data, struct wl_buffer *buffer)
|
||||
{
|
||||
wl_buffer_destroy(buffer);
|
||||
}
|
||||
|
||||
static const struct wl_buffer_listener buffer_listener = {
|
||||
buffer_release
|
||||
};
|
||||
|
||||
static int
|
||||
attach_buffer(struct window *window, int width, int height)
|
||||
{
|
||||
struct wl_shm_pool *pool;
|
||||
struct wl_buffer *buffer;
|
||||
int fd, size, stride;
|
||||
|
||||
stride = width * 4;
|
||||
size = stride * height;
|
||||
|
||||
fd = os_create_anonymous_file(size);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
|
||||
size, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
pool = wl_shm_create_pool(window->display->shm, fd, size);
|
||||
buffer = wl_shm_pool_create_buffer(pool, 0,
|
||||
width, height,
|
||||
stride,
|
||||
WL_SHM_FORMAT_XRGB8888);
|
||||
wl_surface_attach(window->surface, buffer, 0, 0);
|
||||
wl_buffer_add_listener(buffer, &buffer_listener, buffer);
|
||||
wl_shm_pool_destroy(pool);
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_ping(void *data, struct wl_shell_surface *shell_surface,
|
||||
uint32_t serial)
|
||||
{
|
||||
wl_shell_surface_pong(shell_surface, serial);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_configure(void *data, struct wl_shell_surface *shell_surface,
|
||||
uint32_t edges, int32_t width, int32_t height)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_shell_surface_listener shell_surface_listener = {
|
||||
handle_ping,
|
||||
handle_configure,
|
||||
handle_popup_done
|
||||
};
|
||||
|
||||
static struct window *
|
||||
create_window(struct display *display, int width, int height)
|
||||
{
|
||||
struct window *window;
|
||||
|
||||
window = xzalloc(sizeof *window);
|
||||
window->display = display;
|
||||
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);
|
||||
|
||||
if (window->shell_surface)
|
||||
wl_shell_surface_add_listener(window->shell_surface,
|
||||
&shell_surface_listener, window);
|
||||
|
||||
wl_shell_surface_set_title(window->shell_surface, "simple-shm");
|
||||
|
||||
wl_shell_surface_set_toplevel(window->shell_surface);
|
||||
|
||||
wl_surface_damage(window->surface, 0, 0, width, height);
|
||||
attach_buffer(window, width, height);
|
||||
wl_surface_commit(window->surface);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_window(struct window *window)
|
||||
{
|
||||
wl_shell_surface_destroy(window->shell_surface);
|
||||
wl_surface_destroy(window->surface);
|
||||
free(window);
|
||||
}
|
||||
|
||||
static void
|
||||
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
||||
{
|
||||
struct display *d = data;
|
||||
|
||||
d->formats |= (1 << format);
|
||||
}
|
||||
|
||||
struct wl_shm_listener shm_listener = {
|
||||
shm_format
|
||||
};
|
||||
|
||||
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, "wl_shell") == 0) {
|
||||
d->shell = wl_registry_bind(registry,
|
||||
id, &wl_shell_interface, 1);
|
||||
} else if (strcmp(interface, "wl_shm") == 0) {
|
||||
d->shm = wl_registry_bind(registry,
|
||||
id, &wl_shm_interface, 1);
|
||||
wl_shm_add_listener(d->shm, &shm_listener, d);
|
||||
} else if (strcmp(interface, "wl_seat") == 0 &&
|
||||
d->seat == NULL) {
|
||||
d->seat = wl_registry_bind(registry,
|
||||
id, &wl_seat_interface, 3);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
registry_handle_global_remove(void *data, struct wl_registry *registry,
|
||||
uint32_t name)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
registry_handle_global,
|
||||
registry_handle_global_remove
|
||||
};
|
||||
|
||||
static struct display *
|
||||
create_display(void)
|
||||
{
|
||||
struct display *display;
|
||||
|
||||
display = xzalloc(sizeof *display);
|
||||
display->display = wl_display_connect(NULL);
|
||||
assert(display->display);
|
||||
|
||||
display->formats = 0;
|
||||
display->registry = wl_display_get_registry(display->display);
|
||||
wl_registry_add_listener(display->registry,
|
||||
®istry_listener, display);
|
||||
wl_display_roundtrip(display->display);
|
||||
if (display->shm == NULL) {
|
||||
fprintf(stderr, "No wl_shm global\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
wl_display_roundtrip(display->display);
|
||||
|
||||
if (!(display->formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
|
||||
fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
wl_list_init(&display->devices);
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_enter(void *data, struct wl_pointer *pointer,
|
||||
uint32_t serial, struct wl_surface *surface,
|
||||
wl_fixed_t sx_w, wl_fixed_t sy_w)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_leave(void *data, struct wl_pointer *pointer,
|
||||
uint32_t serial, struct wl_surface *surface)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_motion(void *data, struct wl_pointer *pointer,
|
||||
uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
|
||||
uint32_t time, uint32_t button, uint32_t state_w)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_axis(void *data, struct wl_pointer *pointer,
|
||||
uint32_t time, uint32_t axis, wl_fixed_t value)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_pointer_listener pointer_listener = {
|
||||
pointer_handle_enter,
|
||||
pointer_handle_leave,
|
||||
pointer_handle_motion,
|
||||
pointer_handle_button,
|
||||
pointer_handle_axis,
|
||||
};
|
||||
|
||||
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_w)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
|
||||
uint32_t serial, uint32_t mods_depressed,
|
||||
uint32_t mods_latched, uint32_t mods_locked,
|
||||
uint32_t group)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_keyboard_listener keyboard_listener = {
|
||||
keyboard_handle_keymap,
|
||||
keyboard_handle_enter,
|
||||
keyboard_handle_leave,
|
||||
keyboard_handle_key,
|
||||
keyboard_handle_modifiers,
|
||||
};
|
||||
|
||||
static void
|
||||
start_device(struct display *display, struct device *device)
|
||||
{
|
||||
if (display->seat == NULL)
|
||||
return;
|
||||
|
||||
switch (device->type) {
|
||||
case KEYBOARD:
|
||||
if (device->p.keyboard == NULL) {
|
||||
device->p.keyboard =
|
||||
wl_seat_get_keyboard(display->seat);
|
||||
wl_keyboard_add_listener(device->p.keyboard,
|
||||
&keyboard_listener,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
case POINTER:
|
||||
if (device->p.pointer == NULL) {
|
||||
device->p.pointer =
|
||||
wl_seat_get_pointer(display->seat);
|
||||
wl_pointer_add_listener(device->p.pointer,
|
||||
&pointer_listener,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_device(struct device *device)
|
||||
{
|
||||
switch (device->type) {
|
||||
case KEYBOARD:
|
||||
if (device->p.keyboard)
|
||||
wl_keyboard_release(device->p.keyboard);
|
||||
break;
|
||||
case POINTER:
|
||||
if (device->p.pointer)
|
||||
wl_pointer_release(device->p.pointer);
|
||||
break;
|
||||
}
|
||||
|
||||
wl_list_remove(&device->link);
|
||||
free(device);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_devices(struct display *display)
|
||||
{
|
||||
struct device *device, *tmp;
|
||||
|
||||
wl_list_for_each_safe(device, tmp, &display->devices, link)
|
||||
destroy_device(device);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_display(struct display *display)
|
||||
{
|
||||
destroy_devices(display);
|
||||
|
||||
if (display->shm)
|
||||
wl_shm_destroy(display->shm);
|
||||
|
||||
if (display->shell)
|
||||
wl_shell_destroy(display->shell);
|
||||
|
||||
if (display->seat)
|
||||
wl_seat_destroy(display->seat);
|
||||
|
||||
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 int running = 1;
|
||||
|
||||
static void
|
||||
signal_int(int signum)
|
||||
{
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
create_device(struct display *display, const char *time_desc, int type)
|
||||
{
|
||||
int start_time;
|
||||
int end_time = -1;
|
||||
char *tail;
|
||||
struct device *device;
|
||||
|
||||
if (time_desc == NULL) {
|
||||
fprintf(stderr, "missing time description\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
start_time = strtoul(time_desc, &tail, 10);
|
||||
if (errno || tail == time_desc)
|
||||
goto error;
|
||||
|
||||
if (*tail == ':') {
|
||||
time_desc = tail + 1;
|
||||
end_time = strtoul(time_desc, &tail, 10);
|
||||
if (errno || tail == time_desc || *tail != '\0')
|
||||
goto error;
|
||||
} else if (*tail != '\0') {
|
||||
goto error;
|
||||
}
|
||||
|
||||
device = xzalloc(sizeof *device);
|
||||
device->type = type;
|
||||
device->start_time = start_time;
|
||||
device->end_time = end_time;
|
||||
wl_list_insert(&display->devices, &device->link);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
fprintf(stderr, "invalid time description\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct timespec begin_time;
|
||||
|
||||
static void
|
||||
reset_timer(void)
|
||||
{
|
||||
clock_gettime(CLOCK_MONOTONIC, &begin_time);
|
||||
}
|
||||
|
||||
static double
|
||||
read_timer(void)
|
||||
{
|
||||
struct timespec t;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
return (double)(t.tv_sec - begin_time.tv_sec) +
|
||||
1e-9 * (t.tv_nsec - begin_time.tv_nsec);
|
||||
}
|
||||
|
||||
static void
|
||||
main_loop(struct display *display)
|
||||
{
|
||||
reset_timer();
|
||||
|
||||
while (running) {
|
||||
struct device *device, *tmp;
|
||||
struct pollfd fds[1];
|
||||
double sleep_time = DBL_MAX;
|
||||
double now;
|
||||
|
||||
if (wl_display_dispatch_pending(display->display) == -1)
|
||||
break;
|
||||
if (wl_display_flush(display->display) == -1)
|
||||
break;
|
||||
|
||||
now = read_timer();
|
||||
|
||||
wl_list_for_each(device, &display->devices, link) {
|
||||
double next_time = device->start_time - now;
|
||||
if (next_time < 0.0) {
|
||||
sleep_time = 0.0;
|
||||
break;
|
||||
} else if (next_time < sleep_time) {
|
||||
sleep_time = next_time;
|
||||
}
|
||||
next_time = device->end_time - now;
|
||||
if (next_time < 0.0) {
|
||||
sleep_time = 0.0;
|
||||
break;
|
||||
} else if (next_time < sleep_time) {
|
||||
sleep_time = next_time;
|
||||
}
|
||||
}
|
||||
|
||||
fds[0].fd = wl_display_get_fd(display->display);
|
||||
fds[0].events = POLLIN;
|
||||
fds[0].revents = 0;
|
||||
|
||||
poll(fds,
|
||||
sizeof fds / sizeof fds[0],
|
||||
sleep_time == DBL_MAX ? -1 : ceil(sleep_time * 1000.0));
|
||||
|
||||
if (fds[0].revents &&
|
||||
wl_display_dispatch(display->display) == -1)
|
||||
break;
|
||||
|
||||
now = read_timer();
|
||||
|
||||
wl_list_for_each_safe(device, tmp, &display->devices, link) {
|
||||
if (device->start_time <= now)
|
||||
start_device(display, device);
|
||||
if (device->end_time >= 0 && device->end_time <= now)
|
||||
destroy_device(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct sigaction sigint;
|
||||
struct display *display;
|
||||
struct window *window;
|
||||
int i;
|
||||
|
||||
display = create_display();
|
||||
window = create_window(display, 250, 250);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (!strncmp(argv[i], "-p", 2)) {
|
||||
char *arg;
|
||||
if (argv[i][2]) {
|
||||
arg = argv[i] + 2;
|
||||
} else {
|
||||
arg = argv[i + 1];
|
||||
i++;
|
||||
}
|
||||
if (create_device(display, arg, POINTER) == -1)
|
||||
return 1;
|
||||
} else if (!strncmp(argv[i], "-k", 2)) {
|
||||
char *arg;
|
||||
if (argv[i][2]) {
|
||||
arg = argv[i] + 2;
|
||||
} else {
|
||||
arg = argv[i + 1];
|
||||
i++;
|
||||
}
|
||||
if (create_device(display, arg, KEYBOARD) == -1)
|
||||
return 1;
|
||||
} else {
|
||||
fprintf(stderr, "unknown argument %s\n", argv[i]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
sigint.sa_handler = signal_int;
|
||||
sigemptyset(&sigint.sa_mask);
|
||||
sigint.sa_flags = SA_RESETHAND;
|
||||
sigaction(SIGINT, &sigint, NULL);
|
||||
|
||||
main_loop(display);
|
||||
|
||||
fprintf(stderr, "multi-resource exiting\n");
|
||||
destroy_window(window);
|
||||
destroy_display(display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,374 @@
|
|||
/*
|
||||
* Copyright © 2013 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <wayland-egl.h>
|
||||
#include <wayland-cursor.h>
|
||||
|
||||
#include <GLES2/gl2.h>
|
||||
#include <EGL/egl.h>
|
||||
|
||||
#include "shared/platform.h"
|
||||
|
||||
struct window;
|
||||
struct seat;
|
||||
|
||||
struct nested_client {
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
|
||||
EGLDisplay egl_display;
|
||||
EGLContext egl_context;
|
||||
EGLConfig egl_config;
|
||||
EGLSurface egl_surface;
|
||||
struct program *color_program;
|
||||
|
||||
GLuint vert, frag, program;
|
||||
GLuint rotation;
|
||||
GLuint pos;
|
||||
GLuint col;
|
||||
|
||||
struct wl_surface *surface;
|
||||
struct wl_egl_window *native;
|
||||
int width, height;
|
||||
};
|
||||
|
||||
#define POS 0
|
||||
#define COL 1
|
||||
|
||||
static GLuint
|
||||
create_shader(const char *source, GLenum shader_type)
|
||||
{
|
||||
GLuint shader;
|
||||
GLint status;
|
||||
|
||||
shader = glCreateShader(shader_type);
|
||||
if (shader == 0)
|
||||
return 0;
|
||||
|
||||
glShaderSource(shader, 1, (const char **) &source, NULL);
|
||||
glCompileShader(shader);
|
||||
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||
if (!status) {
|
||||
char log[1000];
|
||||
GLsizei len;
|
||||
glGetShaderInfoLog(shader, 1000, &len, log);
|
||||
fprintf(stderr, "Error: compiling %s: %.*s\n",
|
||||
shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
|
||||
len, log);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
static void
|
||||
create_program(struct nested_client *client,
|
||||
const char *vert, const char *frag)
|
||||
{
|
||||
GLint status;
|
||||
|
||||
client->vert = create_shader(vert, GL_VERTEX_SHADER);
|
||||
client->frag = create_shader(frag, GL_FRAGMENT_SHADER);
|
||||
|
||||
client->program = glCreateProgram();
|
||||
glAttachShader(client->program, client->frag);
|
||||
glAttachShader(client->program, client->vert);
|
||||
glBindAttribLocation(client->program, POS, "pos");
|
||||
glBindAttribLocation(client->program, COL, "color");
|
||||
glLinkProgram(client->program);
|
||||
|
||||
glGetProgramiv(client->program, GL_LINK_STATUS, &status);
|
||||
if (!status) {
|
||||
char log[1000];
|
||||
GLsizei len;
|
||||
glGetProgramInfoLog(client->program, 1000, &len, log);
|
||||
fprintf(stderr, "Error: linking:\n%.*s\n", len, log);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
client->rotation =
|
||||
glGetUniformLocation(client->program, "rotation");
|
||||
}
|
||||
|
||||
static const char vertex_shader_text[] =
|
||||
"uniform mat4 rotation;\n"
|
||||
"attribute vec4 pos;\n"
|
||||
"attribute vec4 color;\n"
|
||||
"varying vec4 v_color;\n"
|
||||
"void main() {\n"
|
||||
" gl_Position = rotation * pos;\n"
|
||||
" v_color = color;\n"
|
||||
"}\n";
|
||||
|
||||
static const char color_fragment_shader_text[] =
|
||||
"precision mediump float;\n"
|
||||
"varying vec4 v_color;\n"
|
||||
"void main() {\n"
|
||||
" gl_FragColor = v_color;\n"
|
||||
"}\n";
|
||||
|
||||
static void
|
||||
render_triangle(struct nested_client *client, uint32_t time)
|
||||
{
|
||||
static const GLfloat verts[3][2] = {
|
||||
{ -0.5, -0.5 },
|
||||
{ 0.5, -0.5 },
|
||||
{ 0, 0.5 }
|
||||
};
|
||||
static const GLfloat colors[3][3] = {
|
||||
{ 1, 0, 0 },
|
||||
{ 0, 1, 0 },
|
||||
{ 0, 0, 1 }
|
||||
};
|
||||
GLfloat angle;
|
||||
GLfloat rotation[4][4] = {
|
||||
{ 1, 0, 0, 0 },
|
||||
{ 0, 1, 0, 0 },
|
||||
{ 0, 0, 1, 0 },
|
||||
{ 0, 0, 0, 1 }
|
||||
};
|
||||
static const int32_t speed_div = 5;
|
||||
static uint32_t start_time = 0;
|
||||
|
||||
if (client->program == 0)
|
||||
create_program(client, vertex_shader_text,
|
||||
color_fragment_shader_text);
|
||||
|
||||
if (start_time == 0)
|
||||
start_time = time;
|
||||
|
||||
angle = ((time - start_time) / speed_div) % 360 * M_PI / 180.0;
|
||||
rotation[0][0] = cos(angle);
|
||||
rotation[0][2] = sin(angle);
|
||||
rotation[2][0] = -sin(angle);
|
||||
rotation[2][2] = cos(angle);
|
||||
|
||||
glClearColor(0.4, 0.4, 0.4, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glUseProgram(client->program);
|
||||
|
||||
glViewport(0, 0, client->width, client->height);
|
||||
|
||||
glUniformMatrix4fv(client->rotation, 1, GL_FALSE,
|
||||
(GLfloat *) rotation);
|
||||
|
||||
glVertexAttribPointer(POS, 2, GL_FLOAT, GL_FALSE, 0, verts);
|
||||
glVertexAttribPointer(COL, 3, GL_FLOAT, GL_FALSE, 0, colors);
|
||||
glEnableVertexAttribArray(POS);
|
||||
glEnableVertexAttribArray(COL);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
glDisableVertexAttribArray(POS);
|
||||
glDisableVertexAttribArray(COL);
|
||||
|
||||
glFlush();
|
||||
}
|
||||
|
||||
static void
|
||||
frame_callback(void *data, struct wl_callback *callback, uint32_t time);
|
||||
|
||||
static const struct wl_callback_listener frame_listener = {
|
||||
frame_callback
|
||||
};
|
||||
|
||||
static void
|
||||
frame_callback(void *data, struct wl_callback *callback, uint32_t time)
|
||||
{
|
||||
struct nested_client *client = data;
|
||||
|
||||
if (callback)
|
||||
wl_callback_destroy(callback);
|
||||
|
||||
callback = wl_surface_frame(client->surface);
|
||||
wl_callback_add_listener(callback, &frame_listener, client);
|
||||
|
||||
render_triangle(client, time);
|
||||
|
||||
eglSwapBuffers(client->egl_display, client->egl_surface);
|
||||
}
|
||||
|
||||
static void
|
||||
registry_handle_global(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version)
|
||||
{
|
||||
struct nested_client *client = data;
|
||||
|
||||
if (strcmp(interface, "wl_compositor") == 0) {
|
||||
client->compositor =
|
||||
wl_registry_bind(registry, name,
|
||||
&wl_compositor_interface, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
registry_handle_global_remove(void *data, struct wl_registry *registry,
|
||||
uint32_t name)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
registry_handle_global,
|
||||
registry_handle_global_remove
|
||||
};
|
||||
|
||||
static struct nested_client *
|
||||
nested_client_create(void)
|
||||
{
|
||||
static const EGLint context_attribs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
static const EGLint config_attribs[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_RED_SIZE, 1,
|
||||
EGL_GREEN_SIZE, 1,
|
||||
EGL_BLUE_SIZE, 1,
|
||||
EGL_ALPHA_SIZE, 1,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
EGLint major, minor, n;
|
||||
EGLBoolean ret;
|
||||
|
||||
struct nested_client *client;
|
||||
|
||||
client = malloc(sizeof *client);
|
||||
if (client == NULL)
|
||||
return NULL;
|
||||
|
||||
client->width = 250;
|
||||
client->height = 250;
|
||||
|
||||
client->display = wl_display_connect(NULL);
|
||||
|
||||
client->registry = wl_display_get_registry(client->display);
|
||||
wl_registry_add_listener(client->registry,
|
||||
®istry_listener, client);
|
||||
|
||||
/* get globals */
|
||||
wl_display_roundtrip(client->display);
|
||||
|
||||
client->egl_display =
|
||||
weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR,
|
||||
client->display, NULL);
|
||||
if (client->egl_display == NULL)
|
||||
return NULL;
|
||||
|
||||
ret = eglInitialize(client->egl_display, &major, &minor);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
ret = eglBindAPI(EGL_OPENGL_ES_API);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
ret = eglChooseConfig(client->egl_display, config_attribs,
|
||||
&client->egl_config, 1, &n);
|
||||
if (!ret || n != 1)
|
||||
return NULL;
|
||||
|
||||
client->egl_context = eglCreateContext(client->egl_display,
|
||||
client->egl_config,
|
||||
EGL_NO_CONTEXT,
|
||||
context_attribs);
|
||||
if (!client->egl_context)
|
||||
return NULL;
|
||||
|
||||
client->surface = wl_compositor_create_surface(client->compositor);
|
||||
|
||||
client->native = wl_egl_window_create(client->surface,
|
||||
client->width, client->height);
|
||||
|
||||
client->egl_surface = weston_platform_create_egl_surface(client->egl_display,
|
||||
client->egl_config,
|
||||
client->native, NULL);
|
||||
|
||||
eglMakeCurrent(client->egl_display, client->egl_surface,
|
||||
client->egl_surface, client->egl_context);
|
||||
|
||||
wl_egl_window_resize(client->native,
|
||||
client->width, client->height, 0, 0);
|
||||
|
||||
frame_callback(client, NULL, 0);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
static void
|
||||
nested_client_destroy(struct nested_client *client)
|
||||
{
|
||||
eglMakeCurrent(client->egl_display,
|
||||
EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||
EGL_NO_CONTEXT);
|
||||
|
||||
weston_platform_destroy_egl_surface(client->egl_display,
|
||||
client->egl_surface);
|
||||
wl_egl_window_destroy(client->native);
|
||||
|
||||
wl_surface_destroy(client->surface);
|
||||
|
||||
if (client->compositor)
|
||||
wl_compositor_destroy(client->compositor);
|
||||
|
||||
wl_registry_destroy(client->registry);
|
||||
eglTerminate(client->egl_display);
|
||||
eglReleaseThread();
|
||||
wl_display_flush(client->display);
|
||||
wl_display_disconnect(client->display);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct nested_client *client;
|
||||
int ret = 0;
|
||||
|
||||
if (getenv("WAYLAND_SOCKET") == NULL) {
|
||||
fprintf(stderr,
|
||||
"must be run by nested, don't run standalone\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
client = nested_client_create();
|
||||
if (client == NULL)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
while (ret != -1)
|
||||
ret = wl_display_dispatch(client->display);
|
||||
|
||||
nested_client_destroy(client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,956 @@
|
|||
/*
|
||||
* Copyright © 2011 Benjamin Franzke
|
||||
* Copyright © 2010 Intel Corporation
|
||||
* Copyright © 2014 Collabora, Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include "shared/helpers.h"
|
||||
#include <libweston/zalloc.h>
|
||||
#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,
|
||||
RUN_MODE_FEEDBACK_IDLE,
|
||||
RUN_MODE_PRESENT,
|
||||
};
|
||||
|
||||
static const char * const run_mode_name[] = {
|
||||
[RUN_MODE_FEEDBACK] = "feedback",
|
||||
[RUN_MODE_FEEDBACK_IDLE] = "feedback-idle",
|
||||
[RUN_MODE_PRESENT] = "low-lat present",
|
||||
};
|
||||
|
||||
struct output {
|
||||
struct wl_output *output;
|
||||
uint32_t name;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct display {
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
struct xdg_wm_base *wm_base;
|
||||
|
||||
struct wl_shm *shm;
|
||||
uint32_t formats;
|
||||
|
||||
struct wp_presentation *presentation;
|
||||
clockid_t clk_id;
|
||||
|
||||
struct wl_list output_list; /* struct output::link */
|
||||
};
|
||||
|
||||
struct feedback {
|
||||
struct window *window;
|
||||
unsigned frame_no;
|
||||
struct wp_presentation_feedback *feedback;
|
||||
struct timespec commit;
|
||||
struct timespec target;
|
||||
uint32_t frame_stamp;
|
||||
struct wl_list link;
|
||||
struct timespec present;
|
||||
};
|
||||
|
||||
struct buffer {
|
||||
struct wl_buffer *buffer;
|
||||
void *shm_data;
|
||||
int busy;
|
||||
};
|
||||
|
||||
struct window {
|
||||
struct display *display;
|
||||
int width, height;
|
||||
enum run_mode mode;
|
||||
struct wl_surface *surface;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
uint32_t configure_serial;
|
||||
|
||||
struct buffer *buffers;
|
||||
int num_buffers;
|
||||
int next;
|
||||
int refresh_nsec;
|
||||
int commit_delay_msecs;
|
||||
|
||||
struct wl_callback *callback;
|
||||
struct wl_list feedback_list;
|
||||
|
||||
struct feedback *received_feedback;
|
||||
};
|
||||
|
||||
#define NSEC_PER_SEC 1000000000
|
||||
|
||||
static void
|
||||
buffer_release(void *data, struct wl_buffer *buffer)
|
||||
{
|
||||
struct buffer *mybuf = data;
|
||||
|
||||
mybuf->busy = 0;
|
||||
}
|
||||
|
||||
static const struct wl_buffer_listener buffer_listener = {
|
||||
buffer_release
|
||||
};
|
||||
|
||||
static int
|
||||
create_shm_buffers(struct display *display, struct buffer **buffers,
|
||||
int num_buffers, int width, int height, uint32_t format)
|
||||
{
|
||||
struct buffer *bufs;
|
||||
struct wl_shm_pool *pool;
|
||||
int fd, size, stride, offset;
|
||||
void *data;
|
||||
int i;
|
||||
|
||||
stride = width * 4;
|
||||
size = stride * height * num_buffers;
|
||||
|
||||
fd = os_create_anonymous_file(size);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
|
||||
size, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pool = wl_shm_create_pool(display->shm, fd, size);
|
||||
offset = 0;
|
||||
|
||||
bufs = calloc(num_buffers, sizeof(*bufs));
|
||||
assert(bufs);
|
||||
|
||||
for (i = 0; i < num_buffers; i++) {
|
||||
bufs[i].buffer = wl_shm_pool_create_buffer(pool, offset,
|
||||
width, height,
|
||||
stride, format);
|
||||
assert(bufs[i].buffer);
|
||||
wl_buffer_add_listener(bufs[i].buffer,
|
||||
&buffer_listener, &bufs[i]);
|
||||
|
||||
bufs[i].shm_data = (char *)data + offset;
|
||||
offset += stride * height;
|
||||
}
|
||||
|
||||
wl_shm_pool_destroy(pool);
|
||||
close(fd);
|
||||
|
||||
*buffers = bufs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
xdg_wm_base_handle_ping(void *data, struct xdg_wm_base *xdg_wm_base,
|
||||
uint32_t serial)
|
||||
{
|
||||
xdg_wm_base_pong(xdg_wm_base, serial);
|
||||
}
|
||||
|
||||
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
|
||||
.ping = xdg_wm_base_handle_ping,
|
||||
};
|
||||
|
||||
static void
|
||||
xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface,
|
||||
uint32_t serial)
|
||||
{
|
||||
struct window *window = data;
|
||||
|
||||
window->configure_serial = serial;
|
||||
}
|
||||
|
||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
||||
.configure = xdg_surface_handle_configure,
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *xdg_toplevel,
|
||||
int32_t width, int32_t height,
|
||||
struct wl_array *states)
|
||||
{
|
||||
/* noop */
|
||||
}
|
||||
|
||||
static void
|
||||
xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel)
|
||||
{
|
||||
fprintf(stderr, "presentation-shm exiting\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
||||
.configure = xdg_toplevel_handle_configure,
|
||||
.close = xdg_toplevel_handle_close,
|
||||
};
|
||||
|
||||
static struct window *
|
||||
create_window(struct display *display, int width, int height,
|
||||
enum run_mode mode, int commit_delay_msecs)
|
||||
{
|
||||
struct window *window;
|
||||
char title[128];
|
||||
int ret;
|
||||
|
||||
snprintf(title, sizeof(title),
|
||||
"presentation-shm: %s [Delay %i msecs]", run_mode_name[mode],
|
||||
commit_delay_msecs);
|
||||
|
||||
window = zalloc(sizeof *window);
|
||||
if (!window)
|
||||
return NULL;
|
||||
|
||||
window->commit_delay_msecs = commit_delay_msecs;
|
||||
window->mode = mode;
|
||||
window->callback = NULL;
|
||||
wl_list_init(&window->feedback_list);
|
||||
window->display = display;
|
||||
window->width = width;
|
||||
window->height = height;
|
||||
window->surface = wl_compositor_create_surface(display->compositor);
|
||||
window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base,
|
||||
window->surface);
|
||||
|
||||
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);
|
||||
|
||||
xdg_toplevel_set_title(window->xdg_toplevel, title);
|
||||
xdg_toplevel_set_min_size(window->xdg_toplevel, width, height);
|
||||
xdg_toplevel_set_max_size(window->xdg_toplevel, width, height);
|
||||
|
||||
wl_surface_commit(window->surface);
|
||||
wl_display_roundtrip(window->display->display);
|
||||
|
||||
window->num_buffers = 60;
|
||||
window->refresh_nsec = NSEC_PER_SEC / 60; /* 60 Hz guess */
|
||||
window->next = 0;
|
||||
ret = create_shm_buffers(window->display,
|
||||
&window->buffers, window->num_buffers,
|
||||
window->width, window->height,
|
||||
WL_SHM_FORMAT_XRGB8888);
|
||||
assert(ret == 0);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_feedback(struct feedback *feedback)
|
||||
{
|
||||
if (feedback->feedback)
|
||||
wp_presentation_feedback_destroy(feedback->feedback);
|
||||
|
||||
wl_list_remove(&feedback->link);
|
||||
free(feedback);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_window(struct window *window)
|
||||
{
|
||||
int i;
|
||||
|
||||
while (!wl_list_empty(&window->feedback_list)) {
|
||||
struct feedback *f;
|
||||
|
||||
f = wl_container_of(window->feedback_list.next, f, link);
|
||||
printf("clean up feedback %u\n", f->frame_no);
|
||||
destroy_feedback(f);
|
||||
}
|
||||
|
||||
if (window->callback)
|
||||
wl_callback_destroy(window->callback);
|
||||
|
||||
xdg_surface_destroy(window->xdg_surface);
|
||||
wl_surface_destroy(window->surface);
|
||||
|
||||
for (i = 0; i < window->num_buffers; i++)
|
||||
wl_buffer_destroy(window->buffers[i].buffer);
|
||||
/* munmap(window->buffers[0].shm_data, size); */
|
||||
free(window->buffers);
|
||||
|
||||
free(window);
|
||||
}
|
||||
|
||||
static struct buffer *
|
||||
window_next_buffer(struct window *window)
|
||||
{
|
||||
struct buffer *buf = &window->buffers[window->next];
|
||||
|
||||
window->next = (window->next + 1) % window->num_buffers;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void
|
||||
paint_pixels(void *image, int width, int height, uint32_t phase)
|
||||
{
|
||||
const int halfh = height / 2;
|
||||
const int halfw = width / 2;
|
||||
uint32_t *pixel = image;
|
||||
int y, or;
|
||||
double ang = M_PI * 2.0 / 1000000.0 * phase;
|
||||
double s = sin(ang);
|
||||
double c = cos(ang);
|
||||
|
||||
/* squared radii thresholds */
|
||||
or = (halfw < halfh ? halfw : halfh) - 16;
|
||||
or *= or;
|
||||
|
||||
for (y = 0; y < height; y++) {
|
||||
int x;
|
||||
int oy = y - halfh;
|
||||
int y2 = oy * oy;
|
||||
|
||||
for (x = 0; x < width; x++) {
|
||||
int ox = x - halfw;
|
||||
uint32_t v = 0xff000000;
|
||||
double rx, ry;
|
||||
|
||||
if (ox * ox + y2 > or) {
|
||||
if (ox * oy > 0)
|
||||
*pixel++ = 0xff000000;
|
||||
else
|
||||
*pixel++ = 0xffffffff;
|
||||
continue;
|
||||
}
|
||||
|
||||
rx = c * ox + s * oy;
|
||||
ry = -s * ox + c * oy;
|
||||
|
||||
if (rx < 0.0)
|
||||
v |= 0x00ff0000;
|
||||
if (ry < 0.0)
|
||||
v |= 0x0000ff00;
|
||||
if ((rx < 0.0) == (ry < 0.0))
|
||||
v |= 0x000000ff;
|
||||
|
||||
*pixel++ = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
feedback_sync_output(void *data,
|
||||
struct wp_presentation_feedback *presentation_feedback,
|
||||
struct wl_output *output)
|
||||
{
|
||||
/* not interested */
|
||||
}
|
||||
|
||||
static char *
|
||||
pflags_to_str(uint32_t flags, char *str, unsigned len)
|
||||
{
|
||||
static const struct {
|
||||
uint32_t flag;
|
||||
char sym;
|
||||
} desc[] = {
|
||||
{ WP_PRESENTATION_FEEDBACK_KIND_VSYNC, 's' },
|
||||
{ WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK, 'c' },
|
||||
{ WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION, 'e' },
|
||||
{ WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY, 'z' },
|
||||
};
|
||||
unsigned i;
|
||||
|
||||
*str = '\0';
|
||||
if (len < ARRAY_LENGTH(desc) + 1)
|
||||
return str;
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(desc); i++)
|
||||
str[i] = flags & desc[i].flag ? desc[i].sym : '_';
|
||||
str[ARRAY_LENGTH(desc)] = '\0';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
timespec_to_ms(const struct timespec *ts)
|
||||
{
|
||||
return (uint32_t)ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
static int
|
||||
timespec_diff_to_usec(const struct timespec *a, const struct timespec *b)
|
||||
{
|
||||
time_t secs = a->tv_sec - b->tv_sec;
|
||||
long nsec = a->tv_nsec - b->tv_nsec;
|
||||
|
||||
return secs * 1000000 + nsec / 1000;
|
||||
}
|
||||
|
||||
static void
|
||||
feedback_presented(void *data,
|
||||
struct wp_presentation_feedback *presentation_feedback,
|
||||
uint32_t tv_sec_hi,
|
||||
uint32_t tv_sec_lo,
|
||||
uint32_t tv_nsec,
|
||||
uint32_t refresh_nsec,
|
||||
uint32_t seq_hi,
|
||||
uint32_t seq_lo,
|
||||
uint32_t flags)
|
||||
{
|
||||
struct feedback *feedback = data;
|
||||
struct window *window = feedback->window;
|
||||
struct feedback *prev_feedback = window->received_feedback;
|
||||
uint64_t seq = ((uint64_t)seq_hi << 32) + seq_lo;
|
||||
const struct timespec *prevpresent;
|
||||
uint32_t commit, present;
|
||||
uint32_t f2c, c2p, f2p;
|
||||
int p2p, t2p;
|
||||
char flagstr[10];
|
||||
|
||||
timespec_from_proto(&feedback->present, tv_sec_hi, tv_sec_lo, tv_nsec);
|
||||
commit = timespec_to_ms(&feedback->commit);
|
||||
present = timespec_to_ms(&feedback->present);
|
||||
|
||||
if (prev_feedback)
|
||||
prevpresent = &prev_feedback->present;
|
||||
else
|
||||
prevpresent = &feedback->present;
|
||||
|
||||
f2c = commit - feedback->frame_stamp;
|
||||
c2p = present - commit;
|
||||
f2p = present - feedback->frame_stamp;
|
||||
p2p = timespec_diff_to_usec(&feedback->present, prevpresent);
|
||||
t2p = timespec_diff_to_usec(&feedback->present, &feedback->target);
|
||||
|
||||
switch (window->mode) {
|
||||
case RUN_MODE_PRESENT:
|
||||
printf("%6u: c2p %4u ms, p2p %5d us, t2p %6d us, [%s] "
|
||||
"seq %" PRIu64 "\n", feedback->frame_no, c2p,
|
||||
p2p, t2p,
|
||||
pflags_to_str(flags, flagstr, sizeof(flagstr)), seq);
|
||||
break;
|
||||
case RUN_MODE_FEEDBACK:
|
||||
case RUN_MODE_FEEDBACK_IDLE:
|
||||
printf("%6u: f2c %2u ms, c2p %2u ms, f2p %2u ms, p2p %5d us, "
|
||||
"t2p %6d, [%s], seq %" PRIu64 "\n", feedback->frame_no,
|
||||
f2c, c2p, f2p, p2p, t2p,
|
||||
pflags_to_str(flags, flagstr, sizeof(flagstr)), seq);
|
||||
}
|
||||
|
||||
if (window->received_feedback)
|
||||
destroy_feedback(window->received_feedback);
|
||||
window->received_feedback = feedback;
|
||||
}
|
||||
|
||||
static void
|
||||
feedback_discarded(void *data,
|
||||
struct wp_presentation_feedback *presentation_feedback)
|
||||
{
|
||||
struct feedback *feedback = data;
|
||||
|
||||
printf("discarded %u\n", feedback->frame_no);
|
||||
|
||||
destroy_feedback(feedback);
|
||||
}
|
||||
|
||||
static const struct wp_presentation_feedback_listener feedback_listener = {
|
||||
feedback_sync_output,
|
||||
feedback_presented,
|
||||
feedback_discarded
|
||||
};
|
||||
|
||||
static void
|
||||
window_emulate_rendering(struct window *window)
|
||||
{
|
||||
struct timespec delay;
|
||||
int ret;
|
||||
|
||||
if (window->commit_delay_msecs <= 0)
|
||||
return;
|
||||
|
||||
delay.tv_sec = window->commit_delay_msecs / 1000;
|
||||
delay.tv_nsec = (window->commit_delay_msecs % 1000) * 1000000;
|
||||
|
||||
ret = nanosleep(&delay, NULL);
|
||||
if (ret)
|
||||
printf("nanosleep failed: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
static void
|
||||
window_create_feedback(struct window *window, uint32_t frame_stamp)
|
||||
{
|
||||
static unsigned seq;
|
||||
struct wp_presentation *pres = window->display->presentation;
|
||||
struct feedback *feedback;
|
||||
|
||||
seq++;
|
||||
|
||||
if (!pres)
|
||||
return;
|
||||
|
||||
feedback = zalloc(sizeof *feedback);
|
||||
if (!feedback)
|
||||
return;
|
||||
|
||||
feedback->window = window;
|
||||
feedback->feedback = wp_presentation_feedback(pres, window->surface);
|
||||
wp_presentation_feedback_add_listener(feedback->feedback,
|
||||
&feedback_listener, feedback);
|
||||
|
||||
feedback->frame_no = seq;
|
||||
|
||||
clock_gettime(window->display->clk_id, &feedback->commit);
|
||||
feedback->frame_stamp = frame_stamp;
|
||||
feedback->target = feedback->commit;
|
||||
|
||||
wl_list_insert(&window->feedback_list, &feedback->link);
|
||||
}
|
||||
|
||||
static void
|
||||
window_commit_next(struct window *window)
|
||||
{
|
||||
struct buffer *buffer;
|
||||
|
||||
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);
|
||||
buffer->busy = 1;
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener frame_listener_mode_feedback;
|
||||
|
||||
static void
|
||||
redraw_mode_feedback(void *data, struct wl_callback *callback, uint32_t time)
|
||||
{
|
||||
struct window *window = data;
|
||||
|
||||
if (callback && window->mode == RUN_MODE_FEEDBACK_IDLE)
|
||||
sleep(1);
|
||||
|
||||
if (callback)
|
||||
wl_callback_destroy(callback);
|
||||
|
||||
window_emulate_rendering(window);
|
||||
|
||||
window->callback = wl_surface_frame(window->surface);
|
||||
wl_callback_add_listener(window->callback,
|
||||
&frame_listener_mode_feedback, window);
|
||||
|
||||
window_create_feedback(window, time);
|
||||
window_commit_next(window);
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener frame_listener_mode_feedback = {
|
||||
redraw_mode_feedback
|
||||
};
|
||||
|
||||
static const struct wp_presentation_feedback_listener feedkick_listener;
|
||||
|
||||
static void
|
||||
window_feedkick(struct window *window)
|
||||
{
|
||||
struct wp_presentation *pres = window->display->presentation;
|
||||
struct wp_presentation_feedback *fback;
|
||||
|
||||
fback = wp_presentation_feedback(pres, window->surface);
|
||||
wp_presentation_feedback_add_listener(fback, &feedkick_listener,
|
||||
window);
|
||||
}
|
||||
|
||||
static void
|
||||
feedkick_presented(void *data,
|
||||
struct wp_presentation_feedback *presentation_feedback,
|
||||
uint32_t tv_sec_hi,
|
||||
uint32_t tv_sec_lo,
|
||||
uint32_t tv_nsec,
|
||||
uint32_t refresh_nsec,
|
||||
uint32_t seq_hi,
|
||||
uint32_t seq_lo,
|
||||
uint32_t flags)
|
||||
{
|
||||
struct window *window = data;
|
||||
|
||||
wp_presentation_feedback_destroy(presentation_feedback);
|
||||
window->refresh_nsec = refresh_nsec;
|
||||
|
||||
switch (window->mode) {
|
||||
case RUN_MODE_PRESENT:
|
||||
window_emulate_rendering(window);
|
||||
window_create_feedback(window, 0);
|
||||
window_feedkick(window);
|
||||
window_commit_next(window);
|
||||
break;
|
||||
case RUN_MODE_FEEDBACK:
|
||||
case RUN_MODE_FEEDBACK_IDLE:
|
||||
assert(0 && "bad mode");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
feedkick_discarded(void *data,
|
||||
struct wp_presentation_feedback *presentation_feedback)
|
||||
{
|
||||
struct window *window = data;
|
||||
|
||||
wp_presentation_feedback_destroy(presentation_feedback);
|
||||
|
||||
switch (window->mode) {
|
||||
case RUN_MODE_PRESENT:
|
||||
window_emulate_rendering(window);
|
||||
window_create_feedback(window, 0);
|
||||
window_feedkick(window);
|
||||
window_commit_next(window);
|
||||
break;
|
||||
case RUN_MODE_FEEDBACK:
|
||||
case RUN_MODE_FEEDBACK_IDLE:
|
||||
assert(0 && "bad mode");
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wp_presentation_feedback_listener feedkick_listener = {
|
||||
feedback_sync_output,
|
||||
feedkick_presented,
|
||||
feedkick_discarded
|
||||
};
|
||||
|
||||
static void
|
||||
firstdraw_mode_burst(struct window *window)
|
||||
{
|
||||
window_emulate_rendering(window);
|
||||
|
||||
switch (window->mode) {
|
||||
case RUN_MODE_PRESENT:
|
||||
window_create_feedback(window, 0);
|
||||
break;
|
||||
case RUN_MODE_FEEDBACK:
|
||||
case RUN_MODE_FEEDBACK_IDLE:
|
||||
assert(0 && "bad mode");
|
||||
}
|
||||
|
||||
window_feedkick(window);
|
||||
window_commit_next(window);
|
||||
}
|
||||
|
||||
static void
|
||||
window_prerender(struct window *window)
|
||||
{
|
||||
int i;
|
||||
int timefactor = 1000000 / window->num_buffers;
|
||||
|
||||
for (i = 0; i < window->num_buffers; i++) {
|
||||
struct buffer *buf = &window->buffers[i];
|
||||
|
||||
if (buf->busy)
|
||||
fprintf(stderr, "wl_buffer id %u) busy\n",
|
||||
wl_proxy_get_id(
|
||||
(struct wl_proxy *)buf->buffer));
|
||||
|
||||
paint_pixels(buf->shm_data, window->width, window->height,
|
||||
i * timefactor);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
output_destroy(struct output *o)
|
||||
{
|
||||
wl_output_destroy(o->output);
|
||||
wl_list_remove(&o->link);
|
||||
free(o);
|
||||
}
|
||||
|
||||
static void
|
||||
display_add_output(struct display *d, uint32_t name, uint32_t version)
|
||||
{
|
||||
struct output *o;
|
||||
|
||||
o = zalloc(sizeof(*o));
|
||||
assert(o);
|
||||
|
||||
o->output = wl_registry_bind(d->registry, name,
|
||||
&wl_output_interface, 1);
|
||||
o->name = name;
|
||||
wl_list_insert(&d->output_list, &o->link);
|
||||
}
|
||||
|
||||
static void
|
||||
presentation_clock_id(void *data, struct wp_presentation *presentation,
|
||||
uint32_t clk_id)
|
||||
{
|
||||
struct display *d = data;
|
||||
|
||||
d->clk_id = clk_id;
|
||||
}
|
||||
|
||||
static const struct wp_presentation_listener presentation_listener = {
|
||||
presentation_clock_id
|
||||
};
|
||||
|
||||
static void
|
||||
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
||||
{
|
||||
struct display *d = data;
|
||||
|
||||
d->formats |= (1 << format);
|
||||
}
|
||||
|
||||
static const struct wl_shm_listener shm_listener = {
|
||||
shm_format
|
||||
};
|
||||
|
||||
static void
|
||||
registry_handle_global(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version)
|
||||
{
|
||||
struct display *d = data;
|
||||
|
||||
if (strcmp(interface, "wl_compositor") == 0) {
|
||||
d->compositor =
|
||||
wl_registry_bind(registry,
|
||||
name, &wl_compositor_interface, 1);
|
||||
} else if (strcmp(interface, "xdg_wm_base") == 0) {
|
||||
d->wm_base =
|
||||
wl_registry_bind(registry, name,
|
||||
&xdg_wm_base_interface, version);
|
||||
} else if (strcmp(interface, "wl_shm") == 0) {
|
||||
d->shm = wl_registry_bind(registry,
|
||||
name, &wl_shm_interface, 1);
|
||||
wl_shm_add_listener(d->shm, &shm_listener, d);
|
||||
} else if (strcmp(interface, "wl_output") == 0) {
|
||||
display_add_output(d, name, version);
|
||||
} else if (strcmp(interface, wp_presentation_interface.name) == 0) {
|
||||
d->presentation =
|
||||
wl_registry_bind(registry,
|
||||
name, &wp_presentation_interface, 1);
|
||||
wp_presentation_add_listener(d->presentation,
|
||||
&presentation_listener, d);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
registry_handle_global_remove(void *data, struct wl_registry *registry,
|
||||
uint32_t name)
|
||||
{
|
||||
struct display *d = data;
|
||||
struct output *output, *otmp;
|
||||
|
||||
wl_list_for_each_safe(output, otmp, &d->output_list, link) {
|
||||
if (output->name != name)
|
||||
continue;
|
||||
|
||||
output_destroy(output);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
display->formats = 0;
|
||||
display->clk_id = -1;
|
||||
wl_list_init(&display->output_list);
|
||||
display->registry = wl_display_get_registry(display->display);
|
||||
wl_registry_add_listener(display->registry,
|
||||
®istry_listener, display);
|
||||
wl_display_roundtrip(display->display);
|
||||
if (display->shm == NULL) {
|
||||
fprintf(stderr, "No wl_shm global\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
wl_display_roundtrip(display->display);
|
||||
|
||||
if (!(display->formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
|
||||
fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
wl_display_get_fd(display->display);
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_display(struct display *display)
|
||||
{
|
||||
while (!wl_list_empty(&display->output_list)) {
|
||||
struct output *o;
|
||||
|
||||
o = wl_container_of(display->output_list.next, o, link);
|
||||
output_destroy(o);
|
||||
}
|
||||
|
||||
if (display->shm)
|
||||
wl_shm_destroy(display->shm);
|
||||
|
||||
if (display->wm_base)
|
||||
xdg_wm_base_destroy(display->wm_base);
|
||||
|
||||
if (display->compositor)
|
||||
wl_compositor_destroy(display->compositor);
|
||||
|
||||
wl_registry_destroy(display->registry);
|
||||
wl_display_flush(display->display);
|
||||
wl_display_disconnect(display->display);
|
||||
free(display);
|
||||
}
|
||||
|
||||
static int running = 1;
|
||||
|
||||
static void
|
||||
signal_int(int signum)
|
||||
{
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
usage(const char *prog, int exit_code)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [mode] [options]\n"
|
||||
"where 'mode' is one of\n"
|
||||
" -f\t\trun in feedback mode (default)\n"
|
||||
" -i\t\trun in feedback-idle mode; sleep 1s between frames\n"
|
||||
" -p\t\trun in low-latency presentation mode\n"
|
||||
"and 'options' may include\n"
|
||||
" -d msecs\temulate the time used for rendering by a delay \n"
|
||||
"\t\tof the given milliseconds before commit\n\n",
|
||||
prog);
|
||||
|
||||
fprintf(stderr, "Printed timing statistics, depending on mode:\n"
|
||||
" commit sequence number\n"
|
||||
" f2c: time from frame callback timestamp to commit\n"
|
||||
" c2p: time from commit to presentation\n"
|
||||
" f2p: time from frame callback timestamp to presentation\n"
|
||||
" p2p: time from previous presentation to this one\n"
|
||||
" t2p: time from target timestamp to presentation\n"
|
||||
" seq: MSC\n");
|
||||
|
||||
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct sigaction sigint;
|
||||
struct display *display;
|
||||
struct window *window;
|
||||
int ret = 0;
|
||||
enum run_mode mode = RUN_MODE_FEEDBACK;
|
||||
int i;
|
||||
int commit_delay_msecs = 0;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp("-f", argv[i]) == 0)
|
||||
mode = RUN_MODE_FEEDBACK;
|
||||
else if (strcmp("-i", argv[i]) == 0)
|
||||
mode = RUN_MODE_FEEDBACK_IDLE;
|
||||
else if (strcmp("-p", argv[i]) == 0)
|
||||
mode = RUN_MODE_PRESENT;
|
||||
else if ((strcmp("-d", argv[i]) == 0) && (i + 1 < argc)) {
|
||||
i++;
|
||||
commit_delay_msecs = atoi(argv[i]);
|
||||
}
|
||||
else
|
||||
usage(argv[0], EXIT_FAILURE);
|
||||
}
|
||||
|
||||
display = create_display();
|
||||
window = create_window(display, 250, 250, mode, commit_delay_msecs);
|
||||
if (!window)
|
||||
return 1;
|
||||
|
||||
sigint.sa_handler = signal_int;
|
||||
sigemptyset(&sigint.sa_mask);
|
||||
sigint.sa_flags = SA_RESETHAND;
|
||||
sigaction(SIGINT, &sigint, NULL);
|
||||
|
||||
window_prerender(window);
|
||||
|
||||
switch (mode) {
|
||||
case RUN_MODE_FEEDBACK:
|
||||
case RUN_MODE_FEEDBACK_IDLE:
|
||||
redraw_mode_feedback(window, NULL, 0);
|
||||
break;
|
||||
case RUN_MODE_PRESENT:
|
||||
firstdraw_mode_burst(window);
|
||||
break;
|
||||
}
|
||||
|
||||
while (running && ret != -1)
|
||||
ret = wl_display_dispatch(display->display);
|
||||
|
||||
fprintf(stderr, "presentation-shm exiting\n");
|
||||
destroy_window(window);
|
||||
destroy_display(display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,456 @@
|
|||
/*
|
||||
* Copyright © 2010 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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cairo.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "window.h"
|
||||
#include "shared/xalloc.h"
|
||||
|
||||
struct spring {
|
||||
double current;
|
||||
double target;
|
||||
double previous;
|
||||
};
|
||||
|
||||
struct resizor {
|
||||
struct display *display;
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
struct window *menu;
|
||||
struct spring width;
|
||||
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;
|
||||
};
|
||||
|
||||
static void
|
||||
spring_update(struct spring *spring)
|
||||
{
|
||||
double current, force;
|
||||
|
||||
current = spring->current;
|
||||
force = (spring->target - current) / 20.0 +
|
||||
(spring->previous - current);
|
||||
|
||||
spring->current = current + (current - spring->previous) + force;
|
||||
spring->previous = current;
|
||||
}
|
||||
|
||||
static int
|
||||
spring_done(struct spring *spring)
|
||||
{
|
||||
return fabs(spring->previous - spring->target) < 0.1;
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener listener;
|
||||
|
||||
static void
|
||||
frame_callback(void *data, struct wl_callback *callback, uint32_t time)
|
||||
{
|
||||
struct resizor *resizor = data;
|
||||
|
||||
assert(!callback || callback == resizor->frame_callback);
|
||||
|
||||
if (resizor->frame_callback) {
|
||||
wl_callback_destroy(resizor->frame_callback);
|
||||
resizor->frame_callback = NULL;
|
||||
}
|
||||
|
||||
if (window_is_maximized(resizor->window))
|
||||
return;
|
||||
|
||||
spring_update(&resizor->width);
|
||||
spring_update(&resizor->height);
|
||||
|
||||
widget_schedule_resize(resizor->widget,
|
||||
resizor->width.current + 0.5,
|
||||
resizor->height.current + 0.5);
|
||||
|
||||
if (!spring_done(&resizor->width) || !spring_done(&resizor->height)) {
|
||||
resizor->frame_callback =
|
||||
wl_surface_frame(
|
||||
window_get_wl_surface(resizor->window));
|
||||
wl_callback_add_listener(resizor->frame_callback, &listener,
|
||||
resizor);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener listener = {
|
||||
frame_callback
|
||||
};
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct resizor *resizor = data;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
struct rectangle allocation;
|
||||
|
||||
widget_get_allocation(resizor->widget, &allocation);
|
||||
|
||||
surface = window_get_surface(resizor->window);
|
||||
|
||||
cr = cairo_create(surface);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_rectangle(cr,
|
||||
allocation.x,
|
||||
allocation.y,
|
||||
allocation.width,
|
||||
allocation.height);
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
|
||||
cairo_fill(cr);
|
||||
cairo_destroy(cr);
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_focus_handler(struct window *window,
|
||||
struct input *device, void *data)
|
||||
{
|
||||
struct resizor *resizor = data;
|
||||
|
||||
window_schedule_redraw(resizor->window);
|
||||
}
|
||||
|
||||
static void
|
||||
key_handler(struct window *window, struct input *input, uint32_t time,
|
||||
uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
|
||||
void *data)
|
||||
{
|
||||
struct resizor *resizor = data;
|
||||
struct rectangle allocation;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
|
||||
return;
|
||||
|
||||
window_get_allocation(resizor->window, &allocation);
|
||||
resizor->width.current = allocation.width;
|
||||
if (spring_done(&resizor->width))
|
||||
resizor->width.target = allocation.width;
|
||||
resizor->height.current = allocation.height;
|
||||
if (spring_done(&resizor->height))
|
||||
resizor->height.target = allocation.height;
|
||||
|
||||
switch (sym) {
|
||||
case XKB_KEY_Up:
|
||||
if (allocation.height < 400)
|
||||
break;
|
||||
|
||||
resizor->height.target = allocation.height - 200;
|
||||
break;
|
||||
|
||||
case XKB_KEY_Down:
|
||||
if (allocation.height > 1000)
|
||||
break;
|
||||
|
||||
resizor->height.target = allocation.height + 200;
|
||||
break;
|
||||
|
||||
case XKB_KEY_Left:
|
||||
if (allocation.width < 400)
|
||||
break;
|
||||
|
||||
resizor->width.target = allocation.width - 200;
|
||||
break;
|
||||
|
||||
case XKB_KEY_Right:
|
||||
if (allocation.width > 1000)
|
||||
break;
|
||||
|
||||
resizor->width.target = allocation.width + 200;
|
||||
break;
|
||||
|
||||
case XKB_KEY_Escape:
|
||||
display_exit(resizor->display);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!resizor->frame_callback)
|
||||
frame_callback(resizor, NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
menu_func(void *data, struct input *input, int index)
|
||||
{
|
||||
fprintf(stderr, "picked entry %d\n", index);
|
||||
}
|
||||
|
||||
static void
|
||||
show_menu(struct resizor *resizor, struct input *input, uint32_t time)
|
||||
{
|
||||
int32_t x, y;
|
||||
static const char *entries[] = {
|
||||
"Roy", "Pris", "Leon", "Zhora"
|
||||
};
|
||||
|
||||
input_get_position(input, &x, &y);
|
||||
window_show_menu(resizor->display, input, time, resizor->window,
|
||||
x - 10, y - 10, menu_func, entries, 4);
|
||||
}
|
||||
|
||||
static void
|
||||
locked_pointer_handle_motion(struct window *window,
|
||||
struct input *input,
|
||||
uint32_t time,
|
||||
float dx,
|
||||
float dy,
|
||||
void *data)
|
||||
{
|
||||
struct resizor *resizor = data;
|
||||
|
||||
resizor->width.current += dx;
|
||||
resizor->width.previous = resizor->width.current;
|
||||
resizor->width.target = resizor->width.current;
|
||||
|
||||
resizor->height.current += dy;
|
||||
resizor->height.previous = resizor->height.current;
|
||||
resizor->height.target = resizor->height.current;
|
||||
|
||||
widget_schedule_resize(resizor->widget,
|
||||
resizor->width.current,
|
||||
resizor->height.current);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_pointer_locked(struct window *window, struct input *input, void *data)
|
||||
{
|
||||
struct resizor *resizor = data;
|
||||
|
||||
resizor->pointer_locked = true;
|
||||
input_set_pointer_image(input, CURSOR_BLANK);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_pointer_unlocked(struct window *window, struct input *input, void *data)
|
||||
{
|
||||
struct resizor *resizor = data;
|
||||
|
||||
resizor->pointer_locked = false;
|
||||
input_set_pointer_image(input, CURSOR_LEFT_PTR);
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener locked_pointer_frame_listener;
|
||||
|
||||
static void
|
||||
locked_pointer_frame_callback(void *data,
|
||||
struct wl_callback *callback,
|
||||
uint32_t time)
|
||||
{
|
||||
struct resizor *resizor = data;
|
||||
struct wl_surface *surface;
|
||||
struct rectangle allocation;
|
||||
float x, y;
|
||||
|
||||
if (resizor->pointer_locked) {
|
||||
widget_get_allocation(resizor->widget, &allocation);
|
||||
|
||||
x = resizor->pointer_x + (allocation.width - allocation.x);
|
||||
y = resizor->pointer_y + (allocation.height - allocation.y);
|
||||
|
||||
widget_set_locked_pointer_cursor_hint(resizor->widget, x, y);
|
||||
}
|
||||
|
||||
wl_callback_destroy(callback);
|
||||
|
||||
surface = window_get_wl_surface(resizor->window);
|
||||
callback = wl_surface_frame(surface);
|
||||
wl_callback_add_listener(callback,
|
||||
&locked_pointer_frame_listener,
|
||||
resizor);
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener locked_pointer_frame_listener = {
|
||||
locked_pointer_frame_callback
|
||||
};
|
||||
|
||||
static void
|
||||
button_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t button, enum wl_pointer_button_state state, void *data)
|
||||
{
|
||||
struct resizor *resizor = data;
|
||||
struct rectangle allocation;
|
||||
struct wl_surface *surface;
|
||||
struct wl_callback *callback;
|
||||
|
||||
if (button == BTN_RIGHT && state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
show_menu(resizor, input, time);
|
||||
} else if (button == BTN_LEFT &&
|
||||
state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
window_get_allocation(resizor->window, &allocation);
|
||||
|
||||
resizor->width.current = allocation.width;
|
||||
resizor->width.previous = allocation.width;
|
||||
resizor->width.target = allocation.width;
|
||||
|
||||
resizor->height.current = allocation.height;
|
||||
resizor->height.previous = allocation.height;
|
||||
resizor->height.target = allocation.height;
|
||||
|
||||
window_lock_pointer(resizor->window, input);
|
||||
window_set_pointer_locked_handler(resizor->window,
|
||||
handle_pointer_locked,
|
||||
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);
|
||||
window_unlock_pointer(resizor->window);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_cursor_inv_offset(struct resizor *resizor, float x, float y)
|
||||
{
|
||||
struct rectangle allocation;
|
||||
|
||||
widget_get_allocation(resizor->widget, &allocation);
|
||||
|
||||
resizor->pointer_x = x - (allocation.width - allocation.x);
|
||||
resizor->pointer_y = y - (allocation.height - allocation.y);
|
||||
}
|
||||
|
||||
static int
|
||||
enter_handler(struct widget *widget,
|
||||
struct input *input,
|
||||
float x, float y, void *data)
|
||||
{
|
||||
struct resizor *resizor = data;
|
||||
|
||||
set_cursor_inv_offset(resizor, x , y);
|
||||
|
||||
return CURSOR_LEFT_PTR;
|
||||
}
|
||||
|
||||
static int
|
||||
motion_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
float x, float y, void *data)
|
||||
{
|
||||
struct resizor *resizor = data;
|
||||
|
||||
set_cursor_inv_offset(resizor, x , y);
|
||||
|
||||
return CURSOR_LEFT_PTR;
|
||||
}
|
||||
|
||||
static struct resizor *
|
||||
resizor_create(struct display *display)
|
||||
{
|
||||
struct resizor *resizor;
|
||||
|
||||
resizor = xzalloc(sizeof *resizor);
|
||||
resizor->window = window_create(display);
|
||||
resizor->widget = window_frame_create(resizor->window, resizor);
|
||||
window_set_title(resizor->window, "Wayland Resizor");
|
||||
resizor->display = display;
|
||||
|
||||
window_set_key_handler(resizor->window, key_handler);
|
||||
window_set_user_data(resizor->window, resizor);
|
||||
widget_set_redraw_handler(resizor->widget, redraw_handler);
|
||||
window_set_keyboard_focus_handler(resizor->window,
|
||||
keyboard_focus_handler);
|
||||
|
||||
widget_set_enter_handler(resizor->widget, enter_handler);
|
||||
widget_set_motion_handler(resizor->widget, motion_handler);
|
||||
|
||||
window_set_locked_pointer_motion_handler(
|
||||
resizor->window, locked_pointer_handle_motion);
|
||||
|
||||
widget_set_button_handler(resizor->widget, button_handler);
|
||||
|
||||
resizor->height.previous = 400;
|
||||
resizor->height.current = 400;
|
||||
resizor->height.target = 400;
|
||||
|
||||
resizor->width.previous = 400;
|
||||
resizor->width.current = 400;
|
||||
resizor->width.target = 400;
|
||||
|
||||
widget_schedule_resize(resizor->widget, 400, 400);
|
||||
|
||||
return resizor;
|
||||
}
|
||||
|
||||
static void
|
||||
resizor_destroy(struct resizor *resizor)
|
||||
{
|
||||
if (resizor->frame_callback)
|
||||
wl_callback_destroy(resizor->frame_callback);
|
||||
|
||||
widget_destroy(resizor->widget);
|
||||
window_destroy(resizor->window);
|
||||
free(resizor);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct display *display;
|
||||
struct resizor *resizor;
|
||||
|
||||
display = display_create(&argc, argv);
|
||||
if (display == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
resizor = resizor_create(display);
|
||||
|
||||
display_run(display);
|
||||
|
||||
resizor_destroy(resizor);
|
||||
display_destroy(display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* Copyright © 2008 Kristian Høgsberg
|
||||
* Copyright © 2013 Collabora, Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <cairo.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
#include "window.h"
|
||||
#include "viewporter-client-protocol.h"
|
||||
|
||||
#define BUFFER_SCALE 2
|
||||
static const int BUFFER_WIDTH = 421 * BUFFER_SCALE;
|
||||
static const int BUFFER_HEIGHT = 337 * BUFFER_SCALE;
|
||||
static const int SURFACE_WIDTH = 55 * 4;
|
||||
static const int SURFACE_HEIGHT = 77 * 4;
|
||||
static const double RECT_X = 21 * BUFFER_SCALE; /* buffer coords */
|
||||
static const double RECT_Y = 25 * BUFFER_SCALE;
|
||||
static const double RECT_W = 55 * BUFFER_SCALE;
|
||||
static const double RECT_H = 77 * BUFFER_SCALE;
|
||||
|
||||
struct box {
|
||||
struct display *display;
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
int width, height;
|
||||
|
||||
struct wp_viewporter *viewporter;
|
||||
struct wp_viewport *viewport;
|
||||
|
||||
enum {
|
||||
MODE_NO_VIEWPORT,
|
||||
MODE_SRC_ONLY,
|
||||
MODE_DST_ONLY,
|
||||
MODE_SRC_DST
|
||||
} mode;
|
||||
};
|
||||
|
||||
static void
|
||||
set_my_viewport(struct box *box)
|
||||
{
|
||||
wl_fixed_t src_x, src_y, src_width, src_height;
|
||||
int32_t dst_width = SURFACE_WIDTH;
|
||||
int32_t dst_height = SURFACE_HEIGHT;
|
||||
|
||||
if (box->mode == MODE_NO_VIEWPORT)
|
||||
return;
|
||||
|
||||
/* Cut the green border in half, take white border fully in,
|
||||
* and black border fully out. The borders are 1px wide in buffer.
|
||||
*
|
||||
* The gl-renderer uses linear texture sampling, this means the
|
||||
* top and left edges go to 100% green, bottom goes to 50% blue/black,
|
||||
* right edge has thick white sliding to 50% red.
|
||||
*/
|
||||
src_x = wl_fixed_from_double((RECT_X + 0.5) / BUFFER_SCALE);
|
||||
src_y = wl_fixed_from_double((RECT_Y + 0.5) / BUFFER_SCALE);
|
||||
src_width = wl_fixed_from_double((RECT_W - 0.5) / BUFFER_SCALE);
|
||||
src_height = wl_fixed_from_double((RECT_H - 0.5) / BUFFER_SCALE);
|
||||
|
||||
switch (box->mode){
|
||||
case MODE_SRC_ONLY:
|
||||
/* In SRC_ONLY mode we're just cropping - in order
|
||||
* for the surface size to remain an integer, the
|
||||
* compositor will generate an error if we use a
|
||||
* fractional width or height.
|
||||
*
|
||||
* We use fractional width/height for the other cases
|
||||
* to ensure fractional values are still tested.
|
||||
*/
|
||||
src_width = wl_fixed_from_int(RECT_W / BUFFER_SCALE);
|
||||
src_height = wl_fixed_from_int(RECT_H / BUFFER_SCALE);
|
||||
wp_viewport_set_source(box->viewport, src_x, src_y,
|
||||
src_width, src_height);
|
||||
break;
|
||||
case MODE_DST_ONLY:
|
||||
wp_viewport_set_destination(box->viewport,
|
||||
dst_width, dst_height);
|
||||
break;
|
||||
case MODE_SRC_DST:
|
||||
wp_viewport_set_source(box->viewport, src_x, src_y,
|
||||
src_width, src_height);
|
||||
wp_viewport_set_destination(box->viewport,
|
||||
dst_width, dst_height);
|
||||
break;
|
||||
default:
|
||||
assert(!"not reached");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
resize_handler(struct widget *widget,
|
||||
int32_t width, int32_t height, void *data)
|
||||
{
|
||||
struct box *box = data;
|
||||
|
||||
/* Don't resize me */
|
||||
widget_set_size(box->widget, box->width, box->height);
|
||||
}
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct box *box = data;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
|
||||
surface = window_get_surface(box->window);
|
||||
if (surface == NULL ||
|
||||
cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "failed to create cairo egl surface\n");
|
||||
return;
|
||||
}
|
||||
|
||||
cr = cairo_create(surface);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_line_width(cr, 1.0);
|
||||
cairo_translate(cr, RECT_X, RECT_Y);
|
||||
|
||||
/* red background */
|
||||
cairo_set_source_rgba(cr, 255, 0, 0, 255);
|
||||
cairo_paint(cr);
|
||||
|
||||
/* blue box */
|
||||
cairo_set_source_rgba(cr, 0, 0, 255, 255);
|
||||
cairo_rectangle(cr, 0, 0, RECT_W, RECT_H);
|
||||
cairo_fill(cr);
|
||||
|
||||
/* black border outside the box */
|
||||
cairo_set_source_rgb(cr, 0, 0, 0);
|
||||
cairo_move_to(cr, 0, RECT_H + 0.5);
|
||||
cairo_line_to(cr, RECT_W, RECT_H + 0.5);
|
||||
cairo_stroke(cr);
|
||||
|
||||
/* white border inside the box */
|
||||
cairo_set_source_rgb(cr, 1, 1, 1);
|
||||
cairo_move_to(cr, RECT_W - 0.5, 0);
|
||||
cairo_line_to(cr, RECT_W - 0.5, RECT_H);
|
||||
cairo_stroke(cr);
|
||||
|
||||
/* the green border on inside the box, to be split half by crop */
|
||||
cairo_set_source_rgb(cr, 0, 1, 0);
|
||||
cairo_move_to(cr, 0.5, RECT_H);
|
||||
cairo_line_to(cr, 0.5, 0);
|
||||
cairo_move_to(cr, 0, 0.5);
|
||||
cairo_line_to(cr, RECT_W, 0.5);
|
||||
cairo_stroke(cr);
|
||||
|
||||
cairo_destroy(cr);
|
||||
|
||||
/* TODO: buffer_transform */
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
global_handler(struct display *display, uint32_t name,
|
||||
const char *interface, uint32_t version, void *data)
|
||||
{
|
||||
struct box *box = data;
|
||||
|
||||
if (strcmp(interface, "wp_viewporter") == 0) {
|
||||
box->viewporter = display_bind(display, name,
|
||||
&wp_viewporter_interface, 1);
|
||||
|
||||
box->viewport = wp_viewporter_get_viewport(box->viewporter,
|
||||
widget_get_wl_surface(box->widget));
|
||||
|
||||
set_my_viewport(box);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
button_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t button, enum wl_pointer_button_state state, void *data)
|
||||
{
|
||||
struct box *box = data;
|
||||
|
||||
if (button != BTN_LEFT)
|
||||
return;
|
||||
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
window_move(box->window, input,
|
||||
display_get_serial(box->display));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
touch_down_handler(struct widget *widget, struct input *input,
|
||||
uint32_t serial, uint32_t time, int32_t id,
|
||||
float x, float y, void *data)
|
||||
{
|
||||
struct box *box = data;
|
||||
window_move(box->window, input,
|
||||
display_get_serial(box->display));
|
||||
}
|
||||
|
||||
static void
|
||||
usage(const char *progname)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [mode]\n"
|
||||
"where 'mode' is one of\n"
|
||||
" -b\tset both src and dst in viewport (default)\n"
|
||||
" -d\tset only dst in viewport\n"
|
||||
" -s\tset only src in viewport\n"
|
||||
" -n\tdo not set viewport at all\n\n",
|
||||
progname);
|
||||
|
||||
fprintf(stderr, "Expected output with output_scale=1:\n");
|
||||
|
||||
fprintf(stderr, "Mode -n:\n"
|
||||
" window size %dx%d px\n"
|
||||
" Red box with a blue box in the upper left part.\n"
|
||||
" The blue box has white right edge, black bottom edge,\n"
|
||||
" and thin green left and top edges that can really\n"
|
||||
" be seen only when zoomed in.\n\n",
|
||||
BUFFER_WIDTH / BUFFER_SCALE, BUFFER_HEIGHT / BUFFER_SCALE);
|
||||
|
||||
fprintf(stderr, "Mode -b:\n"
|
||||
" window size %dx%d px\n"
|
||||
" Blue box with green top and left edge,\n"
|
||||
" thick white right edge with a hint of red,\n"
|
||||
" and a hint of black in bottom edge.\n\n",
|
||||
SURFACE_WIDTH, SURFACE_HEIGHT);
|
||||
|
||||
fprintf(stderr, "Mode -s:\n"
|
||||
" window size %.0fx%.0f px\n"
|
||||
" The same as mode -b, but scaled a lot smaller.\n\n",
|
||||
RECT_W / BUFFER_SCALE, RECT_H / BUFFER_SCALE);
|
||||
|
||||
fprintf(stderr, "Mode -d:\n"
|
||||
" window size %dx%d px\n"
|
||||
" This is horizontally squashed version of the -n mode.\n\n",
|
||||
SURFACE_WIDTH, SURFACE_HEIGHT);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct box box;
|
||||
struct display *d;
|
||||
struct timeval tv;
|
||||
int i;
|
||||
|
||||
box.mode = MODE_SRC_DST;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp("-s", argv[i]) == 0)
|
||||
box.mode = MODE_SRC_ONLY;
|
||||
else if (strcmp("-d", argv[i]) == 0)
|
||||
box.mode = MODE_DST_ONLY;
|
||||
else if (strcmp("-b", argv[i]) == 0)
|
||||
box.mode = MODE_SRC_DST;
|
||||
else if (strcmp("-n", argv[i]) == 0)
|
||||
box.mode = MODE_NO_VIEWPORT;
|
||||
else {
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
d = display_create(&argc, argv);
|
||||
if (d == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
srandom(tv.tv_usec);
|
||||
|
||||
box.width = BUFFER_WIDTH / BUFFER_SCALE;
|
||||
box.height = BUFFER_HEIGHT / BUFFER_SCALE;
|
||||
box.display = d;
|
||||
box.window = window_create(d);
|
||||
box.widget = window_add_widget(box.window, &box);
|
||||
window_set_title(box.window, "Scaler Test Box");
|
||||
window_set_buffer_scale(box.window, BUFFER_SCALE);
|
||||
|
||||
widget_set_resize_handler(box.widget, resize_handler);
|
||||
widget_set_redraw_handler(box.widget, redraw_handler);
|
||||
widget_set_button_handler(box.widget, button_handler);
|
||||
widget_set_default_cursor(box.widget, CURSOR_HAND1);
|
||||
widget_set_touch_down_handler(box.widget, touch_down_handler);
|
||||
|
||||
window_schedule_resize(box.window, box.width, box.height);
|
||||
|
||||
display_set_user_data(box.display, &box);
|
||||
display_set_global_handler(box.display, global_handler);
|
||||
|
||||
display_run(d);
|
||||
|
||||
widget_destroy(box.widget);
|
||||
window_destroy(box.window);
|
||||
display_destroy(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
* Copyright © 2008 Kristian Høgsberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/mman.h>
|
||||
#include <cairo.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include "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 */
|
||||
|
||||
|
||||
struct screenshooter_output {
|
||||
struct wl_output *output;
|
||||
struct wl_buffer *buffer;
|
||||
int width, height, offset_x, offset_y;
|
||||
void *data;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct buffer_size {
|
||||
int width, height;
|
||||
|
||||
int min_x, min_y;
|
||||
int max_x, max_y;
|
||||
};
|
||||
|
||||
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,
|
||||
int x,
|
||||
int y,
|
||||
int physical_width,
|
||||
int physical_height,
|
||||
int subpixel,
|
||||
const char *make,
|
||||
const char *model,
|
||||
int transform)
|
||||
{
|
||||
struct screenshooter_output *output;
|
||||
|
||||
output = wl_output_get_user_data(wl_output);
|
||||
|
||||
if (wl_output == output->output) {
|
||||
output->offset_x = x;
|
||||
output->offset_y = y;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
display_handle_mode(void *data,
|
||||
struct wl_output *wl_output,
|
||||
uint32_t flags,
|
||||
int width,
|
||||
int height,
|
||||
int refresh)
|
||||
{
|
||||
struct screenshooter_output *output;
|
||||
|
||||
output = wl_output_get_user_data(wl_output);
|
||||
|
||||
if (wl_output == output->output && (flags & WL_OUTPUT_MODE_CURRENT)) {
|
||||
output->width = width;
|
||||
output->height = height;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_output_listener output_listener = {
|
||||
display_handle_geometry,
|
||||
display_handle_mode
|
||||
};
|
||||
|
||||
static void
|
||||
screenshot_done(void *data, struct weston_screenshooter *screenshooter)
|
||||
{
|
||||
struct screenshooter_data *sh_data = data;
|
||||
sh_data->buffer_copy_done = 1;
|
||||
}
|
||||
|
||||
static const struct weston_screenshooter_listener screenshooter_listener = {
|
||||
screenshot_done
|
||||
};
|
||||
|
||||
static void
|
||||
handle_global(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version)
|
||||
{
|
||||
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(&sh_data->output_list, &output->link);
|
||||
wl_output_add_listener(output->output, &output_listener, output);
|
||||
} else if (strcmp(interface, "wl_shm") == 0) {
|
||||
sh_data->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
|
||||
} else if (strcmp(interface, "weston_screenshooter") == 0) {
|
||||
sh_data->screenshooter = wl_registry_bind(registry, name,
|
||||
&weston_screenshooter_interface,
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
|
||||
{
|
||||
/* XXX: unimplemented */
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
handle_global,
|
||||
handle_global_remove
|
||||
};
|
||||
|
||||
static struct wl_buffer *
|
||||
screenshot_create_shm_buffer(int width, int height, void **data_out,
|
||||
struct wl_shm *shm)
|
||||
{
|
||||
struct wl_shm_pool *pool;
|
||||
struct wl_buffer *buffer;
|
||||
int fd, size, stride;
|
||||
void *data;
|
||||
|
||||
stride = width * 4;
|
||||
size = stride * height;
|
||||
|
||||
fd = os_create_anonymous_file(size);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
|
||||
size, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pool = wl_shm_create_pool(shm, fd, size);
|
||||
close(fd);
|
||||
buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
|
||||
WL_SHM_FORMAT_XRGB8888);
|
||||
wl_shm_pool_destroy(pool);
|
||||
|
||||
*data_out = data;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void
|
||||
screenshot_write_png(const struct buffer_size *buff_size,
|
||||
struct wl_list *output_list)
|
||||
{
|
||||
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 = buff_size->width * 4;
|
||||
|
||||
data = xmalloc(buffer_stride * buff_size->height);
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
wl_list_for_each_safe(output, next, output_list, link) {
|
||||
output_stride = output->width * 4;
|
||||
s = output->data;
|
||||
d = data + (output->offset_y - buff_size->min_y) * buffer_stride +
|
||||
(output->offset_x - buff_size->min_x) * 4;
|
||||
|
||||
for (i = 0; i < output->height; i++) {
|
||||
memcpy(d, s, output_stride);
|
||||
d += buffer_stride;
|
||||
s += output_stride;
|
||||
}
|
||||
|
||||
free(output);
|
||||
}
|
||||
|
||||
surface = cairo_image_surface_create_for_data(data,
|
||||
CAIRO_FORMAT_ARGB32,
|
||||
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
|
||||
screenshot_set_buffer_size(struct buffer_size *buff_size, struct wl_list *output_list)
|
||||
{
|
||||
struct screenshooter_output *output;
|
||||
buff_size->min_x = buff_size->min_y = INT_MAX;
|
||||
buff_size->max_x = buff_size->max_y = INT_MIN;
|
||||
int position = 0;
|
||||
|
||||
wl_list_for_each_reverse(output, output_list, link) {
|
||||
output->offset_x = position;
|
||||
position += output->width;
|
||||
}
|
||||
|
||||
wl_list_for_each(output, output_list, link) {
|
||||
buff_size->min_x = MIN(buff_size->min_x, output->offset_x);
|
||||
buff_size->min_y = MIN(buff_size->min_y, output->offset_y);
|
||||
buff_size->max_x =
|
||||
MAX(buff_size->max_x, output->offset_x + output->width);
|
||||
buff_size->max_y =
|
||||
MAX(buff_size->max_y, output->offset_y + output->height);
|
||||
}
|
||||
|
||||
if (buff_size->max_x <= buff_size->min_x ||
|
||||
buff_size->max_y <= buff_size->min_y)
|
||||
return -1;
|
||||
|
||||
buff_size->width = buff_size->max_x - buff_size->min_x;
|
||||
buff_size->height = buff_size->max_y - buff_size->min_y;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct screenshooter_output *output;
|
||||
struct buffer_size buff_size = {};
|
||||
struct screenshooter_data sh_data = {};
|
||||
|
||||
display = wl_display_connect(NULL);
|
||||
if (display == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
wl_list_init(&sh_data.output_list);
|
||||
registry = wl_display_get_registry(display);
|
||||
wl_registry_add_listener(registry, ®istry_listener, &sh_data);
|
||||
wl_display_dispatch(display);
|
||||
wl_display_roundtrip(display);
|
||||
if (sh_data.screenshooter == NULL) {
|
||||
fprintf(stderr, "display doesn't support screenshooter\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
weston_screenshooter_add_listener(sh_data.screenshooter,
|
||||
&screenshooter_listener,
|
||||
&sh_data);
|
||||
|
||||
if (screenshot_set_buffer_size(&buff_size, &sh_data.output_list))
|
||||
return -1;
|
||||
|
||||
|
||||
wl_list_for_each(output, &sh_data.output_list, link) {
|
||||
output->buffer =
|
||||
screenshot_create_shm_buffer(output->width,
|
||||
output->height,
|
||||
&output->data,
|
||||
sh_data.shm);
|
||||
weston_screenshooter_shoot(sh_data.screenshooter,
|
||||
output->output,
|
||||
output->buffer);
|
||||
sh_data.buffer_copy_done = 0;
|
||||
while (!sh_data.buffer_copy_done)
|
||||
wl_display_roundtrip(display);
|
||||
}
|
||||
|
||||
screenshot_write_png(&buff_size, &sh_data.output_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,957 @@
|
|||
/*
|
||||
* Copyright © 2014 Jason Ekstrand
|
||||
* Copyright © 2011 Benjamin Franzke
|
||||
* Copyright © 2010 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include "shared/os-compatibility.h"
|
||||
#include <libweston/zalloc.h>
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
#include "fullscreen-shell-unstable-v1-client-protocol.h"
|
||||
#include "viewporter-client-protocol.h"
|
||||
|
||||
int print_debug = 0;
|
||||
|
||||
struct display {
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
int compositor_version;
|
||||
struct wl_compositor *compositor;
|
||||
struct wp_viewporter *viewporter;
|
||||
struct xdg_wm_base *wm_base;
|
||||
struct zwp_fullscreen_shell_v1 *fshell;
|
||||
struct wl_shm *shm;
|
||||
uint32_t formats;
|
||||
};
|
||||
|
||||
struct buffer {
|
||||
struct wl_buffer *buffer;
|
||||
uint32_t *shm_data;
|
||||
int busy;
|
||||
};
|
||||
|
||||
enum window_flags {
|
||||
WINDOW_FLAG_USE_VIEWPORT = 0x1,
|
||||
WINDOW_FLAG_ROTATING_TRANSFORM = 0x2,
|
||||
WINDOW_FLAG_USE_DAMAGE_BUFFER = 0x4,
|
||||
};
|
||||
|
||||
struct window {
|
||||
struct display *display;
|
||||
int width, height, border;
|
||||
struct wl_surface *surface;
|
||||
struct wp_viewport *viewport;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
struct wl_callback *callback;
|
||||
struct buffer buffers[2];
|
||||
struct buffer *prev_buffer;
|
||||
bool wait_for_configure;
|
||||
|
||||
enum window_flags flags;
|
||||
int scale;
|
||||
enum wl_output_transform transform;
|
||||
|
||||
struct {
|
||||
float x, y; /* position in pixels */
|
||||
float dx, dy; /* velocity in pixels/second */
|
||||
int radius; /* radius in pixels */
|
||||
uint32_t prev_time;
|
||||
} ball;
|
||||
};
|
||||
|
||||
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
|
||||
create_shm_buffer(struct display *display, struct buffer *buffer,
|
||||
int width, int height, uint32_t format)
|
||||
{
|
||||
struct wl_shm_pool *pool;
|
||||
int fd, size, pitch;
|
||||
void *data;
|
||||
|
||||
pitch = width * 4;
|
||||
size = pitch * height;
|
||||
|
||||
fd = os_create_anonymous_file(size);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
|
||||
size, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pool = wl_shm_create_pool(display->shm, fd, size);
|
||||
buffer->buffer = wl_shm_pool_create_buffer(pool, 0,
|
||||
width, height,
|
||||
pitch, format);
|
||||
wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer);
|
||||
wl_shm_pool_destroy(pool);
|
||||
close(fd);
|
||||
|
||||
buffer->shm_data = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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->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 float
|
||||
bounded_randf(float a, float b)
|
||||
{
|
||||
return a + ((float)rand() / (float)RAND_MAX) * (b - a);
|
||||
}
|
||||
|
||||
static void
|
||||
window_init_game(struct window *window)
|
||||
{
|
||||
int ax1, ay1, ax2, ay2; /* playable arena size */
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
srand(tv.tv_usec);
|
||||
|
||||
window->ball.radius = 10;
|
||||
|
||||
ax1 = window->border + window->ball.radius;
|
||||
ay1 = window->border + window->ball.radius;
|
||||
ax2 = window->width - window->border - window->ball.radius;
|
||||
ay2 = window->height - window->border - window->ball.radius;
|
||||
|
||||
window->ball.x = bounded_randf(ax1, ax2);
|
||||
window->ball.y = bounded_randf(ay1, ay2);
|
||||
|
||||
window->ball.dx = bounded_randf(0, window->width);
|
||||
window->ball.dy = bounded_randf(0, window->height);
|
||||
|
||||
window->ball.prev_time = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
window_advance_game(struct window *window, uint32_t timestamp)
|
||||
{
|
||||
int ax1, ay1, ax2, ay2; /* Arena size */
|
||||
float dt;
|
||||
|
||||
if (window->ball.prev_time == 0) {
|
||||
/* first pass, don't do anything */
|
||||
window->ball.prev_time = timestamp;
|
||||
return;
|
||||
}
|
||||
|
||||
/* dt in seconds */
|
||||
dt = (float)(timestamp - window->ball.prev_time) / 1000.0f;
|
||||
|
||||
ax1 = window->border + window->ball.radius;
|
||||
ay1 = window->border + window->ball.radius;
|
||||
ax2 = window->width - window->border - window->ball.radius;
|
||||
ay2 = window->height - window->border - window->ball.radius;
|
||||
|
||||
window->ball.x += window->ball.dx * dt;
|
||||
while (window->ball.x < ax1 || ax2 < window->ball.x) {
|
||||
if (window->ball.x < ax1)
|
||||
window->ball.x = 2 * ax1 - window->ball.x;
|
||||
if (ax2 <= window->ball.x)
|
||||
window->ball.x = 2 * ax2 - window->ball.x;
|
||||
|
||||
window->ball.dx *= -1.0f;
|
||||
}
|
||||
|
||||
window->ball.y += window->ball.dy * dt;
|
||||
while (window->ball.y < ay1 || ay2 < window->ball.y) {
|
||||
if (window->ball.y < ay1)
|
||||
window->ball.y = 2 * ay1 - window->ball.y;
|
||||
if (ay2 <= window->ball.y)
|
||||
window->ball.y = 2 * ay2 - window->ball.y;
|
||||
|
||||
window->ball.dy *= -1.0f;
|
||||
}
|
||||
|
||||
window->ball.prev_time = timestamp;
|
||||
}
|
||||
|
||||
static struct window *
|
||||
create_window(struct display *display, int width, int height,
|
||||
enum wl_output_transform transform, int scale,
|
||||
enum window_flags flags)
|
||||
{
|
||||
struct window *window;
|
||||
|
||||
if (display->compositor_version < 2 &&
|
||||
(transform != WL_OUTPUT_TRANSFORM_NORMAL ||
|
||||
flags & WINDOW_FLAG_ROTATING_TRANSFORM)) {
|
||||
fprintf(stderr, "wl_surface.buffer_transform unsupported in "
|
||||
"wl_surface version %d\n",
|
||||
display->compositor_version);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (display->compositor_version < 3 &&
|
||||
(! (flags & WINDOW_FLAG_USE_VIEWPORT)) && scale != 1) {
|
||||
fprintf(stderr, "wl_surface.buffer_scale unsupported in "
|
||||
"wl_surface version %d\n",
|
||||
display->compositor_version);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (display->viewporter == NULL && (flags & WINDOW_FLAG_USE_VIEWPORT)) {
|
||||
fprintf(stderr, "Compositor does not support wp_viewport");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (display->compositor_version <
|
||||
WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION &&
|
||||
(flags & WINDOW_FLAG_USE_DAMAGE_BUFFER)) {
|
||||
fprintf(stderr, "wl_surface.damage_buffer unsupported in "
|
||||
"wl_surface version %d\n",
|
||||
display->compositor_version);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
window = zalloc(sizeof *window);
|
||||
if (!window)
|
||||
return NULL;
|
||||
|
||||
window->callback = NULL;
|
||||
window->display = display;
|
||||
window->width = width;
|
||||
window->height = height;
|
||||
window->border = 10;
|
||||
window->flags = flags;
|
||||
window->transform = transform;
|
||||
window->scale = scale;
|
||||
|
||||
window_init_game(window);
|
||||
|
||||
window->surface = wl_compositor_create_surface(display->compositor);
|
||||
|
||||
if (window->flags & WINDOW_FLAG_USE_VIEWPORT)
|
||||
window->viewport = wp_viewporter_get_viewport(display->viewporter,
|
||||
window->surface);
|
||||
|
||||
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-damage");
|
||||
|
||||
window->wait_for_configure = true;
|
||||
wl_surface_commit(window->surface);
|
||||
} else if (display->fshell) {
|
||||
zwp_fullscreen_shell_v1_present_surface(display->fshell,
|
||||
window->surface,
|
||||
ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT,
|
||||
NULL);
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* Initialise damage to full surface, so the padding gets painted */
|
||||
if (window->flags & WINDOW_FLAG_USE_DAMAGE_BUFFER) {
|
||||
wl_surface_damage_buffer(window->surface, 0, 0,
|
||||
INT32_MAX, INT32_MAX);
|
||||
} else {
|
||||
wl_surface_damage(window->surface, 0, 0, INT32_MAX, INT32_MAX);
|
||||
}
|
||||
return window;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_window(struct window *window)
|
||||
{
|
||||
if (window->callback)
|
||||
wl_callback_destroy(window->callback);
|
||||
|
||||
if (window->buffers[0].buffer)
|
||||
wl_buffer_destroy(window->buffers[0].buffer);
|
||||
if (window->buffers[1].buffer)
|
||||
wl_buffer_destroy(window->buffers[1].buffer);
|
||||
|
||||
if (window->xdg_toplevel)
|
||||
xdg_toplevel_destroy(window->xdg_toplevel);
|
||||
if (window->xdg_surface)
|
||||
xdg_surface_destroy(window->xdg_surface);
|
||||
if (window->viewport)
|
||||
wp_viewport_destroy(window->viewport);
|
||||
wl_surface_destroy(window->surface);
|
||||
free(window);
|
||||
}
|
||||
|
||||
static struct buffer *
|
||||
window_next_buffer(struct window *window)
|
||||
{
|
||||
struct buffer *buffer;
|
||||
int ret = 0, bwidth, bheight;
|
||||
|
||||
if (!window->buffers[0].busy)
|
||||
buffer = &window->buffers[0];
|
||||
else if (!window->buffers[1].busy)
|
||||
buffer = &window->buffers[1];
|
||||
else
|
||||
return NULL;
|
||||
|
||||
switch (window->transform) {
|
||||
default:
|
||||
case WL_OUTPUT_TRANSFORM_NORMAL:
|
||||
case WL_OUTPUT_TRANSFORM_180:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
|
||||
bwidth = window->width * window->scale;
|
||||
bheight = window->height * window->scale;
|
||||
break;
|
||||
case WL_OUTPUT_TRANSFORM_90:
|
||||
case WL_OUTPUT_TRANSFORM_270:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
|
||||
bwidth = window->height * window->scale;
|
||||
bheight = window->width * window->scale;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!buffer->buffer) {
|
||||
ret = create_shm_buffer(window->display, buffer,
|
||||
bwidth, bheight,
|
||||
WL_SHM_FORMAT_ARGB8888);
|
||||
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void
|
||||
paint_box(uint32_t *pixels, int pitch, int x, int y, int width, int height,
|
||||
uint32_t color)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (j = y; j < y + height; ++j)
|
||||
for (i = x; i < x + width; ++i)
|
||||
pixels[i + j * pitch] = color;
|
||||
}
|
||||
|
||||
static void
|
||||
paint_circle(uint32_t *pixels, int pitch, float x, float y, int radius,
|
||||
uint32_t color)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (j = y - radius; j <= (int)(y + radius); ++j)
|
||||
for (i = x - radius; i <= (int)(x + radius); ++i)
|
||||
if ((j+0.5f-y)*(j+0.5f-y) + (i+0.5f-x)*(i+0.5f-x) <= radius * radius)
|
||||
pixels[i + j * pitch] = color;
|
||||
}
|
||||
|
||||
static void
|
||||
window_get_transformed_ball(struct window *window, float *bx, float *by)
|
||||
{
|
||||
float wx, wy;
|
||||
|
||||
wx = window->ball.x;
|
||||
wy = window->ball.y;
|
||||
|
||||
switch (window->transform) {
|
||||
default:
|
||||
case WL_OUTPUT_TRANSFORM_NORMAL:
|
||||
*bx = wx;
|
||||
*by = wy;
|
||||
break;
|
||||
case WL_OUTPUT_TRANSFORM_90:
|
||||
*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 = 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 = wy;
|
||||
*by = wx;
|
||||
break;
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
|
||||
*bx = wx;
|
||||
*by = window->height - wy;
|
||||
break;
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
|
||||
*bx = window->height - wy;
|
||||
*by = window->width - wx;
|
||||
break;
|
||||
}
|
||||
|
||||
*bx *= window->scale;
|
||||
*by *= window->scale;
|
||||
|
||||
if (window->viewport) {
|
||||
/* We're drawing half-size because of the viewport */
|
||||
*bx /= 2;
|
||||
*by /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
int off_x = 0, off_y = 0;
|
||||
int bwidth, bheight, bborder, bpitch, bradius;
|
||||
float bx, by;
|
||||
|
||||
buffer = window_next_buffer(window);
|
||||
if (!buffer) {
|
||||
fprintf(stderr,
|
||||
!callback ? "Failed to create the first buffer.\n" :
|
||||
"Both buffers busy at redraw(). Server bug?\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Rotate the damage, but keep the even/odd parity so the
|
||||
* dimensions of the buffers don't change */
|
||||
if (window->flags & WINDOW_FLAG_ROTATING_TRANSFORM)
|
||||
window->transform = (window->transform + 2) % 8;
|
||||
|
||||
switch (window->transform) {
|
||||
default:
|
||||
case WL_OUTPUT_TRANSFORM_NORMAL:
|
||||
case WL_OUTPUT_TRANSFORM_180:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
|
||||
bwidth = window->width * window->scale;
|
||||
bheight = window->height * window->scale;
|
||||
break;
|
||||
case WL_OUTPUT_TRANSFORM_90:
|
||||
case WL_OUTPUT_TRANSFORM_270:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
|
||||
bwidth = window->height * window->scale;
|
||||
bheight = window->width * window->scale;
|
||||
break;
|
||||
}
|
||||
|
||||
bpitch = bwidth;
|
||||
|
||||
bborder = window->border * window->scale;
|
||||
bradius = window->ball.radius * window->scale;
|
||||
|
||||
if (window->viewport) {
|
||||
int tx, ty;
|
||||
/* Fill the whole thing with red to detect viewport errors */
|
||||
paint_box(buffer->shm_data, bpitch, 0, 0, bwidth, bheight,
|
||||
0xffff0000);
|
||||
|
||||
/* The buffer is the same size. However, we crop it
|
||||
* and scale it up by a factor of 2 */
|
||||
bborder /= 2;
|
||||
bradius /= 2;
|
||||
bwidth /= 2;
|
||||
bheight /= 2;
|
||||
|
||||
/* Offset the drawing region */
|
||||
tx = (window->width / 3) * window->scale;
|
||||
ty = (window->height / 5) * window->scale;
|
||||
switch (window->transform) {
|
||||
default:
|
||||
case WL_OUTPUT_TRANSFORM_NORMAL:
|
||||
off_y = ty;
|
||||
off_x = tx;
|
||||
break;
|
||||
case WL_OUTPUT_TRANSFORM_90:
|
||||
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 = 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 = 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 = bheight - tx;
|
||||
off_x = bwidth - ty;
|
||||
break;
|
||||
}
|
||||
wp_viewport_set_source(window->viewport,
|
||||
wl_fixed_from_int(window->width / 3),
|
||||
wl_fixed_from_int(window->height / 5),
|
||||
wl_fixed_from_int(window->width / 2),
|
||||
wl_fixed_from_int(window->height / 2));
|
||||
}
|
||||
|
||||
/* Paint the border */
|
||||
paint_box(buffer->shm_data, bpitch, off_x, off_y,
|
||||
bwidth, bborder, 0xffffffff);
|
||||
paint_box(buffer->shm_data, bpitch, off_x, off_y,
|
||||
bborder, bheight, 0xffffffff);
|
||||
paint_box(buffer->shm_data, bpitch, off_x + bwidth - bborder, off_y,
|
||||
bborder, bheight, 0xffffffff);
|
||||
paint_box(buffer->shm_data, bpitch, off_x, off_y + bheight - bborder,
|
||||
bwidth, bborder, 0xffffffff);
|
||||
|
||||
/* fill with translucent */
|
||||
paint_box(buffer->shm_data, bpitch, off_x + bborder, off_y + bborder,
|
||||
bwidth - 2 * bborder, bheight - 2 * bborder, 0x80000000);
|
||||
|
||||
/* Damage where the ball was */
|
||||
if (window->flags & WINDOW_FLAG_USE_DAMAGE_BUFFER) {
|
||||
window_get_transformed_ball(window, &bx, &by);
|
||||
wl_surface_damage_buffer(window->surface,
|
||||
bx - bradius + off_x,
|
||||
by - bradius + off_y,
|
||||
bradius * 2 + 1,
|
||||
bradius * 2 + 1);
|
||||
} else {
|
||||
wl_surface_damage(window->surface,
|
||||
window->ball.x - window->ball.radius,
|
||||
window->ball.y - window->ball.radius,
|
||||
window->ball.radius * 2 + 1,
|
||||
window->ball.radius * 2 + 1);
|
||||
}
|
||||
window_advance_game(window, time);
|
||||
|
||||
window_get_transformed_ball(window, &bx, &by);
|
||||
|
||||
/* Paint the ball */
|
||||
paint_circle(buffer->shm_data, bpitch, off_x + bx, off_y + by,
|
||||
bradius, 0xff00ff00);
|
||||
|
||||
if (print_debug) {
|
||||
printf("Ball now located at (%f, %f)\n",
|
||||
window->ball.x, window->ball.y);
|
||||
|
||||
printf("Circle painted at (%f, %f), radius %d\n", bx, by,
|
||||
bradius);
|
||||
|
||||
printf("Buffer damage rectangle: (%d, %d) @ %dx%d\n",
|
||||
(int)(bx - bradius) + off_x,
|
||||
(int)(by - bradius) + off_y,
|
||||
bradius * 2 + 1, bradius * 2 + 1);
|
||||
}
|
||||
|
||||
/* Damage where the ball is now */
|
||||
if (window->flags & WINDOW_FLAG_USE_DAMAGE_BUFFER) {
|
||||
wl_surface_damage_buffer(window->surface,
|
||||
bx - bradius + off_x,
|
||||
by - bradius + off_y,
|
||||
bradius * 2 + 1,
|
||||
bradius * 2 + 1);
|
||||
} else {
|
||||
wl_surface_damage(window->surface,
|
||||
window->ball.x - window->ball.radius,
|
||||
window->ball.y - window->ball.radius,
|
||||
window->ball.radius * 2 + 1,
|
||||
window->ball.radius * 2 + 1);
|
||||
}
|
||||
wl_surface_attach(window->surface, buffer->buffer, 0, 0);
|
||||
|
||||
if (window->display->compositor_version >= 2 &&
|
||||
(window->transform != WL_OUTPUT_TRANSFORM_NORMAL ||
|
||||
window->flags & WINDOW_FLAG_ROTATING_TRANSFORM))
|
||||
wl_surface_set_buffer_transform(window->surface,
|
||||
window->transform);
|
||||
|
||||
if (window->viewport)
|
||||
wp_viewport_set_destination(window->viewport,
|
||||
window->width,
|
||||
window->height);
|
||||
|
||||
if (window->scale != 1)
|
||||
wl_surface_set_buffer_scale(window->surface,
|
||||
window->scale);
|
||||
|
||||
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
|
||||
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
||||
{
|
||||
struct display *d = data;
|
||||
|
||||
d->formats |= (1 << format);
|
||||
}
|
||||
|
||||
struct wl_shm_listener shm_listener = {
|
||||
shm_format
|
||||
};
|
||||
|
||||
static void
|
||||
xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
|
||||
{
|
||||
xdg_wm_base_pong(shell, serial);
|
||||
}
|
||||
|
||||
static const struct xdg_wm_base_listener wm_base_listener = {
|
||||
xdg_wm_base_ping,
|
||||
};
|
||||
|
||||
static void
|
||||
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) {
|
||||
if (d->compositor_version > (int)version) {
|
||||
fprintf(stderr, "Compositor does not support "
|
||||
"wl_surface version %d\n", d->compositor_version);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (d->compositor_version < 0)
|
||||
d->compositor_version = version;
|
||||
|
||||
d->compositor =
|
||||
wl_registry_bind(registry,
|
||||
id, &wl_compositor_interface,
|
||||
d->compositor_version);
|
||||
} else if (strcmp(interface, "wp_viewporter") == 0) {
|
||||
d->viewporter = wl_registry_bind(registry, id,
|
||||
&wp_viewporter_interface, 1);
|
||||
} else if (strcmp(interface, "xdg_wm_base") == 0) {
|
||||
d->wm_base = wl_registry_bind(registry,
|
||||
id, &xdg_wm_base_interface, 1);
|
||||
xdg_wm_base_add_listener(d->wm_base, &wm_base_listener, d);
|
||||
} else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
|
||||
d->fshell = wl_registry_bind(registry,
|
||||
id, &zwp_fullscreen_shell_v1_interface, 1);
|
||||
} else if (strcmp(interface, "wl_shm") == 0) {
|
||||
d->shm = wl_registry_bind(registry,
|
||||
id, &wl_shm_interface, 1);
|
||||
wl_shm_add_listener(d->shm, &shm_listener, d);
|
||||
}
|
||||
}
|
||||
|
||||
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(int version)
|
||||
{
|
||||
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);
|
||||
|
||||
display->compositor_version = version;
|
||||
display->formats = 0;
|
||||
display->registry = wl_display_get_registry(display->display);
|
||||
wl_registry_add_listener(display->registry,
|
||||
®istry_listener, display);
|
||||
wl_display_roundtrip(display->display);
|
||||
if (display->shm == NULL) {
|
||||
fprintf(stderr, "No wl_shm global\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
wl_display_roundtrip(display->display);
|
||||
|
||||
if (!(display->formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
|
||||
fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_display(struct display *display)
|
||||
{
|
||||
if (display->shm)
|
||||
wl_shm_destroy(display->shm);
|
||||
|
||||
if (display->wm_base)
|
||||
xdg_wm_base_destroy(display->wm_base);
|
||||
|
||||
if (display->fshell)
|
||||
zwp_fullscreen_shell_v1_release(display->fshell);
|
||||
|
||||
if (display->viewporter)
|
||||
wp_viewporter_destroy(display->viewporter);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void
|
||||
print_usage(int retval)
|
||||
{
|
||||
printf(
|
||||
"usage: weston-simple-damage [options]\n\n"
|
||||
"options:\n"
|
||||
" -h, --help\t\tPring this help\n"
|
||||
" --verbose\t\tPrint verbose log information\n"
|
||||
" --version=VERSION\tVersion of wl_surface to use\n"
|
||||
" --width=WIDTH\t\tWidth of the window\n"
|
||||
" --height=HEIGHT\tHeight of the window\n"
|
||||
" --scale=SCALE\t\tScale factor for the surface\n"
|
||||
" --transform=TRANSFORM\tTransform for the surface\n"
|
||||
" --rotating-transform\tUse a different buffer_transform for each frame\n"
|
||||
" --use-viewport\tUse wp_viewport\n"
|
||||
" --use-damage-buffer\tUse damage_buffer to post damage\n"
|
||||
);
|
||||
|
||||
exit(retval);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_transform(const char *str, enum wl_output_transform *transform)
|
||||
{
|
||||
int i;
|
||||
static const struct {
|
||||
const char *name;
|
||||
enum wl_output_transform transform;
|
||||
} names[] = {
|
||||
{ "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 },
|
||||
};
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (strcmp(names[i].name, str) == 0) {
|
||||
*transform = names[i].transform;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct sigaction sigint;
|
||||
struct display *display;
|
||||
struct window *window;
|
||||
int i, ret = 0;
|
||||
int version = -1;
|
||||
int width = 300, height = 200, scale = 1;
|
||||
enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
enum window_flags flags = 0;
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (strcmp(argv[i], "--help") == 0 ||
|
||||
strcmp(argv[i], "-h") == 0) {
|
||||
print_usage(0);
|
||||
} else if (sscanf(argv[i], "--version=%d", &version) > 0) {
|
||||
if (version < 1 || version > 4) {
|
||||
fprintf(stderr, "Unsupported wl_surface version: %d\n",
|
||||
version);
|
||||
return 1;
|
||||
}
|
||||
continue;
|
||||
} else if (strcmp(argv[i], "--verbose") == 0) {
|
||||
print_debug = 1;
|
||||
continue;
|
||||
} else if (sscanf(argv[i], "--width=%d", &width) > 0) {
|
||||
continue;
|
||||
} else if (sscanf(argv[i], "--height=%d", &height) > 0) {
|
||||
continue;
|
||||
} else if (strncmp(argv[i], "--transform=", 12) == 0 &&
|
||||
parse_transform(argv[i] + 12, &transform) > 0) {
|
||||
continue;
|
||||
} else if (strcmp(argv[i], "--rotating-transform") == 0) {
|
||||
flags |= WINDOW_FLAG_ROTATING_TRANSFORM;
|
||||
continue;
|
||||
} else if (sscanf(argv[i], "--scale=%d", &scale) > 0) {
|
||||
continue;
|
||||
} else if (strcmp(argv[i], "--use-viewport") == 0) {
|
||||
flags |= WINDOW_FLAG_USE_VIEWPORT;
|
||||
continue;
|
||||
} else if (strcmp(argv[i], "--use-damage-buffer") == 0) {
|
||||
flags |= WINDOW_FLAG_USE_DAMAGE_BUFFER;
|
||||
continue;
|
||||
} else {
|
||||
printf("Invalid option: %s\n", argv[i]);
|
||||
print_usage(255);
|
||||
}
|
||||
}
|
||||
|
||||
display = create_display(version);
|
||||
|
||||
window = create_window(display, width, height, transform, scale, flags);
|
||||
if (!window)
|
||||
return 1;
|
||||
|
||||
sigint.sa_handler = signal_int;
|
||||
sigemptyset(&sigint.sa_mask);
|
||||
sigint.sa_flags = SA_RESETHAND;
|
||||
sigaction(SIGINT, &sigint, NULL);
|
||||
|
||||
if (!window->wait_for_configure)
|
||||
redraw(window, NULL, 0);
|
||||
|
||||
while (running && ret != -1)
|
||||
ret = wl_display_dispatch(display->display);
|
||||
|
||||
fprintf(stderr, "simple-shm exiting\n");
|
||||
destroy_window(window);
|
||||
destroy_display(display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,896 @@
|
|||
/*
|
||||
* Copyright © 2011 Benjamin Franzke
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-egl.h>
|
||||
#include <wayland-cursor.h>
|
||||
|
||||
#include <GLES2/gl2.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/platform.h"
|
||||
#include "shared/weston-egl-ext.h"
|
||||
|
||||
struct window;
|
||||
struct seat;
|
||||
|
||||
struct display {
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
struct xdg_wm_base *wm_base;
|
||||
struct wl_seat *seat;
|
||||
struct wl_pointer *pointer;
|
||||
struct wl_touch *touch;
|
||||
struct wl_keyboard *keyboard;
|
||||
struct wl_shm *shm;
|
||||
struct wl_cursor_theme *cursor_theme;
|
||||
struct wl_cursor *default_cursor;
|
||||
struct wl_surface *cursor_surface;
|
||||
struct {
|
||||
EGLDisplay dpy;
|
||||
EGLContext ctx;
|
||||
EGLConfig conf;
|
||||
} egl;
|
||||
struct window *window;
|
||||
|
||||
PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage;
|
||||
};
|
||||
|
||||
struct geometry {
|
||||
int width, height;
|
||||
};
|
||||
|
||||
struct window {
|
||||
struct display *display;
|
||||
struct geometry geometry, window_size;
|
||||
struct {
|
||||
GLuint rotation_uniform;
|
||||
GLuint pos;
|
||||
GLuint col;
|
||||
} gl;
|
||||
|
||||
uint32_t benchmark_time, frames;
|
||||
struct wl_egl_window *native;
|
||||
struct wl_surface *surface;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
EGLSurface egl_surface;
|
||||
struct wl_callback *callback;
|
||||
int fullscreen, maximized, opaque, buffer_size, frame_sync, delay;
|
||||
bool wait_for_configure;
|
||||
};
|
||||
|
||||
static const char *vert_shader_text =
|
||||
"uniform mat4 rotation;\n"
|
||||
"attribute vec4 pos;\n"
|
||||
"attribute vec4 color;\n"
|
||||
"varying vec4 v_color;\n"
|
||||
"void main() {\n"
|
||||
" gl_Position = rotation * pos;\n"
|
||||
" v_color = color;\n"
|
||||
"}\n";
|
||||
|
||||
static const char *frag_shader_text =
|
||||
"precision mediump float;\n"
|
||||
"varying vec4 v_color;\n"
|
||||
"void main() {\n"
|
||||
" gl_FragColor = v_color;\n"
|
||||
"}\n";
|
||||
|
||||
static int running = 1;
|
||||
|
||||
static void
|
||||
init_egl(struct display *display, struct window *window)
|
||||
{
|
||||
static const struct {
|
||||
char *extension, *entrypoint;
|
||||
} swap_damage_ext_to_entrypoint[] = {
|
||||
{
|
||||
.extension = "EGL_EXT_swap_buffers_with_damage",
|
||||
.entrypoint = "eglSwapBuffersWithDamageEXT",
|
||||
},
|
||||
{
|
||||
.extension = "EGL_KHR_swap_buffers_with_damage",
|
||||
.entrypoint = "eglSwapBuffersWithDamageKHR",
|
||||
},
|
||||
};
|
||||
|
||||
static const EGLint context_attribs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
EGL_NONE
|
||||
};
|
||||
const char *extensions;
|
||||
|
||||
EGLint config_attribs[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_RED_SIZE, 1,
|
||||
EGL_GREEN_SIZE, 1,
|
||||
EGL_BLUE_SIZE, 1,
|
||||
EGL_ALPHA_SIZE, 1,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
EGLint major, minor, n, count, i, size;
|
||||
EGLConfig *configs;
|
||||
EGLBoolean ret;
|
||||
|
||||
if (window->opaque || window->buffer_size == 16)
|
||||
config_attribs[9] = 0;
|
||||
|
||||
display->egl.dpy =
|
||||
weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR,
|
||||
display->display, NULL);
|
||||
assert(display->egl.dpy);
|
||||
|
||||
ret = eglInitialize(display->egl.dpy, &major, &minor);
|
||||
assert(ret == EGL_TRUE);
|
||||
ret = eglBindAPI(EGL_OPENGL_ES_API);
|
||||
assert(ret == EGL_TRUE);
|
||||
|
||||
if (!eglGetConfigs(display->egl.dpy, NULL, 0, &count) || count < 1)
|
||||
assert(0);
|
||||
|
||||
configs = calloc(count, sizeof *configs);
|
||||
assert(configs);
|
||||
|
||||
ret = eglChooseConfig(display->egl.dpy, config_attribs,
|
||||
configs, count, &n);
|
||||
assert(ret && n >= 1);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
eglGetConfigAttrib(display->egl.dpy,
|
||||
configs[i], EGL_BUFFER_SIZE, &size);
|
||||
if (window->buffer_size == size) {
|
||||
display->egl.conf = configs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(configs);
|
||||
if (display->egl.conf == NULL) {
|
||||
fprintf(stderr, "did not find config with buffer size %d\n",
|
||||
window->buffer_size);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
display->egl.ctx = eglCreateContext(display->egl.dpy,
|
||||
display->egl.conf,
|
||||
EGL_NO_CONTEXT, context_attribs);
|
||||
assert(display->egl.ctx);
|
||||
|
||||
display->swap_buffers_with_damage = NULL;
|
||||
extensions = eglQueryString(display->egl.dpy, EGL_EXTENSIONS);
|
||||
if (extensions &&
|
||||
weston_check_egl_extension(extensions, "EGL_EXT_buffer_age")) {
|
||||
for (i = 0; i < (int) ARRAY_LENGTH(swap_damage_ext_to_entrypoint); i++) {
|
||||
if (weston_check_egl_extension(extensions,
|
||||
swap_damage_ext_to_entrypoint[i].extension)) {
|
||||
/* The EXTPROC is identical to the KHR one */
|
||||
display->swap_buffers_with_damage =
|
||||
(PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC)
|
||||
eglGetProcAddress(swap_damage_ext_to_entrypoint[i].entrypoint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (display->swap_buffers_with_damage)
|
||||
printf("has EGL_EXT_buffer_age and %s\n", swap_damage_ext_to_entrypoint[i].extension);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
fini_egl(struct display *display)
|
||||
{
|
||||
eglTerminate(display->egl.dpy);
|
||||
eglReleaseThread();
|
||||
}
|
||||
|
||||
static GLuint
|
||||
create_shader(struct window *window, 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);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
static void
|
||||
init_gl(struct window *window)
|
||||
{
|
||||
GLuint frag, vert;
|
||||
GLuint program;
|
||||
GLint status;
|
||||
|
||||
frag = create_shader(window, frag_shader_text, GL_FRAGMENT_SHADER);
|
||||
vert = create_shader(window, vert_shader_text, GL_VERTEX_SHADER);
|
||||
|
||||
program = glCreateProgram();
|
||||
glAttachShader(program, frag);
|
||||
glAttachShader(program, vert);
|
||||
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);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
glUseProgram(program);
|
||||
|
||||
window->gl.pos = 0;
|
||||
window->gl.col = 1;
|
||||
|
||||
glBindAttribLocation(program, window->gl.pos, "pos");
|
||||
glBindAttribLocation(program, window->gl.col, "color");
|
||||
glLinkProgram(program);
|
||||
|
||||
window->gl.rotation_uniform =
|
||||
glGetUniformLocation(program, "rotation");
|
||||
}
|
||||
|
||||
static void
|
||||
handle_surface_configure(void *data, struct xdg_surface *surface,
|
||||
uint32_t serial)
|
||||
{
|
||||
struct window *window = data;
|
||||
|
||||
xdg_surface_ack_configure(surface, serial);
|
||||
|
||||
window->wait_for_configure = false;
|
||||
}
|
||||
|
||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
||||
handle_surface_configure
|
||||
};
|
||||
|
||||
static void
|
||||
handle_toplevel_configure(void *data, struct xdg_toplevel *toplevel,
|
||||
int32_t width, int32_t height,
|
||||
struct wl_array *states)
|
||||
{
|
||||
struct window *window = data;
|
||||
uint32_t *p;
|
||||
|
||||
window->fullscreen = 0;
|
||||
window->maximized = 0;
|
||||
wl_array_for_each(p, states) {
|
||||
uint32_t state = *p;
|
||||
switch (state) {
|
||||
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 && !window->maximized) {
|
||||
window->window_size.width = width;
|
||||
window->window_size.height = height;
|
||||
}
|
||||
window->geometry.width = width;
|
||||
window->geometry.height = height;
|
||||
} else if (!window->fullscreen && !window->maximized) {
|
||||
window->geometry = window->window_size;
|
||||
}
|
||||
|
||||
if (window->native)
|
||||
wl_egl_window_resize(window->native,
|
||||
window->geometry.width,
|
||||
window->geometry.height, 0, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
|
||||
{
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
||||
handle_toplevel_configure,
|
||||
handle_toplevel_close,
|
||||
};
|
||||
|
||||
static void
|
||||
create_surface(struct window *window)
|
||||
{
|
||||
struct display *display = window->display;
|
||||
EGLBoolean ret;
|
||||
|
||||
window->surface = wl_compositor_create_surface(display->compositor);
|
||||
|
||||
window->native =
|
||||
wl_egl_window_create(window->surface,
|
||||
window->geometry.width,
|
||||
window->geometry.height);
|
||||
window->egl_surface =
|
||||
weston_platform_create_egl_surface(display->egl.dpy,
|
||||
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);
|
||||
|
||||
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);
|
||||
assert(ret == EGL_TRUE);
|
||||
|
||||
if (!window->frame_sync)
|
||||
eglSwapInterval(display->egl.dpy, 0);
|
||||
|
||||
if (!display->wm_base)
|
||||
return;
|
||||
|
||||
if (window->fullscreen)
|
||||
xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_surface(struct window *window)
|
||||
{
|
||||
/* Required, otherwise segfault in egl_dri2.c: dri2_make_current()
|
||||
* on eglReleaseThread(). */
|
||||
eglMakeCurrent(window->display->egl.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||
EGL_NO_CONTEXT);
|
||||
|
||||
weston_platform_destroy_egl_surface(window->display->egl.dpy,
|
||||
window->egl_surface);
|
||||
wl_egl_window_destroy(window->native);
|
||||
|
||||
if (window->xdg_toplevel)
|
||||
xdg_toplevel_destroy(window->xdg_toplevel);
|
||||
if (window->xdg_surface)
|
||||
xdg_surface_destroy(window->xdg_surface);
|
||||
wl_surface_destroy(window->surface);
|
||||
|
||||
if (window->callback)
|
||||
wl_callback_destroy(window->callback);
|
||||
}
|
||||
|
||||
static void
|
||||
redraw(void *data, struct wl_callback *callback, uint32_t time)
|
||||
{
|
||||
struct window *window = data;
|
||||
struct display *display = window->display;
|
||||
static const GLfloat verts[3][2] = {
|
||||
{ -0.5, -0.5 },
|
||||
{ 0.5, -0.5 },
|
||||
{ 0, 0.5 }
|
||||
};
|
||||
static const GLfloat colors[3][3] = {
|
||||
{ 1, 0, 0 },
|
||||
{ 0, 1, 0 },
|
||||
{ 0, 0, 1 }
|
||||
};
|
||||
GLfloat angle;
|
||||
GLfloat rotation[4][4] = {
|
||||
{ 1, 0, 0, 0 },
|
||||
{ 0, 1, 0, 0 },
|
||||
{ 0, 0, 1, 0 },
|
||||
{ 0, 0, 0, 1 }
|
||||
};
|
||||
static const uint32_t speed_div = 5, benchmark_interval = 5;
|
||||
struct wl_region *region;
|
||||
EGLint rect[4];
|
||||
EGLint buffer_age = 0;
|
||||
struct timeval tv;
|
||||
|
||||
assert(window->callback == callback);
|
||||
window->callback = NULL;
|
||||
|
||||
if (callback)
|
||||
wl_callback_destroy(callback);
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
if (window->frames == 0)
|
||||
window->benchmark_time = time;
|
||||
if (time - window->benchmark_time > (benchmark_interval * 1000)) {
|
||||
printf("%d frames in %d seconds: %f fps\n",
|
||||
window->frames,
|
||||
benchmark_interval,
|
||||
(float) window->frames / benchmark_interval);
|
||||
window->benchmark_time = time;
|
||||
window->frames = 0;
|
||||
}
|
||||
|
||||
angle = (time / speed_div) % 360 * M_PI / 180.0;
|
||||
rotation[0][0] = cos(angle);
|
||||
rotation[0][2] = sin(angle);
|
||||
rotation[2][0] = -sin(angle);
|
||||
rotation[2][2] = cos(angle);
|
||||
|
||||
if (display->swap_buffers_with_damage)
|
||||
eglQuerySurface(display->egl.dpy, window->egl_surface,
|
||||
EGL_BUFFER_AGE_EXT, &buffer_age);
|
||||
|
||||
glViewport(0, 0, window->geometry.width, window->geometry.height);
|
||||
|
||||
glUniformMatrix4fv(window->gl.rotation_uniform, 1, GL_FALSE,
|
||||
(GLfloat *) rotation);
|
||||
|
||||
glClearColor(0.0, 0.0, 0.0, 0.5);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glVertexAttribPointer(window->gl.pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
|
||||
glVertexAttribPointer(window->gl.col, 3, GL_FLOAT, GL_FALSE, 0, colors);
|
||||
glEnableVertexAttribArray(window->gl.pos);
|
||||
glEnableVertexAttribArray(window->gl.col);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
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,
|
||||
window->geometry.width,
|
||||
window->geometry.height);
|
||||
wl_surface_set_opaque_region(window->surface, region);
|
||||
wl_region_destroy(region);
|
||||
} else {
|
||||
wl_surface_set_opaque_region(window->surface, NULL);
|
||||
}
|
||||
|
||||
if (display->swap_buffers_with_damage && buffer_age > 0) {
|
||||
rect[0] = window->geometry.width / 4 - 1;
|
||||
rect[1] = window->geometry.height / 4 - 1;
|
||||
rect[2] = window->geometry.width / 2 + 2;
|
||||
rect[3] = window->geometry.height / 2 + 2;
|
||||
display->swap_buffers_with_damage(display->egl.dpy,
|
||||
window->egl_surface,
|
||||
rect, 1);
|
||||
} else {
|
||||
eglSwapBuffers(display->egl.dpy, window->egl_surface);
|
||||
}
|
||||
window->frames++;
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_enter(void *data, struct wl_pointer *pointer,
|
||||
uint32_t serial, struct wl_surface *surface,
|
||||
wl_fixed_t sx, wl_fixed_t sy)
|
||||
{
|
||||
struct display *display = data;
|
||||
struct wl_buffer *buffer;
|
||||
struct wl_cursor *cursor = display->default_cursor;
|
||||
struct wl_cursor_image *image;
|
||||
|
||||
if (display->window->fullscreen)
|
||||
wl_pointer_set_cursor(pointer, serial, NULL, 0, 0);
|
||||
else if (cursor) {
|
||||
image = display->default_cursor->images[0];
|
||||
buffer = wl_cursor_image_get_buffer(image);
|
||||
if (!buffer)
|
||||
return;
|
||||
wl_pointer_set_cursor(pointer, serial,
|
||||
display->cursor_surface,
|
||||
image->hotspot_x,
|
||||
image->hotspot_y);
|
||||
wl_surface_attach(display->cursor_surface, buffer, 0, 0);
|
||||
wl_surface_damage(display->cursor_surface, 0, 0,
|
||||
image->width, image->height);
|
||||
wl_surface_commit(display->cursor_surface);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_leave(void *data, struct wl_pointer *pointer,
|
||||
uint32_t serial, struct wl_surface *surface)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_motion(void *data, struct wl_pointer *pointer,
|
||||
uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t serial, uint32_t time, uint32_t button,
|
||||
uint32_t state)
|
||||
{
|
||||
struct display *display = data;
|
||||
|
||||
if (!display->window->xdg_toplevel)
|
||||
return;
|
||||
|
||||
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
xdg_toplevel_move(display->window->xdg_toplevel,
|
||||
display->seat, serial);
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t time, uint32_t axis, wl_fixed_t value)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_pointer_listener pointer_listener = {
|
||||
pointer_handle_enter,
|
||||
pointer_handle_leave,
|
||||
pointer_handle_motion,
|
||||
pointer_handle_button,
|
||||
pointer_handle_axis,
|
||||
};
|
||||
|
||||
static void
|
||||
touch_handle_down(void *data, struct wl_touch *wl_touch,
|
||||
uint32_t serial, uint32_t time, struct wl_surface *surface,
|
||||
int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
|
||||
{
|
||||
struct display *d = (struct display *)data;
|
||||
|
||||
if (!d->wm_base)
|
||||
return;
|
||||
|
||||
xdg_toplevel_move(d->window->xdg_toplevel, d->seat, serial);
|
||||
}
|
||||
|
||||
static void
|
||||
touch_handle_up(void *data, struct wl_touch *wl_touch,
|
||||
uint32_t serial, uint32_t time, int32_t id)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
touch_handle_motion(void *data, struct wl_touch *wl_touch,
|
||||
uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
touch_handle_frame(void *data, struct wl_touch *wl_touch)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
touch_handle_cancel(void *data, struct wl_touch *wl_touch)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_touch_listener touch_listener = {
|
||||
touch_handle_down,
|
||||
touch_handle_up,
|
||||
touch_handle_motion,
|
||||
touch_handle_frame,
|
||||
touch_handle_cancel,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
struct display *d = data;
|
||||
|
||||
if (!d->wm_base)
|
||||
return;
|
||||
|
||||
if (key == KEY_F11 && state) {
|
||||
if (d->window->fullscreen)
|
||||
xdg_toplevel_unset_fullscreen(d->window->xdg_toplevel);
|
||||
else
|
||||
xdg_toplevel_set_fullscreen(d->window->xdg_toplevel, NULL);
|
||||
} else if (key == KEY_ESC && state)
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
|
||||
uint32_t serial, uint32_t mods_depressed,
|
||||
uint32_t mods_latched, uint32_t mods_locked,
|
||||
uint32_t group)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_keyboard_listener keyboard_listener = {
|
||||
keyboard_handle_keymap,
|
||||
keyboard_handle_enter,
|
||||
keyboard_handle_leave,
|
||||
keyboard_handle_key,
|
||||
keyboard_handle_modifiers,
|
||||
};
|
||||
|
||||
static void
|
||||
seat_handle_capabilities(void *data, struct wl_seat *seat,
|
||||
enum wl_seat_capability caps)
|
||||
{
|
||||
struct display *d = data;
|
||||
|
||||
if ((caps & WL_SEAT_CAPABILITY_POINTER) && !d->pointer) {
|
||||
d->pointer = wl_seat_get_pointer(seat);
|
||||
wl_pointer_add_listener(d->pointer, &pointer_listener, d);
|
||||
} else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && d->pointer) {
|
||||
wl_pointer_destroy(d->pointer);
|
||||
d->pointer = NULL;
|
||||
}
|
||||
|
||||
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !d->keyboard) {
|
||||
d->keyboard = wl_seat_get_keyboard(seat);
|
||||
wl_keyboard_add_listener(d->keyboard, &keyboard_listener, d);
|
||||
} else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && d->keyboard) {
|
||||
wl_keyboard_destroy(d->keyboard);
|
||||
d->keyboard = NULL;
|
||||
}
|
||||
|
||||
if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !d->touch) {
|
||||
d->touch = wl_seat_get_touch(seat);
|
||||
wl_touch_set_user_data(d->touch, d);
|
||||
wl_touch_add_listener(d->touch, &touch_listener, d);
|
||||
} else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && d->touch) {
|
||||
wl_touch_destroy(d->touch);
|
||||
d->touch = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_seat_listener seat_listener = {
|
||||
seat_handle_capabilities,
|
||||
};
|
||||
|
||||
static void
|
||||
xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
|
||||
{
|
||||
xdg_wm_base_pong(shell, serial);
|
||||
}
|
||||
|
||||
static const struct xdg_wm_base_listener wm_base_listener = {
|
||||
xdg_wm_base_ping,
|
||||
};
|
||||
|
||||
static void
|
||||
registry_handle_global(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version)
|
||||
{
|
||||
struct display *d = data;
|
||||
|
||||
if (strcmp(interface, "wl_compositor") == 0) {
|
||||
d->compositor =
|
||||
wl_registry_bind(registry, name,
|
||||
&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);
|
||||
wl_seat_add_listener(d->seat, &seat_listener, d);
|
||||
} else if (strcmp(interface, "wl_shm") == 0) {
|
||||
d->shm = wl_registry_bind(registry, name,
|
||||
&wl_shm_interface, 1);
|
||||
d->cursor_theme = wl_cursor_theme_load(NULL, 32, d->shm);
|
||||
if (!d->cursor_theme) {
|
||||
fprintf(stderr, "unable to load default theme\n");
|
||||
return;
|
||||
}
|
||||
d->default_cursor =
|
||||
wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr");
|
||||
if (!d->default_cursor) {
|
||||
fprintf(stderr, "unable to load default left pointer\n");
|
||||
// TODO: abort ?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
signal_int(int signum)
|
||||
{
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
usage(int error_code)
|
||||
{
|
||||
fprintf(stderr, "Usage: simple-egl [OPTIONS]\n\n"
|
||||
" -d <us>\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"
|
||||
" -b\tDon't sync to compositor redraw (eglSwapInterval 0)\n"
|
||||
" -h\tThis help text\n\n");
|
||||
|
||||
exit(error_code);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct sigaction sigint;
|
||||
struct display display = { 0 };
|
||||
struct window window = { 0 };
|
||||
int i, ret = 0;
|
||||
|
||||
window.display = &display;
|
||||
display.window = &window;
|
||||
window.geometry.width = 250;
|
||||
window.geometry.height = 250;
|
||||
window.window_size = window.geometry;
|
||||
window.buffer_size = 32;
|
||||
window.frame_sync = 1;
|
||||
window.delay = 0;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
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;
|
||||
else if (strcmp("-s", argv[i]) == 0)
|
||||
window.buffer_size = 16;
|
||||
else if (strcmp("-b", argv[i]) == 0)
|
||||
window.frame_sync = 0;
|
||||
else if (strcmp("-h", argv[i]) == 0)
|
||||
usage(EXIT_SUCCESS);
|
||||
else
|
||||
usage(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
display.display = wl_display_connect(NULL);
|
||||
assert(display.display);
|
||||
|
||||
display.registry = wl_display_get_registry(display.display);
|
||||
wl_registry_add_listener(display.registry,
|
||||
®istry_listener, &display);
|
||||
|
||||
wl_display_roundtrip(display.display);
|
||||
|
||||
init_egl(&display, &window);
|
||||
create_surface(&window);
|
||||
init_gl(&window);
|
||||
|
||||
display.cursor_surface =
|
||||
wl_compositor_create_surface(display.compositor);
|
||||
|
||||
sigint.sa_handler = signal_int;
|
||||
sigemptyset(&sigint.sa_mask);
|
||||
sigint.sa_flags = SA_RESETHAND;
|
||||
sigaction(SIGINT, &sigint, NULL);
|
||||
|
||||
/* The mainloop here is a little subtle. Redrawing will cause
|
||||
* EGL to read events so we can just call
|
||||
* wl_display_dispatch_pending() to handle any events that got
|
||||
* queued up as a side effect. */
|
||||
while (running && ret != -1) {
|
||||
if (window.wait_for_configure) {
|
||||
ret = wl_display_dispatch(display.display);
|
||||
} else {
|
||||
ret = wl_display_dispatch_pending(display.display);
|
||||
redraw(&window, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "simple-egl exiting\n");
|
||||
|
||||
destroy_surface(&window);
|
||||
fini_egl(&display);
|
||||
|
||||
wl_surface_destroy(display.cursor_surface);
|
||||
if (display.cursor_theme)
|
||||
wl_cursor_theme_destroy(display.cursor_theme);
|
||||
|
||||
if (display.wm_base)
|
||||
xdg_wm_base_destroy(display.wm_base);
|
||||
|
||||
if (display.compositor)
|
||||
wl_compositor_destroy(display.compositor);
|
||||
|
||||
wl_registry_destroy(display.registry);
|
||||
wl_display_flush(display.display);
|
||||
wl_display_disconnect(display.display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,526 @@
|
|||
/*
|
||||
* Copyright © 2012 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
#include "window.h"
|
||||
#include "input-method-unstable-v1-client-protocol.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
enum compose_state {
|
||||
state_normal,
|
||||
state_compose
|
||||
};
|
||||
|
||||
struct compose_seq {
|
||||
uint32_t keys[4];
|
||||
|
||||
const char *text;
|
||||
};
|
||||
|
||||
struct simple_im;
|
||||
|
||||
typedef void (*keyboard_input_key_handler_t)(struct simple_im *keyboard,
|
||||
uint32_t serial,
|
||||
uint32_t time, uint32_t key, uint32_t unicode,
|
||||
enum wl_keyboard_key_state state);
|
||||
|
||||
struct simple_im {
|
||||
struct zwp_input_method_v1 *input_method;
|
||||
struct zwp_input_method_context_v1 *context;
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_keyboard *keyboard;
|
||||
enum compose_state compose_state;
|
||||
struct compose_seq compose_seq;
|
||||
|
||||
struct xkb_context *xkb_context;
|
||||
|
||||
uint32_t modifiers;
|
||||
|
||||
struct xkb_keymap *keymap;
|
||||
struct xkb_state *state;
|
||||
xkb_mod_mask_t control_mask;
|
||||
xkb_mod_mask_t alt_mask;
|
||||
xkb_mod_mask_t shift_mask;
|
||||
|
||||
keyboard_input_key_handler_t key_handler;
|
||||
|
||||
uint32_t serial;
|
||||
};
|
||||
|
||||
static const struct compose_seq compose_seqs[] = {
|
||||
{ { XKB_KEY_quotedbl, XKB_KEY_A, 0 }, "Ä" },
|
||||
{ { XKB_KEY_quotedbl, XKB_KEY_O, 0 }, "Ö" },
|
||||
{ { XKB_KEY_quotedbl, XKB_KEY_U, 0 }, "Ü" },
|
||||
{ { XKB_KEY_quotedbl, XKB_KEY_a, 0 }, "ä" },
|
||||
{ { XKB_KEY_quotedbl, XKB_KEY_o, 0 }, "ö" },
|
||||
{ { XKB_KEY_quotedbl, XKB_KEY_u, 0 }, "ü" },
|
||||
{ { XKB_KEY_apostrophe, XKB_KEY_A, 0 }, "Á" },
|
||||
{ { XKB_KEY_apostrophe, XKB_KEY_a, 0 }, "á" },
|
||||
{ { XKB_KEY_slash, XKB_KEY_O, 0 }, "Ø" },
|
||||
{ { XKB_KEY_slash, XKB_KEY_o, 0 }, "ø" },
|
||||
{ { XKB_KEY_less, XKB_KEY_3, 0 }, "♥" },
|
||||
{ { XKB_KEY_A, XKB_KEY_A, 0 }, "Å" },
|
||||
{ { XKB_KEY_A, XKB_KEY_E, 0 }, "Æ" },
|
||||
{ { XKB_KEY_O, XKB_KEY_C, 0 }, "©" },
|
||||
{ { XKB_KEY_O, XKB_KEY_R, 0 }, "®" },
|
||||
{ { XKB_KEY_s, XKB_KEY_s, 0 }, "ß" },
|
||||
{ { XKB_KEY_a, XKB_KEY_e, 0 }, "æ" },
|
||||
{ { XKB_KEY_a, XKB_KEY_a, 0 }, "å" },
|
||||
};
|
||||
|
||||
static const uint32_t ignore_keys_on_compose[] = {
|
||||
XKB_KEY_Shift_L,
|
||||
XKB_KEY_Shift_R
|
||||
};
|
||||
|
||||
static void
|
||||
handle_surrounding_text(void *data,
|
||||
struct zwp_input_method_context_v1 *context,
|
||||
const char *text,
|
||||
uint32_t cursor,
|
||||
uint32_t anchor)
|
||||
{
|
||||
fprintf(stderr, "Surrounding text updated: %s\n", text);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_reset(void *data,
|
||||
struct zwp_input_method_context_v1 *context)
|
||||
{
|
||||
struct simple_im *keyboard = data;
|
||||
|
||||
fprintf(stderr, "Reset pre-edit buffer\n");
|
||||
|
||||
keyboard->compose_state = state_normal;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_content_type(void *data,
|
||||
struct zwp_input_method_context_v1 *context,
|
||||
uint32_t hint,
|
||||
uint32_t purpose)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
handle_invoke_action(void *data,
|
||||
struct zwp_input_method_context_v1 *context,
|
||||
uint32_t button,
|
||||
uint32_t index)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
handle_commit_state(void *data,
|
||||
struct zwp_input_method_context_v1 *context,
|
||||
uint32_t serial)
|
||||
{
|
||||
struct simple_im *keyboard = data;
|
||||
|
||||
keyboard->serial = serial;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_preferred_language(void *data,
|
||||
struct zwp_input_method_context_v1 *context,
|
||||
const char *language)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct zwp_input_method_context_v1_listener input_method_context_listener = {
|
||||
handle_surrounding_text,
|
||||
handle_reset,
|
||||
handle_content_type,
|
||||
handle_invoke_action,
|
||||
handle_commit_state,
|
||||
handle_preferred_language
|
||||
};
|
||||
|
||||
static void
|
||||
input_method_keyboard_keymap(void *data,
|
||||
struct wl_keyboard *wl_keyboard,
|
||||
uint32_t format,
|
||||
int32_t fd,
|
||||
uint32_t size)
|
||||
{
|
||||
struct simple_im *keyboard = data;
|
||||
char *map_str;
|
||||
|
||||
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (map_str == MAP_FAILED) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
keyboard->keymap =
|
||||
xkb_keymap_new_from_string(keyboard->xkb_context,
|
||||
map_str,
|
||||
XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
|
||||
munmap(map_str, size);
|
||||
close(fd);
|
||||
|
||||
if (!keyboard->keymap) {
|
||||
fprintf(stderr, "Failed to compile keymap\n");
|
||||
return;
|
||||
}
|
||||
|
||||
keyboard->state = xkb_state_new(keyboard->keymap);
|
||||
if (!keyboard->state) {
|
||||
fprintf(stderr, "Failed to create XKB state\n");
|
||||
xkb_keymap_unref(keyboard->keymap);
|
||||
return;
|
||||
}
|
||||
|
||||
keyboard->control_mask =
|
||||
1 << xkb_keymap_mod_get_index(keyboard->keymap, "Control");
|
||||
keyboard->alt_mask =
|
||||
1 << xkb_keymap_mod_get_index(keyboard->keymap, "Mod1");
|
||||
keyboard->shift_mask =
|
||||
1 << xkb_keymap_mod_get_index(keyboard->keymap, "Shift");
|
||||
}
|
||||
|
||||
static void
|
||||
input_method_keyboard_key(void *data,
|
||||
struct wl_keyboard *wl_keyboard,
|
||||
uint32_t serial,
|
||||
uint32_t time,
|
||||
uint32_t key,
|
||||
uint32_t state_w)
|
||||
{
|
||||
struct simple_im *keyboard = data;
|
||||
uint32_t code;
|
||||
uint32_t num_syms;
|
||||
const xkb_keysym_t *syms;
|
||||
xkb_keysym_t sym;
|
||||
enum wl_keyboard_key_state state = state_w;
|
||||
|
||||
if (!keyboard->state)
|
||||
return;
|
||||
|
||||
code = key + 8;
|
||||
num_syms = xkb_state_key_get_syms(keyboard->state, code, &syms);
|
||||
|
||||
sym = XKB_KEY_NoSymbol;
|
||||
if (num_syms == 1)
|
||||
sym = syms[0];
|
||||
|
||||
if (keyboard->key_handler)
|
||||
(*keyboard->key_handler)(keyboard, serial, time, key, sym,
|
||||
state);
|
||||
}
|
||||
|
||||
static void
|
||||
input_method_keyboard_modifiers(void *data,
|
||||
struct wl_keyboard *wl_keyboard,
|
||||
uint32_t serial,
|
||||
uint32_t mods_depressed,
|
||||
uint32_t mods_latched,
|
||||
uint32_t mods_locked,
|
||||
uint32_t group)
|
||||
{
|
||||
struct simple_im *keyboard = data;
|
||||
struct zwp_input_method_context_v1 *context = keyboard->context;
|
||||
xkb_mod_mask_t mask;
|
||||
|
||||
xkb_state_update_mask(keyboard->state, mods_depressed,
|
||||
mods_latched, mods_locked, 0, 0, group);
|
||||
mask = xkb_state_serialize_mods(keyboard->state,
|
||||
XKB_STATE_MODS_DEPRESSED |
|
||||
XKB_STATE_MODS_LATCHED);
|
||||
|
||||
keyboard->modifiers = 0;
|
||||
if (mask & keyboard->control_mask)
|
||||
keyboard->modifiers |= MOD_CONTROL_MASK;
|
||||
if (mask & keyboard->alt_mask)
|
||||
keyboard->modifiers |= MOD_ALT_MASK;
|
||||
if (mask & keyboard->shift_mask)
|
||||
keyboard->modifiers |= MOD_SHIFT_MASK;
|
||||
|
||||
zwp_input_method_context_v1_modifiers(context, serial,
|
||||
mods_depressed, mods_depressed,
|
||||
mods_latched, group);
|
||||
}
|
||||
|
||||
static const struct wl_keyboard_listener input_method_keyboard_listener = {
|
||||
input_method_keyboard_keymap,
|
||||
NULL, /* enter */
|
||||
NULL, /* leave */
|
||||
input_method_keyboard_key,
|
||||
input_method_keyboard_modifiers
|
||||
};
|
||||
|
||||
static void
|
||||
input_method_activate(void *data,
|
||||
struct zwp_input_method_v1 *input_method,
|
||||
struct zwp_input_method_context_v1 *context)
|
||||
{
|
||||
struct simple_im *keyboard = data;
|
||||
|
||||
if (keyboard->context)
|
||||
zwp_input_method_context_v1_destroy(keyboard->context);
|
||||
|
||||
keyboard->compose_state = state_normal;
|
||||
|
||||
keyboard->serial = 0;
|
||||
|
||||
keyboard->context = context;
|
||||
zwp_input_method_context_v1_add_listener(context,
|
||||
&input_method_context_listener,
|
||||
keyboard);
|
||||
keyboard->keyboard = zwp_input_method_context_v1_grab_keyboard(context);
|
||||
wl_keyboard_add_listener(keyboard->keyboard,
|
||||
&input_method_keyboard_listener,
|
||||
keyboard);
|
||||
}
|
||||
|
||||
static void
|
||||
input_method_deactivate(void *data,
|
||||
struct zwp_input_method_v1 *input_method,
|
||||
struct zwp_input_method_context_v1 *context)
|
||||
{
|
||||
struct simple_im *keyboard = data;
|
||||
|
||||
if (!keyboard->context)
|
||||
return;
|
||||
|
||||
zwp_input_method_context_v1_destroy(keyboard->context);
|
||||
keyboard->context = NULL;
|
||||
}
|
||||
|
||||
static const struct zwp_input_method_v1_listener input_method_listener = {
|
||||
input_method_activate,
|
||||
input_method_deactivate
|
||||
};
|
||||
|
||||
static void
|
||||
registry_handle_global(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version)
|
||||
{
|
||||
struct simple_im *keyboard = data;
|
||||
|
||||
if (!strcmp(interface, "zwp_input_method_v1")) {
|
||||
keyboard->input_method =
|
||||
wl_registry_bind(registry, name,
|
||||
&zwp_input_method_v1_interface, 1);
|
||||
zwp_input_method_v1_add_listener(keyboard->input_method,
|
||||
&input_method_listener, keyboard);
|
||||
}
|
||||
}
|
||||
|
||||
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 int
|
||||
compare_compose_keys(const void *c1, const void *c2)
|
||||
{
|
||||
const struct compose_seq *cs1 = c1;
|
||||
const struct compose_seq *cs2 = c2;
|
||||
int i;
|
||||
|
||||
for (i = 0; cs1->keys[i] != 0 && cs2->keys[i] != 0; i++) {
|
||||
if (cs1->keys[i] != cs2->keys[i])
|
||||
return cs1->keys[i] - cs2->keys[i];
|
||||
}
|
||||
|
||||
if (cs1->keys[i] == cs2->keys[i]
|
||||
|| cs1->keys[i] == 0)
|
||||
return 0;
|
||||
|
||||
return cs1->keys[i] - cs2->keys[i];
|
||||
}
|
||||
|
||||
static void
|
||||
simple_im_key_handler(struct simple_im *keyboard,
|
||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t sym,
|
||||
enum wl_keyboard_key_state state)
|
||||
{
|
||||
struct zwp_input_method_context_v1 *context = keyboard->context;
|
||||
char text[64];
|
||||
|
||||
if (sym == XKB_KEY_Multi_key &&
|
||||
state == WL_KEYBOARD_KEY_STATE_RELEASED &&
|
||||
keyboard->compose_state == state_normal) {
|
||||
keyboard->compose_state = state_compose;
|
||||
memset(&keyboard->compose_seq, 0, sizeof(struct compose_seq));
|
||||
return;
|
||||
}
|
||||
|
||||
if (keyboard->compose_state == state_compose) {
|
||||
uint32_t i = 0;
|
||||
struct compose_seq *cs;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(ignore_keys_on_compose); i++) {
|
||||
if (sym == ignore_keys_on_compose[i]) {
|
||||
zwp_input_method_context_v1_key(context,
|
||||
keyboard->serial,
|
||||
time,
|
||||
key,
|
||||
state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; keyboard->compose_seq.keys[i] != 0; i++);
|
||||
|
||||
keyboard->compose_seq.keys[i] = sym;
|
||||
|
||||
cs = bsearch (&keyboard->compose_seq, compose_seqs,
|
||||
ARRAY_LENGTH(compose_seqs),
|
||||
sizeof(compose_seqs[0]), compare_compose_keys);
|
||||
|
||||
if (cs) {
|
||||
if (cs->keys[i + 1] == 0) {
|
||||
zwp_input_method_context_v1_preedit_cursor(keyboard->context,
|
||||
0);
|
||||
zwp_input_method_context_v1_preedit_string(keyboard->context,
|
||||
keyboard->serial,
|
||||
"", "");
|
||||
zwp_input_method_context_v1_cursor_position(keyboard->context,
|
||||
0, 0);
|
||||
zwp_input_method_context_v1_commit_string(keyboard->context,
|
||||
keyboard->serial,
|
||||
cs->text);
|
||||
keyboard->compose_state = state_normal;
|
||||
} else {
|
||||
uint32_t j = 0, idx = 0;
|
||||
|
||||
for (; j <= i; j++) {
|
||||
idx += xkb_keysym_to_utf8(cs->keys[j], text + idx, sizeof(text) - idx);
|
||||
}
|
||||
|
||||
zwp_input_method_context_v1_preedit_cursor(keyboard->context,
|
||||
strlen(text));
|
||||
zwp_input_method_context_v1_preedit_string(keyboard->context,
|
||||
keyboard->serial,
|
||||
text,
|
||||
text);
|
||||
}
|
||||
} else {
|
||||
uint32_t j = 0, idx = 0;
|
||||
|
||||
for (; j <= i; j++) {
|
||||
idx += xkb_keysym_to_utf8(keyboard->compose_seq.keys[j], text + idx, sizeof(text) - idx);
|
||||
}
|
||||
zwp_input_method_context_v1_preedit_cursor(keyboard->context,
|
||||
0);
|
||||
zwp_input_method_context_v1_preedit_string(keyboard->context,
|
||||
keyboard->serial,
|
||||
"", "");
|
||||
zwp_input_method_context_v1_cursor_position(keyboard->context,
|
||||
0, 0);
|
||||
zwp_input_method_context_v1_commit_string(keyboard->context,
|
||||
keyboard->serial,
|
||||
text);
|
||||
keyboard->compose_state = state_normal;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0) {
|
||||
zwp_input_method_context_v1_key(context, serial, time, key, state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
return;
|
||||
|
||||
zwp_input_method_context_v1_cursor_position(keyboard->context,
|
||||
0, 0);
|
||||
zwp_input_method_context_v1_commit_string(keyboard->context,
|
||||
keyboard->serial,
|
||||
text);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct simple_im simple_im;
|
||||
int ret = 0;
|
||||
|
||||
memset(&simple_im, 0, sizeof(simple_im));
|
||||
|
||||
simple_im.display = wl_display_connect(NULL);
|
||||
if (simple_im.display == NULL) {
|
||||
fprintf(stderr, "Failed to connect to server: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
simple_im.registry = wl_display_get_registry(simple_im.display);
|
||||
wl_registry_add_listener(simple_im.registry,
|
||||
®istry_listener, &simple_im);
|
||||
wl_display_roundtrip(simple_im.display);
|
||||
if (simple_im.input_method == NULL) {
|
||||
fprintf(stderr, "No input_method global\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
simple_im.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (simple_im.xkb_context == NULL) {
|
||||
fprintf(stderr, "Failed to create XKB context\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
simple_im.context = NULL;
|
||||
simple_im.key_handler = simple_im_key_handler;
|
||||
|
||||
while (ret != -1)
|
||||
ret = wl_display_dispatch(simple_im.display);
|
||||
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "Dispatch error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
* Copyright © 2011 Benjamin Franzke
|
||||
* Copyright © 2010 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include "shared/os-compatibility.h"
|
||||
#include <libweston/zalloc.h>
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
#include "fullscreen-shell-unstable-v1-client-protocol.h"
|
||||
|
||||
struct display {
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
struct xdg_wm_base *wm_base;
|
||||
struct zwp_fullscreen_shell_v1 *fshell;
|
||||
struct wl_shm *shm;
|
||||
bool has_xrgb;
|
||||
};
|
||||
|
||||
struct buffer {
|
||||
struct wl_buffer *buffer;
|
||||
void *shm_data;
|
||||
int busy;
|
||||
};
|
||||
|
||||
struct window {
|
||||
struct display *display;
|
||||
int width, height;
|
||||
struct wl_surface *surface;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
struct buffer buffers[2];
|
||||
struct buffer *prev_buffer;
|
||||
struct wl_callback *callback;
|
||||
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
|
||||
create_shm_buffer(struct display *display, struct buffer *buffer,
|
||||
int width, int height, uint32_t format)
|
||||
{
|
||||
struct wl_shm_pool *pool;
|
||||
int fd, size, stride;
|
||||
void *data;
|
||||
|
||||
stride = width * 4;
|
||||
size = stride * height;
|
||||
|
||||
fd = os_create_anonymous_file(size);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
|
||||
size, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pool = wl_shm_create_pool(display->shm, fd, size);
|
||||
buffer->buffer = wl_shm_pool_create_buffer(pool, 0,
|
||||
width, height,
|
||||
stride, format);
|
||||
wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer);
|
||||
wl_shm_pool_destroy(pool);
|
||||
close(fd);
|
||||
|
||||
buffer->shm_data = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_xdg_surface_configure(void *data, struct xdg_surface *surface,
|
||||
uint32_t serial)
|
||||
{
|
||||
struct window *window = data;
|
||||
|
||||
xdg_surface_ack_configure(surface, serial);
|
||||
|
||||
if (window->wait_for_configure) {
|
||||
redraw(window, NULL, 0);
|
||||
window->wait_for_configure = false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
||||
handle_xdg_surface_configure,
|
||||
};
|
||||
|
||||
static void
|
||||
handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
|
||||
int32_t width, int32_t height,
|
||||
struct wl_array *state)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
handle_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
|
||||
{
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
||||
handle_xdg_toplevel_configure,
|
||||
handle_xdg_toplevel_close,
|
||||
};
|
||||
|
||||
static struct window *
|
||||
create_window(struct display *display, int width, int height)
|
||||
{
|
||||
struct window *window;
|
||||
|
||||
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-shm");
|
||||
wl_surface_commit(window->surface);
|
||||
window->wait_for_configure = true;
|
||||
} else if (display->fshell) {
|
||||
zwp_fullscreen_shell_v1_present_surface(display->fshell,
|
||||
window->surface,
|
||||
ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_DEFAULT,
|
||||
NULL);
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_window(struct window *window)
|
||||
{
|
||||
if (window->callback)
|
||||
wl_callback_destroy(window->callback);
|
||||
|
||||
if (window->buffers[0].buffer)
|
||||
wl_buffer_destroy(window->buffers[0].buffer);
|
||||
if (window->buffers[1].buffer)
|
||||
wl_buffer_destroy(window->buffers[1].buffer);
|
||||
|
||||
if (window->xdg_toplevel)
|
||||
xdg_toplevel_destroy(window->xdg_toplevel);
|
||||
if (window->xdg_surface)
|
||||
xdg_surface_destroy(window->xdg_surface);
|
||||
wl_surface_destroy(window->surface);
|
||||
free(window);
|
||||
}
|
||||
|
||||
static struct buffer *
|
||||
window_next_buffer(struct window *window)
|
||||
{
|
||||
struct buffer *buffer;
|
||||
int ret = 0;
|
||||
|
||||
if (!window->buffers[0].busy)
|
||||
buffer = &window->buffers[0];
|
||||
else if (!window->buffers[1].busy)
|
||||
buffer = &window->buffers[1];
|
||||
else
|
||||
return NULL;
|
||||
|
||||
if (!buffer->buffer) {
|
||||
ret = create_shm_buffer(window->display, buffer,
|
||||
window->width, window->height,
|
||||
WL_SHM_FORMAT_XRGB8888);
|
||||
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
/* paint the padding */
|
||||
memset(buffer->shm_data, 0xff,
|
||||
window->width * window->height * 4);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void
|
||||
paint_pixels(void *image, int padding, int width, int height, uint32_t time)
|
||||
{
|
||||
const int halfh = padding + (height - padding * 2) / 2;
|
||||
const int halfw = padding + (width - padding * 2) / 2;
|
||||
int ir, or;
|
||||
uint32_t *pixel = image;
|
||||
int y;
|
||||
|
||||
/* squared radii thresholds */
|
||||
or = (halfw < halfh ? halfw : halfh) - 8;
|
||||
ir = or - 32;
|
||||
or *= or;
|
||||
ir *= ir;
|
||||
|
||||
pixel += padding * width;
|
||||
for (y = padding; y < height - padding; y++) {
|
||||
int x;
|
||||
int y2 = (y - halfh) * (y - halfh);
|
||||
|
||||
pixel += padding;
|
||||
for (x = padding; x < width - padding; x++) {
|
||||
uint32_t v;
|
||||
|
||||
/* squared distance from center */
|
||||
int r2 = (x - halfw) * (x - halfw) + y2;
|
||||
|
||||
if (r2 < ir)
|
||||
v = (r2 / 32 + time / 64) * 0x0080401;
|
||||
else if (r2 < or)
|
||||
v = (y + time / 32) * 0x0080401;
|
||||
else
|
||||
v = (x + time / 16) * 0x0080401;
|
||||
v &= 0x00ffffff;
|
||||
|
||||
/* cross if compositor uses X from XRGB as alpha */
|
||||
if (abs(x - y) > 6 && abs(x + y - height) > 6)
|
||||
v |= 0xff000000;
|
||||
|
||||
*pixel++ = v;
|
||||
}
|
||||
|
||||
pixel += padding;
|
||||
}
|
||||
}
|
||||
|
||||
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" :
|
||||
"Both buffers busy at redraw(). Server bug?\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
paint_pixels(buffer->shm_data, 20, window->width, window->height, time);
|
||||
|
||||
wl_surface_attach(window->surface, buffer->buffer, 0, 0);
|
||||
wl_surface_damage(window->surface,
|
||||
20, 20, window->width - 40, window->height - 40);
|
||||
|
||||
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
|
||||
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
||||
{
|
||||
struct display *d = data;
|
||||
|
||||
if (format == WL_SHM_FORMAT_XRGB8888)
|
||||
d->has_xrgb = true;
|
||||
}
|
||||
|
||||
struct wl_shm_listener shm_listener = {
|
||||
shm_format
|
||||
};
|
||||
|
||||
static void
|
||||
xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
|
||||
{
|
||||
xdg_wm_base_pong(shell, serial);
|
||||
}
|
||||
|
||||
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
|
||||
xdg_wm_base_ping,
|
||||
};
|
||||
|
||||
static void
|
||||
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, "wl_shm") == 0) {
|
||||
d->shm = wl_registry_bind(registry,
|
||||
id, &wl_shm_interface, 1);
|
||||
wl_shm_add_listener(d->shm, &shm_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);
|
||||
|
||||
display->has_xrgb = false;
|
||||
display->registry = wl_display_get_registry(display->display);
|
||||
wl_registry_add_listener(display->registry,
|
||||
®istry_listener, display);
|
||||
wl_display_roundtrip(display->display);
|
||||
if (display->shm == NULL) {
|
||||
fprintf(stderr, "No wl_shm global\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
wl_display_roundtrip(display->display);
|
||||
|
||||
/*
|
||||
* Why do we need two roundtrips here?
|
||||
*
|
||||
* wl_display_get_registry() sends a request to the server, to which
|
||||
* the server replies by emitting the wl_registry.global events.
|
||||
* The first wl_display_roundtrip() sends wl_display.sync. The server
|
||||
* first processes the wl_display.get_registry which includes sending
|
||||
* the global events, and then processes the sync. Therefore when the
|
||||
* sync (roundtrip) returns, we are guaranteed to have received and
|
||||
* processed all the global events.
|
||||
*
|
||||
* While we are inside the first wl_display_roundtrip(), incoming
|
||||
* events are dispatched, which causes registry_handle_global() to
|
||||
* be called for each global. One of these globals is wl_shm.
|
||||
* registry_handle_global() sends wl_registry.bind request for the
|
||||
* wl_shm global. However, wl_registry.bind request is sent after
|
||||
* the first wl_display.sync, so the reply to the sync comes before
|
||||
* the initial events of the wl_shm object.
|
||||
*
|
||||
* The initial events that get sent as a reply to binding to wl_shm
|
||||
* include wl_shm.format. These tell us which pixel formats are
|
||||
* supported, and we need them before we can create buffers. They
|
||||
* don't change at runtime, so we receive them as part of init.
|
||||
*
|
||||
* When the reply to the first sync comes, the server may or may not
|
||||
* have sent the initial wl_shm events. Therefore we need the second
|
||||
* wl_display_roundtrip() call here.
|
||||
*
|
||||
* The server processes the wl_registry.bind for wl_shm first, and
|
||||
* the second wl_display.sync next. During our second call to
|
||||
* wl_display_roundtrip() the initial wl_shm events are received and
|
||||
* processed. Finally, when the reply to the second wl_display.sync
|
||||
* arrives, it guarantees we have processed all wl_shm initial events.
|
||||
*
|
||||
* This sequence contains two examples on how wl_display_roundtrip()
|
||||
* can be used to guarantee, that all reply events to a request
|
||||
* have been received and processed. This is a general Wayland
|
||||
* technique.
|
||||
*/
|
||||
|
||||
if (!display->has_xrgb) {
|
||||
fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_display(struct display *display)
|
||||
{
|
||||
if (display->shm)
|
||||
wl_shm_destroy(display->shm);
|
||||
|
||||
if (display->wm_base)
|
||||
xdg_wm_base_destroy(display->wm_base);
|
||||
|
||||
if (display->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);
|
||||
|
||||
/* Initialise damage to full surface, so the padding gets painted */
|
||||
wl_surface_damage(window->surface, 0, 0,
|
||||
window->width, window->height);
|
||||
|
||||
if (!window->wait_for_configure)
|
||||
redraw(window, NULL, 0);
|
||||
|
||||
while (running && ret != -1)
|
||||
ret = wl_display_dispatch(display->display);
|
||||
|
||||
fprintf(stderr, "simple-shm exiting\n");
|
||||
|
||||
destroy_window(window);
|
||||
destroy_display(display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
* Copyright © 2011 Benjamin Franzke
|
||||
* Copyright © 2011 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/os-compatibility.h"
|
||||
|
||||
struct seat {
|
||||
struct touch *touch;
|
||||
struct wl_seat *seat;
|
||||
struct wl_touch *wl_touch;
|
||||
};
|
||||
|
||||
struct touch {
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
struct wl_shell *shell;
|
||||
struct wl_shm *shm;
|
||||
struct wl_pointer *pointer;
|
||||
struct wl_keyboard *keyboard;
|
||||
struct wl_surface *surface;
|
||||
struct wl_shell_surface *shell_surface;
|
||||
struct wl_buffer *buffer;
|
||||
int has_argb;
|
||||
int width, height;
|
||||
void *data;
|
||||
};
|
||||
|
||||
static void
|
||||
create_shm_buffer(struct touch *touch)
|
||||
{
|
||||
struct wl_shm_pool *pool;
|
||||
int fd, size, stride;
|
||||
|
||||
stride = touch->width * 4;
|
||||
size = stride * touch->height;
|
||||
|
||||
fd = os_create_anonymous_file(size);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
|
||||
size, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
touch->data =
|
||||
mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (touch->data == MAP_FAILED) {
|
||||
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pool = wl_shm_create_pool(touch->shm, fd, size);
|
||||
touch->buffer =
|
||||
wl_shm_pool_create_buffer(pool, 0,
|
||||
touch->width, touch->height, stride,
|
||||
WL_SHM_FORMAT_ARGB8888);
|
||||
wl_shm_pool_destroy(pool);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void
|
||||
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
||||
{
|
||||
struct touch *touch = data;
|
||||
|
||||
if (format == WL_SHM_FORMAT_ARGB8888)
|
||||
touch->has_argb = 1;
|
||||
}
|
||||
|
||||
struct wl_shm_listener shm_listener = {
|
||||
shm_format
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
touch_paint(struct touch *touch, int32_t x, int32_t y, int32_t id)
|
||||
{
|
||||
uint32_t *p, c;
|
||||
static const uint32_t colors[] = {
|
||||
0xffff0000,
|
||||
0xffffff00,
|
||||
0xff0000ff,
|
||||
0xffff00ff,
|
||||
0xff00ff00,
|
||||
0xff00ffff,
|
||||
};
|
||||
|
||||
if (id < (int32_t) ARRAY_LENGTH(colors))
|
||||
c = colors[id];
|
||||
else
|
||||
c = 0xffffffff;
|
||||
|
||||
if (x < 2 || x >= touch->width - 2 ||
|
||||
y < 2 || y >= touch->height - 2)
|
||||
return;
|
||||
|
||||
p = (uint32_t *) touch->data + (x - 2) + (y - 2) * touch->width;
|
||||
p[2] = c;
|
||||
p += touch->width;
|
||||
p[1] = c;
|
||||
p[2] = c;
|
||||
p[3] = c;
|
||||
p += touch->width;
|
||||
p[0] = c;
|
||||
p[1] = c;
|
||||
p[2] = c;
|
||||
p[3] = c;
|
||||
p[4] = c;
|
||||
p += touch->width;
|
||||
p[1] = c;
|
||||
p[2] = c;
|
||||
p[3] = c;
|
||||
p += touch->width;
|
||||
p[2] = c;
|
||||
|
||||
wl_surface_attach(touch->surface, touch->buffer, 0, 0);
|
||||
wl_surface_damage(touch->surface, x - 2, y - 2, 5, 5);
|
||||
/* todo: We could queue up more damage before committing, if there
|
||||
* are more input events to handle.
|
||||
*/
|
||||
wl_surface_commit(touch->surface);
|
||||
}
|
||||
|
||||
static void
|
||||
touch_handle_down(void *data, struct wl_touch *wl_touch,
|
||||
uint32_t serial, uint32_t time, struct wl_surface *surface,
|
||||
int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
|
||||
{
|
||||
struct touch *touch = data;
|
||||
float x = wl_fixed_to_double(x_w);
|
||||
float y = wl_fixed_to_double(y_w);
|
||||
|
||||
touch_paint(touch, x, y, id);
|
||||
}
|
||||
|
||||
static void
|
||||
touch_handle_up(void *data, struct wl_touch *wl_touch,
|
||||
uint32_t serial, uint32_t time, int32_t id)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
touch_handle_motion(void *data, struct wl_touch *wl_touch,
|
||||
uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
|
||||
{
|
||||
struct touch *touch = data;
|
||||
float x = wl_fixed_to_double(x_w);
|
||||
float y = wl_fixed_to_double(y_w);
|
||||
|
||||
touch_paint(touch, x, y, id);
|
||||
}
|
||||
|
||||
static void
|
||||
touch_handle_frame(void *data, struct wl_touch *wl_touch)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
touch_handle_cancel(void *data, struct wl_touch *wl_touch)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_touch_listener touch_listener = {
|
||||
touch_handle_down,
|
||||
touch_handle_up,
|
||||
touch_handle_motion,
|
||||
touch_handle_frame,
|
||||
touch_handle_cancel,
|
||||
};
|
||||
|
||||
static void
|
||||
seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
||||
enum wl_seat_capability caps)
|
||||
{
|
||||
struct seat *seat = data;
|
||||
struct touch *touch = seat->touch;
|
||||
|
||||
if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !seat->wl_touch) {
|
||||
seat->wl_touch = wl_seat_get_touch(wl_seat);
|
||||
wl_touch_set_user_data(seat->wl_touch, touch);
|
||||
wl_touch_add_listener(seat->wl_touch, &touch_listener, touch);
|
||||
} else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->wl_touch) {
|
||||
wl_touch_destroy(seat->wl_touch);
|
||||
seat->wl_touch = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_seat_listener seat_listener = {
|
||||
seat_handle_capabilities,
|
||||
};
|
||||
|
||||
static void
|
||||
add_seat(struct touch *touch, uint32_t name, uint32_t version)
|
||||
{
|
||||
struct seat *seat;
|
||||
|
||||
seat = malloc(sizeof *seat);
|
||||
assert(seat);
|
||||
|
||||
seat->touch = touch;
|
||||
seat->wl_touch = NULL;
|
||||
seat->seat = wl_registry_bind(touch->registry, name,
|
||||
&wl_seat_interface, 1);
|
||||
wl_seat_add_listener(seat->seat, &seat_listener, seat);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_ping(void *data, struct wl_shell_surface *shell_surface,
|
||||
uint32_t serial)
|
||||
{
|
||||
wl_shell_surface_pong(shell_surface, serial);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_configure(void *data, struct wl_shell_surface *shell_surface,
|
||||
uint32_t edges, int32_t width, int32_t height)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_shell_surface_listener shell_surface_listener = {
|
||||
handle_ping,
|
||||
handle_configure,
|
||||
handle_popup_done
|
||||
};
|
||||
|
||||
static void
|
||||
handle_global(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version)
|
||||
{
|
||||
struct touch *touch = data;
|
||||
|
||||
if (strcmp(interface, "wl_compositor") == 0) {
|
||||
touch->compositor =
|
||||
wl_registry_bind(registry, name,
|
||||
&wl_compositor_interface, 1);
|
||||
} else if (strcmp(interface, "wl_shell") == 0) {
|
||||
touch->shell =
|
||||
wl_registry_bind(registry, name,
|
||||
&wl_shell_interface, 1);
|
||||
} else if (strcmp(interface, "wl_shm") == 0) {
|
||||
touch->shm = wl_registry_bind(registry, name,
|
||||
&wl_shm_interface, 1);
|
||||
wl_shm_add_listener(touch->shm, &shm_listener, touch);
|
||||
} else if (strcmp(interface, "wl_seat") == 0) {
|
||||
add_seat(touch, name, version);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
handle_global,
|
||||
handle_global_remove
|
||||
};
|
||||
|
||||
static struct touch *
|
||||
touch_create(int width, int height)
|
||||
{
|
||||
struct touch *touch;
|
||||
|
||||
touch = malloc(sizeof *touch);
|
||||
if (touch == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
touch->display = wl_display_connect(NULL);
|
||||
assert(touch->display);
|
||||
|
||||
touch->has_argb = 0;
|
||||
touch->registry = wl_display_get_registry(touch->display);
|
||||
wl_registry_add_listener(touch->registry, ®istry_listener, touch);
|
||||
wl_display_dispatch(touch->display);
|
||||
wl_display_roundtrip(touch->display);
|
||||
|
||||
if (!touch->has_argb) {
|
||||
fprintf(stderr, "WL_SHM_FORMAT_ARGB32 not available\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
touch->width = width;
|
||||
touch->height = height;
|
||||
touch->surface = wl_compositor_create_surface(touch->compositor);
|
||||
touch->shell_surface = wl_shell_get_shell_surface(touch->shell,
|
||||
touch->surface);
|
||||
create_shm_buffer(touch);
|
||||
|
||||
if (touch->shell_surface) {
|
||||
wl_shell_surface_add_listener(touch->shell_surface,
|
||||
&shell_surface_listener, touch);
|
||||
wl_shell_surface_set_toplevel(touch->shell_surface);
|
||||
}
|
||||
|
||||
wl_surface_set_user_data(touch->surface, touch);
|
||||
wl_shell_surface_set_title(touch->shell_surface, "simple-touch");
|
||||
|
||||
memset(touch->data, 64, width * height * 4);
|
||||
wl_surface_attach(touch->surface, touch->buffer, 0, 0);
|
||||
wl_surface_damage(touch->surface, 0, 0, width, height);
|
||||
wl_surface_commit(touch->surface);
|
||||
|
||||
return touch;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct touch *touch;
|
||||
int ret = 0;
|
||||
|
||||
touch = touch_create(600, 500);
|
||||
|
||||
while (ret != -1)
|
||||
ret = wl_display_dispatch(touch->display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* Copyright © 2010 Kristian Høgsberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include <cairo.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include "window.h"
|
||||
|
||||
struct smoke {
|
||||
struct display *display;
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
int width, height;
|
||||
int current;
|
||||
struct { float *d, *u, *v; } b[2];
|
||||
};
|
||||
|
||||
static void diffuse(struct smoke *smoke, uint32_t time,
|
||||
float *source, float *dest)
|
||||
{
|
||||
float *s, *d;
|
||||
int x, y, k, stride;
|
||||
float t, a = 0.0002;
|
||||
|
||||
stride = smoke->width;
|
||||
|
||||
for (k = 0; k < 5; k++) {
|
||||
for (y = 1; y < smoke->height - 1; y++) {
|
||||
s = source + y * stride;
|
||||
d = dest + y * stride;
|
||||
for (x = 1; x < smoke->width - 1; x++) {
|
||||
t = d[x - 1] + d[x + 1] +
|
||||
d[x - stride] + d[x + stride];
|
||||
d[x] = (s[x] + a * t) / (1 + 4 * a) * 0.995;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void advect(struct smoke *smoke, uint32_t time,
|
||||
float *uu, float *vv, float *source, float *dest)
|
||||
{
|
||||
float *s, *d;
|
||||
float *u, *v;
|
||||
int x, y, stride;
|
||||
int i, j;
|
||||
float px, py, fx, fy;
|
||||
|
||||
stride = smoke->width;
|
||||
|
||||
for (y = 1; y < smoke->height - 1; y++) {
|
||||
d = dest + y * stride;
|
||||
u = uu + y * stride;
|
||||
v = vv + y * stride;
|
||||
|
||||
for (x = 1; x < smoke->width - 1; x++) {
|
||||
px = x - u[x];
|
||||
py = y - v[x];
|
||||
if (px < 0.5)
|
||||
px = 0.5;
|
||||
if (py < 0.5)
|
||||
py = 0.5;
|
||||
if (px > smoke->width - 1.5)
|
||||
px = smoke->width - 1.5;
|
||||
if (py > smoke->height - 1.5)
|
||||
py = smoke->height - 1.5;
|
||||
i = (int) px;
|
||||
j = (int) py;
|
||||
fx = px - i;
|
||||
fy = py - j;
|
||||
s = source + j * stride + i;
|
||||
d[x] = (s[0] * (1 - fx) + s[1] * fx) * (1 - fy) +
|
||||
(s[stride] * (1 - fx) + s[stride + 1] * fx) * fy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void project(struct smoke *smoke, uint32_t time,
|
||||
float *u, float *v, float *p, float *div)
|
||||
{
|
||||
int x, y, k, l, s;
|
||||
float h;
|
||||
|
||||
h = 1.0 / smoke->width;
|
||||
s = smoke->width;
|
||||
memset(p, 0, smoke->height * smoke->width);
|
||||
for (y = 1; y < smoke->height - 1; y++) {
|
||||
l = y * s;
|
||||
for (x = 1; x < smoke->width - 1; x++) {
|
||||
div[l + x] = -0.5 * h * (u[l + x + 1] - u[l + x - 1] +
|
||||
v[l + x + s] - v[l + x - s]);
|
||||
p[l + x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (k = 0; k < 5; k++) {
|
||||
for (y = 1; y < smoke->height - 1; y++) {
|
||||
l = y * s;
|
||||
for (x = 1; x < smoke->width - 1; x++) {
|
||||
p[l + x] = (div[l + x] +
|
||||
p[l + x - 1] +
|
||||
p[l + x + 1] +
|
||||
p[l + x - s] +
|
||||
p[l + x + s]) / 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (y = 1; y < smoke->height - 1; y++) {
|
||||
l = y * s;
|
||||
for (x = 1; x < smoke->width - 1; x++) {
|
||||
u[l + x] -= 0.5 * (p[l + x + 1] - p[l + x - 1]) / h;
|
||||
v[l + x] -= 0.5 * (p[l + x + s] - p[l + x - s]) / h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void render(struct smoke *smoke, cairo_surface_t *surface)
|
||||
{
|
||||
unsigned char *dest;
|
||||
int x, y, width, height, stride;
|
||||
float *s;
|
||||
uint32_t *d, c, a;
|
||||
|
||||
dest = cairo_image_surface_get_data(surface);
|
||||
width = cairo_image_surface_get_width(surface);
|
||||
height = cairo_image_surface_get_height(surface);
|
||||
stride = cairo_image_surface_get_stride(surface);
|
||||
|
||||
for (y = 1; y < height - 1; y++) {
|
||||
s = smoke->b[smoke->current].d + y * smoke->height;
|
||||
d = (uint32_t *) (dest + y * stride);
|
||||
for (x = 1; x < width - 1; x++) {
|
||||
c = (int) (s[x] * 800);
|
||||
if (c > 255)
|
||||
c = 255;
|
||||
a = c;
|
||||
if (a < 0x33)
|
||||
a = 0x33;
|
||||
d[x] = (a << 24) | (c << 16) | (c << 8) | c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct smoke *smoke = data;
|
||||
uint32_t time = widget_get_last_time(smoke->widget);
|
||||
cairo_surface_t *surface;
|
||||
|
||||
diffuse(smoke, time / 30, smoke->b[0].u, smoke->b[1].u);
|
||||
diffuse(smoke, time / 30, smoke->b[0].v, smoke->b[1].v);
|
||||
project(smoke, time / 30,
|
||||
smoke->b[1].u, smoke->b[1].v,
|
||||
smoke->b[0].u, smoke->b[0].v);
|
||||
advect(smoke, time / 30,
|
||||
smoke->b[1].u, smoke->b[1].v,
|
||||
smoke->b[1].u, smoke->b[0].u);
|
||||
advect(smoke, time / 30,
|
||||
smoke->b[1].u, smoke->b[1].v,
|
||||
smoke->b[1].v, smoke->b[0].v);
|
||||
project(smoke, time / 30,
|
||||
smoke->b[0].u, smoke->b[0].v,
|
||||
smoke->b[1].u, smoke->b[1].v);
|
||||
|
||||
diffuse(smoke, time / 30, smoke->b[0].d, smoke->b[1].d);
|
||||
advect(smoke, time / 30,
|
||||
smoke->b[0].u, smoke->b[0].v,
|
||||
smoke->b[1].d, smoke->b[0].d);
|
||||
|
||||
surface = window_get_surface(smoke->window);
|
||||
|
||||
render(smoke, surface);
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
|
||||
widget_schedule_redraw(smoke->widget);
|
||||
}
|
||||
|
||||
static void
|
||||
smoke_motion_handler(struct smoke *smoke, float x, float y)
|
||||
{
|
||||
int i, i0, i1, j, j0, j1, k, d = 5;
|
||||
|
||||
if (x - d < 1)
|
||||
i0 = 1;
|
||||
else
|
||||
i0 = x - d;
|
||||
if (i0 + 2 * d > smoke->width - 1)
|
||||
i1 = smoke->width - 1;
|
||||
else
|
||||
i1 = i0 + 2 * d;
|
||||
|
||||
if (y - d < 1)
|
||||
j0 = 1;
|
||||
else
|
||||
j0 = y - d;
|
||||
if (j0 + 2 * d > smoke->height - 1)
|
||||
j1 = smoke->height - 1;
|
||||
else
|
||||
j1 = j0 + 2 * d;
|
||||
|
||||
for (i = i0; i < i1; i++)
|
||||
for (j = j0; j < j1; j++) {
|
||||
k = j * smoke->width + i;
|
||||
smoke->b[0].u[k] += 256 - (random() & 512);
|
||||
smoke->b[0].v[k] += 256 - (random() & 512);
|
||||
smoke->b[0].d[k] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
mouse_motion_handler(struct widget *widget, struct input *input,
|
||||
uint32_t time, float x, float y, void *data)
|
||||
{
|
||||
smoke_motion_handler(data, x, y);
|
||||
|
||||
return CURSOR_HAND1;
|
||||
}
|
||||
|
||||
static void
|
||||
touch_motion_handler(struct widget *widget, struct input *input,
|
||||
uint32_t time, int32_t id, float x, float y, void *data)
|
||||
{
|
||||
smoke_motion_handler(data, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
resize_handler(struct widget *widget,
|
||||
int32_t width, int32_t height, void *data)
|
||||
{
|
||||
struct smoke *smoke = data;
|
||||
|
||||
/* Don't resize me */
|
||||
widget_set_size(smoke->widget, smoke->width, smoke->height);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct timespec ts;
|
||||
struct smoke smoke;
|
||||
struct display *d;
|
||||
int size;
|
||||
|
||||
d = display_create(&argc, argv);
|
||||
if (d == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
smoke.width = 200;
|
||||
smoke.height = 200;
|
||||
smoke.display = d;
|
||||
smoke.window = window_create(d);
|
||||
smoke.widget = window_add_widget(smoke.window, &smoke);
|
||||
window_set_title(smoke.window, "smoke");
|
||||
|
||||
window_set_buffer_type(smoke.window, WINDOW_BUFFER_TYPE_SHM);
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
srandom(ts.tv_nsec);
|
||||
|
||||
smoke.current = 0;
|
||||
size = smoke.height * smoke.width;
|
||||
smoke.b[0].d = calloc(size, sizeof(float));
|
||||
smoke.b[0].u = calloc(size, sizeof(float));
|
||||
smoke.b[0].v = calloc(size, sizeof(float));
|
||||
smoke.b[1].d = calloc(size, sizeof(float));
|
||||
smoke.b[1].u = calloc(size, sizeof(float));
|
||||
smoke.b[1].v = calloc(size, sizeof(float));
|
||||
|
||||
widget_set_motion_handler(smoke.widget, mouse_motion_handler);
|
||||
widget_set_touch_motion_handler(smoke.widget, touch_motion_handler);
|
||||
widget_set_resize_handler(smoke.widget, resize_handler);
|
||||
widget_set_redraw_handler(smoke.widget, redraw_handler);
|
||||
|
||||
window_set_user_data(smoke.window, &smoke);
|
||||
|
||||
widget_schedule_resize(smoke.widget, smoke.width, smoke.height);
|
||||
|
||||
display_run(d);
|
||||
|
||||
widget_destroy(smoke.widget);
|
||||
window_destroy(smoke.window);
|
||||
display_destroy(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* Copyright © 2013 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 <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <cairo.h>
|
||||
#include <wayland-util.h>
|
||||
|
||||
#include "shared/helpers.h"
|
||||
#include "window.h"
|
||||
|
||||
struct stacking {
|
||||
struct display *display;
|
||||
struct window *root_window;
|
||||
};
|
||||
|
||||
static void
|
||||
button_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t button,
|
||||
enum wl_pointer_button_state state, void *data);
|
||||
static void
|
||||
key_handler(struct window *window,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
|
||||
void *data);
|
||||
static void
|
||||
keyboard_focus_handler(struct window *window,
|
||||
struct input *device, void *data);
|
||||
static void
|
||||
fullscreen_handler(struct window *window, void *data);
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data);
|
||||
|
||||
/* Iff parent_window is set, the new window will be transient. */
|
||||
static struct window *
|
||||
new_window(struct stacking *stacking, struct window *parent_window)
|
||||
{
|
||||
struct window *new_window;
|
||||
struct widget *new_widget;
|
||||
|
||||
new_window = window_create(stacking->display);
|
||||
window_set_parent(new_window, parent_window);
|
||||
|
||||
new_widget = window_frame_create(new_window, new_window);
|
||||
|
||||
window_set_title(new_window, "Stacking Test");
|
||||
window_set_key_handler(new_window, key_handler);
|
||||
window_set_keyboard_focus_handler(new_window, keyboard_focus_handler);
|
||||
window_set_fullscreen_handler(new_window, fullscreen_handler);
|
||||
widget_set_button_handler(new_widget, button_handler);
|
||||
widget_set_redraw_handler(new_widget, redraw_handler);
|
||||
window_set_user_data(new_window, stacking);
|
||||
|
||||
window_schedule_resize(new_window, 300, 300);
|
||||
|
||||
return new_window;
|
||||
}
|
||||
|
||||
static void
|
||||
show_popup_cb(void *data, struct input *input, int index)
|
||||
{
|
||||
/* Ignore the selected menu item. */
|
||||
}
|
||||
|
||||
static void
|
||||
show_popup(struct stacking *stacking, struct input *input, uint32_t time,
|
||||
struct window *window)
|
||||
{
|
||||
int32_t x, y;
|
||||
static const char *entries[] = {
|
||||
"Test Entry",
|
||||
"Another Test Entry",
|
||||
};
|
||||
|
||||
input_get_position(input, &x, &y);
|
||||
window_show_menu(stacking->display, input, time, window, x, y,
|
||||
show_popup_cb, entries, ARRAY_LENGTH(entries));
|
||||
}
|
||||
|
||||
static void
|
||||
button_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t button,
|
||||
enum wl_pointer_button_state state, void *data)
|
||||
{
|
||||
struct stacking *stacking = data;
|
||||
|
||||
switch (button) {
|
||||
case BTN_RIGHT:
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
show_popup(stacking, input, time,
|
||||
widget_get_user_data(widget));
|
||||
break;
|
||||
|
||||
case BTN_LEFT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
key_handler(struct window *window,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
|
||||
void *data)
|
||||
{
|
||||
struct stacking *stacking = data;
|
||||
|
||||
if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
return;
|
||||
|
||||
switch (sym) {
|
||||
case XKB_KEY_f:
|
||||
fullscreen_handler(window, data);
|
||||
break;
|
||||
|
||||
case XKB_KEY_m:
|
||||
window_set_maximized(window, !window_is_maximized(window));
|
||||
break;
|
||||
|
||||
case XKB_KEY_n:
|
||||
/* New top-level window. */
|
||||
new_window(stacking, NULL);
|
||||
break;
|
||||
|
||||
case XKB_KEY_p:
|
||||
show_popup(stacking, input, time, window);
|
||||
break;
|
||||
|
||||
case XKB_KEY_q:
|
||||
exit (0);
|
||||
break;
|
||||
|
||||
case XKB_KEY_t:
|
||||
/* New transient window. */
|
||||
new_window(stacking, window);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_focus_handler(struct window *window,
|
||||
struct input *device, void *data)
|
||||
{
|
||||
window_schedule_redraw(window);
|
||||
}
|
||||
|
||||
static void
|
||||
fullscreen_handler(struct window *window, void *data)
|
||||
{
|
||||
window_set_fullscreen(window, !window_is_fullscreen(window));
|
||||
}
|
||||
|
||||
static void
|
||||
draw_string(cairo_t *cr,
|
||||
const char *fmt, ...) WL_PRINTF(2, 3);
|
||||
|
||||
static void
|
||||
draw_string(cairo_t *cr,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
char buffer[4096];
|
||||
char *p, *end;
|
||||
va_list argp;
|
||||
cairo_text_extents_t text_extents;
|
||||
cairo_font_extents_t font_extents;
|
||||
|
||||
cairo_save(cr);
|
||||
|
||||
cairo_select_font_face(cr, "sans",
|
||||
CAIRO_FONT_SLANT_NORMAL,
|
||||
CAIRO_FONT_WEIGHT_NORMAL);
|
||||
cairo_set_font_size(cr, 14);
|
||||
|
||||
cairo_font_extents(cr, &font_extents);
|
||||
|
||||
va_start(argp, fmt);
|
||||
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, argp);
|
||||
|
||||
p = buffer;
|
||||
while (*p) {
|
||||
end = strchr(p, '\n');
|
||||
if (end)
|
||||
*end = 0;
|
||||
|
||||
cairo_show_text(cr, p);
|
||||
cairo_text_extents(cr, p, &text_extents);
|
||||
cairo_rel_move_to(cr, -text_extents.x_advance, font_extents.height);
|
||||
|
||||
if (end)
|
||||
p = end + 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
va_end(argp);
|
||||
|
||||
cairo_restore(cr);
|
||||
}
|
||||
|
||||
static void
|
||||
set_window_background_colour(cairo_t *cr, struct window *window)
|
||||
{
|
||||
if (window_get_parent(window))
|
||||
cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 0.4);
|
||||
else if (window_is_maximized(window))
|
||||
cairo_set_source_rgba(cr, 1.0, 1.0, 0.0, 0.6);
|
||||
else if (window_is_fullscreen(window))
|
||||
cairo_set_source_rgba(cr, 0.0, 1.0, 1.0, 0.6);
|
||||
else
|
||||
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct window *window;
|
||||
struct rectangle allocation;
|
||||
cairo_t *cr;
|
||||
|
||||
widget_get_allocation(widget, &allocation);
|
||||
window = widget_get_user_data(widget);
|
||||
|
||||
cr = widget_cairo_create(widget);
|
||||
cairo_translate(cr, allocation.x, allocation.y);
|
||||
|
||||
/* Draw background. */
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
set_window_background_colour(cr, window);
|
||||
cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
|
||||
cairo_fill(cr);
|
||||
|
||||
/* Print the instructions. */
|
||||
cairo_move_to(cr, 5, 15);
|
||||
cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
|
||||
|
||||
draw_string(cr,
|
||||
"Window: %p\n"
|
||||
"Fullscreen? %u\n"
|
||||
"Maximized? %u\n"
|
||||
"Transient? %u\n"
|
||||
"Keys: (f)ullscreen, (m)aximize,\n"
|
||||
" (n)ew window, (p)opup,\n"
|
||||
" (q)uit, (t)ransient window\n",
|
||||
window, window_is_fullscreen(window),
|
||||
window_is_maximized(window), window_get_parent(window) ? 1 : 0);
|
||||
|
||||
cairo_destroy(cr);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct stacking stacking;
|
||||
|
||||
memset(&stacking, 0, sizeof stacking);
|
||||
|
||||
stacking.display = display_create(&argc, argv);
|
||||
if (stacking.display == NULL) {
|
||||
fprintf(stderr, "Failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
display_set_user_data(stacking.display, &stacking);
|
||||
|
||||
stacking.root_window = new_window(&stacking, NULL);
|
||||
|
||||
display_run(stacking.display);
|
||||
|
||||
window_destroy(stacking.root_window);
|
||||
display_destroy(stacking.display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,811 @@
|
|||
/*
|
||||
* Copyright © 2010 Intel Corporation
|
||||
* Copyright © 2011 Benjamin Franzke
|
||||
* Copyright © 2012-2013 Collabora, Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cairo.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include <wayland-egl.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/xalloc.h"
|
||||
#include <libweston/zalloc.h>
|
||||
#include "window.h"
|
||||
|
||||
#if 0
|
||||
#define DBG(fmt, ...) \
|
||||
fprintf(stderr, "%d:%s " fmt, __LINE__, __func__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define DBG(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
static int32_t option_red_mode;
|
||||
static int32_t option_triangle_mode;
|
||||
static bool option_no_triangle;
|
||||
static bool option_help;
|
||||
|
||||
static const struct weston_option options[] = {
|
||||
{ WESTON_OPTION_INTEGER, "red-mode", 'r', &option_red_mode },
|
||||
{ WESTON_OPTION_INTEGER, "triangle-mode", 't', &option_triangle_mode },
|
||||
{ WESTON_OPTION_BOOLEAN, "no-triangle", 'n', &option_no_triangle },
|
||||
{ WESTON_OPTION_BOOLEAN, "help", 'h', &option_help },
|
||||
};
|
||||
|
||||
static enum subsurface_mode
|
||||
int_to_mode(int32_t i)
|
||||
{
|
||||
switch (i) {
|
||||
case 0:
|
||||
return SUBSURFACE_DESYNCHRONIZED;
|
||||
case 1:
|
||||
return SUBSURFACE_SYNCHRONIZED;
|
||||
default:
|
||||
fprintf(stderr, "error: %d is not a valid commit mode.\n", i);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static const char help_text[] =
|
||||
"Usage: %s [options]\n"
|
||||
"\n"
|
||||
" -r, --red-mode=MODE\t\tthe commit mode for the red sub-surface (0)\n"
|
||||
" -t, --triangle-mode=MODE\tthe commit mode for the GL sub-surface (0)\n"
|
||||
" -n, --no-triangle\t\tDo not create the GL sub-surface.\n"
|
||||
"\n"
|
||||
"The MODE is the wl_subsurface commit mode used by default for the\n"
|
||||
"given sub-surface. Valid values are the integers:\n"
|
||||
" 0\tfor desynchronized, i.e. free-running\n"
|
||||
" 1\tfor synchronized\n"
|
||||
"\n"
|
||||
"This program demonstrates sub-surfaces with the toytoolkit.\n"
|
||||
"The main surface contains the decorations, a green canvas, and a\n"
|
||||
"green spinner. One sub-surface is red with a red spinner. These\n"
|
||||
"are rendered with Cairo. The other sub-surface contains a spinning\n"
|
||||
"triangle rendered in EGL/GLESv2, without Cairo, i.e. it is a raw GL\n"
|
||||
"widget.\n"
|
||||
"\n"
|
||||
"The GL widget animates on its own. The spinners follow wall clock\n"
|
||||
"time and update only when their surface is repainted, so you see\n"
|
||||
"which surfaces get redrawn. The red sub-surface animates on its own,\n"
|
||||
"but can be toggled with the spacebar.\n"
|
||||
"\n"
|
||||
"Even though the sub-surfaces attempt to animate on their own, they\n"
|
||||
"are subject to the commit mode. If commit mode is synchronized,\n"
|
||||
"they will need a commit on the main surface to actually display.\n"
|
||||
"You can trigger a main surface repaint, without a resize, by\n"
|
||||
"hovering the pointer over the title bar buttons.\n"
|
||||
"\n"
|
||||
"Resizing will temporarily toggle the commit mode of all sub-surfaces\n"
|
||||
"to guarantee synchronized rendering on size changes. It also forces\n"
|
||||
"a repaint of all surfaces.\n"
|
||||
"\n"
|
||||
"Using -t1 -r1 is especially useful for trying to catch inconsistent\n"
|
||||
"rendering and deadlocks, since free-running sub-surfaces would\n"
|
||||
"immediately hide the problem.\n"
|
||||
"\n"
|
||||
"Key controls:\n"
|
||||
" space - toggle red sub-surface animation loop\n"
|
||||
" up - step window size shorter\n"
|
||||
" down - step window size taller\n"
|
||||
"\n";
|
||||
|
||||
struct egl_state {
|
||||
EGLDisplay dpy;
|
||||
EGLContext ctx;
|
||||
EGLConfig conf;
|
||||
};
|
||||
|
||||
struct triangle_gl_state {
|
||||
GLuint rotation_uniform;
|
||||
GLuint pos;
|
||||
GLuint col;
|
||||
};
|
||||
|
||||
struct triangle {
|
||||
struct egl_state *egl;
|
||||
|
||||
struct wl_surface *wl_surface;
|
||||
struct wl_egl_window *egl_window;
|
||||
EGLSurface egl_surface;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
struct triangle_gl_state gl;
|
||||
|
||||
struct widget *widget;
|
||||
uint32_t time;
|
||||
struct wl_callback *frame_cb;
|
||||
};
|
||||
|
||||
/******** Pure EGL/GLESv2/libwayland-client component: ***************/
|
||||
|
||||
static const char *vert_shader_text =
|
||||
"uniform mat4 rotation;\n"
|
||||
"attribute vec4 pos;\n"
|
||||
"attribute vec4 color;\n"
|
||||
"varying vec4 v_color;\n"
|
||||
"void main() {\n"
|
||||
" gl_Position = rotation * pos;\n"
|
||||
" v_color = color;\n"
|
||||
"}\n";
|
||||
|
||||
static const char *frag_shader_text =
|
||||
"precision mediump float;\n"
|
||||
"varying vec4 v_color;\n"
|
||||
"void main() {\n"
|
||||
" gl_FragColor = v_color;\n"
|
||||
"}\n";
|
||||
|
||||
static void
|
||||
egl_print_config_info(struct egl_state *egl)
|
||||
{
|
||||
EGLint r, g, b, a;
|
||||
|
||||
printf("Chosen EGL config details:\n");
|
||||
|
||||
printf("\tRGBA bits");
|
||||
if (eglGetConfigAttrib(egl->dpy, egl->conf, EGL_RED_SIZE, &r) &&
|
||||
eglGetConfigAttrib(egl->dpy, egl->conf, EGL_GREEN_SIZE, &g) &&
|
||||
eglGetConfigAttrib(egl->dpy, egl->conf, EGL_BLUE_SIZE, &b) &&
|
||||
eglGetConfigAttrib(egl->dpy, egl->conf, EGL_ALPHA_SIZE, &a))
|
||||
printf(": %d %d %d %d\n", r, g, b, a);
|
||||
else
|
||||
printf(" unknown\n");
|
||||
|
||||
printf("\tswap interval range");
|
||||
if (eglGetConfigAttrib(egl->dpy, egl->conf, EGL_MIN_SWAP_INTERVAL, &a) &&
|
||||
eglGetConfigAttrib(egl->dpy, egl->conf, EGL_MAX_SWAP_INTERVAL, &b))
|
||||
printf(": %d - %d\n", a, b);
|
||||
else
|
||||
printf(" unknown\n");
|
||||
}
|
||||
|
||||
static struct egl_state *
|
||||
egl_state_create(struct wl_display *display)
|
||||
{
|
||||
struct egl_state *egl;
|
||||
|
||||
static const EGLint context_attribs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
EGLint config_attribs[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_RED_SIZE, 1,
|
||||
EGL_GREEN_SIZE, 1,
|
||||
EGL_BLUE_SIZE, 1,
|
||||
EGL_ALPHA_SIZE, 1,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
EGLint major, minor, n;
|
||||
EGLBoolean ret;
|
||||
|
||||
egl = zalloc(sizeof *egl);
|
||||
assert(egl);
|
||||
|
||||
egl->dpy =
|
||||
weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR,
|
||||
display, NULL);
|
||||
assert(egl->dpy);
|
||||
|
||||
ret = eglInitialize(egl->dpy, &major, &minor);
|
||||
assert(ret == EGL_TRUE);
|
||||
ret = eglBindAPI(EGL_OPENGL_ES_API);
|
||||
assert(ret == EGL_TRUE);
|
||||
|
||||
ret = eglChooseConfig(egl->dpy, config_attribs, &egl->conf, 1, &n);
|
||||
assert(ret && n == 1);
|
||||
|
||||
egl->ctx = eglCreateContext(egl->dpy, egl->conf,
|
||||
EGL_NO_CONTEXT, context_attribs);
|
||||
assert(egl->ctx);
|
||||
egl_print_config_info(egl);
|
||||
|
||||
return egl;
|
||||
}
|
||||
|
||||
static void
|
||||
egl_state_destroy(struct egl_state *egl)
|
||||
{
|
||||
/* Required, otherwise segfault in egl_dri2.c: dri2_make_current()
|
||||
* on eglReleaseThread(). */
|
||||
eglMakeCurrent(egl->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||
EGL_NO_CONTEXT);
|
||||
|
||||
eglTerminate(egl->dpy);
|
||||
eglReleaseThread();
|
||||
free(egl);
|
||||
}
|
||||
|
||||
static void
|
||||
egl_make_swapbuffers_nonblock(struct egl_state *egl)
|
||||
{
|
||||
EGLint a = EGL_MIN_SWAP_INTERVAL;
|
||||
EGLint b = EGL_MAX_SWAP_INTERVAL;
|
||||
|
||||
if (!eglGetConfigAttrib(egl->dpy, egl->conf, a, &a) ||
|
||||
!eglGetConfigAttrib(egl->dpy, egl->conf, b, &b)) {
|
||||
fprintf(stderr, "warning: swap interval range unknown\n");
|
||||
} else if (a > 0) {
|
||||
fprintf(stderr, "warning: minimum swap interval is %d, "
|
||||
"while 0 is required to not deadlock on resize.\n", a);
|
||||
}
|
||||
|
||||
/*
|
||||
* We rely on the Wayland compositor to sync to vblank anyway.
|
||||
* We just need to be able to call eglSwapBuffers() without the
|
||||
* risk of waiting for a frame callback in it.
|
||||
*/
|
||||
if (!eglSwapInterval(egl->dpy, 0)) {
|
||||
fprintf(stderr, "error: eglSwapInterval() failed.\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);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
static void
|
||||
triangle_init_gl(struct triangle_gl_state *trigl)
|
||||
{
|
||||
GLuint frag, vert;
|
||||
GLuint program;
|
||||
GLint status;
|
||||
|
||||
frag = create_shader(frag_shader_text, GL_FRAGMENT_SHADER);
|
||||
vert = create_shader(vert_shader_text, GL_VERTEX_SHADER);
|
||||
|
||||
program = glCreateProgram();
|
||||
glAttachShader(program, frag);
|
||||
glAttachShader(program, vert);
|
||||
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);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
glUseProgram(program);
|
||||
|
||||
trigl->pos = 0;
|
||||
trigl->col = 1;
|
||||
|
||||
glBindAttribLocation(program, trigl->pos, "pos");
|
||||
glBindAttribLocation(program, trigl->col, "color");
|
||||
glLinkProgram(program);
|
||||
|
||||
trigl->rotation_uniform = glGetUniformLocation(program, "rotation");
|
||||
}
|
||||
|
||||
static void
|
||||
triangle_draw(const struct triangle_gl_state *trigl, uint32_t time)
|
||||
{
|
||||
static const GLfloat verts[3][2] = {
|
||||
{ -0.5, -0.5 },
|
||||
{ 0.5, -0.5 },
|
||||
{ 0, 0.5 }
|
||||
};
|
||||
static const GLfloat colors[3][3] = {
|
||||
{ 1, 0, 0 },
|
||||
{ 0, 1, 0 },
|
||||
{ 0, 0, 1 }
|
||||
};
|
||||
GLfloat angle;
|
||||
GLfloat rotation[4][4] = {
|
||||
{ 1, 0, 0, 0 },
|
||||
{ 0, 1, 0, 0 },
|
||||
{ 0, 0, 1, 0 },
|
||||
{ 0, 0, 0, 1 }
|
||||
};
|
||||
static const int32_t speed_div = 5;
|
||||
|
||||
angle = (time / speed_div) % 360 * M_PI / 180.0;
|
||||
rotation[0][0] = cos(angle);
|
||||
rotation[0][2] = sin(angle);
|
||||
rotation[2][0] = -sin(angle);
|
||||
rotation[2][2] = cos(angle);
|
||||
|
||||
glUniformMatrix4fv(trigl->rotation_uniform, 1, GL_FALSE,
|
||||
(GLfloat *) rotation);
|
||||
|
||||
glClearColor(0.0, 0.0, 0.0, 0.5);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glVertexAttribPointer(trigl->pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
|
||||
glVertexAttribPointer(trigl->col, 3, GL_FLOAT, GL_FALSE, 0, colors);
|
||||
glEnableVertexAttribArray(trigl->pos);
|
||||
glEnableVertexAttribArray(trigl->col);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
glDisableVertexAttribArray(trigl->pos);
|
||||
glDisableVertexAttribArray(trigl->col);
|
||||
}
|
||||
|
||||
static void
|
||||
triangle_frame_callback(void *data, struct wl_callback *callback,
|
||||
uint32_t time);
|
||||
|
||||
static const struct wl_callback_listener triangle_frame_listener = {
|
||||
triangle_frame_callback
|
||||
};
|
||||
|
||||
static void
|
||||
triangle_frame_callback(void *data, struct wl_callback *callback,
|
||||
uint32_t time)
|
||||
{
|
||||
struct triangle *tri = data;
|
||||
|
||||
DBG("%stime %u\n", callback ? "" : "artificial ", time);
|
||||
assert(callback == tri->frame_cb);
|
||||
tri->time = time;
|
||||
|
||||
if (callback)
|
||||
wl_callback_destroy(callback);
|
||||
|
||||
eglMakeCurrent(tri->egl->dpy, tri->egl_surface,
|
||||
tri->egl_surface, tri->egl->ctx);
|
||||
|
||||
glViewport(0, 0, tri->width, tri->height);
|
||||
|
||||
triangle_draw(&tri->gl, tri->time);
|
||||
|
||||
tri->frame_cb = wl_surface_frame(tri->wl_surface);
|
||||
wl_callback_add_listener(tri->frame_cb, &triangle_frame_listener, tri);
|
||||
|
||||
eglSwapBuffers(tri->egl->dpy, tri->egl_surface);
|
||||
}
|
||||
|
||||
static void
|
||||
triangle_create_egl_surface(struct triangle *tri, int width, int height)
|
||||
{
|
||||
EGLBoolean ret;
|
||||
|
||||
tri->wl_surface = widget_get_wl_surface(tri->widget);
|
||||
tri->egl_window = wl_egl_window_create(tri->wl_surface, width, height);
|
||||
tri->egl_surface = weston_platform_create_egl_surface(tri->egl->dpy,
|
||||
tri->egl->conf,
|
||||
tri->egl_window, NULL);
|
||||
|
||||
ret = eglMakeCurrent(tri->egl->dpy, tri->egl_surface,
|
||||
tri->egl_surface, tri->egl->ctx);
|
||||
assert(ret == EGL_TRUE);
|
||||
|
||||
egl_make_swapbuffers_nonblock(tri->egl);
|
||||
triangle_init_gl(&tri->gl);
|
||||
}
|
||||
|
||||
/********* The widget code interfacing the toolkit agnostic code: **********/
|
||||
|
||||
static void
|
||||
triangle_resize_handler(struct widget *widget,
|
||||
int32_t width, int32_t height, void *data)
|
||||
{
|
||||
struct triangle *tri = data;
|
||||
|
||||
DBG("to %dx%d\n", width, height);
|
||||
tri->width = width;
|
||||
tri->height = height;
|
||||
|
||||
if (tri->egl_surface) {
|
||||
wl_egl_window_resize(tri->egl_window, width, height, 0, 0);
|
||||
} else {
|
||||
triangle_create_egl_surface(tri, width, height);
|
||||
triangle_frame_callback(tri, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
triangle_redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct triangle *tri = data;
|
||||
int w, h;
|
||||
|
||||
wl_egl_window_get_attached_size(tri->egl_window, &w, &h);
|
||||
|
||||
DBG("previous %dx%d, new %dx%d\n", w, h, tri->width, tri->height);
|
||||
|
||||
/* If size is not changing, do not redraw ahead of time.
|
||||
* That would risk blocking in eglSwapbuffers().
|
||||
*/
|
||||
if (w == tri->width && h == tri->height)
|
||||
return;
|
||||
|
||||
if (tri->frame_cb) {
|
||||
wl_callback_destroy(tri->frame_cb);
|
||||
tri->frame_cb = NULL;
|
||||
}
|
||||
triangle_frame_callback(tri, NULL, tri->time);
|
||||
}
|
||||
|
||||
static void
|
||||
set_empty_input_region(struct widget *widget, struct display *display)
|
||||
{
|
||||
struct wl_compositor *compositor;
|
||||
struct wl_surface *surface;
|
||||
struct wl_region *region;
|
||||
|
||||
compositor = display_get_compositor(display);
|
||||
surface = widget_get_wl_surface(widget);
|
||||
region = wl_compositor_create_region(compositor);
|
||||
wl_surface_set_input_region(surface, region);
|
||||
wl_region_destroy(region);
|
||||
}
|
||||
|
||||
static struct triangle *
|
||||
triangle_create(struct window *window, struct egl_state *egl)
|
||||
{
|
||||
struct triangle *tri;
|
||||
|
||||
tri = xzalloc(sizeof *tri);
|
||||
|
||||
tri->egl = egl;
|
||||
tri->widget = window_add_subsurface(window, tri,
|
||||
int_to_mode(option_triangle_mode));
|
||||
widget_set_use_cairo(tri->widget, 0);
|
||||
widget_set_resize_handler(tri->widget, triangle_resize_handler);
|
||||
widget_set_redraw_handler(tri->widget, triangle_redraw_handler);
|
||||
|
||||
set_empty_input_region(tri->widget, window_get_display(window));
|
||||
|
||||
return tri;
|
||||
}
|
||||
|
||||
static void
|
||||
triangle_destroy(struct triangle *tri)
|
||||
{
|
||||
if (tri->egl_surface)
|
||||
weston_platform_destroy_egl_surface(tri->egl->dpy,
|
||||
tri->egl_surface);
|
||||
|
||||
if (tri->egl_window)
|
||||
wl_egl_window_destroy(tri->egl_window);
|
||||
|
||||
widget_destroy(tri->widget);
|
||||
free(tri);
|
||||
}
|
||||
|
||||
/************** The toytoolkit application code: *********************/
|
||||
|
||||
struct demoapp {
|
||||
struct display *display;
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
struct widget *subsurface;
|
||||
|
||||
struct egl_state *egl;
|
||||
struct triangle *triangle;
|
||||
|
||||
int animate;
|
||||
};
|
||||
|
||||
static void
|
||||
draw_spinner(cairo_t *cr, const struct rectangle *rect, uint32_t time)
|
||||
{
|
||||
double cx, cy, r, angle;
|
||||
unsigned t;
|
||||
|
||||
cx = rect->x + rect->width / 2;
|
||||
cy = rect->y + rect->height / 2;
|
||||
r = (rect->width < rect->height ? rect->width : rect->height) * 0.3;
|
||||
t = time % 2000;
|
||||
angle = t * (M_PI / 500.0);
|
||||
|
||||
cairo_set_line_width(cr, 4.0);
|
||||
|
||||
if (t < 1000)
|
||||
cairo_arc(cr, cx, cy, r, 0.0, angle);
|
||||
else
|
||||
cairo_arc(cr, cx, cy, r, angle, 0.0);
|
||||
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
|
||||
static void
|
||||
sub_redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct demoapp *app = data;
|
||||
cairo_t *cr;
|
||||
struct rectangle allocation;
|
||||
uint32_t time;
|
||||
|
||||
widget_get_allocation(app->subsurface, &allocation);
|
||||
|
||||
cr = widget_cairo_create(widget);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
|
||||
/* debug: paint whole surface magenta; no magenta should show */
|
||||
cairo_set_source_rgba(cr, 0.9, 0.0, 0.9, 1.0);
|
||||
cairo_paint(cr);
|
||||
|
||||
cairo_rectangle(cr,
|
||||
allocation.x,
|
||||
allocation.y,
|
||||
allocation.width,
|
||||
allocation.height);
|
||||
cairo_clip(cr);
|
||||
|
||||
cairo_set_source_rgba(cr, 0.8, 0, 0, 0.8);
|
||||
cairo_paint(cr);
|
||||
|
||||
time = widget_get_last_time(widget);
|
||||
cairo_set_source_rgba(cr, 1.0, 0.5, 0.5, 1.0);
|
||||
draw_spinner(cr, &allocation, time);
|
||||
|
||||
cairo_destroy(cr);
|
||||
|
||||
if (app->animate)
|
||||
widget_schedule_redraw(app->subsurface);
|
||||
DBG("%dx%d @ %d,%d, last time %u\n",
|
||||
allocation.width, allocation.height,
|
||||
allocation.x, allocation.y, time);
|
||||
}
|
||||
|
||||
static void
|
||||
sub_resize_handler(struct widget *widget,
|
||||
int32_t width, int32_t height, void *data)
|
||||
{
|
||||
DBG("%dx%d\n", width, height);
|
||||
widget_input_region_add(widget, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct demoapp *app = data;
|
||||
cairo_t *cr;
|
||||
struct rectangle allocation;
|
||||
uint32_t time;
|
||||
|
||||
widget_get_allocation(app->widget, &allocation);
|
||||
|
||||
cr = widget_cairo_create(widget);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_rectangle(cr,
|
||||
allocation.x,
|
||||
allocation.y,
|
||||
allocation.width,
|
||||
allocation.height);
|
||||
cairo_set_source_rgba(cr, 0, 0.8, 0, 0.8);
|
||||
cairo_fill(cr);
|
||||
|
||||
time = widget_get_last_time(widget);
|
||||
cairo_set_source_rgba(cr, 0.5, 1.0, 0.5, 1.0);
|
||||
draw_spinner(cr, &allocation, time);
|
||||
|
||||
cairo_destroy(cr);
|
||||
|
||||
DBG("%dx%d @ %d,%d, last time %u\n",
|
||||
allocation.width, allocation.height,
|
||||
allocation.x, allocation.y, time);
|
||||
}
|
||||
|
||||
static void
|
||||
resize_handler(struct widget *widget,
|
||||
int32_t width, int32_t height, void *data)
|
||||
{
|
||||
struct demoapp *app = data;
|
||||
struct rectangle area;
|
||||
int side, h;
|
||||
|
||||
widget_get_allocation(widget, &area);
|
||||
|
||||
side = area.width < area.height ? area.width / 2 : area.height / 2;
|
||||
h = area.height - side;
|
||||
|
||||
widget_set_allocation(app->subsurface,
|
||||
area.x + area.width - side,
|
||||
area.y,
|
||||
side, h);
|
||||
|
||||
if (app->triangle) {
|
||||
widget_set_allocation(app->triangle->widget,
|
||||
area.x + area.width - side,
|
||||
area.y + h,
|
||||
side, side);
|
||||
}
|
||||
|
||||
DBG("green %dx%d, red %dx%d, GL %dx%d\n",
|
||||
area.width, area.height, side, h, side, side);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_focus_handler(struct window *window,
|
||||
struct input *device, void *data)
|
||||
{
|
||||
struct demoapp *app = data;
|
||||
|
||||
window_schedule_redraw(app->window);
|
||||
}
|
||||
|
||||
static void
|
||||
key_handler(struct window *window, struct input *input, uint32_t time,
|
||||
uint32_t key, uint32_t sym,
|
||||
enum wl_keyboard_key_state state, void *data)
|
||||
{
|
||||
struct demoapp *app = data;
|
||||
struct rectangle winrect;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
|
||||
return;
|
||||
|
||||
switch (sym) {
|
||||
case XKB_KEY_space:
|
||||
app->animate = !app->animate;
|
||||
window_schedule_redraw(window);
|
||||
break;
|
||||
case XKB_KEY_Up:
|
||||
window_get_allocation(window, &winrect);
|
||||
winrect.height -= 100;
|
||||
if (winrect.height < 150)
|
||||
winrect.height = 150;
|
||||
window_schedule_resize(window, winrect.width, winrect.height);
|
||||
break;
|
||||
case XKB_KEY_Down:
|
||||
window_get_allocation(window, &winrect);
|
||||
winrect.height += 100;
|
||||
if (winrect.height > 600)
|
||||
winrect.height = 600;
|
||||
window_schedule_resize(window, winrect.width, winrect.height);
|
||||
break;
|
||||
case XKB_KEY_Escape:
|
||||
display_exit(app->display);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct demoapp *
|
||||
demoapp_create(struct display *display)
|
||||
{
|
||||
struct demoapp *app;
|
||||
|
||||
app = xzalloc(sizeof *app);
|
||||
|
||||
app->egl = egl_state_create(display_get_display(display));
|
||||
|
||||
app->display = display;
|
||||
display_set_user_data(app->display, app);
|
||||
|
||||
app->window = window_create(app->display);
|
||||
app->widget = window_frame_create(app->window, app);
|
||||
window_set_title(app->window, "Wayland Sub-surface Demo");
|
||||
|
||||
window_set_key_handler(app->window, key_handler);
|
||||
window_set_user_data(app->window, app);
|
||||
window_set_keyboard_focus_handler(app->window, keyboard_focus_handler);
|
||||
|
||||
widget_set_redraw_handler(app->widget, redraw_handler);
|
||||
widget_set_resize_handler(app->widget, resize_handler);
|
||||
|
||||
app->subsurface = window_add_subsurface(app->window, app,
|
||||
int_to_mode(option_red_mode));
|
||||
widget_set_redraw_handler(app->subsurface, sub_redraw_handler);
|
||||
widget_set_resize_handler(app->subsurface, sub_resize_handler);
|
||||
|
||||
if (app->egl && !option_no_triangle)
|
||||
app->triangle = triangle_create(app->window, app->egl);
|
||||
|
||||
/* minimum size */
|
||||
widget_schedule_resize(app->widget, 100, 100);
|
||||
|
||||
/* initial size */
|
||||
widget_schedule_resize(app->widget, 400, 300);
|
||||
|
||||
app->animate = 1;
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
static void
|
||||
demoapp_destroy(struct demoapp *app)
|
||||
{
|
||||
if (app->triangle)
|
||||
triangle_destroy(app->triangle);
|
||||
|
||||
if (app->egl)
|
||||
egl_state_destroy(app->egl);
|
||||
|
||||
widget_destroy(app->subsurface);
|
||||
widget_destroy(app->widget);
|
||||
window_destroy(app->window);
|
||||
free(app);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct display *display;
|
||||
struct demoapp *app;
|
||||
|
||||
if (parse_options(options, ARRAY_LENGTH(options), &argc, argv) > 1
|
||||
|| option_help) {
|
||||
printf(help_text, argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
display = display_create(&argc, argv);
|
||||
if (display == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!display_has_subcompositor(display)) {
|
||||
fprintf(stderr, "compositor does not support "
|
||||
"the subcompositor extension\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
app = demoapp_create(display);
|
||||
|
||||
display_run(display);
|
||||
|
||||
demoapp_destroy(app);
|
||||
display_destroy(display);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cairo.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "clients/window.h"
|
||||
#include "shared/helpers.h"
|
||||
#include <libweston/matrix.h>
|
||||
|
||||
#include "weston-touch-calibration-client-protocol.h"
|
||||
|
||||
enum exit_code {
|
||||
CAL_EXIT_SUCCESS = 0,
|
||||
CAL_EXIT_ERROR = 1,
|
||||
CAL_EXIT_CANCELLED = 2,
|
||||
};
|
||||
|
||||
static int debug_;
|
||||
static int verbose_;
|
||||
|
||||
#define pr_ver(...) do { \
|
||||
if (verbose_) \
|
||||
printf(__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define pr_dbg(...) do { \
|
||||
if (debug_) \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static void
|
||||
pr_err(const char *fmt, ...) WL_PRINTF(1, 2);
|
||||
|
||||
/* Our points for the calibration must be not be on a line */
|
||||
static const struct {
|
||||
float x_ratio, y_ratio;
|
||||
} test_ratios[] = {
|
||||
{ 0.15, 0.10 }, /* three points for calibration */
|
||||
{ 0.85, 0.13 },
|
||||
{ 0.20, 0.80 },
|
||||
{ 0.70, 0.75 } /* and one for verification */
|
||||
};
|
||||
|
||||
#define NR_SAMPLES ((int)ARRAY_LENGTH(test_ratios))
|
||||
|
||||
struct point {
|
||||
double x;
|
||||
double y;
|
||||
};
|
||||
|
||||
struct sample {
|
||||
int ind;
|
||||
struct point drawn; /**< drawn point, pixels */
|
||||
struct weston_touch_coordinate *pending;
|
||||
struct point drawn_cal; /**< drawn point, converted */
|
||||
bool conv_done;
|
||||
struct point touched; /**< touch point, normalized */
|
||||
bool touch_done;
|
||||
};
|
||||
|
||||
struct poly {
|
||||
struct color {
|
||||
double r, g, b, a;
|
||||
} color;
|
||||
int n_verts;
|
||||
const struct point *verts;
|
||||
};
|
||||
|
||||
/** Touch event handling state machine
|
||||
*
|
||||
* Only a complete down->up->frame sequence should be accepted with user
|
||||
* feedback "right", and anything that deviates from that (invalid_touch,
|
||||
* cancel, multiple touch-downs) needs to undo the current sample and
|
||||
* possibly show user feedback "wrong".
|
||||
*
|
||||
* \<STATE\>
|
||||
* - \<triggers\>: \<actions\>
|
||||
*
|
||||
* IDLE
|
||||
* - touch down: sample, -> DOWN
|
||||
* - touch up: no-op
|
||||
* - frame: no-op
|
||||
* - invalid_touch: (undo), wrong, -> WAIT
|
||||
* - cancel: no-op
|
||||
* DOWN (first touch down)
|
||||
* - touch down: undo, wrong, -> WAIT
|
||||
* - touch up: -> UP
|
||||
* - frame: no-op
|
||||
* - invalid_touch: undo, wrong, -> WAIT
|
||||
* - cancel: undo, -> IDLE
|
||||
* UP (first touch was down and up)
|
||||
* - touch down: undo, wrong, -> WAIT
|
||||
* - touch up: no-op
|
||||
* - frame: right, touch finish, -> WAIT
|
||||
* - invalid_touch: undo, wrong, -> WAIT
|
||||
* - cancel: undo, -> IDLE
|
||||
* WAIT (show user feedback)
|
||||
* - touch down: no-op
|
||||
* - touch up: no-op
|
||||
* - frame, cancel, timer: if num_tp == 0 && timer_done -> IDLE
|
||||
* - invalid_touch: no-op
|
||||
*/
|
||||
enum touch_state {
|
||||
STATE_IDLE,
|
||||
STATE_DOWN,
|
||||
STATE_UP,
|
||||
STATE_WAIT
|
||||
};
|
||||
|
||||
struct calibrator {
|
||||
struct sample samples[NR_SAMPLES];
|
||||
int current_sample;
|
||||
|
||||
struct display *display;
|
||||
struct weston_touch_calibration *calibration;
|
||||
struct weston_touch_calibrator *calibrator;
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
|
||||
int n_devices_listed;
|
||||
char *match_name;
|
||||
char *device_name;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
|
||||
bool cancelled;
|
||||
|
||||
const struct poly *current_poly;
|
||||
bool exiting;
|
||||
|
||||
struct toytimer wait_timer;
|
||||
bool timer_pending;
|
||||
enum touch_state state;
|
||||
|
||||
int num_tp; /* touch points down count */
|
||||
};
|
||||
|
||||
static struct sample *
|
||||
current_sample(struct calibrator *cal)
|
||||
{
|
||||
return &cal->samples[cal->current_sample];
|
||||
}
|
||||
|
||||
static void
|
||||
sample_start(struct calibrator *cal, int i)
|
||||
{
|
||||
struct sample *s = &cal->samples[i];
|
||||
|
||||
assert(i >= 0 && i < NR_SAMPLES);
|
||||
|
||||
s->ind = i;
|
||||
s->drawn.x = round(test_ratios[i].x_ratio * cal->width);
|
||||
s->drawn.y = round(test_ratios[i].y_ratio * cal->height);
|
||||
s->pending = NULL;
|
||||
s->conv_done = false;
|
||||
s->touch_done = false;
|
||||
|
||||
cal->current_sample = i;
|
||||
}
|
||||
|
||||
static struct point
|
||||
wire_to_point(uint32_t xu, uint32_t yu)
|
||||
{
|
||||
struct point p = {
|
||||
.x = (double)xu / 0xffffffff,
|
||||
.y = (double)yu / 0xffffffff
|
||||
};
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static void
|
||||
sample_touch_down(struct calibrator *cal, uint32_t xu, uint32_t yu)
|
||||
{
|
||||
struct sample *s = current_sample(cal);
|
||||
|
||||
s->touched = wire_to_point(xu, yu);
|
||||
s->touch_done = true;
|
||||
|
||||
pr_dbg("Down[%d] (%f, %f)\n", s->ind, s->touched.x, s->touched.y);
|
||||
}
|
||||
|
||||
static void
|
||||
coordinate_result_handler(void *data, struct weston_touch_coordinate *interface,
|
||||
uint32_t xu, uint32_t yu)
|
||||
{
|
||||
struct sample *s = data;
|
||||
|
||||
weston_touch_coordinate_destroy(s->pending);
|
||||
s->pending = NULL;
|
||||
|
||||
s->drawn_cal = wire_to_point(xu, yu);
|
||||
s->conv_done = true;
|
||||
|
||||
pr_dbg("Conv[%d] (%f, %f)\n", s->ind, s->drawn_cal.x, s->drawn_cal.y);
|
||||
}
|
||||
|
||||
struct weston_touch_coordinate_listener coordinate_listener = {
|
||||
coordinate_result_handler
|
||||
};
|
||||
|
||||
static void
|
||||
sample_undo(struct calibrator *cal)
|
||||
{
|
||||
struct sample *s = current_sample(cal);
|
||||
|
||||
pr_dbg("Undo[%d]\n", s->ind);
|
||||
|
||||
s->touch_done = false;
|
||||
s->conv_done = false;
|
||||
if (s->pending) {
|
||||
weston_touch_coordinate_destroy(s->pending);
|
||||
s->pending = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sample_finish(struct calibrator *cal)
|
||||
{
|
||||
struct sample *s = current_sample(cal);
|
||||
|
||||
pr_dbg("Finish[%d]\n", s->ind);
|
||||
|
||||
assert(!s->pending && !s->conv_done);
|
||||
|
||||
s->pending = weston_touch_calibrator_convert(cal->calibrator,
|
||||
(int32_t)s->drawn.x,
|
||||
(int32_t)s->drawn.y);
|
||||
weston_touch_coordinate_add_listener(s->pending,
|
||||
&coordinate_listener, s);
|
||||
|
||||
if (cal->current_sample + 1 < NR_SAMPLES) {
|
||||
sample_start(cal, cal->current_sample + 1);
|
||||
} else {
|
||||
pr_dbg("got all touches\n");
|
||||
cal->exiting = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Calibration algorithm:
|
||||
*
|
||||
* The equation we want to apply at event time where x' and y' are the
|
||||
* calibrated co-ordinates.
|
||||
*
|
||||
* x' = Ax + By + C
|
||||
* y' = Dx + Ey + F
|
||||
*
|
||||
* For example "zero calibration" would be A=1.0 B=0.0 C=0.0, D=0.0, E=1.0,
|
||||
* and F=0.0.
|
||||
*
|
||||
* With 6 unknowns we need 6 equations to find the constants:
|
||||
*
|
||||
* x1' = Ax1 + By1 + C
|
||||
* y1' = Dx1 + Ey1 + F
|
||||
* ...
|
||||
* x3' = Ax3 + By3 + C
|
||||
* y3' = Dx3 + Ey3 + F
|
||||
*
|
||||
* In matrix form:
|
||||
*
|
||||
* x1' x1 y1 1 A
|
||||
* x2' = x2 y2 1 x B
|
||||
* x3' x3 y3 1 C
|
||||
*
|
||||
* So making the matrix M we can find the constants with:
|
||||
*
|
||||
* A x1'
|
||||
* B = M^-1 x x2'
|
||||
* C x3'
|
||||
*
|
||||
* (and similarly for D, E and F)
|
||||
*
|
||||
* For the calibration the desired values x, y are the same values at which
|
||||
* we've drawn at.
|
||||
*
|
||||
*/
|
||||
static int
|
||||
compute_calibration(struct calibrator *cal, float *result)
|
||||
{
|
||||
struct weston_matrix m;
|
||||
struct weston_matrix inverse;
|
||||
struct weston_vector x_calib;
|
||||
struct weston_vector y_calib;
|
||||
int i;
|
||||
|
||||
assert(NR_SAMPLES >= 3);
|
||||
|
||||
/*
|
||||
* x1 y1 1 0
|
||||
* x2 y2 1 0
|
||||
* x3 y3 1 0
|
||||
* 0 0 0 1
|
||||
*/
|
||||
weston_matrix_init(&m);
|
||||
for (i = 0; i < 3; i++) {
|
||||
m.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;
|
||||
}
|
||||
|
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* Copyright © 2008 Kristian Høgsberg
|
||||
* Copyright © 2012 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include <cairo.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <wayland-client.h>
|
||||
#include "window.h"
|
||||
|
||||
struct transformed {
|
||||
struct display *display;
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
int width, height;
|
||||
int fullscreen;
|
||||
};
|
||||
|
||||
static void
|
||||
draw_stuff(cairo_t *cr, int width, int height)
|
||||
{
|
||||
cairo_matrix_t m;
|
||||
cairo_get_matrix (cr, &m);
|
||||
|
||||
cairo_translate(cr, width / 2, height / 2);
|
||||
cairo_scale(cr, width / 2, height / 2);
|
||||
|
||||
cairo_set_source_rgba(cr, 0, 0, 0.3, 1.0);
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 1.0);
|
||||
cairo_rectangle(cr, -1, -1, 2, 2);
|
||||
cairo_fill(cr);
|
||||
|
||||
cairo_set_source_rgb(cr, 1, 0, 0);
|
||||
cairo_move_to(cr, 0, 0);
|
||||
cairo_line_to(cr, 0, -1);
|
||||
|
||||
cairo_save(cr);
|
||||
cairo_set_matrix(cr, &m);
|
||||
cairo_set_line_width(cr, 2.0);
|
||||
cairo_stroke(cr);
|
||||
cairo_restore(cr);
|
||||
|
||||
cairo_set_source_rgb(cr, 0, 1, 0);
|
||||
cairo_move_to(cr, 0, 0);
|
||||
cairo_line_to(cr, 1, 0);
|
||||
|
||||
cairo_save(cr);
|
||||
cairo_set_matrix(cr, &m);
|
||||
cairo_set_line_width(cr, 2.0);
|
||||
cairo_stroke(cr);
|
||||
cairo_restore(cr);
|
||||
|
||||
cairo_set_source_rgb(cr, 1, 1, 1);
|
||||
cairo_move_to(cr, 0, 0);
|
||||
cairo_line_to(cr, 0, 1);
|
||||
cairo_move_to(cr, 0, 0);
|
||||
cairo_line_to(cr, -1, 0);
|
||||
|
||||
cairo_save(cr);
|
||||
cairo_set_matrix(cr, &m);
|
||||
cairo_set_line_width(cr, 2.0);
|
||||
cairo_stroke(cr);
|
||||
cairo_restore(cr);
|
||||
|
||||
cairo_destroy(cr);
|
||||
}
|
||||
|
||||
static void
|
||||
fullscreen_handler(struct window *window, void *data)
|
||||
{
|
||||
struct transformed *transformed = data;
|
||||
|
||||
transformed->fullscreen ^= 1;
|
||||
window_set_fullscreen(window, transformed->fullscreen);
|
||||
}
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct transformed *transformed = data;
|
||||
struct rectangle allocation;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
|
||||
surface = window_get_surface(transformed->window);
|
||||
if (surface == NULL ||
|
||||
cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "failed to create cairo egl surface\n");
|
||||
return;
|
||||
}
|
||||
|
||||
widget_get_allocation(transformed->widget, &allocation);
|
||||
|
||||
cr = widget_cairo_create(widget);
|
||||
draw_stuff(cr, allocation.width, allocation.height);
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
output_handler(struct window *window, struct output *output, int enter,
|
||||
void *data)
|
||||
{
|
||||
if (!enter)
|
||||
return;
|
||||
|
||||
window_set_buffer_transform(window, output_get_transform(output));
|
||||
window_set_buffer_scale(window, output_get_scale(output));
|
||||
window_schedule_redraw(window);
|
||||
}
|
||||
|
||||
static void
|
||||
key_handler(struct window *window, struct input *input, uint32_t time,
|
||||
uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
|
||||
void *data)
|
||||
{
|
||||
int transform, scale;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
|
||||
return;
|
||||
|
||||
transform = window_get_buffer_transform (window);
|
||||
scale = window_get_buffer_scale (window);
|
||||
switch (sym) {
|
||||
case XKB_KEY_Left:
|
||||
if (transform == 0)
|
||||
transform = 3;
|
||||
else if (transform == 4)
|
||||
transform = 7;
|
||||
else
|
||||
transform--;
|
||||
break;
|
||||
|
||||
case XKB_KEY_Right:
|
||||
if (transform == 3)
|
||||
transform = 0;
|
||||
else if (transform == 7)
|
||||
transform = 4;
|
||||
else
|
||||
transform++;
|
||||
break;
|
||||
|
||||
case XKB_KEY_space:
|
||||
if (transform >= 4)
|
||||
transform -= 4;
|
||||
else
|
||||
transform += 4;
|
||||
break;
|
||||
|
||||
case XKB_KEY_z:
|
||||
if (scale == 1)
|
||||
scale = 2;
|
||||
else
|
||||
scale = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
printf ("setting buffer transform to %d\n", transform);
|
||||
printf ("setting buffer scale to %d\n", scale);
|
||||
window_set_buffer_transform(window, transform);
|
||||
window_set_buffer_scale(window, scale);
|
||||
window_schedule_redraw(window);
|
||||
}
|
||||
|
||||
static void
|
||||
button_handler(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t button, enum wl_pointer_button_state state, void *data)
|
||||
{
|
||||
struct transformed *transformed = data;
|
||||
|
||||
switch (button) {
|
||||
case BTN_LEFT:
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
window_move(transformed->window, input,
|
||||
display_get_serial(transformed->display));
|
||||
break;
|
||||
case BTN_MIDDLE:
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
widget_schedule_redraw(widget);
|
||||
break;
|
||||
case BTN_RIGHT:
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
window_show_frame_menu(transformed->window, input, time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
touch_handler(struct widget *widget, struct input *input,
|
||||
uint32_t serial, uint32_t time, int32_t id,
|
||||
float x, float y, void *data)
|
||||
{
|
||||
struct transformed *transformed = data;
|
||||
window_move(transformed->window, input, display_get_serial(transformed->display));
|
||||
}
|
||||
|
||||
static void
|
||||
usage(int error_code)
|
||||
{
|
||||
fprintf(stderr, "Usage: transformed [OPTIONS]\n\n"
|
||||
" -w <width>\tSet window width to <width>\n"
|
||||
" -h <height>\tSet window height to <height>\n"
|
||||
" --help\tShow this help text\n\n");
|
||||
|
||||
fprintf(stderr, "This version has been fixed for "
|
||||
"https://gitlab.freedesktop.org/wayland/weston/issues/99 .\n");
|
||||
|
||||
exit(error_code);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct transformed transformed;
|
||||
struct display *d;
|
||||
int i;
|
||||
|
||||
transformed.width = 500;
|
||||
transformed.height = 250;
|
||||
transformed.fullscreen = 0;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-w") == 0) {
|
||||
if (++i >= argc)
|
||||
usage(EXIT_FAILURE);
|
||||
|
||||
transformed.width = atol(argv[i]);
|
||||
} else if (strcmp(argv[i], "-h") == 0) {
|
||||
if (++i >= argc)
|
||||
usage(EXIT_FAILURE);
|
||||
|
||||
transformed.height = atol(argv[i]);
|
||||
} else if (strcmp(argv[i], "--help") == 0)
|
||||
usage(EXIT_SUCCESS);
|
||||
else
|
||||
usage(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
d = display_create(&argc, argv);
|
||||
if (d == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
transformed.display = d;
|
||||
transformed.window = window_create(d);
|
||||
transformed.widget =
|
||||
window_add_widget(transformed.window, &transformed);
|
||||
|
||||
window_set_title(transformed.window, "Transformed");
|
||||
|
||||
widget_set_transparent(transformed.widget, 0);
|
||||
widget_set_default_cursor(transformed.widget, CURSOR_BLANK);
|
||||
|
||||
widget_set_redraw_handler(transformed.widget, redraw_handler);
|
||||
widget_set_button_handler(transformed.widget, button_handler);
|
||||
|
||||
widget_set_touch_down_handler(transformed.widget, touch_handler);
|
||||
|
||||
window_set_key_handler(transformed.window, key_handler);
|
||||
window_set_fullscreen_handler(transformed.window, fullscreen_handler);
|
||||
window_set_output_handler(transformed.window, output_handler);
|
||||
|
||||
window_set_user_data(transformed.window, &transformed);
|
||||
window_schedule_resize(transformed.window,
|
||||
transformed.width, transformed.height);
|
||||
|
||||
display_run(d);
|
||||
widget_destroy(transformed.widget);
|
||||
window_destroy(transformed.window);
|
||||
display_destroy(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,501 @@
|
|||
/*
|
||||
* Copyright © 2017 Pekka Paalanen <pq@iki.fi>
|
||||
* Copyright © 2018 Zodiac Inflight Innovations
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "shared/helpers.h"
|
||||
#include <libweston/zalloc.h>
|
||||
#include "weston-debug-client-protocol.h"
|
||||
|
||||
struct debug_app {
|
||||
struct {
|
||||
bool help;
|
||||
bool list;
|
||||
bool bind_all;
|
||||
char *output;
|
||||
char *outfd;
|
||||
} opt;
|
||||
|
||||
int out_fd;
|
||||
struct wl_display *dpy;
|
||||
struct wl_registry *registry;
|
||||
struct weston_debug_v1 *debug_iface;
|
||||
struct wl_list stream_list;
|
||||
};
|
||||
|
||||
struct debug_stream {
|
||||
struct wl_list link;
|
||||
bool should_bind;
|
||||
char *name;
|
||||
char *desc;
|
||||
struct weston_debug_stream_v1 *obj;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called either through stream_find in response to an advertisement
|
||||
* event (see comment on stream_find) when we have all the information,
|
||||
* or directly from option parsing to make a placeholder entry when the
|
||||
* stream was explicitly named on the command line to bind to.
|
||||
*/
|
||||
static struct debug_stream *
|
||||
stream_alloc(struct debug_app *app, const char *name, const char *desc)
|
||||
{
|
||||
struct debug_stream *stream;
|
||||
|
||||
stream = zalloc(sizeof *stream);
|
||||
if (!stream)
|
||||
return NULL;
|
||||
|
||||
stream->name = strdup(name);
|
||||
if (!stream->name) {
|
||||
free(stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (desc) {
|
||||
stream->desc = strdup(desc);
|
||||
if (!stream->desc) {
|
||||
free(stream->name);
|
||||
free(stream);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
stream->should_bind = app->opt.bind_all;
|
||||
wl_list_insert(app->stream_list.prev, &stream->link);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called in response to a stream advertisement event. If our stream was
|
||||
* manually specified on the command line, then it will already have a
|
||||
* dummy entry in stream_list: we fill in its description and return.
|
||||
* If there's no entry in the list, we make a new one and return that.
|
||||
*/
|
||||
static struct debug_stream *
|
||||
stream_find(struct debug_app *app, const char *name, const char *desc)
|
||||
{
|
||||
struct debug_stream *stream;
|
||||
|
||||
wl_list_for_each(stream, &app->stream_list, link) {
|
||||
if (strcmp(stream->name, name) == 0) {
|
||||
assert(stream->desc == NULL);
|
||||
if (desc)
|
||||
stream->desc = strdup(desc);
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
return stream_alloc(app, name, desc);
|
||||
}
|
||||
|
||||
static void
|
||||
stream_destroy(struct debug_stream *stream)
|
||||
{
|
||||
if (stream->obj)
|
||||
weston_debug_stream_v1_destroy(stream->obj);
|
||||
|
||||
wl_list_remove(&stream->link);
|
||||
free(stream->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;
|
||||
}
|
||||
|
|
@ -0,0 +1,744 @@
|
|||
/*
|
||||
* Copyright © 2008 Kristian Høgsberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _WINDOW_H_
|
||||
#define _WINDOW_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <wayland-client.h>
|
||||
#include <cairo.h>
|
||||
#include <libweston/config-parser.h>
|
||||
#include <libweston/zalloc.h>
|
||||
#include "shared/platform.h"
|
||||
|
||||
struct window;
|
||||
struct widget;
|
||||
struct display;
|
||||
struct input;
|
||||
struct output;
|
||||
|
||||
struct task {
|
||||
void (*run)(struct task *task, uint32_t events);
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct rectangle {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
};
|
||||
|
||||
struct display *
|
||||
display_create(int *argc, char *argv[]);
|
||||
|
||||
void
|
||||
display_destroy(struct display *display);
|
||||
|
||||
void
|
||||
display_set_user_data(struct display *display, void *data);
|
||||
|
||||
void *
|
||||
display_get_user_data(struct display *display);
|
||||
|
||||
struct wl_display *
|
||||
display_get_display(struct display *display);
|
||||
|
||||
int
|
||||
display_has_subcompositor(struct display *display);
|
||||
|
||||
cairo_device_t *
|
||||
display_get_cairo_device(struct display *display);
|
||||
|
||||
struct wl_compositor *
|
||||
display_get_compositor(struct display *display);
|
||||
|
||||
struct output *
|
||||
display_get_output(struct display *display);
|
||||
|
||||
uint32_t
|
||||
display_get_serial(struct display *display);
|
||||
|
||||
typedef void (*display_global_handler_t)(struct display *display,
|
||||
uint32_t name,
|
||||
const char *interface,
|
||||
uint32_t version, void *data);
|
||||
|
||||
void
|
||||
display_set_global_handler(struct display *display,
|
||||
display_global_handler_t handler);
|
||||
void
|
||||
display_set_global_handler_remove(struct display *display,
|
||||
display_global_handler_t remove_handler);
|
||||
void *
|
||||
display_bind(struct display *display, uint32_t name,
|
||||
const struct wl_interface *interface, uint32_t version);
|
||||
|
||||
typedef void (*display_output_handler_t)(struct output *output, void *data);
|
||||
|
||||
/*
|
||||
* The output configure handler is called, when a new output is connected
|
||||
* and we know its current mode, or when the current mode changes.
|
||||
* Test and set the output user data in your handler to know, if the
|
||||
* output is new. Note: 'data' in the configure handler is the display
|
||||
* user data.
|
||||
*/
|
||||
void
|
||||
display_set_output_configure_handler(struct display *display,
|
||||
display_output_handler_t handler);
|
||||
|
||||
struct wl_data_source *
|
||||
display_create_data_source(struct display *display);
|
||||
|
||||
#ifdef EGL_NO_DISPLAY
|
||||
EGLDisplay
|
||||
display_get_egl_display(struct display *d);
|
||||
|
||||
EGLConfig
|
||||
display_get_argb_egl_config(struct display *d);
|
||||
|
||||
int
|
||||
display_acquire_window_surface(struct display *display,
|
||||
struct window *window,
|
||||
EGLContext ctx);
|
||||
void
|
||||
display_release_window_surface(struct display *display,
|
||||
struct window *window);
|
||||
#endif
|
||||
|
||||
#define SURFACE_OPAQUE 0x01
|
||||
#define SURFACE_SHM 0x02
|
||||
|
||||
#define SURFACE_HINT_RESIZE 0x10
|
||||
|
||||
cairo_surface_t *
|
||||
display_create_surface(struct display *display,
|
||||
struct wl_surface *surface,
|
||||
struct rectangle *rectangle,
|
||||
uint32_t flags);
|
||||
|
||||
struct wl_buffer *
|
||||
display_get_buffer_for_surface(struct display *display,
|
||||
cairo_surface_t *surface);
|
||||
|
||||
struct wl_cursor_image *
|
||||
display_get_pointer_image(struct display *display, int pointer);
|
||||
|
||||
void
|
||||
display_defer(struct display *display, struct task *task);
|
||||
|
||||
void
|
||||
display_watch_fd(struct display *display,
|
||||
int fd, uint32_t events, struct task *task);
|
||||
|
||||
void
|
||||
display_unwatch_fd(struct display *display, int fd);
|
||||
|
||||
void
|
||||
display_run(struct display *d);
|
||||
|
||||
void
|
||||
display_exit(struct display *d);
|
||||
|
||||
int
|
||||
display_get_data_device_manager_version(struct display *d);
|
||||
|
||||
enum cursor_type {
|
||||
CURSOR_BOTTOM_LEFT,
|
||||
CURSOR_BOTTOM_RIGHT,
|
||||
CURSOR_BOTTOM,
|
||||
CURSOR_DRAGGING,
|
||||
CURSOR_LEFT_PTR,
|
||||
CURSOR_LEFT,
|
||||
CURSOR_RIGHT,
|
||||
CURSOR_TOP_LEFT,
|
||||
CURSOR_TOP_RIGHT,
|
||||
CURSOR_TOP,
|
||||
CURSOR_IBEAM,
|
||||
CURSOR_HAND1,
|
||||
CURSOR_WATCH,
|
||||
CURSOR_DND_MOVE,
|
||||
CURSOR_DND_COPY,
|
||||
CURSOR_DND_FORBIDDEN,
|
||||
|
||||
CURSOR_BLANK
|
||||
};
|
||||
|
||||
typedef void (*window_key_handler_t)(struct window *window, struct input *input,
|
||||
uint32_t time, uint32_t key, uint32_t unicode,
|
||||
enum wl_keyboard_key_state state, void *data);
|
||||
|
||||
typedef void (*window_keyboard_focus_handler_t)(struct window *window,
|
||||
struct input *device, void *data);
|
||||
|
||||
typedef void (*window_data_handler_t)(struct window *window,
|
||||
struct input *input,
|
||||
float x, float y,
|
||||
const char **types,
|
||||
void *data);
|
||||
|
||||
typedef void (*window_drop_handler_t)(struct window *window,
|
||||
struct input *input,
|
||||
int32_t x, int32_t y, void *data);
|
||||
|
||||
typedef void (*window_close_handler_t)(void *data);
|
||||
typedef void (*window_fullscreen_handler_t)(struct window *window, void *data);
|
||||
|
||||
typedef void (*window_output_handler_t)(struct window *window, struct output *output,
|
||||
int enter, void *data);
|
||||
typedef void (*window_state_changed_handler_t)(struct window *window,
|
||||
void *data);
|
||||
|
||||
|
||||
typedef void (*window_locked_pointer_motion_handler_t)(struct window *window,
|
||||
struct input *input,
|
||||
uint32_t time,
|
||||
float x, float y,
|
||||
void *data);
|
||||
|
||||
typedef void (*locked_pointer_locked_handler_t)(struct window *window,
|
||||
struct input *input,
|
||||
void *data);
|
||||
|
||||
typedef void (*locked_pointer_unlocked_handler_t)(struct window *window,
|
||||
struct input *input,
|
||||
void *data);
|
||||
|
||||
typedef void (*confined_pointer_confined_handler_t)(struct window *window,
|
||||
struct input *input,
|
||||
void *data);
|
||||
|
||||
typedef void (*confined_pointer_unconfined_handler_t)(struct window *window,
|
||||
struct input *input,
|
||||
void *data);
|
||||
|
||||
typedef void (*widget_resize_handler_t)(struct widget *widget,
|
||||
int32_t width, int32_t height,
|
||||
void *data);
|
||||
typedef void (*widget_redraw_handler_t)(struct widget *widget, void *data);
|
||||
|
||||
typedef int (*widget_enter_handler_t)(struct widget *widget,
|
||||
struct input *input,
|
||||
float x, float y, void *data);
|
||||
typedef void (*widget_leave_handler_t)(struct widget *widget,
|
||||
struct input *input, void *data);
|
||||
typedef int (*widget_motion_handler_t)(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
float x, float y, void *data);
|
||||
typedef void (*widget_button_handler_t)(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t button,
|
||||
enum wl_pointer_button_state state,
|
||||
void *data);
|
||||
typedef void (*widget_touch_down_handler_t)(struct widget *widget,
|
||||
struct input *input,
|
||||
uint32_t serial,
|
||||
uint32_t time,
|
||||
int32_t id,
|
||||
float x,
|
||||
float y,
|
||||
void *data);
|
||||
typedef void (*widget_touch_up_handler_t)(struct widget *widget,
|
||||
struct input *input,
|
||||
uint32_t serial,
|
||||
uint32_t time,
|
||||
int32_t id,
|
||||
void *data);
|
||||
typedef void (*widget_touch_motion_handler_t)(struct widget *widget,
|
||||
struct input *input,
|
||||
uint32_t time,
|
||||
int32_t id,
|
||||
float x,
|
||||
float y,
|
||||
void *data);
|
||||
typedef void (*widget_touch_frame_handler_t)(struct widget *widget,
|
||||
struct input *input, void *data);
|
||||
typedef void (*widget_touch_cancel_handler_t)(struct widget *widget,
|
||||
struct input *input, void *data);
|
||||
typedef void (*widget_axis_handler_t)(struct widget *widget,
|
||||
struct input *input, uint32_t time,
|
||||
uint32_t axis,
|
||||
wl_fixed_t value,
|
||||
void *data);
|
||||
|
||||
typedef void (*widget_pointer_frame_handler_t)(struct widget *widget,
|
||||
struct input *input,
|
||||
void *data);
|
||||
|
||||
typedef void (*widget_axis_source_handler_t)(struct widget *widget,
|
||||
struct input *input,
|
||||
uint32_t source,
|
||||
void *data);
|
||||
|
||||
typedef void (*widget_axis_stop_handler_t)(struct widget *widget,
|
||||
struct input *input,
|
||||
uint32_t time,
|
||||
uint32_t axis,
|
||||
void *data);
|
||||
|
||||
typedef void (*widget_axis_discrete_handler_t)(struct widget *widget,
|
||||
struct input *input,
|
||||
uint32_t axis,
|
||||
int32_t discrete,
|
||||
void *data);
|
||||
|
||||
struct window *
|
||||
window_create(struct display *display);
|
||||
struct window *
|
||||
window_create_custom(struct display *display);
|
||||
|
||||
void
|
||||
window_set_parent(struct window *window, struct window *parent_window);
|
||||
struct window *
|
||||
window_get_parent(struct window *window);
|
||||
|
||||
int
|
||||
window_has_focus(struct window *window);
|
||||
|
||||
typedef void (*menu_func_t)(void *data, struct input *input, int index);
|
||||
|
||||
void
|
||||
window_show_menu(struct display *display,
|
||||
struct input *input, uint32_t time, struct window *parent,
|
||||
int32_t x, int32_t y,
|
||||
menu_func_t func, const char **entries, int count);
|
||||
|
||||
void
|
||||
window_show_frame_menu(struct window *window,
|
||||
struct input *input, uint32_t time);
|
||||
|
||||
int
|
||||
window_get_buffer_transform(struct window *window);
|
||||
|
||||
void
|
||||
window_set_buffer_transform(struct window *window,
|
||||
enum wl_output_transform transform);
|
||||
|
||||
uint32_t
|
||||
window_get_buffer_scale(struct window *window);
|
||||
|
||||
void
|
||||
window_set_buffer_scale(struct window *window,
|
||||
int32_t scale);
|
||||
|
||||
uint32_t
|
||||
window_get_output_scale(struct window *window);
|
||||
|
||||
void
|
||||
window_destroy(struct window *window);
|
||||
|
||||
struct widget *
|
||||
window_add_widget(struct window *window, void *data);
|
||||
|
||||
enum subsurface_mode {
|
||||
SUBSURFACE_SYNCHRONIZED,
|
||||
SUBSURFACE_DESYNCHRONIZED
|
||||
};
|
||||
|
||||
struct widget *
|
||||
window_add_subsurface(struct window *window, void *data,
|
||||
enum subsurface_mode default_mode);
|
||||
|
||||
typedef void (*data_func_t)(void *data, size_t len,
|
||||
int32_t x, int32_t y, void *user_data);
|
||||
|
||||
struct display *
|
||||
window_get_display(struct window *window);
|
||||
void
|
||||
window_move(struct window *window, struct input *input, uint32_t time);
|
||||
void
|
||||
window_get_allocation(struct window *window, struct rectangle *allocation);
|
||||
void
|
||||
window_schedule_redraw(struct window *window);
|
||||
void
|
||||
window_schedule_resize(struct window *window, int width, int height);
|
||||
|
||||
int
|
||||
window_lock_pointer(struct window *window, struct input *input);
|
||||
|
||||
void
|
||||
window_unlock_pointer(struct window *window);
|
||||
|
||||
void
|
||||
widget_set_locked_pointer_cursor_hint(struct widget *widget,
|
||||
float x, float y);
|
||||
|
||||
int
|
||||
window_confine_pointer_to_rectangles(struct window *window,
|
||||
struct input *input,
|
||||
struct rectangle *rectangles,
|
||||
int num_rectangles);
|
||||
|
||||
void
|
||||
window_update_confine_rectangles(struct window *window,
|
||||
struct rectangle *rectangles,
|
||||
int num_rectangles);
|
||||
|
||||
int
|
||||
window_confine_pointer_to_widget(struct window *window,
|
||||
struct widget *widget,
|
||||
struct input *input);
|
||||
|
||||
void
|
||||
window_unconfine_pointer(struct window *window);
|
||||
|
||||
cairo_surface_t *
|
||||
window_get_surface(struct window *window);
|
||||
|
||||
struct wl_surface *
|
||||
window_get_wl_surface(struct window *window);
|
||||
|
||||
struct wl_subsurface *
|
||||
widget_get_wl_subsurface(struct widget *widget);
|
||||
|
||||
enum window_buffer_type {
|
||||
WINDOW_BUFFER_TYPE_EGL_WINDOW,
|
||||
WINDOW_BUFFER_TYPE_SHM,
|
||||
};
|
||||
|
||||
void
|
||||
display_surface_damage(struct display *display, cairo_surface_t *cairo_surface,
|
||||
int32_t x, int32_t y, int32_t width, int32_t height);
|
||||
|
||||
void
|
||||
window_set_buffer_type(struct window *window, enum window_buffer_type type);
|
||||
|
||||
enum window_buffer_type
|
||||
window_get_buffer_type(struct window *window);
|
||||
|
||||
int
|
||||
window_is_fullscreen(struct window *window);
|
||||
|
||||
void
|
||||
window_set_fullscreen(struct window *window, int fullscreen);
|
||||
|
||||
int
|
||||
window_is_maximized(struct window *window);
|
||||
|
||||
void
|
||||
window_set_maximized(struct window *window, int maximized);
|
||||
|
||||
int
|
||||
window_is_resizing(struct window *window);
|
||||
|
||||
void
|
||||
window_set_minimized(struct window *window);
|
||||
|
||||
void
|
||||
window_set_user_data(struct window *window, void *data);
|
||||
|
||||
void *
|
||||
window_get_user_data(struct window *window);
|
||||
|
||||
void
|
||||
window_set_key_handler(struct window *window,
|
||||
window_key_handler_t handler);
|
||||
|
||||
void
|
||||
window_set_keyboard_focus_handler(struct window *window,
|
||||
window_keyboard_focus_handler_t handler);
|
||||
|
||||
void
|
||||
window_set_data_handler(struct window *window,
|
||||
window_data_handler_t handler);
|
||||
|
||||
void
|
||||
window_set_drop_handler(struct window *window,
|
||||
window_drop_handler_t handler);
|
||||
|
||||
void
|
||||
window_set_close_handler(struct window *window,
|
||||
window_close_handler_t handler);
|
||||
void
|
||||
window_set_fullscreen_handler(struct window *window,
|
||||
window_fullscreen_handler_t handler);
|
||||
void
|
||||
window_set_output_handler(struct window *window,
|
||||
window_output_handler_t handler);
|
||||
void
|
||||
window_set_state_changed_handler(struct window *window,
|
||||
window_state_changed_handler_t handler);
|
||||
|
||||
void
|
||||
window_set_pointer_locked_handler(struct window *window,
|
||||
locked_pointer_locked_handler_t locked,
|
||||
locked_pointer_unlocked_handler_t unlocked);
|
||||
|
||||
void
|
||||
window_set_pointer_confined_handler(struct window *window,
|
||||
confined_pointer_confined_handler_t confined,
|
||||
confined_pointer_unconfined_handler_t unconfined);
|
||||
|
||||
void
|
||||
window_set_locked_pointer_motion_handler(
|
||||
struct window *window, window_locked_pointer_motion_handler_t handler);
|
||||
|
||||
void
|
||||
window_set_title(struct window *window, const char *title);
|
||||
|
||||
const char *
|
||||
window_get_title(struct window *window);
|
||||
|
||||
void
|
||||
window_set_text_cursor_position(struct window *window, int32_t x, int32_t y);
|
||||
|
||||
int
|
||||
widget_set_tooltip(struct widget *parent, char *entry, float x, float y);
|
||||
|
||||
void
|
||||
widget_destroy_tooltip(struct widget *parent);
|
||||
|
||||
struct widget *
|
||||
widget_add_widget(struct widget *parent, void *data);
|
||||
|
||||
void
|
||||
widget_destroy(struct widget *widget);
|
||||
void
|
||||
widget_set_default_cursor(struct widget *widget, int cursor);
|
||||
void
|
||||
widget_get_allocation(struct widget *widget, struct rectangle *allocation);
|
||||
|
||||
void
|
||||
widget_set_allocation(struct widget *widget,
|
||||
int32_t x, int32_t y, int32_t width, int32_t height);
|
||||
void
|
||||
widget_set_size(struct widget *widget, int32_t width, int32_t height);
|
||||
void
|
||||
widget_set_transparent(struct widget *widget, int transparent);
|
||||
void
|
||||
widget_schedule_resize(struct widget *widget, int32_t width, int32_t height);
|
||||
|
||||
void *
|
||||
widget_get_user_data(struct widget *widget);
|
||||
|
||||
cairo_t *
|
||||
widget_cairo_create(struct widget *widget);
|
||||
|
||||
struct wl_surface *
|
||||
widget_get_wl_surface(struct widget *widget);
|
||||
|
||||
uint32_t
|
||||
widget_get_last_time(struct widget *widget);
|
||||
|
||||
void
|
||||
widget_input_region_add(struct widget *widget, const struct rectangle *rect);
|
||||
|
||||
void
|
||||
widget_set_redraw_handler(struct widget *widget,
|
||||
widget_redraw_handler_t handler);
|
||||
void
|
||||
widget_set_resize_handler(struct widget *widget,
|
||||
widget_resize_handler_t handler);
|
||||
void
|
||||
widget_set_enter_handler(struct widget *widget,
|
||||
widget_enter_handler_t handler);
|
||||
void
|
||||
widget_set_leave_handler(struct widget *widget,
|
||||
widget_leave_handler_t handler);
|
||||
void
|
||||
widget_set_motion_handler(struct widget *widget,
|
||||
widget_motion_handler_t handler);
|
||||
void
|
||||
widget_set_button_handler(struct widget *widget,
|
||||
widget_button_handler_t handler);
|
||||
void
|
||||
widget_set_touch_down_handler(struct widget *widget,
|
||||
widget_touch_down_handler_t handler);
|
||||
void
|
||||
widget_set_touch_up_handler(struct widget *widget,
|
||||
widget_touch_up_handler_t handler);
|
||||
void
|
||||
widget_set_touch_motion_handler(struct widget *widget,
|
||||
widget_touch_motion_handler_t handler);
|
||||
void
|
||||
widget_set_touch_frame_handler(struct widget *widget,
|
||||
widget_touch_frame_handler_t handler);
|
||||
void
|
||||
widget_set_touch_cancel_handler(struct widget *widget,
|
||||
widget_touch_cancel_handler_t handler);
|
||||
void
|
||||
widget_set_axis_handler(struct widget *widget,
|
||||
widget_axis_handler_t handler);
|
||||
void
|
||||
widget_set_pointer_frame_handler(struct widget *widget,
|
||||
widget_pointer_frame_handler_t handler);
|
||||
void
|
||||
widget_set_axis_handlers(struct widget *widget,
|
||||
widget_axis_handler_t axis_handler,
|
||||
widget_axis_source_handler_t axis_source_handler,
|
||||
widget_axis_stop_handler_t axis_stop_handler,
|
||||
widget_axis_discrete_handler_t axis_discrete_handler);
|
||||
|
||||
void
|
||||
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);
|
||||
|
||||
void
|
||||
window_frame_set_child_size(struct widget *widget, int child_width,
|
||||
int child_height);
|
||||
|
||||
void
|
||||
input_set_pointer_image(struct input *input, int pointer);
|
||||
|
||||
void
|
||||
input_get_position(struct input *input, int32_t *x, int32_t *y);
|
||||
|
||||
int
|
||||
input_get_touch(struct input *input, int32_t id, float *x, float *y);
|
||||
|
||||
#define MOD_SHIFT_MASK 0x01
|
||||
#define MOD_ALT_MASK 0x02
|
||||
#define MOD_CONTROL_MASK 0x04
|
||||
|
||||
uint32_t
|
||||
input_get_modifiers(struct input *input);
|
||||
|
||||
void
|
||||
touch_grab(struct input *input, int32_t touch_id);
|
||||
|
||||
void
|
||||
touch_ungrab(struct input *input);
|
||||
|
||||
void
|
||||
input_grab(struct input *input, struct widget *widget, uint32_t button);
|
||||
|
||||
void
|
||||
input_ungrab(struct input *input);
|
||||
|
||||
struct widget *
|
||||
input_get_focus_widget(struct input *input);
|
||||
|
||||
struct display *
|
||||
input_get_display(struct input *input);
|
||||
|
||||
struct wl_seat *
|
||||
input_get_seat(struct input *input);
|
||||
|
||||
struct wl_data_device *
|
||||
input_get_data_device(struct input *input);
|
||||
|
||||
void
|
||||
input_set_selection(struct input *input,
|
||||
struct wl_data_source *source, uint32_t time);
|
||||
|
||||
void
|
||||
input_accept(struct input *input, const char *type);
|
||||
|
||||
|
||||
void
|
||||
input_receive_drag_data(struct input *input, const char *mime_type,
|
||||
data_func_t func, void *user_data);
|
||||
int
|
||||
input_receive_drag_data_to_fd(struct input *input,
|
||||
const char *mime_type, int fd);
|
||||
|
||||
int
|
||||
input_receive_selection_data(struct input *input, const char *mime_type,
|
||||
data_func_t func, void *data);
|
||||
int
|
||||
input_receive_selection_data_to_fd(struct input *input,
|
||||
const char *mime_type, int fd);
|
||||
|
||||
void
|
||||
output_set_user_data(struct output *output, void *data);
|
||||
|
||||
void *
|
||||
output_get_user_data(struct output *output);
|
||||
|
||||
void
|
||||
output_set_destroy_handler(struct output *output,
|
||||
display_output_handler_t handler);
|
||||
|
||||
void
|
||||
output_get_allocation(struct output *output, struct rectangle *allocation);
|
||||
|
||||
struct wl_output *
|
||||
output_get_wl_output(struct output *output);
|
||||
|
||||
enum wl_output_transform
|
||||
output_get_transform(struct output *output);
|
||||
|
||||
uint32_t
|
||||
output_get_scale(struct output *output);
|
||||
|
||||
const char *
|
||||
output_get_make(struct output *output);
|
||||
|
||||
const char *
|
||||
output_get_model(struct output *output);
|
||||
|
||||
void
|
||||
keysym_modifiers_add(struct wl_array *modifiers_map,
|
||||
const char *name);
|
||||
|
||||
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
|
||||
|
|
@ -0,0 +1,588 @@
|
|||
/*
|
||||
* Copyright © 2013 Richard Hughes
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <colord.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include "weston.h"
|
||||
#include "cms-helper.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
struct cms_colord {
|
||||
struct weston_compositor *ec;
|
||||
CdClient *client;
|
||||
GHashTable *devices; /* key = device-id, value = cms_output */
|
||||
GHashTable *pnp_ids; /* key = pnp-id, value = vendor */
|
||||
gchar *pnp_ids_data;
|
||||
GMainLoop *loop;
|
||||
GThread *thread;
|
||||
GList *pending;
|
||||
GMutex pending_mutex;
|
||||
struct wl_event_source *source;
|
||||
int readfd;
|
||||
int writefd;
|
||||
struct wl_listener destroy_listener;
|
||||
struct wl_listener output_created_listener;
|
||||
};
|
||||
|
||||
struct cms_output {
|
||||
CdDevice *device;
|
||||
guint32 backlight_value;
|
||||
struct cms_colord *cms;
|
||||
struct weston_color_profile *p;
|
||||
struct weston_output *o;
|
||||
struct wl_listener destroy_listener;
|
||||
};
|
||||
|
||||
static gint
|
||||
colord_idle_find_output_cb(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
struct cms_output *ocms = (struct cms_output *) a;
|
||||
struct weston_output *o = (struct weston_output *) b;
|
||||
return ocms->o == o ? 0 : -1;
|
||||
}
|
||||
|
||||
static void
|
||||
colord_idle_cancel_for_output(struct cms_colord *cms, struct weston_output *o)
|
||||
{
|
||||
GList *l;
|
||||
|
||||
/* cancel and remove any helpers that match the output */
|
||||
g_mutex_lock(&cms->pending_mutex);
|
||||
l = g_list_find_custom (cms->pending, o, colord_idle_find_output_cb);
|
||||
if (l) {
|
||||
struct cms_output *ocms = l->data;
|
||||
cms->pending = g_list_remove (cms->pending, ocms);
|
||||
}
|
||||
g_mutex_unlock(&cms->pending_mutex);
|
||||
}
|
||||
|
||||
static bool
|
||||
edid_value_valid(const char *str)
|
||||
{
|
||||
if (str == NULL)
|
||||
return false;
|
||||
if (str[0] == '\0')
|
||||
return false;
|
||||
if (strcmp(str, "unknown") == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
get_output_id(struct cms_colord *cms, struct weston_output *o)
|
||||
{
|
||||
struct weston_head *head;
|
||||
const gchar *tmp;
|
||||
GString *device_id;
|
||||
|
||||
/* XXX: What to do with multiple heads?
|
||||
* This is potentially unstable, if head configuration is changed
|
||||
* while the output is enabled. */
|
||||
head = weston_output_get_first_head(o);
|
||||
|
||||
if (wl_list_length(&o->head_list) > 1) {
|
||||
weston_log("colord: WARNING: multiple heads are not supported (output %s).\n",
|
||||
o->name);
|
||||
}
|
||||
|
||||
/* see https://github.com/hughsie/colord/blob/master/doc/device-and-profile-naming-spec.txt
|
||||
* for format and allowed values */
|
||||
device_id = g_string_new("xrandr");
|
||||
if (edid_value_valid(head->make)) {
|
||||
tmp = g_hash_table_lookup(cms->pnp_ids, head->make);
|
||||
if (tmp == NULL)
|
||||
tmp = head->make;
|
||||
g_string_append_printf(device_id, "-%s", tmp);
|
||||
}
|
||||
if (edid_value_valid(head->model))
|
||||
g_string_append_printf(device_id, "-%s", head->model);
|
||||
if (edid_value_valid(head->serial_number))
|
||||
g_string_append_printf(device_id, "-%s", head->serial_number);
|
||||
|
||||
/* no EDID data, so use fallback */
|
||||
if (strcmp(device_id->str, "xrandr") == 0)
|
||||
g_string_append_printf(device_id, "-drm-%i", o->id);
|
||||
|
||||
return g_string_free(device_id, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
update_device_with_profile_in_idle(struct cms_output *ocms)
|
||||
{
|
||||
gboolean signal_write = FALSE;
|
||||
ssize_t rc;
|
||||
struct cms_colord *cms = ocms->cms;
|
||||
|
||||
colord_idle_cancel_for_output(cms, ocms->o);
|
||||
g_mutex_lock(&cms->pending_mutex);
|
||||
if (cms->pending == NULL)
|
||||
signal_write = TRUE;
|
||||
cms->pending = g_list_prepend(cms->pending, ocms);
|
||||
g_mutex_unlock(&cms->pending_mutex);
|
||||
|
||||
/* signal we've got updates to do */
|
||||
if (signal_write) {
|
||||
gchar tmp = '\0';
|
||||
rc = write(cms->writefd, &tmp, 1);
|
||||
if (rc == 0)
|
||||
weston_log("colord: failed to write to pending fd\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
colord_update_output_from_device (struct cms_output *ocms)
|
||||
{
|
||||
CdProfile *profile;
|
||||
const gchar *tmp;
|
||||
gboolean ret;
|
||||
GError *error = NULL;
|
||||
gint percentage;
|
||||
|
||||
/* old profile is no longer valid */
|
||||
weston_cms_destroy_profile(ocms->p);
|
||||
ocms->p = NULL;
|
||||
|
||||
ret = cd_device_connect_sync(ocms->device, NULL, &error);
|
||||
if (!ret) {
|
||||
weston_log("colord: failed to connect to device %s: %s\n",
|
||||
cd_device_get_object_path (ocms->device),
|
||||
error->message);
|
||||
g_error_free(error);
|
||||
goto out;
|
||||
}
|
||||
profile = cd_device_get_default_profile(ocms->device);
|
||||
if (!profile) {
|
||||
weston_log("colord: no assigned color profile for %s\n",
|
||||
cd_device_get_id (ocms->device));
|
||||
goto out;
|
||||
}
|
||||
ret = cd_profile_connect_sync(profile, NULL, &error);
|
||||
if (!ret) {
|
||||
weston_log("colord: failed to connect to profile %s: %s\n",
|
||||
cd_profile_get_object_path (profile),
|
||||
error->message);
|
||||
g_error_free(error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* get the calibration brightness level (only set for some profiles) */
|
||||
tmp = cd_profile_get_metadata_item(profile, CD_PROFILE_METADATA_SCREEN_BRIGHTNESS);
|
||||
if (tmp != NULL) {
|
||||
percentage = atoi(tmp);
|
||||
if (percentage > 0 && percentage <= 100)
|
||||
ocms->backlight_value = percentage * 255 / 100;
|
||||
}
|
||||
|
||||
ocms->p = weston_cms_load_profile(cd_profile_get_filename(profile));
|
||||
if (ocms->p == NULL) {
|
||||
weston_log("colord: warning failed to load profile %s: %s\n",
|
||||
cd_profile_get_object_path (profile),
|
||||
error->message);
|
||||
g_error_free(error);
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
update_device_with_profile_in_idle(ocms);
|
||||
}
|
||||
|
||||
static void
|
||||
colord_device_changed_cb(CdDevice *device, struct cms_output *ocms)
|
||||
{
|
||||
weston_log("colord: device %s changed, update output\n",
|
||||
cd_device_get_object_path (ocms->device));
|
||||
colord_update_output_from_device(ocms);
|
||||
}
|
||||
|
||||
static void
|
||||
colord_notifier_output_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct cms_output *ocms =
|
||||
container_of(listener, struct cms_output, destroy_listener);
|
||||
struct weston_output *o = (struct weston_output *) data;
|
||||
struct cms_colord *cms = ocms->cms;
|
||||
gchar *device_id;
|
||||
|
||||
device_id = get_output_id(cms, o);
|
||||
g_hash_table_remove (cms->devices, device_id);
|
||||
g_free (device_id);
|
||||
}
|
||||
|
||||
static void
|
||||
colord_output_created(struct cms_colord *cms, struct weston_output *o)
|
||||
{
|
||||
struct weston_head *head;
|
||||
CdDevice *device;
|
||||
const gchar *tmp;
|
||||
gchar *device_id;
|
||||
GError *error = NULL;
|
||||
GHashTable *device_props;
|
||||
struct cms_output *ocms;
|
||||
|
||||
/* XXX: What to do with multiple heads? */
|
||||
head = weston_output_get_first_head(o);
|
||||
|
||||
/* create device */
|
||||
device_id = get_output_id(cms, o);
|
||||
weston_log("colord: output added %s\n", device_id);
|
||||
device_props = g_hash_table_new_full(g_str_hash, g_str_equal,
|
||||
g_free, g_free);
|
||||
g_hash_table_insert (device_props,
|
||||
g_strdup(CD_DEVICE_PROPERTY_KIND),
|
||||
g_strdup(cd_device_kind_to_string (CD_DEVICE_KIND_DISPLAY)));
|
||||
g_hash_table_insert (device_props,
|
||||
g_strdup(CD_DEVICE_PROPERTY_FORMAT),
|
||||
g_strdup("ColorModel.OutputMode.OutputResolution"));
|
||||
g_hash_table_insert (device_props,
|
||||
g_strdup(CD_DEVICE_PROPERTY_COLORSPACE),
|
||||
g_strdup(cd_colorspace_to_string(CD_COLORSPACE_RGB)));
|
||||
if (edid_value_valid(head->make)) {
|
||||
tmp = g_hash_table_lookup(cms->pnp_ids, head->make);
|
||||
if (tmp == NULL)
|
||||
tmp = head->make;
|
||||
g_hash_table_insert (device_props,
|
||||
g_strdup(CD_DEVICE_PROPERTY_VENDOR),
|
||||
g_strdup(tmp));
|
||||
}
|
||||
if (edid_value_valid(head->model)) {
|
||||
g_hash_table_insert (device_props,
|
||||
g_strdup(CD_DEVICE_PROPERTY_MODEL),
|
||||
g_strdup(head->model));
|
||||
}
|
||||
if (edid_value_valid(head->serial_number)) {
|
||||
g_hash_table_insert (device_props,
|
||||
g_strdup(CD_DEVICE_PROPERTY_SERIAL),
|
||||
g_strdup(head->serial_number));
|
||||
}
|
||||
if (head->connection_internal) {
|
||||
g_hash_table_insert (device_props,
|
||||
g_strdup (CD_DEVICE_PROPERTY_EMBEDDED),
|
||||
NULL);
|
||||
}
|
||||
device = cd_client_create_device_sync(cms->client,
|
||||
device_id,
|
||||
CD_OBJECT_SCOPE_TEMP,
|
||||
device_props,
|
||||
NULL,
|
||||
&error);
|
||||
if (g_error_matches (error,
|
||||
CD_CLIENT_ERROR,
|
||||
CD_CLIENT_ERROR_ALREADY_EXISTS)) {
|
||||
g_clear_error(&error);
|
||||
device = cd_client_find_device_sync (cms->client,
|
||||
device_id,
|
||||
NULL,
|
||||
&error);
|
||||
}
|
||||
if (!device) {
|
||||
weston_log("colord: failed to create new or "
|
||||
"find existing device: %s\n",
|
||||
error->message);
|
||||
g_error_free(error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* create object and watch for the output to be destroyed */
|
||||
ocms = g_slice_new0(struct cms_output);
|
||||
ocms->cms = cms;
|
||||
ocms->o = o;
|
||||
ocms->device = g_object_ref(device);
|
||||
ocms->destroy_listener.notify = colord_notifier_output_destroy;
|
||||
wl_signal_add(&o->destroy_signal, &ocms->destroy_listener);
|
||||
|
||||
/* add to local cache */
|
||||
g_hash_table_insert (cms->devices, g_strdup(device_id), ocms);
|
||||
g_signal_connect (ocms->device, "changed",
|
||||
G_CALLBACK (colord_device_changed_cb), ocms);
|
||||
|
||||
/* get profiles */
|
||||
colord_update_output_from_device (ocms);
|
||||
out:
|
||||
g_hash_table_unref (device_props);
|
||||
if (device)
|
||||
g_object_unref (device);
|
||||
g_free (device_id);
|
||||
}
|
||||
|
||||
static void
|
||||
colord_notifier_output_created(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct weston_output *o = (struct weston_output *) data;
|
||||
struct cms_colord *cms =
|
||||
container_of(listener, struct cms_colord, destroy_listener);
|
||||
weston_log("colord: output %s created\n", o->name);
|
||||
colord_output_created(cms, o);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
colord_run_loop_thread(gpointer data)
|
||||
{
|
||||
struct cms_colord *cms = (struct cms_colord *) data;
|
||||
struct weston_output *o;
|
||||
|
||||
/* coldplug outputs */
|
||||
wl_list_for_each(o, &cms->ec->output_list, link) {
|
||||
weston_log("colord: output %s coldplugged\n", o->name);
|
||||
colord_output_created(cms, o);
|
||||
}
|
||||
|
||||
g_main_loop_run(cms->loop);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
colord_dispatch_all_pending(int fd, uint32_t mask, void *data)
|
||||
{
|
||||
gchar tmp;
|
||||
GList *l;
|
||||
ssize_t rc;
|
||||
struct cms_colord *cms = data;
|
||||
struct cms_output *ocms;
|
||||
|
||||
weston_log("colord: dispatching events\n");
|
||||
g_mutex_lock(&cms->pending_mutex);
|
||||
for (l = cms->pending; l != NULL; l = l->next) {
|
||||
ocms = l->data;
|
||||
|
||||
/* optionally set backlight to calibration value */
|
||||
if (ocms->o->set_backlight && ocms->backlight_value != 0) {
|
||||
weston_log("colord: profile calibration backlight to %i/255\n",
|
||||
ocms->backlight_value);
|
||||
ocms->o->set_backlight(ocms->o, ocms->backlight_value);
|
||||
}
|
||||
|
||||
weston_cms_set_color_profile(ocms->o, ocms->p);
|
||||
}
|
||||
g_list_free (cms->pending);
|
||||
cms->pending = NULL;
|
||||
g_mutex_unlock(&cms->pending_mutex);
|
||||
|
||||
/* done */
|
||||
rc = read(cms->readfd, &tmp, 1);
|
||||
if (rc == 0)
|
||||
weston_log("colord: failed to read from pending fd\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
colord_load_pnp_ids(struct cms_colord *cms)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gchar *tmp;
|
||||
GError *error = NULL;
|
||||
guint i;
|
||||
const gchar *pnp_ids_fn[] = { "/usr/share/hwdata/pnp.ids",
|
||||
"/usr/share/misc/pnp.ids",
|
||||
NULL };
|
||||
|
||||
/* find and load file */
|
||||
for (i = 0; pnp_ids_fn[i] != NULL; i++) {
|
||||
if (!g_file_test(pnp_ids_fn[i], G_FILE_TEST_EXISTS))
|
||||
continue;
|
||||
ret = g_file_get_contents(pnp_ids_fn[i],
|
||||
&cms->pnp_ids_data,
|
||||
NULL,
|
||||
&error);
|
||||
if (!ret) {
|
||||
weston_log("colord: failed to load %s: %s\n",
|
||||
pnp_ids_fn[i], error->message);
|
||||
g_error_free(error);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!ret) {
|
||||
weston_log("colord: no pnp.ids found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse fixed offsets into lines */
|
||||
tmp = cms->pnp_ids_data;
|
||||
for (i = 0; cms->pnp_ids_data[i] != '\0'; i++) {
|
||||
if (cms->pnp_ids_data[i] != '\n')
|
||||
continue;
|
||||
cms->pnp_ids_data[i] = '\0';
|
||||
if (tmp[0] && tmp[1] && tmp[2] && tmp[3] == '\t' && tmp[4]) {
|
||||
tmp[3] = '\0';
|
||||
g_hash_table_insert(cms->pnp_ids, tmp, tmp+4);
|
||||
tmp = &cms->pnp_ids_data[i+1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
colord_module_destroy(struct cms_colord *cms)
|
||||
{
|
||||
if (cms->loop) {
|
||||
g_main_loop_quit(cms->loop);
|
||||
g_main_loop_unref(cms->loop);
|
||||
}
|
||||
if (cms->thread)
|
||||
g_thread_join(cms->thread);
|
||||
|
||||
/* cms->devices must be destroyed before other resources, as
|
||||
* the other resources are needed during output cleanup in
|
||||
* cms->devices unref.
|
||||
*/
|
||||
if (cms->devices)
|
||||
g_hash_table_unref(cms->devices);
|
||||
if (cms->client)
|
||||
g_object_unref(cms->client);
|
||||
if (cms->readfd)
|
||||
close(cms->readfd);
|
||||
if (cms->writefd)
|
||||
close(cms->writefd);
|
||||
|
||||
g_free(cms->pnp_ids_data);
|
||||
g_hash_table_unref(cms->pnp_ids);
|
||||
|
||||
wl_list_remove(&cms->destroy_listener.link);
|
||||
free(cms);
|
||||
}
|
||||
|
||||
static void
|
||||
colord_notifier_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct cms_colord *cms =
|
||||
container_of(listener, struct cms_colord, destroy_listener);
|
||||
colord_module_destroy(cms);
|
||||
}
|
||||
|
||||
static void
|
||||
colord_cms_output_destroy(gpointer data)
|
||||
{
|
||||
struct cms_output *ocms = (struct cms_output *) data;
|
||||
struct cms_colord *cms = ocms->cms;
|
||||
struct weston_output *o = ocms->o;
|
||||
gboolean ret;
|
||||
gchar *device_id;
|
||||
GError *error = NULL;
|
||||
|
||||
colord_idle_cancel_for_output(cms, o);
|
||||
device_id = get_output_id(cms, o);
|
||||
weston_log("colord: output unplugged %s\n", device_id);
|
||||
|
||||
wl_list_remove(&ocms->destroy_listener.link);
|
||||
g_signal_handlers_disconnect_by_data(ocms->device, ocms);
|
||||
|
||||
ret = cd_client_delete_device_sync (cms->client,
|
||||
ocms->device,
|
||||
NULL,
|
||||
&error);
|
||||
|
||||
if (!ret) {
|
||||
weston_log("colord: failed to delete device: %s\n",
|
||||
error->message);
|
||||
g_error_free(error);
|
||||
}
|
||||
|
||||
g_object_unref(ocms->device);
|
||||
g_slice_free(struct cms_output, ocms);
|
||||
g_free (device_id);
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
wet_module_init(struct weston_compositor *ec,
|
||||
int *argc, char *argv[])
|
||||
{
|
||||
gboolean ret;
|
||||
GError *error = NULL;
|
||||
int fd[2];
|
||||
struct cms_colord *cms;
|
||||
struct wl_event_loop *loop;
|
||||
|
||||
weston_log("colord: initialized\n");
|
||||
|
||||
/* create local state object */
|
||||
cms = zalloc(sizeof *cms);
|
||||
if (cms == NULL)
|
||||
return -1;
|
||||
cms->ec = ec;
|
||||
|
||||
if (!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
|
||||
cms->client = cd_client_new();
|
||||
ret = cd_client_connect_sync(cms->client, NULL, &error);
|
||||
if (!ret) {
|
||||
weston_log("colord: failed to contact daemon: %s\n", error->message);
|
||||
g_error_free(error);
|
||||
colord_module_destroy(cms);
|
||||
return -1;
|
||||
}
|
||||
g_mutex_init(&cms->pending_mutex);
|
||||
cms->devices = g_hash_table_new_full(g_str_hash, g_str_equal,
|
||||
g_free, colord_cms_output_destroy);
|
||||
|
||||
/* devices added */
|
||||
cms->output_created_listener.notify = colord_notifier_output_created;
|
||||
wl_signal_add(&ec->output_created_signal, &cms->output_created_listener);
|
||||
|
||||
/* add all the PNP IDs */
|
||||
cms->pnp_ids = g_hash_table_new_full(g_str_hash,
|
||||
g_str_equal,
|
||||
NULL,
|
||||
NULL);
|
||||
colord_load_pnp_ids(cms);
|
||||
|
||||
/* setup a thread for the GLib callbacks */
|
||||
cms->loop = g_main_loop_new(NULL, FALSE);
|
||||
cms->thread = g_thread_new("colord CMS main loop",
|
||||
colord_run_loop_thread, cms);
|
||||
|
||||
/* batch device<->profile updates */
|
||||
if (pipe2(fd, O_CLOEXEC) == -1) {
|
||||
colord_module_destroy(cms);
|
||||
return -1;
|
||||
}
|
||||
cms->readfd = fd[0];
|
||||
cms->writefd = fd[1];
|
||||
loop = wl_display_get_event_loop(ec->wl_display);
|
||||
cms->source = wl_event_loop_add_fd (loop,
|
||||
cms->readfd,
|
||||
WL_EVENT_READABLE,
|
||||
colord_dispatch_all_pending,
|
||||
cms);
|
||||
if (!cms->source) {
|
||||
colord_module_destroy(cms);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright © 2013 Richard Hughes
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef HAVE_LCMS
|
||||
#include <lcms2.h>
|
||||
#endif
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include "cms-helper.h"
|
||||
|
||||
#ifdef HAVE_LCMS
|
||||
static void
|
||||
weston_cms_gamma_clear(struct weston_output *o)
|
||||
{
|
||||
int i;
|
||||
uint16_t *red;
|
||||
|
||||
if (!o->set_gamma)
|
||||
return;
|
||||
|
||||
red = calloc(o->gamma_size, sizeof(uint16_t));
|
||||
for (i = 0; i < o->gamma_size; i++)
|
||||
red[i] = (uint32_t) 0xffff * (uint32_t) i / (uint32_t) (o->gamma_size - 1);
|
||||
o->set_gamma(o, o->gamma_size, red, red, red);
|
||||
free(red);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
weston_cms_set_color_profile(struct weston_output *o,
|
||||
struct weston_color_profile *p)
|
||||
{
|
||||
#ifdef HAVE_LCMS
|
||||
cmsFloat32Number in;
|
||||
const cmsToneCurve **vcgt;
|
||||
int i;
|
||||
int size;
|
||||
uint16_t *red = NULL;
|
||||
uint16_t *green = NULL;
|
||||
uint16_t *blue = NULL;
|
||||
|
||||
if (!o->set_gamma)
|
||||
return;
|
||||
if (!p) {
|
||||
weston_cms_gamma_clear(o);
|
||||
return;
|
||||
}
|
||||
|
||||
weston_log("Using ICC profile %s\n", p->filename);
|
||||
vcgt = cmsReadTag (p->lcms_handle, cmsSigVcgtTag);
|
||||
if (vcgt == NULL || vcgt[0] == NULL) {
|
||||
weston_cms_gamma_clear(o);
|
||||
return;
|
||||
}
|
||||
|
||||
size = o->gamma_size;
|
||||
red = calloc(size, sizeof(uint16_t));
|
||||
green = calloc(size, sizeof(uint16_t));
|
||||
blue = calloc(size, sizeof(uint16_t));
|
||||
for (i = 0; i < size; i++) {
|
||||
in = (cmsFloat32Number) i / (cmsFloat32Number) (size - 1);
|
||||
red[i] = cmsEvalToneCurveFloat(vcgt[0], in) * (double) 0xffff;
|
||||
green[i] = cmsEvalToneCurveFloat(vcgt[1], in) * (double) 0xffff;
|
||||
blue[i] = cmsEvalToneCurveFloat(vcgt[2], in) * (double) 0xffff;
|
||||
}
|
||||
o->set_gamma(o, size, red, green, blue);
|
||||
free(red);
|
||||
free(green);
|
||||
free(blue);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
weston_cms_destroy_profile(struct weston_color_profile *p)
|
||||
{
|
||||
if (!p)
|
||||
return;
|
||||
#ifdef HAVE_LCMS
|
||||
cmsCloseProfile(p->lcms_handle);
|
||||
#endif
|
||||
free(p->filename);
|
||||
free(p);
|
||||
}
|
||||
|
||||
struct weston_color_profile *
|
||||
weston_cms_create_profile(const char *filename,
|
||||
void *lcms_profile)
|
||||
{
|
||||
struct weston_color_profile *p;
|
||||
p = zalloc(sizeof(struct weston_color_profile));
|
||||
p->filename = strdup(filename);
|
||||
p->lcms_handle = lcms_profile;
|
||||
return p;
|
||||
}
|
||||
|
||||
struct weston_color_profile *
|
||||
weston_cms_load_profile(const char *filename)
|
||||
{
|
||||
struct weston_color_profile *p = NULL;
|
||||
#ifdef HAVE_LCMS
|
||||
cmsHPROFILE lcms_profile;
|
||||
lcms_profile = cmsOpenProfileFromFile(filename, "r");
|
||||
if (lcms_profile)
|
||||
p = weston_cms_create_profile(filename, lcms_profile);
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright © 2013 Richard Hughes
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _WESTON_CMS_H_
|
||||
#define _WESTON_CMS_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
|
||||
/* General overview on how to be a CMS plugin:
|
||||
*
|
||||
* First, some nomenclature:
|
||||
*
|
||||
* CMF: Color management framework, i.e. "Use foo.icc for device $bar"
|
||||
* CMM: Color management module that converts pixel colors, which is
|
||||
* usually lcms2 on any modern OS.
|
||||
* CMS: Color management system that encompasses both a CMF and CMM.
|
||||
* ICC: International Color Consortium, the people that define the
|
||||
* binary encoding of a .icc file.
|
||||
* VCGT: Video Card Gamma Tag. An Apple extension to the ICC specification
|
||||
* that allows the calibration state to be stored in the ICC profile
|
||||
* Output: Physical port with a display attached, e.g. LVDS1
|
||||
*
|
||||
* As a CMF is probably something you don't want or need on an embedded install
|
||||
* these functions will not be called if the icc_profile key is set for a
|
||||
* specific [output] section in weston.ini
|
||||
*
|
||||
* Most desktop environments want the CMF to decide what profile to use in
|
||||
* different situations, so that displays can be profiled and also so that
|
||||
* the ICC profiles can be changed at runtime depending on the task or ambient
|
||||
* environment.
|
||||
*
|
||||
* The CMF can be selected using the 'modules' key in the [core] section.
|
||||
*/
|
||||
|
||||
struct weston_color_profile {
|
||||
char *filename;
|
||||
void *lcms_handle;
|
||||
};
|
||||
|
||||
void
|
||||
weston_cms_set_color_profile(struct weston_output *o,
|
||||
struct weston_color_profile *p);
|
||||
struct weston_color_profile *
|
||||
weston_cms_create_profile(const char *filename,
|
||||
void *lcms_profile);
|
||||
struct weston_color_profile *
|
||||
weston_cms_load_profile(const char *filename);
|
||||
void
|
||||
weston_cms_destroy_profile(struct weston_color_profile *p);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright © 2013 Richard Hughes
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include "cms-helper.h"
|
||||
#include "shared/helpers.h"
|
||||
#include "weston.h"
|
||||
|
||||
struct cms_static {
|
||||
struct weston_compositor *ec;
|
||||
struct wl_listener destroy_listener;
|
||||
struct wl_listener output_created_listener;
|
||||
};
|
||||
|
||||
static void
|
||||
cms_output_created(struct cms_static *cms, struct weston_output *o)
|
||||
{
|
||||
struct weston_color_profile *p;
|
||||
struct weston_config_section *s;
|
||||
char *profile;
|
||||
|
||||
weston_log("cms-static: output %i [%s] created\n", o->id, o->name);
|
||||
|
||||
if (o->name == NULL)
|
||||
return;
|
||||
s = weston_config_get_section(wet_get_config(cms->ec),
|
||||
"output", "name", o->name);
|
||||
if (s == NULL)
|
||||
return;
|
||||
if (weston_config_section_get_string(s, "icc_profile", &profile, NULL) < 0)
|
||||
return;
|
||||
p = weston_cms_load_profile(profile);
|
||||
if (p == NULL && strlen(profile) > 0) {
|
||||
weston_log("cms-static: failed to load %s\n", profile);
|
||||
} else {
|
||||
weston_log("cms-static: loading %s for %s\n",
|
||||
(p != NULL) ? profile : "identity LUT",
|
||||
o->name);
|
||||
weston_cms_set_color_profile(o, p);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cms_notifier_output_created(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct weston_output *o = (struct weston_output *) data;
|
||||
struct cms_static *cms =
|
||||
container_of(listener, struct cms_static, output_created_listener);
|
||||
cms_output_created(cms, o);
|
||||
}
|
||||
|
||||
static void
|
||||
cms_module_destroy(struct cms_static *cms)
|
||||
{
|
||||
free(cms);
|
||||
}
|
||||
|
||||
static void
|
||||
cms_notifier_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct cms_static *cms = container_of(listener, struct cms_static, destroy_listener);
|
||||
cms_module_destroy(cms);
|
||||
}
|
||||
|
||||
|
||||
WL_EXPORT int
|
||||
wet_module_init(struct weston_compositor *ec,
|
||||
int *argc, char *argv[])
|
||||
{
|
||||
struct cms_static *cms;
|
||||
struct weston_output *output;
|
||||
|
||||
weston_log("cms-static: initialized\n");
|
||||
|
||||
/* create local state object */
|
||||
cms = zalloc(sizeof *cms);
|
||||
if (cms == NULL)
|
||||
return -1;
|
||||
|
||||
cms->ec = ec;
|
||||
|
||||
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);
|
||||
|
||||
/* discover outputs */
|
||||
wl_list_for_each(output, &ec->output_list, link)
|
||||
cms_output_created(cms, output);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "weston.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
return wet_main(argc, argv);
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
srcs_weston = [
|
||||
git_version_h,
|
||||
'main.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,
|
||||
]
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
)
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright (c) 2015 General Electric Company. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <systemd/sd-daemon.h>
|
||||
#include <sys/socket.h>
|
||||
#include <wayland-server.h>
|
||||
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/string-helpers.h"
|
||||
#include <libweston/zalloc.h>
|
||||
#include <libweston/libweston.h>
|
||||
#include "weston.h"
|
||||
|
||||
struct systemd_notifier {
|
||||
int watchdog_time;
|
||||
struct wl_event_source *watchdog_source;
|
||||
struct wl_listener compositor_destroy_listener;
|
||||
};
|
||||
|
||||
static int
|
||||
add_systemd_sockets(struct weston_compositor *compositor)
|
||||
{
|
||||
int fd;
|
||||
int cnt_systemd_sockets;
|
||||
int current_fd = 0;
|
||||
|
||||
cnt_systemd_sockets = sd_listen_fds(1);
|
||||
|
||||
if (cnt_systemd_sockets < 0) {
|
||||
weston_log("sd_listen_fds failed with: %d\n",
|
||||
cnt_systemd_sockets);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* socket-based activation not used, return silently */
|
||||
if (cnt_systemd_sockets == 0)
|
||||
return 0;
|
||||
|
||||
while (current_fd < cnt_systemd_sockets) {
|
||||
fd = SD_LISTEN_FDS_START + current_fd;
|
||||
|
||||
if (sd_is_socket(fd, AF_UNIX, SOCK_STREAM,1) <= 0) {
|
||||
weston_log("invalid socket provided from systemd\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wl_display_add_socket_fd(compositor->wl_display, fd)) {
|
||||
weston_log("wl_display_add_socket_fd failed"
|
||||
"for systemd provided socket\n");
|
||||
return -1;
|
||||
}
|
||||
current_fd++;
|
||||
}
|
||||
|
||||
weston_log("info: add %d socket(s) provided by systemd\n",
|
||||
current_fd);
|
||||
|
||||
return current_fd;
|
||||
}
|
||||
|
||||
static int
|
||||
watchdog_handler(void *data)
|
||||
{
|
||||
struct systemd_notifier *notifier = data;
|
||||
|
||||
wl_event_source_timer_update(notifier->watchdog_source,
|
||||
notifier->watchdog_time);
|
||||
|
||||
sd_notify(0, "WATCHDOG=1");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_compositor_destroy_listener(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct systemd_notifier *notifier;
|
||||
|
||||
sd_notify(0, "STOPPING=1");
|
||||
|
||||
notifier = container_of(listener,
|
||||
struct systemd_notifier,
|
||||
compositor_destroy_listener);
|
||||
|
||||
if (notifier->watchdog_source)
|
||||
wl_event_source_remove(notifier->watchdog_source);
|
||||
|
||||
wl_list_remove(¬ifier->compositor_destroy_listener.link);
|
||||
free(notifier);
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
wet_module_init(struct weston_compositor *compositor,
|
||||
int *argc, char *argv[])
|
||||
{
|
||||
char *watchdog_time_env;
|
||||
struct wl_event_loop *loop;
|
||||
int32_t watchdog_time_conv;
|
||||
struct systemd_notifier *notifier;
|
||||
|
||||
notifier = zalloc(sizeof *notifier);
|
||||
if (notifier == NULL)
|
||||
return -1;
|
||||
|
||||
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;
|
||||
|
||||
sd_notify(0, "READY=1");
|
||||
|
||||
/* 'WATCHDOG_USEC' is environment variable that is set
|
||||
* by systemd to transfer 'WatchdogSec' watchdog timeout
|
||||
* setting from service file.*/
|
||||
watchdog_time_env = getenv("WATCHDOG_USEC");
|
||||
if (!watchdog_time_env)
|
||||
return 0;
|
||||
|
||||
if (!safe_strtoint(watchdog_time_env, &watchdog_time_conv))
|
||||
return 0;
|
||||
|
||||
/* Convert 'WATCHDOG_USEC' to milliseconds and notify
|
||||
* systemd every half of that time.*/
|
||||
watchdog_time_conv /= 1000 * 2;
|
||||
if (watchdog_time_conv <= 0)
|
||||
return 0;
|
||||
|
||||
notifier->watchdog_time = watchdog_time_conv;
|
||||
|
||||
loop = wl_display_get_event_loop(compositor->wl_display);
|
||||
notifier->watchdog_source =
|
||||
wl_event_loop_add_timer(loop, watchdog_handler, notifier);
|
||||
wl_event_source_timer_update(notifier->watchdog_source,
|
||||
notifier->watchdog_time);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <wayland-util.h>
|
||||
#include "weston.h"
|
||||
|
||||
static struct wet_testsuite_data *wet_testsuite_data_global;
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Copyright © 2008-2011 Kristian Høgsberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include "weston.h"
|
||||
#include "weston-screenshooter-server-protocol.h"
|
||||
#include "shared/helpers.h"
|
||||
#include <libweston/weston-log.h>
|
||||
|
||||
struct screenshooter {
|
||||
struct weston_compositor *ec;
|
||||
struct wl_global *global;
|
||||
struct wl_client *client;
|
||||
struct weston_process process;
|
||||
struct wl_listener destroy_listener;
|
||||
struct weston_recorder *recorder;
|
||||
};
|
||||
|
||||
static void
|
||||
screenshooter_done(void *data, enum weston_screenshooter_outcome outcome)
|
||||
{
|
||||
struct wl_resource *resource = data;
|
||||
|
||||
switch (outcome) {
|
||||
case WESTON_SCREENSHOOTER_SUCCESS:
|
||||
weston_screenshooter_send_done(resource);
|
||||
break;
|
||||
case WESTON_SCREENSHOOTER_NO_MEMORY:
|
||||
wl_resource_post_no_memory(resource);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
screenshooter_shoot(struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *output_resource,
|
||||
struct wl_resource *buffer_resource)
|
||||
{
|
||||
struct weston_output *output =
|
||||
weston_head_from_resource(output_resource)->output;
|
||||
struct weston_buffer *buffer =
|
||||
weston_buffer_from_resource(buffer_resource);
|
||||
|
||||
if (buffer == NULL) {
|
||||
wl_resource_post_no_memory(resource);
|
||||
return;
|
||||
}
|
||||
|
||||
weston_screenshooter_shoot(output, buffer, screenshooter_done, resource);
|
||||
}
|
||||
|
||||
struct weston_screenshooter_interface screenshooter_implementation = {
|
||||
screenshooter_shoot
|
||||
};
|
||||
|
||||
static void
|
||||
bind_shooter(struct wl_client *client,
|
||||
void *data, uint32_t version, uint32_t id)
|
||||
{
|
||||
struct screenshooter *shooter = data;
|
||||
struct wl_resource *resource;
|
||||
bool debug_enabled =
|
||||
weston_compositor_is_debug_protocol_enabled(shooter->ec);
|
||||
|
||||
resource = wl_resource_create(client,
|
||||
&weston_screenshooter_interface, 1, id);
|
||||
|
||||
if (!debug_enabled && !shooter->client) {
|
||||
wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
|
||||
"screenshooter failed: permission denied. "\
|
||||
"Debug protocol must be enabled");
|
||||
return;
|
||||
} else if (!debug_enabled && client != shooter->client) {
|
||||
wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
|
||||
"screenshooter failed: permission denied.");
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource_set_implementation(resource, &screenshooter_implementation,
|
||||
data, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
screenshooter_sigchld(struct weston_process *process, int status)
|
||||
{
|
||||
struct screenshooter *shooter =
|
||||
container_of(process, struct screenshooter, process);
|
||||
|
||||
shooter->client = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
screenshooter_binding(struct weston_keyboard *keyboard,
|
||||
const struct timespec *time, uint32_t key, void *data)
|
||||
{
|
||||
struct screenshooter *shooter = data;
|
||||
char *screenshooter_exe;
|
||||
|
||||
|
||||
screenshooter_exe = wet_get_bindir_path("weston-screenshooter");
|
||||
if (!screenshooter_exe) {
|
||||
weston_log("Could not construct screenshooter path.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shooter->client)
|
||||
shooter->client = weston_client_launch(shooter->ec,
|
||||
&shooter->process,
|
||||
screenshooter_exe, screenshooter_sigchld);
|
||||
free(screenshooter_exe);
|
||||
}
|
||||
|
||||
static void
|
||||
recorder_binding(struct weston_keyboard *keyboard, const struct timespec *time,
|
||||
uint32_t key, void *data)
|
||||
{
|
||||
struct weston_compositor *ec = keyboard->seat->compositor;
|
||||
struct weston_output *output;
|
||||
struct screenshooter *shooter = data;
|
||||
struct weston_recorder *recorder = shooter->recorder;;
|
||||
static const char filename[] = "capture.wcap";
|
||||
|
||||
if (recorder) {
|
||||
weston_recorder_stop(recorder);
|
||||
shooter->recorder = NULL;
|
||||
} else {
|
||||
if (keyboard->focus && keyboard->focus->output)
|
||||
output = keyboard->focus->output;
|
||||
else
|
||||
output = container_of(ec->output_list.next,
|
||||
struct weston_output, link);
|
||||
|
||||
shooter->recorder = weston_recorder_start(output, filename);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
screenshooter_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct screenshooter *shooter =
|
||||
container_of(listener, struct screenshooter, destroy_listener);
|
||||
|
||||
wl_list_remove(&shooter->destroy_listener.link);
|
||||
|
||||
wl_global_destroy(shooter->global);
|
||||
free(shooter);
|
||||
}
|
||||
|
||||
WL_EXPORT void
|
||||
screenshooter_create(struct weston_compositor *ec)
|
||||
{
|
||||
struct screenshooter *shooter;
|
||||
|
||||
shooter = zalloc(sizeof *shooter);
|
||||
if (shooter == NULL)
|
||||
return;
|
||||
|
||||
shooter->ec = ec;
|
||||
|
||||
shooter->global = wl_global_create(ec->wl_display,
|
||||
&weston_screenshooter_interface, 1,
|
||||
shooter, bind_shooter);
|
||||
weston_compositor_add_key_binding(ec, KEY_S, MODIFIER_SUPER,
|
||||
screenshooter_binding, shooter);
|
||||
weston_compositor_add_key_binding(ec, KEY_R, MODIFIER_SUPER,
|
||||
recorder_binding, shooter);
|
||||
|
||||
shooter->destroy_listener.notify = screenshooter_destroy;
|
||||
wl_signal_add(&ec->destroy_signal, &shooter->destroy_listener);
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
[Desktop Entry]
|
||||
Name=Weston
|
||||
Comment=The reference Wayland server
|
||||
Exec=weston
|
||||
Type=Application
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright © 2016 Giulio Camuffo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef WESTON_H
|
||||
#define WESTON_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/config-parser.h>
|
||||
|
||||
void
|
||||
screenshooter_create(struct weston_compositor *ec);
|
||||
|
||||
struct weston_process;
|
||||
typedef void (*weston_process_cleanup_func_t)(struct weston_process *process,
|
||||
int status);
|
||||
|
||||
struct weston_process {
|
||||
pid_t pid;
|
||||
weston_process_cleanup_func_t cleanup;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct wl_client *
|
||||
weston_client_launch(struct weston_compositor *compositor,
|
||||
struct weston_process *proc,
|
||||
const char *path,
|
||||
weston_process_cleanup_func_t cleanup);
|
||||
|
||||
struct wl_client *
|
||||
weston_client_start(struct weston_compositor *compositor, const char *path);
|
||||
|
||||
void
|
||||
weston_watch_process(struct weston_process *process);
|
||||
|
||||
struct weston_config *
|
||||
wet_get_config(struct weston_compositor *compositor);
|
||||
|
||||
void *
|
||||
wet_load_module_entrypoint(const char *name, const char *entrypoint);
|
||||
|
||||
int
|
||||
wet_shell_init(struct weston_compositor *ec,
|
||||
int *argc, char *argv[]);
|
||||
int
|
||||
wet_module_init(struct weston_compositor *ec,
|
||||
int *argc, char *argv[]);
|
||||
int
|
||||
wet_load_module(struct weston_compositor *compositor,
|
||||
const char *name, int *argc, char *argv[]);
|
||||
|
||||
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);
|
||||
|
||||
struct text_backend;
|
||||
|
||||
struct text_backend *
|
||||
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
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Copyright © 2011 Intel Corporation
|
||||
* Copyright © 2016 Giulio Camuffo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include "compositor/weston.h"
|
||||
#include <libweston/xwayland-api.h>
|
||||
#include "shared/helpers.h"
|
||||
|
||||
struct wet_xwayland {
|
||||
struct weston_compositor *compositor;
|
||||
const struct weston_xwayland_api *api;
|
||||
struct weston_xwayland *xwayland;
|
||||
struct wl_event_source *sigusr1_source;
|
||||
struct wl_client *client;
|
||||
int wm_fd;
|
||||
struct weston_process process;
|
||||
};
|
||||
|
||||
static int
|
||||
handle_sigusr1(int signal_number, void *data)
|
||||
{
|
||||
struct wet_xwayland *wxw = data;
|
||||
|
||||
/* We'd be safer if we actually had the struct
|
||||
* signalfd_siginfo from the signalfd data and could verify
|
||||
* this came from Xwayland.*/
|
||||
wxw->api->xserver_loaded(wxw->xwayland, wxw->client, wxw->wm_fd);
|
||||
wl_event_source_remove(wxw->sigusr1_source);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static pid_t
|
||||
spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd)
|
||||
{
|
||||
struct wet_xwayland *wxw = user_data;
|
||||
pid_t pid;
|
||||
char s[12], abstract_fd_str[12], unix_fd_str[12], wm_fd_str[12];
|
||||
int sv[2], wm[2], fd;
|
||||
char *xserver = NULL;
|
||||
struct weston_config *config = wet_get_config(wxw->compositor);
|
||||
struct weston_config_section *section;
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
|
||||
weston_log("wl connection socketpair failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wm) < 0) {
|
||||
weston_log("X wm connection socketpair failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
case 0:
|
||||
/* SOCK_CLOEXEC closes both ends, so we need to unset
|
||||
* the flag on the client fd. */
|
||||
fd = dup(sv[1]);
|
||||
if (fd < 0)
|
||||
goto fail;
|
||||
snprintf(s, sizeof s, "%d", fd);
|
||||
setenv("WAYLAND_SOCKET", s, 1);
|
||||
|
||||
fd = dup(abstract_fd);
|
||||
if (fd < 0)
|
||||
goto fail;
|
||||
snprintf(abstract_fd_str, sizeof abstract_fd_str, "%d", fd);
|
||||
fd = dup(unix_fd);
|
||||
if (fd < 0)
|
||||
goto fail;
|
||||
snprintf(unix_fd_str, sizeof unix_fd_str, "%d", fd);
|
||||
fd = dup(wm[1]);
|
||||
if (fd < 0)
|
||||
goto fail;
|
||||
snprintf(wm_fd_str, sizeof wm_fd_str, "%d", fd);
|
||||
|
||||
section = weston_config_get_section(config,
|
||||
"xwayland", NULL, NULL);
|
||||
weston_config_section_get_string(section, "path",
|
||||
&xserver, XSERVER_PATH);
|
||||
|
||||
/* Ignore SIGUSR1 in the child, which will make the X
|
||||
* server send SIGUSR1 to the parent (weston) when
|
||||
* it's done with initialization. During
|
||||
* initialization the X server will round trip and
|
||||
* block on the wayland compositor, so avoid making
|
||||
* blocking requests (like xcb_connect_to_fd) until
|
||||
* it's done with that. */
|
||||
signal(SIGUSR1, SIG_IGN);
|
||||
|
||||
if (execl(xserver,
|
||||
xserver,
|
||||
display,
|
||||
"-rootless",
|
||||
"-listen", abstract_fd_str,
|
||||
"-listen", unix_fd_str,
|
||||
"-wm", wm_fd_str,
|
||||
"-terminate",
|
||||
NULL) < 0)
|
||||
weston_log("exec of '%s %s -rootless "
|
||||
"-listen %s -listen %s -wm %s "
|
||||
"-terminate' failed: %s\n",
|
||||
xserver, display,
|
||||
abstract_fd_str, unix_fd_str, wm_fd_str,
|
||||
strerror(errno));
|
||||
fail:
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
default:
|
||||
close(sv[1]);
|
||||
wxw->client = wl_client_create(wxw->compositor->wl_display, sv[0]);
|
||||
|
||||
close(wm[1]);
|
||||
wxw->wm_fd = wm[0];
|
||||
|
||||
wxw->process.pid = pid;
|
||||
weston_watch_process(&wxw->process);
|
||||
break;
|
||||
|
||||
case -1:
|
||||
weston_log("Failed to fork to spawn xserver process\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
static void
|
||||
xserver_cleanup(struct weston_process *process, int status)
|
||||
{
|
||||
struct wet_xwayland *wxw =
|
||||
container_of(process, struct wet_xwayland, process);
|
||||
struct wl_event_loop *loop =
|
||||
wl_display_get_event_loop(wxw->compositor->wl_display);
|
||||
|
||||
wxw->api->xserver_exited(wxw->xwayland, status);
|
||||
wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1,
|
||||
handle_sigusr1, wxw);
|
||||
wxw->client = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
wet_load_xwayland(struct weston_compositor *comp)
|
||||
{
|
||||
const struct weston_xwayland_api *api;
|
||||
struct weston_xwayland *xwayland;
|
||||
struct wet_xwayland *wxw;
|
||||
struct wl_event_loop *loop;
|
||||
|
||||
if (weston_compositor_load_xwayland(comp) < 0)
|
||||
return -1;
|
||||
|
||||
api = weston_xwayland_get_api(comp);
|
||||
if (!api) {
|
||||
weston_log("Failed to get the xwayland module API.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
xwayland = api->get(comp);
|
||||
if (!xwayland) {
|
||||
weston_log("Failed to get the xwayland object.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wxw = zalloc(sizeof *wxw);
|
||||
if (!wxw)
|
||||
return -1;
|
||||
|
||||
wxw->compositor = comp;
|
||||
wxw->api = api;
|
||||
wxw->xwayland = xwayland;
|
||||
wxw->process.cleanup = xserver_cleanup;
|
||||
if (api->listen(xwayland, wxw, spawn_xserver) < 0)
|
||||
return -1;
|
||||
|
||||
loop = wl_display_get_event_loop(comp->wl_display);
|
||||
wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1,
|
||||
handle_sigusr1, wxw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
For the DMZ cursors:
|
||||
|
||||
(c) 2007-2010 Novell, Inc.
|
||||
|
||||
This work is licenced under the Creative Commons Attribution-Share Alike 3.0
|
||||
United States License. To view a copy of this licence, visit
|
||||
http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative
|
||||
Commons, 171 Second Street, Suite 300, San Francisco, California 94105, USA.
|
||||
|
||||
The terminal icon is taken from the gnome-icon-theme collection which
|
||||
is also distributed under the Creative Commons BY-SA 3.0 license.
|
||||
|
||||
|
||||
(C) 2013 DENSO CORPORATION
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell following listed images
|
||||
for any purpose is hereby granted without fee, provided
|
||||
that the above copyright notice appear in all copies and that both that
|
||||
copyright notice and this permission notice appear in supporting
|
||||
documentation, and that the name of the copyright holders not be used in
|
||||
advertising or publicity pertaining to distribution of the images
|
||||
without specific, written prior permission. The copyright holders make
|
||||
no representations about the suitability of these images for any
|
||||
purpose. It is provided "as is" without express or implied warranty.
|
||||
|
||||
background.png
|
||||
tiling.png
|
||||
fullscreen.png
|
||||
panel.png
|
||||
random.png
|
||||
sidebyside.png
|
||||
home.png
|
||||
icon_ivi_clickdot.png
|
||||
icon_ivi_flower.png
|
||||
icon_ivi_simple-egl.png
|
||||
icon_ivi_simple-shm.png
|
||||
icon_ivi_smoke.png
|
||||
|
||||
|
||||
For the SVG icons:
|
||||
|
||||
© 2016 Samsung Electronics Co., Ltd
|
||||
|
||||
This work is dual-licenced under both the MIT "Expat" License and the
|
||||
Creative Commons Attribution-Share Alike 3.0 United States License, and
|
||||
may be redistributed under either (or both) licenses as desired. See
|
||||
Weston's COPYING for details of the MIT license. To view a copy of the
|
||||
CC-SA-3.0 licence, visit http://creativecommons.org/licenses/by-sa/3.0/
|
||||
or send a letter to Creative Commons, 171 Second Street, Suite 300, San
|
||||
Francisco, California 94105, USA.
|
||||
|
||||
icons.svg
|
||||
icon_terminal.png
|
||||
icon_editor.png
|
||||
icon_flower.png
|
||||
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 737 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 525 B |
|
After Width: | Height: | Size: 86 B |
|
After Width: | Height: | Size: 47 KiB |
|
|
@ -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')
|
||||
)
|
||||
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 825 B |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 104 B |
|
After Width: | Height: | Size: 85 B |
|
After Width: | Height: | Size: 79 B |
|
After Width: | Height: | Size: 765 B |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
|
@ -0,0 +1,737 @@
|
|||
/*
|
||||
* Copyright © 2010-2012 Intel Corporation
|
||||
* Copyright © 2011-2012 Collabora, Ltd.
|
||||
* Copyright © 2013 Raspberry Pi Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include "shell.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
struct exposay_surface {
|
||||
struct desktop_shell *shell;
|
||||
struct exposay_output *eoutput;
|
||||
struct weston_surface *surface;
|
||||
struct weston_view *view;
|
||||
struct wl_listener view_destroy_listener;
|
||||
struct wl_list link;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
double scale;
|
||||
|
||||
int row;
|
||||
int column;
|
||||
|
||||
/* The animations only apply a transformation for their own lifetime,
|
||||
* and don't have an option to indefinitely maintain the
|
||||
* transformation in a steady state - so, we apply our own once the
|
||||
* animation has finished. */
|
||||
struct weston_transform transform;
|
||||
};
|
||||
|
||||
static void exposay_set_state(struct desktop_shell *shell,
|
||||
enum exposay_target_state state,
|
||||
struct weston_seat *seat);
|
||||
static void exposay_check_state(struct desktop_shell *shell);
|
||||
|
||||
static void
|
||||
exposay_surface_destroy(struct exposay_surface *esurface)
|
||||
{
|
||||
wl_list_remove(&esurface->link);
|
||||
wl_list_remove(&esurface->view_destroy_listener.link);
|
||||
|
||||
if (esurface->shell->exposay.focus_current == esurface->view)
|
||||
esurface->shell->exposay.focus_current = NULL;
|
||||
if (esurface->shell->exposay.focus_prev == esurface->view)
|
||||
esurface->shell->exposay.focus_prev = NULL;
|
||||
|
||||
free(esurface);
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_in_flight_inc(struct desktop_shell *shell)
|
||||
{
|
||||
shell->exposay.in_flight++;
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_in_flight_dec(struct desktop_shell *shell)
|
||||
{
|
||||
if (--shell->exposay.in_flight > 0)
|
||||
return;
|
||||
|
||||
exposay_check_state(shell);
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_animate_in_done(struct weston_view_animation *animation, void *data)
|
||||
{
|
||||
struct exposay_surface *esurface = data;
|
||||
|
||||
wl_list_insert(&esurface->view->geometry.transformation_list,
|
||||
&esurface->transform.link);
|
||||
weston_matrix_init(&esurface->transform.matrix);
|
||||
weston_matrix_scale(&esurface->transform.matrix,
|
||||
esurface->scale, esurface->scale, 1.0f);
|
||||
weston_matrix_translate(&esurface->transform.matrix,
|
||||
esurface->x - esurface->view->geometry.x,
|
||||
esurface->y - esurface->view->geometry.y,
|
||||
0);
|
||||
|
||||
weston_view_geometry_dirty(esurface->view);
|
||||
weston_compositor_schedule_repaint(esurface->view->surface->compositor);
|
||||
|
||||
exposay_in_flight_dec(esurface->shell);
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_animate_in(struct exposay_surface *esurface)
|
||||
{
|
||||
exposay_in_flight_inc(esurface->shell);
|
||||
|
||||
weston_move_scale_run(esurface->view,
|
||||
esurface->x - esurface->view->geometry.x,
|
||||
esurface->y - esurface->view->geometry.y,
|
||||
1.0, esurface->scale, 0,
|
||||
exposay_animate_in_done, esurface);
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_animate_out_done(struct weston_view_animation *animation, void *data)
|
||||
{
|
||||
struct exposay_surface *esurface = data;
|
||||
struct desktop_shell *shell = esurface->shell;
|
||||
|
||||
exposay_surface_destroy(esurface);
|
||||
|
||||
exposay_in_flight_dec(shell);
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_animate_out(struct exposay_surface *esurface)
|
||||
{
|
||||
exposay_in_flight_inc(esurface->shell);
|
||||
|
||||
/* Remove the static transformation set up by
|
||||
* exposay_transform_in_done(). */
|
||||
wl_list_remove(&esurface->transform.link);
|
||||
weston_view_geometry_dirty(esurface->view);
|
||||
|
||||
weston_move_scale_run(esurface->view,
|
||||
esurface->x - esurface->view->geometry.x,
|
||||
esurface->y - esurface->view->geometry.y,
|
||||
1.0, esurface->scale, 1,
|
||||
exposay_animate_out_done, esurface);
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_highlight_surface(struct desktop_shell *shell,
|
||||
struct exposay_surface *esurface)
|
||||
{
|
||||
struct weston_view *view = esurface->view;
|
||||
|
||||
if (shell->exposay.focus_current == view)
|
||||
return;
|
||||
|
||||
shell->exposay.row_current = esurface->row;
|
||||
shell->exposay.column_current = esurface->column;
|
||||
shell->exposay.cur_output = esurface->eoutput;
|
||||
|
||||
activate(shell, view, shell->exposay.seat,
|
||||
WESTON_ACTIVATE_FLAG_NONE);
|
||||
shell->exposay.focus_current = view;
|
||||
}
|
||||
|
||||
static int
|
||||
exposay_is_animating(struct desktop_shell *shell)
|
||||
{
|
||||
if (shell->exposay.state_cur == EXPOSAY_LAYOUT_INACTIVE ||
|
||||
shell->exposay.state_cur == EXPOSAY_LAYOUT_OVERVIEW)
|
||||
return 0;
|
||||
|
||||
return (shell->exposay.in_flight > 0);
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_pick(struct desktop_shell *shell, int x, int y)
|
||||
{
|
||||
struct exposay_surface *esurface;
|
||||
|
||||
if (exposay_is_animating(shell))
|
||||
return;
|
||||
|
||||
wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
|
||||
if (x < esurface->x || x > esurface->x + esurface->width)
|
||||
continue;
|
||||
if (y < esurface->y || y > esurface->y + esurface->height)
|
||||
continue;
|
||||
|
||||
exposay_highlight_surface(shell, esurface);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_view_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct exposay_surface *esurface = container_of(listener,
|
||||
struct exposay_surface,
|
||||
view_destroy_listener);
|
||||
|
||||
exposay_surface_destroy(esurface);
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
struct workspace *workspace = shell->exposay.workspace;
|
||||
struct weston_output *output = shell_output->output;
|
||||
struct exposay_output *eoutput = &shell_output->eoutput;
|
||||
struct weston_view *view;
|
||||
struct exposay_surface *esurface, *highlight = NULL;
|
||||
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;
|
||||
|
||||
eoutput->num_surfaces = 0;
|
||||
wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) {
|
||||
if (!get_shell_surface(view->surface))
|
||||
continue;
|
||||
if (view->output != output)
|
||||
continue;
|
||||
eoutput->num_surfaces++;
|
||||
}
|
||||
|
||||
if (eoutput->num_surfaces == 0) {
|
||||
eoutput->grid_size = 0;
|
||||
eoutput->padding_inner = 0;
|
||||
eoutput->surface_size = 0;
|
||||
return EXPOSAY_LAYOUT_OVERVIEW;
|
||||
}
|
||||
|
||||
/* 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++;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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) {
|
||||
|
||||
if (!get_shell_surface(view->surface))
|
||||
continue;
|
||||
if (view->output != output)
|
||||
continue;
|
||||
|
||||
esurface = malloc(sizeof(*esurface));
|
||||
if (!esurface) {
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL,
|
||||
shell->exposay.seat);
|
||||
break;
|
||||
}
|
||||
|
||||
wl_list_insert(&shell->exposay.surface_list, &esurface->link);
|
||||
esurface->shell = shell;
|
||||
esurface->eoutput = eoutput;
|
||||
esurface->view = view;
|
||||
|
||||
esurface->row = i / eoutput->grid_size;
|
||||
esurface->column = i % eoutput->grid_size;
|
||||
|
||||
esurface->x = left_margin + (pad * esurface->column);
|
||||
esurface->y = top_margin + (pad * esurface->row);
|
||||
|
||||
/* 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;
|
||||
else
|
||||
esurface->scale = eoutput->surface_size / (float) view->surface->height;
|
||||
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;
|
||||
|
||||
exposay_animate_in(esurface);
|
||||
|
||||
/* We want our destroy handler to be after the animation
|
||||
* destroy handler in the list, this way when the view is
|
||||
* destroyed, the animation can safely call the animation
|
||||
* completion callback before we free the esurface in our
|
||||
* destroy handler.
|
||||
*/
|
||||
esurface->view_destroy_listener.notify = handle_view_destroy;
|
||||
wl_signal_add(&view->destroy_signal, &esurface->view_destroy_listener);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (highlight) {
|
||||
shell->exposay.focus_current = NULL;
|
||||
exposay_highlight_surface(shell, highlight);
|
||||
}
|
||||
|
||||
weston_compositor_schedule_repaint(shell->compositor);
|
||||
|
||||
return EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW;
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_focus(struct weston_pointer_grab *grab)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_motion(struct weston_pointer_grab *grab,
|
||||
const struct timespec *time,
|
||||
struct weston_pointer_motion_event *event)
|
||||
{
|
||||
struct desktop_shell *shell =
|
||||
container_of(grab, struct desktop_shell, exposay.grab_ptr);
|
||||
|
||||
weston_pointer_move(grab->pointer, event);
|
||||
|
||||
exposay_pick(shell,
|
||||
wl_fixed_to_int(grab->pointer->x),
|
||||
wl_fixed_to_int(grab->pointer->y));
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_button(struct weston_pointer_grab *grab, const struct timespec *time,
|
||||
uint32_t button, uint32_t state_w)
|
||||
{
|
||||
struct desktop_shell *shell =
|
||||
container_of(grab, struct desktop_shell, exposay.grab_ptr);
|
||||
struct weston_seat *seat = grab->pointer->seat;
|
||||
enum wl_pointer_button_state state = state_w;
|
||||
|
||||
if (button != BTN_LEFT)
|
||||
return;
|
||||
|
||||
/* Store the surface we clicked on, and don't do anything if we end up
|
||||
* releasing on a different surface. */
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
shell->exposay.clicked = shell->exposay.focus_current;
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell->exposay.focus_current == shell->exposay.clicked)
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
|
||||
else
|
||||
shell->exposay.clicked = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_axis(struct weston_pointer_grab *grab,
|
||||
const struct timespec *time,
|
||||
struct weston_pointer_axis_event *event)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_axis_source(struct weston_pointer_grab *grab, uint32_t source)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_frame(struct weston_pointer_grab *grab)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_pointer_grab_cancel(struct weston_pointer_grab *grab)
|
||||
{
|
||||
struct desktop_shell *shell =
|
||||
container_of(grab, struct desktop_shell, exposay.grab_ptr);
|
||||
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
|
||||
}
|
||||
|
||||
static const struct weston_pointer_grab_interface exposay_ptr_grab = {
|
||||
exposay_focus,
|
||||
exposay_motion,
|
||||
exposay_button,
|
||||
exposay_axis,
|
||||
exposay_axis_source,
|
||||
exposay_frame,
|
||||
exposay_pointer_grab_cancel,
|
||||
};
|
||||
|
||||
static int
|
||||
exposay_maybe_move(struct desktop_shell *shell, int row, int column)
|
||||
{
|
||||
struct exposay_surface *esurface;
|
||||
|
||||
wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
|
||||
if (esurface->eoutput != shell->exposay.cur_output ||
|
||||
esurface->row != row || esurface->column != column)
|
||||
continue;
|
||||
|
||||
exposay_highlight_surface(shell, esurface);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_key(struct weston_keyboard_grab *grab, const struct timespec *time,
|
||||
uint32_t key, uint32_t state_w)
|
||||
{
|
||||
struct weston_seat *seat = grab->keyboard->seat;
|
||||
struct desktop_shell *shell =
|
||||
container_of(grab, struct desktop_shell, exposay.grab_kbd);
|
||||
enum wl_keyboard_key_state state = state_w;
|
||||
|
||||
if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
|
||||
return;
|
||||
|
||||
switch (key) {
|
||||
case KEY_ESC:
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
|
||||
break;
|
||||
case KEY_ENTER:
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
|
||||
break;
|
||||
case KEY_UP:
|
||||
exposay_maybe_move(shell, shell->exposay.row_current - 1,
|
||||
shell->exposay.column_current);
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
/* Special case for trying to move to the bottom row when it
|
||||
* has fewer items than all the others. */
|
||||
if (!exposay_maybe_move(shell, shell->exposay.row_current + 1,
|
||||
shell->exposay.column_current) &&
|
||||
shell->exposay.row_current < (shell->exposay.cur_output->grid_size - 1)) {
|
||||
exposay_maybe_move(shell, shell->exposay.row_current + 1,
|
||||
(shell->exposay.cur_output->num_surfaces %
|
||||
shell->exposay.cur_output->grid_size) - 1);
|
||||
}
|
||||
break;
|
||||
case KEY_LEFT:
|
||||
exposay_maybe_move(shell, shell->exposay.row_current,
|
||||
shell->exposay.column_current - 1);
|
||||
break;
|
||||
case KEY_RIGHT:
|
||||
exposay_maybe_move(shell, shell->exposay.row_current,
|
||||
shell->exposay.column_current + 1);
|
||||
break;
|
||||
case KEY_TAB:
|
||||
/* Try to move right, then down (and to the leftmost column),
|
||||
* then if all else fails, to the top left. */
|
||||
if (!exposay_maybe_move(shell, shell->exposay.row_current,
|
||||
shell->exposay.column_current + 1) &&
|
||||
!exposay_maybe_move(shell, shell->exposay.row_current + 1, 0))
|
||||
exposay_maybe_move(shell, 0, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_modifier(struct weston_keyboard_grab *grab, uint32_t serial,
|
||||
uint32_t mods_depressed, uint32_t mods_latched,
|
||||
uint32_t mods_locked, uint32_t group)
|
||||
{
|
||||
struct desktop_shell *shell =
|
||||
container_of(grab, struct desktop_shell, exposay.grab_kbd);
|
||||
struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat;
|
||||
|
||||
/* We want to know when mod has been pressed and released.
|
||||
* FIXME: There is a problem here: if mod is pressed, then a key
|
||||
* is pressed and released, then mod is released, we will treat that
|
||||
* as if only mod had been pressed and released. */
|
||||
if (seat->modifier_state) {
|
||||
if (seat->modifier_state == shell->binding_modifier) {
|
||||
shell->exposay.mod_pressed = true;
|
||||
} else {
|
||||
shell->exposay.mod_invalid = true;
|
||||
}
|
||||
} else {
|
||||
if (shell->exposay.mod_pressed && !shell->exposay.mod_invalid)
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
|
||||
|
||||
shell->exposay.mod_invalid = false;
|
||||
shell->exposay.mod_pressed = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_cancel(struct weston_keyboard_grab *grab)
|
||||
{
|
||||
struct desktop_shell *shell =
|
||||
container_of(grab, struct desktop_shell, exposay.grab_kbd);
|
||||
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
|
||||
}
|
||||
|
||||
static const struct weston_keyboard_grab_interface exposay_kbd_grab = {
|
||||
exposay_key,
|
||||
exposay_modifier,
|
||||
exposay_cancel,
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the transition from overview -> inactive has completed.
|
||||
*/
|
||||
static enum exposay_layout_state
|
||||
exposay_set_inactive(struct desktop_shell *shell)
|
||||
{
|
||||
struct weston_seat *seat = shell->exposay.seat;
|
||||
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
|
||||
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
|
||||
|
||||
if (pointer)
|
||||
weston_pointer_end_grab(pointer);
|
||||
|
||||
if (keyboard) {
|
||||
weston_keyboard_end_grab(keyboard);
|
||||
if (keyboard->input_method_resource)
|
||||
keyboard->grab = &keyboard->input_method_grab;
|
||||
}
|
||||
|
||||
return EXPOSAY_LAYOUT_INACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins the transition from overview to inactive. */
|
||||
static enum exposay_layout_state
|
||||
exposay_transition_inactive(struct desktop_shell *shell, int switch_focus)
|
||||
{
|
||||
struct exposay_surface *esurface;
|
||||
|
||||
/* Call activate() before we start the animations to avoid
|
||||
* animating back the old state and then immediately transitioning
|
||||
* to the new. */
|
||||
if (switch_focus && shell->exposay.focus_current)
|
||||
activate(shell, shell->exposay.focus_current,
|
||||
shell->exposay.seat,
|
||||
WESTON_ACTIVATE_FLAG_CONFIGURE);
|
||||
else if (shell->exposay.focus_prev)
|
||||
activate(shell, shell->exposay.focus_prev,
|
||||
shell->exposay.seat,
|
||||
WESTON_ACTIVATE_FLAG_CONFIGURE);
|
||||
|
||||
wl_list_for_each(esurface, &shell->exposay.surface_list, link)
|
||||
exposay_animate_out(esurface);
|
||||
weston_compositor_schedule_repaint(shell->compositor);
|
||||
|
||||
return EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE;
|
||||
}
|
||||
|
||||
static enum exposay_layout_state
|
||||
exposay_transition_active(struct desktop_shell *shell)
|
||||
{
|
||||
struct weston_seat *seat = shell->exposay.seat;
|
||||
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
|
||||
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
|
||||
struct shell_output *shell_output;
|
||||
bool animate = false;
|
||||
|
||||
shell->exposay.workspace = get_current_workspace(shell);
|
||||
shell->exposay.focus_prev = get_default_view(keyboard->focus);
|
||||
shell->exposay.focus_current = get_default_view(keyboard->focus);
|
||||
shell->exposay.clicked = NULL;
|
||||
wl_list_init(&shell->exposay.surface_list);
|
||||
|
||||
lower_fullscreen_layer(shell, NULL);
|
||||
shell->exposay.grab_kbd.interface = &exposay_kbd_grab;
|
||||
weston_keyboard_start_grab(keyboard,
|
||||
&shell->exposay.grab_kbd);
|
||||
weston_keyboard_set_focus(keyboard, NULL);
|
||||
|
||||
shell->exposay.grab_ptr.interface = &exposay_ptr_grab;
|
||||
if (pointer) {
|
||||
weston_pointer_start_grab(pointer,
|
||||
&shell->exposay.grab_ptr);
|
||||
weston_pointer_clear_focus(pointer);
|
||||
}
|
||||
wl_list_for_each(shell_output, &shell->output_list, link) {
|
||||
enum exposay_layout_state state;
|
||||
|
||||
state = exposay_layout(shell, shell_output);
|
||||
|
||||
if (state == EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW)
|
||||
animate = true;
|
||||
}
|
||||
|
||||
return animate ? EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW
|
||||
: EXPOSAY_LAYOUT_OVERVIEW;
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_check_state(struct desktop_shell *shell)
|
||||
{
|
||||
enum exposay_layout_state state_new = shell->exposay.state_cur;
|
||||
int do_switch = 0;
|
||||
|
||||
/* Don't do anything whilst animations are running, just store up
|
||||
* target state changes and only act on them when the animations have
|
||||
* completed. */
|
||||
if (exposay_is_animating(shell))
|
||||
return;
|
||||
|
||||
switch (shell->exposay.state_target) {
|
||||
case EXPOSAY_TARGET_OVERVIEW:
|
||||
switch (shell->exposay.state_cur) {
|
||||
case EXPOSAY_LAYOUT_OVERVIEW:
|
||||
goto out;
|
||||
case EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW:
|
||||
state_new = EXPOSAY_LAYOUT_OVERVIEW;
|
||||
break;
|
||||
default:
|
||||
state_new = exposay_transition_active(shell);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EXPOSAY_TARGET_SWITCH:
|
||||
do_switch = 1; /* fallthrough */
|
||||
case EXPOSAY_TARGET_CANCEL:
|
||||
switch (shell->exposay.state_cur) {
|
||||
case EXPOSAY_LAYOUT_INACTIVE:
|
||||
goto out;
|
||||
case EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE:
|
||||
state_new = exposay_set_inactive(shell);
|
||||
break;
|
||||
default:
|
||||
state_new = exposay_transition_inactive(shell, do_switch);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
shell->exposay.state_cur = state_new;
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_set_state(struct desktop_shell *shell, enum exposay_target_state state,
|
||||
struct weston_seat *seat)
|
||||
{
|
||||
shell->exposay.state_target = state;
|
||||
shell->exposay.seat = seat;
|
||||
exposay_check_state(shell);
|
||||
}
|
||||
|
||||
void
|
||||
exposay_binding(struct weston_keyboard *keyboard, enum weston_keyboard_modifier modifier,
|
||||
void *data)
|
||||
{
|
||||
struct desktop_shell *shell = data;
|
||||
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_OVERVIEW, keyboard->seat);
|
||||
}
|
||||
|
|
@ -0,0 +1,412 @@
|
|||
/*
|
||||
* Copyright © 2010-2012 Intel Corporation
|
||||
* Copyright © 2011-2012 Collabora, Ltd.
|
||||
* Copyright © 2013 Raspberry Pi Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "shell.h"
|
||||
#include "input-method-unstable-v1-server-protocol.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
struct input_panel_surface {
|
||||
struct wl_resource *resource;
|
||||
struct wl_signal destroy_signal;
|
||||
|
||||
struct desktop_shell *shell;
|
||||
|
||||
struct wl_list link;
|
||||
struct weston_surface *surface;
|
||||
struct weston_view *view;
|
||||
struct wl_listener surface_destroy_listener;
|
||||
|
||||
struct weston_view_animation *anim;
|
||||
|
||||
struct weston_output *output;
|
||||
uint32_t panel;
|
||||
};
|
||||
|
||||
static void
|
||||
input_panel_slide_done(struct weston_view_animation *animation, void *data)
|
||||
{
|
||||
struct input_panel_surface *ipsurf = data;
|
||||
|
||||
ipsurf->anim = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
show_input_panel_surface(struct input_panel_surface *ipsurf)
|
||||
{
|
||||
struct desktop_shell *shell = ipsurf->shell;
|
||||
struct weston_seat *seat;
|
||||
struct weston_surface *focus;
|
||||
float x, y;
|
||||
|
||||
wl_list_for_each(seat, &shell->compositor->seat_list, link) {
|
||||
struct weston_keyboard *keyboard =
|
||||
weston_seat_get_keyboard(seat);
|
||||
|
||||
if (!keyboard || !keyboard->focus)
|
||||
continue;
|
||||
focus = weston_surface_get_main_surface(keyboard->focus);
|
||||
if (!focus)
|
||||
continue;
|
||||
ipsurf->output = focus->output;
|
||||
x = ipsurf->output->x + (ipsurf->output->width - ipsurf->surface->width) / 2;
|
||||
y = ipsurf->output->y + ipsurf->output->height - ipsurf->surface->height;
|
||||
weston_view_set_position(ipsurf->view, x, y);
|
||||
}
|
||||
|
||||
weston_layer_entry_insert(&shell->input_panel_layer.view_list,
|
||||
&ipsurf->view->layer_link);
|
||||
weston_view_geometry_dirty(ipsurf->view);
|
||||
weston_view_update_transform(ipsurf->view);
|
||||
ipsurf->surface->is_mapped = true;
|
||||
ipsurf->view->is_mapped = true;
|
||||
weston_surface_damage(ipsurf->surface);
|
||||
|
||||
if (ipsurf->anim)
|
||||
weston_view_animation_destroy(ipsurf->anim);
|
||||
|
||||
ipsurf->anim =
|
||||
weston_slide_run(ipsurf->view,
|
||||
ipsurf->surface->height * 0.9, 0,
|
||||
input_panel_slide_done, ipsurf);
|
||||
}
|
||||
|
||||
static void
|
||||
show_input_panels(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct desktop_shell *shell =
|
||||
container_of(listener, struct desktop_shell,
|
||||
show_input_panel_listener);
|
||||
struct input_panel_surface *ipsurf, *next;
|
||||
|
||||
shell->text_input.surface = (struct weston_surface*)data;
|
||||
|
||||
if (shell->showing_input_panels)
|
||||
return;
|
||||
|
||||
shell->showing_input_panels = true;
|
||||
|
||||
if (!shell->locked)
|
||||
weston_layer_set_position(&shell->input_panel_layer,
|
||||
WESTON_LAYER_POSITION_TOP_UI);
|
||||
|
||||
wl_list_for_each_safe(ipsurf, next,
|
||||
&shell->input_panel.surfaces, link) {
|
||||
if (ipsurf->surface->width == 0)
|
||||
continue;
|
||||
|
||||
show_input_panel_surface(ipsurf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
hide_input_panels(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct desktop_shell *shell =
|
||||
container_of(listener, struct desktop_shell,
|
||||
hide_input_panel_listener);
|
||||
struct weston_view *view, *next;
|
||||
|
||||
if (!shell->showing_input_panels)
|
||||
return;
|
||||
|
||||
shell->showing_input_panels = false;
|
||||
|
||||
if (!shell->locked)
|
||||
weston_layer_unset_position(&shell->input_panel_layer);
|
||||
|
||||
wl_list_for_each_safe(view, next,
|
||||
&shell->input_panel_layer.view_list.link,
|
||||
layer_link.link)
|
||||
weston_view_unmap(view);
|
||||
}
|
||||
|
||||
static void
|
||||
update_input_panels(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct desktop_shell *shell =
|
||||
container_of(listener, struct desktop_shell,
|
||||
update_input_panel_listener);
|
||||
|
||||
memcpy(&shell->text_input.cursor_rectangle, data, sizeof(pixman_box32_t));
|
||||
}
|
||||
|
||||
static int
|
||||
input_panel_get_label(struct weston_surface *surface, char *buf, size_t len)
|
||||
{
|
||||
return snprintf(buf, len, "input panel");
|
||||
}
|
||||
|
||||
static void
|
||||
input_panel_committed(struct weston_surface *surface, int32_t sx, int32_t sy)
|
||||
{
|
||||
struct input_panel_surface *ip_surface = surface->committed_private;
|
||||
struct desktop_shell *shell = ip_surface->shell;
|
||||
struct weston_view *view;
|
||||
float x, y;
|
||||
|
||||
if (surface->width == 0)
|
||||
return;
|
||||
|
||||
if (ip_surface->panel) {
|
||||
view = get_default_view(shell->text_input.surface);
|
||||
if (view == NULL)
|
||||
return;
|
||||
x = view->geometry.x + shell->text_input.cursor_rectangle.x2;
|
||||
y = view->geometry.y + shell->text_input.cursor_rectangle.y2;
|
||||
} else {
|
||||
x = ip_surface->output->x + (ip_surface->output->width - surface->width) / 2;
|
||||
y = ip_surface->output->y + ip_surface->output->height - surface->height;
|
||||
}
|
||||
|
||||
weston_view_set_position(ip_surface->view, x, y);
|
||||
|
||||
if (!weston_surface_is_mapped(surface) && shell->showing_input_panels)
|
||||
show_input_panel_surface(ip_surface);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_input_panel_surface(struct input_panel_surface *input_panel_surface)
|
||||
{
|
||||
wl_signal_emit(&input_panel_surface->destroy_signal, input_panel_surface);
|
||||
|
||||
wl_list_remove(&input_panel_surface->surface_destroy_listener.link);
|
||||
wl_list_remove(&input_panel_surface->link);
|
||||
|
||||
input_panel_surface->surface->committed = NULL;
|
||||
weston_surface_set_label_func(input_panel_surface->surface, NULL);
|
||||
weston_view_destroy(input_panel_surface->view);
|
||||
|
||||
free(input_panel_surface);
|
||||
}
|
||||
|
||||
static struct input_panel_surface *
|
||||
get_input_panel_surface(struct weston_surface *surface)
|
||||
{
|
||||
if (surface->committed == input_panel_committed) {
|
||||
return surface->committed_private;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_panel_handle_surface_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct input_panel_surface *ipsurface = container_of(listener,
|
||||
struct input_panel_surface,
|
||||
surface_destroy_listener);
|
||||
|
||||
if (ipsurface->resource) {
|
||||
wl_resource_destroy(ipsurface->resource);
|
||||
} else {
|
||||
destroy_input_panel_surface(ipsurface);
|
||||
}
|
||||
}
|
||||
|
||||
static struct input_panel_surface *
|
||||
create_input_panel_surface(struct desktop_shell *shell,
|
||||
struct weston_surface *surface)
|
||||
{
|
||||
struct input_panel_surface *input_panel_surface;
|
||||
|
||||
input_panel_surface = calloc(1, sizeof *input_panel_surface);
|
||||
if (!input_panel_surface)
|
||||
return NULL;
|
||||
|
||||
surface->committed = input_panel_committed;
|
||||
surface->committed_private = input_panel_surface;
|
||||
weston_surface_set_label_func(surface, input_panel_get_label);
|
||||
|
||||
input_panel_surface->shell = shell;
|
||||
|
||||
input_panel_surface->surface = surface;
|
||||
input_panel_surface->view = weston_view_create(surface);
|
||||
|
||||
wl_signal_init(&input_panel_surface->destroy_signal);
|
||||
input_panel_surface->surface_destroy_listener.notify = input_panel_handle_surface_destroy;
|
||||
wl_signal_add(&surface->destroy_signal,
|
||||
&input_panel_surface->surface_destroy_listener);
|
||||
|
||||
wl_list_init(&input_panel_surface->link);
|
||||
|
||||
return input_panel_surface;
|
||||
}
|
||||
|
||||
static void
|
||||
input_panel_surface_set_toplevel(struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *output_resource,
|
||||
uint32_t position)
|
||||
{
|
||||
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);
|
||||
|
||||
head = weston_head_from_resource(output_resource);
|
||||
input_panel_surface->output = head->output;
|
||||
input_panel_surface->panel = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
input_panel_surface_set_overlay_panel(struct wl_client *client,
|
||||
struct wl_resource *resource)
|
||||
{
|
||||
struct input_panel_surface *input_panel_surface =
|
||||
wl_resource_get_user_data(resource);
|
||||
struct desktop_shell *shell = input_panel_surface->shell;
|
||||
|
||||
wl_list_insert(&shell->input_panel.surfaces,
|
||||
&input_panel_surface->link);
|
||||
|
||||
input_panel_surface->panel = 1;
|
||||
}
|
||||
|
||||
static const struct zwp_input_panel_surface_v1_interface input_panel_surface_implementation = {
|
||||
input_panel_surface_set_toplevel,
|
||||
input_panel_surface_set_overlay_panel
|
||||
};
|
||||
|
||||
static void
|
||||
destroy_input_panel_surface_resource(struct wl_resource *resource)
|
||||
{
|
||||
struct input_panel_surface *ipsurf =
|
||||
wl_resource_get_user_data(resource);
|
||||
|
||||
destroy_input_panel_surface(ipsurf);
|
||||
}
|
||||
|
||||
static void
|
||||
input_panel_get_input_panel_surface(struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
uint32_t id,
|
||||
struct wl_resource *surface_resource)
|
||||
{
|
||||
struct weston_surface *surface =
|
||||
wl_resource_get_user_data(surface_resource);
|
||||
struct desktop_shell *shell = wl_resource_get_user_data(resource);
|
||||
struct input_panel_surface *ipsurf;
|
||||
|
||||
if (get_input_panel_surface(surface)) {
|
||||
wl_resource_post_error(surface_resource,
|
||||
WL_DISPLAY_ERROR_INVALID_OBJECT,
|
||||
"wl_input_panel::get_input_panel_surface already requested");
|
||||
return;
|
||||
}
|
||||
|
||||
ipsurf = create_input_panel_surface(shell, surface);
|
||||
if (!ipsurf) {
|
||||
wl_resource_post_error(surface_resource,
|
||||
WL_DISPLAY_ERROR_INVALID_OBJECT,
|
||||
"surface->committed already set");
|
||||
return;
|
||||
}
|
||||
|
||||
ipsurf->resource =
|
||||
wl_resource_create(client,
|
||||
&zwp_input_panel_surface_v1_interface,
|
||||
1,
|
||||
id);
|
||||
wl_resource_set_implementation(ipsurf->resource,
|
||||
&input_panel_surface_implementation,
|
||||
ipsurf,
|
||||
destroy_input_panel_surface_resource);
|
||||
}
|
||||
|
||||
static const struct zwp_input_panel_v1_interface input_panel_implementation = {
|
||||
input_panel_get_input_panel_surface
|
||||
};
|
||||
|
||||
static void
|
||||
unbind_input_panel(struct wl_resource *resource)
|
||||
{
|
||||
struct desktop_shell *shell = wl_resource_get_user_data(resource);
|
||||
|
||||
shell->input_panel.binding = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
bind_input_panel(struct wl_client *client,
|
||||
void *data, uint32_t version, uint32_t id)
|
||||
{
|
||||
struct desktop_shell *shell = data;
|
||||
struct wl_resource *resource;
|
||||
|
||||
resource = wl_resource_create(client,
|
||||
&zwp_input_panel_v1_interface, 1, id);
|
||||
|
||||
if (shell->input_panel.binding == NULL) {
|
||||
wl_resource_set_implementation(resource,
|
||||
&input_panel_implementation,
|
||||
shell, unbind_input_panel);
|
||||
shell->input_panel.binding = resource;
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
|
||||
"interface object already bound");
|
||||
}
|
||||
|
||||
void
|
||||
input_panel_destroy(struct desktop_shell *shell)
|
||||
{
|
||||
wl_list_remove(&shell->show_input_panel_listener.link);
|
||||
wl_list_remove(&shell->hide_input_panel_listener.link);
|
||||
}
|
||||
|
||||
int
|
||||
input_panel_setup(struct desktop_shell *shell)
|
||||
{
|
||||
struct weston_compositor *ec = shell->compositor;
|
||||
|
||||
shell->show_input_panel_listener.notify = show_input_panels;
|
||||
wl_signal_add(&ec->show_input_panel_signal,
|
||||
&shell->show_input_panel_listener);
|
||||
shell->hide_input_panel_listener.notify = hide_input_panels;
|
||||
wl_signal_add(&ec->hide_input_panel_signal,
|
||||
&shell->hide_input_panel_listener);
|
||||
shell->update_input_panel_listener.notify = update_input_panels;
|
||||
wl_signal_add(&ec->update_input_panel_signal,
|
||||
&shell->update_input_panel_listener);
|
||||
|
||||
wl_list_init(&shell->input_panel.surfaces);
|
||||
|
||||
if (wl_global_create(shell->compositor->wl_display,
|
||||
&zwp_input_panel_v1_interface, 1,
|
||||
shell, bind_input_panel) == NULL)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* Copyright © 2010-2012 Intel Corporation
|
||||
* Copyright © 2011-2012 Collabora, Ltd.
|
||||
* Copyright © 2013 Raspberry Pi Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/xwayland-api.h>
|
||||
|
||||
#include "weston-desktop-shell-server-protocol.h"
|
||||
|
||||
enum animation_type {
|
||||
ANIMATION_NONE,
|
||||
|
||||
ANIMATION_ZOOM,
|
||||
ANIMATION_FADE,
|
||||
ANIMATION_DIM_LAYER,
|
||||
};
|
||||
|
||||
enum fade_type {
|
||||
FADE_IN,
|
||||
FADE_OUT
|
||||
};
|
||||
|
||||
enum exposay_target_state {
|
||||
EXPOSAY_TARGET_OVERVIEW, /* show all windows */
|
||||
EXPOSAY_TARGET_CANCEL, /* return to normal, same focus */
|
||||
EXPOSAY_TARGET_SWITCH, /* return to normal, switch focus */
|
||||
};
|
||||
|
||||
enum exposay_layout_state {
|
||||
EXPOSAY_LAYOUT_INACTIVE = 0, /* normal desktop */
|
||||
EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE, /* in transition to normal */
|
||||
EXPOSAY_LAYOUT_OVERVIEW, /* show all windows */
|
||||
EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW, /* in transition to all windows */
|
||||
};
|
||||
|
||||
struct exposay_output {
|
||||
int num_surfaces;
|
||||
int grid_size;
|
||||
int surface_size;
|
||||
int padding_inner;
|
||||
};
|
||||
|
||||
struct exposay {
|
||||
/* XXX: Make these exposay_surfaces. */
|
||||
struct weston_view *focus_prev;
|
||||
struct weston_view *focus_current;
|
||||
struct weston_view *clicked;
|
||||
struct workspace *workspace;
|
||||
struct weston_seat *seat;
|
||||
|
||||
struct wl_list surface_list;
|
||||
|
||||
struct weston_keyboard_grab grab_kbd;
|
||||
struct weston_pointer_grab grab_ptr;
|
||||
|
||||
enum exposay_target_state state_target;
|
||||
enum exposay_layout_state state_cur;
|
||||
int in_flight; /* number of animations still running */
|
||||
|
||||
int row_current;
|
||||
int column_current;
|
||||
struct exposay_output *cur_output;
|
||||
|
||||
bool mod_pressed;
|
||||
bool mod_invalid;
|
||||
};
|
||||
|
||||
struct focus_surface {
|
||||
struct weston_surface *surface;
|
||||
struct weston_view *view;
|
||||
struct weston_transform workspace_transform;
|
||||
};
|
||||
|
||||
struct workspace {
|
||||
struct weston_layer layer;
|
||||
|
||||
struct wl_list focus_list;
|
||||
struct wl_listener seat_destroyed_listener;
|
||||
|
||||
struct focus_surface *fsurf_front;
|
||||
struct focus_surface *fsurf_back;
|
||||
struct weston_view_animation *focus_animation;
|
||||
};
|
||||
|
||||
struct shell_output {
|
||||
struct desktop_shell *shell;
|
||||
struct weston_output *output;
|
||||
struct exposay_output eoutput;
|
||||
struct wl_listener destroy_listener;
|
||||
struct wl_list link;
|
||||
|
||||
struct weston_surface *panel_surface;
|
||||
struct wl_listener panel_surface_listener;
|
||||
|
||||
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;
|
||||
struct desktop_shell {
|
||||
struct weston_compositor *compositor;
|
||||
struct weston_desktop *desktop;
|
||||
const struct weston_xwayland_surface_api *xwayland_surface_api;
|
||||
|
||||
struct wl_listener idle_listener;
|
||||
struct wl_listener wake_listener;
|
||||
struct wl_listener transform_listener;
|
||||
struct wl_listener resized_listener;
|
||||
struct wl_listener destroy_listener;
|
||||
struct wl_listener show_input_panel_listener;
|
||||
struct wl_listener hide_input_panel_listener;
|
||||
struct wl_listener update_input_panel_listener;
|
||||
|
||||
struct weston_layer fullscreen_layer;
|
||||
struct weston_layer panel_layer;
|
||||
struct weston_layer background_layer;
|
||||
struct weston_layer lock_layer;
|
||||
struct weston_layer input_panel_layer;
|
||||
|
||||
struct wl_listener pointer_focus_listener;
|
||||
struct weston_surface *grab_surface;
|
||||
|
||||
struct {
|
||||
struct wl_client *client;
|
||||
struct wl_resource *desktop_shell;
|
||||
struct wl_listener client_destroy_listener;
|
||||
|
||||
unsigned deathcount;
|
||||
struct timespec deathstamp;
|
||||
} child;
|
||||
|
||||
bool locked;
|
||||
bool showing_input_panels;
|
||||
bool prepare_event_sent;
|
||||
|
||||
struct text_backend *text_backend;
|
||||
|
||||
struct {
|
||||
struct weston_surface *surface;
|
||||
pixman_box32_t cursor_rectangle;
|
||||
} text_input;
|
||||
|
||||
struct weston_surface *lock_surface;
|
||||
struct wl_listener lock_surface_listener;
|
||||
|
||||
struct {
|
||||
struct wl_array array;
|
||||
unsigned int current;
|
||||
unsigned int num;
|
||||
|
||||
struct wl_list client_list;
|
||||
|
||||
struct weston_animation animation;
|
||||
struct wl_list anim_sticky_list;
|
||||
int anim_dir;
|
||||
struct timespec anim_timestamp;
|
||||
double anim_current;
|
||||
struct workspace *anim_from;
|
||||
struct workspace *anim_to;
|
||||
} workspaces;
|
||||
|
||||
struct {
|
||||
struct wl_resource *binding;
|
||||
struct wl_list surfaces;
|
||||
} input_panel;
|
||||
|
||||
struct exposay exposay;
|
||||
|
||||
bool allow_zap;
|
||||
uint32_t binding_modifier;
|
||||
uint32_t exposay_modifier;
|
||||
enum animation_type win_animation_type;
|
||||
enum animation_type win_close_animation_type;
|
||||
enum animation_type startup_animation_type;
|
||||
enum animation_type focus_animation_type;
|
||||
|
||||
struct weston_layer minimized_layer;
|
||||
|
||||
struct wl_listener seat_create_listener;
|
||||
struct wl_listener output_create_listener;
|
||||
struct wl_listener output_move_listener;
|
||||
struct wl_list output_list;
|
||||
|
||||
enum weston_desktop_shell_panel_position panel_position;
|
||||
|
||||
char *client;
|
||||
|
||||
struct timespec startup_time;
|
||||
};
|
||||
|
||||
struct weston_output *
|
||||
get_default_output(struct weston_compositor *compositor);
|
||||
|
||||
struct weston_view *
|
||||
get_default_view(struct weston_surface *surface);
|
||||
|
||||
struct shell_surface *
|
||||
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);
|
||||
|
||||
void
|
||||
activate(struct desktop_shell *shell, struct weston_view *view,
|
||||
struct weston_seat *seat, uint32_t flags);
|
||||
|
||||
void
|
||||
exposay_binding(struct weston_keyboard *keyboard,
|
||||
enum weston_keyboard_modifier modifier,
|
||||
void *data);
|
||||
int
|
||||
input_panel_setup(struct desktop_shell *shell);
|
||||
void
|
||||
input_panel_destroy(struct desktop_shell *shell);
|
||||
|
||||
typedef void (*shell_for_each_layer_func_t)(struct desktop_shell *,
|
||||
struct weston_layer *, void *);
|
||||
|
||||
void
|
||||
shell_for_each_layer(struct desktop_shell *shell,
|
||||
shell_for_each_layer_func_t func,
|
||||
void *data);
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright © 2015 Samsung Electronics Co., 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
@mainpage
|
||||
|
||||
- @ref zunitc - Simple test framework
|
||||
|
||||
@section tools_overview Overview
|
||||
|
||||
The tools area currently consists of one sub-project (@ref zunitc) that is
|
||||
refined from the prior single weston/tests source folder.
|
||||
|
||||
@subsection tools_overview_old Old Code Organization
|
||||
|
||||
The original 'tests' folder contained basic weston testing with an
|
||||
integrated test runner framework. Over time things progressed to the
|
||||
stage where splitting apart into discrete layers was warranted.
|
||||
|
||||
@dotfile tools_arch_old.gv "Original test code organization"
|
||||
|
||||
@subsection tools_overview_new New Code Organization
|
||||
|
||||
The test code that is not weston-specific gets split out to a separate
|
||||
folder and/or folders.
|
||||
|
||||
@dotfile tools_arch_new.gv "Refactored test code organization"
|
||||
|
||||
*/
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
PROJECT_NAME = "Tool Internals"
|
||||
OUTPUT_DIRECTORY = @top_builddir@/docs/developer
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||
EXTRACT_ALL = YES
|
||||
INPUT = \
|
||||
@top_srcdir@/doc/doxygen/devtools.dox \
|
||||
@top_srcdir@/tools/zunitc
|
||||
RECURSIVE = YES
|
||||
GENERATE_LATEX = NO
|
||||
DOTFILE_DIRS = @top_srcdir@/doc/doxygen
|
||||
STRIP_FROM_PATH = @top_srcdir@
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright © 2015 Samsung Electronics Co., 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
@mainpage
|
||||
|
||||
- @ref zunitc - Simple test framework
|
||||
|
||||
*/
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
PROJECT_NAME = "Tools"
|
||||
OUTPUT_DIRECTORY = @top_builddir@/docs/tools
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||
INPUT = \
|
||||
@top_srcdir@/doc/doxygen/tools.dox \
|
||||
@top_srcdir@/tools/zunitc/doc/zunitc.dox \
|
||||
@top_srcdir@/tools/zunitc/inc/zunitc/zunitc.h
|
||||
GENERATE_LATEX = NO
|
||||
DOTFILE_DIRS = @top_srcdir@/doc/doxygen
|
||||
STRIP_FROM_PATH = @top_srcdir@
|
||||