Compare commits

..

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

548 changed files with 14240 additions and 192937 deletions

View file

@ -1,82 +0,0 @@
AlignAfterOpenBracket: Align
# This option we want but it's frequently broken and causes bad
# misalignment. The canary is wheel_click_count_parser, if that works
# we can actually enable it.
# AlignArrayOfStructures: Left
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignConsecutiveMacros: true
AlignConsecutiveShortCaseStatements:
Enabled: true
AcrossEmptyLines: false
AcrossComments: true
AlignCaseColons: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: true
BinPackArguments: false
BinPackParameters: OnePerLine
BraceWrapping:
AfterFunction: true
BreakAfterReturnType: All
BreakBeforeBraces: Custom
BreakStringLiterals: false
ColumnLimit: 88
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^(<|")config\.h(>|")'
Priority: 0
SortPriority: 0
- Regex: '^<.*'
Priority: 1
SortPriority: 0
- Regex: '^"util-.*'
Priority: 2
SortPriority: 0
- Regex: '.*'
Priority: 3
SortPriority: 0
IndentCaseLabels: false
IndentGotoLabels: false
IndentWidth: 8
MaxEmptyLinesToKeep: 1
PointerAlignment: Right
ReflowComments: true
RemoveEmptyLinesInUnwrappedLines: true
RemoveParentheses: MultipleParentheses
RemoveSemicolon: true
SkipMacroDefinitionBody: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeParens: ControlStatementsExceptControlMacros
SpacesInContainerLiterals: true
SpacesInParens: Custom
TabWidth: 8
UseTab: ForContinuationAndIndentation
ForEachMacros:
- ARRAY_FOR_EACH
- list_for_each
- list_for_each_safe
- tp_for_each_touch
- range_for_each
- litest_log_group
- litest_with_logcapture
- litest_with_parameters
- litest_with_event_frame
- udev_list_entry_foreach
# END_TEST is defined as something that enforces a line break
Macros: [ "CASE_RETURN_STRING(s)=case s: return s", "START_TEST(s)=static void s(void)", "END_TEST=enum foo;"]

View file

@ -1 +0,0 @@
include/**/*

View file

@ -1,7 +0,0 @@
# optin.core.unix.Malloc: disabled so we can use __attribute__((cleanup)) without leak complaints
# optin.core.EnumCastOutOfRange: disabled because we use a lot of "wrong" enum values for testing
# and internally and don't want those values leak into the public API
Checks: >
-clang-analyzer-unix.Malloc,
-clang-analyzer-optin.core.EnumCastOutOfRange
WarningsAsErrors: '*'

View file

@ -1 +0,0 @@
include/*

View file

@ -1,4 +0,0 @@
((nil . ((indent-tabs-mode . t)
(tab-width . 8)
(fill-column . 80)))
(c-mode . ((c-basic-offset . 8))))

View file

@ -1,25 +0,0 @@
# https://editorconfig.org/
root = true
[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.{c,h}]
indent_size = 8
indent_style = tab
[*.py]
indent_size = 4
indent_style = space
[{meson.build,meson_options.txt}]
indent_size = 8
indent_style = tab
[*.sym]
indent_size = 8
indent_style = tab

View file

@ -1,21 +0,0 @@
# This file contains revisions to be ignored by git blame.
# These revisions are expected to be formatting-only changes.
#
# Calling `git blame --ignore-revs-file .git-blame-ignore-revs` will
# tell git blame to ignore changes made by these revisions when assigning
# assigning blame, as if the change never happened.
#
# You can enable this as a default for your local repository by running
# `git config blame.ignoreRevsFile .git-blame-ignore-revs`
# Important: if you do this, then switch to a branch without this file,
# `git blame` will fail with an error.
#
# Run clang-format over the code
2a1095924b0be60f822bc0ff20d567e209a9db73
# Add trailing commas to prevent clang-format oddities
aebf3cd4915adc576cf055926caa621e098ec32b
# Fix some inconsistent whitespace.
60abf1575560cb8e2220ae48a67e7f74b5dc70dd

38
.gitignore vendored
View file

@ -1,12 +1,30 @@
*.o
*.pc
*.la
*.lo
*.swp
*~
*.sig
*.tar.*
*.announce
*.patch
*.rej
*.trs
*.gcda
*.gcno
Makefile
Makefile.in
aclocal.m4
autom4te.cache/
config.guess
config.h
config.h.in
config.log
config.status
config.sub
configure
depcomp
install-sh
libtool
ltmain.sh
missing
stamp-h1
.libs/
.deps/
src/libinput-version.h
doc/libinput.doxygen
doc/html
tags
*.gnuplot
test/test-*
test-driver

File diff suppressed because it is too large Load diff

View file

@ -1,803 +0,0 @@
# vim: set expandtab shiftwidth=2 tabstop=8 textwidth=0 filetype=yaml:
{# You're looking at the template here, so you can ignore the below
warning. This is the right file to edit #}
########################################
# #
# THIS FILE IS GENERATED, DO NOT EDIT #
# #
########################################
# To change the gitlab CI, edit .gitlab-ci/ci.template and/or .gitlab-ci/config.yaml
# and run ci-fairy generate-template. For details, see
# https://freedesktop.pages.freedesktop.org/ci-templates/ci-fairy.html#templating-gitlab-ci-yml
# This is a bit complicated for two reasons:
# - we really want to run dnf/apt/... only once, updating on the test runner for
# each job takes forever. So we create a container image for each distribution
# tested, then run the tests on this container image.
#
# This is handled by the ci-templates, ensuring containers are only rebuilt
# when the TAG changes.
#
# - GitLab only allows one script: set per job but we have a bunch of commands
# we need to re-run for each build (meson && ninja && etc). YAML cannot merge
# arrays so we're screwed.
#
# So instead we use a default_build template and override everything with
# variables. The only two variables that matter:
# MESON_ARGS=-Denable-something=true
# NINJA_ARGS=dist ... to run 'ninja -C builddir dist'
# Note that you cannot use scripts: in any target if you expect default_build
# to work.
#
#
# All jobs must follow the naming scheme of
# <distribution>:<version>@activity:
# e.g. fedora:31@build-default
.templates_sha: &template_sha c6aeb16f86e32525fa630fb99c66c4f3e62fc3cb
include:
- project: 'freedesktop/ci-templates'
ref: *template_sha
file:
- '/templates/ci-fairy.yml'
{% for distro in distributions|sort(attribute="name") %}
# {{ distro.name.capitalize() }} container builder template
- '/templates/{{distro.name}}.yml'
{% endfor %}
workflow:
rules:
# do not duplicate pipelines on merge pipelines
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
when: never
# merge pipeline
- if: &is-merge-attempt $GITLAB_USER_LOGIN == "marge-bot" && $CI_PIPELINE_SOURCE == "merge_request_event"
variables:
FDO_RUNNER_JOB_PRIORITY_TAG_X86_64: priority:high
FDO_RUNNER_JOB_PRIORITY_TAG_X86_64_KVM: priority:high-kvm
FDO_RUNNER_JOB_PRIORITY_TAG_AARCH64: priority:high-aarch64
# post-merge pipeline
- if: &is-post-merge $GITLAB_USER_LOGIN == "marge-bot" && $CI_PIPELINE_SOURCE == "push"
variables:
FDO_RUNNER_JOB_PRIORITY_TAG_X86_64: priority:high
FDO_RUNNER_JOB_PRIORITY_TAG_X86_64_KVM: priority:high-kvm
FDO_RUNNER_JOB_PRIORITY_TAG_AARCH64: priority:high-aarch64
# Pre-merge pipeline
- if: &is-pre-merge $CI_PIPELINE_SOURCE == "merge_request_event"
# Push to a branch on a fork
- if: $CI_COMMIT_BRANCH
stages:
- sanity check # CI/commit checks
- prep # prep work like rebuilding the container images if there is a change
- build # for actually building and testing things in a container
- test-suite # for running the test suite in a VM
- test-suite-no-libwacom # for running the test suite in a VM (libwacom disabled)
- valgrind # for running the test suite under valgrind in a VM
- distro # distribs test
- deploy # trigger wayland's website generation
- container_clean # clean up unused container images (scheduled jobs only)
variables:
###############################################################################
# This is the list of packages required to build libinput with the default #
# configuration. #
# #
# Run dnf install/apt-get install/.. with the list of packages for your #
# distribution #
# #
# See the documentation here: #
# https://wayland.freedesktop.org/libinput/doc/latest/building.html #
###############################################################################
{% for distro in distributions %}
{{"%-17s" | format(distro.name.upper() + '_PACKAGES:')}} '{{ distro.packages|join(' ')}}'
{% endfor %}
############################ end of package lists #############################
# these tags should be updated each time the list of packages is updated
# changing these will force rebuilding the associated image
# Note: these tags have no meaning and are not tied to a particular
# libinput version
{% for distro in distributions %}
{{"%-13s"| format(distro.name.upper() + '_TAG:')}}'{{distro.tag}}'
{% endfor %}
FDO_UPSTREAM_REPO: libinput/libinput
MESON_BUILDDIR: "build dir"
NINJA_ARGS: ''
MESON_ARGS: ''
MESON_TEST_ARGS: '--no-suite=hardware'
# udev isn't available/working properly in the containers
UDEV_NOT_AVAILABLE: 1
GIT_DEPTH: 1
# Default priority for non-merge pipelines
FDO_RUNNER_JOB_PRIORITY_TAG_X86_64: "" # Empty tags are ignored by gitlab
FDO_RUNNER_JOB_PRIORITY_TAG_X86_64_KVM: kvm
FDO_RUNNER_JOB_PRIORITY_TAG_AARCH64: aarch64
.policy:
retry:
max: 2
when:
- runner_system_failure
- stuck_or_timeout_failure
# cancel run when a newer version is pushed to the branch
interruptible: true
dependencies: []
.policy-retry-on-failure:
retry:
max: 1
when:
- runner_system_failure
- stuck_or_timeout_failure
# cancel run when a newer version is pushed to the branch
interruptible: true
dependencies: []
.default_artifacts:
artifacts:
name: "meson-logs-$CI_JOB_NAME"
when: always
expire_in: 1 week
paths:
- $MESON_BUILDDIR/meson-logs
- $MESON_BUILDDIR/valgrind.*.log
reports:
junit: $MESON_BUILDDIR/*junit*.xml
.fdo-runner-tags:
tags:
- $FDO_RUNNER_JOB_PRIORITY_TAG_X86_64
#################################################################
# #
# sanity check stage #
# #
#################################################################
fail-if-fork-is-not-public:
extends:
- .fdo-runner-tags
stage: sanity check
script:
- |
if [ $CI_PROJECT_VISIBILITY != "public" ]; then
echo "*************************************************************************************"
echo "Project visibility must be set to 'public'"
echo "Change this in $CI_PROJECT_URL/edit under 'Visibility, project features, permissions'"
echo "*************************************************************************************"
exit 1
fi
except:
- main@libinput/libinput
# Re-generate the CI script and make sure it's the one currently checked in
# If this job fails, re-generate the gitlab-ci.yml script, see
# $SRCDIR/.gitlab-ci/generate-gitlab-ci.py
#
check-ci-script:
extends:
- .fdo.ci-fairy
- .fdo-runner-tags
stage: sanity check
script:
- ci-fairy generate-template --verify && exit 0 || true
- >
printf "%s\n" \
"Committed gitlab-ci.yml differs from generated gitlab-ci.yml. Please verify" \
"https://wayland.freedesktop.org/libinput/doc/latest/contributing.html"
- exit 1
#
# Verify that commit messages are as expected, etc.
#
check-commit:
extends:
- .fdo.ci-fairy
- .fdo-runner-tags
stage: sanity check
script:
- ci-fairy -vv check-commits --junit-xml=results.xml && exit 0 || true
- >
printf "%s\n" \
"Error checking commit format. Please verify" \
"https://wayland.freedesktop.org/libinput/doc/latest/contributing.html"
- exit 1
except:
- main@libinput/libinput
variables:
GIT_DEPTH: 100
artifacts:
reports:
junit: results.xml
#
# Check for trailing whitespaces
#
check-whitespace:
extends:
- .fdo.ci-fairy
- .fdo-runner-tags
stage: sanity check
script:
- .gitlab-ci/whitespace-check.py $(git ls-files)
#
# pre-commit hooks
#
pre-commit-hooks:
extends:
- .fdo.ci-fairy
- .fdo-runner-tags
stage: sanity check
script:
- python3 -m venv venv
- source venv/bin/activate
- pip3 install pre-commit
- pre-commit run --all-files
- git diff --exit-code || (echo "ERROR - Code style errors found, please fix" && false)
#################################################################
# #
# prep stage #
# #
#################################################################
{% for distro in distributions %}
{% for version in distro.versions %}
{{distro.name}}:{{version}}@container-prep:
extends:
{% if distro.qemu_based %}
- .fdo.qemu-build@{{distro.name}}
{% else %}
- .fdo.container-build@{{distro.name}}
{% endif %}
- .policy
- .fdo-runner-tags
{% if distro.qemu_based %}
tags:
- $FDO_RUNNER_JOB_PRIORITY_TAG_X86_64_KVM
{% endif %}
stage: prep
variables:
GIT_STRATEGY: none
FDO_DISTRIBUTION_VERSION: '{{version}}'
FDO_DISTRIBUTION_PACKAGES: ${{distro.name.upper()}}_PACKAGES
FDO_DISTRIBUTION_TAG: ${{distro.name.upper()}}_TAG
{% endfor %}
{% endfor %}
#################################################################
# #
# container clean stage #
# run during the clean stage #
# #
#################################################################
#
# This stage will look for the container images we currently have in
# the registry and will remove any that are not tagged with the provided
# $container_image:$tag
#
.container-clean:
extends:
- .policy
- .fdo.ci-fairy
- .fdo-runner-tags
stage: container_clean
script:
# Go to your Profile, Settings, Access Tokens
# Create a personal token with 'api' scope, copy the value.
# Go to CI/CD, Schedules, schedule a new monthly job (or edit the existing one)
# Define a variable of type File named AUTHFILE. Content is that token
# value.
- ci-fairy -v --authfile $AUTHFILE delete-image
--repository $FDO_DISTRIBUTION_NAME/$FDO_DISTRIBUTION_VERSION
--exclude-tag $FDO_DISTRIBUTION_TAG
allow_failure: true
only:
- schedules
{% for distro in distributions %}
{% for version in distro.versions %}
{{distro.name}}:{{version}}@container-clean:
extends:
- .policy
- .container-clean
variables:
GIT_STRATEGY: none
CURRENT_CONTAINER_IMAGE: $CI_REGISTRY_IMAGE/{{distro.name}}/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG
FDO_DISTRIBUTION_VERSION: '{{version}}'
FDO_DISTRIBUTION_TAG: ${{distro.name.upper()}}_TAG
{% endfor %}
{% endfor %}
#################################################################
# #
# build stage #
# #
#################################################################
.build@template:
extends:
- .policy
- .default_artifacts
- .fdo-runner-tags
stage: build
script:
- .gitlab-ci/meson-build.sh
# Run meson and meson test in the container image through qemu
.build-in-vng@template:
extends:
- .policy
- .default_artifacts
tags:
- $FDO_RUNNER_JOB_PRIORITY_TAG_X86_64_KVM
variables:
MESON_BUILDDIR: build_dir
VNG_KERNEL: {{ vng.kernel }}
script:
# first build in the host container
- .gitlab-ci/meson-build.sh --skip-test
- mkdir -p $MESON_BUILDDIR
- curl -LO $VNG_KERNEL
- export -p > .vngenv
# runs the test suite only
- |
vng --run ./bzImage \
--user root \
--overlay-rwdir=$HOME \
--append HOME=$HOME \
--overlay-rwdir=$(pwd) \
--rwdir=$MESON_BUILDDIR \
--exec "source $PWD/.vngenv; rm $PWD/.vngenv; .gitlab-ci/meson-build.sh --skip-setup --skip-build --run-test"
#
# Fedora
#
.check_tainted: &check_tainted |
# make sure the kernel is not tainted
if [[ "$(ssh localhost -p 5555 cat /proc/sys/kernel/tainted)" -gt 0 ]];
then
echo tainted kernel ;
exit 1 ;
fi
# Run meson and meson test in the qemu image
.build-in-qemu@template:
extends:
- .policy
tags:
- $FDO_RUNNER_JOB_PRIORITY_TAG_X86_64_KVM
variables:
MESON_BUILDDIR: build_dir
script:
# start our vm, no args required
- /app/vmctl start || (echo "Error - Failed to start the VM." && exit 1)
- *check_tainted
- "scp -r $PWD vm:"
- echo "CI_JOB_ID=\"$CI_JOB_ID\"" > sshenv
- echo "CI_JOB_NAME=\"$CI_JOB_NAME\"" >> sshenv
- echo "MESON_ARGS=\"$MESON_ARGS\"" >> sshenv
- echo "MESON_BUILDDIR=\"$MESON_BUILDDIR\"" >> sshenv
- echo "MESON_TEST_ARGS=\"$MESON_TEST_ARGS\"" >> sshenv
- echo "NINJA_ARGS=\"$NINJA_ARGS\"" >> sshenv
- "scp sshenv vm:~/$CI_PROJECT_NAME/.meson_environment"
- /app/vmctl exec "cd $CI_PROJECT_NAME ; .gitlab-ci/meson-build.sh" && touch .success || true
# no matter the results of the tests, we want to fetch the logs
- scp -r vm:$CI_PROJECT_NAME/$MESON_BUILDDIR .
- *check_tainted
- /app/vmctl stop
- if [[ ! -e .success ]] ;
then
exit 1 ;
fi
artifacts:
name: "qemu-meson-logs-$CI_JOB_NAME"
when: always
expire_in: 1 week
paths:
- $MESON_BUILDDIR/meson-logs
- console.out
reports:
junit: $MESON_BUILDDIR/*junit*.xml
# Run in a test suite. Special variables:
# - SUITES: the meson test suites to run, or
# - SUITE_NAMES: all elements will be expanded to libinput-test-suite-$value
# Set one or the other, not both.
.test-suite-vm:
extends:
- .build-in-vng@template
stage: test-suite
variables:
# remove the global --no-suite=hardware
MESON_TEST_ARGS: ''
LITEST_JOBS: 4
before_script:
- if ! [[ -z $SUITE_NAMES ]]; then SUITES=$(echo $SUITE_NAMES | sed 's/\([^ ]*\)/libinput-test-suite-\1/g'); fi
- echo "Testing $SUITES"
- export MESON_TEST_ARGS="$MESON_TEST_ARGS $SUITES"
{# qemu tests are only done for the latest version of any distribution #}
{% for distro in distributions if distro.use_for_qemu_tests %}
{% set version = "{}".format(distro.versions|last()) %}
.{{distro.name}}:{{version}}@test-suite-vm:
extends:
- .fdo.distribution-image@{{distro.name}}
- .test-suite-vm
variables:
FDO_DISTRIBUTION_VERSION: {{version}}
FDO_DISTRIBUTION_TAG: ${{distro.name.upper()}}_TAG
needs:
- "{{distro.name}}:{{version}}@container-prep"
{% for suite in test_suites %}
vm-{{suite.name}}:
extends:
- .{{distro.name}}:{{version}}@test-suite-vm
variables:
SUITE_NAMES: '{{suite.suites|join(' ')}}'
vm-{{suite.name}}-no-libwacom:
extends:
- vm-{{suite.name}}
stage: test-suite-no-libwacom
variables:
MESON_ARGS: '-Dlibwacom=false'
{% endfor %}
{% for suite in test_suites %}
vm-valgrind-{{suite.name}}:
stage: valgrind
extends:
- vm-{{suite.name}}
- .policy-retry-on-failure
variables:
MESON_TEST_ARGS: '--setup=valgrind'
LITEST_JOBS: 0
retry:
max: 2
rules:
- if: $GITLAB_USER_LOGIN != "marge-bot"
{% endfor %}
{% endfor %}{# for if distro.use_for_qemu_tests #}
{% for distro in distributions if distro.use_for_custom_build_tests %}
{% set version = "{}".format(distro.versions|last()) %}
.{{distro.name}}-build@template:
extends:
- .fdo.distribution-image@{{distro.name}}
- .build@template
variables:
FDO_DISTRIBUTION_VERSION: '{{version}}'
FDO_DISTRIBUTION_TAG: ${{distro.name.upper()}}_TAG
needs:
- "{{distro.name}}:{{version}}@container-prep"
default-build-release@{{distro.name}}:{{version}}:
stage: distro
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Dbuildtype=release"
CFLAGS: "-Werror"
clang-tidy@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
NINJA_ARGS: ''
MESON_TEST_ARGS: ''
CC: 'clang'
script:
- .gitlab-ci/meson-build.sh
- ninja -C "$MESON_BUILDDIR" clang-tidy
# Below jobs are build option combinations. We only
# run them on one image, they shouldn't fail on one distro
# when they succeed on another.
build-no-libwacom@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Dlibwacom=false"
build-no-libwacom-nodeps@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Dlibwacom=false"
before_script:
- dnf remove -y libwacom libwacom-devel
build-no-mtdev@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Dmtdev=false"
build-no-mtdev-nodeps@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Dmtdev=false"
before_script:
- dnf remove -y mtdev mtdev-devel
build-no-lua@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Dlua-plugins=disabled"
build-no-lua-nodeps@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Dlua-plugins=disabled"
before_script:
- dnf remove -y lua lua-devel
build-docs@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Ddocumentation=true"
build-no-docs-nodeps@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Ddocumentation=false"
before_script:
- dnf remove -y doxygen graphviz
build-no-debuggui@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Ddebug-gui=false"
build-no-debuggui-nodeps@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Ddebug-gui=false"
before_script:
- dnf remove -y gtk3-devel gtk4-devel
build-no-tests@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Dtests=false"
build-no-tests-nodeps@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_ARGS: "-Dtests=false"
before_script:
- dnf remove -y check-devel
valgrind@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
variables:
MESON_TEST_ARGS: '--suite=valgrind --no-suite=hardware --setup=valgrind'
# Python checks, only run on Fedora
usr-bin-env-python@{{distro.name}}:{{version}}:
extends:
- .{{distro.name}}-build@template
script:
- |
if git grep -l '^#!/usr/bin/python'; then
echo "Use '/usr/bin/env python3' in the above files";
/bin/false
fi
# A job to check we're actually running all test suites in the CI
check-test-suites:
extends:
- .{{distro.name}}-build@template
script:
- meson setup builddir
- meson introspect builddir --test | jq -r '.[].name' | grep 'libinput-test-suite' | sort > meson-testsuites
- |
cat <<EOF > ci-testsuites ;
{% for suite in test_suites %}
{% for name in suite.suites %}
libinput-test-suite-{{name}}
{% endfor %}
{% endfor %}
EOF
- sort -o ci-testsuites ci-testsuites
- diff -u8 -w ci-testsuites meson-testsuites || (echo "Some test suites are not run in the CI" && false)
only:
changes:
- "meson.build"
- ".gitlab-ci.yml"
{% endfor %}
#
# coverity run
#
# This requires the COVERITY_SCAN_TOKEN. Log into scan.coverity.com and get
# the token from the respective project settings page.
# Schedule a pipeline and set a variable COVERITY_SCAN_TOKEN with the token value.
# https://gitlab.freedesktop.org/$CI_PROJECT_PATH/-/pipeline_schedules
# Email from coverity will be sent to the GITLAB_USER_EMAIL that scheduled the
# job.
#
# Coverity ratelimits submissions and the coverity tools download is about
# 700M, do not run this too often.
#
coverity:
extends:
- .fdo.distribution-image@debian
- .policy
- .fdo-runner-tags
stage: build
variables:
FDO_DISTRIBUTION_VERSION: 'stable'
FDO_DISTRIBUTION_TAG: $DEBIAN_TAG
# so git-describe works, or should work
GIT_DEPTH: 200
only:
variables:
- $COVERITY_SCAN_TOKEN
script:
- curl https://scan.coverity.com/download/linux64
-o /tmp/cov-analysis-linux64.tgz
--form project=$CI_PROJECT_NAME
--form token=$COVERITY_SCAN_TOKEN
- tar xfz /tmp/cov-analysis-linux64.tgz
# coverity has special build options in meson, make sure we enable those
- meson coverity-build -Ddocumentation=false -Dcoverity=true
- cov-analysis-linux64-*/bin/cov-build --dir cov-int ninja -C coverity-build
- tar cfz cov-int.tar.gz cov-int
- curl https://scan.coverity.com/builds?project=$CI_PROJECT_NAME
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
--form file=@cov-int.tar.gz --form version="$(git describe --tags)"
--form description="$(git describe --tags) / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID"
artifacts:
name: "coverity-submit-data"
when: always
expire_in: 1 week
paths:
- cov-int.tar.gz
needs:
- "debian:stable@container-prep"
#################################################################
# #
# distro stage #
# #
#################################################################
{% for distro in distributions %}
{% if not distro.qemu_based %}
{% for version in distro.versions %}
{{distro.name}}:{{version}}@default-build:
stage: distro
extends:
- .build@template
- .fdo.distribution-image@{{distro.name}}
variables:
FDO_DISTRIBUTION_VERSION: '{{version}}'
FDO_DISTRIBUTION_TAG: ${{distro.name.upper()}}_TAG
{# Where we have extra_variables defined, add them to the list #}
{% if distro.build is defined and distro.build.extra_variables is defined %}
{% for var in distro.build.extra_variables %}
{{var}}
{% endfor %}
{% endif %}
needs:
- "{{distro.name}}:{{version}}@container-prep"
{% endfor %}
{% else %}
{% set version = "{}".format(distro.versions|last()) %}
{{distro.name}}:{{version}}@default-build:
stage: distro
extends:
- .build-in-qemu@template
- .fdo.distribution-image@{{distro.name}}
variables:
FDO_DISTRIBUTION_VERSION: '{{version}}'
FDO_DISTRIBUTION_TAG: ${{distro.name.upper()}}_TAG
{# Where we have extra_variables defined, add them to the list #}
{% if distro.build is defined and distro.build.extra_variables is defined %}
{% for var in distro.build.extra_variables %}
{{var}}
{% endfor %}
{% endif %}
needs:
- "{{distro.name}}:{{version}}@container-prep"
{% endif %}
{% endfor %}
#################################################################
# #
# deploy stage #
# #
#################################################################
{% for distro in distributions if distro.name == "fedora" %}
{% set version = "{}".format(distro.versions|last()) %}
build rpm:
extends:
- .fdo.distribution-image@fedora
- .policy
- .fdo-runner-tags
stage: deploy
variables:
FDO_DISTRIBUTION_VERSION: '{{version}}'
FDO_DISTRIBUTION_TAG: $FEDORA_TAG
needs:
- "fedora:{{version}}@container-prep"
script:
- meson "$MESON_BUILDDIR"
- VERSION=$(meson introspect "$MESON_BUILDDIR" --projectinfo | jq -r .version)
- sed -e "s/@PIPELINEID@/${CI_PIPELINE_ID}/"
-e "s/@GITVERSION@/${CI_COMMIT_SHA}/"
-e "s/@VERSION@/${VERSION}/" .gitlab-ci/libinput.spec.in > libinput.spec
- git config --local user.name 'gitlab CI'
- git config --local user.email 'noreply@nowhere'
- git add libinput.spec && git commit -m 'Add libinput.spec for build testing' libinput.spec
- cd "$MESON_BUILDDIR"
- meson dist --no-test
- rpmbuild -ta meson-dist/libinput*.tar.xz
{% endfor %}
wayland-web:
stage: deploy
trigger: wayland/wayland.freedesktop.org
variables:
MESON_ARGS: '-Ddocumentation=true -Ddebug-gui=false -Dlibwacom=false -Dtests=false'
MESON_BUILDDIR: 'builddir'
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: never
- if: '$CI_COMMIT_BRANCH == "main" && $GITLAB_USER_LOGIN != "marge-bot" && $CI_PROJECT_PATH == $FDO_UPSTREAM_REPO'
when: on_success
- when: never

View file

@ -1,217 +0,0 @@
# This file contains the configuration for the gitlab ci.
# See the .gitlab-ci/generate-gitlab-ci.py file for more info
#
# We're happy to rebuild all containers when one changes.
.default_tag: &default_tag '2026-01-09.0'
distributions:
- name: fedora
tag: *default_tag
versions:
- '42'
- '43'
use_for_custom_build_tests: true
use_for_qemu_tests: true
packages:
- git-core
- gcc
- gcc-c++
- pkgconf-pkg-config
- meson
- check-devel
- libudev-devel
- libevdev-devel
- doxygen
- graphviz
- python3-sphinx
- python3-recommonmark
- python3-sphinx_rtd_theme
- python3-pytest-xdist
- libwacom-devel
- cairo-devel
- gtk4-devel
- glib2-devel
- mtdev-devel
- diffutils
- wayland-protocols-devel
- black # for the Python black job, optional
- clang # for the clang-tidy build, optional
- clang-tools-extra # for clang-tidy, optional
- jq # for the test suite check job, optional
- rpmdevtools # for the rpm build job, optional
- valgrind # for the valgrind run, optional
# below packages are for the qemu runs, so optional
- systemd-udev # for the qemu run
- qemu-img
- qemu-system-x86-core
- qemu-system-aarch64-core
- jq
- python3-click
- python3-rich
- virtme-ng
- lua-devel
- name: debian
tag: *default_tag
versions:
- 'stable'
packages:
- git
- gcc
- g++
- pkg-config
- meson
- check
- libudev-dev
- libevdev-dev
- doxygen
- graphviz
- python3-sphinx
- python3-recommonmark
- python3-sphinx-rtd-theme
- python3-pytest-xdist
- libwacom-dev
- libcairo2-dev
- libgtk-3-dev
- libglib2.0-dev
- libmtdev-dev
- curl # for the coverity job
- lua5.4-dev
- name: ubuntu
tag: *default_tag
versions:
- '25.10'
packages:
- git
- gcc
- g++
- pkg-config
- meson
- check
- libudev-dev
- libevdev-dev
- doxygen
- graphviz
- python3-sphinx
- python3-recommonmark
- python3-sphinx-rtd-theme
- python3-pytest-xdist
- libwacom-dev
- libcairo2-dev
- libgtk-3-dev
- libglib2.0-dev
- libmtdev-dev
- lua5.4-dev
- name: arch
tag: *default_tag
versions:
- 'rolling'
packages:
- git
- gcc
- pkgconfig
- meson
- check
- libsystemd
- libevdev
- python-pytest-xdist
- libwacom
- gtk4
- mtdev
- diffutils
- lua
build:
extra_variables:
- "MESON_ARGS: '-Ddocumentation=false'" # python-recommonmark is no longer in the repos
- name: alpine
tag: *default_tag
versions:
- 'latest'
packages:
- git
- gcc build-base
- pkgconfig
- meson
- check-dev
- eudev-dev
- libevdev-dev
- libwacom-dev
- cairo-dev
- gtk4.0-dev
- mtdev-dev
- bash
- lua5.4-dev
build:
extra_variables:
- "MESON_ARGS: '-Ddocumentation=false' # alpine does not have python-recommonmark"
# We don't run the tests on alpine. The litest-selftest fails
# for any tcase_add_exit_test/tcase_add_test_raise_signal
# but someone more invested in musl will have to figure that out.
- "MESON_TEST_ARGS: '' # litest-selftest fails on musl"
test_suites:
- name: touchpad
suites:
- touchpad
- name: touchpad_palm
suites:
- touchpad_palm
- name: touchpad_dwt
suites:
- touchpad_dwt
- name: tap
suites:
- touchpad_tap
- name: tap-drag
suites:
- touchpad_tap_drag
- name: tap-palm
suites:
- touchpad_tap_palm
- name: touchpad-buttons
suites:
- touchpad_buttons
- name: tablet
suites:
- tablet
- name: tablet_left_handed
suites:
- tablet_left_handed
- name: tablet_proximity_tip
suites:
- tablet_proximity
- tablet_tip
- name: tablet_eraser
suites:
- tablet_eraser
- name: gestures
suites:
- gestures
- name: backends
suites:
- path
- udev
- name: misc
suites:
- log
- misc
- quirks
- device
- name: other devices
suites:
- keyboard
- pad
- switch
- trackball
- trackpoint
- totem
- touch
- name: pointer
suites:
- pointer
- name: lua
suites:
- lua
vng:
kernel: https://gitlab.freedesktop.org/api/v4/projects/libevdev%2Fhid-tools/packages/generic/kernel-x86_64/v6.14/bzImage

View file

@ -1,149 +0,0 @@
# This specfile should not be used outside the CI
# Its main purpose is to sound alarm if files disappear or are added that
# weren't intended.
%global udevdir %(pkg-config --variable=udevdir udev)
%global pipelineid @PIPELINEID@
%global gitversion @GITVERSION@
Name: libinput
Version: @VERSION@
Release: %{pipelineid}git%{gitversion}%{?dist}
Summary: Input device library
License: MIT
URL: http://www.freedesktop.org/wiki/Software/libinput/
Source0: %{name}-%{version}.tar.xz
# No BuildRequires, we rely on the container setup to have
# all the requires installed
%description
libinput is a library that handles input devices for display servers and other
applications that need to directly deal with input devices.
It provides device detection, device handling, input device event processing
and abstraction so minimize the amount of custom input code the user of
libinput need to provide the common set of functionality that users expect.
%package devel
Summary: Development files for %{name}
Requires: %{name}%{?_isa} = %{version}-%{release}
%description devel
The %{name}-devel package contains libraries and header files for
developing applications that use %{name}.
%package utils
Summary: Utilities and tools for debugging %{name}
Requires: %{name}%{?_isa} = %{version}-%{release}
Requires: python3-pyudev python3-libevdev
%description utils
The %{name}-utils package contains tools to debug hardware and analyze
%{name}.
%package test
Summary: libinput integration test suite
Requires: %{name}%{?_isa} = %{version}-%{release}
%description test
The %{name}-test package contains the libinput test suite. It is not
intended to be run by users.
%prep
%autosetup -S git -n %{name}-%{version}
%build
%meson -Dtests=true \
-Dinstall-tests=true \
-Dudev-dir=%{udevdir}
%meson_build
%install
%meson_install
%post
%{?ldconfig}
%ldconfig_postun
%files
%doc COPYING
%dir %{_sysconfdir}/libinput
%{_libdir}/libinput.so.*
%{udevdir}/libinput-device-group
%{udevdir}/libinput-fuzz-extract
%{udevdir}/libinput-fuzz-to-zero
%{udevdir}/rules.d/80-libinput-device-groups.rules
%{udevdir}/rules.d/90-libinput-fuzz-override.rules
%{_bindir}/libinput
%dir %{_libexecdir}/libinput/
%{_libexecdir}/libinput/libinput-debug-events
%{_libexecdir}/libinput/libinput-list-devices
%{_mandir}/man1/libinput.1*
%{_datadir}/libinput/*.quirks
%dir %{_datadir}/zsh
%dir %{_datadir}/zsh/site-functions
%{_datadir}/zsh/site-functions/*
%{_mandir}/man1/libinput-list-devices.1*
%{_mandir}/man1/libinput-debug-events.1*
%files devel
%{_includedir}/libinput.h
%{_libdir}/libinput.so
%{_libdir}/pkgconfig/libinput.pc
%files utils
%{_libexecdir}/libinput/libinput-debug-gui
%{_libexecdir}/libinput/libinput-debug-tablet
%{_libexecdir}/libinput/libinput-debug-tablet-pad
%{_libexecdir}/libinput/libinput-list-kernel-devices
%{_libexecdir}/libinput/libinput-measure
%{_libexecdir}/libinput/libinput-measure-fuzz
%{_libexecdir}/libinput/libinput-measure-touchpad-tap
%{_libexecdir}/libinput/libinput-measure-touchpad-pressure
%{_libexecdir}/libinput/libinput-measure-touch-size
%{_libexecdir}/libinput/libinput-measure-touchpad-size
%{_libexecdir}/libinput/libinput-quirks
%{_libexecdir}/libinput/libinput-record
%{_libexecdir}/libinput/libinput-replay
%{_libexecdir}/libinput/libinput-analyze
%{_libexecdir}/libinput/libinput-analyze-buttons
%{_libexecdir}/libinput/libinput-analyze-per-slot-delta
%{_libexecdir}/libinput/libinput-analyze-recording
%{_libexecdir}/libinput/libinput-analyze-touch-down-state
%{_mandir}/man1/libinput-debug-gui.1*
%{_mandir}/man1/libinput-debug-tablet.1*
%{_mandir}/man1/libinput-debug-tablet-pad.1*
%{_mandir}/man1/libinput-list-kernel-devices.1*
%{_mandir}/man1/libinput-measure.1*
%{_mandir}/man1/libinput-measure-fuzz.1*
%{_mandir}/man1/libinput-measure-touchpad-tap.1*
%{_mandir}/man1/libinput-measure-touch-size.1*
%{_mandir}/man1/libinput-measure-touchpad-size.1*
%{_mandir}/man1/libinput-measure-touchpad-pressure.1*
%{_mandir}/man1/libinput-quirks.1*
%{_mandir}/man1/libinput-quirks-list.1*
%{_mandir}/man1/libinput-quirks-validate.1*
%{_mandir}/man1/libinput-record.1*
%{_mandir}/man1/libinput-replay.1*
%{_mandir}/man1/libinput-analyze.1*
%{_mandir}/man1/libinput-analyze-buttons.1*
%{_mandir}/man1/libinput-analyze-per-slot-delta.1*
%{_mandir}/man1/libinput-analyze-recording.1*
%{_mandir}/man1/libinput-analyze-touch-down-state.1*
%files test
%{_libexecdir}/libinput/libinput-test
%{_libexecdir}/libinput/libinput-test-suite
%{_libexecdir}/libinput/libinput-test-utils
%{_mandir}/man1/libinput-test.1*
%{_mandir}/man1/libinput-test-suite.1*
%changelog
* Wed Jul 15 2020 Peter Hutterer <peter.hutterer@redhat.com>
- Add basic spec file for package build testing

View file

@ -1,88 +0,0 @@
#!/usr/bin/env bash
#
# This script is sourced from here:
# https://gitlab.freedesktop.org/whot/meson-helper
#
# SPDX-License-Identifier: MIT
set -x
if [[ -f .meson_environment ]]; then
. .meson_environment
fi
# If test args are set, we assume we want to run the tests
MESON_RUN_TEST="$MESON_TEST_ARGS"
while [[ $# -gt 0 ]]; do
case $1 in
--skip-setup)
shift
MESON_SKIP_SETUP="1"
;;
--skip-build)
shift
MESON_SKIP_BUILD="1"
;;
--skip-test)
shift
MESON_RUN_TEST=""
;;
--run-test)
shift
MESON_RUN_TEST="1"
;;
*)
echo "Unknow commandline argument $1"
exit 1
;;
esac
done
if [[ -z "$MESON_BUILDDIR" ]]; then
echo "\$MESON_BUILDDIR undefined."
exit 1
fi
# emulate a few gitlab variables to make it easier to
# run and debug locally.
if [[ -z "$CI_JOB_ID" ]] || [[ -z "$CI_JOB_NAME" ]]; then
echo "Missing \$CI_JOB_ID or \$CI_JOB_NAME".
CI_PROJECT_NAME=$(basename "$PWD")
CI_JOB_ID=$(date +%s)
CI_JOB_NAME="$CI_PROJECT_NAME-job-local"
echo "Simulating gitlab environment: "
echo " CI_JOB_ID=$CI_JOB_ID"
echo " CI_JOB_NAME=$CI_JOB_NAME"
fi
if [[ -n "$FDO_CI_CONCURRENT" ]]; then
jobcount="-j$FDO_CI_CONCURRENT"
export MESON_TESTTHREADS="$FDO_CI_CONCURRENT"
fi
echo "*************************************************"
echo "builddir: $MESON_BUILDDIR"
echo "meson args: $MESON_ARGS"
echo "ninja args: $NINJA_ARGS"
echo "meson test args: $MESON_TEST_ARGS"
echo "job count: ${jobcount-0}"
echo "*************************************************"
set -e
if [[ -z "$MESON_SKIP_SETUP" ]]; then
rm -rf "$MESON_BUILDDIR"
meson setup "$MESON_BUILDDIR" $MESON_ARGS
fi
meson configure "$MESON_BUILDDIR"
if [[ -z "$MESON_SKIP_BUILD" ]]; then
if [[ -n "$NINJA_ARGS" ]]; then
ninja_args="--ninja-args $NINJA_ARGS"
fi
meson compile -v -C "$MESON_BUILDDIR" $jobcount $ninja_args
fi
if [[ -n "$MESON_RUN_TEST" ]]; then
meson test -C "$MESON_BUILDDIR" $MESON_TEST_ARGS --print-errorlogs
fi

View file

@ -1,2 +0,0 @@
#!/bin/sh
scan-build -v --status-bugs -plist-html "$@"

View file

@ -1,119 +0,0 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
from pathlib import Path
from dataclasses import dataclass
import argparse
import itertools
import os
import sys
@dataclass
class WhitespaceError:
message: str
lineno: int
nlines: int = 1
column: None | int = None
ncolumns: int = 1
def test_duplicate_empty_lines(lines: list[str]) -> list[WhitespaceError]:
errors = []
for idx, (l1, l2) in enumerate(itertools.pairwise(lines)):
if not l1 and not l2:
errors.append(WhitespaceError("Duplicated empty lines", idx, nlines=2))
return errors
def test_tab_after_space(lines: list[str]) -> list[WhitespaceError]:
errors = []
for idx, l in enumerate(lines):
index = l.find(" \t")
if index > -1:
errors.append(
WhitespaceError(
"Tab after space", idx, nlines=index, column=index, ncolumns=2
)
)
return errors
def test_trailing_whitespace(lines: list[str]) -> list[WhitespaceError]:
errors = []
for idx, l in enumerate(lines):
if l.rstrip() != l:
errors.append(WhitespaceError("Trailing whitespace", idx))
return errors
def test_empty_line_between_braces(lines: list[str]) -> list[WhitespaceError]:
errors = []
for idx in range(len(lines) - 3):
l1 = lines[idx]
l2 = lines[idx + 1]
l3 = lines[idx + 2]
if l1.strip() == "}" and l3.strip() == "}" and l2.strip() == "":
errors.append(WhitespaceError("Empty line between closing braces", idx + 1))
return errors
def main():
parser = argparse.ArgumentParser(description="Whitespace checker script")
parser.add_argument(
"files",
metavar="FILES",
type=Path,
nargs="+",
help="The files to check",
)
args = parser.parse_args()
have_errors: bool = False
if os.isatty(sys.stderr.fileno()):
red = "\x1b[0;31m"
reset = "\x1b[0m"
else:
red = ""
reset = ""
for file in args.files:
lines = [l.rstrip("\n") for l in file.open().readlines()]
errors = []
errors.extend(test_tab_after_space(lines))
errors.extend(test_trailing_whitespace(lines))
if any(file.name.endswith(suffix) for suffix in [".c", ".h"]):
if not file.parts[0] == "include":
errors.extend(test_duplicate_empty_lines(lines))
errors.extend(test_empty_line_between_braces(lines))
for e in errors:
print(f"{red}ERROR: {e.message} in {file}:{reset}", file=sys.stderr)
print(f"{'-' * 72}", file=sys.stderr)
lineno = max(0, e.lineno - 5)
for idx, l in enumerate(lines[lineno : lineno + 10]):
if e.lineno <= lineno + idx < e.lineno + e.nlines:
prefix = "->"
hl = red
nohl = reset
else:
prefix = " "
hl = ""
nohl = ""
print(f"{hl}{lineno + idx:3d}: {prefix} {l.rstrip()}{nohl}")
print(f"{'-' * 72}", file=sys.stderr)
if errors:
have_errors = True
if have_errors:
sys.exit(1)
if __name__ == "__main__":
main()

View file

@ -1,32 +0,0 @@
<!--
Before your file a feature request, please read
https://wayland.freedesktop.org/libinput/doc/latest/what-is-libinput.html
The amount of developer time libinput has available is very small.
Requesting a feature is no guarantee that it will get implemented. Someone
(you!) needs to step up to do the work.
-->
## Summary
<!-- Summarize the requested feature in a few sentences. -->
## Feature details
<!-- A step-by-step list of what the feature should achieve (where applicable) -->
## Affected Hardware
<!-- Which hardware types would be affected by this -->
## Implementation in Other Systems
<!-- Does this feature already exist elsewhere? How does it work there? Try
to provide as many details as possible -->
/label ~enhancement ~"needs triage"

View file

@ -1,35 +0,0 @@
## Summary
<!--
Summarize the bug encountered concisely. See
https://wayland.freedesktop.org/libinput/doc/latest/reporting-bugs.html for
detailed instructions to report bugs
-->
## Steps to reproduce
<!-- How to reproduce the issue on a developer machine - this is very important -->
## Required information
<!-- Note: if your libinput version is older than the current stable version,
please reproduce with a current version instead -->
- libinput version:
- hardware information:
- `libinput record` output: do not paste, **attach** the file
- `libinput debug-events --verbose` output: do not paste, **attach the file**
<!--
Paste any other relevant logs - please use code blocks (```) to format
console output, logs, and code as it's very hard to read otherwise.)
Do not paste logs longer than 10 lines, **attach** those instead.
If your libinput record is longer than 5-10s, we will not be able to process
it.
-->
/label ~"bug" ~"needs triage"

View file

@ -1,31 +0,0 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- id: no-commit-to-branch
args: ['--branch', 'main']
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.2
hooks:
- id: ruff-check
args: ['--ignore=E741,E501', '--extend-exclude=subprojects', '.']
- id: ruff-format
args: ['--check', '--diff']
- repo: https://gitlab.freedesktop.org/freedesktop/ci-templates.git
rev: e195d80f35b45cc73668be3767b923fd76c70ed5
hooks:
- id: check-commits
- id: generate-template
- repo: local
hooks:
- id: run-sed-script
name: Check for whitespace errors
entry: ./.gitlab-ci/whitespace-check.py
language: system
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v20.1.6
hooks:
- id: clang-format
types_or: [c]

View file

@ -1,244 +0,0 @@
# This is a set of bugbot commands for issues and merge requests - setting any of the
# bugbot::foo labels will trigger gitlab-triage to run with this ruleset (well, the
# one we have on the main branch at the time)
#
# Note that for adding labels, the label must first created in the project.
# Re-used in issues and mrs
.close_needinfo: &close_needinfo
name: "Close stale needinfo bugs"
conditions:
labels:
- "bugbot::needinfo-timeout"
actions:
remove_labels:
- "bugbot::needinfo-timeout"
comment: |
I'm closing this bug because some information we requested a while ago was never supplied and
we're not able to continue without this information.
Please feel free to re-open.
status: "close"
.remind_needinfo: &remind_needinfo
name: "Remind users of needinfo bugs"
conditions:
labels:
- "bugbot::needinfo-reminder"
actions:
labels:
- "waiting on reporter"
remove_labels:
- "bugbot::needinfo-reminder"
comment: |
Hi. This is a friendly reminder that the maintainers are waiting on some information by
you (or maybe someone cc'd on this bug). If the information is not provided we may not
be able to proceed with this issue or merge request. Please check the recent comments, thanks.
.help_needed: &help_needed
name: "Remind users help is needed"
conditions:
labels:
- "bugbot::help-needed"
actions:
labels:
- "help needed"
remove_labels:
- "bugbot::help-needed"
comment: |
Hi. This issue or merge request needs help. This simply means that for
the foreseeable future, the maintainers will not have time to work on
this.
If this is a request for a new feature, then the feature is unlikely to
be implemented unless you or another contributor files a merge request.
If a merge request already exists maybe it needs finishing which often
involves adding documentation or tests.
If this is an issue affecting a specific device then it is unlikely to be
fixed. This may be because it requires specific hardware to reproduce or
it may be that the use case is niche enough that the maintainers do not
have time to implement it.
In short, to resolve this issue or get this merge request into libinput
help is needed.
.libinput_record: &libinput_record
name: "Request libinput record output"
conditions:
labels:
- "bugbot::libinput-record"
actions:
remove_labels:
- "bugbot::libinput-record"
comment: |
Looks like we may need some extra information. Please **attach** (do not paste) the full output
of `libinput record` and `libinput debug-events --verbose` (if you haven't yet).
The [documentation](https://wayland.freedesktop.org/libinput/doc/latest/tools.html#libinput-record-and-libinput-replay)
has some information on what we're looking for to be able to triage bugs.
.hid_recorder: &hid_recorder
name: "Request hid-recorder output"
conditions:
labels:
- "bugbot::hid-recorder"
actions:
remove_labels:
- "bugbot::hid-recorder"
comment: |
Looks like we may need some extra information that isn't yet available in this issue.
Please **attach** (do not paste) the output of [`hid-recorder`](https://github.com/hidutils/hid-recorder/)
for this device (run `sudo hid-recorder` without argument and it will let you pick the device).
This should show the data the kernel receives from the device and may provide a hint on what's going on here.
.udev_hid_bpf: &udev_hid_bpf
name: "Punt to udev-hid-bpf"
conditions:
labels:
- "bugbot::udev-hid-bpf"
actions:
remove_labels:
- "bugbot::udev-hid-bpf"
comment: |
This issue looks like it could or should be fixed with [udev-hid-bpf](https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/).
udev-hid-bpf is a collection of BPF programs that modify the HID Report Descriptor and/or HID Reports from the device,
making it possible to change the properties of a device and/or the events to make them compatible with the
expectations the kernel and userspace has of such devices.
Please see the [udev-hid-bpf documentation](https://libevdev.pages.freedesktop.org/udev-hid-bpf/) for details
and/or in particular the [udev-hid-bpf tutorial](https://libevdev.pages.freedesktop.org/udev-hid-bpf/tutorial.html)
if you need to enable a new device.
status: "close"
resource_rules:
issues:
rules:
- name: "Close kernel bugs"
conditions:
labels:
- "bugbot::kernel"
actions:
labels:
- "kernel"
remove_labels:
- "bugbot::kernel"
comment: |
This bug looks like a kernel issue and it cannot be fixed
in libinput directly. I'm closing this bug but do feel free
to continue discussing the issue here.
Kernel bugs are usually best sent to the [`linux-input` list](https://lore.kernel.org/linux-input/).
status: "close"
- name: "Expect a merge request"
conditions:
labels:
- "bugbot::expect-mr"
actions:
remove_labels:
- "bugbot::expect-mr"
comment: |
I'm closing this bug in anticipation of a merge request that fixes this issue.
If you are a new contributor, please see
[the freedesktop.org wiki](https://gitlab.freedesktop.org/freedesktop/freedesktop/-/wikis/home)
on how to get permissions to fork a project and file a merge request.
The [libinput documentation](https://wayland.freedesktop.org/libinput/doc/latest/contributing.html)
also has more details on how to get started.
status: "close"
- name: "Point to 60-evdev.hwdb"
conditions:
labels:
- "bugbot::evdev-hwdb"
actions:
remove_labels:
- "bugbot::evdev-hwdb"
comment: |
Looks like this issue may be solved with a device-specific entry in systemd's hwdb.
You should have a /usr/lib/udev/hwdb.d/60-evdev.hwdb file which includes those quirks. Please see
the top of the file for instructions and follow those. Once the quirk is confirmed working
this issue (or parts thereof) should be fixed and you can submit a pull request to the
[systemd](https://github.com/systemd/systemd/) repository to get that quirk included.
Please link to the systemd issue here, thanks.
I'm closing this issue now, if the hwdb entry does not fix this issue here, please re-open.
status: "close"
- name: "Close bug for reopening"
conditions:
labels:
- "bugbot::close"
actions:
remove_labels:
- "bugbot::close"
comment: |
I'm temporarily closing this bug. The bug may not be fixed yet (see any comments above)
and we **want you to reopen it when/if it becomes actionable again**.
This process may feel unfamiliar but unfortunately closing/re-opening is the only action
all GitLab users are permitted to do. So we close it, you re-open it when whatever
above has been addressed and then we know we need to look at it again.
This issue may be closed more than once in a similar fashion but I only leave this
comment once since now you understand how it works. :smile:
For a detailed explanation on the how and why of this process please see
the [Closed Issues wiki page](https://gitlab.freedesktop.org/libinput/libinput/-/wikis/Closed-Issues).
status: "close"
- name: "Re-close bug for reopening"
conditions:
labels:
- "bugbot::re-close"
actions:
remove_labels:
- "bugbot::re-close"
comment: |
I'm temporarily closing this bug again. This is not a final close, see my comments above for the open/close process.
status: "close"
- *udev_hid_bpf
- *libinput_record
- *hid_recorder
- *close_needinfo
- *remind_needinfo
- *help_needed
merge_requests:
rules:
- name: "Remind contributor of commit rules"
conditions:
labels:
- "bugbot::commit-rules"
actions:
remove_labels:
- "bugbot::commit-rules"
comment: |
Hi. Looks like the pipeline failed because one or more of the commits in this MR do not meet our requirements.
Most commonly this the format of the commit message itself. The "Test summary" above has the details.
Please see [our docs for commit messages](https://wayland.freedesktop.org/libinput/doc/latest/contributing.html#commit-messages)
and [our docs for submitting code](https://wayland.freedesktop.org/libinput/doc/latest/contributing.html#submitting-code)
that explain how to amend and force-push to this repo.
- name: "Remind contributor that info needs to be in commit messages"
conditions:
labels:
- "bugbot::info-in-commit-message"
actions:
remove_labels:
- "bugbot::info-in-commit-message"
comment: |
Hi. Thanks for the merge request. I'm here to request that you add
some documentation about this merge request to the
**commit message** (or messages). You may have already written some
in the merge request description and in many cases it's fine to
copy/paste that into the commit message(s).
The reason is simple: once merged, no-one really looks at this this
page here anymore. The git log on the other hand is what developers
will use to understand the code so the information must be quickly
accessible via git.
Please see [our docs for submitting code](https://wayland.freedesktop.org/libinput/doc/latest/contributing.html#submitting-code)
that explain how to amend and force-push to this repo.
- *udev_hid_bpf
- *libinput_record
- *hid_recorder
- *close_needinfo
- *remind_needinfo
- *help_needed

View file

@ -1 +0,0 @@
set noexpandtab shiftwidth=8 cinoptions=:0,+0,(0

View file

@ -1,244 +0,0 @@
# Coding style
- Indentation in tabs, 8 characters wide, spaces after the tabs where
vertical alignment is required (see below)
**Note: this file uses spaces due to markdown rendering issues for tabs.
Code must be implemented using tabs.**
- Max line width 80ch, do not break up printed strings though
- Break up long lines at logical groupings, one line for each logical group
```c
int a = somelongname() +
someotherlongname();
if (a < 0 &&
(b > 20 & d < 10) &&
d != 0.0)
somelongfunctioncall(arg1,
arg2,
arg3);
```
- Function declarations: return type on separate line, {} on separate line,
arguments broken up as above.
```c
static inline int
foobar(int a, int b)
{
}
void
somenamethatiswaytoolong(int a,
int b,
int c)
{
}
```
- `/* comments only */`, no `// comments`
- `variable_name`, not `VariableName` or `variableName`. same for functions.
- no typedefs of structs, enums, unions
- if it generates a compiler warning, it needs to be fixed
- if it generates a static checker warning, it needs to be fixed or
commented
- declare variables when they are used first and try to keep them as local as possible.
Exception: basic loop variables, e.g. for (int i = 0; ...) should always be
declared inside the loop even where multiple loops exist
```c
int a;
if (foo) {
int b = 10;
a = get_value();
usevalue(a, b);
}
if (bar) {
a = get_value();
useit(a);
}
int c = a * 100;
useit(c);
```
- avoid uninitialized variables where possible, declare them late instead.
Note that most of libinput predates this style, try to stick with the code
around you if in doubt.
wrong:
```c
int *a;
int b = 7;
... some code ...
a = zalloc(32);
```
right:
```c
int b = 7;
... some code ...
int *a = zalloc(32);
```
- avoid calling non-obvious functions inside declaration blocks for multiple
variables.
bad:
```c
{
int a = 7;
int b = some_complicated_function();
int *c = zalloc(32);
}
```
better:
```c
{
int a = 7;
int *c = zalloc(32);
int b = some_complicated_function();
}
```
There is a bit of gut-feeling involved with this, but the goal is to make
the variable values immediately recognizable.
- Where statements are near-identical and repeated, try to keep them
identical:
bad:
```c
int a = get_some_value(x++);
do_something(a);
a = get_some_value(x++);
do_something(a);
a = get_some_value(x++);
do_something(a);
```
better:
```c
int a;
a = = get_some_value(x++);
do_something(a);
a = get_some_value(x++);
do_something(a);
a = get_some_value(x++);
do_something(a);
```
- if/else: { on the same line, no curly braces if both blocks are a single
statement. If either if or else block are multiple statements, both must
have curly braces.
```c
if (foo) {
blah();
bar();
} else {
a = 10;
}
```
- public functions MUST be doxygen-commented, use doxygen's `@foo` rather than
`\foo` notation
- `#include "config.h"` comes first, followed by system headers, followed by
external library headers, followed by internal headers.
sort alphabetically where it makes sense (specifically system headers)
```c
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <libevdev/libevdev.h>
#include "libinput-private.h"
```
- goto jumps only to the end of the function, and only for good reasons
(usually cleanup). goto never jumps backwards
- Use stdbool.h's bool for booleans within the library (instead of `int`).
Exception: the public API uses int, not bool.
# Git commit message requirements
Our CI will check the commit messages for a few requirements. Below is the
list of what we expect from a git commit.
## Commit message content
A [good commit message](http://who-t.blogspot.com/2009/12/on-commit-messages.html) needs to
answer three questions:
- Why is it necessary? It may fix a bug, it may add a feature, it may
improve performance, reliabilty, stability, or just be a change for the
sake of correctness.
- How does it address the issue? For short obvious patches this part can be
omitted, but it should be a high level description of what the approach
was.
- What effects does the patch have? (In addition to the obvious ones, this
may include benchmarks, side effects, etc.)
These three questions establish the context for the actual code changes, put
reviewers and others into the frame of mind to look at the diff and check if
the approach chosen was correct. A good commit message also helps
maintainers to decide if a given patch is suitable for stable branches or
inclusion in a distribution.
## Commit message format
The canonical git commit message format is:
```
one line as the subject line with a high-level note
full explanation of the patch follows after an empty line. This explanation
can be multiple paragraphs and is largely free-form. Markdown is not
supported.
You can include extra data where required like:
- benchmark one says 10s
- benchmark two says 12s
```
The subject line is the first thing everyone sees about this commit, so make
sure it's on point.
## Commit message technical requirements
- The commit message should use present tense (not past tense). Do write
"change foo to bar", not "changed foo to bar".
- The text width of the commit should be 78 chars or less, especially the
subject line.
- The author must be the name you usually identify as and email address. We do
not accept the default `@users.noreply` gitlab addresses.
```
git config --global user.name Your Name
git config --global user.email your@email
```

View file

@ -1,4 +0,0 @@
Thank you for your interest in contributing to libinput.
Please find more information about how to contribute in
[the documentation](https://wayland.freedesktop.org/libinput/doc/latest/contributing.html).

44
COPYING
View file

@ -1,34 +1,24 @@
Copyright © 2006-2009 Simon Thum
Copyright © 2008-2012 Kristian Høgsberg
Copyright © 2010-2012 Intel Corporation
Copyright © 2010-2011 Benjamin Franzke
Copyright © 2011-2012 Collabora, Ltd.
Copyright © 2013-2014 Jonas Ådahl
Copyright © 2013-2015 Red Hat, Inc.
Copyright © 2013-2014 Red Hat, Inc.
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:
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that copyright
notice and this permission notice appear in supporting documentation, and
that the name of the copyright holders not be used in advertising or
publicity pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no representations
about the suitability of this software for any purpose. It is provided "as
is" without express or implied warranty.
The 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.
libinput ships a copy of the GPL-licensed Linux kernel's linux/input.h
header file. [1] This does not make libinput GPL.
This copy is provided to provide consistent behavior regardless which kernel
version libinput is compiled against. The header is used during compilation
only, libinput does not link against GPL libraries.
[1] https://gitlab.freedesktop.org/libinput/libinput/blob/main/include/linux/input.h
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.

6
Makefile.am Normal file
View file

@ -0,0 +1,6 @@
SUBDIRS = src doc test tools
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
valgrind:
(cd test; $(MAKE) valgrind)

19
README Normal file
View file

@ -0,0 +1,19 @@
libinput
libinput is a library that handles input devices for display servers and other
applications that need to directly deal with input devices.
It provides device detection, device handling, input device event processing
and abstraction so minimize the amount of custom input code the user of
libinput need to provide the common set of functionality that users expect.
Input event processing includes scaling touch coordinates, generating
pointer events from touchpads, pointer acceleration, etc.
libinput originates from weston, the Wayland reference compositor.
The source code of libinput can be found at:
http://cgit.freedesktop.org/wayland/libinput
For more information, visit:
http://www.freedesktop.org/wiki/Software/libinput/

View file

@ -1,84 +0,0 @@
libinput
========
libinput is a library that provides a full input stack for display servers
and other applications that need to handle input devices provided by the
kernel.
libinput provides device detection, event handling and abstraction to
minimize the amount of custom input code the user of libinput needs to
provide the common set of functionality that users expect. Input event
processing includes scaling touch coordinates, generating
relative pointer events from touchpads, pointer acceleration, etc.
User documentation
------------------
Documentation explaining features available in libinput is available
[here](https://wayland.freedesktop.org/libinput/doc/latest/features.html).
This includes the [FAQ](https://wayland.freedesktop.org/libinput/doc/latest/faqs.html)
and the instructions on
[reporting bugs](https://wayland.freedesktop.org/libinput/doc/latest/reporting-bugs.html).
Source code
-----------
The source code of libinput can be found at:
https://gitlab.freedesktop.org/libinput/libinput
For a list of current and past releases visit:
https://www.freedesktop.org/wiki/Software/libinput/
Build instructions:
https://wayland.freedesktop.org/libinput/doc/latest/building.html
Reporting Bugs
--------------
Bugs can be filed on freedesktop.org GitLab:
https://gitlab.freedesktop.org/libinput/libinput/issues/
Where possible, please provide the `libinput record` output
of the input device and/or the event sequence in question.
See https://wayland.freedesktop.org/libinput/doc/latest/reporting-bugs.html
for more info.
Documentation
-------------
- Developer API documentation: https://wayland.freedesktop.org/libinput/doc/latest/development.html
- High-level documentation about libinput's features:
https://wayland.freedesktop.org/libinput/doc/latest/features.html
- Build instructions:
https://wayland.freedesktop.org/libinput/doc/latest/building.html
- Documentation for previous versions of libinput: https://wayland.freedesktop.org/libinput/doc/
Examples of how to use libinput are the debugging tools in the libinput
repository. Developers are encouraged to look at those tools for a
real-world (yet simple) example on how to use libinput.
- A commandline debugging tool: https://gitlab.freedesktop.org/libinput/libinput/tree/main/tools/libinput-debug-events.c
- A GTK application that draws cursor/touch/tablet positions: https://gitlab.freedesktop.org/libinput/libinput/tree/main/tools/libinput-debug-gui.c
License
-------
libinput is licensed under the MIT license.
> 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: [...]
See the [COPYING](https://gitlab.freedesktop.org/libinput/libinput/tree/main/COPYING)
file for the full license information.
About
-----
Documentation generated from git commit [__GIT_VERSION__](https://gitlab.freedesktop.org/libinput/libinput/commit/__GIT_VERSION__)

9
autogen.sh Executable file
View file

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

View file

@ -1,282 +0,0 @@
#compdef libinput
(( $+functions[_libinput_commands] )) || _libinput_commands()
{
local -a commands
commands=(
"list-devices:List all devices recognized by libinput"
"debug-events:Print all events as seen by libinput"
"debug-gui:Show a GUI to visualize libinput's events"
"debug-tablet:Show tablet axis and button values"
"measure:Measure various properties of devices"
"analyze:Analyze device data"
"record:Record the events from a device"
"replay:Replay the events from a device"
)
_describe -t commands 'command' commands
}
__all_seats()
{
# Obviously only works with logind
local -a seats
seats=${(f)"$(loginctl --no-legend --no-pager list-seats 2>/dev/null)"}
if [[ -z $seats ]]; then
# Can always offer seat0, even if we can't enumerate the seats
compadd "$@" - seat0
else
compadd "$@" - $seats
fi
}
(( $+functions[_libinput_list-devices] )) || _libinput_list-devices()
{
_arguments \
'--help[Show help and exit]' \
'--version[show version information and exit]'
}
(( $+functions[_libinput_debug-events] )) || _libinput_debug-events()
{
_arguments \
'--help[Show debug-events help and exit]' \
'--quiet[Only print libinput messages and nothing from this tool]' \
'--verbose[Use verbose output]' \
'--show-keycodes[Make all keycodes visible]' \
'--grab[Exclusively grab all opened devices]' \
'--compress-motion-events[Compress repeated motion events on a TTY]' \
'--device=[Use the given device with the path backend]:device:_files -W /dev/input/ -P /dev/input/' \
'--udev=[Listen for notifications on the given seat]:seat:__all_seats' \
'--apply-to=[Apply configuration options where the device name matches the pattern]:pattern' \
'--disable-sendevents=[Disable send-events option for the devices matching the pattern]:pattern' \
'--set-area=[Set the desired area as "x1/y1 x2/y2" (within \[0.0, 1.0\]) ]' \
'--set-calibration=[Set the first 6 elements of the 3x3 calibration matrix ("1.0 0.0 0.0 0.0 1.0 0.0")]' \
'--set-click-method=[Set the desired click method]:click-method:(none clickfinger buttonareas)' \
'--set-clickfinger-map=[Set button mapping for clickfinger]:tap-map:(( \
lrm\:2-fingers\ right-click\ /\ 3-fingers\ middle-click \
lmr\:2-fingers\ middle-click\ /\ 3-fingers\ right-click \
))' \
'--set-eraser-button-button=[Set button mapping for the eraser button]:eraser-button:(BTN_STYLUS BTN_STYLUS2 BTN_STYLUS3)' \
'--set-eraser-button-mode=[Set the eraser button mode]:eraser-mode:(default button)' \
'--set-pressure-range=[Set the tablet tool pressure range (within range \[0.0, 1.0\])]' \
'--set-profile=[Set pointer acceleration profile]:accel-profile:(adaptive flat custom)' \
'--set-rotation-angle=[Set the rotation angle in degrees]' \
'--set-scroll-button=[Set the button to the given button code]' \
'--set-scroll-method=[Set the desired scroll method]:scroll-method:(none twofinger edge button)' \
'--set-speed=[Set pointer acceleration speed (within range \[-1, 1\])]' \
'--set-tap-map=[Set button mapping for tapping]:tap-map:(( \
lrm\:2-fingers\ right-click\ /\ 3-fingers\ middle-click \
lmr\:2-fingers\ middle-click\ /\ 3-fingers\ right-click \
))' \
+ '(custom pointer acceleration)' \
'--set-custom-points=[Set n points defining a custom acceleration function]' \
'--set-custom-step=[Set the distance along the x axis between the custom points]' \
'--set-custom-type=[Set the type of the acceleration function]:custom-type:(fallback motion scroll)' \
+ '(drag)' \
'--enable-drag[Enable tap-and-drag]' \
'--disable-drag[Disable tap-and-drag]' \
+ '(drag-lock)' \
'--enable-drag-lock[Enable drag-lock]' \
'--disable-drag-lock[Disable drag-lock]' \
+ '(dwt)' \
'--enable-dwt[Enable disable-while-typing]' \
'--disable-dwt[Disable disable-while-typing]' \
+ '(dwtp)' \
'--enable-dwtp[Enable disable-while-trackpointing]' \
'--disable-dwtp[Disable disable-while-trackpointing]' \
+ '(left-handed)' \
'--enable-left-handed[Enable left handed button configuration]' \
'--disable-left-handed[Disable left handed button configuration]' \
+ '(middlebutton)' \
'--enable-middlebutton[Enable middle button emulation]' \
'--disable-middlebutton[Disable middle button emulation]' \
+ '(natural-scrolling)' \
'--enable-natural-scrolling[Enable natural scrolling]' \
'--disable-natural-scrolling[Disable natural scrolling]' \
+ '(plugins)' \
'--enable-plugins[Enable plugins]' \
'--disable-plugins[Disable plugins]' \
+ '(tap-to-click)' \
'--enable-tap[Enable tap-to-click]' \
'--disable-tap[Disable tap-to-click]'
}
(( $+functions[_libinput_debug-gui] )) || _libinput_debug-gui()
{
_arguments \
'--help[Show debug-gui help and exit]' \
'--verbose[Use verbose output]' \
'--grab[Exclusively grab all opened devices]' \
'--device=[Use the given device with the path backend]:device:_files -W /dev/input/ -P /dev/input/' \
'--udev=[Listen for notifications on the given seat]:seat:_libinput_all_seats'
}
(( $+functions[_libinput_debug-tablet] )) || _libinput_debug-tablet()
{
_arguments \
'--help[Show debug-tablet help and exit]' \
'--device=[Use the given device with the path backend]:device:_files -W /dev/input/ -P /dev/input/' \
'--udev=[Use the first tablet device on the given seat]:seat:_libinput_all_seats'
}
(( $+functions[_libinput_measure] )) || _libinput_measure()
{
local curcontext=$curcontext state line ret=1
local features
features=(
"fuzz:Measure touch fuzz to avoid pointer jitter"
"touch-size:Measure touch size and orientation"
"touchpad-tap:Measure tap-to-click time"
"touchpad-pressure:Measure touch pressure"
)
_arguments -C \
'--help[Print help and exit]' \
':feature:->feature' \
'*:: :->option-or-argument'
case $state in
(feature)
_describe -t features 'feature' features
;;
(option-or-argument)
curcontext=${curcontext%:*:*}:libinput-measure-$words[1]:
if ! _call_function ret _libinput_measure_$words[1]; then
_message "unknown feature: $words[1]"
fi
;;
esac
return ret
}
(( $+functions[_libinput_measure_fuzz] )) || _libinput_measure_fuzz()
{
_arguments \
'--help[Show help message and exit]' \
':device:_files -W /dev/input/ -P /dev/input/'
}
(( $+functions[_libinput_measure_touch-size] )) || _libinput_measure_touch-size()
{
_arguments \
'--help[Show help message and exit]' \
'--touch-threshold=[Assume a touch pressure threshold of "down:up"]' \
'--palm-threshold=[Assume a palm threshold of N]' \
':device:_files -W /dev/input/ -P /dev/input/'
}
(( $+functions[_libinput_measure_touchpad-pressure] )) || _libinput_measure_touchpad-pressure()
{
_arguments \
'--help[Show help message and exit]' \
'--touch-threshold=[Assume a touch pressure threshold of "down:up"]' \
'--palm-threshold=[Assume a palm threshold of N]' \
':device:_files -W /dev/input/ -P /dev/input/'
}
(( $+functions[_libinput_measure_touchpad-tap] )) || _libinput_measure_touchpad-tap()
{
_arguments \
'--help[Show help message and exit]' \
'--format=dat[Specify the data format to be printed. The default is "summary"]' \
':device:_files -W /dev/input/ -P /dev/input/'
}
(( $+functions[_libinput_analyze_per-slot-delta] )) || _libinput_analyze_per-slot-delta()
{
_arguments \
'--help[Show help message and exit]' \
':recording:_files'
}
(( $+functions[_libinput_analyze_touch-down-state] )) || _libinput_analyze_touch-down-state()
{
_arguments \
'--help[Show help message and exit]' \
':recording:_files'
}
(( $+functions[_libinput_analyze_recording] )) || _libinput_analyze_recording()
{
_arguments \
'--help[Show help message and exit]' \
':recording:_files'
}
(( $+functions[_libinput_analyze] )) || _libinput_analyze()
{
local curcontext=$curcontext state line ret=1
local features
features=(
"per-slot-delta:analyze relative movement per touch per slot"
"recording:analyze a recording by printing a pretty table"
"touch-down-state:analyze a recording for logical touch down states"
)
_arguments -C \
'--help[Print help and exit]' \
':feature:->feature' \
'*:: :->option-or-argument'
case $state in
(feature)
_describe -t features 'feature' features
;;
(option-or-argument)
curcontext=${curcontext%:*:*}:libinput-analyze-$words[1]:
if ! _call_function ret _libinput_analyze_$words[1]; then
_message "unknown feature: $words[1]"
fi
;;
esac
return ret
}
(( $+functions[_libinput_record] )) || _libinput_record()
{
_arguments \
'--help[Show help message and exit]' \
'--all[Record all /dev/input/event* devices available on the system]' \
'--autorestart=[Terminate the current recording after s seconds of device inactivity]' \
{-o+,--output=}'[Specify the output file to use]:file:_files -g "*.yml"' \
'--multiple[Record multiple devices at once]' \
'--show-keycodes[Show keycodes as-is in the recording]' \
'--with-libinput[Record libinput events alongside device events]' \
'--with-hidraw[Record hidraw events alongside device events]' \
'*::device:_files -W /dev/input/ -P /dev/input/'
}
(( $+functions[_libinput_replay] )) || _libinput_replay()
{
_arguments \
'--help[Show help message and exit]' \
':recording:_files'
}
_libinput()
{
local curcontext=$curcontext state line ret=1
_arguments -C \
{-h,--help}'[Show help message and exit]' \
'--version[Show version information and exit]' \
':command:->command' \
'*:: :->option-or-argument' && return
case $state in
(command)
_libinput_commands && ret=0
;;
(option-or-argument)
curcontext=${curcontext%:*:*}:libinput-$words[1]:
if ! _call_function ret _libinput_$words[1]; then
_message "unknown libinput command: $words[1]"
fi
;;
esac
return ret
}
_libinput

View file

@ -1,12 +0,0 @@
zshcompletiondir = get_option('zshcompletiondir')
if zshcompletiondir == ''
zshcompletiondir = get_option('datadir') / 'zsh' / 'site-functions'
endif
if zshcompletiondir != 'no'
install_data(
'_libinput',
install_dir: zshcompletiondir,
install_mode: 'rw-r--r--',
)
endif

121
configure.ac Normal file
View file

@ -0,0 +1,121 @@
AC_PREREQ([2.64])
m4_define([libinput_major_version], [0])
m4_define([libinput_minor_version], [4])
m4_define([libinput_micro_version], [0])
m4_define([libinput_version],
[libinput_major_version.libinput_minor_version.libinput_micro_version])
AC_INIT([libinput],
[libinput_version],
[https://bugs.freedesktop.org/enter_bug.cgi?product=Wayland&component=libinput&version=libinput_version],
[libinput],
[http://www.freedesktop.org/wiki/Software/libinput/])
AC_SUBST([LIBINPUT_VERSION_MAJOR], [libinput_major_version])
AC_SUBST([LIBINPUT_VERSION_MINOR], [libinput_minor_version])
AC_SUBST([LIBINPUT_VERSION_MICRO], [libinput_micro_version])
AC_SUBST([LIBINPUT_VERSION], [libinput_version])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
# Before making a release, the LIBINPUT_LT_VERSION string should be
# modified.
# The string is of the form C:R:A.
# - If interfaces have been changed or added, but binary compatibility has
# been preserved, change to C+1:0:A+1
# - If binary compatibility has been broken (eg removed or changed interfaces)
# change to C+1:0:0
# - If the interface is the same as the previous version, change to C:R+1:A
LIBINPUT_LT_VERSION=3:0:0
AC_SUBST(LIBINPUT_LT_VERSION)
AM_SILENT_RULES([yes])
# Check for programs
AC_PROG_CC_C99
AC_PROG_CXX # Only used by build C++ test
# Initialize libtool
LT_PREREQ([2.2])
LT_INIT
AC_CHECK_DECL(EPOLL_CLOEXEC, [],
[AC_MSG_ERROR("EPOLL_CLOEXEC is needed to compile libinput")],
[[#include <sys/epoll.h>]])
AC_CHECK_DECL(TFD_CLOEXEC,[],
[AC_MSG_ERROR("TFD_CLOEXEC is needed to compile libinput")],
[[#include <sys/timerfd.h>]])
AC_CHECK_DECL(CLOCK_MONOTONIC,[],
[AC_MSG_ERROR("CLOCK_MONOTONIC is needed to compile libinput")],
[[#include <time.h>]])
PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(MTDEV, [mtdev >= 1.1.0])
PKG_CHECK_MODULES(LIBUDEV, [libudev])
PKG_CHECK_MODULES(LIBEVDEV, [libevdev >= 0.4])
if test "x$GCC" = "xyes"; then
GCC_CXXFLAGS="-Wall -Wextra -Wno-unused-parameter -g -fvisibility=hidden"
GCC_CFLAGS="$GCC_CXXFLAGS -Wmissing-prototypes -Wstrict-prototypes"
fi
AC_SUBST(GCC_CFLAGS)
AC_SUBST(GCC_CXXFLAGS)
AC_PATH_PROG(DOXYGEN, [doxygen])
if test "x$DOXYGEN" = "x"; then
AC_MSG_WARN([doxygen not found - required for documentation])
have_doxygen="no"
else
have_doxygen="yes"
fi
AM_CONDITIONAL([HAVE_DOXYGEN], [test "x$have_doxygen" = "xyes"])
AC_ARG_ENABLE(event-gui,
AS_HELP_STRING([--enable-event-gui], [Build the GUI event viewer (default=auto)]),
[build_eventgui="$enableval"],
[build_eventgui="auto"])
PKG_CHECK_EXISTS([cairo glib-2.0 gtk+-3.0], [HAVE_GUILIBS="yes"], [HAVE_GUILIBS="no"])
if test "x$build_eventgui" = "xauto"; then
build_eventgui="$HAVE_GUILIBS"
fi
if test "x$build_eventgui" = "xyes"; then
PKG_CHECK_MODULES(CAIRO, [cairo])
PKG_CHECK_MODULES(GTK, [glib-2.0 gtk+-3.0])
fi
AM_CONDITIONAL(BUILD_EVENTGUI, [test "x$build_eventgui" = "xyes"])
AC_ARG_ENABLE(tests,
AS_HELP_STRING([--enable-tests], [Build the tests (default=auto)]),
[build_tests="$enableval"],
[build_tests="auto"])
PKG_CHECK_MODULES(CHECK, [check >= 0.9.9], [HAVE_CHECK="yes"], [HAVE_CHECK="no"])
if test "x$build_tests" = "xauto"; then
build_tests="$HAVE_CHECK"
fi
if test "x$build_tests" = "xyes"; then
if test "x$HAVE_CHECK" = "xno"; then
AC_MSG_ERROR([Cannot build tests, check is missing])
fi
AC_PATH_PROG(VALGRIND, [valgrind])
fi
AM_CONDITIONAL(HAVE_VALGRIND, [test "x$VALGRIND" != "x"])
AM_CONDITIONAL(BUILD_TESTS, [test "x$build_tests" = "xyes"])
AC_CONFIG_FILES([Makefile
doc/Makefile
doc/libinput.doxygen
src/Makefile
src/libinput.pc
src/libinput-version.h
test/Makefile
tools/Makefile])
AC_OUTPUT

28
doc/Makefile.am Normal file
View file

@ -0,0 +1,28 @@
EXTRA_DIST = touchpad-tap-state-machine.svg touchpad-softbutton-state-machine.svg
if HAVE_DOXYGEN
noinst_DATA = html/index.html
header_files = \
$(top_srcdir)/src/libinput.h
html/index.html: libinput.doxygen $(header_files)
$(AM_V_GEN)$(DOXYGEN) $<
clean-local:
$(AM_V_at)rm -rf html
doc_src= $(shell find html -type f -printf "html/%P\n" 2>/dev/null)
EXTRA_DIST += $(builddir)/html/index.html $(doc_src)
endif
# make sure doc was built before running dist
dist-hook:
@test -f $(distdir)/html/index.html || (\
echo "******************************************************" && \
echo "Couldn't find documentation files, refusing make dist." && \
echo "Install doxygen to build documentation for tarball." && \
echo "******************************************************" && \
test )

View file

@ -1,34 +0,0 @@
PROJECT_NAME = @PACKAGE_NAME@
PROJECT_NUMBER = @PACKAGE_VERSION@
PROJECT_BRIEF = "A wrapper library for input devices"
JAVADOC_AUTOBRIEF = YES
TAB_SIZE = 8
OPTIMIZE_OUTPUT_FOR_C = YES
EXTRACT_ALL = YES
EXTRACT_STATIC = YES
MAX_INITIALIZER_LINES = 0
WARNINGS = YES
QUIET = YES
INPUT = "@builddir@"
IMAGE_PATH = "@builddir@"
OUTPUT_DIRECTORY = doc
GENERATE_HTML = YES
HTML_OUTPUT = html
SEARCHENGINE = NO
USE_MATHJAX = YES
MATHJAX_RELPATH = https://cdn.mathjax.org/mathjax/latest
GENERATE_LATEX = NO
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
PREDEFINED = LIBINPUT_ATTRIBUTE_PRINTF(f, a)= \
LIBINPUT_ATTRIBUTE_DEPRECATED
DOTFILE_DIRS = "@builddir@"
EXAMPLE_PATH = "@builddir@"
SHOW_NAMESPACES = NO
HAVE_DOT = YES
HTML_HEADER = "@builddir@/header.html"
HTML_FOOTER = "@builddir@/footer.html"
HTML_EXTRA_STYLESHEET = "@builddir@/bootstrap.css" \
"@builddir@/customdoxygen.css" \
"@builddir@/libinputdoxygen.css"

View file

@ -1,135 +0,0 @@
/**
@mainpage
This is the libinput API reference.
This documentation is aimed at developers of Wayland compositors. User
documentation is available
[here](https://wayland.freedesktop.org/libinput/doc/latest).
@section concepts Concepts
@subsection concepts_initialization Initialization of a libinput context
libinput provides two different backends:
- a @ref libinput_udev_create_context "udev backend" where notifications
about new and removed devices are provided by udev, and
- a @ref libinput_path_create_context "path backend" where
@ref libinput_path_add_device "device addition" and
@ref libinput_path_remove_device "device removal" need to be handled by
the caller.
See section @ref base for information about initializing a libinput context.
@subsection concepts_events Monitoring for events
libinput exposes a single @ref libinput_get_fd "file descriptor" to the
caller. This file descriptor should be monitored by the caller, whenever
data is available the caller **must** immediately call libinput_dispatch().
Failure to do so will result in erroneous behavior.
libinput_dispatch() may result in one or more events being available to the
caller. After libinput_dispatch() a caller **should** call
libinput_get_event() to retrieve and process this event. Whenever
libinput_get_event() returns `NULL`, no further events are available.
See section @ref event for more information about events.
@subsection concepts_seats Device grouping into seats
All devices are grouped into physical and logical seats. Button and key
states are available per-device and per-seat. See @ref seat for more
information.
@subsection concepts_devices Device capabilities
libinput does not use device types. All devices have @ref
libinput_device_has_capability "capabilities" that define which events may
be generated. See @ref device for more information about devices.
Specific event types include:
- @ref event_keyboard
- @ref event_pointer
- @ref event_touch
- @ref event_gesture
- @ref event_tablet
- @ref event_tablet_pad
- @ref event_switch
@subsection concepts_configuration Device configuration
libinput relies on the caller for device configuration. See
@ref config for more information.
@subsection example An example libinput program
The simplest libinput program looks like this:
@code
static int open_restricted(const char *path, int flags, void *user_data)
{
int fd = open(path, flags);
return fd < 0 ? -errno : fd;
}
static void close_restricted(int fd, void *user_data)
{
close(fd);
}
const static struct libinput_interface interface = {
.open_restricted = open_restricted,
.close_restricted = close_restricted,
};
int main(void) {
struct libinput *li;
struct libinput_event *event;
li = libinput_udev_create_context(&interface, NULL, udev);
libinput_udev_assign_seat(li, "seat0");
libinput_dispatch(li);
while ((event = libinput_get_event(li)) != NULL) {
// handle the event here
libinput_event_destroy(event);
libinput_dispatch(li);
}
libinput_unref(li);
return 0;
}
@endcode
@section building_against Building against libinput
libinput provides a
[pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/) file.
Software that uses libinput should use pkg-config and the
`PKG_CHECK_MODULES` autoconf macro.
Otherwise, the most rudimentary way to compile and link a program against
libinput is:
@verbatim
gcc -o myprogram myprogram.c `pkg-config --cflags --libs libinput`
@endverbatim
For further information on using pkgconfig see the pkg-config documentation.
@section stability Backwards-compatibility
libinput promises backwards-compatibility across all the 1.x.y version. An
application built against libinput 1.x.y will work with any future 1.*.*
release.
@section About
Documentation generated from git commit [__GIT_VERSION__](https://gitlab.freedesktop.org/libinput/libinput/commit/__GIT_VERSION__)
*/

View file

@ -1,52 +0,0 @@
prg_install = find_program('install')
doxygen = find_program('doxygen', required : false)
if not doxygen.found()
error('Program "doxygen" not found or not executable. Try building with -Ddocumentation=false')
endif
dot = find_program('dot', required : false)
if not dot.found()
error('Program "dot" not found or not executable. Try building with -Ddocumentation=false')
endif
mainpage = vcs_tag(command : ['git', 'log', '-1', '--format=%h'],
fallback : 'unknown',
input : 'mainpage.dox',
output : 'mainpage.dox',
replace_string: '__GIT_VERSION__')
src_doxygen = files(
# source files
'../../src/libinput.h',
# style files
'style/header.html',
'style/footer.html',
'style/customdoxygen.css',
'style/bootstrap.css',
'style/libinputdoxygen.css',
)
doxyfiles = []
foreach f : src_doxygen
df = configure_file(input: f,
output: '@PLAINNAME@',
copy : true)
doxyfiles += [ df ]
endforeach
doc_config = configuration_data()
doc_config.set('PACKAGE_NAME', meson.project_name())
doc_config.set('PACKAGE_VERSION', meson.project_version())
doc_config.set('builddir', meson.current_build_dir())
doxyfile = configure_file(input : 'libinput.doxygen.in',
output : 'libinput.doxygen',
configuration : doc_config)
custom_target('doxygen',
input : [ doxyfiles, doxyfile, mainpage ] + src_doxygen,
output : [ 'html' ],
command : [ doxygen, doxyfile ],
install : false,
depends: [ mainpage ],
build_by_default : true)

View file

@ -1,7 +0,0 @@
/**
@page Tablets
- @subpage tablet serial numbers
*/

View file

@ -1,229 +0,0 @@
These licenses apply to the doxygen documentation HTML style only. They do
not apply or affect libinput itself.
Apache: https://github.com/Velron/doxygen-bootstrapped/
MIT: https://bootswatch.com/paper/bootstrap.css
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
The MIT License (MIT)
Copyright (c) 2011-2015 Twitter, Inc
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 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 diff suppressed because it is too large Load diff

View file

@ -1,254 +0,0 @@
h1, .h1, h2, .h2, h3, .h3{
font-weight: 200 !important;
}
#navrow1, #navrow2, #navrow3, #navrow4, #navrow5{
border-bottom: 1px solid #EEEEEE;
}
.adjust-right {
margin-left: 30px !important;
font-size: 1.15em !important;
}
.navbar{
border: 0px solid #222 !important;
}
/* Sticky footer styles
-------------------------------------------------- */
html,
body {
height: 100%;
/* The html and body elements cannot have any padding or margin. */
}
/* Wrapper for page content to push down footer */
#wrap {
min-height: 100%;
height: auto;
/* Negative indent footer by its height */
margin: 0 auto -60px;
/* Pad bottom by footer height */
padding: 0 0 60px;
}
/* Set the fixed height of the footer here */
#footer {
font-size: 0.9em;
padding: 8px 0px;
background-color: #f5f5f5;
}
.footer-row {
line-height: 44px;
}
#footer > .container {
padding-left: 15px;
padding-right: 15px;
}
.footer-follow-icon {
margin-left: 3px;
text-decoration: none !important;
}
.footer-follow-icon img {
width: 20px;
}
.footer-link {
padding-top: 5px;
display: inline-block;
color: #999999;
text-decoration: none;
}
.footer-copyright {
text-align: center;
}
@media (min-width: 992px) {
.footer-row {
text-align: left;
}
.footer-icons {
text-align: right;
}
}
@media (max-width: 991px) {
.footer-row {
text-align: center;
}
.footer-icons {
text-align: center;
}
}
/* DOXYGEN Code Styles
----------------------------------- */
a.qindex {
font-weight: bold;
}
a.qindexHL {
font-weight: bold;
background-color: #9CAFD4;
color: #ffffff;
border: 1px double #869DCA;
}
.contents a.qindexHL:visited {
color: #ffffff;
}
a.code, a.code:visited, a.line, a.line:visited {
color: #4665A2;
}
a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited {
color: #4665A2;
}
/* @end */
dl.el {
margin-left: -1cm;
}
pre.fragment {
border: 1px solid #C4CFE5;
background-color: #FBFCFD;
padding: 4px 6px;
margin: 4px 8px 4px 2px;
overflow: auto;
word-wrap: break-word;
font-size: 9pt;
line-height: 125%;
font-family: monospace, fixed;
font-size: 105%;
}
div.fragment {
padding: 4px 6px;
margin: 4px 8px 4px 2px;
border: 1px solid #C4CFE5;
}
div.line {
font-family: monospace, fixed;
font-size: 13px;
min-height: 13px;
line-height: 1.0;
text-wrap: unrestricted;
white-space: -moz-pre-wrap; /* Moz */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
white-space: pre-wrap; /* CSS3 */
word-wrap: break-word; /* IE 5.5+ */
text-indent: -53px;
padding-left: 53px;
padding-bottom: 0px;
margin: 0px;
-webkit-transition-property: background-color, box-shadow;
-webkit-transition-duration: 0.5s;
-moz-transition-property: background-color, box-shadow;
-moz-transition-duration: 0.5s;
-ms-transition-property: background-color, box-shadow;
-ms-transition-duration: 0.5s;
-o-transition-property: background-color, box-shadow;
-o-transition-duration: 0.5s;
transition-property: background-color, box-shadow;
transition-duration: 0.5s;
}
div.line.glow {
background-color: cyan;
box-shadow: 0 0 10px cyan;
}
span.lineno {
padding-right: 4px;
text-align: right;
border-right: 2px solid #0F0;
background-color: #E8E8E8;
white-space: pre;
}
span.lineno a {
background-color: #D8D8D8;
}
span.lineno a:hover {
background-color: #C8C8C8;
}
div.groupHeader {
margin-left: 16px;
margin-top: 12px;
font-weight: bold;
}
div.groupText {
margin-left: 16px;
font-style: italic;
}
/* @group Code Colorization */
span.keyword {
color: #008000
}
span.keywordtype {
color: #604020
}
span.keywordflow {
color: #e08000
}
span.comment {
color: #800000
}
span.preprocessor {
color: #806020
}
span.stringliteral {
color: #002080
}
span.charliteral {
color: #008080
}
span.vhdldigit {
color: #ff00ff
}
span.vhdlchar {
color: #000000
}
span.vhdlkeyword {
color: #700070
}
span.vhdllogic {
color: #ff0000
}
blockquote {
background-color: #F7F8FB;
border-left: 2px solid #9CAFD4;
margin: 0 24px 0 4px;
padding: 0 12px 0 16px;
}

View file

@ -1,26 +0,0 @@
<!-- HTML footer for doxygen 1.8.8-->
<!-- start footer part -->
<!--BEGIN GENERATE_TREEVIEW-->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
$navpath
<li class="footer">$generatedby
<a href="http://www.doxygen.org/index.html">
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
</ul>
</div>
<!--END GENERATE_TREEVIEW-->
</div>
</div>
</div>
</div>
</div>
<!--BEGIN !GENERATE_TREEVIEW-->
<hr class="footer"/><address class="footer"><small>
$generatedby &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/>
</a> $doxygenversion
</small></address>
<!--END !GENERATE_TREEVIEW-->
</body>
</html>

View file

@ -1,40 +0,0 @@
<!-- HTML header for doxygen 1.8.8-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- For Mobile Devices -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<!--<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>-->
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
<script type="text/javascript" src="doxy-boot.js"></script>
</head>
<body>
<nav class="navbar navbar-default" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand">$projectname $projectnumber</a>
</div>
</div>
</nav>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div class="content" id="content">
<div class="container">
<div class="row">
<div class="col-sm-12 panel panel-default" style="padding-bottom: 15px;">
<div style="margin-bottom: 15px;">
<!-- end header part -->

View file

@ -1,120 +0,0 @@
@import url("https://fonts.googleapis.com/css?family=Roboto+Mono");
dd {
margin-left: 30px;
}
.title {
font-size: 200%;
font-weight: bold;
}
.title .ingroups {
font-size: 50%;
}
h1 {
font-size: 150%;
color: #354C7B;
background: none;
border-bottom: 1px solid #879ECB;
font-size: 150%;
font-weight: normal;
padding-top: 8px;
padding-bottom: 8px;
padding-left: 0px;
width: 100%;
}
h2 {
font-size: 120%;
color: #354C7B;
background: none;
border-bottom: 1px solid #879ECB;
font-size: 150%;
font-weight: normal;
padding-top: 8px;
padding-bottom: 8px;
padding-left: 0px;
width: 100%;
}
.sm-dox li {
float:left;
border-top: 0;
padding-right: 20px;
}
.sm li, .sm a {
position: relative;
}
.sm, .sm ul, .sm li {
list-style: none;
display: block;
line-height: normal;
direction: ltr;
text-align: left;
}
.sm, .sm *, .sm *::before, .sm *::after {
box-sizing: border-box;
}
#main-nav {
padding: 30px;
}
/* Main menu sub-items like file-list, etc */
#main-menu li ul {
display: none;
}
.paramname {
padding-right: 10px;
}
.memtitle {
background-image: none;
background-color: #F0F0F0;
}
.memproto {
background-color: #F0F0F0;
}
.headertitle {
background-image: none;
background-color: #F0F0F0;
}
div.header {
border: none;
}
td.fieldname {
font-family: 'Roboto Mono', monospace;
}
.fieldtable th {
background-image: none;
background-color: #F0F0F0;
}
body {
letter-spacing: 0px;
}
.mdescLeft, .mdescRight, .memItemLeft, .memItemRight, .memTemplItemLeft, .memTemplItemRight, .memTemplParams {
background-color: #F0F0F0;
}
a {
color: #2873b0;
}
.navpath ul {
background-image: none;
background-color: #F0F0F0;
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 97 KiB

View file

@ -1,50 +0,0 @@
# Source for the button debouncing wave diagram
# Paste into http://wavedrom.com/editor.html
{signal: [
{name:'current mode', wave: '3............', data: ['normal button press and release']},
{name:'physical button', wave: '01......0....'},
{name:'application ', wave: '01......0....'},
{},
['bounce mode',
{name:'current mode', wave: '4............', data: ['debounced button press']},
{name:'physical button', wave: '0101...0.....'},
{name: 'timeouts', wave: '01...0.1...0.'},
{name:'application ', wave: '01.....0.....'},
{},
{name:'current mode', wave: '4............', data: ['debounced button release']},
{name:'physical button', wave: '1...010......'},
{name: 'timeouts', wave: '0...1...0....'},
{name:'application ', wave: '1...0........'},
{},
{name:'current mode', wave: '5............', data: ['delayed button press']},
{name:'physical button', wave: '1...01.......'},
{name: 'timeouts', wave: '0...1...0....'},
{name:'application ', wave: '1...0...1....'},
{},
{name:'current mode', wave: '5............', data: ['delayed button release']},
{name:'physical button', wave: '0...10.......'},
{name: 'timeouts', wave: '0...1...0....'},
{name:'application ', wave: '0...1...0....'},
],
{},
['spurious mode',
{name:'current mode', wave: '3............', data: ['first spurious button release ']},
{name:'physical button', wave: '1.......01...'},
{name:'application ', wave: '1.......01...'},
{},
{name:'current mode', wave: '3............', data: ['later spurious button release ']},
{name:'physical button', wave: '1....01......'},
{name: 'timeouts', wave: '0....1..0....'},
{name:'application ', wave: '1............'},
{},
{name:'current mode', wave: '3............', data: ['delayed release in spurious mode ']},
{name:'physical button', wave: '1....0.......'},
{name: 'timeouts', wave: '0....1..0....'},
{name:'application ', wave: '1.......0....'}
],
],
head:{
text:'Button Debouncing Scenarios',
},
}

1854
doc/libinput.doxygen.in Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 112 KiB

View file

@ -1,262 +0,0 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1490px" height="1399px" version="1.1">
<defs/>
<g transform="translate(0.5,0.5)">
<ellipse cx="261" cy="143" rx="63" ry="45.5" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="261" y="126">NONE</text>
<text x="261" y="140">on-entry:</text>
<text x="261" y="154">edge = none</text>
<text x="261" y="168">threshold = def</text>
</g>
<rect x="30" y="386" width="150" height="101" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="105" y="419">EDGE_NEW</text>
<text x="105" y="433">on-entry:</text>
<text x="105" y="447">edge = get_edge()</text>
<text x="105" y="461">set_timer()</text>
</g>
<rect x="348" y="386" width="130" height="100" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="413" y="419">AREA</text>
<text x="413" y="433">on-entry:</text>
<text x="413" y="447">edge = none</text>
<text x="413" y="461">set_pointer()</text>
</g>
<path d="M 237 7 C 239 4 243 2 246 2 L 275 2 C 278 2 282 4 284 7 L 301 30 C 301 31 301 33 301 34 L 284 57 C 282 60 278 62 275 62 L 246 62 C 243 62 239 60 237 57 L 220 34 C 220 33 220 31 220 30 L 237 7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="261" y="36">release</text>
</g>
<path d="M 237 222 C 239 219 243 217 246 217 L 276 217 C 279 217 283 219 285 222 L 303 245 C 303 246 303 248 303 249 L 285 272 C 283 275 279 277 276 277 L 246 277 C 243 277 239 275 237 272 L 219 249 C 219 248 219 246 219 245 L 237 222 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="261" y="251">touch</text>
</g>
<path d="M 261 188 L 261 211" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 261 216 L 258 209 L 261 211 L 265 209 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 218 535 C 223 531 229 528 235 528 L 285 528 C 291 528 297 531 302 535 L 331 570 C 332 571 332 574 331 575 L 302 610 C 297 614 291 617 285 617 L 235 617 C 229 617 223 614 218 610 L 189 575 C 188 574 188 571 189 570 L 218 535 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="260" y="563">touch,</text>
<text x="260" y="577">edge &amp;= get_edge()</text>
</g>
<path d="M 220 526 L 158 487" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 224 529 L 217 528 L 220 526 L 220 522 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 105 617 L 105 650" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 105 656 L 101 649 L 105 650 L 108 649 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 478 436 L 498 436 Q 508 436 508 426 L 508 42 Q 508 32 498 32 L 307 32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 302 32 L 309 29 L 307 32 L 309 36 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 261 62 L 261 71 Q 261 80 261 85 L 261 91" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 261 96 L 257 89 L 261 91 L 264 89 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<ellipse cx="1133" cy="67" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<path d="M 1133 82 L 1133 110 Q 1133 120 1133 130 L 1133 155" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1129 147 L 1133 156 L 1138 147" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="1083" y="12" width="100" height="40" fill="none" stroke="none" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1133" y="36">tp_edge_scroll_post_events()</text>
</g>
<path d="M 1133 212 L 1193 242 L 1133 272 L 1073 242 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1133" y="246">dirty?</text>
</g>
<path d="M 1193 242 L 1278 242 Q 1288 242 1298 242 L 1456 242" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1448 247 L 1457 242 L 1448 238" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1225" y="223" width="18" height="18" stroke-width="0"/>
<text x="1226" y="236">no</text>
</g>
<path d="M 1133 272 L 1133 285 Q 1133 295 1133 305 L 1133 316" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1129 308 L 1133 317 L 1138 308" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1150" y="292" width="24" height="18" stroke-width="0"/>
<text x="1150" y="302">yes</text>
</g>
<ellipse cx="1473" cy="242" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="1473" cy="242" rx="15" ry="15" fill="transparent" stroke="#ff0000" pointer-events="none"/>
<rect x="1033" y="97" width="200" height="70" rx="28" ry="28" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1133" y="115">current = buttons.state &amp; 0x01</text>
<text x="1133" y="129">old = buttons.old_state &amp; 0x01</text>
<text x="1133" y="143">button = 0</text>
<text x="1133" y="157">is_top = 0</text>
</g>
<path d="M 1133 167 L 1133 180 Q 1133 190 1133 200 L 1133 210" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1129 202 L 1133 211 L 1138 202" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="1237" y="436" width="188" height="50" rx="20" ry="20" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1331" y="458">notify_axis(last_axis, 0.0)</text>
<text x="1331" y="472">last_axis = -1</text>
</g>
<path d="M 1134 516 L 1194 552 L 1134 587 L 1073 552 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1134" y="556">edge == right</text>
</g>
<path d="M 1194 552 L 1268 552 Q 1278 552 1278 553 L 1278 554 Q 1278 554 1268 554 L 1250 554" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1258 550 L 1249 554 L 1258 559" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1202" y="559" width="24" height="18" stroke-width="0"/>
<text x="1203" y="569">yes</text>
</g>
<rect x="1248" y="534" width="160" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1328" y="551">axis = scroll_vertical</text>
<text x="1328" y="565">delta = dy</text>
</g>
<path d="M 1408 554 L 1468 554 Q 1478 554 1478 564 L 1478 952 Q 1478 962 1468 962 L 1233 962" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1241 958 L 1232 962 L 1241 967" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1133 318 L 1196 352 L 1133 387 L 1071 352 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1133" y="356">edge == none</text>
</g>
<path d="M 1134 386 L 1134 441 Q 1134 451 1134 461 L 1134 514" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1129 506 L 1134 515 L 1138 506" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1135" y="449" width="18" height="18" stroke-width="0"/>
<text x="1136" y="458">no</text>
</g>
<path d="M 1133 657 L 1194 692 L 1133 727 L 1072 692 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1133" y="696">edge == bottom</text>
</g>
<path d="M 1194 692 L 1208 692 Q 1218 692 1228 692 L 1249 692" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1241 697 L 1250 692 L 1241 688" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1213" y="700" width="24" height="18" stroke-width="0"/>
<text x="1214" y="709">yes</text>
</g>
<path d="M 1134 587 L 1133 587 Q 1133 587 1133 597 L 1133 647 Q 1133 657 1133 656 L 1133 655" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1129 647 L 1133 656 L 1138 647" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1135" y="607" width="18" height="18" stroke-width="0"/>
<text x="1135" y="621">no</text>
</g>
<rect x="1251" y="672" width="160" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1331" y="689">axis = scroll_horizontal</text>
<text x="1331" y="703">delta = dx</text>
</g>
<path d="M 1133 727 L 1133 727 Q 1134 727 1134 737 L 1134 807 Q 1134 817 1134 816 L 1134 815" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1129 807 L 1134 816 L 1138 807" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1135" y="756" width="18" height="18" stroke-width="0"/>
<text x="1136" y="770">no</text>
</g>
<rect x="1036" y="917" width="195" height="90" rx="36" ry="36" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1134" y="966">get_delta()</text>
</g>
<path d="M 1134 1007 L 1134 1037 Q 1134 1047 1134 1057 L 1134 1070" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1129 1062 L 1134 1071 L 1138 1062" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1411 692 L 1468 692 Q 1478 692 1478 702 L 1478 952 Q 1478 962 1468 962 L 1233 962" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1241 958 L 1232 962 L 1241 967" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="1031" y="1197" width="205" height="90" rx="36" ry="36" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1134" y="1232">notify_axis(axis, delta)</text>
<text x="1134" y="1246">last_axis = axis</text>
<text x="1134" y="1260">emit(scroll_event_posted)</text>
</g>
<path d="M 1134 1072 L 1200 1107 L 1134 1142 L 1067 1107 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1134" y="1111">delta &lt; threshold</text>
</g>
<path d="M 1200 1107 L 1318 1107 Q 1328 1107 1338 1107 L 1456 1107" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1448 1112 L 1457 1107 L 1448 1103" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1259" y="1118" width="24" height="18" stroke-width="0"/>
<text x="1260" y="1131">yes</text>
</g>
<path d="M 1134 1142 L 1134 1160 Q 1134 1170 1134 1180 L 1134 1195" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1129 1187 L 1134 1196 L 1138 1187" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1135" y="1150" width="18" height="18" stroke-width="0"/>
<text x="1136" y="1159">no</text>
</g>
<ellipse cx="1473" cy="1107" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="1473" cy="1107" rx="15" ry="15" fill="transparent" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="1473" cy="352" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="1473" cy="352" rx="15" ry="15" fill="transparent" stroke="#ff0000" pointer-events="none"/>
<path d="M 1331 318 L 1394 352 L 1331 387 L 1269 352 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1331" y="356">last_axis != -1</text>
</g>
<rect x="30" y="657" width="150" height="101" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="105" y="697">EDGE</text>
<text x="105" y="711">on-entry:</text>
<text x="105" y="725">threshold = 0.01</text>
</g>
<path d="M 30 707 L 18 707 Q 8 707 8 697 L 8 42 Q 8 32 18 32 L 214 32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 219 32 L 212 36 L 214 32 L 212 29 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 30 436 L 18 436 Q 8 436 8 426 L 8 42 Q 8 32 18 32 L 214 32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 219 32 L 212 36 L 214 32 L 212 29 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 64 535 C 68 531 74 528 80 528 L 130 528 C 136 528 142 531 146 535 L 175 570 C 176 571 176 574 175 575 L 146 610 C 142 614 136 617 130 617 L 80 617 C 74 617 68 614 64 610 L 35 575 C 34 574 34 571 35 570 L 64 535 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="105" y="563">timeout ||</text>
<text x="105" y="577">scroll_event_posted</text>
</g>
<path d="M 105 487 L 105 522" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 105 527 L 102 520 L 105 522 L 109 520 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 260 528 L 261 472" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 261 467 L 264 474 L 261 472 L 257 474 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1196 352 L 1222 352 Q 1232 352 1242 352 L 1266 352" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1258 357 L 1267 352 L 1258 348" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1224" y="360" width="24" height="18" stroke-width="0"/>
<text x="1224" y="369">yes</text>
</g>
<path d="M 1331 387 L 1331 401 Q 1331 411 1331 421 L 1331 434" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1327 426 L 1331 435 L 1336 426" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1333" y="394" width="24" height="18" stroke-width="0"/>
<text x="1333" y="403">yes</text>
</g>
<path d="M 1394 352 L 1408 352 Q 1418 352 1428 352 L 1456 352" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1448 357 L 1457 352 L 1448 348" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1417" y="360" width="18" height="18" stroke-width="0"/>
<text x="1418" y="369">no</text>
</g>
<ellipse cx="1473" cy="461" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="1473" cy="461" rx="15" ry="15" fill="transparent" stroke="#ff0000" pointer-events="none"/>
<path d="M 1425 461 L 1438 461 Q 1448 461 1452 461 L 1456 461" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1448 466 L 1457 461 L 1448 457" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<ellipse cx="1134" cy="832" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="1134" cy="832" rx="15" ry="15" fill="transparent" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="1134" cy="1382" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="1134" cy="1382" rx="15" ry="15" fill="transparent" stroke="#ff0000" pointer-events="none"/>
<path d="M 1134 1287 L 1134 1365" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1130 1359 L 1134 1366 L 1137 1359" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 261 277 L 261 299" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 261 304 L 258 297 L 261 299 L 265 297 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 208 337 L 115 337 Q 105 337 105 347 L 105 376 Q 105 386 105 382 L 105 378" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 105 385 L 100 376 L 105 378 L 109 376 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="174" y="345" width="24" height="18" stroke-width="0"/>
<text x="175" y="354">yes</text>
</g>
<path d="M 314 337 L 403 337 Q 413 337 413 347 L 413 376 Q 413 386 413 382 L 413 378" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 413 385 L 408 376 L 413 378 L 417 376 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="350" y="345" width="18" height="18" stroke-width="0"/>
<text x="350" y="354">no</text>
</g>
<path d="M 261 305 L 314 337 L 261 369 L 208 337 Z" fill="#ffd966" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="261" y="341">get_edge()</text>
</g>
<path d="M 261 406 L 309 436 L 261 466 L 213 436 Z" fill="#ffd966" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="261" y="440">edge</text>
</g>
<path d="M 309 436 L 319 436 Q 328 436 334 436 L 340 436" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 347 436 L 338 441 L 340 436 L 338 432 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="311" y="444" width="18" height="18" stroke-width="0"/>
<text x="311" y="453">no</text>
</g>
<path d="M 213 436 L 211 436 Q 208 436 198 436 L 188 436" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 181 436 L 190 432 L 188 436 L 190 441 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="189" y="444" width="24" height="18" stroke-width="0"/>
<text x="190" y="453">yes</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 147 KiB

View file

@ -1,386 +1,532 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2180px" height="1624px" version="1.1">
<defs/>
<g transform="translate(0.5,0.5)">
<path d="M 862 441 L 894 441" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 899 441 L 892 445 L 894 441 L 892 438 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<ellipse cx="785" cy="151" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="785" y="141">NONE</text>
<text x="785" y="155">on-entry:</text>
<text x="785" y="169">curr = none</text>
</g>
<rect x="712" y="391" width="150" height="101" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="787" y="431">BOTTOM</text>
<text x="787" y="445">on-entry:</text>
<text x="787" y="459">curr = button</text>
</g>
<rect x="1023" y="392" width="130" height="100" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1088" y="425">AREA</text>
<text x="1088" y="439">on-entry:</text>
<text x="1088" y="453">curr =area</text>
<text x="1088" y="467">set_pointer()</text>
</g>
<path d="M 921 411 C 923 408 927 405 932 405 L 965 405 C 970 405 974 408 976 411 L 996 439 C 997 440 997 442 996 444 L 976 471 C 974 475 970 477 965 477 L 932 477 C 927 477 923 475 921 471 L 901 444 C 900 442 900 440 901 439 L 921 411 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="949" y="438">finger in</text>
<text x="949" y="452">area or top</text>
</g>
<path d="M 759 7 C 762 4 766 2 770 2 L 804 2 C 808 2 812 4 815 7 L 835 30 C 836 31 836 33 835 34 L 815 57 C 812 60 808 62 804 62 L 770 62 C 766 62 762 60 759 57 L 739 34 C 739 33 739 31 739 30 L 759 7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="787" y="29">finger</text>
<text x="787" y="43">up</text>
</g>
<path d="M 997 441 L 1016 442" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1022 442 L 1015 445 L 1016 442 L 1015 438 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 766 235 C 768 232 771 230 774 230 L 800 230 C 803 230 806 232 808 235 L 824 259 C 824 260 824 262 824 263 L 808 287 C 806 290 803 292 800 292 L 774 292 C 771 292 768 290 766 287 L 750 263 C 750 262 750 260 750 259 L 766 235 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="787" y="258">finger in</text>
<text x="787" y="272">bottom</text>
</g>
<path d="M 785 181 L 786 224" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 786 229 L 783 222 L 786 224 L 790 222 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 787 292 L 787 384" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 787 390 L 784 383 L 787 384 L 791 383 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1068 236 C 1070 233 1073 231 1076 231 L 1100 231 C 1103 231 1105 233 1107 236 L 1121 259 C 1122 261 1122 262 1121 263 L 1107 287 C 1105 290 1103 292 1100 292 L 1076 292 C 1073 292 1070 290 1068 287 L 1054 263 C 1054 262 1054 261 1054 259 L 1068 236 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1088" y="258">finger in</text>
<text x="1088" y="272">area</text>
</g>
<path d="M 1088 292 L 1088 386" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1088 391 L 1084 384 L 1088 386 L 1091 384 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 834 151 L 1048 246" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1053 248 L 1045 249 L 1048 246 L 1048 242 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 922 563 C 925 559 930 556 934 556 L 971 556 C 975 556 980 559 983 563 L 1004 594 C 1005 595 1005 597 1004 599 L 983 630 C 980 634 975 636 971 636 L 934 636 C 930 636 925 634 922 630 L 901 599 C 900 597 900 595 901 594 L 922 563 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="952" y="586">finger in</text>
<text x="952" y="600">bottom</text>
<text x="952" y="614">button != curr</text>
</g>
<path d="M 927 558 L 872 487" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 869 483 L 876 486 L 872 487 L 870 491 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 808 492 L 895 555" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 899 558 L 891 557 L 895 555 L 895 551 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1153 442 L 1358 442 Q 1368 442 1368 432 L 1368 42 Q 1368 32 1358 32 L 842 32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 837 32 L 844 29 L 842 32 L 844 36 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 712 441 L 668 441 Q 658 441 658 451 L 658 762" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 786 62 L 786 81 Q 786 91 786 101 L 786 114" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 786 120 L 783 113 L 786 114 L 790 113 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="1753" y="412" width="120" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="429">Check state of</text>
<text x="1813" y="443">all touches</text>
</g>
<path d="M 1813 452 L 1813 458 Q 1813 465 1813 470 L 1813 475" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 467 L 1813 476 L 1818 467" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<ellipse cx="1813" cy="72" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<path d="M 1813 87 L 1813 115 Q 1813 125 1813 135 L 1813 160" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 152 L 1813 161 L 1818 152" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="1763" y="17" width="100" height="40" fill="none" stroke="none" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="41">tp_post_softbutton_buttons()</text>
</g>
<path d="M 1813 192 L 1908 247 L 1813 302 L 1718 247 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="244">!buttons.click_pend</text>
<text x="1813" y="258">&amp;&amp; current == old</text>
</g>
<path d="M 1908 247 L 1968 247 Q 1978 247 1988 247 L 2056 247" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 2048 252 L 2057 247 L 2048 243" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1998" y="228" width="24" height="18" stroke-width="0"/>
<text x="1999" y="241">yes</text>
</g>
<path d="M 1813 302 L 1813 307 Q 1813 313 1813 317 L 1813 321" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 313 L 1813 322 L 1818 313" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1830" y="310" width="18" height="18" stroke-width="0"/>
<text x="1830" y="319">no</text>
</g>
<ellipse cx="2073" cy="247" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="2073" cy="247" rx="15" ry="15" fill="transparent" stroke="#ff0000" pointer-events="none"/>
<rect x="1713" y="102" width="200" height="70" rx="28" ry="28" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="120">current = buttons.state &amp; 0x01</text>
<text x="1813" y="134">old = buttons.old_state &amp; 0x01</text>
<text x="1813" y="148">button = 0</text>
<text x="1813" y="162">is_top = 0</text>
</g>
<path d="M 1813 172 L 1813 177 Q 1813 182 1813 186 L 1813 190" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 182 L 1813 191 L 1818 182" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1813 568 L 1918 626 L 1813 684 L 1708 626 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="630">All touches are in state none</text>
</g>
<path d="M 1813 684 L 1813 691 Q 1813 697 1813 702 L 1813 708" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 700 L 1813 709 L 1818 700" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1820" y="691" width="18" height="18" stroke-width="0"/>
<text x="1820" y="704">no</text>
</g>
<path d="M 1918 626 L 1936 626 Q 1946 626 1955 626 L 1971 626" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1963 631 L 1972 626 L 1963 622" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1920" y="634" width="24" height="18" stroke-width="0"/>
<text x="1920" y="643">yes</text>
</g>
<rect x="1973" y="606" width="150" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="2048" y="630">buttons.click_pend = 1</text>
</g>
<path d="M 2123 626 L 2128 626 Q 2133 626 2137 626 L 2141 626" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 2133 631 L 2142 626 L 2133 622" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1813 710 L 1938 795 L 1813 880 L 1688 795 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="785">(Some touches are in middle) ||</text>
<text x="1813" y="799">((Some touches are in right) &amp;&amp;</text>
<text x="1813" y="813">(Some touches are in left))</text>
</g>
<path d="M 1938 795 L 1943 795 Q 1948 795 1957 795 L 1966 795" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1958 800 L 1967 795 L 1958 791" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1941" y="803" width="24" height="18" stroke-width="0"/>
<text x="1942" y="812">yes</text>
</g>
<rect x="1968" y="775" width="160" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="2048" y="799">button = BTN_MIDDLE</text>
</g>
<path d="M 2128 795 L 2158 795 Q 2168 795 2168 805 L 2168 1197 Q 2168 1207 2158 1207 L 1913 1207" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1921 1203 L 1912 1207 L 1921 1212" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1813 323 L 1876 357 L 1813 392 L 1751 357 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="361">current</text>
</g>
<path d="M 1751 357 L 1606 357 Q 1596 357 1596 367 L 1596 1160" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1592 1152 L 1596 1161 L 1601 1152" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1687" y="338" width="18" height="18" stroke-width="0"/>
<text x="1687" y="351">no</text>
</g>
<path d="M 1813 392 L 1813 397 Q 1813 402 1813 406 L 1813 410" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 402 L 1813 411 L 1818 402" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1830" y="390" width="24" height="18" stroke-width="0"/>
<text x="1830" y="399">yes</text>
</g>
<path d="M 1813 902 L 1909 967 L 1813 1032 L 1717 967 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="971">Some touches are in right</text>
</g>
<path d="M 1909 967 L 1928 967 Q 1938 967 1948 967 L 1966 967" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1958 972 L 1967 967 L 1958 963" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1930" y="975" width="24" height="18" stroke-width="0"/>
<text x="1930" y="984">yes</text>
</g>
<path d="M 1813 880 L 1813 886 Q 1813 891 1813 895 L 1813 900" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 892 L 1813 901 L 1818 892" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1820" y="883" width="18" height="18" stroke-width="0"/>
<text x="1820" y="896">no</text>
</g>
<rect x="1968" y="947" width="160" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="2048" y="971">button = BTN_RIGHT</text>
</g>
<rect x="1733" y="1082" width="160" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="1106">button = BTN_LEFT</text>
</g>
<path d="M 1813 1032 L 1813 1047 Q 1813 1057 1813 1067 L 1813 1080" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 1072 L 1813 1081 L 1818 1072" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1815" y="1040" width="18" height="18" stroke-width="0"/>
<text x="1815" y="1053">no</text>
</g>
<rect x="1716" y="1162" width="195" height="90" rx="36" ry="36" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1814" y="1197">buttons.active = button</text>
<text x="1814" y="1211">buttons.active_is_top = is_top</text>
<text x="1814" y="1225">state = BUTTON_PRESSED</text>
</g>
<path d="M 1814 1252 L 1814 1282 Q 1814 1292 1804 1292 L 1713 1292 Q 1703 1292 1703 1302 L 1703 1330" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1699 1322 L 1703 1331 L 1708 1322" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1813 1122 L 1813 1122 Q 1813 1122 1813 1132 L 1813 1152 Q 1813 1162 1813 1161 L 1813 1160" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 1152 L 1813 1161 L 1818 1152" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 2128 967 L 2158 967 Q 2168 967 2168 977 L 2168 1197 Q 2168 1207 2158 1207 L 1913 1207" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1921 1203 L 1912 1207 L 1921 1212" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="1493" y="1162" width="205" height="90" rx="36" ry="36" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1596" y="1183">button = buttons.active</text>
<text x="1596" y="1197">is_top = buttons.active_is_top</text>
<text x="1596" y="1211">buttons.active = 0</text>
<text x="1596" y="1225">buttons.active_is_top = 0</text>
<text x="1596" y="1239">state = BUTTON_RELEASED</text>
</g>
<rect x="1628" y="1332" width="150" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1703" y="1356">buttons.click_pend = 0</text>
</g>
<path d="M 1703 1372 L 1703 1380 Q 1703 1387 1703 1393 L 1703 1400" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1699 1392 L 1703 1401 L 1708 1392" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1596 1252 L 1596 1282 Q 1596 1292 1606 1292 L 1693 1292 Q 1703 1292 1703 1302 L 1703 1330" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1699 1322 L 1703 1331 L 1708 1322" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1703 1402 L 1743 1422 L 1703 1442 L 1663 1422 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1703" y="1426">button</text>
</g>
<path d="M 1743 1422 L 1808 1422 Q 1818 1422 1818 1432 L 1818 1597 Q 1818 1607 1808 1607 L 1720 1607" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1728 1603 L 1719 1607 L 1728 1612" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1745" y="1403" width="18" height="18" stroke-width="0"/>
<text x="1745" y="1416">no</text>
</g>
<path d="M 1703 1442 L 1703 1452 Q 1703 1462 1703 1471 L 1703 1480" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1699 1472 L 1703 1481 L 1708 1472" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1705" y="1450" width="24" height="18" stroke-width="0"/>
<text x="1705" y="1459">yes</text>
</g>
<rect x="1618" y="1482" width="170" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1703" y="1499">tp_notify_softbutton(</text>
<text x="1703" y="1513">button, is_top, state)</text>
</g>
<path d="M 1703 1522 L 1703 1542 Q 1703 1552 1703 1562 L 1703 1590" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1699 1582 L 1703 1591 L 1708 1582" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<ellipse cx="1703" cy="1607" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="1703" cy="1607" rx="15" ry="15" fill="transparent" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="2158" cy="626" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="2158" cy="626" rx="15" ry="15" fill="transparent" stroke="#ff0000" pointer-events="none"/>
<path d="M 456 232 C 458 229 461 227 464 227 L 490 227 C 493 227 496 229 498 232 L 514 256 C 514 257 514 259 514 260 L 498 284 C 496 287 493 289 490 289 L 464 289 C 461 289 458 287 456 284 L 440 260 C 440 259 440 257 440 256 L 456 232 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="477" y="255">finger in</text>
<text x="477" y="269">top</text>
</g>
<path d="M 735 151 L 520 243" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 515 245 L 520 239 L 520 243 L 523 245 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="402" y="392" width="150" height="101" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="477" y="425">TOP_NEW</text>
<text x="477" y="439">on-entry:</text>
<text x="477" y="453">curr = button</text>
<text x="477" y="467">start enter timeout</text>
</g>
<path d="M 477 289 L 477 330 Q 477 340 477 350 L 477 385" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 477 391 L 473 384 L 477 385 L 480 384 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 552 442 L 648 442 Q 658 442 658 452 L 658 762" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="412" y="712" width="130" height="100" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="477" y="766">TOP</text>
</g>
<path d="M 542 762 L 658 762" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 524 571 C 527 568 531 566 535 566 L 569 566 C 573 566 577 568 580 571 L 600 594 C 601 596 601 597 600 598 L 580 621 C 577 625 573 626 569 626 L 535 626 C 531 626 527 625 524 621 L 504 598 C 504 597 504 596 504 594 L 524 571 Z" fill="#000000" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#FFFFFF" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="552" y="586">phys</text>
<text x="552" y="600">button</text>
<text x="552" y="614">press</text>
</g>
<path d="M 412 571 C 414 568 418 566 423 566 L 456 566 C 461 566 465 568 467 571 L 487 594 C 488 596 488 597 487 598 L 467 621 C 465 625 461 626 456 626 L 423 626 C 418 626 414 625 412 621 L 392 598 C 391 597 391 596 392 594 L 412 571 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="440" y="593">enter</text>
<text x="440" y="607">timeout</text>
</g>
<path d="M 280 563 C 283 559 288 556 292 556 L 329 556 C 333 556 338 559 341 563 L 362 594 C 363 595 363 597 362 599 L 341 630 C 338 634 333 636 329 636 L 292 636 C 288 636 283 634 280 630 L 259 599 C 258 597 258 595 259 594 L 280 563 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="310" y="586">finger in</text>
<text x="310" y="600">top</text>
<text x="310" y="614">button != curr</text>
</g>
<path d="M 477 493 L 547 562" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 551 566 L 544 563 L 547 562 L 549 558 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 477 493 L 442 561" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 440 565 L 440 558 L 442 561 L 446 561 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 465 492 L 368 557" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 364 560 L 368 554 L 368 557 L 371 559 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 344 556 L 396 494" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 399 490 L 397 497 L 396 494 L 392 493 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 440 626 L 470 703" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 472 708 L 466 703 L 470 703 L 472 700 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 552 626 L 503 704" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 501 708 L 501 700 L 503 704 L 507 704 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 427 712 L 355 641" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 351 637 L 359 640 L 355 641 L 354 645 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 636 325 C 638 321 642 319 645 319 L 675 319 C 679 319 682 321 684 325 L 702 353 C 702 354 702 356 702 357 L 684 385 C 682 389 679 391 675 391 L 645 391 C 642 391 638 389 636 385 L 618 357 C 618 356 618 354 618 353 L 636 325 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="660" y="345">finger in</text>
<text x="660" y="359">area or</text>
<text x="660" y="373">bottom</text>
</g>
<path d="M 702 355 L 1048 355 Q 1058 355 1058 365 L 1058 386" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1058 391 L 1055 384 L 1058 386 L 1062 384 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 612 355 L 518 355 Q 508 355 508 365 L 508 392" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 617 355 L 610 359 L 612 355 L 610 352 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="66" y="715" width="194" height="94" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="163" y="752">TOP_TO_IGNORE</text>
<text x="163" y="766">on-entry:</text>
<text x="163" y="780">start leave timeout</text>
</g>
<path d="M 301 678 C 304 674 309 672 314 672 L 352 672 C 357 672 362 674 365 678 L 387 708 C 388 709 388 711 387 713 L 365 742 C 362 746 357 748 352 748 L 314 748 C 309 748 304 746 301 742 L 279 713 C 278 711 278 709 279 708 L 301 678 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="333" y="700">finger in</text>
<text x="333" y="714">area or bottom</text>
</g>
<path d="M 304 769 C 307 765 311 762 316 762 L 352 762 C 357 762 361 765 364 769 L 385 799 C 386 801 386 803 385 805 L 364 835 C 361 840 357 842 352 842 L 316 842 C 311 842 307 840 304 835 L 283 805 C 282 803 282 801 283 799 L 304 769 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="334" y="785">finger in</text>
<text x="334" y="799">top</text>
<text x="334" y="813">button == curr</text>
</g>
<path d="M 278 710 L 262 756" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 260 761 L 259 753 L 262 756 L 266 756 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 279 797 L 260 762" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 282 801 L 275 797 L 279 797 L 281 793 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 412 762 L 391 716" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 388 711 L 395 716 L 391 716 L 388 719 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 409 767 L 386 802" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 411 763 L 410 771 L 409 767 L 405 767 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 238 712 L 282 642" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 285 637 L 284 645 L 282 642 L 278 641 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="98" y="391" width="130" height="100" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="163" y="431">IGNORE</text>
<text x="163" y="445">on-entry:</text>
<text x="163" y="459">curr =none</text>
</g>
<path d="M 135 541 C 138 538 142 536 146 536 L 180 536 C 184 536 188 538 191 541 L 211 564 C 211 566 211 567 211 568 L 191 591 C 188 595 184 596 180 596 L 146 596 C 142 596 138 595 135 591 L 115 568 C 114 567 114 566 115 564 L 135 541 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="163" y="563">leave</text>
<text x="163" y="577">timeout</text>
</g>
<path d="M 163 536 L 163 497" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 163 492 L 166 499 L 163 497 L 159 499 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 163 715 L 163 603" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 163 597 L 166 604 L 163 603 L 159 604 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 98 441 L 18 441 Q 8 441 8 431 L 8 42 Q 8 32 18 32 L 732 32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 737 32 L 730 36 L 732 32 L 730 29 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 66 762 L 18 762 Q 8 762 8 752 L 8 42 Q 8 32 18 32 L 732 32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 737 32 L 730 36 L 732 32 L 730 29 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1813 477 L 1876 511 L 1813 546 L 1751 511 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="515">touches in top?</text>
</g>
<path d="M 1876 511 L 1928 511 Q 1938 511 1938 518 L 1938 524" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1934 516 L 1938 525 L 1943 516" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1890" y="500" width="24" height="18" stroke-width="0"/>
<text x="1890" y="509">yes</text>
</g>
<rect x="1898" y="526" width="80" height="30" rx="12" ry="12" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1938" y="545">is_top = 1</text>
</g>
<path d="M 1938 556 L 1938 562 Q 1938 568 1928 568 L 1815 568" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1823 564 L 1814 568 L 1823 573" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1813 546 L 1813 554 Q 1813 562 1813 564 L 1813 566" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 558 L 1813 567 L 1818 558" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1820" y="550" width="18" height="18" stroke-width="0"/>
<text x="1820" y="559">no</text>
</g>
</g>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2190px" height="1624px" version="1.1">
<defs/>
<g transform="translate(0.5,0.5)">
<path d="M 862 425 L 894 418" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 899 417 L 893 422 L 894 418 L 891 415 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<ellipse cx="785" cy="151" rx="49.5" ry="30" fill="#ccccff" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="785" y="141">
NONE</text>
<text x="785" y="155">
on-entry:</text>
<text x="785" y="169">
curr = none</text>
</g>
<rect x="712" y="391" width="150" height="101" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="787" y="424">
BOTTOM_NEW</text>
<text x="787" y="438">
on-entry:</text>
<text x="787" y="452">
curr = button</text>
<text x="787" y="466">
start enter timeout</text>
</g>
<rect x="1023" y="392" width="130" height="100" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1088" y="425">
AREA</text>
<text x="1088" y="439">
on-entry:</text>
<text x="1088" y="453">
curr =area</text>
<text x="1088" y="467">
set_pointer()</text>
</g>
<path d="M 921 376 C 923 373 927 370 932 370 L 965 370 C 970 370 974 373 976 376 L 996 404 C 997 405 997 407 996 409 L 976 436 C 974 440 970 442 965 442 L 932 442 C 927 442 923 440 921 436 L 901 409 C 900 407 900 405 901 404 L 921 376 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="949" y="403">
finger in</text>
<text x="949" y="417">
area or top</text>
</g>
<rect x="722" y="712" width="130" height="100" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="787" y="766">
BOTTOM</text>
</g>
<path d="M 759 7 C 762 4 766 2 770 2 L 804 2 C 808 2 812 4 815 7 L 835 30 C 836 31 836 33 835 34 L 815 57 C 812 60 808 62 804 62 L 770 62 C 766 62 762 60 759 57 L 739 34 C 739 33 739 31 739 30 L 759 7 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="787" y="29">
finger</text>
<text x="787" y="43">
up</text>
</g>
<path d="M 694 571 C 697 568 701 566 705 566 L 739 566 C 743 566 747 568 750 571 L 770 594 C 771 596 771 597 770 598 L 750 621 C 747 625 743 626 739 626 L 705 626 C 701 626 697 625 694 621 L 674 598 C 674 597 674 596 674 594 L 694 571 Z" fill="#000000" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#FFFFFF" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="722" y="586">
phys</text>
<text x="722" y="600">
button</text>
<text x="722" y="614">
press</text>
</g>
<path d="M 997 419 L 1017 424" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1022 425 L 1014 427 L 1017 424 L 1016 420 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 798 571 C 800 568 804 566 809 566 L 842 566 C 847 566 851 568 853 571 L 873 594 C 874 596 874 597 873 598 L 853 621 C 851 625 847 626 842 626 L 809 626 C 804 626 800 625 798 621 L 778 598 C 777 597 777 596 778 594 L 798 571 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="826" y="593">
enter</text>
<text x="826" y="607">
timeout</text>
</g>
<path d="M 787 492 L 812 560" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 814 565 L 808 560 L 812 560 L 815 558 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 819 626 L 800 706" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 799 711 L 797 703 L 800 706 L 804 705 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 895 694 C 898 690 902 688 906 688 L 940 688 C 944 688 948 690 951 694 L 971 718 C 971 719 971 721 971 722 L 951 747 C 948 750 944 752 940 752 L 906 752 C 902 752 898 750 895 747 L 875 722 C 874 721 874 719 875 718 L 895 694 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="923" y="717">
finger in</text>
<text x="923" y="731">
area or top</text>
</g>
<path d="M 852 762 L 871 726" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 874 721 L 874 729 L 871 726 L 867 726 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="991" y="718" width="194" height="94" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1088" y="755">
BOTTOM_TO_AREA</text>
<text x="1088" y="769">
on-entry:</text>
<text x="1088" y="783">
start leave timeout</text>
</g>
<path d="M 971 720 L 988 759" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 991 764 L 985 759 L 988 759 L 991 756 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1060 541 C 1063 538 1067 536 1071 536 L 1105 536 C 1109 536 1113 538 1116 541 L 1136 564 C 1136 566 1136 567 1136 568 L 1116 591 C 1113 595 1109 596 1105 596 L 1071 596 C 1067 596 1063 595 1060 591 L 1040 568 C 1039 567 1039 566 1040 564 L 1060 541 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1088" y="563">
leave</text>
<text x="1088" y="577">
timeout</text>
</g>
<path d="M 1088 718 L 1088 603" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1088 597 L 1091 604 L 1088 603 L 1084 604 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1088 536 L 1088 498" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1088 493 L 1091 500 L 1088 498 L 1084 500 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 766 235 C 768 232 771 230 774 230 L 800 230 C 803 230 806 232 808 235 L 824 259 C 824 260 824 262 824 263 L 808 287 C 806 290 803 292 800 292 L 774 292 C 771 292 768 290 766 287 L 750 263 C 750 262 750 260 750 259 L 766 235 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="787" y="258">
finger in</text>
<text x="787" y="272">
bottom</text>
</g>
<path d="M 785 181 L 786 224" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 786 229 L 783 222 L 786 224 L 790 222 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 787 292 L 787 384" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 787 390 L 784 383 L 787 384 L 791 383 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1068 236 C 1070 233 1073 231 1076 231 L 1100 231 C 1103 231 1105 233 1107 236 L 1121 259 C 1122 261 1122 262 1121 263 L 1107 287 C 1105 290 1103 292 1100 292 L 1076 292 C 1073 292 1070 290 1068 287 L 1054 263 C 1054 262 1054 261 1054 259 L 1068 236 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1088" y="258">
finger in</text>
<text x="1088" y="272">
area</text>
</g>
<path d="M 1088 292 L 1088 386" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1088 391 L 1084 384 L 1088 386 L 1091 384 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 834 151 L 1048 246" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1053 248 L 1045 249 L 1048 246 L 1048 242 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 922 563 C 925 559 930 556 934 556 L 971 556 C 975 556 980 559 983 563 L 1004 594 C 1005 595 1005 597 1004 599 L 983 630 C 980 634 975 636 971 636 L 934 636 C 930 636 925 634 922 630 L 901 599 C 900 597 900 595 901 594 L 922 563 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="952" y="586">
finger in</text>
<text x="952" y="600">
bottom</text>
<text x="952" y="614">
button != curr</text>
</g>
<path d="M 927 558 L 872 487" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 869 483 L 876 486 L 872 487 L 870 491 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1050 718 L 988 641" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 985 637 L 992 641 L 988 641 L 987 645 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 722 626 L 783 707" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 786 711 L 779 708 L 783 707 L 785 703 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 787 492 L 726 562" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 723 566 L 725 558 L 726 562 L 730 563 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 896 772 C 899 768 903 765 908 765 L 944 765 C 949 765 953 768 956 772 L 977 802 C 978 804 978 806 977 808 L 956 838 C 953 843 949 845 944 845 L 908 845 C 903 845 899 843 896 838 L 875 808 C 874 806 874 804 875 802 L 896 772 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="926" y="788">
finger in</text>
<text x="926" y="802">
bottom</text>
<text x="926" y="816">
button == curr</text>
</g>
<path d="M 991 765 L 980 799" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 978 804 L 977 796 L 980 799 L 984 799 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 874 805 L 855 768" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 853 763 L 859 768 L 855 768 L 853 771 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 837 712 L 908 641" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 912 637 L 909 645 L 908 641 L 904 640 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 808 492 L 895 555" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 899 558 L 891 557 L 895 555 L 895 551 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1185 765 L 1358 765 Q 1368 765 1368 755 L 1368 42 Q 1368 32 1358 32 L 842 32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 837 32 L 844 29 L 842 32 L 844 36 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1153 442 L 1358 442 Q 1368 442 1368 432 L 1368 42 Q 1368 32 1358 32 L 842 32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 837 32 L 844 29 L 842 32 L 844 36 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 712 441 L 668 441 Q 658 441 658 451 L 658 862" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 786 62 L 786 81 Q 786 91 786 101 L 786 114" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 786 120 L 783 113 L 786 114 L 790 113 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="1753" y="472" width="120" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="489">
Check state of</text>
<text x="1813" y="503">
all touches</text>
</g>
<path d="M 1813 512 L 1813 518 Q 1813 524 1813 529 L 1813 534" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 526 L 1813 535 L 1818 526" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<ellipse cx="1813" cy="72" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<path d="M 1813 87 L 1813 115 Q 1813 125 1813 135 L 1813 160" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 152 L 1813 161 L 1818 152" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="1763" y="17" width="100" height="40" fill="none" stroke="none" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="41">
tp_post_softbutton_buttons()</text>
</g>
<path d="M 1813 212 L 1908 267 L 1813 322 L 1718 267 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="264">
!buttons.click_pend</text>
<text x="1813" y="278">
&amp;&amp; current == old</text>
</g>
<path d="M 1908 267 L 1968 267 Q 1978 267 1988 267 L 2056 267" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 2048 272 L 2057 267 L 2048 263" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1998" y="248" width="24" height="18" stroke-width="0"/>
<text x="1999" y="261">
yes</text>
</g>
<path d="M 1813 322 L 1813 332 Q 1813 342 1813 351 L 1813 360" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 352 L 1813 361 L 1818 352" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1815" y="330" width="18" height="18" stroke-width="0"/>
<text x="1815" y="339">
no</text>
</g>
<ellipse cx="2073" cy="267" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="2073" cy="267" rx="15" ry="15" fill="transparent" stroke="#ff0000" pointer-events="none"/>
<rect x="1708" y="112" width="200" height="60" rx="24" ry="24" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1808" y="132">
current = buttons.state &amp; 0x01</text>
<text x="1808" y="146">
old = buttons.old_state &amp; 0x01</text>
<text x="1808" y="160">
button = 0</text>
</g>
<path d="M 1813 172 L 1813 182 Q 1813 192 1813 201 L 1813 210" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 202 L 1813 211 L 1818 202" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1813 536 L 1928 607 L 1813 678 L 1698 607 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="611">
All touches are in state none</text>
</g>
<path d="M 1813 678 L 1813 684 Q 1813 690 1813 695 L 1813 700" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 692 L 1813 701 L 1818 692" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1820" y="683" width="18" height="18" stroke-width="0"/>
<text x="1820" y="696">
no</text>
</g>
<path d="M 1928 607 L 1938 607 Q 1948 607 1957 607 L 1966 607" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1958 612 L 1967 607 L 1958 603" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1930" y="615" width="24" height="18" stroke-width="0"/>
<text x="1930" y="624">
yes</text>
</g>
<rect x="1968" y="587" width="150" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="2043" y="611">
buttons.click_pend = 1</text>
</g>
<path d="M 2118 607 L 2128 607 Q 2138 607 2147 607 L 2156 607" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 2148 612 L 2157 607 L 2148 603" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1813 702 L 1938 787 L 1813 872 L 1688 787 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="777">
(Some touches are in middle) ||</text>
<text x="1813" y="791">
((Some touches are in right) &amp;&amp;</text>
<text x="1813" y="805">
(Some touches are in left))</text>
</g>
<path d="M 1938 787 L 1943 787 Q 1948 787 1957 787 L 1966 787" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1958 792 L 1967 787 L 1958 783" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1941" y="795" width="24" height="18" stroke-width="0"/>
<text x="1942" y="804">
yes</text>
</g>
<rect x="1968" y="767" width="160" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="2048" y="791">
button = BTN_MIDDLE</text>
</g>
<path d="M 2128 787 L 2158 787 Q 2168 787 2168 797 L 2168 1207 Q 2168 1217 2158 1217 L 1913 1217" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1921 1213 L 1912 1217 L 1921 1222" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1813 362 L 1876 396 L 1813 431 L 1751 396 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="400">
current</text>
</g>
<path d="M 1751 396 L 1601 396 Q 1591 396 1591 406 L 1591 1172 Q 1591 1182 1591 1182 L 1592 1182" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1596 1180 L 1591 1182 L 1596 1184" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1688" y="377" width="18" height="18" stroke-width="0"/>
<text x="1688" y="390">
no</text>
</g>
<path d="M 1813 431 L 1813 441 Q 1813 451 1813 461 L 1813 470" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 462 L 1813 471 L 1818 462" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1815" y="438" width="24" height="18" stroke-width="0"/>
<text x="1815" y="448">
yes</text>
</g>
<path d="M 1813 902 L 1909 967 L 1813 1032 L 1717 967 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="971">
Some touches are in right</text>
</g>
<path d="M 1909 967 L 1928 967 Q 1938 967 1948 967 L 1966 967" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1958 972 L 1967 967 L 1958 963" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1930" y="975" width="24" height="18" stroke-width="0"/>
<text x="1930" y="984">
yes</text>
</g>
<path d="M 1813 872 L 1813 880 Q 1813 887 1813 893 L 1813 900" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 892 L 1813 901 L 1818 892" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1820" y="883" width="18" height="18" stroke-width="0"/>
<text x="1820" y="896">
no</text>
</g>
<rect x="1968" y="947" width="160" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="2048" y="971">
button = BTN_RIGHT</text>
</g>
<rect x="1733" y="1082" width="160" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="1106">
button = BTN_LEFT</text>
</g>
<path d="M 1813 1032 L 1813 1047 Q 1813 1057 1813 1067 L 1813 1080" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 1072 L 1813 1081 L 1818 1072" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1815" y="1040" width="18" height="18" stroke-width="0"/>
<text x="1815" y="1053">
no</text>
</g>
<rect x="1716" y="1182" width="195" height="70" rx="28" ry="28" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1813" y="1214">
buttons.active = button</text>
<text x="1813" y="1228">
state = BUTTON_PRESSED</text>
</g>
<path d="M 1813 1252 L 1813 1282 Q 1813 1292 1803 1292 L 1713 1292 Q 1703 1292 1703 1302 L 1703 1330" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1699 1322 L 1703 1331 L 1708 1322" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1813 1122 L 1813 1142 Q 1813 1152 1813 1162 L 1813 1180" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1809 1172 L 1813 1181 L 1818 1172" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 2128 967 L 2158 967 Q 2168 967 2168 977 L 2168 1207 Q 2168 1217 2158 1217 L 1913 1217" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1921 1213 L 1912 1217 L 1921 1222" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="1493" y="1182" width="195" height="70" rx="28" ry="28" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1591" y="1207">
button = buttons.active</text>
<text x="1591" y="1221">
buttons.active = 0</text>
<text x="1591" y="1235">
state = BUTTON_RELEASED</text>
</g>
<rect x="1628" y="1332" width="150" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1703" y="1356">
buttons.click_pend = 0</text>
</g>
<path d="M 1703 1372 L 1703 1380 Q 1703 1387 1703 1393 L 1703 1400" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1699 1392 L 1703 1401 L 1708 1392" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1591 1252 L 1591 1282 Q 1591 1292 1601 1292 L 1693 1292 Q 1703 1292 1703 1302 L 1703 1330" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1699 1322 L 1703 1331 L 1708 1322" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1703 1402 L 1743 1422 L 1703 1442 L 1663 1422 Z" fill="#ffffc0" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1703" y="1426">
button</text>
</g>
<path d="M 1743 1422 L 1808 1422 Q 1818 1422 1818 1432 L 1818 1597 Q 1818 1607 1808 1607 L 1720 1607" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1728 1603 L 1719 1607 L 1728 1612" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1745" y="1403" width="18" height="18" stroke-width="0"/>
<text x="1745" y="1416">
no</text>
</g>
<path d="M 1703 1442 L 1703 1452 Q 1703 1462 1703 1471 L 1703 1480" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1699 1472 L 1703 1481 L 1708 1472" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" font-size="11px">
<rect fill="#ffffff" stroke="none" x="1705" y="1450" width="24" height="18" stroke-width="0"/>
<text x="1705" y="1459">
yes</text>
</g>
<rect x="1618" y="1482" width="170" height="40" rx="16" ry="16" fill="#ffffc0" stroke="#ff0000" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="1703" y="1499">
pointer_notify_button(</text>
<text x="1703" y="1513">
button, state)</text>
</g>
<path d="M 1703 1522 L 1703 1542 Q 1703 1552 1703 1562 L 1703 1590" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1699 1582 L 1703 1591 L 1708 1582" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/>
<ellipse cx="1703" cy="1607" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="1703" cy="1607" rx="15" ry="15" fill="transparent" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="2173" cy="607" rx="11" ry="11" fill="#000000" stroke="#ff0000" pointer-events="none"/>
<ellipse cx="2173" cy="607" rx="15" ry="15" fill="transparent" stroke="#ff0000" pointer-events="none"/>
<path d="M 456 232 C 458 229 461 227 464 227 L 490 227 C 493 227 496 229 498 232 L 514 256 C 514 257 514 259 514 260 L 498 284 C 496 287 493 289 490 289 L 464 289 C 461 289 458 287 456 284 L 440 260 C 440 259 440 257 440 256 L 456 232 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="477" y="255">
finger in</text>
<text x="477" y="269">
top</text>
</g>
<path d="M 722 762 L 668 762 Q 658 762 658 772 L 658 892 Q 658 902 668 902 L 1358 902 Q 1368 902 1368 892 L 1368 52" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 735 151 L 520 243" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 515 245 L 520 239 L 520 243 L 523 245 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="402" y="392" width="150" height="101" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="477" y="425">
TOP_NEW</text>
<text x="477" y="439">
on-entry:</text>
<text x="477" y="453">
curr = button</text>
<text x="477" y="467">
start enter timeout</text>
</g>
<path d="M 477 289 L 477 385" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 477 391 L 474 384 L 477 385 L 481 384 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 552 442 L 648 442 Q 658 442 658 452 L 658 872" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="412" y="712" width="130" height="100" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="477" y="766">
TOP</text>
</g>
<path d="M 542 762 L 648 762 Q 658 762 658 772 L 658 872" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 524 571 C 527 568 531 566 535 566 L 569 566 C 573 566 577 568 580 571 L 600 594 C 601 596 601 597 600 598 L 580 621 C 577 625 573 626 569 626 L 535 626 C 531 626 527 625 524 621 L 504 598 C 504 597 504 596 504 594 L 524 571 Z" fill="#000000" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#FFFFFF" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="552" y="586">
phys</text>
<text x="552" y="600">
button</text>
<text x="552" y="614">
press</text>
</g>
<path d="M 412 571 C 414 568 418 566 423 566 L 456 566 C 461 566 465 568 467 571 L 487 594 C 488 596 488 597 487 598 L 467 621 C 465 625 461 626 456 626 L 423 626 C 418 626 414 625 412 621 L 392 598 C 391 597 391 596 392 594 L 412 571 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="440" y="593">
enter</text>
<text x="440" y="607">
timeout</text>
</g>
<path d="M 280 563 C 283 559 288 556 292 556 L 329 556 C 333 556 338 559 341 563 L 362 594 C 363 595 363 597 362 599 L 341 630 C 338 634 333 636 329 636 L 292 636 C 288 636 283 634 280 630 L 259 599 C 258 597 258 595 259 594 L 280 563 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="310" y="586">
finger in</text>
<text x="310" y="600">
top</text>
<text x="310" y="614">
button != curr</text>
</g>
<path d="M 477 493 L 547 562" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 551 566 L 544 563 L 547 562 L 549 558 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 477 493 L 442 561" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 440 565 L 440 558 L 442 561 L 446 561 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 465 492 L 368 557" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 364 560 L 368 554 L 368 557 L 371 559 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 344 556 L 396 494" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 399 490 L 397 497 L 396 494 L 392 493 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 440 626 L 470 703" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 472 708 L 466 703 L 470 703 L 472 700 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 552 626 L 503 704" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 501 708 L 501 700 L 503 704 L 507 704 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 427 712 L 355 641" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 351 637 L 359 640 L 355 641 L 354 645 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 636 325 C 638 321 642 319 645 319 L 675 319 C 679 319 682 321 684 325 L 702 353 C 702 354 702 356 702 357 L 684 385 C 682 389 679 391 675 391 L 645 391 C 642 391 638 389 636 385 L 618 357 C 618 356 618 354 618 353 L 636 325 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="660" y="345">
finger in</text>
<text x="660" y="359">
area or</text>
<text x="660" y="373">
bottom</text>
</g>
<path d="M 702 355 L 1048 355 Q 1058 355 1058 365 L 1058 386" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 1058 391 L 1055 384 L 1058 386 L 1062 384 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 612 355 L 518 355 Q 508 355 508 365 L 508 392" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 617 355 L 610 359 L 612 355 L 610 352 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="66" y="715" width="194" height="94" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="163" y="752">
TOP_TO_IGNORE</text>
<text x="163" y="766">
on-entry:</text>
<text x="163" y="780">
start leave timeout</text>
</g>
<path d="M 301 678 C 304 674 309 672 314 672 L 352 672 C 357 672 362 674 365 678 L 387 708 C 388 709 388 711 387 713 L 365 742 C 362 746 357 748 352 748 L 314 748 C 309 748 304 746 301 742 L 279 713 C 278 711 278 709 279 708 L 301 678 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="333" y="700">
finger in</text>
<text x="333" y="714">
area or bottom</text>
</g>
<path d="M 304 769 C 307 765 311 762 316 762 L 352 762 C 357 762 361 765 364 769 L 385 799 C 386 801 386 803 385 805 L 364 835 C 361 840 357 842 352 842 L 316 842 C 311 842 307 840 304 835 L 283 805 C 282 803 282 801 283 799 L 304 769 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="334" y="785">
finger in</text>
<text x="334" y="799">
top</text>
<text x="334" y="813">
button == curr</text>
</g>
<path d="M 278 710 L 262 756" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 260 761 L 259 753 L 262 756 L 266 756 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 279 797 L 260 762" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 282 801 L 275 797 L 279 797 L 281 793 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 412 762 L 391 716" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 388 711 L 395 716 L 391 716 L 388 719 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 409 767 L 386 802" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 411 763 L 410 771 L 409 767 L 405 767 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 238 712 L 282 642" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 285 637 L 284 645 L 282 642 L 278 641 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<rect x="98" y="391" width="130" height="100" rx="6" ry="6" fill="#ccffcc" stroke="#000000" stroke-width="2" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="163" y="431">
IGNORE</text>
<text x="163" y="445">
on-entry:</text>
<text x="163" y="459">
curr =none</text>
</g>
<path d="M 135 541 C 138 538 142 536 146 536 L 180 536 C 184 536 188 538 191 541 L 211 564 C 211 566 211 567 211 568 L 191 591 C 188 595 184 596 180 596 L 146 596 C 142 596 138 595 135 591 L 115 568 C 114 567 114 566 115 564 L 135 541 Z" fill="#ffd966" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/>
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
<text x="163" y="563">
leave</text>
<text x="163" y="577">
timeout</text>
</g>
<path d="M 163 536 L 163 497" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 163 492 L 166 499 L 163 497 L 159 499 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 163 715 L 163 603" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 163 597 L 166 604 L 163 603 L 159 604 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 98 441 L 18 441 Q 8 441 8 431 L 8 42 Q 8 32 18 32 L 732 32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 737 32 L 730 36 L 732 32 L 730 29 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 66 762 L 18 762 Q 8 762 8 752 L 8 42 Q 8 32 18 32 L 732 32" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
<path d="M 737 32 L 730 36 L 732 32 L 730 29 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 37 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 489 KiB

After

Width:  |  Height:  |  Size: 63 KiB

View file

@ -1,9 +0,0 @@
:orphan:
===
404
===
This page has permanently moved, probably to `<@TARGET@>`_
This placeholder page will be removed soon.

View file

@ -1,144 +0,0 @@
.. _absolute_axes:
==============================================================================
Absolute axes
==============================================================================
Devices with absolute axes are those that send positioning data for an axis in
a device-specific coordinate range, defined by a minimum and a maximum value.
Compare this to relative devices (e.g. a mouse) that can only detect
directional data, not positional data.
libinput supports three types of devices with absolute axes:
- multi-touch screens
- single-touch screens
- :ref:`graphics tablets <tablet-support>`
Touchpads are technically absolute devices but libinput converts the axis values
to directional motion and posts events as relative events. Touchpads do not count
as absolute devices in libinput.
For all absolute devices in libinput, the default unit for x/y coordinates is
in mm off the top left corner on the device, or more specifically off the
device's sensor. If the device is physically rotated from its natural
position and this rotation was communicated to libinput (e.g. by setting
the device left-handed),
the coordinate origin is the top left corner in the current rotation.
.. _absolute_axes_handling:
------------------------------------------------------------------------------
Handling of absolute coordinates
------------------------------------------------------------------------------
In most use-cases, absolute input devices are mapped to a single screen. For
direct input devices such as touchscreens the aspect ratio of the screen and
the device match. Mapping the input device position to the output position is
thus a simple mapping between two coordinates. libinput provides the API for
this with
- **libinput_event_pointer_get_absolute_x_transformed()** for pointer events
- **libinput_event_touch_get_x_transformed()** for touch events
libinput's API only provides the call to map into a single coordinate range.
If the coordinate range has an offset, the compositor is responsible for
applying that offset after the mapping. For example, if the device is mapped
to the right of two outputs, add the output offset to the transformed
coordinate.
.. _absolute_axes_nores:
------------------------------------------------------------------------------
Devices without x/y resolution
------------------------------------------------------------------------------
An absolute device that does not provide a valid resolution is considered
buggy and must be fixed in the kernel. Some touchpad devices do not
provide resolution, those devices are correctly handled within libinput
(touchpads are not absolute devices, as mentioned above).
.. _calibration:
------------------------------------------------------------------------------
Calibration of absolute devices
------------------------------------------------------------------------------
Absolute devices may require calibration to map precisely into the output
range required. This is done by setting a transformation matrix, see
**libinput_device_config_calibration_set_matrix()** which is applied to
each input coordinate.
.. math::
\begin{pmatrix}
cos\theta & -sin\theta & xoff \\
sin\theta & cos\theta & yoff \\
0 & 0 & 1
\end{pmatrix} \begin{pmatrix}
x \\ y \\ 1
\end{pmatrix}
:math:`\theta` is the rotation angle. The offsets :math:`xoff` and :math:`yoff` are
specified in device dimensions, i.e. a value of 1 equals one device width or
height. Note that rotation applies to the device's origin, rotation usually
requires an offset to move the coordinates back into the original range.
The most common matrices are:
- 90 degree clockwise:
.. math::
\begin{pmatrix}
0 & -1 & 1 \\
1 & 0 & 0 \\
0 & 0 & 1
\end{pmatrix}
- 180 degree clockwise:
.. math::
\begin{pmatrix}
-1 & 0 & 1 \\
0 & -1 & 1 \\
0 & 0 & 1
\end{pmatrix}
- 270 degree clockwise:
.. math::
\begin{pmatrix}
0 & 1 & 0 \\
-1 & 0 & 1 \\
0 & 0 & 1
\end{pmatrix}
- reflection along y axis:
.. math::
\begin{pmatrix}
-1 & 0 & 1 \\
1 & 0 & 0 \\
0 & 0 & 1
\end{pmatrix}
See Wikipedia's
`Transformation Matrix article <http://en.wikipedia.org/wiki/Transformation_matrix>`_
for more information on the matrix maths. See
**libinput_device_config_calibration_get_default_matrix()** for how these
matrices must be supplied to libinput.
Once applied, any x and y axis value has the calibration applied before it
is made available to the caller. libinput does not provide access to the
raw coordinates before the calibration is applied.
.. _absolute_axes_nonorm:
------------------------------------------------------------------------------
Why x/y coordinates are not normalized
------------------------------------------------------------------------------
x/y are not given in :ref:`normalized coordinates <motion_normalization>`
([0..1]) for one simple reason: the aspect ratio of virtually all current
devices is something other than 1:1. A normalized axes thus is only useful to
determine that the stylus is e.g. at 78% from the left, 34% from the top of
the device. Without knowing the per-axis resolution, these numbers are
meaningless. Worse, calculation based on previous coordinates is simply wrong:
a movement from 0/0 to 50%/50% is not a 45-degree line.
This could be alleviated by providing resolution and information about the
aspect ratio to the caller. Which shifts processing and likely errors into the
caller for little benefit. Providing the x/y axes in mm from the outset
removes these errors.

View file

@ -1,153 +0,0 @@
.. _absolute_coordinate_ranges:
==============================================================================
Coordinate ranges for absolute axes
==============================================================================
libinput requires that all touchpads provide a correct axis range and
resolution. These are used to enable or disable certain features or adapt
the interaction with the touchpad. For example, the software button area is
narrower on small touchpads to avoid reducing the interactive surface too
much. Likewise, palm detection works differently on small touchpads as palm
interference is less likely to happen.
Touchpads with incorrect axis ranges generate error messages
in the form:
<blockquote>
Axis 0x35 value 4000 is outside expected range [0, 3000]
</blockquote>
This error message indicates that the ABS_MT_POSITION_X axis (i.e. the x
axis) generated an event outside the expected range of 0-3000. In this case
the value was 4000.
This discrepancy between the coordinate range the kernels advertises vs.
what the touchpad sends can be the source of a number of perceived
bugs in libinput.
.. _absolute_coordinate_ranges_fix:
------------------------------------------------------------------------------
Measuring and fixing touchpad ranges
------------------------------------------------------------------------------
To fix the touchpad you need to:
#. measure the physical size of your touchpad in mm
#. run the ``libinput measure touchpad-size`` tool
#. verify the hwdb entry provided by this tool
#. test locally
#. send a patch to the `systemd project <https://github.com/systemd/systemd>`_.
Detailed explanations are below.
The ``libinput measure touchpad-size`` tool is an interactive tool. It must
be called with the physical dimensions of the touchpad in mm. In the example
below, we use 100mm wide and 55mm high. The tool will find the touchpad device
automatically.
::
$> sudo libinput measure touchpad-size 100x55
Using "Touchpad SynPS/2 Synaptics TouchPad": /dev/input/event4
Kernel specified touchpad size: 99.7x75.9mm
User specified touchpad size: 100.0x55.0mm
Kernel axis range: x [1024..5112], y [2024..4832]
Detected axis range: x [ 0.. 0], y [ 0.. 0]
Move one finger along all edges of the touchpad
until the detected axis range stops changing.
...
Move the finger around until the detected axis range matches the data sent
by the device. ``Ctrl+C`` terminates the tool and prints a
suggested hwdb entry. ::
...
Kernel axis range: x [1024..5112], y [2024..4832]
^C
Detected axis range: x [2072..4880], y [2159..4832]
Resolutions calculated based on user-specified size: x 28, y 49 units/mm
Suggested hwdb entry:
Note: the dmi modalias match is a guess based on your machine's modalias:
dmi:bvnLENOVO:bvrGJET72WW(2.22):bd02/21/2014:svnLENOVO:pn20ARS25701:pvrThinkPadT440s:rvnLENOVO:rn20ARS25701:rvrSDK0E50512STD:cvnLENOVO:ct10:cvrNotAvailable:
Please verify that this is the most sensible match and adjust if necessary.
-8<--------------------------
# Laptop model description (e.g. Lenovo X1 Carbon 5th)
evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO:*pvrThinkPadT440s*
EVDEV_ABS_00=2072:4880:28
EVDEV_ABS_01=2159:4832:49
EVDEV_ABS_35=2072:4880:28
EVDEV_ABS_36=2159:4832:49
-8<--------------------------
Instructions on what to do with this snippet are in /usr/lib/udev/hwdb.d/60-evdev.hwdb
If there are discrepancies between the coordinate range the kernels
advertises and what the touchpad sends, the hwdb entry should be added to the
``60-evdev.hwdb`` file provided by the `systemd project <https://github.com/systemd/systemd>`_.
An example commit can be found
`here <https://github.com/systemd/systemd/commit/26f667eac1c5e89b689aa0a1daef6a80f473e045>`_.
The ``libinput measure touchpad-size`` tool attempts to provide the correct
dmi match but it does require user verification.
In most cases the dmi match can and should be trimmed to the system vendor (``svn``)
and the product version (``pvr``) or product name (``pn``), with everything else
replaced by a wildcard (``*``). In the above case, the match string is:
::
evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO:*pvrThinkPadT440s*
As a general rule: for Lenovo devices use ``pvr`` and for all others use
``pn``.
.. note:: hwdb match strings only allow for alphanumeric ascii characters. Use a
wildcard (* or ?, whichever appropriate) for special characters.
The actual axis overrides are in the form:
::
# axis number=min:max:resolution
EVDEV_ABS_00=2072:4880:28
or, if the range is correct but the resolution is wrong
::
# axis number=::resolution
EVDEV_ABS_00=::28
Note the leading single space. The axis numbers are in hex and can be found
in ``linux/input-event-codes.h``. For touchpads ``ABS_X``, ``ABS_Y``,
``ABS_MT_POSITION_X`` and ``ABS_MT_POSITION_Y`` are required.
.. note:: The touchpad's ranges and/or resolution should only be fixed when
there is a significant discrepancy. A few units do not make a
difference and a resolution that is off by 2 or less usually does
not matter either.
Once a match and override rule has been found, follow the instructions at
the top of the
`60-evdev.hwdb <https://github.com/systemd/systemd/blob/main/hwdb.d/60-evdev.hwdb>`_
file to save it locally and trigger the udev hwdb reload. Rebooting is
always a good idea. If the match string is correct, the new properties will
show up in the
output of
::
udevadm info /sys/class/input/event4
Adjust the command for the event node of your touchpad.
A udev builtin will apply the new axis ranges automatically.
When the axis override is confirmed to work, please submit it as a pull
request to the `systemd project <https://github.com/systemd/systemd>`_.

View file

@ -1,416 +0,0 @@
.. _architecture:
==============================================================================
libinput's internal architecture
==============================================================================
This page provides an outline of libinput's internal architecture. The goal
here is to get the high-level picture across and point out the components
and their interplay to new developers.
The public facing API is in ``libinput.c``, this file is thus the entry point
for almost all API calls. General device handling is in ``evdev.c`` with the
device-type-specific implementations in ``evdev-<type>.c``. It is not
necessary to understand all of libinput to contribute a patch.
As of libinput 1.29 libinput has an internal plugin pipeline that modifies
the event stream before libinput proper sees it, see
:ref:`architecture-plugins`.
:ref:`architecture-contexts` is the only user-visible implementation detail,
everything else is purely internal implementation and may change when
required.
.. _architecture-contexts:
------------------------------------------------------------------------------
The udev and path contexts
------------------------------------------------------------------------------
The first building block is the "context" which can be one of
two types, "path" and "udev". See **libinput_path_create_context()** and
**libinput_udev_create_context()**. The path/udev specific bits are in
``path-seat.c`` and ``udev-seat.c``. This includes the functions that add new
devices to a context.
.. graphviz::
digraph context
{
compound=true;
rankdir="LR";
node [
shape="box";
]
libudev [label="libudev 'add' event"]
udev [label="**libinput_udev_create_context()**"];
udev_backend [label="udev-specific backend"];
context [label="libinput context"]
udev -> udev_backend;
libudev -> udev_backend;
udev_backend -> context;
}
The udev context provides automatic device hotplugging as udev's "add"
events are handled directly by libinput. The path context requires that the
caller adds devices.
.. graphviz::
digraph context
{
compound=true;
rankdir="LR";
node [
shape="box";
]
path [label="**libinput_path_create_context()**"];
path_backend [label="path-specific backend"];
xdriver [label="**libinput_path_add_device()**"]
context [label="libinput context"]
path -> path_backend;
xdriver -> path_backend;
path_backend -> context;
}
As a general rule: all Wayland compositors use a udev context, the X.org
stack uses a path context.
Which context was initialized only matters for creating/destroying a context
and adding devices. The device handling itself is the same for both types of
context.
.. _architecture-device:
------------------------------------------------------------------------------
Device initialization
------------------------------------------------------------------------------
libinput only supports evdev devices, all the device initialization is done
in ``evdev.c``. Much of the libinput public API is also a thin wrapper around
the matching implementation in the evdev device.
There is a 1:1 mapping between libinput devices and ``/dev/input/eventX``
device nodes.
.. graphviz::
digraph context
{
compound=true;
rankdir="LR";
node [
shape="box";
]
devnode [label="/dev/input/event0"]
libudev [label="libudev 'add' event"]
xdriver [label="**libinput_path_add_device()**"]
context [label="libinput context"]
evdev [label="evdev_device_create()"]
devnode -> xdriver;
devnode -> libudev;
xdriver -> context;
libudev -> context;
context->evdev;
}
Entry point for all devices is ``evdev_device_create()``, this function
decides to create a ``struct evdev_device`` for the given device node.
Based on the udev tags (e.g. ``ID_INPUT_TOUCHPAD``), a
:ref:`architecture-dispatch` is initialized. All event handling is then in this
dispatch.
Rejection of devices and the application of quirks is generally handled in
``evdev.c`` as well. Common functionality shared across multiple device types
(like button-scrolling) is also handled here.
.. _architecture-dispatch:
------------------------------------------------------------------------------
Device-type specific event dispatch
------------------------------------------------------------------------------
Depending on the device type, ``evdev_configure_device`` creates the matching
``struct evdev_dispatch``. This dispatch interface contains the function
pointers to handle events. Four such dispatch methods are currently
implemented: touchpad, tablet, tablet pad, and the fallback dispatch which
handles mice, keyboards and touchscreens.
.. graphviz::
digraph context
{
compound=true;
rankdir="LR";
node [
shape="box";
]
evdev [label="evdev_device_create()"]
fallback [label="evdev-fallback.c"]
touchpad [label="evdev-mt-touchpad.c"]
tablet [label="evdev-tablet.c"]
pad [label="evdev-tablet-pad.c"]
evdev -> fallback;
evdev -> touchpad;
evdev -> tablet;
evdev -> pad;
}
Event dispatch is done per "evdev frame", a collection of events up until including
the ``SYN_REPORT``. One such ``struct evdev_frame`` represents all state **updates**
to the previous frame.
While ``evdev.c`` pulls the event out of libevdev, the actual handling of the
events is performed within the dispatch method.
.. graphviz::
digraph context
{
compound=true;
rankdir="LR";
node [
shape="box";
]
evdev [label="evdev_device_dispatch()"]
plugins [label="plugin pipeline"]
fallback [label="fallback_interface_process()"];
touchpad [label="tp_interface_process()"]
tablet [label="tablet_process()"]
pad [label="pad_process()"]
evdev -> plugins;
plugins -> fallback;
plugins -> touchpad;
plugins -> tablet;
plugins -> pad;
}
The dispatch methods then look at the ``struct evdev_frame`` and proceed to
update the state.
.. _architecture-plugins:
------------------------------------------------------------------------------
The Plugin Pipeline
------------------------------------------------------------------------------
As of libinput 1.29 libinput has an **internal** plugin pipeline. These plugins
logically sit between libevdev and the :ref:`architecture-dispatch` and modify
the device and/or event stream. The primary motivation of such plugins is that
modifying the event stream is often simpler than analyzing the state later.
Plugins are loaded on libinput context startup and are executed in-order. The last
plugin is the hardcoded `evdev-plugin.c` which takes the modified event stream and
passes the events to the dispatch.
.. graphviz::
digraph context
{
compound=true;
rankdir="LR";
node [
shape="box";
]
evdev [label="evdev_device_dispatch()"]
p1 [label="P1"]
p2 [label="P2"]
p3 [label="P3"]
ep [label="evdev-plugin"]
fallback [label="fallback_interface_process()"];
touchpad [label="tp_interface_process()"]
tablet [label="tablet_process()"]
pad [label="pad_process()"]
evdev -> p1;
p1 -> p2;
p2 -> p3;
p3 -> ep;
ep -> fallback;
ep -> touchpad;
ep -> tablet;
ep -> pad;
}
Each plugin may not only modify the current event frame (this includes adding/removing events
from the frame), it may also append or prepend additional event frames. For
example the tablet proximity-timer plugin adds proximity in/out events to the
event stream.
.. graphviz::
digraph context
{
compound=true;
rankdir="LR";
node [
shape="box";
]
n0 [label= "", shape=none,height=.0,width=.0]
n1 [label= "", shape=none,height=.0,width=.0]
p1 [label="P1"]
p2 [label="P2"]
p3 [label="P3"]
ep [label="evdev-plugin"]
n0 -> p1 [label="F1"];
p1 -> p2 [label="F1"];
p2 -> p3 [label="F1,F2"];
p3 -> ep [label="F3,F1,F2"];
ep -> n1 [label="F3,F1,F2"];
}
In the diagram above, the plugin ``P2`` *appends* a new frame (``F2``), the plugin ``P3``
*prepends* a new frame (``F3``). The original event frame ``F1`` thus becomes the event frame
sequence ``F3``, ``F1``, ``F2`` by the time it reaches the :ref:`architecture-dispatch`.
Note that each plugin only sees one event frame at a time, so ``P3`` would see ``F1`` first,
decides to prepend ``F3`` and passes ``F1`` through. It then sees ``F2`` but does nothing with
it (optionally modified in-place).
.. _architecture-configuration:
------------------------------------------------------------------------------
Device configuration
------------------------------------------------------------------------------
All device-specific configuration is handled through ``struct
libinput_device_config_FOO`` instances. These are set up during device init
and provide the function pointers for the ``get``, ``set``, ``get_default``
triplet of configuration queries (or more, where applicable).
For example, the ``struct tablet_dispatch`` for tablet devices has a
``struct libinput_device_config_accel``. This struct is set up with the
required function pointers to change the profiles.
.. graphviz::
digraph context
{
compound=true;
rankdir="LR";
node [
shape="box";
]
tablet [label="struct tablet_dispatch"]
config [label="struct libinput_device_config_accel"];
tablet_config [label="tablet_accel_config_set_profile()"];
tablet->config;
config->tablet_config;
}
When the matching ``**libinput_device_config_set_FOO()**`` is called, this goes
through to the config struct and invokes the function there. Thus, it is
possible to have different configuration functions for a mouse vs a
touchpad, even though the interface is the same.
.. graphviz::
digraph context
{
compound=true;
rankdir="LR";
node [
shape="box";
]
libinput [label="**libinput_device_config_accel_set_profile()**"];
tablet_config [label="tablet_accel_config_set_profile()"];
libinput->tablet_config;
}
.. _architecture-filter:
------------------------------------------------------------------------------
Pointer acceleration filters
------------------------------------------------------------------------------
All pointer acceleration is handled in the ``filter.c`` file and its
associated files.
The ``struct motion_filter`` is initialized during device init, whenever
deltas are available they are passed to ``filter_dispatch()``. This function
returns a set of :ref:`normalized coordinates <motion_normalization_customization>`.
All actual acceleration is handled within the filter, the device itself has
no further knowledge. Thus it is possible to have different acceleration
filters for the same device types (e.g. the Lenovo X230 touchpad has a
custom filter).
.. graphviz::
digraph context
{
compound=true;
rankdir="LR";
node [
shape="box";
]
fallback [label="fallback deltas"];
touchpad [label="touchpad deltas"];
tablet [label="tablet deltas"];
filter [label="filter_dispatch"];
fallback->filter;
touchpad->filter;
tablet->filter;
flat [label="accelerator_interface_flat()"];
x230 [label="accelerator_filter_x230()"];
pen [label="tablet_accelerator_filter_flat_pen()"];
filter->flat;
filter->x230;
filter->pen;
}
Most filters convert the deltas (incl. timestamps) to a motion speed and
then apply a so-called profile function. This function returns a factor that
is then applied to the current delta, converting it into an accelerated
delta. See :ref:`pointer-acceleration` for more details.
the current

View file

@ -1,299 +0,0 @@
.. _building_libinput:
==============================================================================
libinput build instructions
==============================================================================
.. contents::
:local:
:backlinks: entry
Instructions on how to build libinput and its tools and how to build against
libinput.
The build instruction on this page detail how to overwrite your
system-provided libinput with one from the git repository,
see :ref:`reverting_install` to revert to the previous state.
.. _distribution_repos:
------------------------------------------------------------------------------
Distribution repositories for libinput from git
------------------------------------------------------------------------------
Some distributions provide package repositories for users that want to test
the latest libinput without building it manually.
.. note:: The list below is provided for convenience. The libinput community
cannot provide any guarantees that the packages in those repositories are
correct, up-to-date and/or unmodified from the git branch. Due diligence
is recommended.
The following repositories provide an up-to-date package for libinput:
- **Arch:** https://aur.archlinux.org/packages/libinput-git/
- **Fedora:** https://copr.fedorainfracloud.org/coprs/whot/libinput-git/
Please follow the respective repositories for instructions on how to enable
the repository and install libinput.
.. _building:
------------------------------------------------------------------------------
Building libinput
------------------------------------------------------------------------------
libinput uses `meson <https://www.mesonbuild.com>`_ and
`ninja <https://www.ninja-build.org>`_. A build is usually the three-step
process below. A successful build requires the
:ref:`building_dependencies` to be installed before running meson.
::
$> git clone https://gitlab.freedesktop.org/libinput/libinput
$> cd libinput
$> meson setup --prefix=/usr builddir/
$> ninja -C builddir/
$> sudo ninja -C builddir/ install
When running libinput versions 1.11.x or earlier, you must run
::
$> sudo systemd-hwdb update
Additional options may also be specified. For example:
::
$> meson setup --prefix=/usr -Ddocumentation=false builddir/
We recommend that users disable the documentation, it's not usually required
for testing and reduces the number of dependencies needed.
The ``prefix`` or other options can be changed later with the
``meson configure`` command. For example:
::
$> meson configure builddir/ -Dprefix=/some/other/prefix -Ddocumentation=true
$> ninja -C builddir
$> sudo ninja -C builddir/ install
Running ``meson configure builddir/`` with no other arguments lists all
configurable options meson provides.
To rebuild from scratch, simply remove the build directory and run meson
again:
::
$> rm -r builddir/
$> meson setup --prefix=....
.. _verifying_install:
..............................................................................
Verifying the install
..............................................................................
To verify the install worked correctly, check that libinput.so.x.x.x is in
the library path and that all symlinks point to the new library.
::
$> ldconfig -p | grep libinput | awk '{print $NF}' | xargs ls -l
lrwxrwxrwx 1 root root 14 lug 22 13:06 /usr/lib/x86_64-linux-gnu/libinput.so -> libinput.so.10
lrwxrwxrwx 1 root root 19 lug 22 13:06 /usr/lib/x86_64-linux-gnu/libinput.so.10 -> libinput.so.10.13.0
-rwxr-xr-x 1 root root 1064144 lug 22 13:06 /usr/lib/x86_64-linux-gnu/libinput.so.10.13.0
.. _reverting_install:
..............................................................................
Reverting to the system-provided libinput package
..............................................................................
The recommended way to revert to the system install is to use the package
manager to reinstall the libinput package. In some cases, this may leave
files in the system (e.g. ``/usr/lib/libinput.la``) but these files are
usually harmless. To definitely remove all files, run the following command
from the libinput source directory:
::
$> sudo ninja -C builddir/ uninstall
# WARNING: Do not restart the computer/X/the Wayland compositor after
# uninstall, reinstall the system package immediately!
The following commands reinstall the current system package for libinput,
overwriting manually installed files.
- **Debian/Ubuntu** based distributions: ``sudo apt-get install --reinstall libinput``
- **Fedora 22** and later: ``sudo dnf reinstall libinput``
- **RHEL/CentOS/Fedora 21** and earlier: ``sudo yum reinstall libinput``
- **openSUSE**: ``sudo zypper install --force libinput10``
- **Arch**: ``sudo pacman -S libinput``
.. _building_selinux:
..............................................................................
SELinux adjustments
..............................................................................
.. note:: This section only applies to meson version < 0.42.0
On systems with SELinux, overwriting the distribution-provided package with
a manually built libinput may cause SELinux denials. This usually manifests
when gdm does not start because it is denied access to libinput. The journal
shows a log message in the form of:
::
May 25 15:28:42 localhost.localdomain audit[23268]: AVC avc: denied { execute } for pid=23268 comm="gnome-shell" path="/usr/lib64/libinput.so.10.12.2" dev="dm-0" ino=1709093 scontext=system_u:system_r:xdm_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
May 25 15:28:42 localhost.localdomain org.gnome.Shell.desktop[23270]: /usr/bin/gnome-shell: error while loading shared libraries: libinput.so.10: failed to map segment from shared object
The summary of this error message is that gdm's gnome-shell runs in the
``system_u:system_r:xdm_t`` context but libinput is installed with the
context ``unconfined_u:object_r:user_home_t``.
To avoid this issue, restore the SELinux context for any system files.
::
$> sudo restorecon /usr/lib*/libinput.so.*
This issue is tracked in https://github.com/mesonbuild/meson/issues/1967.
.. _building_dependencies:
------------------------------------------------------------------------------
Build dependencies
------------------------------------------------------------------------------
libinput has a few build-time dependencies that must be installed prior to
running meson.
.. hint:: The build dependencies for some distributions can be found in the
`GitLab Continuous Integration file <https://gitlab.freedesktop.org/libinput/libinput/blob/main/.gitlab-ci.yml>`_.
Search for **FEDORA_PACKAGES** in the **variables:** definition
and check the list for an entry for your distribution.
In most cases, it is sufficient to install the dependencies that your
distribution uses to build the libinput package. These can be installed
with one of the following commands:
- **Debian/Ubuntu** based distributions: ``sudo apt-get build-dep libinput``
- **Fedora 22** and later: ``sudo dnf builddep libinput``
- **RHEL/CentOS/Fedora 21** and earlier: ``sudo yum-builddep libinput``
- **openSUSE**: ::
$> sudo zypper modifyrepo --enable ``zypper repos | grep source | awk '{print $5}'``
$> sudo zypper source-install -d libinput10
$> sudo zypper install autoconf automake libtool
$> sudo zypper modifyrepo --disable ``zypper repos | grep source | awk '{print $5}'``
- **Arch**: ::
$> sudo pacman -S asp
$> cd $(mktemp -d)
$> asp export libinput
$> cd libinput
$> makepkg --syncdeps --nobuild --noextract
If dependencies are missing, meson shows a message ``No package 'foo'
found``. See
`this blog post here <https://who-t.blogspot.com/2018/07/meson-fails-with-native-dependency-not-found.html>`_
for instructions on how to fix it.
..............................................................................
Build dependencies per distribution
..............................................................................
.. include:: dependencies.rst
.. _building_conditional:
------------------------------------------------------------------------------
Conditional builds
------------------------------------------------------------------------------
libinput supports several meson options to disable parts of the build. See
the ``meson_options.txt`` file in the source tree for a full list of
available options. The default build enables most options and thus requires
more build dependencies. On systems where build dependencies are an issue,
options may be disabled with this meson command: ::
meson setup --prefix=/usr -Dsomefeature=false builddir
Where ``-Dsomefeature=false`` may be one of:
- ``-Ddocumentation=false``
Disables the documentation build (this website). Building the
documentation is only needed on the maintainer machine.
- ``-Dtests=false``
Disables the test suite. The test suite is only needed on developer
systems.
- ``-Ddebug-gui=false``
Disables the ``libinput debug-gui`` helper tool (see :ref:`tools`),
dropping GTK and other build dependencies. The debug-gui is only
required for troubleshooting.
- ``-Dlibwacom=false``
libwacom is required by libinput's tablet code to gather additional
information about tablets that is not available from the kernel device.
It is not recommended to disable libwacom unless libinput is used in an
environment where tablet support is not required. libinput provides tablet
support even without libwacom, but some features may be missing or working
differently.
.. _building_against:
------------------------------------------------------------------------------
Building against libinput
------------------------------------------------------------------------------
libinput provides a
`pkg-config <https://www.freedesktop.org/wiki/Software/pkg-config/>`_ file.
Software that uses autotools should use the ``PKG_CHECK_MODULES`` autoconf
macro: ::
PKG_CHECK_MODULES(LIBINPUT, "libinput")
Software that uses meson should use the ``dependency()`` function: ::
pkgconfig = import('pkgconfig')
dep_libinput = dependency('libinput')
Software that uses CMake should use: ::
find_package(Libinput)
target_link_libraries(myprogram PRIVATE Libinput::Libinput)
Otherwise, the most rudimentary way to compile and link a program against
libinput is:
::
gcc -o myprogram myprogram.c ``pkg-config --cflags --libs libinput``
For further information on using pkgconfig see the pkg-config documentation.

View file

@ -1,56 +0,0 @@
.. _button_debouncing:
==============================================================================
Button debouncing
==============================================================================
Physical buttons experience wear-and-tear with usage. On some devices this
can result in an effect called "contact bouncing" or "chatter". This effect
can cause the button to send multiple events within a short time frame, even
though the user only pressed or clicked the button once. This effect can be
counteracted by "debouncing" the buttons, usually by ignoring erroneous
events.
libinput provides two methods of debouncing buttons, referred to as the
"bounce" and "spurious" methods:
- In the "bounce" method, libinput monitors hardware bouncing on button
state changes, i.e. when a user clicks or releases a button. For example,
if a user presses a button but the hardware generates a
press-release-press sequence in quick succession, libinput ignores the
release and second press event. This method is always enabled.
- in the "spurious" method, libinput detects spurious releases of a button
while the button is physically held down by the user. These releases are
immediately followed by a press event. libinput monitors for these events
and ignores the release and press event. This method is disabled by
default and enables once libinput detects the first faulty event sequence.
The "bounce" method guarantees that all press events are delivered
immediately and most release events are delivered immediately. The
"spurious" method requires that release events are delayed, libinput thus
does not enable this method unless a faulty event sequence is detected. A
message is printed to the log when spurious debouncing was detected.
libinput's debouncing is supposed to correct hardware damage or
substandard hardware. Debouncing also exists as an accessibility feature
but the requirements are different. In the accessibility feature, multiple
physical key presses, usually caused by involuntary muscle movement, must be
filtered to only one key press. This feature must be implemented higher in
the stack, libinput is limited to hardware debouncing.
Below is an illustration of the button debouncing modes to show the relation
of the physical button state and the application state. Where applicable, an
extra line is added to show the timeouts used by libinput that
affect the button state handling. The waveform's high and low states
correspond to the buttons 'pressed' and 'released' states, respectively.
.. figure:: button-debouncing-wave-diagram.svg
:align: center
Diagram illustrating button debouncing
Some devices send events in bursts, erroneously triggering the button
debouncing detection. Please :ref:`file a bug <reporting_bugs>` if that
occurs for your device.

View file

@ -1,144 +0,0 @@
.. _clickpad_softbuttons:
==============================================================================
Clickpad software button behavior
==============================================================================
"Clickpads" are touchpads without separate physical buttons. Instead, the
whole touchpad acts as a button and left or right button clicks are
distinguished by :ref:`the location of the fingers <software_buttons>` or
the :ref:`number of fingers on the touchpad <clickfinger>`.
"ClickPad" is a trademark by `Synaptics Inc. <http://www.synaptics.com/en/clickpad.php>`_
but for simplicity we refer to any touchpad with the above feature as Clickpad,
regardless of the manufacturer.
The kernel marks clickpads with the
`INPUT_PROP_BUTTONPAD <https://www.kernel.org/doc/Documentation/input/event-codes.txt>`_
property. Without this property, libinput would not know whether a touchpad
is a clickpad or not. To perform a right-click on a Clickpad, libinput
provides :ref:`software_buttons` and :ref:`clickfinger`.
.. note:: The term "click" refers refer to a physical button press
and/or release of the touchpad, the term "button event" refers to
the events generated by libinput in response to a click.
.. _software_buttons:
------------------------------------------------------------------------------
Software button areas
------------------------------------------------------------------------------
The bottom of the touchpad is split into three distinct areas generate left,
middle or right button events on click. The height of the button area
depends on the hardware but is usually around 10mm.
.. figure :: software-buttons-visualized.svg
:align: center
The locations of the virtual button areas.
Left, right and middle button events can be triggered as follows:
- if a finger is in the main area or the left button area, a click generates
left button events.
- if a finger is in the right area, a click generates right button events.
- if a finger is in the middle area, a click generates middle button events.
.. figure:: software-buttons.svg
:align: center
Left, right and middle-button click with software button areas
The middle button is always centered on the touchpad and smaller in size
than the left or right button. The actual size is device-dependent. Many
touchpads do not have visible markings so the exact location of the button
is unfortunately not visibly obvious.
.. note:: If :ref:`middle button emulation <middle_button_emulation>` is
enabled on a clickpad, only left and right button areas are
available.
If fingers are down in the main area in addition to fingers in the
left or right button area, those fingers are are ignored.
A release event always releases the buttons logically down, regardless of
the current finger position
.. figure:: software-buttons-thumbpress.svg
:align: center
Only the location of the thumb determines whether it is a left, right or
middle click.
The movement of a finger can alter the button area behavior:
- if a finger starts in the main area and moves into the software button
area, the software buttons do not apply to that finger
- once a finger has moved out of the button area, it cannot move back in and
trigger a right or middle button event
- a finger moving within the software button area does not move the pointer
- once a finger moves out out of the button area it will control the
pointer (this only applies if there is no other finger down on the
touchpad)
.. figure:: software-buttons-conditions.svg
:align: center
**Left:** moving a finger into the right button area does not trigger a
right-button click.
**Right:** moving within the button areas does not generate pointer
motion.
On some touchpads, notably the 2015 Lenovo X1 Carbon 3rd series, the very
bottom end of the touchpad is outside of the sensor range but it is possible
to trigger a physical click there. To libinput, the click merely shows up as
a left button click without any positional finger data and it is
impossible to determine whether it is a left or a right click. libinput
ignores such button clicks, this behavior is intentional.
.. _clickfinger:
------------------------------------------------------------------------------
Clickfinger behavior
------------------------------------------------------------------------------
This is the default behavior on Apple touchpads. Here, a left, right, middle
button event is generated when one, two, or three fingers are held down on the
touchpad when a physical click is generated, given the default mapping. The
location of the fingers does not matter and there are no software-defined
button areas. It is possible to swap right and middle buttons, the same way as
with :ref:`tapping <tapping>`.
.. figure:: clickfinger.svg
:align: center
One, two and three-finger click with Clickfinger behavior
On some touchpads, libinput imposes a limit on how the fingers may be placed
on the touchpad. In the most common use-case this allows for a user to
trigger a click with the thumb while leaving the pointer-moving finger on
the touchpad.
.. figure:: clickfinger-distance.svg
:align: center
Illustration of the distance detection algorithm
In the illustration above the red area marks the proximity area around the
first finger. Since the thumb is outside of that area libinput considers the
click a single-finger click rather than a two-finger click.
.. _special_clickpads:
------------------------------------------------------------------------------
Special Clickpads
------------------------------------------------------------------------------
The Lenovo \*40 series laptops have a clickpad that provides two software button sections, one at
the top and one at the bottom. See :ref:`Lenovo \*40 series touchpad support <t440_support>`
for details on the top software button.
Some Clickpads, notably some Cypress ones, perform right button detection in
firmware and appear to userspace as if the touchpad had physical buttons.
While physically clickpads, these are not handled by the software and
treated like traditional touchpads.

View file

@ -1,46 +0,0 @@
.. _clickpads_with_right_buttons:
==============================================================================
Clickpads with a fake right button
==============================================================================
libinput relies on the kernel to label :ref:`Clickpads <touchpads_buttons_clickpads>`
with the ``INPUT_PROP_BUTTONPAD`` property so it can enable the correct
:ref:`clickpad_softbuttons`. Clickpads are not expected to have a right button
since the whole surface can be depressed.
A common bug encountered with :ref:`Clickpads <touchpads_buttons_clickpads>`
is that the device advertises a physical right button where no such button
exists. This is usually a bug in the firmware of the device and causes the
following warning to be emitted by libinput::
"<device name> clickpad advertising right button"
The user-visible effect of this is usually negligible since these devices
cannot actually trigger a right click and libinput's default behaviors for
clickpads work as expected.
However, we should nonetheless correct the device to get rid of this warning
and avoid potential issues with future features. The :ref:`device-quirks`
provide a simple way to disable the fake right button on the device. The
following quirk disables the right button on the MyModel laptop from the
MyVendor OEM::
[MyVendor MyModel Touchpad]
MatchName=Foo Bar Touchpad
MatchUdevtype=touchpad
MatchDMIModAlias=dmi:*:svnMyVendor:pnMyModel:*
AttrEventCode=-BTN_RIGHT
The name of the device can be obtained using :ref:`libinput record <libinput-record>`,
the modalias match is a shell-style glob against the value of ``/sys/class/dmi/id/modalias``.
In most cases, matching should be against ``svn`` (system vendor) and one of
``pn`` (product name) or ``pvr`` (product version), whichever provides a
useful description of the individual laptop model. See the
:ref:`device-quirks` documentation for details on testing local quirks.
For reference, some example commits that add such a quirk are:
- `bf61ab9bb0694d0ac3d60a7f815779abfe4886e6 <https://gitlab.freedesktop.org/libinput/libinput/-/commit/bf61ab9bb0694d0ac3d60a7f815779abfe4886e6>`__
- `74fac6d040ac62048882dfb6f73da567ace6a6f5 <https://gitlab.freedesktop.org/libinput/libinput/-/commit/74fac6d040ac62048882dfb6f73da567ace6a6f5>`__
- `89cd0f990e3bee9906754d6ca8484ed5aa392249 <https://gitlab.freedesktop.org/libinput/libinput/-/commit/89cd0f990e3bee9906754d6ca8484ed5aa392249>`__

View file

@ -1,193 +0,0 @@
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/stable/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# sys.path.insert(0, os.path.abspath('.'))
import sys
import os
sys.path.insert(0, os.path.abspath('@BUILDDIR@'))
# -- Project information -----------------------------------------------------
project = '@PROJECT_NAME@'
copyright = '2019, the libinput authors'
author = 'the libinput authors'
# The short X.Y version
version = '@PROJECT_VERSION@'
# The full version, including alpha/beta/rc tags
release = '@PROJECT_VERSION@'
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.mathjax',
'sphinx.ext.graphviz',
'sphinx.ext.extlinks',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'en'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path .
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
highlight_language = 'none'
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
html_theme_options = {
'collapse_navigation': False,
'navigation_depth': 3,
}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = '@PROJECT_NAME@doc'
# -- Options for LaTeX output ------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, '@PROJECT_NAME@.tex', '@PROJECT_NAME@ Documentation',
'Peter Hutterer', 'manual'),
]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, '@PROJECT_NAME@', '@PROJECT_NAME@ Documentation',
[author], 1)
]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, '@PROJECT_NAME@', '@PROJECT_NAME@ Documentation',
author, '@PROJECT_NAME@', 'One line description of project.',
'Miscellaneous'),
]
# -- Extension configuration -------------------------------------------------
from recommonmark.parser import CommonMarkParser
extlinks = { 'commit' :
('https://gitlab.freedesktop.org/libinput/libinput/commit/%s',
'git commit %s')
}
# -- git version hack -------------------------------------------------
#
# meson doesn't take configuration_data() for vcs_tag, so we cannot replace
# two substrings in the same file.
#
# sphinx cannot do ..include:: without linebreaks, so in-line replacements
# are a no-go.
#
# Work around this by generating a mini python module in meson through
# vcs_tag, then use that to generate the replacements in rst_prolog.
import git_version
rst_prolog = """
.. |git_version| replace:: :commit:`{}`
.. |git_version_full| replace:: :commit:`{}`
""".format(git_version.get_git_version(),
git_version.get_git_version_full)

View file

@ -1,237 +0,0 @@
.. _config_options:
==============================================================================
Configuration options
==============================================================================
Below is a list of configurable options exposed to the users.
.. contents::
:depth: 1
:local:
.. hint:: Not all configuration options are available on all devices. Use
:ref:`libinput list-devices <libinput-list-devices>` to show the
configuration options for local devices.
libinput's configuration interface is available to the caller only, not
directly to the user. Thus it is the responsibility of the caller to expose
the various options and how these options are exposed. For example, the
`xf86-input-libinput driver <https://gitlab.freedesktop.org/xorg/driver/xf86-input-libinput/>`_
exposes the options through X Input device properties and `xorg.conf.d
<https://linux.die.net/man/5/xorg.conf.d>`_ options. See the `libinput(4)
<https://www.mankier.com/4/libinput>`_ man page for more details.
------------------------------------------------------------------------------
Tap-to-click
------------------------------------------------------------------------------
See :ref:`tapping` for details on how this feature works. Configuration
options exposed by libinput are:
- how many tapping fingers are supported by this device
- a toggle to enable/disable tapping
- a toggle to enable/disable tap-and-drag, see :ref:`tapndrag`.
- a toggle to enable/disable tap-and-drag drag lock, see :ref:`tapndrag`
- The default order is 1, 2, 3 finger tap mapping to left, right, middle
click, respectively. This order can be changed to left, middle, right click,
respectively.
Tapping is usually available on touchpads and the touchpad part of external
graphics tablets. Tapping is usually **not** available on touch screens,
for those devices it is expected to be implemented by the toolkit.
------------------------------------------------------------------------------
Three-finger drag
------------------------------------------------------------------------------
Three-finger drag emulates the mouse button down while three fingers
are down on a touchpad without the need to press a physical button or use
:ref:`tapndrag`. See :ref:`drag_3fg` for details on how this feature works.
Three-finger drag is usually available on touchpads and the touchpad part of
external graphics tablets. Three-finger drag is usually **not** available on
touch screens, for those devices it is expected to be implemented by the
toolkit.
------------------------------------------------------------------------------
Send Events Mode
------------------------------------------------------------------------------
The Send Events Mode is libinput's terminology for disabling a device. It is
more precise in that the device only stops sending events but may not get
fully disabled. For example, disabling the touchpad on a
:ref:`Lenovo T440 and similar <t440_support>` leaves the top software
buttons enabled for the trackpoint. Available options are
**enabled** (send events normally), **disabled** ( do not send events),
**disabled on external mouse** (disable the device while an external mouse
is plugged in).
.. _config_pointer_acceleration:
------------------------------------------------------------------------------
Pointer acceleration
------------------------------------------------------------------------------
Pointer acceleration is a function to convert input deltas to output deltas,
usually based on the movement speed of the device, see
:ref:`pointer-acceleration` for details.
Pointer acceleration is normalized into a [-1, 1] range, where -1 is
"slowest" and 1 is "fastest". Most devices use a default speed of 0.
The pointer acceleration profile defines **how** the input deltas are
converted, see :ref:`ptraccel-profiles`. Most devices have their default
profile (usually called "adaptive") and a "flat" profile. The flat profile
does not apply any acceleration.
------------------------------------------------------------------------------
Scrolling
------------------------------------------------------------------------------
"Natural scrolling" is the terminology for moving the content in the
direction of scrolling, i.e. moving the wheel or fingers down moves the page
down. Traditional scrolling moves the content in the opposite direction.
Natural scrolling can be turned on or off, it is usually off by default.
The scroll method defines how to trigger scroll events. On touchpads
libinput provides two-finger scrolling and edge scrolling. Two-finger
scrolling converts a movement with two fingers to a series of scroll events.
Edge scrolling converts a movement with one finger along the right or bottom
edge of the touchpad into a series of scroll events.
On other libinput provides button-scrolling - movement of the device while
the designated scroll button is down is converted to scroll events. The
button used for scrolling is configurable.
The scroll method can be chosen or disabled altogether but most devices only
support a subset of available scroll methods. libinput's default is
two-finger scrolling for multi-touch touchpads, edge scrolling for
single-touch touchpads. On tracksticks, button scrolling is enabled by
default.
See :ref:`scrolling` for more details on how the scroll methods work.
------------------------------------------------------------------------------
Left-handed Mode
------------------------------------------------------------------------------
Left-handed mode switches the device's functionality to be more
accommodating for left-handed users. On mice this usually means swapping the
left and right mouse button, on tablets this allows the tablet to be used
upside-down to present the pad buttons for the non-dominant right hand. Not
all devices have left-handed mode.
Left-handed mode can be enabled or disabled and is disabled by default.
------------------------------------------------------------------------------
Middle Button Emulation
------------------------------------------------------------------------------
Middle button emulation converts a simultaneous left and right button click
into a middle button. The emulation can be enabled or disabled. Middle
button emulation is usually enabled when the device does not provide a
middle button.
------------------------------------------------------------------------------
Click method
------------------------------------------------------------------------------
The click method defines how button events are triggered on a :ref:`clickpad
<clickpad_softbuttons>`. When set to button areas, the bottom area of the
touchpad is divided into a left, middle and right button area. When set to
clickfinger, the number of fingers on the touchpad decide the button type.
Clicking with 1, 2, 3 fingers triggers a left, right, or middle click,
respectively. The default click method is software button areas. Click
methods are usually only available on :ref:`clickpads
<clickpad_softbuttons>`.
------------------------------------------------------------------------------
Disable while typing
------------------------------------------------------------------------------
DWT is the most generic form of palm detection on touchpad. While the user
is typing on an internal keyboard the touchpad is disabled, the touchpad
is enabled again after a timeout. See :ref:`disable-while-typing` for more
info.
Disable-while-typing can be enabled or disabled, it is enabled by default on
most touchpads.
------------------------------------------------------------------------------
Disable while trackpointing
------------------------------------------------------------------------------
DWTP is a form of palm detection for devices that have a trackpoint (like
Thinkpads). While the user is using the trackpoint, the touchpad is disabled,
being enabled again after a timeout. See :ref:`disable-while-trackpointing` for
more info.
Disable-while-trackpointing can be enabled or disabled, it is enabled by
default.
------------------------------------------------------------------------------
Calibration
------------------------------------------------------------------------------
Calibration is available for some direct-input devices (touch screens,
graphics tablets, etc.). The purpose of calibration is to ensure the input
lines up with the output and the configuration data is a transformation
matrix. It is thus not expected that the user sets this option. The desktop
environment should provide an interface for this.
------------------------------------------------------------------------------
Rotation
------------------------------------------------------------------------------
The device rotation applies a corrective angle to relative input events,
allowing the device to be used e.g. sideways or upside-down. For example, a
trackball may be used in a 90° rotated position for accessibility reasons -
such a rotated position allows triggering the buttons with the thumb or
the non-dominant hand.
Note that where a device rotation is higher than 160 but less than 200 degrees,
the direction of wheels is also inverted. For all other angles, the wheel
direction is left as-is.
.. _config-tablet-pressure-range:
------------------------------------------------------------------------------
Tablet tool pressure range
------------------------------------------------------------------------------
The pressure range on a :ref:`Tablet tool <tablet-tools>` can be reduced
from the full available hardware range to a subset of that range. The effect
of this is that the tablet will not register pressure below the given
the given threshold is met, and will reach the maximum logical pressure
before the maximum hardware-supported pressure is reached.
See :ref:`tablet-pressure-range` for more info.
.. _config-tablet-eraser-buttons:
------------------------------------------------------------------------------
Tablet tool eraser buttons
------------------------------------------------------------------------------
On many contemporary :ref:`Tablet tools <tablet-tools>` one button is hardcoded
in firmware to emulate an eraser. This button can be remapped to provide
a normal stylus button instead.
See :ref:`tablet-eraser-button` for more info.
------------------------------------------------------------------------------
Area configuration
------------------------------------------------------------------------------
Area configuration is available for some indirect input devices such as
graphics tablets. This configuration allows reducing the active area of
such a device to a subset of the physically possible area.
An example use-case for this is to match the aspect ratio of the device to that
of the screen.
See :ref:`tablet-area` for more info.

View file

@ -1,421 +0,0 @@
.. _contributing:
==============================================================================
Contributing to libinput
==============================================================================
So you want to contribute to libinput? Great! We'd love to help you be a part
of our community. Here is some important information to help you.
.. contents::
:local:
------------------------------------------------------------------------------
Code of Conduct
------------------------------------------------------------------------------
As a freedesktop.org project, libinput follows the `freedesktop.org
Contributor Covenant <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.
------------------------------------------------------------------------------
Contact
------------------------------------------------------------------------------
Questions can be asked on ``#wayland`` on oftc or on the
`wayland-devel@lists.freedesktop.org
<https://lists.freedesktop.org/mailman/listinfo/wayland-devel>`_ mailing
list.
For IRC, ping user ``whot`` (Peter Hutterer, the libinput maintainer) though
note that he lives on UTC+10 and thus the rest of the world is out of sync
by default ;)
For anything that appears to be device specific and/or related to a new
feature, just file `an issue in our issue tracker
<https://gitlab.freedesktop.org/libinput/libinput/issues>`_. It's usually the
most efficient way to get answers.
------------------------------------------------------------------------------
What to work on?
------------------------------------------------------------------------------
If you don't already know what you want to improve or fix with libinput,
then a good way of finding something is to search for the ``help needed``
tag in our `issue tracker <https://gitlab.freedesktop.org/libinput/libinput/issues?label_name%5B%5D=help+needed>`_.
These are issues that have been triaged to some degree and deemed to be a
possible future feature to libinput.
.. note:: Some of these issue may require specific hardware to reproduce.
Another good place to help out with is the documentation. For anything you
find in these pages that isn't clear enough please feel free to reword it
and add what is missing.
------------------------------------------------------------------------------
Getting the code
------------------------------------------------------------------------------
The :ref:`building_libinput` have all the details but the short solution
will be:
::
$> git clone https://gitlab.freedesktop.org/libinput/libinput
$> cd libinput
$> meson setup --prefix=/usr builddir/
$> ninja -C builddir/
$> sudo ninja -C builddir/ install
You can omit the last step if you only want to test locally.
------------------------------------------------------------------------------
Working on the code
------------------------------------------------------------------------------
If you are planning to send patches, it's a good idea to set up
`pre-commit <https://pre-commit.com/>`_ with these commands::
$> pre-commit install
$> pre-commit install --hook-type pre-push
This will check a few things before you commit and/or push to your repos to
reduce the turnaround time for some common mistakes.
libinput has a roughly three-parts architecture:
- the front-end code which handles the ``libinput_some_function()`` API calls in ``libinput.c``
- the generic evdev interface handling which maps those API calls to the
backend calls (``evdev.c``).
- there are device-specific backends which do most of the actual work -
``evdev-mt-touchpad.c`` is the one for touchpads for example.
In general, things that only affect the internal workings of a device only
get implemented in the device-specific backend. You only need to touch the
API when you are adding configuration options. For more details, please read
the :ref:`architecture` document. There's also a `blog post describing the
building blocks
<https://who-t.blogspot.com/2019/03/libinputs-internal-building-blocks.html>`_
that may help to understand how it all fits together.
Documentation is in ``/doc/api`` for the doxygen-generated API documentation.
These are extracted from the libinput source code directly. The
documentation you're reading right now is in ``/doc/user`` and generated with
sphinx. Simply running ``ninja -C builddir`` will rebuild it and the final
product ends up in ``builddir/Documentation``.
------------------------------------------------------------------------------
Testing the code
------------------------------------------------------------------------------
libinput provides a bunch of :ref:`tools` to debug any changes - without
having to install libinput.
The two most useful ones are :ref:`libinput debug-events
<libinput-debug-events>` and :ref:`libinput debug-gui <libinput-debug-gui>`.
Both tools can be run from the build directory directly and are great for
quick test iterations::
$> sudo ./builddir/libinput-debug-events --verbose
$> sudo ./builddir/libinput-debug-gui --verbose
The former provides purely textual output and is useful for verifying event
streams from buttons, etc. The latter is particularly useful when you are
trying to debug pointer movement or placement. ``libinput debug-gui`` will
also visualize the raw data from the device so you can compare pointer
behavior with what comes from the kernel.
These tools create a new libinput context and will not affect your session's
behavior. Only once you've installed libinput and restarted your session
will your changes affect the X server/Wayland compositor.
Once everything seems to be correct, it's time to run the
:ref:`test-suite`::
$> sudo ./builddir/libinput-test-suite
This test suite can take test names etc. as arguments, have a look at
:ref:`test-suite` for more info. There are a bunch of other tests that are
run by the CI on merge requests, you can run those locally with ::
$> sudo ninja -C builddir check
So it always pays to run that before submitting. This will also run the code
through valgrind and pick up any memory leaks.
.. _contributing_submitting_code:
------------------------------------------------------------------------------
Submitting Code
------------------------------------------------------------------------------
Any patches should be sent via a Merge Request (see the `GitLab docs
<https://docs.gitlab.com/ce/gitlab-basics/add-merge-request.htm>`_)
in the `libinput GitLab instance hosted by freedesktop.org
<https://gitlab.freedesktop.org/libinput/libinput>`_.
.. note:: freedesktop.org's GitLab instance has restrictions to prevent Spam
and you cannot fork libinput until you have successfully
`applied for fork permissions <https://gitlab.freedesktop.org/freedesktop/freedesktop/-/wikis/home>`_.
Below are the steps required to submit a merge request. They do not
replace `learning git <https://git-scm.com/doc>`__ but they should be
sufficient to make some of the more confusing steps obvious.
- `Register an account <https://gitlab.freedesktop.org/users/sign_in>`_ in
the freedesktop.org GitLab instance and
`apply for fork permissions <https://gitlab.freedesktop.org/freedesktop/freedesktop/-/wikis/home>`_.
- `Fork libinput <https://gitlab.freedesktop.org/libinput/libinput/-/forks/new>`_
into your username's namespace. Select public visibility.
- Get libinput's main repository. git will call this repository ``origin``. ::
git clone https://gitlab.freedesktop.org/libinput/libinput.git
- Add the forked git repository to your remotes (replace ``USERNAME``
with your username). git will call this repository ``gitlab``. ::
cd /path/to/libinput.git
git remote add gitlab git@ssh.gitlab.freedesktop.org:USERNAME/libinput.git
git fetch gitlab
- Create a new branch and commit your changes to that branch. ::
git switch -C mynewbranch
# edit files, make changes
git add file1 file2
git commit -s
# edit commit message in the editor
Replace ``mynewbranch`` (here and in the commands below) with a meaningful
name. See :ref:`contributing_commit_messages` for details on the commit
message format.
- Push your changes to your fork and submit a merge request ::
git push gitlab mynewbranch
This command will print the URL to file a merge request, you then only
have to click through. Alternatively you can go to:
https://gitlab.freedesktop.org/USERNAME/libinput/merge_requests
Select your branch name to merge and ``libinput/libinput`` ``main`` as target branch.
- Verify that the CI completes successfully by visiting the merge request
page. A successful pipeline shows only green ticks, failure is indicated
by a red cross or a yellow exclamation mark (see
the `GitLab Docs
<https://docs.gitlab.com/ee/ci/pipelines/#pipeline-mini-graphs>`__). For
details about the failures, click on the failed jobs in the pipelines
and/or click the ``Expand`` button in the box for the test summaries.
A merge request without a successful pipeline may never be looked at by a
maintainer.
- If changes are requested by the maintainers, please **amend** the
commit(s) and **force-push** the updated branch. ::
# edits in file foo.c
git add foo.c
git commit --amend
git push -f gitlab mynewbranch
A force-push will re-trigger the CI and notify the merge request that new
changes are available.
If the branch contains more than one commit, please look at
`git interactive rebases
<https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History>`__
to learn how to change multiple commits, or squash new changes into older
commits.
------------------------------------------------------------------------------
Commit History
------------------------------------------------------------------------------
libinput strives to have 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 example: ::
touchpad: add software button behavior
fallback: disable button debouncing on device foo
If in doubt what prefix to use, look at other commits that change the
same file(s) as the patch being sent.
.. _contributing_commit_messages:
------------------------------------------------------------------------------
Commit Messages
------------------------------------------------------------------------------
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.
If your commit solves a GitLab issue, add a ``Closes:`` tag followed by the
issue number at the end of your commit message. For example: ::
Closes: #974
If your commit fixes an issue introduced by another commit, use a ``Fixes`` tag
followed by the first 12 characters of the SHA-1 ID and the commit one line
summary at the end of your commit message. For example: ::
Fixes: 123456789012 ("The commit that caused the issue")
For further reading, please see
`'on commit messages' <http://who-t.blogspot.de/2009/12/on-commit-messages.html>`_
as a general guideline on what commit messages should contain.
------------------------------------------------------------------------------
Coding Style
------------------------------------------------------------------------------
Please see the `CODING_STYLE.md
<https://gitlab.freedesktop.org/libinput/libinput/blob/main/CODING_STYLE.md>`_
document in the source tree.
------------------------------------------------------------------------------
Tracking patches and follow-ups
------------------------------------------------------------------------------
Once submitted to GitLab, your patches will be reviewed by the libinput
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
libinput'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, 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.
------------------------------------------------------------------------------
Failed pipeline errors
------------------------------------------------------------------------------
After submitting your merge request to GitLab, you might receive an email
informing you that your pipeline failed.
Visit your merge request page and check the `pipeline mini graph
<https://docs.gitlab.com/ee/ci/pipelines/#pipeline-mini-graphs>`_ to know which
step failed.
Follow the appropriate section to fix the errors.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Committed gitlab-ci.yml differs from generated gitlab-ci.yml. Please verify
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When your merge request modifies the CI templates, you might see this error
mainly due two reasons: the wrong file was modified and/or
``ci-fairy generate-template`` wasn't run.
``.gitlab-ci.yaml`` is auto generated, changes should be made in:
- ``.gitlab-ci/ci.template``
- ``.gitlab-ci/config.yaml``
Once the changes are ready, run
`ci-fairy <https://freedesktop.pages.freedesktop.org/ci-templates/ci-fairy.html#templating-gitlab-ci-yml>`_
to update ``.gitlab-ci.yaml``: ::
ci-fairy generate-template
Finally, force-push you changes. See :ref:`contributing_submitting_code` for
more details.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Build errors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Usually, checking the CI log is enough to catch this errors. However, your merge
request is built using different configurations you might have not tested.
In order to fix this kind of problems, you can compile libinput using the same
flags used by the CI.
For example, if an error is found in the ``build-no-libwacom`` step, open the
log and search the build options: ::
[...]
+ rm -rf 'build dir'
+ meson 'build dir' -Dlibwacom=false
The Meson build system
[...]
Use the same flags to fix the issue and force-push you changes. See
:ref:`contributing_submitting_code` for more details.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Test errors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The test suite is run for your merge request to check for bugs, regressions and
memory leaks among other issues.
Open the CI error log and search for a message similar to: ::
:: Failure: ../test/test-touchpad.c:465: touchpad_2fg_scroll_slow_distance(synaptics-t440)
See :ref:`test-suite` to learn how to run the failing tests.
Once the tests are fixed, force-push you changes. See
:ref:`contributing_submitting_code` for more details.

View file

@ -1,21 +0,0 @@
.. warning:: The package lists are autogenerated from the `CI <https://gitlab.freedesktop.org/libinput/libinput/-/tree/master/.gitlab-ci.yml>`_.
- Fedora: ::
dnf install @FEDORA_PACKAGES@
- Ubuntu: ::
apt install @UBUNTU_PACKAGES@
- Debian: ::
apt install @DEBIAN_PACKAGES@
- Arch: ::
pacman -S @ARCH_PACKAGES@
- Alpine: ::
apk add @ALPINE_PACKAGES@

View file

@ -1,53 +0,0 @@
.. _development:
==============================================================================
Information for developers
==============================================================================
Below is a list of topics of interest to developers, divided into
information for those :ref:`using_libinput_as_library` in a Wayland compositor
or other project. The :ref:`hacking_on_libinput` section applies to developers working on
libinput itself.
.. note:: If you use or work on libinput you should get in touch with the
libinput developers on the wayland-devel@lists.freedesktop.org
mailing lists
.. _using_libinput_as_library:
------------------------------------------------------------------------------
Using libinput as library
------------------------------------------------------------------------------
See :ref:`building_against` for information on how to integrate libinput
with your project's build system.
.. note:: **libinput's API documentation is available here:**
http://wayland.freedesktop.org/libinput/doc/latest/api/
Topics below explain some behaviors of libinput.
.. toctree::
:maxdepth: 1
absolute-axes.rst
absolute-coordinate-ranges.rst
normalization-of-relative-motion.rst
seats.rst
timestamps.rst
wheel-api.rst
.. _hacking_on_libinput:
------------------------------------------------------------------------------
Hacking on libinput
------------------------------------------------------------------------------
.. toctree::
:maxdepth: 1
architecture
test-suite.rst
pointer-acceleration.rst
device-configuration-via-udev.rst

View file

@ -1,245 +0,0 @@
.. _udev_config:
==============================================================================
Static device configuration via udev
==============================================================================
libinput supports some static configuration through udev properties.
These properties are read when the device is initially added
to libinput's device list, i.e. before the
**LIBINPUT_EVENT_DEVICE_ADDED** event is generated.
The following udev properties are supported:
LIBINPUT_CALIBRATION_MATRIX
Sets the calibration matrix, see
**libinput_device_config_calibration_get_default_matrix()**. If unset,
defaults to the identity matrix.
The udev property is parsed as 6 floating point numbers separated by a
single space each (scanf(3) format ``"%f %f %f %f %f %f"``).
The 6 values represent the first two rows of the calibration matrix as
described in **libinput_device_config_calibration_set_matrix()**.
Example values are: ::
ENV{LIBINPUT_CALIBRATION_MATRIX}="1 0 0 0 1 0" # default
ENV{LIBINPUT_CALIBRATION_MATRIX}="0 -1 1 1 0 0" # 90 degree clockwise
ENV{LIBINPUT_CALIBRATION_MATRIX}="-1 0 1 0 -1 1" # 180 degree clockwise
ENV{LIBINPUT_CALIBRATION_MATRIX}="0 1 0 -1 0 1" # 270 degree clockwise
ENV{LIBINPUT_CALIBRATION_MATRIX}="-1 0 1 0 1 0" # reflect along y axis
LIBINPUT_DEVICE_GROUP
A string identifying the **libinput_device_group** for this device. Two
devices with the same property value are grouped into the same device group,
the value itself is irrelevant otherwise.
LIBINPUT_IGNORE_DEVICE
If set to anything other than "0", the device is ignored by libinput.
See :ref:`ignoring_devices` for more details.
ID_SEAT
Assigns the physical :ref:`seat <seats>` for this device. See
**libinput_seat_get_physical_name()**. Defaults to "seat0".
ID_INPUT
If this property is set, the device is considered an input device. Any
device with this property missing will be ignored, see :ref:`udev_device_type`.
ID_INPUT_KEYBOARD, ID_INPUT_KEY, ID_INPUT_MOUSE, ID_INPUT_TOUCHPAD, ID_INPUT_TOUCHSCREEN, ID_INPUT_TABLET, ID_INPUT_JOYSTICK, ID_INPUT_ACCELEROMETER
If any of the above is set, libinput initializes the device as the given
type, see :ref:`udev_device_type`. Note that for historical reasons more than
one of these may be set at any time, libinput will select only one of these
to determine the device type. To ensure libinput selects the correct device
type, only set one of them.
WL_SEAT
Assigns the logical :ref:`seat <seats>` for this device. See
**libinput_seat_get_logical_name()** context. Defaults to "default".
MOUSE_DPI
HW resolution and sampling frequency of a relative pointer device.
See :ref:`motion_normalization` for details.
MOUSE_WHEEL_CLICK_ANGLE
The angle in degrees for each click on a mouse wheel. See
**libinput_pointer_get_axis_source()** for details.
Below is an example udev rule to assign "seat1" to a device from vendor
``0x012a`` with the model ID of ``0x034b``. ::
$ cat /etc/udev/rules.d/99-my-device-is-on-seat1.rules
ACTION!="remove", KERNEL=="event[0-9]*", \
ENV{ID_VENDOR_ID}=="012a", \
ENV{ID_MODEL_ID}=="034b", \
ENV{ID_SEAT}="seat1"
.. _udev_device_type:
------------------------------------------------------------------------------
Device type assignment via udev
------------------------------------------------------------------------------
libinput requires the **ID_INPUT** property to be set on a device,
otherwise the device will be ignored. In addition, one of
**ID_INPUT_KEYBOARD, ID_INPUT_KEY, ID_INPUT_MOUSE, ID_INPUT_TOUCHPAD,
ID_INPUT_TOUCHSCREEN, ID_INPUT_TABLET, ID_INPUT_JOYSTICK,
ID_INPUT_ACCELEROMETER** must be set on the device to determine the
device type. The usual error handling applies within libinput and a device
type label does not guarantee that the device is initialized by libinput.
If a device fails to meet the requirements for a device type (e.g. a keyboard
labelled as touchpad) the device will not be available through libinput.
Only one device type should be set per device at a type, though libinput can
handle some combinations for historical reasons.
Below is an example udev rule to remove an **ID_INPUT_TOUCHPAD** setting
and change it into an **ID_INPUT_TABLET** setting. This rule would apply
for a device with the vendor/model ID of ``012a``/``034b``. ::
$ cat /etc/udev/rules.d/99-my-device-is-a-tablet.rules
ACTION!="remove", KERNEL=="event[0-9]*", \
ENV{ID_VENDOR_ID}=="012a", \
ENV{ID_MODEL_ID}=="034b", \
ENV{ID_INPUT_TOUCHPAD}="", ENV{ID_INPUT_TABLET}="1"
.. _model_specific_configuration:
------------------------------------------------------------------------------
Model-specific configuration
------------------------------------------------------------------------------
As of libinput 1.12, model-specific configuration is stored in the
:ref:`device-quirks` and not in the hwdb anymore. Please see
:ref:`device-quirks` for
details.
.. _model_specific_configuration_x220fw81:
..............................................................................
Lenovo x220 with touchpad firmware v8.1
..............................................................................
The property **LIBINPUT_MODEL_LENOVO_X220_TOUCHPAD_FW81** may be set by a
user in a local hwdb file. This property designates the touchpad on a Lenovo
x220 with a touchpad firmware version 8.1. When this firmware version is
installed, the touchpad is imprecise. The touchpad device does not send
continuous x/y axis position updates, a behavior also observed on its
successor model, the Lenovo x230 which has the same firmware version. If the
above property is set, libinput adjusts its behavior to better suit this
particular model.
The touchpad firmware version cannot be detected automatically by libinput,
local configuration is required to set this property. Refer to the libinput
model quirks hwdb for instructions.
This property must not be used for any other purpose, no specific behavior
is guaranteed.
.. _hwdb:
------------------------------------------------------------------------------
Configuring the hwdb
------------------------------------------------------------------------------
This section outlines how to query the
`udev hwdb <https://www.freedesktop.org/software/systemd/man/hwdb.html>`_
and reload properties so they are available to libinput.
The hwdb contains a set of match rules that assign udev properties that are
available to libinput when the device is connected and/or libinput is
initialized. This section only describes the hwdb in relation to libinput,
it is not a full documentation on how the hwdb works.
libinput's use of the hwdb is limited to properties systemd and custom
rules files (where available) provide. Hardware-specific quirks as used by
libinput are in the :ref:`device-quirks` system.
.. _hwdb_querying:
..............................................................................
Querying the hwdb
..............................................................................
libinput only uses device nodes in the form of ``/dev/input/eventX`` where X
is the number of the specific device. Running ``libinput debug-events`` lists
all devices currently available to libinput and their event node name: ::
$> sudo libinput debug-events
-event2 DEVICE_ADDED Power Button seat0 default group1 cap:k
-event5 DEVICE_ADDED Video Bus seat0 default group2 cap:k
-event0 DEVICE_ADDED Lid Switch seat0 default group3 cap:S
...
Note the event node name for your device and translate it into a syspath in
the form of ``/sys/class/input/eventX``. This path can be supplied to ``udevadm
info`` ::
$> udevadm info
P: /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0D:00/input/input0/event0
N: input/event0
E: DEVNAME=/dev/input/event0
E: DEVPATH=/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0D:00/input/input0/event0
E: ID_INPUT=1
E: ID_INPUT_SWITCH=1
E: MAJOR=13
E: MINOR=64
E: SUBSYSTEM=input
E: TAGS=:power-switch:
E: USEC_INITIALIZED=7167898
Lines starting with ``E:`` are udev properties available to libinput. For
example, the above device's ``ID_INPUT_SWITCH`` property will cause libinput
to treat this device as switch device.
.. _hwdb_reloading:
..............................................................................
Reloading the hwdb
..............................................................................
The actual hwdb is stored in binary file on-disk and must be updated
manually whenever a ``.hwdb`` file changes. This is required both when a user
manually edits the ``.hwdb`` file but also when the git tree is updated (and
that update causes a hwdb change).
To update the binary file on-disk, run: ::
sudo systemd-hwdb update
Then, to trigger a reload of all properties on your device, run: ::
sudo udevadm trigger /sys/class/input/eventX
Then check with ``udevadm info`` whether the properties were updated, see
:ref:`hwdb_querying`. If a new property does not appear on the device, use ``udevadm
test`` to check for error messages by udev and the hwdb (e.g. syntax errors
in the udev rules files). ::
sudo udevadm test /sys/class/input/eventX
.. warning:: ``udevadm test`` does not run commands specified in ``RUN``
directives. This affects the udev properties relying on e.g.
the udev keyboard builtin such as the :ref:`touchpad_jitter`
workarounds.
.. _hwdb_modifying:
..............................................................................
Modifying the hwdb
..............................................................................
.. warning:: This section has been removed as it no longer applies in libinput 1.12
and later. libinput users should not need to modify the hwdb, any
device-specific quirks must go in to the :ref:`device-quirks` system.
For information about older libinput versions, please see the documentation
for your version available in: https://wayland.freedesktop.org/libinput/doc/

View file

@ -1,244 +0,0 @@
.. _device-quirks:
==============================================================================
Device quirks
==============================================================================
libinput requires extra information from devices that is not always readily
available. For example, some touchpads are known to have jumping cursors
under specific conditions. libinput ships a set of files containing the
so-called model quirks to provide that information. Model quirks are usually
installed under ``/usr/share/libinput/<filename>.quirks`` and are standard
``.ini`` files. A file may contain multiple section headers (``[some
identifier]``) followed by one or more :ref:`MatchFoo=Bar <device-quirks-matches>`
directives, followed by at least one of ``ModelFoo=1`` or ``AttrFoo=bar`` directive.
See the ``quirks/README.md`` file in the libinput source repository for more
details on their contents.
.. warning:: Model quirks are internal API and may change at any time. No
backwards-compatibility is guaranteed.
For example, a quirks file may have this content to label all keyboards on
the serial bus (PS/2) as internal keyboards: ::
[Serial Keyboards]
MatchUdevType=keyboard
MatchBus=serial
AttrKeyboardIntegration=internal
The model quirks are part of the source distribution and should never be
modified locally. Updates to libinput may overwrite modifications or even
stop parsing any property. For temporary local workarounds, see
:ref:`device-quirks-local`.
Device quirks are parsed on libinput initialization. A parsing error in the
device quirks disables **all** device quirks and may negatively impact
device behavior on the host. If the quirks cannot be loaded, an error
message is posted to the log and users should use the information in
:ref:`device-quirks-debugging` to verify their quirks files.
.. _device-quirks-local:
------------------------------------------------------------------------------
Installing temporary local device quirks
------------------------------------------------------------------------------
The model quirks are part of the source distribution and should never be
modified. For temporary local workarounds, libinput reads the
``/etc/libinput/local-overrides.quirks`` file. Users may add sections to
this file to add a device quirk for a local device but beware that **any
modification must be upstreamed** or it may cease to work at any time.
.. warning:: Model quirks are internal API and may change at any time. No
backwards-compatibility is guaranteed. Local overrides should only
be used until the distribution updates the libinput packages.
The ``local-overrides.quirks`` file usually needs to be created by the user.
Once the required section has been added, use the information from section
:ref:`device-quirks-debugging` to validate and test the quirks.
.. _device-quirks-debugging:
------------------------------------------------------------------------------
Debugging device quirks
------------------------------------------------------------------------------
libinput provides the ``libinput quirks`` tool to debug the quirks database.
This tool takes an action as first argument, the most common invocation is
``libinput quirks list`` to list model quirks that apply to one or more local
devices. ::
$ libinput quirks list /dev/input/event19
$ libinput quirks list /dev/input/event0
AttrLidSwitchReliability=unreliable
The device `event19` does not have any quirks assigned.
When called with the ``--verbose`` argument, ``libinput quirks list`` prints
information about all files and its attempts to match the device: ::
$ libinput quirks list --verbose /dev/input/event0
quirks debug: /usr/share/share/libinput is data root
quirks debug: /usr/share/share/libinput/10-generic-keyboard.quirks
quirks debug: /usr/share/share/libinput/10-generic-lid.quirks
[...]
quirks debug: /usr/share/etc/libinput/local-overrides.quirks
quirks debug: /dev/input/event0: fetching quirks
quirks debug: [Serial Keyboards] (10-generic-keyboard.quirks) wants MatchBus but we don't have that
quirks debug: [Lid Switch Ct9] (10-generic-lid.quirks) matches for MatchName
quirks debug: [Lid Switch Ct10] (10-generic-lid.quirks) matches for MatchName
quirks debug: [Lid Switch Ct10] (10-generic-lid.quirks) matches for MatchDMIModalias
quirks debug: [Lid Switch Ct10] (10-generic-lid.quirks) is full match
quirks debug: property added: AttrLidSwitchReliability from [Lid Switch Ct10] (10-generic-lid.quirks)
quirks debug: [Aiptek No Tilt Tablet] (30-vendor-aiptek.quirks) wants MatchBus but we don't have that
[...]
quirks debug: [HUION PenTablet] (30-vendor-huion.quirks) wants MatchBus but we don't have that
quirks debug: [Logitech Marble Mouse Trackball] (30-vendor-logitech.quirks) wants MatchBus but we don't have that
quirks debug: [Logitech K400] (30-vendor-logitech.quirks) wants MatchBus but we don't have that
quirks debug: [Logitech K400r] (30-vendor-logitech.quirks) wants MatchBus but we don't have that
quirks debug: [Logitech K830] (30-vendor-logitech.quirks) wants MatchBus but we don't have that
quirks debug: [Logitech K400Plus] (30-vendor-logitech.quirks) wants MatchBus but we don't have that
quirks debug: [Logitech Wireless Touchpad] (30-vendor-logitech.quirks) wants MatchBus but we don't have that
quirks debug: [Microsoft Surface 3 Lid Switch] (30-vendor-microsoft.quirks) matches for MatchName
[...]
AttrLidSwitchReliability
Note that this is an example only, the output may change over time. The tool
uses the same parser as libinput and any parsing errors will show up in the
output.
.. _device-quirks-list:
------------------------------------------------------------------------------
List of currently available device quirks
------------------------------------------------------------------------------
This list is a guide for developers to ease the process of submitting
patches upstream. This section shows device quirks currently available in
|git_version|.
.. warning:: Quirks are internal API and may change at any time for any reason.
No guarantee is given that any quirk below works on your version of
libinput.
In the documentation below, the letters N, M, O, P refer to arbitrary integer
values.
Quirks starting with **Model*** triggers implementation-defined behaviour
for this device not needed for any other device. Only the more
general-purpose **Model*** flags are listed here.
ModelALPSTouchpad, ModelAppleTouchpad, ModelWacomTouchpad, ModelChromebook
Reserved for touchpads made by the respective vendors
ModelTouchpadVisibleMarker
Indicates the touchpad has a drawn-on visible marker between the software
buttons.
ModelTabletModeNoSuspend
Indicates that the device does not need to be
suspended in :ref:`switches_tablet_mode`.
ModelTabletModeSwitchUnreliable
Indicates that this tablet mode switch's state cannot be relied upon.
ModelTrackball
Reserved for trackballs
ModelBouncingKeys
Indicates that the device may send fake bouncing key events and
timestamps can not be relied upon.
ModelSynapticsSerialTouchpad
Reserved for touchpads made by Synaptics on the serial bus
ModelPressurePad
.. warning:: This quirk is no longer in use. Use
``AttrInputProp=+INPUT_PROP_PRESSUREPAD`` instead.
Unlike in traditional touchpads, whose pressure value equals contact size,
on pressure pads pressure is a real physical axis.
Indicates that the device is a pressure pad.
ModelTouchpadPhantomClicks
Some laptops are prone to registering touchpad clicks when the case is
bent. Indicates that clicks should be ignored if no fingers are on the
touchpad.
ModelScrollOnMiddleClick
Some mice can generate unwanted high-resolution scroll events when the wheel
is pressed. Increases the scroll threshold required to start scrolling to
avoid accidentally scrolling when middle clicking.
AttrSizeHint=NxM, AttrResolutionHint=N
Hints at the width x height of the device in mm, or the resolution
of the x/y axis in units/mm. These may only be used where they apply to
a large proportion of matching devices. They should not be used for any
specific device, override ``EVDEV_ABS_*`` instead, see
:ref:`absolute_coordinate_ranges_fix`.
AttrTouchSizeRange=N:M, AttrPalmSizeThreshold=O
Specifies the touch size required to trigger a press (N) and to trigger
a release (M). O > N > M. See :ref:`touchpad_touch_size_hwdb` for more
details.
An AttrPalmSizeThreshold of zero unsets any threshold that has been
inherited from another quirk.
AttrPressureRange=N:M, AttrPalmPressureThreshold=O, AttrThumbPressureThreshold=P
Specifies the touch pressure required to trigger a press (N) and to
trigger a release (M), when a palm touch is triggered (O) and when a
thumb touch is triggered (P). O > P > N > M. See
:ref:`touchpad_pressure_hwdb` for more details.
An AttrPalmPressureThreshold of zero unsets any threshold that has been
inherited from another quirk.
AttrLidSwitchReliability=reliable|unreliable|write_open
Indicates the reliability of the lid switch. This is a string enum.
Very few devices need this, if in doubt do not set. See :ref:`switches_lid`
for details. libinput 1.21.0 changed the default from unreliable to
reliable, which may be removed from local overrides.
AttrKeyboardIntegration=internal|external
Indicates the integration of the keyboard. This is a string enum.
Generally only needed for USB keyboards.
AttrTPKComboLayout=below
Indicates the position of the touchpad on an external touchpad+keyboard
combination device. This is a string enum. Don't specify it unless the
touchpad is below.
AttrEventCode=+EV_ABS;-BTN_STYLUS;+EV_KEY:0x123;
Enables or disables the evdev event type/code tuples on the device. The prefix
for each entry is either '+' (enable) or '-' (disable). Entries may be
a named event type, or a named event code, or a named event type with a
hexadecimal event code, separated by a single colon.
AttrInputProp=+INPUT_PROP_BUTTONPAD;-INPUT_PROP_POINTER;
Enables or disables the evdev input property on the device. The prefix
for each entry is either '+' (enable) or '-' (disable). Entries may be
a named input property or the hexadecimal value of that property.
The most common use of this is ``AttrInputProp=+INPUT_PROP_PRESSUREPAD``
which marks a touchpad as a :ref:`forcepad or pressurepad <touchpads_buttons_forcepads>`.
AttrPointingStickIntegration=internal|external
Indicates the integration of the pointing stick. This is a string enum.
Only needed for external pointing sticks. These are rare.
AttrTabletSmoothing=1|0
Enables (1) or disables (0) input smoothing for tablet devices. Smoothing is enabled
by default, except on AES devices.
.. _device-quirks-matches:
------------------------------------------------------------------------------
List of currently available matches
------------------------------------------------------------------------------
``Match*`` statements are how quirks are assigned to a device. Quirks with multiple
match statements must match all of those to apply.
.. warning:: Quirks are internal API and may change at any time for any reason.
No guarantee is given that any ``Match`` statement below works on
your version of libinput.
MatchName, MatchUniq
Match on the ``NAME`` or ``UNIQ`` udev property on this device. These properties
are typically derived from the device's kernel name or uniq but may be overridden
by a udev rule. These matches use ``fnmatch()`` globs.
MatchBus
A lower-case bus name. Currently supported are ``usb``, ``bluetooth``, ``ps2``,
``rmi``, ``i2c``, and ``spi``.
MatchVendor, MatchProduct, MatchVersion
The hexadecimal 4-digit vendor ID, product ID or driver version as
exported, uppercase with a ``0x`` prefix, e.g. ``0x12AB```.
MatchDMIModalias, MatchDeviceTree
An ``fnmatch()`` glob for the DMI modalias or the DeviceTree ``compatible`` string.
See ``/sys/class/dmi/id/modalias`` and ``/sys/firmware/devicetree/base/compatible``.
MatchUdevType
One of ``touchpad``, ``mouse``, ``pointingstick``, ``keyboard``, ``joystick``,
``tablet``, ``tablet-pad``. Matches the corresponding ``ID_INPUT_*`` udev
property.

View file

@ -1,19 +0,0 @@
digraph stack
{
compound=true;
rankdir="LR";
node [
shape="box";
]
kernel [label="Kernel"];
libinput;
xserver [label="X Server"];
kernel -> libinput
libinput -> xserver
kernel -> evemu
evemu -> stdout
}

View file

@ -1,36 +0,0 @@
digraph stack
{
compound=true;
splines=line;
rankdir="LR";
node [
shape="box";
]
subgraph cluster_2 {
label="Kernel";
event0 [label="/dev/input/event0"];
event1 [label="/dev/input/event1"];
}
subgraph cluster_0 {
label="Compositor process";
libinput [label="libinput context 1"];
}
subgraph cluster_1 {
label="libinput debug-events";
libinput2 [label="libinput context 2"];
}
stdout;
client [label="Wayland client"];
event0:e -> libinput:w;
event1:e -> libinput:w;
event0:e -> libinput2:w;
event1:e -> libinput2:w;
libinput -> client [ltail=cluster_0 label="Wayland protocol"];
libinput2 -> stdout [ltail=cluster_1];
}

View file

@ -1,20 +0,0 @@
digraph stack
{
compound=true;
rankdir="LR";
node [
shape="box";
]
kernel [label="Kernel"];
libinput;
xserver [label="X Server"];
record [label="libinput record"];
kernel -> libinput
libinput -> xserver
kernel -> record;
record -> stdout
}

View file

@ -1,30 +0,0 @@
digraph stack
{
compound=true;
rankdir="LR";
node [
shape="box";
]
gcc -> gsettings
xf86libinput -> libinput
subgraph cluster0 {
label="X.Org";
xf86libinput [label="xf86-input-libinput"];
xserver [label="X Server"];
xserver -> xf86libinput;
}
gcc [label="gnome-control-center"];
subgraph cluster3 {
gsettings
}
gsd [label="mutter"];
gsd -> gsettings
gsd -> xserver
}

View file

@ -1,25 +0,0 @@
digraph stack
{
compound=true;
rankdir="LR";
node [
shape="box";
]
subgraph cluster_2 {
label="Kernel";
event0 [label="/dev/input/event0"]
event1 [label="/dev/input/event1"]
}
subgraph cluster_0 {
label="Compositor process";
libinput;
}
client [label="Wayland client"];
event0 -> libinput;
event1 -> libinput;
libinput -> client [ltail=cluster_0 label="Wayland protocol"];
}

View file

@ -1,29 +0,0 @@
digraph stack
{
compound=true;
rankdir="LR";
node [
shape="box";
]
subgraph cluster_2 {
label="Kernel";
event0 [label="/dev/input/event0"]
event1 [label="/dev/input/event1"]
}
subgraph cluster_0 {
label="X server process";
subgraph cluster_1 {
label="xf86-input-libinput"
libinput;
}
}
libinput;
client [label="X11 client"];
event0 -> libinput;
event1 -> libinput;
libinput -> client [ltail=cluster_0 label="X protocol"];
}

View file

@ -1,31 +0,0 @@
digraph stack
{
compound=true;
rankdir="LR";
node [
shape="box";
]
subgraph cluster_2 {
label="Kernel";
event0 [label="/dev/input/event0"]
}
subgraph cluster_1 {
label="libinput";
subgraph cluster_0 {
label="Plugin pipeline";
p1 [label="00-foo.lua"];
p2 [label="10-bar.lua"];
}
libinput [label="libinput core"];
}
compositor [label="Compositor"];
event0 -> p1;
p1 -> p2;
p2 -> libinput;
libinput -> compositor [ltail=cluster_1 label="libinput API"];
}

View file

@ -1,29 +0,0 @@
digraph seats_libinput
{
rankdir="BT";
node [
shape="box";
]
ctx1 [label="libinput context 1"; URL="\ref libinput"];
ctx2 [label="libinput context 2"; URL="\ref libinput"];
seat0 [ label="seat phys 0 logical A"];
seat1 [ label="seat phys 0 logical B"];
seat2 [ label="seat phys 1 logical C"];
dev1 [label="device 'Foo'"];
dev2 [label="device 'Bar'"];
dev3 [label="device 'Spam'"];
dev4 [label="device 'Egg'"];
ctx1 -> dev1
ctx1 -> dev2
ctx1 -> dev3
ctx2 -> dev4
dev1 -> seat0
dev2 -> seat0
dev3 -> seat1
dev4 -> seat2
}

View file

@ -1,51 +0,0 @@
digraph seats
{
rankdir="BT";
node [
shape="box";
]
kernel [label="Kernel"];
event0 [URL="\ref libinput_event"];
event1 [URL="\ref libinput_event"];
event2 [URL="\ref libinput_event"];
event3 [URL="\ref libinput_event"];
pseat0 [label="phys seat0"; URL="\ref libinput_seat_get_physical_name"];
pseat1 [label="phys seat1"; URL="\ref libinput_seat_get_physical_name"];
lseatA [label="logical seat A"; URL="\ref libinput_seat_get_logical_name"];
lseatB [label="logical seat B"; URL="\ref libinput_seat_get_logical_name"];
lseatC [label="logical seat C"; URL="\ref libinput_seat_get_logical_name"];
ctx1 [label="libinput context 1"; URL="\ref libinput"];
ctx2 [label="libinput context 2"; URL="\ref libinput"];
dev1 [label="device 'Foo'"];
dev2 [label="device 'Bar'"];
dev3 [label="device 'Spam'"];
dev4 [label="device 'Egg'"];
kernel -> event0
kernel -> event1
kernel -> event2
kernel -> event3
event0 -> pseat0
event1 -> pseat0
event2 -> pseat0
event3 -> pseat1
pseat0 -> ctx1
pseat1 -> ctx2
ctx1 -> lseatA
ctx1 -> lseatB
ctx2 -> lseatC
lseatA -> dev1
lseatA -> dev2
lseatB -> dev3
lseatC -> dev4
}

View file

@ -1,40 +0,0 @@
.. _drag_3fg:
==============================================================================
Three-finger drag
==============================================================================
Three-finger drag is a feature available on touchpads that emulates logical
button presses if three fingers are moving on the touchpad.
Three-finger drag is independent from :ref:`tapping` though some specific
behaviors may change when both features are enabled. For example, with
tapping *disabled* a three-finger gesture will virtually always be a three-finger
drag. With tapping *enabled* a three finger gesture may be a three finger drag
and a short delay is required to disambiguate between the two.
The exact behavior of three-finger drag is implementation defined and may
subtly change. As a general rule, the following constraints can be expected:
- three fingers down and movement trigger a button down and subsequent motion
events (i.e. a drag)
- releasing one finger while keeping two fingers down will keep the drag
and *not* switch to :ref:`twofinger_scrolling`.
- releasing two fingers while keeping one finger down will end the drag
(and thus release the button) and switch to normal pointer motion
- releasing all three fingers and putting three fingers back on the touchpad
immediately will keep the drag (i.e. behave as if the fingers were
never lifted)
- if tapping is enabled: a three finger tap immediately after a three-finger
drag will *not* tap, the user needs to wait past the timeout to
three-finger tap
- releasing all three fingers and putting one or two fingers back on
the touchpad will end the drag (and thus release the button)
and proceed with pointer motion or two-finger scrolling, if applicable
- if tapping is enabled: a one or two finger tap immediately after a
three-finger drag will trigger a one or two finger tap. The user does
not have to wait past the drag release timeout

View file

@ -1,390 +0,0 @@
.. _faq:
==============================================================================
FAQs - Frequently Asked Questions
==============================================================================
Frequently asked questions about libinput.
.. contents::
:local:
:backlinks: entry
.. _faq_feature:
------------------------------------------------------------------------------
Why doesn't libinput support ...?
------------------------------------------------------------------------------
First, read :ref:`what_is_libinput` If you have a feature that you think
libinput needs to support, please file a bug report. See :ref:`reporting_bugs`
for more details.
.. _faq_fast_mouse:
------------------------------------------------------------------------------
My mouse moves too fast, even at the slowest setting
------------------------------------------------------------------------------
This is a symptom of high-dpi mice (greater than 1000dpi). These devices
need a udev hwdb entry to normalize their motion. See
:ref:`motion_normalization` for a detailed explanation.
.. _faq_fast_trackpoint:
------------------------------------------------------------------------------
My trackpoint moves too slow or too fast
------------------------------------------------------------------------------
This is a symptom of an invalid trackpoint multiplier. These devices need
:ref:`device-quirks` to specify the range available so libinput can adjust the
pointer acceleration accordingly. See :ref:`trackpoint_range` for a detailed
explanation.
.. _faq_pointer_acceleration:
------------------------------------------------------------------------------
Why is libinput's pointer acceleration worse than synaptics/evdev
------------------------------------------------------------------------------
This is a known problem affecting some devices and/or use-case but the exact
cause is still unknown. It may be a device-specific issue, it may be a bug
in libinput's acceleration code, it may be a disagreement about how pointer
acceleration should feel. Unfortunately this is something that affected
users need to investigate and analyze.
.. _faq_enable_tapping:
------------------------------------------------------------------------------
Why isn't touchpad tap-to-click enabled by default
------------------------------------------------------------------------------
See :ref:`tapping_default`
.. _faq_touchpad_pressure:
------------------------------------------------------------------------------
Why does my touchpad lose track of touches
------------------------------------------------------------------------------
The most common cause for this is an incorrect pressure threshold range.
See :ref:`touchpad_pressure` for more info.
.. _faq_kinetic_scrolling:
------------------------------------------------------------------------------
Kinetic scrolling does not work
------------------------------------------------------------------------------
The X.Org synaptics driver implemented kinetic scrolling in the driver. It
measures the scroll speed and once the finger leaves the touchpad the driver
keeps sending scroll events for a predetermined time. This effectively
provides for kinetic scrolling without client support but triggers an
unfixable `bug <https://bugs.freedesktop.org/show_bug.cgi?id=38909>`_: the
client cannot know that the events are from a kinetic scroll source. Scroll
events in X are always sent to the current cursor position, a movement of the
cursor after lifting the finger will send the kinetic scroll events to the
new client, something the user does not usually expect. A key event during
the kinetic scroll procedure causes side-effects such as triggering zoom.
libinput does not implement kinetic scrolling for touchpads. Instead it
provides the **libinput_event_pointer_get_axis_source()** function that enables
callers to implement kinetic scrolling on a per-widget basis, see
:ref:`scroll_sources`.
.. _faq_gpl:
------------------------------------------------------------------------------
Is libinput GPL-licensed?
------------------------------------------------------------------------------
No, libinput is MIT licensed. The Linux kernel header file linux/input.h in
libinput's tree is provided to ensure the same behavior regardless of which
kernel version libinput is built on. It does not make libinput GPL-licensed.
.. _faq_config_options:
------------------------------------------------------------------------------
Where is the configuration stored?
------------------------------------------------------------------------------
libinput does not store configuration options, it is up to the caller to
manage these and decide which configuration option to apply to each device.
This must be done at startup, after a resume and whenever a new device is
detected.
One commonly used way to configure libinput is to have the Wayland
compositor expose a compositor-specific configuration option. For example,
in a GNOME stack, the gnome-control-center modifies dconf entries. These
changes are read by mutter and applied to libinput. Changing these entries
via the gsettings commandline tool has the same effect.
Another commonly used way to configure libinput is to have xorg.conf.d
snippets. When libinput is used with the xf86-input-libinput driver in an
X.Org stack, these options are read on startup and apply to each device.
Changing properties at runtime with the xinput commandline tool has the same
effect.
In both cases, the selection of available options and how they are exposed
depends on the libinput caller (e.g. mutter or xf86-input-libinput).
.. graphviz:: libinput-stack-gnome.gv
This has an effect on the availability of configuration options: if an
option is not exposed by the intermediary, it cannot be configured by the
client. Also some configuration options that are provided by the
intermediary may not be libinput-specific configuration options.
.. _faq_configure_wayland:
------------------------------------------------------------------------------
How do I configure my device on Wayland?
------------------------------------------------------------------------------
See :ref:`faq_config_options` Use the configuration tool provided by your
desktop environment (e.g. gnome-control-center) or direct access to your
desktop environment's configuration storage (e.g. gsettings).
.. _faq_configure_xorg:
------------------------------------------------------------------------------
How do I configure my device on X?
------------------------------------------------------------------------------
See :ref:`faq_config_options` If your desktop environment does not provide a
graphical configuration tool you can use an
`xorg.conf.d snippet <https://www.x.org/archive/current/doc/man/man5/xorg.conf.5.xhtml>`_.
Usually, such a snippet looks like this:
::
$> cat /etc/X11/xorg.conf.d/99-libinput-custom-config.conf
Section "InputClass"
Identifier "something to identify this snippet"
MatchDriver "libinput"
MatchProduct "substring of the device name"
Option "some option name" "the option value"
EndSection
The identifier is merely a human-readable string that shows up in the log
file. The MatchProduct line should contain the device name or a substring of
the device name that the snippet should apply to. For a full list of option
names and permitted values, see the
`libinput man page <https://www.mankier.com/4/libinput>`_.
xorg.conf.d snippets like the above apply to hotplugged devices but can be
overwritten at runtime by desktop tools. Multiple snippets may be placed
into the same file.
For run-time configuration and testing, the
`xinput <https://www.x.org/archive/X11R7.5/doc/man/man1/xinput.1.html>`_
debugging tool can modify a devices' properties. See the
`libinput man page <https://www.mankier.com/4/libinput>`_
for supported property names and values. Usually, an invocation looks like
this:
::
$> xinput set-prop "the device name" "the property name" value [value2] [value3]
.. note:: Changes performed by xinput do not persist across device hotplugs. xinput
is considered a debugging and testing tool only and should not be used
for permanent configurations.
.. _faq_configuration:
------------------------------------------------------------------------------
Can you add a configuration option for $FEATURE?
------------------------------------------------------------------------------
No. At least that's going to be the initial answer. Read
`Why libinput doesn't have a lot of configuration options <http://who-t.blogspot.com/2016/04/why-libinput-doesnt-have-lot-of-config.html>`_
first. Configuration options for most features are a signal that we are incapable
of handling it correctly. To get to that point, we want to be sure we're
truly incapable of doing so. libinput has several features that
are handled automatically (and correctly) that users wanted to have
configuration options for initially.
So the answer to this question will almost always be 'no'. A configuration
option is, in most cases, a cop-out.
.. _faq_synclient:
------------------------------------------------------------------------------
Why don't synclient and syndaemon work with libinput?
------------------------------------------------------------------------------
Synclient and syndaemon rely on X input device properties that are specific
to the xf86-input-synaptics X.Org input driver. Both were written when the
synaptics driver was the only common touchpad driver in existence. They
assume that if the properties aren't available, no touchpad is available
either. The xf86-input-libinput X.Org input driver does not export these
driver-specific properties, synclient/syndaemon will thus not detect the
touchpad and refuse to work. Other tools that rely on synclient/syndaemon or
those same properties also do not work with xf86-input-libinput.
Most of syndaemon's functionality is built into libinput, see
:ref:`disable-while-typing`. synclient is merely a configuration tool, see
:ref:`faq_configure_xorg` for similar functionality.
See also the blog posts
`The definitive guide to synclient <http://who-t.blogspot.com.au/2017/01/the-definitive-guide-to-synclient.html>`_ and
`The future of xinput, xmodmap, setxkbmap, xsetwacom and other tools under Wayland <http://who-t.blogspot.com.au/2016/12/the-future-of-xinput-xmodmap-setxkbmap.html>`_
.. _faq_tablets:
------------------------------------------------------------------------------
Does libinput support non-Wacom tablets?
------------------------------------------------------------------------------
Yes, though unfortunately many non-Wacom tablets suffer from bad firmware
and don't send the required events. But they should all work nonetheless. If
you have a tablet that does not work with libinput, please
:ref:`file a bug <reporting_bugs>`.
.. _faq_tablet_capabilities:
------------------------------------------------------------------------------
My tablet doesn't work
------------------------------------------------------------------------------
If you see the message
::
libinput bug: device does not meet tablet criteria. Ignoring this device.
or the message
::
missing tablet capabilities [...] Ignoring this device.
your tablet device does not have the required capabilities to be treated as
a tablet. This is usually a problem with the device and the kernel driver.
See :ref:`tablet-capabilities` for more details.
.. _faq_hwdb_changes:
------------------------------------------------------------------------------
How to apply hwdb changes
------------------------------------------------------------------------------
Sometimes users are asked to test updates to the
`udev hwdb <https://www.freedesktop.org/software/systemd/man/hwdb.html>`_
or patches that include a change to the hwdb. See :ref:`hwdb` for
details on the hwdb and how to modify it locally.
.. note:: As of libinput 1.12, libinput-specific properties are now stored in
the :ref:`device-quirks` system. There are no libinput-specific hwdb
entries anymore and any changes to the hwdb must be merged into the
systemd repository.
.. _faq_timer_offset:
------------------------------------------------------------------------------
What causes the "your system is too slow" warning?
------------------------------------------------------------------------------
libinput relies on the caller to call **libinput_dispatch()** whenever data is
available. **libinput_dispatch()** will process the state of all devices,
including some time-sensitive features (e.g. palm detection, tap-to-click,
disable-while-typing, etc.).
If the time between the event and the call to **libinput_dispatch()**
is excessive, those features may not work correctly. For example, a delay in
touch event processing may cause wrong or missing tap-to-click events or
a palm may not be detected correctly.
When this warning appears, it simply means that too much time has passed
between the event occurring and the current time. In almost all cases this
is an indication of the caller being overloaded and not handling events as
speedily as required.
The warning has no immediate effect on libinput's behavior but some of the
functionality that relies on the timer may be impeded. This is not a bug in
libinput. libinput does not control how quickly **libinput_dispatch()** is
called.
.. _faq_wayland:
------------------------------------------------------------------------------
Is libinput required for Wayland?
------------------------------------------------------------------------------
Technically - no. But for your use-case - probably.
Wayland is a display server communication protocol. libinput is a low-level
library to simplify handling input devices and their events. They have no
direct connection. As a technical analogy, the question is similar to "is
glibc required for HTTP", or (stretching the analogy a bit further) "Is a
pen required to write English". No, it isn't.
You can use libinput without a Wayland compositor, you can write a Wayland
compositor without libinput. On most major distributions, libinput is the
standard input stack used with the X.Org X server through the
xf86-input-libinput driver.
So why "for your use-case - probably"? All general-purpose Wayland
compositors use libinput for their input stack. Wayland compositors that
are more specialized (e.g. in-vehicle infotainment or IVI) can handle input
devices directly but the compositor you want to use
on your desktop needs an input stack that is more complex. And right now,
libinput is the only input stack that exists for this use-case.
.. _faq_separate_contexts:
------------------------------------------------------------------------------
Can I write a program to make libinput do $FOO
------------------------------------------------------------------------------
A common question is whether it's possible to write a program that can change
libinput's behavior - specifically the libinput that is used inside the
compositor. This indicates a misunderstanding of how libinput works:
libinput is a library that converts kernel events into libinput events, much
like ``sed`` reads data in, modifies it, and provides it to stdout.
.. graphviz:: libinput-contexts.gv
A libinput context is private to the process and cannot be modified from the
outside. To use the ``sed`` analogy again: if ``sed`` is used by a
shell-script, that script has full control over how ``sed`` processes data. In
this analogy, ``sed`` is libinput and the shell script is the compositor. It is
not possible to write a program to modify the behavior of the ``sed`` instance
used inside that shell script.
Writing a program that uses libinput is akin to writing a new script that
invoke ``sed``. It will not have any effect on the original ``sed`` instance.
The only way to modify libinput's behavior is to use the configuration options
exposed by the respective compositor. Those affect the libinput context inside
the compositor and thus have an effect on the input device behavior.
.. _faq_debug_events_not_showing_configuration:
------------------------------------------------------------------------------
Why doesn't libinput debug-events show my configuration
------------------------------------------------------------------------------
See :ref:`faq_separate_contexts`.
------------------------------------------------------------------------------
Can I configure scroll speed?
------------------------------------------------------------------------------
No, or at least, not as a libinput option.
When using a mouse, libinput notifies callers about physical scroll wheel
movement. When using another device, libinput notifies scroll in scroll units.
It is up to the caller to transform those events into a number of pixels to
scroll and, if desired, provide a way to adjust scroll speed.
This transformation cannot be done in libinput because it may depend on context
only known by the caller. For example, a caller may want to scroll faster
depending on how many pages a document has or depending on the widget that
receives the scroll events.

View file

@ -1,29 +0,0 @@
.. _features:
==============================================================================
libinput Features
==============================================================================
Below is a list of features supported by libinput. The availability of
features usually depends on the device type and a device's capabilities.
Not all features are user-configurable, some rely on :ref:`device-quirks`
to be useful.
.. toctree::
:maxdepth: 1
button-debouncing.rst
clickpad-softbuttons.rst
gestures.rst
middle-button-emulation.rst
palm-detection.rst
touchpad-thumb-detection.rst
scrolling.rst
t440-support.rst
tapping.rst
drag-3fg.rst
tablet-support.rst
switches.rst
touchpad-pressure.rst
trackpoints.rst

View file

@ -1,373 +0,0 @@
.. _gestures:
==============================================================================
Gestures
==============================================================================
libinput supports :ref:`gestures_pinch` and :ref:`gestures_swipe` on most
modern touchpads and other indirect touch devices. Note that libinput **does
not** support gestures on touchscreens, see :ref:`gestures_touchscreens`.
.. _gestures_lifetime:
-----------------------------------------------------------------------------
Lifetime of a gesture
-----------------------------------------------------------------------------
A gesture starts when the finger position and/or finger motion is
unambiguous as to what gesture to trigger and continues until the first
finger belonging to this gesture is lifted.
A single gesture cannot change the finger count. For example, if a user
puts down a fourth finger during a three-finger swipe gesture, libinput will
end (cancel) the three-finger gesture and, if applicable, start a
four-finger swipe gesture. A caller may however decide that those gestures
are semantically identical and continue the two gestures as one single
gesture.
.. _gestures_pinch:
------------------------------------------------------------------------------
Pinch gestures
------------------------------------------------------------------------------
Pinch gestures are executed when two or more fingers are located on the
touchpad and are either changing the relative distance to each other
(pinching) or are changing the relative angle (rotate). Pinch gestures may
change both rotation and distance at the same time. For such gestures,
libinput calculates a logical center for the gestures and provides the
caller with the delta x/y coordinates of that center, the relative angle of
the fingers compared to the previous event, and the absolute scale compared
to the initial finger position.
.. figure:: pinch-gestures.svg
:align: center
The pinch and rotate gestures
The illustration above shows a basic pinch in the left image and a rotate in
the right angle. Not shown is a movement of the logical center if the
fingers move unevenly. Such a movement is supported by libinput, it is
merely left out of the illustration.
Note that while position and angle is relative to the previous event, the
scale is always absolute and a multiplier of the initial finger position's
scale.
.. _gestures_swipe:
------------------------------------------------------------------------------
Swipe gestures
------------------------------------------------------------------------------
Swipe gestures are executed when three or more fingers are moved
synchronously in the same direction. libinput provides x and y coordinates
in the gesture and thus allows swipe gestures in any direction, including
the tracing of complex paths. It is up to the caller to interpret the
gesture into an action or limit a gesture to specific directions only.
.. figure:: swipe-gestures.svg
:align: center
The swipe gestures
The illustration above shows a vertical three-finger swipe. The coordinates
provided during the gesture are the movements of the logical center.
.. _gestures_hold:
------------------------------------------------------------------------------
Hold gestures
------------------------------------------------------------------------------
A hold gesture is one where the user places one or more fingers on the
device without significant movement. The exact conditions when a hold gesture
transitions to pointer motion, scrolling or other gestures
are implementation-defined.
The hold gesture is intended to allow for the implementation of two specific
features:
- where a two-finger scrolling starts kinetic scrolling in the caller, a
subsequent hold gesture can be used to stop that kinetic scroll motion,
and
- hold-to-trigger interactions where the interaction could be a click, a
context menu, or some other context-specific interaction.
Hold gestures have three potential logical states:
- **begin**: one or more fingers are placed on the device at the same time
- **end**: all fingers are removed and the device enters a neutral logical state
- **end(cancelled)**: all fingers are part of a known interaction and the
current hold gesture is no longer active. This may also occur when
switching between hold gestures with different finger counts.
.. note:: By definition, a hold gesture does not move and thus no coordinate
updates are available.
For example, a user that puts one finger, then a second finger down and
releases them later may trigger the following event sequence:
============= ============ ============
Action Event Finger count
============= ============ ============
Finger 1 down <no event>
Finger 2 down **begin** 2
Finger 2 up **end** 2
Finger 1 up <no event>
============= ============ ============
A hold gesture may by be **cancelled**. This occurs
when the hold gesture changes into some other interaction and should no
longer be considered the current hold gesture. A **end(cancelled)** event
applies to the whole gesture (all fingers). For example, a pointer motion on
a touchpad may trigger this sequence:
+-------------------+-----------------------+
| Action | Event |
+===================+=======================+
| | Finger 1 down | | **hold begin** |
+-------------------+-----------------------+
| | Finger 1 motion | | **hold cancel** |
| | | | **pointer motion** |
+-------------------+-----------------------+
| | Finger 1 motion | | **pointer motion** |
+-------------------+-----------------------+
| | Finger 1 up | | *no event* |
+-------------------+-----------------------+
.. note:: Many interactions with a touchpad will start with a hold
gesture that is then cancelled as that gesture turns into e.g.
pointer motion. A caller **must** handle hold gesture
cancellations correctly.
A two-finger scroll motion on a touchpad may trigger this sequence:
+------------------------+---------------------+--------------+
| Action | Event | Finger count |
+========================+=====================+==============+
| | Finger 1 down | | **hold begin** | | 1 |
+------------------------+---------------------+--------------+
| | Finger 2 down | | **hold cancel** | | 1 |
| | | | **hold begin** | | 2 |
+------------------------+---------------------+--------------+
| | Finger 1+2 motion | | **hold cancel** | | 2 |
| | | | **pointer axis** | | |
+------------------------+---------------------+--------------+
| | Finger 1+2 motion | | **pointer axis** | |
+------------------------+---------------------+--------------+
| | Finger 1 up | | **pointer axis** | |
| | Finger 2 up | | (scroll stop) | |
+------------------------+---------------------+--------------+
A three-finger-swipe on a touchpad may trigger this sequence:
+---------------------+---------------------+--------------+
| Action | Event | Finger count |
+=====================+=====================+==============+
| | Finger 1 down | | **hold begin** | | 1 |
+---------------------+---------------------+--------------+
| | Finger 2 down | | **hold cancel** | | 1 |
| | | | **hold begin** | | 2 |
+---------------------+---------------------+--------------+
| | Finger 3 down | | **hold cancel** | | 2 |
| | | | **hold begin** | | 3 |
+---------------------+---------------------+--------------+
| | Finger motion | | **hold cancel** | | 3 |
| | | | **swipe begin** | | 3 |
+---------------------+---------------------+--------------+
| | Finger motion | | **swipe update** | | 3 |
+---------------------+---------------------+--------------+
| | Finger 1 up | | **swipe end** | | 3 |
| | Finger 2 up | | | | |
| | Finger 3 up | | | | |
+---------------------+---------------------+--------------+
Single-finger hold gestures
...........................
libinput uses implementation-defined timeouts based on other interactions
to determine whether a single-finger hold gestures should start. In other
words, a caller **must not** rely on a hold gesture always being triggered
as soon as a single finger is placed on the touchpad. This is true for any
hold gesture but especially so for single-finger hold gestures.
Hold gestures with a single finger are prone to being extremely short-lived.
On many devices it is impossible to hold a finger still enough for there to
be no pointer motion events, even if those deltas are miniscule. Changing
movement thresholds to rely on hold gestures would reduce device
responsiveness.
It is thus the responsibility of the caller to determine where hold gestures
transition in and out of other interactions. For example, a two-finger hold
may produce a cancelled single-finger hold gesture first:
+--------------------+----------------------+--------------+--------------+
| Action | Event | Finger count | Notes |
+====================+======================+==============+==============+
| | Finger 1 down | | **hold begin** | | 1 | |
+--------------------+----------------------+--------------+--------------+
| | Finger 1 motion | | **hold cancel** | | 1 | | tiny deltas|
| | | | **pointer motion** | | | | |
+--------------------+----------------------+--------------+--------------+
| | Finger 2 down | | **hold begin** | | 2 | |
+--------------------+----------------------+--------------+--------------+
| | Finger 1 up | | **hold end** | | | |
| | Finger 2 up | | | | | |
+--------------------+----------------------+--------------+--------------+
Note how the second hold gesture started with a finger count of 2 - without
the user ever lifting the first finger. Cancellation of hold gesture does
not imply the user has lifted a finger.
A hold gesture may start after a previous gesture completed. For example, a
single finger move-and-hold may trigger different sequences for the same
user interaction:
+--------------------+---------------------+-------------------+--------------+
| Action | Device 1 | Device 2 | Notes |
+====================+=====================+===================+==============+
| | Finger 1 down | | **hold begin** | | **hold begin** | |
+--------------------+---------------------+-------------------+--------------+
| | Finger 1 motion | | **hold cancel** | | | tiny deltas|
| | | **pointer motion**| | | |
+--------------------+---------------------+-------------------+--------------+
| | | **hold begin** | | |
+--------------------+---------------------+-------------------+--------------+
| | Finger 1 up | | **hold end** | | **hold end** | |
+--------------------+---------------------+-------------------+--------------+
A caller that wants to use hold gestures must thus be able to infer the same
interaction based on a stream of pointer motion events with small deltas.
libinput may start a new hold begin gesture once the pointer stops moving.
The time between the last pointer motion event and the hold begin event is
implementation-defined.
Hold gestures and thumb/palm detection
......................................
Thumb and palm detection effectively remove touches from being counted
towards an interaction, see :ref:`thumb_detection` and
:ref:`palm_detection` for details.
In the context of hold gestures, thumbs and palms are treated by libinput as
if the finger was removed from the device. Where other non-thumb/non-palm
fingers remain on the device, an **hold update** event is sent. Otherwise,
the hold gesture terminates with a **hold cancel** event.
Notably, libinput's thumb and palm detection is not a simple boolean per
touch but specific to the state of that touch in the overall context. For
example, a touch may be a thumb for tapping but not for clickfinger
interactions. A caller must not infer the number of physical fingers from
the hold gesture.
Likewise, libinput may classify a finger as thumb in the same hardware event
as a new finger is placed on the touchpad. In that case, the hold gesture
**may** continue as one-finger gesture despite there being two physical
touch points.
Information to determine whether a touch is a thumb or a palm may not be
available until some time into an interaction. Thus very short brushes
of the touchpad by a palm may trigger a **hold begin** followed by an
immediate **hold end** as libinput lacks sufficient information to identify
the touch as thumb/palm and send the corresponding **hold cancel**
event. A caller must not assume that a hold gesture always represents a
valid finger down.
Hold gestures and tap-to-click
..............................
:ref:`tapping` is the feature that enables short-lived touches to trigger
button presses.
.. warning:: Summary: do not use hold gestures to do your own tap-to-click
implementation
In the context of hold gestures, tap-to-click cancels current hold gestures
and a finger dragging (see :ref:`tapndrag`) does not begin a hold
gesture. Where tap-to-click is disabled a tap-like gesture may create
**hold begin** followed by a **hold end** event. Callers **must not** use
hold gestures for their own tap-to-click implementation as the data is not
reliable enough. libinput may change internal timeouts and thresholds
depending on whether tap-to-click is enabled and the hold gesture event may
not match touch sequences that a user would expect to be a tap-to-click
interaction.
.. _gestures_touchscreens:
------------------------------------------------------------------------------
Touchscreen gestures
------------------------------------------------------------------------------
Touchscreen gestures are **not** interpreted by libinput. Rather, any touch
point is passed to the caller and any interpretation of gestures is up to
the caller or, eventually, the X or Wayland client.
Interpreting gestures on a touchscreen requires context that libinput does
not have, such as the location of windows and other virtual objects on the
screen as well as the context of those virtual objects:
.. figure:: touchscreen-gestures.svg
:align: center
Context-sensitivity of touchscreen gestures
In the above example, the finger movements are identical but in the left
case both fingers are located within the same window, thus suggesting an
attempt to zoom. In the right case both fingers are located on a window
border, thus suggesting a window movement. libinput has no knowledge of the
window coordinates and thus cannot differentiate the two.
.. _gestures_softbuttons:
------------------------------------------------------------------------------
Gestures with enabled software buttons
------------------------------------------------------------------------------
If the touchpad device is a :ref:`Clickpad <touchpads_buttons_clickpads>`, it
is recommended that a caller switches to :ref:`clickfinger`.
Usually fingers placed in a :ref:`software button area <software_buttons>`
are not considered for gestures, resulting in some gestures to be
interpreted as pointer motion or two-finger scroll events.
.. figure:: pinch-gestures-softbuttons.svg
:align: center
Interference of software buttons and pinch gestures
In the example above, the software button area is highlighted in red. The
user executes a three-finger pinch gesture, with the thumb remaining in the
software button area. libinput ignores fingers within the software button
areas, the movement of the remaining fingers is thus interpreted as a
two-finger scroll motion.
.. _gestures_twofinger_touchpads:
------------------------------------------------------------------------------
Gestures on two-finger touchpads
------------------------------------------------------------------------------
As of kernel 4.2, many :ref:`touchpads_touch_partial_mt` provide only two
slots. This affects how gestures can be interpreted. Touchpads with only two
slots can identify two touches by position but can usually tell that there
is a third (or fourth) finger down on the touchpad - without providing
positional information for that finger.
Touchpoints are assigned in sequential order and only the first two touch
points are trackable. For libinput this produces an ambiguity where it is
impossible to detect whether a gesture is a pinch gesture or a swipe gesture
whenever a user puts the index and middle finger down first. Since the third
finger does not have positional information, its location cannot be
determined.
.. figure:: gesture-2fg-ambiguity.svg
:align: center
Ambiguity of three-finger gestures on two-finger touchpads
The image above illustrates this ambiguity. The index and middle finger are
set down first, the data stream from both finger positions looks identical.
In this case, libinput assumes the fingers are in a horizontal arrangement
(the right image above) and use a swipe gesture.

View file

@ -1,5 +0,0 @@
def get_git_version():
return "__GIT_VERSION__"[:7]
def get_git_version_full():
return "__GIT_VERSION__"

View file

@ -1,36 +0,0 @@
.. _ignoring_devices:
==============================================================================
Ignoring specific devices
==============================================================================
If a device has the **LIBINPUT_IGNORE_DEVICE** udev property set to any
value but "0", that device is not initialized by libinput.
For a context created with **libinput_udev_create_context()**, the device is
silently ignored and never shows up. If the device is added with
**libinput_path_add_device()** to a context created with
**libinput_path_create_context()**, adding the device will fail and return NULL
(see that function's documentation for more
information).
If the property value is exactly "0", then the property is considered unset
and libinput initializes the device normally.
This property should be used for devices that are correctly detected as
input devices (see :ref:`udev_device_type`) but that should not be used by
libinput. It is recommended that devices that should not be handled as input
devices at all unset the **ID_INPUT** and related properties instead. The
**LIBINPUT_IGNORE_DEVICE** property signals that only libinput should
ignore this property but other parts of the stack (if any) should continue
treating this device normally.
Below is an example udev rule to assign **LIBINPUT_IGNORE_DEVICE** to the
device with the vendor/model ID of ``012a``/``034b``. ::
$ cat /etc/udev/rules.d/99-ignore-my-device.rules
ACTION!="remove", KERNEL=="event[0-9]*", \
ENV{ID_VENDOR_ID}=="012a", \
ENV{ID_MODEL_ID}=="034b", \
ENV{LIBINPUT_IGNORE_DEVICE}="1"
See :ref:`udev_config` for more details on libinput's udev properties.

View file

@ -1,62 +0,0 @@
.. _incorrectly_enabled_hires:
==============================================================================
Incorrectly enabled high-resolution scroll
==============================================================================
Some devices might announce support for high-resolution scroll wheel by enabling
``REL_WHEEL_HI_RES`` and/or ``REL_HWHEEL_HI_RES`` but never send a
high-resolution scroll event.
When the first low-resolution scroll event is received without any previous
high-resolution event, libinput prints a bug warning with the text **"device
supports high-resolution scroll but only low-resolution events have been
received"** and a link to this page.
.. note:: This warning will be printed only once
In most cases this is a bug on the device firmware, the kernel driver or in a
software used to create user-space devices through uinput.
Once the bug is detected, libinput will start emulating high-resolution scroll
events.
------------------------------------------------------------------------------
Detecting and fixing the issue
------------------------------------------------------------------------------
Events sent by a buggy device can be shown in the
:ref:`libinput record <libinput-record>` output for the device. Notice that
``REL_WHEEL_HI_RES`` and ``REL_HWHEEL_HI_RES`` are set but only ``REL_WHEEL``
events are sent: ::
# Supported Events:
# Event type 0 (EV_SYN)
# Event type 1 (EV_KEY)
# Event code 272 (BTN_LEFT)
# Event type 2 (EV_REL)
# Event code 0 (REL_X)
# Event code 1 (REL_Y)
# Event code 6 (REL_HWHEEL)
# Event code 8 (REL_WHEEL)
# Event code 11 (REL_WHEEL_HI_RES)
# Event code 12 (REL_HWHEEL_HI_RES)
[...]
quirks:
events:
- evdev:
- [ 0, 0, 2, 8, 1] # EV_REL / REL_WHEEL 1
- [ 0, 0, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +0ms
- evdev:
- [ 0, 15126, 2, 8, 1] # EV_REL / REL_WHEEL 1
- [ 0, 15126, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +15ms
- evdev:
- [ 0, 30250, 2, 8, 1] # EV_REL / REL_WHEEL 1
- [ 0, 30250, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +15ms
The issue can be fixed by adding a quirk to unset the ``REL_WHEEL_HI_RES`` and
``REL_HWHEEL_HI_RES`` event codes: ::
AttrEventCode=-REL_WHEEL_HI_RES;-REL_HWHEEL_HI_RES;
Please see :ref:`device-quirks` for details.

View file

@ -1,74 +0,0 @@
.. toctree::
:maxdepth: 2
:hidden:
what-is-libinput
features
configuration
building
faqs
reporting-bugs
troubleshooting
contributing
development
lua-plugins
API documentation <@HTTP_DOC_LINK@/api/>
++++++++++++++++++++++++++++++
libinput
++++++++++++++++++++++++++++++
libinput is a library that provides a full input stack for display servers
and other applications that need to handle input devices provided by the
kernel.
libinput provides device detection, event handling and abstraction to
minimize the amount of custom input code the user of libinput need to
provide the common set of functionality that users expect. Input event
processing includes scaling touch coordinates, generating
relative pointer events from touchpads, pointer acceleration, etc.
libinput is not used directly by applications. Think of it more as a device
driver than an application library. See :ref:`what_is_libinput` for more details.
--------------------
Users and Developers
--------------------
Please use the side-bar to navigate through the various documentation items.
-----------------
API documentation
-----------------
The API documentation is available here:
https://wayland.freedesktop.org/libinput/doc/latest/api/
.. note:: This documentation is generally only needed by authors of Wayland
compositors or other developers dealing with input events directly.
-------
License
-------
libinput is licensed under the MIT license
.. code-block:: none
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: [...]
See the
`COPYING <https://gitlab.freedesktop.org/libinput/libinput/tree/master/COPYING>`_
file for the full license information.
.....
About
.....
Documentation generated from |git_version|

View file

@ -1,661 +0,0 @@
.. _lua_plugins:
==============================================================================
Lua Plugins
==============================================================================
libinput provides a plugin system that allows users to modify the behavior
of devices. For example, a plugin may add or remove axes and/or buttons on a
device and/or modify the event stream seen by this device before it is passed
to libinput.
Plugins are implemented in `Lua <https://www.lua.org/>`_ (version 5.4)
and are typically loaded from the following paths:
- ``/etc/libinput/plugins/*.lua``, and
- ``/usr/lib{64}/libinput/plugins/*.lua``
Plugins are loaded in alphabetical order and where
multiple plugins share the same file name, the one in the highest precedence
directory is used. Plugins in ``/etc`` take precedence over
plugins in ``/usr``.
.. note:: Plugins lookup paths and their order are decided by the compositor.
Some compositors may support more/fewer/other lookup paths than the
above defaults.
Plugins are run sequentially in ascending sort-order (i.e. ``00-foo.lua`` runs
before ``10-bar.lua``) and each plugin sees the state left by any previous
plugins. For example if ``00-foo.lua`` changes all left button events to right
button events, ``10-bar.lua`` only ever sees right button events.
See the `Lua Reference manual <https://www.lua.org/manual/5.4/manual.html>`_ for
details on the Lua language.
.. note:: Plugins are **not** loaded by default, it is up to the compositor
whether to allow plugins. An explicit call to
``libinput_plugin_system_load_plugins()`` is required.
------------------------------------------------------------------------------
Limitations
------------------------------------------------------------------------------
Each script runs in its own sandbox and cannot communicate or share state with
other scripts.
Tables that hold API methods are not writable, i.e. it is not possible
to overwrite the default functionality of those APIs.
The Lua API available to plugins is limited to the following calls::
assert error ipairs next pairs tonumber
pcall select print tostring type xpcall
table string math _VERSION
It is not possible to e.g. use the ``io`` module from a script.
To use methods on instantiated objects, the ``object:method`` method call
syntax must be used. For example:
.. code-block:: lua
libinput:register()
libinput.register() -- this will fail
------------------------------------------------------------------------------
When to use plugins
------------------------------------------------------------------------------
libinput plugins are a relatively niche use-case that typically need to
address either once-off issues (e.g. those caused by worn-out hardware) or
user preferences that libinput does not and will not cater for.
Plugins should not be used for issues that can be fixed generically, for
example via :ref:`device-quirks`.
As a rule of thumb: a plugin should be a once-off that only works for one
user's hardware. If a plugin can be shared with many users then the plugin
implements functionality that should be integrated into libinput proper.
------------------------------------------------------------------------------
Testing plugins
------------------------------------------------------------------------------
Our :ref:`tools` support plugins if passed the ``--enable-plugins`` commandline
option. For implementing and testing plugins the easiest commands to test are
- ``libinput debug-events --enable-plugins`` (see :ref:`libinput-debug-events` docs)
- ``libinput debug-gui --enable-plugins`` (see :ref:`libinput-debug-gui` docs)
Where libinput is built and run from git, the tools will also look for plugins
in the meson build directory. See the ``plugins/meson.build`` file for details.
.. _plugins_api_lua:
--------------------------------------------------------------------------------
Lua Plugin API
--------------------------------------------------------------------------------
Lua plugins sit effectively below libinput and the API is not a
representation of the libinput API. Plugins modify the evdev event stream
received from the kernel.
.. graphviz:: plugin-stack.gv
The API revolves around two types: ``libinput`` and ``EvdevDevice``. The
``libinput`` type is used to register a plugin from a script, the
``EvdevDevice`` represents one device that is present in the system (but may
not have yet been added by libinput).
Typically a script does the following steps:
- register with libinput via ``libinput:register({versions})``
- connect to the ``"new-evdev-device"`` event
- receive an ``EvdevDevice`` object in the ``"new-evdev-device"`` callback
- check and/or modify the evdev event codes on the device
- connect to the device's ``"evdev-frame"`` event
- receive an :ref:`evdev frame <plugins_api_evdev_frame>` in the device's
``"evdev-frame"`` callback
- check and/or modify the events in that frame
Where multiple plugins are active, the evdev frame passed to the callback is
the combined frame as processed by all previous plugins in ascending sort order.
For example, if one plugin discards all button events subsequent plugins will
never see those button events in the frame.
.. _plugins_api_version_stability:
..............................................................................
Plugin version stability
..............................................................................
Plugin API version stability is provided on a best effort basis. We aim to provide
stable plugin versions for as long as feasible but may need to retire some older
versions over time. For this reason a plugin can select multiple versions it
implements, libinput will pick one supported version and adjust the plugin
behavior to match that version. See the ``libinput:register()`` call for details.
--------------------------------------------------------------------------------
Lua Plugin API Reference
--------------------------------------------------------------------------------
libinput provides the following globals and types:
.. _plugins_api_evdev_usage:
................................................................................
Evdev Usages
................................................................................
Evdev usages are a libinput-specific wrapper around the ``linux/input-event-codes.h``
evdev types and codes. They are used by libinput internally and are a 32-bit
combination of ``type << 16 | code``. Each usage carries the type and code and
is thus simpler to pass around and less prone to type confusion.
The :ref:`evdev global <plugins_api_evdev_global>` attempts to provide all
available usages but for the niche cases where it does not provide a named constant
the value can be crafted manually:
.. code-block:: lua
evdev_type = 0x3 -- EV_REL
evdev_code = 0x1 -- REL_Y
evdev_usage = (evdev_type << 16) | evdev_code
assert(usage == evdev.REL_Y)
.. _plugins_api_evdev_global:
................................................................................
The ``evdev`` global
................................................................................
The ``evdev`` global represents all known :ref:`plugins_api_evdev_usage`,
effectively in the form:
.. code-block:: lua
evdev = {
ABS_X = (3 << 16) | 0,
ABS_Y = (3 << 16) | 1,
...
REL_X = (2 << 16) | 0,
REL_Y = (2 << 16) | 1,
...
}
This global is provided for convenience to improve readability in the code.
Note that the name uses the event code name only (e.g. ``evdev.ABS_Y``) but the
value is an :ref:`Evdev Usage <plugins_api_evdev_usage>` (type and code).
See the ``linux/input-event-codes.h`` header file provided by your kernel
for a list of all evdev types and codes.
The evdev global also provides the bus type constants, e.g. ``evdev.BUS_USB``.
See the ``linux/input.h`` header file provided by your kernel
for a list of bus types.
.. _plugins_api_evdev_frame:
................................................................................
Evdev frames
................................................................................
Evdev frames represent a single frame of evdev events for a device. A frame
is a group of events that occurred at the same time. The frame usually only
contains state that has changed compared to the previous frame.
In our API a frame is exposed as a nested table with the following structure:
.. code-block:: lua
frame1 = {
{ usage = evdev.ABS_X, value = 123 },
{ usage = evdev.ABS_Y, value = 456 },
{ usage = evdev.BTN_LEFT, value = 1 },
}
frame2 = {
{ usage = evdev.ABS_Y, value = 457 },
}
frame3 = {
{ usage = evdev.ABS_X, value = 124 },
{ usage = evdev.BTN_LEFT, value = 0 },
}
.. note:: This API does not use ``SYN_REPORT`` events, it is implied at the
end of the table. Where a plugin writes a ``SYN_REPORT`` into the
list of events, that ``SYN_REPORT`` terminates the event frame
(similar to writing a ``\0`` into the middle of a C string).
A frame containing only a ``SYN_REPORT`` is functionally equivalent
to an empty frame.
Events or frames do not have a timestamp. Where a timestamp is required, that
timestamp is passed as additional argument to the function or return value.
See :ref:`plugins_api_evdev_global` for a list of known usages.
.. warning:: Evdev frames have an implementation-defined size limit of how many
events can be added to a single frame. This limit should never be
hit by valid plugins.
.. _plugins_api_libinputglobal:
................................................................................
The ``libinput`` global object
................................................................................
The core of our plugin API is the ``libinput`` global object. A script must
immediately ``register()`` to be active, otherwise it is unloaded immediately.
All libinput-specific APIs can be accessed through the ``libinput`` object.
.. function:: libinput:register({1, 2, ...})
Register this plugin with the given table of supported version numbers and
returns the version number selected by libinput for this plugin. See
:ref:`plugins_api_version_stability` for details.
.. code-block:: lua
-- this plugin can support versions 1, 4 and 5
version = libinput:register({1, 4, 5})
if version == 1 then
...
This function must be the first function called.
If the plugin calls any other functions before ``register()``, those functions
return the default zero value for the return type (``nil``, ``0``, an empty
table, etc.).
If the plugin does not call ``register()`` it will be removed immediately.
Once registered, any connected callbacks will be invoked whenever libinput
detects new devices, removes devices, etc.
This function must only be called once.
.. function:: libinput:unregister()
Unregister this plugin. This removes the plugin from libinput and releases
any resources associated with this plugin. This call must be the last call
in your plugin, it is effectively equivalent to Lua's
`os.exit() <https://www.lua.org/manual/5.4/manual.html#pdf-os.exit>`_.
.. function:: libinput:log_debug(message)
Log a message at the libinput debug log priority. See
``libinput:log_error()`` for details.
.. function:: libinput:log_info(message)
Log a message at the libinput info log priority. See
``libinput:log_error()`` for details.
.. function:: libinput:log_error(message)
Log a message at the libinput error log priority. Whether a message is
displayed in the log depends on libinput's log priority, set by the caller.
A compositor may disable stdout and stderr. Log messages should be preferred
over Lua's ``print()`` function to ensure the messages end up in the same
location as other libinput log messages and are not discarded.
.. function:: libinput:now()
Returns the current time in microseconds in ``CLOCK_MONOTONIC``. This is
the timestamp libinput uses internally. This timestamp cannot be mapped
to any particular time of day, see the
`clock_gettime() man page <https://man7.org/linux/man-pages/man3/clock_gettime.3.html>`_
for details.
.. function:: libinput:version()
Returns the agreed-on version of the plugin, see ``libinput:register()``.
If called before ``libinput:register()`` this function returns ``0``.
.. function:: libinput:connect(name, function)
Set the callback to the given event name. Only one callback
may be set for an event name at any time, subsequent callbacks
will replace any earlier callbacks for the same name.
Version 1 of the plugin API supports the following events and callback arguments:
- ``"new-evdev-device"``: A new :ref:`EvdevDevice <plugins_api_evdevdevice>`
has been seen by libinput but not yet added.
.. code-block:: lua
libinput:connect("new-evdev-device", function (device) ... end)
- ``"timer-expired"``: The timer for this plugin has expired. This event is
only sent if the plugin has set a timer with ``timer_set()``.
.. code-block:: lua
libinput:connect("timer-expired", function (now) ... end)
The ``now`` argument is the current time in microseconds in
``CLOCK_MONOTONIC`` (see ``libinput:now()``).
.. function:: libinput:timer_cancel()
Cancel the timer for this plugin. This is a no-op if the timer
has not been set or has already expired.
.. function:: libinput:timer_set_absolute(time)
Set a timer for this plugin, with the given time in microseconds.
The timeout specifies an absolute time in microseconds (see
``libinput:now()``) The timer will expire once and then call the
``"timer-expired"`` event handler (if any).
See ``libinput:timer_set_relative()`` for a relative timer.
The following two lines of code are equivalent:
.. code-block:: lua
libinput:timer_set_relative(1000000) -- 1 second from now
libinput:timer_set_absolute(libinput:now() + 1000000) -- 1 second from now
Calling this function will cancel any existing (relative or absolute) timer.
.. function:: libinput:timer_set_relative(timeout)
Set a timer for this plugin, with the given timeout in microseconds from
the current time. The timer will expire once and then call the
``"timer-expired"`` event handler (if any).
See ``libinput:timer_set_absolute()`` for an absolute timer.
The following two lines of code are equivalent:
.. code-block:: lua
libinput:timer_set_relative(1000000) -- 1 second from now
libinput:timer_set_absolute(libinput:now() + 1000000) -- 1 second from now
Calling this function will cancel any existing (relative or absolute) timer.
.. _plugins_api_evdevdevice:
................................................................................
The ``EvdevDevice`` type
................................................................................
The ``EvdevDevice`` type represents a device available in the system
but not (yet) added by libinput. This device may be used to modify
a device's capabilities before the device is processed by libinput.
A plugin should always ``connect()`` to the ``"device-removed"`` callback
to be notified when a device is removed. If the plugin keeps a reference
to this device but the device is discarded by libinput, the device's query
methods will return zero values (e.g. ``nil``, ``0``, an empty table) and
methods will be noops.
.. function:: EvdevDevice:info()
A table containing static information about the device, e.g.
.. code-block:: lua
{
bustype = evdev.BUS_USB,
vid = 0x1234,
pid = 0x5678,
}
A plugin must ignore keys it does not know about.
Version 1 of the plugin API supports the following keys and values:
- ``bustype``: The numeric bustype of the device. See the
``BUS_*`` defines in ``linux/input.h`` for the list of possible values.
- ``vid``: The 16-bit vendor ID of the device
- ``pid``: The 16-bit product ID of the device
If the device has since been discarded by libinput, this function returns an
empty table.
.. function:: EvdevDevice:name()
The device name as set by the kernel
.. function:: EvdevDevice:usages()
Returns a table of all usages that are currently enabled for this
device. Any type that exists on the device has a table assigned and in this
table any code that exists on the device is a boolean true.
For example:
.. code-block:: lua
{
evdev.REL_X = true,
evdev.REL_Y = true,
evdev.BTN_LEFT = true,
}
All other usages are ``nil``, so that the following code is possible:
.. code-block:: lua
local usages = device:usages()
if usages[evdev.REL_X] then
-- do something
end
If the device has since been discarded by libinput, this function returns an
empty table.
.. function:: EvdevDevice:absinfos()
Returns a table of all ``EV_ABS`` codes that are currently enabled for this device.
The event code is the key, each value is a table containing the following keys:
``minimum``, ``maximum``, ``fuzz``, ``flat``, ``resolution``.
.. code-block:: lua
{
evdev.ABS_X = {
minimum = 0,
maximum = 1234,
fuzz = 0,
flat = 0,
resolution = 45,
},
}
If the device has since been discarded by libinput, this function returns an
empty table.
.. function:: EvdevDevice:udev_properties()
Returns a table containing a filtered list of udev properties available on this device
in the form ``{ property_name = property_value, ... }``.
udev properties used as a boolean (e.g. ``ID_INPUT``) are only present if their
value is a logical true.
Version 1 of the plugin API supports the following udev properties:
- ``ID_INPUT`` and all of ``ID_INPUT_*`` that denote the device type as assigned
by udev. This information is usually used by libinput to determine a
device type. Note that for historical reasons these properties have
varying rules - some properties may be mutually exclusive, others are
independent, others may only be set if another property is set. Refer to
the udev documentation (if any) for details. ``ID_INPUT_WIDTH_MM`` and
``ID_INPUT_HEIGHT_MM`` are excluded from this set.
If the device has since been discarded by libinput, this function returns an
empty table.
.. function:: EvdevDevice:enable_evdev_usage(usage)
Enable the given :ref:`evdev usage <plugins_api_evdev_usage>` for this device.
Use :ref:`plugins_api_evdev_global` for better readability,
e.g. ``device:enable_evdev_usage(evdev.REL_X)``.
This function must not be used for ``ABS_*`` events, use ``set_absinfo()``
instead.
Once a usage is enabled, events for that usage may be added to a device's
frame.
If the device has since been discarded by libinput, this function does nothing.
.. function:: EvdevDevice:disable_evdev_usage(usage)
Disable the given :ref:`evdev usage <plugins_api_evdev_usage>` for this device.
Use :ref:`plugins_api_evdev_global` for better readability,
e.g. ``device:disable_evdev_usage(evdev.REL_X)``.
Once a usage is disabled, events for that usage are discarded from any
device frame.
If the device has since been discarded by libinput, this function does nothing.
.. function:: EvdevDevice:set_absinfo(usage, absinfo)
Set the absolute axis information for the given :ref:`evdev usage <plugins_api_evdev_usage>`
and enable it if it does not yet exist on the device. The ``absinfo`` argument is a table
containing zero or more of the following keys: ``minimum``, ``maximum``, ``fuzz``,
``flat``, ``resolution``. Any missing key defaults the corresponding
value from the device if the device already has this event usage or zero otherwise.
For example, the following code changes the resolution but leaves everything
else as-is:
.. code-block:: lua
local absinfo = {
resolution = 40,
}
device:set_absinfo(evdev.ABS_X, absinfo)
device:set_absinfo(evdev.ABS_Y, absinfo)
Use :ref:`plugins_api_evdev_global` for better readability as shown in the
example above.
If the device has since been discarded by libinput, this function does nothing.
.. note:: Overriding the absinfo values often indicates buggy firmware. This should
typically be fixed with an entry in the
`60-evdev.hwdb <https://github.com/systemd/systemd/blob/main/hwdb.d/60-evdev.hwdb>`_
or :ref:`device-quirks` instead of a plugin so all users of that
device can benefit from the fix.
.. function:: EvdevDevice:connect(name, function)
Set the callback to the given event name. Only one callback
may be set for an event name at any time, subsequent callbacks
will overwrite any earlier callbacks for the same name.
If the device has since been discarded by libinput, this function does nothing.
Version 1 of the plugin API supports the following events and callback arguments:
- ``"evdev-frame"``: A new :ref:`evdev frame <plugins_api_evdev_frame>` has
started for this device. If the callback returns a value other than
``nil``, that value is the frame with any modified events.
An empty frame (``{}``) causes libinput to drop the current event frame.
.. code-block:: lua
device:connect("evdev-frame", function (device, frame, timestamp)
-- change any event into a movement left by 1 pixel
move_left = {
{ usage = evdev.REL_X, value = -1, },
}
return move_left
end
The timestamp of an event frame is in microseconds in ``CLOCK_MONOTONIC``, see
``libinput:now()`` for details.
For performance reasons plugins that do not modify the event frame should
return ``nil`` (or nothing) instead of the event frame that was passed
as argument.
- ``"device-removed"``: This device was removed by libinput. This may happen
without the device ever becoming a libinput device as seen by libinput's
public API (e.g. if the device does not meet the requirements to be
added). Once this callback is invoked, the plugin should remove any
references to this device and stop using it.
.. code-block:: lua
device:connect("device-removed", function (device) ... end)
Functions to query the device's capabilities (e.g. ``usages()``) will
return an empty table.
.. function:: EvdevDevice:disconnect(name)
Disconnect the existing callback (if any) for the given event name. See
``EvdevDevice:connect()`` for a list of supported names.
.. function:: EvdevDevice:prepend_frame(frame)
Prepend an :ref:`evdev frame <plugins_api_evdev_frame>` for this device
**before** the current frame (if any). The **next** plugin will see the
prepended frame first followed by the current frame.
This function can only be called from within a device's ``"evdev-frame"``
handler or from within the plugin's timer callback function.
For example, to change a single event into a drag, prepend a button
down and append a button up before each event:
.. code:: lua
function frame_handler(device, frame, timestamp)
device:prepend_frame({
{ usage = evdev.BTN_LEFT, value = 1}
})
device:append_frame({
{ usage = evdev.BTN_LEFT, value = 0}
})
return nil -- return the current frame unmodified
-- The next plugin sees the event sequence:
-- button down, frame, button up
end
If called from within the plugin's timer there is no current frame and this
function is identical to ``append_frame()``.
.. function:: EvdevDevice:append_frame(frame)
Appends an :ref:`evdev frame <plugins_api_evdev_frame>` for this device
**after** the current frame (if any). This function can only be called from
within a device's ``"evdev-frame"`` handler or from within the plugin's timer
callback function.
If called from within the plugin's timer there is no current frame and this
function is identical to ``prepend_frame()``.
See ``prepend_frame()`` for more details.
.. function:: EvdevDevice:disable_feature(feature_name)
Disable the given libinput-internal feature for this device. This should be used
by plugins that replace that feature with a custom implementation for this device.
libinput may have multiple internal implementations for any given feature, disabling
it via this API disables any and all of those implementations, causing the feature to
no longer work at all. It is up to the plugin implementation to re-implement that
feature to match the user's expectation.
Version 1 of the plugin API supports the following features:
- ``"button-debouncing"``: see :ref:`button_debouncing`
- ``"touchpad-hysteresis"``: see :ref:`touchpad_jitter`
- ``"touchpad-jump-detection"``: see :ref:`touchpad_jumping_cursor`
- ``"touchpad-palm-detection"``: see :ref:`palm_detection`
- ``"wheel-debouncing"``: some high-resolution mouse wheel movements inside libinput
are delayed and/or modified

View file

@ -1,224 +0,0 @@
# Sphinx build
sphinx = find_program('sphinx-build-3', 'sphinx-build', required : false)
if not sphinx.found()
error('Program "sphinx-build" not found or not executable. Try building with -Ddocumentation=false')
endif
yq = find_program('yq', required : false)
if not yq.found()
warning('Program "yq" not found or not executable. Dependency list will not be built.')
endif
sphinx_config = configuration_data()
sphinx_config.set('PROJECT_NAME', meson.project_name())
sphinx_config.set('PROJECT_VERSION', meson.project_version())
sphinx_config.set('BUILDDIR', meson.current_build_dir())
sphinx_config.set('HTTP_DOC_LINK', doc_url)
git_version_page = vcs_tag(command : ['git', 'log', '-1', '--format=%H'],
fallback : 'unknown',
input : 'git_version.py.in',
output : 'git_version.py',
replace_string: '__GIT_VERSION__')
sphinx_conf_py = configure_file(input : 'conf.py.in',
output : 'conf.py',
configuration : sphinx_config)
# 404 replacements for old URLs
# The switch to sphinx caused a few pages to be renamed, sphinx uses
# filename.html whereas doxygen used whatever the @page foo was. So old docs
# *mostly* used underscores, now we're consistent with dashes.
# We can't use htaccess on the server, so let's auto-generate a 404 list
# with a basic page telling users that the link has moved. This can be
# removed in a few months, towards the end of 2018.
#
# File list is: [current-sphinx-input-file, old-generated-page]
# If they're the same they'll be ignored.
src_404s = [
[ 'absolute-axes.rst', 'absolute_axes.html'],
[ 'absolute-coordinate-ranges.rst', 'absolute_coordinate_ranges.html'],
[ 'architecture.rst', 'architecture.html'],
[ 'building.rst', 'building_libinput.html'],
[ 'button-debouncing.rst', 'button_debouncing.html'],
[ 'clickpad-softbuttons.rst', 'clickpad_softbuttons.html'],
[ 'configuration.rst', 'config_options.html'],
[ 'contributing.rst', 'contributing.html'],
[ 'development.rst', 'development.html'],
[ 'device-configuration-via-udev.rst', 'udev_config.html'],
[ 'device-quirks.rst', 'device-quirks.html'],
[ 'faqs.rst', 'faq.html'],
[ 'features.rst', 'features.html'],
[ 'gestures.rst', 'gestures.html'],
[ 'incorrectly-enabled-hires.rst', 'incorrectly-enabled-hires.html'],
[ 'middle-button-emulation.rst', 'middle_button_emulation.html'],
[ 'normalization-of-relative-motion.rst', 'motion_normalization.html'],
[ 'palm-detection.rst', 'palm_detection.html'],
[ 'pointer-acceleration.rst', 'pointer-acceleration.html'],
[ 'reporting-bugs.rst', 'reporting_bugs.html'],
[ 'scrolling.rst', 'scrolling.html'],
[ 'seats.rst', 'seats.html'],
[ 'switches.rst', 'switches.html'],
[ 't440-support.rst', 't440_support.html'],
[ 'tablet-support.rst', 'tablet-support.html'],
[ 'tapping.rst', 'tapping.html'],
[ 'test-suite.rst', 'test-suite.html'],
[ 'timestamps.rst', 'timestamps.html'],
[ 'tools.rst', 'tools.html'],
[ 'touchpad-jitter.rst', 'touchpad_jitter.html'],
[ 'touchpad-jumping-cursors.rst', 'touchpad_jumping_cursor.html'],
[ 'touchpad-pressure.rst', 'touchpad_pressure.html'],
[ 'touchpads.rst', 'touchpads.html'],
[ 'trackpoints.rst', 'trackpoints.html'],
[ 'troubleshooting.rst', 'troubleshooting.html'],
[ 'what-is-libinput.rst', 'what_is_libinput.html'],
]
dst_404s = []
foreach s404 : src_404s
target = s404[0]
oldpage = s404[1]
tname = target.split('.rst')[0]
oname = oldpage.split('.html')[0]
if tname != oname
config_404 = configuration_data()
config_404.set('TARGET', '@0@.html'.format(tname))
c = configure_file(input : '404.rst',
output : '@0@.rst'.format(oname),
configuration : config_404)
dst_404s += [c]
endif
endforeach
src_rst = files(
# dot drawings
'dot/seats-sketch.gv',
'dot/seats-sketch-libinput.gv',
'dot/libinput-contexts.gv',
'dot/libinput-stack-wayland.gv',
'dot/libinput-stack-xorg.gv',
'dot/libinput-stack-gnome.gv',
'dot/evemu.gv',
'dot/libinput-record.gv',
'dot/plugin-stack.gv',
# svgs
'svg/button-debouncing-wave-diagram.svg',
'svg/button-scrolling.svg',
'svg/clickfinger.svg',
'svg/clickfinger-distance.svg',
'svg/edge-scrolling.svg',
'svg/gesture-2fg-ambiguity.svg',
'svg/palm-detection.svg',
'svg/pinch-gestures.svg',
'svg/pinch-gestures-softbuttons.svg',
'svg/ptraccel-custom.svg',
'svg/ptraccel-linear.svg',
'svg/ptraccel-low-dpi.svg',
'svg/ptraccel-touchpad.svg',
'svg/ptraccel-trackpoint.svg',
'svg/software-buttons.svg',
'svg/software-buttons-conditions.svg',
'svg/software-buttons-thumbpress.svg',
'svg/software-buttons-visualized.svg',
'svg/swipe-gestures.svg',
'svg/tablet-area.svg',
'svg/tablet-axes.svg',
'svg/tablet-cintiq24hd-modes.svg',
'svg/tablet-eraser-invert.svg',
'svg/tablet-eraser-button.svg',
'svg/tablet-interfaces.svg',
'svg/tablet-intuos-modes.svg',
'svg/tablet-left-handed.svg',
'svg/tablet-out-of-bounds.svg',
'svg/tablet.svg',
'svg/tap-n-drag.svg',
'svg/thumb-detection.svg',
'svg/top-software-buttons.svg',
'svg/touchscreen-gestures.svg',
'svg/trackpoint-delta-illustration.svg',
'svg/twofinger-scrolling.svg',
# rst files
'absolute-axes.rst',
'absolute-coordinate-ranges.rst',
'architecture.rst',
'building.rst',
'button-debouncing.rst',
'clickpad-softbuttons.rst',
'clickpad-with-right-button.rst',
'contributing.rst',
'device-configuration-via-udev.rst',
'device-quirks.rst',
'drag-3fg.rst',
'faqs.rst',
'gestures.rst',
'incorrectly-enabled-hires.rst',
'ignoring-devices.rst',
'middle-button-emulation.rst',
'normalization-of-relative-motion.rst',
'palm-detection.rst',
'lua-plugins.rst',
'pointer-acceleration.rst',
'reporting-bugs.rst',
'scrolling.rst',
'seats.rst',
'switches.rst',
't440-support.rst',
'tablet-support.rst',
'tapping.rst',
'test-suite.rst',
'timestamps.rst',
'tablet-debugging.rst',
'tools.rst',
'touchpad-jumping-cursors.rst',
'touchpad-pressure.rst',
'touchpad-pressure-debugging.rst',
'touchpad-jitter.rst',
'touchpad-thumb-detection.rst',
'touchpads.rst',
'trackpoints.rst',
'trackpoint-configuration.rst',
'what-is-libinput.rst',
'wheel-api.rst',
'features.rst',
'development.rst',
'troubleshooting.rst',
'configuration.rst',
)
src_sphinx = []
foreach f : src_rst
sf = configure_file(input: f,
output: '@PLAINNAME@',
copy : true)
src_sphinx += [ sf ]
endforeach
configure_file(input: 'index.rst',
output: 'index.rst',
configuration: sphinx_config)
dependencies_config = configuration_data()
if yq.found()
distributions = ['fedora', 'ubuntu', 'debian', 'arch', 'alpine']
foreach distro : distributions
yq_filter = '.distributions[] | select(.name == "@0@") | .packages | join(" ")'.format(distro)
deps = run_command(yq, '-r', yq_filter,
dir_gitlab_ci / 'config.yml',
check: true).stdout()
dependencies_config.set('@0@_PACKAGES'.format(distro.to_upper()), deps)
endforeach
endif
configure_file(input: 'dependencies.rst',
output: 'dependencies.rst',
configuration: dependencies_config)
# do not use -j, it breaks on Ubuntu
sphinx_output_dir = 'Documentation'
custom_target('sphinx',
input : [ sphinx_conf_py, git_version_page ] + src_sphinx + dst_404s,
output : [ sphinx_output_dir ],
command : [ sphinx, '-q', '-b', 'html',
'-d', meson.current_build_dir() / 'doctrees',
meson.current_build_dir(), sphinx_output_dir],
build_by_default : true)

View file

@ -1,35 +0,0 @@
.. _middle_button_emulation:
==============================================================================
Middle button emulation
==============================================================================
Middle button emulation provides users with the ability to generate a middle
click even when the device does not have a physical middle button available.
When middle button emulation is enabled, a simultaneous press of the left
and right button generates a middle mouse button event. Releasing the
buttons generates a middle mouse button release, the left and right button
events are discarded otherwise.
The middle button release event may be generated when either button is
released, or when both buttons have been released. The exact behavior is
device-dependent, libinput will implement the behavior that is most
appropriate to the physical device.
The middle button emulation behavior when combined with other device
buttons, including a physical middle button is device-dependent.
For example, :ref:`clickpad_softbuttons` provides a middle button area when
middle button emulation is disabled. That middle button area disappears
when middle button emulation is enabled - a middle click can then only be
triggered by a simultaneous left + right click.
Some devices provide middle mouse button emulation but do not allow
enabling/disabling that emulation. Likewise, some devices may allow middle
button emulation but have it disabled by default. This is the case for most
mouse-like devices where a middle button is detected.
libinput provides **libinput_device_config_middle_emulation_set_enabled()** to
enable or disable middle button emulation. See :ref:`faq_configure_wayland`
and :ref:`faq_configure_xorg` for info on how to enable or disable middle
button emulation in the Wayland compositor or the X stack.

View file

@ -1,97 +0,0 @@
.. _motion_normalization:
==============================================================================
Normalization of relative motion
==============================================================================
Most relative input devices generate input in so-called "mickeys". A
mickey is in device-specific units that depend on the resolution
of the sensor. Most optical mice use sensors with 1000dpi resolution, but
some devices range from 100dpi to well above 8000dpi.
Without a physical reference point, a relative coordinate cannot be
interpreted correctly. A delta of 10 mickeys may be a millimeter of
physical movement or 10 millimeters, depending on the sensor. This
affects pointer acceleration in libinput and interpretation of relative
coordinates in callers.
libinput does partial normalization of relative input. For devices with a
resolution of 1000dpi and higher, motion events are normalized to a default
of 1000dpi before pointer acceleration is applied. As a result, devices with
1000dpi and above feel the same.
Devices below 1000dpi are not normalized (normalization of a 1-device unit
movement on a 400dpi mouse would cause a 2.5 pixel movement). Instead,
libinput applies a dpi-dependent acceleration function. At low speeds, a
1-device unit movement usually translates into a 1-pixel movements. As the
movement speed increases, acceleration is applied - at high speeds a low-dpi
device will roughly feel the same as a higher-dpi mouse.
The reason for the normalization is convenience: a caller can assume that a
delta of 1 should result in a movement of 1 pixel on a traditional
(low-dpi) screen. On screens with high resolutions, the caller must scale
according to the UI scale factors.
This normalization only applies to accelerated coordinates, unaccelerated
coordinates are left in device-units. It is up to the caller to interpret
those coordinates correctly.
.. _motion_normalization_touchpad:
------------------------------------------------------------------------------
Normalization of touchpad coordinates
------------------------------------------------------------------------------
Touchpads may have a different resolution for the horizontal and vertical
axis. Interpreting coordinates from the touchpad without taking resolution
into account results in uneven motion.
libinput scales unaccelerated touchpad motion to the resolution of the
touchpad's x axis, i.e. the unaccelerated value for the y axis is:
``y = (x / resolution_x) * resolution_y``.
.. _motion_normalization_tablet:
------------------------------------------------------------------------------
Normalization of tablet coordinates
------------------------------------------------------------------------------
See :ref:`tablet-relative-motion`
.. _motion_normalization_customization:
------------------------------------------------------------------------------
Setting custom DPI settings
------------------------------------------------------------------------------
Devices usually do not advertise their resolution and libinput relies on
the udev property **MOUSE_DPI** for this information. This property is usually
set via the
`udev hwdb <http://cgit.freedesktop.org/systemd/systemd/tree/hwdb/70-mouse.hwdb>`_.
The ``mouse-dpi-tool`` utility provided by
`libevdev <https://freedesktop.org/wiki/Software/libevdev/>`_ should be
used to measure a device's resolution.
The format of the property for single-resolution mice is: ::
MOUSE_DPI=resolution@frequency
The resolution is in dots per inch, the frequency in Hz.
The format of the property for multi-resolution mice may list multiple
resolutions and frequencies: ::
MOUSE_DPI=r1@f1 *r2@f2 r3@f3
The default frequency must be pre-fixed with an asterisk.
For example, these two properties are valid: ::
MOUSE_DPI=800@125
MOUSE_DPI=400@125 800@125 *1000@500 5500@500
The behavior for a malformed property is undefined. If the property is
unset, libinput assumes the resolution is 1000dpi.
Note that HW does not usually provide information about run-time
resolution changes, libinput will thus not detect when a resolution
changes to the non-default value.

View file

@ -1,227 +0,0 @@
.. _palm_detection:
==============================================================================
Palm detection
==============================================================================
Palm detection tries to identify accidental touches while typing, while
using the trackpoint and/or during general use of the touchpad area.
On most laptops typing on the keyboard generates accidental touches on the
touchpad with the palm (usually the area below the thumb). This can lead to
cursor jumps or accidental clicks. On large touchpads, the palm may also
touch the bottom edges of the touchpad during normal interaction.
Interference from a palm depends on the size of the touchpad and the position
of the user's hand. Data from touchpads showed that almost all palm events
during typing on a Lenovo T440 happened in the left-most and right-most 5% of
the touchpad. The T440 series has one of the largest touchpads, other
touchpads are less affected by palm touches.
libinput has multiple ways of detecting a palm, each of which depends on
hardware-specific capabilities.
- :ref:`palm_tool`
- :ref:`palm_pressure`
- :ref:`palm_touch_size`
- :ref:`palm_exclusion_zones`
- :ref:`trackpoint-disabling`
- :ref:`disable-while-typing`
- :ref:`disable-while-trackpointing`
- :ref:`stylus-touch-arbitration`
Palm detection is always enabled, with the exception of
disable-while-typing.
.. _palm_tool:
------------------------------------------------------------------------------
Palm detection based on firmware labelling
------------------------------------------------------------------------------
Some devices provide palm detection in the firmware, forwarded by the kernel
as the ``EV_ABS/ABS_MT_TOOL`` axis with a value of ``MT_TOOL_PALM``
(whenever a palm is detected). libinput honors that value and switches that
touch to a palm.
.. _palm_pressure:
------------------------------------------------------------------------------
Palm detection based on pressure
------------------------------------------------------------------------------
The simplest form of palm detection labels a touch as palm when the pressure
value goes above a certain threshold. This threshold is usually high enough
that it cannot be triggered by a finger movement. Once a touch is labelled as
palm based on pressure, it will remain so even if the pressure drops below
the threshold again. This ensures that a palm remains a palm even when the
pressure changes as the user is typing.
For some information on how to detect pressure on a touch and debug the
pressure ranges, see :ref:`touchpad_pressure`.
.. _palm_touch_size:
------------------------------------------------------------------------------
Palm detection based on touch size
------------------------------------------------------------------------------
On touchpads that support the ``ABS_MT_TOUCH_MAJOR`` axes, libinput can perform
palm detection based on the size of the touch ellipse. This works similar to
the pressure-based palm detection in that a touch is labelled as palm when
it exceeds the (device-specific) touch size threshold.
For some information on how to detect the size of a touch and debug the
touch size ranges, see :ref:`touchpad_pressure`.
.. _palm_exclusion_zones:
------------------------------------------------------------------------------
Palm exclusion zones
------------------------------------------------------------------------------
libinput enables palm detection on the left, right and top edges of the
touchpad. Two exclusion zones are defined on the left and right edge of the
touchpad. If a touch starts in the exclusion zone, it is considered a palm
and the touch point is ignored. However, for fast cursor movements across
the screen, it is common for a finger to start inside an exclusion zone and
move rapidly across the touchpad. libinput detects such movements and avoids
palm detection on such touch sequences.
Another exclusion zone is defined on the top edge of the touchpad. As with
the edge zones, libinput detects vertical movements out of the edge zone and
avoids palm detection on such touch sequences.
A touch starting in the exclusion zone does not trigger a tap (see
:ref:`tapping`).
In the diagram below, the exclusion zones are painted red.
Touch 'A' starts inside the exclusion zone and moves
almost vertically. It is considered a palm and ignored for cursor movement,
despite moving out of the exclusion zone.
Touch 'B' starts inside the exclusion zone but moves horizontally out of the
zone. It is considered a valid touch and controls the cursor.
Touch 'C' occurs in the exclusion zone. Despite being a tapping motion, it does
not generate an emulated button event.
.. figure:: palm-detection.svg
:align: center
.. _trackpoint-disabling:
------------------------------------------------------------------------------
Palm detection during trackpoint use
------------------------------------------------------------------------------
If a device provides a
`trackpoint <http://en.wikipedia.org/wiki/Pointing_stick>`_, it is
usually located above the touchpad. This increases the likelihood of
accidental touches whenever the trackpoint is used.
libinput disables the touchpad whenever it detects trackpoint activity for a
certain timeout until after trackpoint activity stops. Touches generated
during this timeout will not move the pointer, and touches started during
this timeout will likewise not move the pointer (allowing for a user to rest
the palm on the touchpad while using the trackstick).
If the touchpad is disabled, the :ref:`top software buttons <t440_support>`
remain enabled.
.. _disable-while-typing:
------------------------------------------------------------------------------
Disable-while-typing
------------------------------------------------------------------------------
libinput automatically disables the touchpad for a timeout after a key
press, a feature traditionally referred to as "disable while typing" and
previously available through the
`syndaemon(1) <http://linux.die.net/man/1/syndaemon>`_ command. libinput does
not require an external command and the feature is currently enabled for all
touchpads but will be reduced in the future to only apply to touchpads where
finger width or pressure data is unreliable.
Notable behaviors of libinput's disable-while-typing feature:
- Two different timeouts are used, after a single key press the timeout is
short to ensure responsiveness. After multiple key events, the timeout is
longer to avoid accidental pointer manipulation while typing.
- Some keys do not trigger the timeout, specifically some modifier keys
(Ctrl, Alt, Shift, and Fn). Actions such as Ctrl + click thus stay
responsive.
- Touches started while typing do not control the cursor even after typing
has stopped, it is thus possible to rest the palm on the touchpad while
typing.
- Physical buttons work even while the touchpad is disabled. This includes
:ref:`software-emulated buttons <t440_support>`.
- libinput pairs touchpads and keyboards for the disable-while-typing
feature. In the most common case, the internal touchpad is paired only
with the internal keyboard. Typing on an external keyboard will thus not
disable the touchpad. Some devices require a :ref:`quirk <device-quirks>`
to be correctly paired.
Disable-while-typing can be enabled and disabled by calling
**libinput_device_config_dwt_set_enabled()**.
.. _disable-while-trackpointing:
------------------------------------------------------------------------------
Disable-while-trackpointing
------------------------------------------------------------------------------
libinput automatically disables the touchpad for a timeout after the trackpoint
is moved, a feature referred to as "disable while trackpointing". libinput does
not require an external command and the feature is currently enabled for all
touchpads.
Disable-while-trackpointing can be enabled and disabled by calling
**libinput_device_config_dwtp_set_enabled()**.
.. _stylus-touch-arbitration:
------------------------------------------------------------------------------
Stylus-touch arbitration
------------------------------------------------------------------------------
A special case of palm detection is touch arbitration on devices that
support styli. When interacting with a stylus on the screen, parts of the
hand may touch the surface and trigger touches. As the user is currently
interacting with the stylus, these touches would interfere with the correct
working of the stylus.
libinput employs a method similar to :ref:`disable-while-typing` to detect
these touches and disables the touchpad accordingly.
.. _thumb-detection:
------------------------------------------------------------------------------
Thumb detection
------------------------------------------------------------------------------
Many users rest their thumb on the touchpad while using the index finger to
move the finger around. For clicks, often the thumb is used rather than the
finger. The thumb should otherwise be ignored as a touch, i.e. it should not
count towards :ref:`clickfinger` and it should not cause a single-finger
movement to trigger :ref:`twofinger_scrolling`.
libinput uses two triggers for thumb detection: pressure and
location. A touch exceeding a pressure threshold is considered a thumb if it
is within the thumb detection zone.
.. note:: "Pressure" on touchpads is synonymous with "contact area." A large touch
surface area has a higher pressure and thus hints at a thumb or palm
touching the surface.
Pressure readings are unreliable at the far bottom of the touchpad as a
thumb hanging mostly off the touchpad will have a small surface area.
libinput has a definitive thumb zone where any touch is considered a resting
thumb.
.. figure:: thumb-detection.svg
:align: center
The picture above shows the two detection areas. In the larger (light red)
area, a touch is labelled as thumb when it exceeds a device-specific
pressure threshold. In the lower (dark red) area, a touch is labelled as
thumb if it remains in that area for a time without moving outside.

View file

@ -1,296 +0,0 @@
.. _pointer-acceleration:
==============================================================================
Pointer acceleration
==============================================================================
libinput uses device-specific pointer acceleration methods, with the default
being the :ref:`ptraccel-linear`. The methods share common properties, such as
:ref:`ptraccel-velocity`.
This page explains the high-level concepts used in the code. It aims to
provide an overview for developers and is not necessarily useful for
users.
.. _ptraccel-profiles:
------------------------------------------------------------------------------
Pointer acceleration profiles
------------------------------------------------------------------------------
The profile decides the general method of pointer acceleration.
libinput currently supports three profiles: **"adaptive"**, **"flat"** and
**"custom"**.
- The **adaptive** profile is the default profile for all devices and takes the
current speed of the device into account when deciding on acceleration.
- The **flat** profile is simply a constant factor applied to all device deltas,
regardless of the speed of motion (see :ref:`ptraccel-profile-flat`).
- The **custom** profile allows the user to define a custom acceleration
function, giving full control over accelerations behavior at different speed
(see :ref:`ptraccel-profile-custom`).
Most of this document describes the adaptive pointer acceleration.
.. _ptraccel-velocity:
------------------------------------------------------------------------------
Velocity calculation
------------------------------------------------------------------------------
The device's speed of movement is measured across multiple input events
through so-called "trackers". Each event prepends a tracker item, each
subsequent tracker contains the delta of that item to the current position,
the timestamp of the event that created it and the cardinal direction of the
movement at the time. If a device moves into the same direction, the
velocity is calculated across multiple trackers. For example, if a device
moves steadily for 10 events to the left, the velocity is calculated across
all 10 events.
Whenever the movement changes direction or significantly changes speed, the
velocity is calculated from the direction/speed change only. For example, if
a device moves steadily for 8 events to the left and then 2 events to the
right, the velocity is only that of the last 2 events.
An extra time limit prevents events that are too old to factor into the
velocity calculation. For example, if a device moves steadily for 5 events
to the left, then pauses, then moves again for 5 events to the left, only
the last 5 events are used for velocity calculation.
The velocity is then used to calculate the acceleration factor
.. _ptraccel-factor:
------------------------------------------------------------------------------
Acceleration factor
------------------------------------------------------------------------------
The acceleration factor is the final outcome of the pointer acceleration
calculations. It is a unitless factor that is applied to the current delta,
a factor of 2 doubles the delta (i.e. speeds up the movement), a factor of
less than 1 reduces the delta (i.e. slows the movement).
Any factor less than 1 requires the user to move the device further to move
the visible pointer. This is called deceleration and enables high precision
target selection through subpixel movements. libinput's current maximum
deceleration factor is 0.3 (i.e. slow down to 30% of the pointer speed).
A factor higher than 1 moves the pointer further than the physical device
moves. This is acceleration and allows a user to cross the screen quickly
but effectively skips pixels. libinput's current maximum acceleration factor
is 3.5.
.. _ptraccel-linear:
------------------------------------------------------------------------------
Linear pointer acceleration
------------------------------------------------------------------------------
The linear pointer acceleration method is the default for most pointer
devices. It provides deceleration at very slow movements, a 1:1 mapping for
regular movements and a linear increase to the maximum acceleration factor
for fast movements.
Linear pointer acceleration applies to devices with above 1000dpi resolution
and after :ref:`motion_normalization` is applied.
.. figure:: ptraccel-linear.svg
:align: center
Linear pointer acceleration
The image above shows the linear pointer acceleration settings at various
speeds. The line for 0.0 is the default acceleration curve, speed settings
above 0.0 accelerate sooner, faster and to a higher maximum acceleration.
Speed settings below 0 delay when acceleration kicks in, how soon the
maximum acceleration is reached and the maximum acceleration factor.
Extremely low speed settings provide no acceleration and additionally
decelerate all movement by a constant factor.
.. _ptraccel-low-dpi:
------------------------------------------------------------------------------
Pointer acceleration for low-dpi devices
------------------------------------------------------------------------------
Low-dpi devices are those with a physical resolution of less than 1000 dots
per inch (dpi). The pointer acceleration is adjusted to provide roughly the
same feel for all devices at normal to high speeds. At slow speeds, the
pointer acceleration works on device-units rather than normalized
coordinates (see :ref:`motion_normalization`).
.. figure:: ptraccel-low-dpi.svg
:align: center
Pointer acceleration for low-dpi devices
The image above shows the default pointer acceleration curve for a speed of
0.0 at different DPI settings. A device with low DPI has the acceleration
applied sooner and with a stronger acceleration factor.
.. _ptraccel-touchpad:
------------------------------------------------------------------------------
Pointer acceleration on touchpads
------------------------------------------------------------------------------
Touchpad pointer acceleration uses the same approach as the
:ref:`ptraccel-linear` profile, with a constant deceleration factor applied. The
user expectation of how much a pointer should move in response to finger
movement is different to that of a mouse device, hence the constant
deceleration factor.
.. figure:: ptraccel-touchpad.svg
:align: center
Pointer acceleration curve for touchpads
The image above shows the touchpad acceleration profile in comparison to the
:ref:`ptraccel-linear`. The shape of the curve is identical but vertically squashed.
.. _ptraccel-trackpoint:
------------------------------------------------------------------------------
Pointer acceleration on trackpoints
------------------------------------------------------------------------------
The main difference between trackpoint hardware and mice or touchpads is
that trackpoint speed is a function of pressure rather than moving speed.
But trackpoint hardware is quite varied in how it reacts to user pressure
and unlike other devices it cannot easily be normalized for physical
properties. Measuring pressure objectively across a variety of hardware is
nontrivial. See :ref:`trackpoints` for more details.
The deltas for trackpoints are converted units/ms but there is no common
physical reference point for a unit. Thus, the same pressure on different
trackpoints will generate different speeds and thus different acceleration
behaviors. Additionally, some trackpoints provide the ability to adjust the
sensitivity in hardware by modifying a sysfs file on the serio node. A
higher sensitivity results in higher deltas, thus changing the definition of
what is a unit again.
libinput attempts to normalize unit data to the best of its abilities, see
:ref:`trackpoint_multiplier`. Beyond this, it is not possible to have
consistent behavior across different trackpoint devices.
.. figure:: ptraccel-trackpoint.svg
:align: center
Pointer acceleration curves for trackpoints
The image above shows the trackpoint acceleration profile for the speed in
units/ms.
.. _ptraccel-profile-flat:
------------------------------------------------------------------------------
The flat pointer acceleration profile
------------------------------------------------------------------------------
In a flat profile, the acceleration factor is constant regardless of the
velocity of the pointer and each delta (dx, dy) results in an accelerated delta
(dx * factor, dy * factor). This provides 1:1 movement between the device
and the pointer on-screen.
.. _ptraccel-tablet:
------------------------------------------------------------------------------
Pointer acceleration on tablets
------------------------------------------------------------------------------
Pointer acceleration for relative motion on tablet devices is a flat
acceleration, with the speed setting slowing down or speeding up the pointer
motion by a constant factor. Tablets do not allow for switchable profiles.
.. _ptraccel-profile-custom:
------------------------------------------------------------------------------
The custom acceleration profile
------------------------------------------------------------------------------
libinput supports a user-defined custom acceleration profile, which can be
adjusted for different movement types supported by a device. Movement types
include pointer movement, scrolling, etc. but the set of supported
movement types depends on the device.
The custom pointer acceleration profile gives users full control over the
acceleration behavior at different speeds. libinput exposes
an acceleration function ``f(x)`` where the x axis is the device speed in
device units per millisecond and the y axis is the pointer speed. By
supplying the y axis values for this function, users can control the
behavior of the device.
The user should take into account the native device dpi and screen dpi in
order to achieve the desired behavior/feel.
The custom acceleration function is defined using ``n`` points which are spaced
uniformly along the x axis, starting from 0 and continuing in constant steps.
At least two points must be defined and there is an implementation-defined
limit on how many points may be added.
Thus the points defining the custom function are:
``(0 * step, f[0]), (1 * step, f[1]), ..., ((n-1) * step, f[n-1])``
where ``f`` is a list of ``n`` values defining the output velocity for each
input velocity.
The acceleration factor is defined by the ratio of the output velocity to the
input velocity.
When a velocity value does not lie exactly on those points, a linear
interpolation of the two closest points will be calculated.
When a velocity value is greater than the max point defined, a linear
extrapolation of the two biggest points will be calculated.
the calculation made by libinput: ::
input_delta = device delta units
delta_time = time in ms since last input_delta
input_speed = hypot(input_delta) / delta_time
output_speed = user_custom_function(input_speed)
acceleration_factor = output_speed / input_speed
output_delta = input_delta * acceleration_factor
An example is the curve of ``0.0, 1.0`` with a step of ``1.0``. This curve
is the equivalent of the flat acceleration profile with any input speed ``N``
mapped to the same pointer speed ``N``. The curve ``1.0, 1.0`` neutralizes
any input speed differences and results in a fixed pointer speed.
Another example is the custom acceleration function ``x**2``,
sampling the function at ``4`` points up to
a maximum input speed of ``9`` will give us a custom function with
a step of ``3`` and points ``[0.0, 9.0, 36.0, 81.0]``:
.. figure:: ptraccel-custom.svg
:align: center
More sampled points can be added to improve the accuracy of the user custom
function.
Supported Movement types:
+---------------+---------------------------------+----------------------+
| Movement type | Uses | supported by |
+===============+=================================+======================+
| Fallback | Catch-all default movement type | All devices |
+---------------+---------------------------------+----------------------+
| Motion | Used for pointer motion | All devices |
+---------------+---------------------------------+----------------------+
| Scroll | Used for scroll movement | Mouse, Touchpad |
+---------------+---------------------------------+----------------------+
If a user does not provide the fallback custom acceleration function, a
flat acceleration function is used, i.e. no acceleration.
The fallback acceleration may be used for different types of movements, it is
strongly recommended that this acceleration function is a constant function.
For example, a touchpad has multiple movement types: pointer
movement, scroll movement, zoom movement (pinch), etc. As there is no separate
movement type for zoom yet, zoom movement is accelerated using the Fallback
acceleration function. Pointer movement is accelerated using the Motion
acceleration function, and Scroll movement is accelerated using the Scroll
acceleration function. If no Motion/Scroll acceleration function is set, the
Fallback acceleration function is used.
When using custom acceleration profile, any calls to set the speed have no
effect on the behavior of the custom acceleration function, but any future calls to
get the speed will reflect the requested speed setting.

View file

@ -1,395 +0,0 @@
.. _reporting_bugs:
==============================================================================
Reporting bugs
==============================================================================
A new bug can be filed here:
https://gitlab.freedesktop.org/libinput/libinput/issues/new
.. hint:: libinput has lots of users but very few developers. It is in your
own interest to follow the steps here precisely to ensure your bug can be
dealt with efficiently.
When reporting bugs against libinput, you will need:
- a reliable :ref:`reproducer <reporting_bugs_reproducer>` for the bug
- a :ref:`recording <libinput-record>` of the device while the bug is reproduced
- device-specific information, see
- :ref:`reporting_bugs_touchpad`
- :ref:`reporting_bugs_mouse`
- :ref:`reporting_bugs_keyboard`
- :ref:`reporting_bugs_trackpoint`
- :ref:`reporting_bugs_other`
- the :ref:`libinput version <reporting_bugs_version>` you are on.
- the :ref:`configuration options <reporting_bugs_options>` you have set
- a `gitlab account <https://gitlab.freedesktop.org/users/sign_in>`_
Stay technical, on-topic, and keep the description concise.
.. _reporting_bugs_version:
------------------------------------------------------------------------------
Obtaining the libinput version
------------------------------------------------------------------------------
If your libinput version is older than the current stable branch, please try
the latest version. If you run a distribution-provided
libinput, use the package manager to get the **full** package name and
version of libinput, e.g.
- ``rpm -q libinput``
- ``dpkg -s libinput10``
If you run a self-compiled version of libinput provide the git commit you
have built or the tarball name.
As a last resort, use ``libinput --version``
.. _reporting_bugs_reproducer:
------------------------------------------------------------------------------
Reproducing bugs
------------------------------------------------------------------------------
Try to identify the bug by reproducing it reliably. Bugs without a
reliable reproducer will have lowest priority. The more specific a bug
description and reproducer is, the easier it is to fix.
Try to replicate the series of events that lead to the bug being triggered.
Narrow it down until you have a reliable sequence that can trigger the bug.
For the vast majority of bugs you should not take longer than 5 seconds or
three interactions (clicks, touches, taps, ...) with the device to
reproduce. If it takes longer than that, you can narrow it down further.
Once you can reproduce it, use the :ref:`libinput-debug-events` helper
tool::
$> libinput debug-events --verbose
The output is textual and can help identify whether the bug is in libinput
at all. Note that any configuration options you have set must be specified
on the commandline, see the :ref:`libinput-debug-events`
man page. Use the ``--verbose`` flag to get more information about how
libinput processes events.
If the bug cannot be reproduced with the :ref:`libinput-debug-events` helper,
even with the correct configuration options set, it is likely not a bug in
libinput.
.. _reporting_bugs_options:
------------------------------------------------------------------------------
libinput configuration settings
------------------------------------------------------------------------------
libinput has a number of device-specific default configuration settings that
may differ from the ones your desktop environment picks by default. You may
have changed some options in a settings panel or in an the xorg.conf snippet
yourself.
You must provide these options in the bug report, otherwise a developer
reproducing the issue may not be able to do so.
If you are on X11, the current settings can be can be obtained with
``xinput list-props "your device name"``. Use ``xinput list`` to
obtain the device name.
If you are on Wayland, provide a manual summary of the options you have
changed from the default (e.g. "I enabled tap-to-click").
.. _reporting_bugs_touchpad:
------------------------------------------------------------------------------
Reporting touchpad bugs
------------------------------------------------------------------------------
When you file a bug, please attach the following information:
- a virtual description of your input device, see :ref:`libinput-record`.
This is the most important piece of information, do not forget it!
- the output from udevadm info, see :ref:`udev_info`.
- the vendor model number of your laptop (e.g. "Lenovo Thinkpad T440s")
- and the content of ``/sys/class/dmi/id/modalias``.
- run ``libinput measure touchpad-size`` tool (see :ref:`absolute_coordinate_ranges_fix`)
and verify that the ranges and sizes it prints match the touchpad (up to 5mm
difference is ok)
If you are reporting a bug related to button event generation:
- does your touchpad have (separate) physical hardware buttons or is the
whole touchpad clickable?
- Are you using software buttons or clickfinger? See :ref:`clickpad_softbuttons`.
- Do you have :ref:`tapping` enabled?
.. _reporting_bugs_mouse:
------------------------------------------------------------------------------
Reporting mouse bugs
------------------------------------------------------------------------------
When you file a bug, please attach the following information:
- a virtual description of your input device, see :ref:`libinput-record`.
This is the most important piece of information, do not forget it!
- the vendor model number of the device (e.g. "Logitech M325")
- the output from udevadm info, see :ref:`udev_info`.
If the bug is related to the :ref:`speed of the mouse <motion_normalization_customization>`:
- the resolution of the mouse as specified by the vendor (in DPI)
- the output of the ``mouse-dpi-tool`` (provided by libevdev)
.. _reporting_bugs_keyboard:
------------------------------------------------------------------------------
Reporting keyboard bugs
------------------------------------------------------------------------------
Is your bug related to a keyboard layout? libinput does not handle keyboard
layouts and merely forwards the physical key events. File the bug with your
desktop environment instead (e.g. GNOME, KDE, ...), that's most likely where
the issue is.
When you file a bug, please attach the following information:
- a virtual description of your input device, see :ref:`libinput-record`.
This is the most important piece of information, do not forget it!
.. _reporting_bugs_trackpoint:
------------------------------------------------------------------------------
Reporting trackpoint bugs
------------------------------------------------------------------------------
When you file a bug, please attach the following information:
- a virtual description of your input device, see :ref:`libinput-record`.
This is the most important piece of information, do not forget it!
- the vendor model number of the device (e.g. "Logitech M325")
- the output from udevadm info, see :ref:`udev_info`.
- the sensitivity of the trackpoint if it exists (adjust the event node number as needed): ::
$ cat /sys/class/input/event17/device/device/sensitivity
.. _reporting_bugs_other:
------------------------------------------------------------------------------
All other devices
------------------------------------------------------------------------------
When you file a bug, please attach the following information:
- a virtual description of your input device, see :ref:`libinput-record`.
This is the most important piece of information, do not forget it!
- the vendor model number of the device (e.g. "Sony PlayStation3 controller")
.. _udev_info:
------------------------------------------------------------------------------
udev information for the device
------------------------------------------------------------------------------
In many cases, we require the udev properties assigned to the device to
verify whether device-specific quirks were applied. This can be obtained
with ``udevadm info /sys/class/input/eventX``, with the correct event
node for your device. An example output is below: ::
$ udevadm info /sys/class/input/event4
P: /devices/platform/i8042/serio1/input/input5/event4
N: input/event4
E: DEVNAME=/dev/input/event4
E: DEVPATH=/devices/platform/i8042/serio1/input/input5/event4
E: EVDEV_ABS_00=::41
E: EVDEV_ABS_01=::37
E: EVDEV_ABS_35=::41
E: EVDEV_ABS_36=::37
E: ID_INPUT=1
E: ID_INPUT_HEIGHT_MM=66
E: ID_INPUT_TOUCHPAD=1
E: ID_INPUT_WIDTH_MM=97
E: MAJOR=13
E: MINOR=68
E: SUBSYSTEM=input
E: USEC_INITIALIZED=5463031
.. _evemu:
------------------------------------------------------------------------------
Recording devices with evemu
------------------------------------------------------------------------------
.. warning:: Where available, the :ref:`libinput-record` tools should be used instead
of evemu
`evemu-record <https://www.freedesktop.org/wiki/Evemu/>`_ records the
device capabilities together with the event stream from the kernel. On our
side, this allows us to recreate a virtual device identical to your device
and re-play the event sequence, hopefully triggering the same bug.
evemu-record takes a ``/dev/input/eventX`` event node, but without arguments
it will simply show the list of devices and let you select: ::
$ sudo evemu-record > scroll.evemu
Available devices:
/dev/input/event0: Lid Switch
/dev/input/event1: Sleep Button
/dev/input/event2: Power Button
/dev/input/event3: AT Translated Set 2 keyboard
/dev/input/event4: SynPS/2 Synaptics TouchPad
/dev/input/event5: Video Bus
/dev/input/event6: ELAN Touchscreen
/dev/input/event10: ThinkPad Extra Buttons
/dev/input/event11: HDA Intel HDMI HDMI/DP,pcm=3
/dev/input/event12: HDA Intel HDMI HDMI/DP,pcm=7
/dev/input/event13: HDA Intel HDMI HDMI/DP,pcm=8
/dev/input/event14: HDA Intel PCH Dock Mic
/dev/input/event15: HDA Intel PCH Mic
/dev/input/event16: HDA Intel PCH Dock Headphone
/dev/input/event17: HDA Intel PCH Headphone
/dev/input/event18: Integrated Camera
/dev/input/event19: TPPS/2 IBM TrackPoint
Select the device event number [0-19]:
Select the device that triggers the issue, then reproduce the bug and Ctrl+C
the process. The resulting recording, ("scroll.evemu" in this example) will
contain the sequence required to reproduce the bug. If the bug fails to
reproduce during recording, simply Ctrl+C and restart evemu-record.
Always start the recording from a neutral state, i.e. without any buttons or
keys down, with the position of the device in the neutral position, without
touching the screen/touchpad.
.. note:: The longer the recording, the harder it is to identify the event
sequence triggering the bug. Please keep the event sequence as short
as possible.
To verify that the recording contains the bug, you can replay it on your
device. For example, to replay the sequence recorded in the example above: ::
$ sudo evemu-play /dev/input/event4 < scroll.evemu
If the bug is triggered by replaying on your device, attach the recording to
the bug report.
libinput does not affect the evemu recording. libinput and evemu talk
directly to the kernel's device nodes. An evemu recording is not
influenced by the libinput version or whether a libinput context is
currently active.
.. graphviz:: evemu.gv
.. _fixed_bugs:
------------------------------------------------------------------------------
My bug was closed as fixed, what now?
------------------------------------------------------------------------------
libinput's policy on closing bugs is: once the fix for a given bug is on git
master, the bug is considered fixed and the gitlab issue will be closed
accordingly.
Of course, unless you actually run git master, the bug will continue to
affect you on your local machine. You are most likely running the
distribution's package and you will need to wait until the distribution has
updated its package accordingly.
.. warning:: Do not re-open a bug just because it hasn't trickled down to
your distribution's package version yet.
Whether the bug fix ends up in your distribution depends on a number of
things. Any given bug fix **may** be cherry-picked into the current stable
branch, depending on its severity, impact, and likelihood to cause
regressions. Once cherry-picked it will land in the next stable branch
release. These are usually a few weeks apart.
.. warning:: Do not re-open a bug because it wasn't picked into a stable branch
release or because your distribution didn't update to the latest stable
branch release.
Stable branches are usually discontinued when the next release comes out.
Your distribution may pick a patch up immediately and ship the fix
even before the next stable branch update is released. For example, Fedora
does this frequently.
.. hint:: If a bug needs to be fixed urgently, file a bug in your
distribution's bug tracker.
Patches on git master will end up in the next libinput release. Once your
distribution updates to that release, your local libinput version will
contain the fix.
.. warning:: Do not re-open a bug because your distribution didn't update to
the release.
You can always run libinput from git master (see :ref:`building_libinput`).
Even while in development, libinput is very stable so this option isn't as
scary as it may sounds.
.. _reporting_bugs_reopen:
..............................................................................
When is it ok to re-open a fixed bug?
..............................................................................
Any time the bug was considered fixed but it turns out that the fix is
insufficient and/or causes a regression.
However, if the regression is in behavior unrelated to the fix itself it is
usually better to file a new bug to reduce the noise. For example, if a fix
to improve tapping breaks two-finger scrolling behavior, you should file a
new bug but reference the original bug.
.. _reporting_bugs_tags:
------------------------------------------------------------------------------
Gitlab issue tracker tags
------------------------------------------------------------------------------
The gitlab issue tracker allows developers to add tags to bugs to classify
them.
- **being worked on**: someone is currently working on this feature. This
tag is used for features that will take a long time to implement fully and
prevents others from having to duplicate the work. Do reach out and ask if
help and/or further testing is needed.
- **bug**: issue is confirmed to be a bug
- **cantfix**: for technical reasons, this bug cannot be fixed, or at least
it cannot be fixed in libinput.
- **enhancement**: this issue describes a future feature, not a bug.
- **help needed**: this issue requires someone outside the libinput core
developer team to implement it. It is unlikely to be implemented
without someone stepping up to do the work. If you do see this tag, do ask
for guidance on how to implement it.
- **hw issue**: an issue that affects a specific device and is a hardware
bug, not a software bug. Often these need to be worked around in libinput
but there are cases where a hw issue ends up as *cantfix*.
- **janitor**: a cleanup task that does not substantially affect how
libinput works. These are usually good bugs for newcomers to start on.
- **kernel**: this issue is a kernel bug, not a libinput bug. Often closed
as *cantfix* of *wontfix* as we wait for the kernel to address the issue
instead.
- **needs triage**: bug has not yet been confirmed by a core developer.
- **not our bug**: the issue is in some other component of the stack and
needs to be addressed there.
- **please test**: a fix is available but not yet merged and should be
tested by the reporter or others affected by the issue.
- **quirk**: this is issue needs :ref:`device-quirks` to be fixed
- **regression**: the issue is a regression to previous versions of
libinput. These issues get priorities.
- **waiting on reporter**: some more information is required from the
reporter and the issue cannot be fixed until the issue has been provided.
Where a bug is left in this state for too long, the bug will be closed as
*cantfix*.
- **wontfix**: this issue will not get fixed. This tag is usually assigned
to feature requests that are outside the scope of libinput or would put an
unreasonable maintenance burden on the maintainers.
These tags are high-level categories only, always look for the comments in
the issue to get further details.

View file

@ -1,178 +0,0 @@
.. _scrolling:
==============================================================================
Scrolling
==============================================================================
libinput supports three different types of scrolling methods:
:ref:`twofinger_scrolling`, :ref:`edge_scrolling` and
:ref:`button_scrolling`. Some devices support multiple methods, though only
one can be enabled at a time. As a general overview:
- touchpad devices with physical buttons below the touchpad support edge and
two-finger scrolling
- touchpad devices without physical buttons (:ref:`ClickPads <clickpad_softbuttons>`)
support two-finger scrolling only
- pointing sticks provide on-button scrolling by default
- mice and other pointing devices support on-button scrolling but it is not
enabled by default
A device may differ from the above based on its capabilities. See
**libinput_device_config_scroll_set_method()** for documentation on how to
switch methods and **libinput_device_config_scroll_get_methods()** for
documentation on how to query a device for available scroll methods.
.. _horizontal_scrolling:
------------------------------------------------------------------------------
Horizontal scrolling
------------------------------------------------------------------------------
Scroll movements provide vertical and horizontal directions, each
scroll event contains both directions where applicable, see
**libinput_event_pointer_get_axis_value()**. libinput does not provide separate
toggles to enable or disable horizontal scrolling. Instead, horizontal
scrolling is always enabled. This is intentional, libinput does not have
enough context to know when horizontal scrolling is appropriate for a given
widget. The task of filtering horizontal movements is up to the caller.
.. _twofinger_scrolling:
------------------------------------------------------------------------------
Two-finger scrolling
------------------------------------------------------------------------------
The default on two-finger capable touchpads (almost all modern touchpads are
capable of detecting two fingers). Scrolling is triggered by two fingers
being placed on the surface of the touchpad, then moving those fingers
vertically or horizontally.
.. figure:: twofinger-scrolling.svg
:align: center
Vertical and horizontal two-finger scrolling
For scrolling to trigger, a built-in distance threshold has to be met, but once
engaged, any movement will scroll. In other words: to start scrolling, a
sufficiently large movement is required; once scrolling, tiny amounts of
movements will translate into tiny scroll movements.
Scrolling in both directions at once is possible by meeting the required
distance thresholds to enable each direction separately.
When a scroll gesture remains close to perfectly straight, it will be held to
exact 90-degree angles; but if the gesture moves diagonally, it is free to
scroll in any direction.
Two-finger scrolling requires the touchpad to track both touch points with
reasonable precision. Unfortunately, some so-called "semi-mt" touchpads can
only track the bounding box of the two fingers rather than the actual
position of each finger. In addition, that bounding box usually suffers from
a low resolution, causing jumpy movement during two-finger scrolling.
libinput does not provide two-finger scrolling on those touchpads.
.. _edge_scrolling:
------------------------------------------------------------------------------
Edge scrolling
------------------------------------------------------------------------------
On some touchpads, edge scrolling is available, triggered by moving a single
finger along the right edge (vertical scroll) or bottom edge (horizontal
scroll).
.. figure:: edge-scrolling.svg
:align: center
Vertical and horizontal edge scrolling
Due to the layout of the edges, diagonal scrolling is not possible. The
behavior of edge scrolling using both edges at the same time is undefined.
Edge scrolling overlaps with :ref:`clickpad_softbuttons`. A physical click on
a clickpad ends scrolling.
.. _button_scrolling:
------------------------------------------------------------------------------
On-Button scrolling
------------------------------------------------------------------------------
On-button scrolling converts the motion of a device into scroll events while
a designated button is held down. For example, Lenovo devices provide a
`pointing stick <http://en.wikipedia.org/wiki/Pointing_stick>`_ that emulates
scroll events when the trackstick's middle mouse button is held down.
.. note:: On-button scrolling is enabled by default for pointing sticks. This
prevents middle-button dragging; all motion events while the middle
button is down are converted to scroll events.
.. figure:: button-scrolling.svg
:align: center
Button scrolling
The button may be changed with
**libinput_device_config_scroll_set_button()** but must be on the same device as
the motion events. Cross-device scrolling is not supported but
for one exception: libinput's :ref:`t440_support` enables the use of the middle
button for button scrolling (even when the touchpad is disabled).
If the scroll button lock is enabled (see
**libinput_device_config_scroll_set_button_lock()**), the button does not
need to be held down. Pressing and releasing the button once enables the
button lock, the button is now considered logically held down. Pressing and
releasing the button a second time logically releases the button. While the
button is logically held down, motion events are converted to scroll events.
If the button is held and used to scroll for longer than a short grace
period, releasing the button does not engage the lock. This allows
hold-to-scroll for short, precise adjustments without accidentally toggling
the lock. A quick click or a brief scroll within the grace period still
engages the lock as normal.
.. _scroll_sources:
------------------------------------------------------------------------------
Scroll sources
------------------------------------------------------------------------------
.. note:: Scroll sources are deprecated with libinput 1.19. The scroll
source is now encoded in the event type.
libinput provides a pointer axis *source* for each scroll event. The
source can be obtained with the **libinput_event_pointer_get_axis_source()**
function and is one of **wheel**, **finger**, or **continuous**. The source
information lets a caller decide when to implement kinetic scrolling.
Usually, a caller will process events of source wheel as they come in.
For events of source finger a caller should calculate the velocity of the
scroll motion and upon finger release start a kinetic scrolling motion (i.e.
continue executing a scroll according to some friction factor).
libinput expects the caller to be in charge of widget handling, the source
information is thus enough to provide kinetic scrolling on a per-widget
basis. A caller should cancel kinetic scrolling when the pointer leaves the
current widget or when a key is pressed.
See the **libinput_event_pointer_get_axis_source()** for details on the
behavior of each scroll source.
See also http://who-t.blogspot.com.au/2015/03/libinput-scroll-sources.html
.. _natural_scrolling:
------------------------------------------------------------------------------
Natural scrolling vs. traditional scrolling
------------------------------------------------------------------------------
Natural scrolling is the term (probably) coined by Apple for matching
the motion of the scroll device with the direction of the **content**.
In traditional scrolling, moving the wheel down causes the scroll bar
indicators to move down and the content to move up. In natural scrolling,
moving the wheel down causes the content to move down and the scroll bar
indicators to move up. This method of scrolling matches the interaction
with content on touch screens where a movement down also moves the content
down.
libinput supports natural scrolling for all its scroll methods; it can
be enabled with the
**libinput_device_config_scroll_set_natural_scroll_enabled()** function.

View file

@ -1,85 +0,0 @@
.. _seats:
==============================================================================
Seats
==============================================================================
Each device in libinput is assigned to one seat.
A seat has two identifiers, the physical name and the logical name. The
physical name is summarized as the list of devices a process on the same
physical seat has access to. The logical seat name is the seat name for a
logical group of devices. A compositor may use that to create additional
seats as independent device sets. Alternatively, a compositor may limit
itself to a single logical seat, leaving a second compositor to manage
devices on the other logical seats.
.. _seats_overview:
------------------------------------------------------------------------------
Overview
------------------------------------------------------------------------------
Below is an illustration of how physical seats and logical seats interact:
.. graphviz:: seats-sketch.gv
The devices "Foo", "Bar" and "Spam" share the same physical seat and are
thus available in the same libinput context. Only "Foo" and "Bar" share the
same logical seat. The device "Egg" is not available in the libinput context
associated with the physical seat 0.
The above graph is for illustration purposes only. In libinput, a struct
**libinput_seat** comprises both physical seat and logical seat. From a
caller's point-of-view the above device layout is presented as:
.. graphviz:: seats-sketch-libinput.gv
Thus, devices "Foo" and "Bar" both reference the same struct
**libinput_seat**, all other devices reference their own respective seats.
.. _seats_and_features:
------------------------------------------------------------------------------
The effect of seat assignment
------------------------------------------------------------------------------
A logical set is interpreted as a group of devices that usually belong to a
single user that interacts with a computer. Thus, the devices are
semantically related. This means for devices within the same logical seat:
- if the same button is pressed on different devices, the button should only
be considered logically pressed once.
- if the same button is released on one device, the button should be
considered logically down if still down on another device.
- if two different buttons or keys are pressed on different devices, the
logical state is that of both buttons/keys down.
- if a button is pressed on one device and another device moves, this should
count as dragging.
- if two touches are down on different devices, the logical state is that of
two touches down.
libinput provides functions to aid with the above:
**libinput_event_pointer_get_seat_button_count()**,
**libinput_event_keyboard_get_seat_key_count()**, and
**libinput_event_touch_get_seat_slot()**.
Internally, libinput counts devices within the same logical seat as related.
Cross-device features only activate if all required devices are in the same
logical seat. For example, libinput will only activate the top software
buttons (see :ref:`t440_support`) if both trackstick and touchpad are assigned
to the same logical seat.
.. _changing_seats:
------------------------------------------------------------------------------
Changing seats
------------------------------------------------------------------------------
A device may change the logical seat it is assigned to at runtime with
**libinput_device_set_seat_logical_name()**. The physical seat is immutable and
may not be changed.
Changing the logical seat for a device is equivalent to unplugging the
device and plugging it back in with the new logical seat. No device state
carries over across a logical seat change.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 105 KiB

View file

@ -1,292 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="393"
height="438.42642"
id="svg2">
<defs
id="defs4">
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart-4"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707-7"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend-2"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710-3"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
</defs>
<g
transform="translate(-34.54288,-505.04248)"
id="layer3"
style="display:inline">
<rect
width="58"
height="58"
x="386"
y="65.208893"
transform="translate(-343.95712,527.33359)"
id="rect11748"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="122.04288"
y="592.54248"
id="rect11778"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="202.04288"
y="592.54248"
id="rect11780"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="282.04288"
y="592.54248"
id="rect11782"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="362.04288"
y="592.54248"
id="rect11784"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="82.042877"
y="512.54248"
id="rect11786"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="162.04288"
y="512.54248"
id="rect11788"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="242.04288"
y="512.54248"
id="rect11790"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="58"
height="58"
x="322.04288"
y="512.54248"
id="rect11792"
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<text
x="57.815342"
y="633.93506"
id="text11796"
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
x="57.815342"
y="633.93506"
id="tspan11798"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">C</tspan></text>
<text
x="137.81534"
y="633.93506"
id="text11800"
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
x="137.81534"
y="633.93506"
id="tspan11802"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">V</tspan></text>
<text
x="217.81534"
y="633.93506"
id="text11804"
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
x="217.81534"
y="633.93506"
id="tspan11806"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">B</tspan></text>
<text
x="297.81534"
y="633.93506"
id="text11808"
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
x="297.81534"
y="633.93506"
id="tspan11810"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">N</tspan></text>
<text
x="377.81534"
y="633.93506"
id="text11812"
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
x="377.81534"
y="633.93506"
id="tspan11814"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">M</tspan></text>
<text
x="337.81534"
y="553.93506"
id="text11816"
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
x="337.81534"
y="553.93506"
id="tspan11818"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">J</tspan></text>
<text
x="257.81534"
y="553.93506"
id="text11820"
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
x="257.81534"
y="553.93506"
id="tspan11822"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">H</tspan></text>
<text
x="177.81534"
y="553.93506"
id="text11824"
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
x="177.81534"
y="553.93506"
id="tspan11826"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">G</tspan></text>
<text
x="97.815338"
y="553.93506"
id="text11828"
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Utopia;-inkscape-font-specification:Utopia"><tspan
x="97.815338"
y="553.93506"
id="tspan11830"
style="font-size:36px;fill:#ffffff;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">F</tspan></text>
<path
d="m 590.15063,48.238251 a 12.628045,12.628045 0 1 1 -25.25609,0 12.628045,12.628045 0 1 1 25.25609,0 z"
transform="matrix(1.5,0,0,1.5,-635.24402,504.05633)"
id="path11832"
style="color:#000000;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:#e50000;stroke-width:2.33333325;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<g
transform="matrix(0.98314313,0.18283763,-0.18283763,0.98314313,38.34468,-177.34601)"
id="g3663-9-5">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path2820-6-6"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path2824-1-1"
style="color:#000000;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-1-4"
style="opacity:0.92000002;color:#000000;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</g>
<g
transform="translate(-34.54288,-505.04248)"
id="layer1"
style="display:inline">
<rect
width="135"
height="63"
x="55.166245"
y="782.95221"
id="rect11656"
style="color:#000000;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="135"
height="63"
x="258.97995"
y="782.95221"
id="rect11656-7"
style="color:#000000;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="64.642883"
height="68.303299"
x="192.25995"
y="778.95221"
id="rect11656-1"
style="color:#000000;fill:#333333;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.99999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<g
transform="matrix(-0.96540637,0.18283763,0.17953908,0.98314313,431.3343,62.30631)"
id="g3663-9">
<path
d="m 359.78441,828.85231 -28.35583,-65.06155 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 47.45034,95.30837 47.45034,95.30837 l -63.38022,25.11033 z"
id="path2820-6"
style="fill:none;stroke:#000000;stroke-width:1.00914431px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
d="m 360.32021,827.78041 c 0,0 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 49.57778,98.62829 49.57778,98.62829 0,0 -61.86167,24.42554 -61.86167,24.42554 z"
id="path2824-1"
style="color:#000000;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.00201829;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-1"
style="opacity:0.92000002;color:#000000;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
<path
d="m 232.30267,551.36809 0,46.72376"
id="path11849"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Lstart-4);marker-end:url(#Arrow1Lend-2)" />
<path
d="m 255.66455,574.72997 -46.72376,0"
id="path12611"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Lstart-4);marker-end:url(#Arrow1Lend-2)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 17 KiB

View file

@ -1,106 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="89.829216mm"
height="59.06765mm"
viewBox="0 0 318.2925 209.29482"
id="svg4184"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="clickfinger-distance.svg">
<defs
id="defs4186" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="235.68795"
inkscape:cy="163.39995"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1136"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata4189">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-257.99662,-299.41313)">
<rect
width="313.09872"
height="167.89594"
x="260.59351"
y="302.01001"
id="rect2858-0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.19376326;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" />
<rect
style="opacity:0.92000002;fill:#7b0000;fill-opacity:0.2983426;stroke:#000000;stroke-width:1.00100005;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4788"
width="188.57143"
height="79.285713"
x="355"
y="309.50507" />
<g
transform="matrix(0.79657897,0.11742288,-0.14814182,0.631399,276.6631,-158.96703)"
id="g3663-9-5">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path2820-6-6"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path2824-1-1"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-1-4"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.00100005;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path2824-1-1-3"
d="m 353.70196,495.15765 c -24.01774,-7.29937 -29.0012,-10.10221 -30.51977,-10.54973 -10.67294,-3.14527 -18.27051,-5.54063 -23.77758,-13.4704 -5.50707,-7.92977 -5.34967,-20.78347 8.87612,-26.31604 14.2258,-5.53257 39.34351,8.79597 60.13061,16.16341 20.7871,7.36744 33.04563,11.44545 39.33422,13.87551 -8.10022,18.05041 -7.22129,21.15857 -10.11054,33.34117 -0.0481,0.20261 -17.87459,-5.12433 -43.93306,-13.04392 z"
sodipodi:nodetypes="sszzzcss" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
id="path2824-7-1-4-3"
d="m 324.44991,483.39364 c -10.67294,-1.94747 -17.88441,-5.64478 -21.62691,-8.75386 -8.11652,-9.03765 -6.31775,-15.03428 -3.3272,-13.99784 8.90495,-0.9097 30.20384,9.01528 33.86042,10.17935 -5.80268,11.37909 -1.08919,13.70271 -8.90631,12.57235 z"
sodipodi:nodetypes="ccccc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.9 KiB

View file

@ -1,207 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="1084.4291"
height="218.18733"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="clickfinger.svg">
<metadata
id="metadata4314">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1136"
id="namedview4312"
showgrid="false"
inkscape:zoom="0.81739538"
inkscape:cx="347.81182"
inkscape:cy="125.36322"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-page="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0">
<sodipodi:guide
position="563.61797,339.58481"
orientation="0,1"
id="guide4473" />
</sodipodi:namedview>
<defs
id="defs4" />
<g
id="g4405"
transform="matrix(0.81023703,0,0,0.6422249,-2.3181067e-6,0.47063383)">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:7.20000076;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect2858-0"
y="3.6000037"
x="3.6000032"
height="261.42856"
width="386.42856" />
<g
id="g3663-9-5"
transform="matrix(0.98314313,0.18283763,-0.18283763,0.98314313,-4.7772181,-625.20496)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path2820-6-6"
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path2824-1-1"
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
id="path2824-7-1-4"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
</g>
<g
id="g4394"
transform="matrix(0.81023703,0,0,0.6422249,-2.3181067e-6,0.47063383)">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:7.20000076;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect2858"
y="3.6000218"
x="475.52567"
height="261.42856"
width="386.42856" />
<g
id="g3663"
transform="matrix(0.98196551,0.12493315,-0.14261338,1.1209308,508.26203,-717.12108)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:0.94553083px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path2820"
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.00189106;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path2824"
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
id="path2824-7"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
<g
id="g3663-9"
transform="matrix(0.98314313,0.18283763,-0.18283763,0.98314313,458.58466,-619.69966)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path2820-6"
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path2824-1"
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
id="path2824-7-1"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
</g>
<rect
width="313.09872"
height="167.89594"
x="768.41345"
y="2.3120098"
id="rect2858-7"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.19376326;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" />
<g
transform="matrix(0.79562482,0.08023518,-0.11555064,0.71988967,731.4982,-458.98163)"
id="g3663-3">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path2820-0"
style="fill:none;stroke:#000000;stroke-width:0.94553083px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path2824-9"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.00189106;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-8"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(0.79657897,0.11742288,-0.14814182,0.631399,697.24775,-392.41517)"
id="g3663-9-8">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path2820-6-7"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path2824-1-6"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-1-7"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(0.79562482,0.08023518,-0.11555064,0.71988967,793.15605,-458.77793)"
id="g3663-3-0">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path2820-0-5"
style="fill:none;stroke:#000000;stroke-width:0.94553083px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path2824-9-3"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.00189106;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-8-8"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,126 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="865.5542"
height="341.20889"
id="svg2">
<defs
id="defs4">
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart-4"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707-7"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend-2"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710-3"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
</defs>
<g
transform="translate(343.95712,-527.33359)"
id="layer3"
style="display:inline">
<rect
width="386.42856"
height="261.42856"
x="-340.35712"
y="530.93359"
id="rect2858-0"
style="color:#000000;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:7.20000076;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 21.360531,774.95272 0,-196.97975"
id="path8036"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend)" />
<g
transform="matrix(0.98314313,0.18283763,-0.18283763,0.98314313,-175.46474,-125.7572)"
id="g3663-9-5">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path2820-6-6"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path2824-1-1"
style="color:#000000;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-1-4"
style="opacity:0.92000002;color:#000000;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</g>
<g
transform="translate(343.95712,-527.33359)"
id="layer1"
style="display:inline">
<rect
width="386.42856"
height="261.42856"
x="131.56854"
y="530.93359"
id="rect2858"
style="color:#000000;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:7.20000076;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 455.56456,762.87232 c 0,0 -235.01231,1.01015 -235.01231,1.01015"
id="path3702"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Lstart-4);marker-end:url(#Arrow1Lend-2)" />
<g
transform="matrix(0.98314313,0.18283763,-0.18283763,0.98314313,132.81621,-12.620089)"
id="g3663-9">
<path
d="m 359.78441,828.85231 -28.35583,-65.06155 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 47.45034,95.30837 47.45034,95.30837 l -63.38022,25.11033 z"
id="path2820-6"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
d="m 360.32021,827.78041 c 0,0 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 49.57778,98.62829 49.57778,98.62829 0,0 -61.86167,24.42554 -61.86167,24.42554 z"
id="path2824-1"
style="color:#000000;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path2824-7-1"
style="opacity:0.92000002;color:#000000;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7 KiB

View file

@ -1,496 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210.95885mm"
height="65.170769mm"
viewBox="0 0 747.49199 230.92005"
id="svg4313"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="gesture-2fg-ambiguity.svg">
<defs
id="defs4315">
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4506"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4494"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Mstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4491"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(0.4,0,0,0.4,4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker7694"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path7696"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="marker7562"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path7564"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4995"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(1.1,0,0,1.1,1.1,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker7356"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Mstart">
<path
transform="matrix(0.4,0,0,0.4,4,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
id="path7358"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Send"
orient="auto"
refY="0"
refX="0"
id="Arrow1Send"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4992"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Sstart"
orient="auto"
refY="0"
refX="0"
id="Arrow2Sstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5007"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(0.3,0,0,0.3,-0.69,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker6499"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lstart">
<path
transform="matrix(1.1,0,0,1.1,1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path6501"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="marker5837"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5839"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker5365"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend">
<path
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path5367"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="marker5291"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5293"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4980"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4977"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.8735525"
inkscape:cx="423.84385"
inkscape:cy="193.39486"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1136"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata4318">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-1.110285,-124.21518)">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.76355982;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect2858-0"
y="127.09696"
x="3.992065"
height="209.27208"
width="309.33386" />
<g
id="g4897"
transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,-136.33634,-258.03322)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4899"
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path4901"
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
id="path4903"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
<g
transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,-167.25783,-222.59332)"
id="g5039">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path5041"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path5043"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path5045"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
id="g7665"
transform="matrix(0.98639446,-0.16439576,0.16439576,0.98639446,-43.838837,-4.728819)">
<path
sodipodi:nodetypes="sszzzcss"
d="m 136.26948,381.29633 c -24.01774,-7.29937 -29.0012,-10.10221 -30.51977,-10.54973 -10.672936,-3.14527 -18.270506,-5.54063 -23.777576,-13.4704 -5.50707,-7.92977 -5.34967,-20.78347 8.87612,-26.31603 14.225796,-5.53258 39.343506,8.79596 60.130606,16.16341 20.7871,7.36743 33.04562,11.44544 39.33421,13.8755 -8.10021,18.05041 -7.22128,21.15857 -10.11054,33.34117 -0.0481,0.20261 -17.87458,-5.12433 -43.93305,-13.04392 z"
id="path2824-1-1-3"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.00100005;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
sodipodi:nodetypes="ccccc"
d="m 107.01743,369.53232 c -10.672936,-1.94747 -17.884406,-5.64477 -21.626906,-8.75386 -8.11652,-9.03765 -6.31775,-15.03428 -3.3272,-13.99784 8.90495,-0.9097 30.203836,9.01528 33.860416,10.17935 -5.80268,11.37909 -1.08919,13.70271 -8.90631,12.57235 z"
id="path2824-7-1-4-3"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
id="g9940"
transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,-167.25783,-222.59332)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path9942"
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path9944"
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
id="path9946"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
<rect
width="309.33386"
height="209.27208"
x="423.99207"
y="127.09696"
id="rect9948"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.76355982;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" />
<g
transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,283.66366,-258.03322)"
id="g9950">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path9952"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path9954"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path9956"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
id="g9958"
transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,252.74217,-222.59332)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path9960"
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path9962"
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
id="path9964"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
<g
transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,252.74217,-222.59332)"
id="g9972">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path9974"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path9976"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path9978"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
id="g9980"
transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,342.56693,-256.56067)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path9982"
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path9984"
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
id="path9986"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 146.82277,201.01077 419.52386,-1.0675"
id="path9992"
inkscape:connector-curvature="0" />
<rect
y="188.0482"
x="330.96494"
height="22.417305"
width="77.393082"
id="rect10086"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.97799999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.78531075" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="336.83612"
y="205.01161"
id="text10074"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan10076"
x="336.83612"
y="205.01161">Touch 1</tspan></text>
<path
inkscape:connector-curvature="0"
id="path10082"
d="m 180.82277,169.01077 419.52386,-1.0675"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<rect
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.97799999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.78531075"
id="rect10084"
width="77.393082"
height="22.417305"
x="330.96494"
y="156.0482" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.5px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="335.76865"
y="173.66182"
id="text10078"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan10080"
x="335.76865"
y="173.66182">Touch 2</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 28 KiB

View file

@ -1,201 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="393.62857"
height="268.62857"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="palm-detection.svg">
<metadata
id="metadata3479">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1016"
id="namedview3477"
showgrid="false"
inkscape:zoom="3.5662625"
inkscape:cx="180.54059"
inkscape:cy="269.48563"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
inkscape:document-rotation="0" />
<defs
id="defs4">
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0.0"
refX="0.0"
id="marker4663"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4407"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.8) translate(12.5,0)" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lstart-4"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(0.8,0,0,0.8,10,0)"
id="path3707-7"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
<marker
refX="0"
refY="0"
orient="auto"
id="Arrow1Lend-2"
style="overflow:visible">
<path
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
id="path3710-3"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" />
</marker>
</defs>
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:7.20000076;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect2858-0"
y="3.6000037"
x="3.6000032"
height="261.42856"
width="386.42856" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:3.5;marker:none;enable-background:accumulate"
id="rect12924"
y="7.1355872"
x="7.0710421"
height="254.3844"
width="65.281105" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:3.5;marker:none;enable-background:accumulate"
id="rect13482"
y="6.8830237"
x="321.22849"
height="254.3844"
width="65.281105" />
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:6, 1;stroke-dashoffset:0;stroke-opacity:1;marker-mid:none;marker-end:url(#Arrow1Lend-2)"
id="path13492"
d="m 38.928571,67.914286 c 0,0 3.508205,24.810617 9.642857,57.857144 6.134651,33.04652 23.277202,79.68584 89.642852,90.35714" />
<text
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:0%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"
id="text13874"
y="63.628628"
x="33.214291"><tspan
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
id="tspan13876"
y="63.628628"
x="33.214291">A</tspan></text>
<text
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:0%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"
id="text13874-8"
y="98.748993"
x="351.85422"><tspan
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
id="tspan13876-7"
y="98.748993"
x="351.85422">B</tspan></text>
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Lend-2)"
id="path13903"
d="m 347.5,90.414286 c 0,0 -28.20972,-6.408104 -85,-6.071429 -22.06971,0.130838 -66.07143,4.285715 -66.07143,4.285715" />
<g
transform="translate(343.95712,-527.33359)"
id="layer1"
style="display:inline" />
<text
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:0%;font-family:Utopia;-inkscape-font-specification:Utopia;text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"
id="text13874-8-1"
y="46.009491"
x="342.27759"><tspan
style="font-size:18px;font-family:Arial;-inkscape-font-specification:Arial"
id="tspan13876-7-9"
y="46.009491"
x="342.27759">C</tspan></text>
<circle
style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1"
id="path4401"
cx="-360.181"
cy="24.53549"
r="4.0658817"
transform="scale(-1,1)" />
<rect
width="248.87633"
height="6.8111157"
x="72.35215"
y="7.1355872"
id="rect4355"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ff0000;fill-opacity:0.32017547;fill-rule:nonzero;stroke:none;stroke-width:1.11822701;marker:none;enable-background:accumulate" />
<rect
y="7.1355872"
x="72.35215"
height="6.8111153"
width="248.87634"
id="rect4353"
style="fill:#000000;fill-opacity:0.3559322;fill-rule:evenodd;stroke:none;stroke-width:1.44321382px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</svg>

Before

Width:  |  Height:  |  Size: 7.5 KiB

View file

@ -1,365 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="88.927498mm"
height="60.687836mm"
viewBox="0 0 315.09744 215.03564"
id="svg4313"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="pinch-gestures-softbuttons.svg">
<defs
id="defs4315">
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4506"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4494"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Mstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4491"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(0.4,0,0,0.4,4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker7694"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path7696"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="marker7562"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path7564"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4995"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(1.1,0,0,1.1,1.1,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker7356"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Mstart">
<path
transform="matrix(0.4,0,0,0.4,4,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
id="path7358"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Send"
orient="auto"
refY="0"
refX="0"
id="Arrow1Send"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4992"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Sstart"
orient="auto"
refY="0"
refX="0"
id="Arrow2Sstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5007"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(0.3,0,0,0.3,-0.69,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker6499"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lstart">
<path
transform="matrix(1.1,0,0,1.1,1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path6501"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="marker5837"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5839"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker5365"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend">
<path
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path5367"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="marker5291"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5293"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4980"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4977"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.6496034"
inkscape:cx="131.47222"
inkscape:cy="93.839318"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1136"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata4318">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-1.110285,-124.21518)">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.76355982;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect2858-0"
y="127.09696"
x="3.992065"
height="209.27208"
width="309.33386" />
<g
id="g4897"
transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,-136.33634,-278.03322)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4899"
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path4901"
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
id="path4903"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
<rect
style="opacity:0.92000002;fill:#ff0000;fill-opacity:0.31073447;stroke:#000000;stroke-width:0.97799999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.78531075"
id="rect8337"
width="303.45676"
height="57.133244"
x="6.9702415"
y="276.61765" />
<g
transform="matrix(0.64785166,-0.12161418,0.12161418,0.64785166,-167.25783,-242.59332)"
id="g5039">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path5041"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path5043"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path5045"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
id="g7665"
transform="matrix(0.98639446,-0.16439576,0.16439576,0.98639446,-83.838837,-24.728819)">
<path
sodipodi:nodetypes="sszzzcss"
d="m 136.26948,381.29633 c -24.01774,-7.29937 -29.0012,-10.10221 -30.51977,-10.54973 -10.672936,-3.14527 -18.270506,-5.54063 -23.777576,-13.4704 -5.50707,-7.92977 -5.34967,-20.78347 8.87612,-26.31603 14.225796,-5.53258 39.343506,8.79596 60.130606,16.16341 20.7871,7.36743 33.04562,11.44544 39.33421,13.8755 -8.10021,18.05041 -7.22128,21.15857 -10.11054,33.34117 -0.0481,0.20261 -17.87458,-5.12433 -43.93305,-13.04392 z"
id="path2824-1-1-3"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.00100005;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
sodipodi:nodetypes="ccccc"
d="m 107.01743,369.53232 c -10.672936,-1.94747 -17.884406,-5.64477 -21.626906,-8.75386 -8.11652,-9.03765 -6.31775,-15.03428 -3.3272,-13.99784 8.90495,-0.9097 30.203836,9.01528 33.860416,10.17935 -5.80268,11.37909 -1.08919,13.70271 -8.90631,12.57235 z"
id="path2824-7-1-4-3"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend)"
d="m 126.40547,189.76337 -18.14734,34.1597"
id="path8341"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path8343"
d="m 94.40547,249.76337 -18.14734,34.1597"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Lstart)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,612 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="193.91415mm"
height="71.032608mm"
viewBox="0 0 687.09741 251.69034"
id="svg4313"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="pinch-gestures.svg">
<defs
id="defs4315">
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker7694"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path7696"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="marker7562"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path7564"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4995"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(1.1,0,0,1.1,1.1,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker7356"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Mstart">
<path
transform="matrix(0.4,0,0,0.4,4,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
id="path7358"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker7250"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Mend"
inkscape:collect="always">
<path
transform="matrix(-0.4,0,0,-0.4,-4,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
id="path7252"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path4986"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Send"
orient="auto"
refY="0"
refX="0"
id="Arrow1Send"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4992"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Mstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mstart"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path4983"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.4,0,0,0.4,4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Sstart"
orient="auto"
refY="0"
refX="0"
id="Arrow2Sstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5007"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(0.3,0,0,0.3,-0.69,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker6499"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lstart">
<path
transform="matrix(1.1,0,0,1.1,1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path6501"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker6261"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend"
inkscape:collect="always">
<path
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path6263"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="marker5837"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5839"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker5365"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Lend">
<path
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path5367"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path4998"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="marker5291"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5293"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4980"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.8,0,0,-0.8,-10,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4977"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.93677625"
inkscape:cx="60.063165"
inkscape:cy="195.28138"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1136"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata4318">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-1.110285,-124.21518)">
<rect
width="309.33386"
height="209.27208"
x="375.99207"
y="127.09696"
id="rect14688"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.76355982;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.76355982;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect2858-0"
y="127.09696"
x="3.992065"
height="209.27208"
width="309.33386" />
<g
id="g3663-9-5"
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,-40.638425,-251.35321)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path2820-6-6"
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path2824-1-1"
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
id="path2824-7-1-4"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
<g
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,87.872503,-305.95371)"
id="g4877">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path4879"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path4881"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path4883"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,-40.638425,-251.35321)"
id="g4889">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path4891"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path4893"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path4895"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
id="g4897"
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,87.872503,-305.95371)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4899"
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path4901"
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
id="path4903"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
<g
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,327.582,-251.35321)"
id="g4915">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path4917"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path4919"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path4921"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
id="g4923"
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,456.09293,-305.95371)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4925"
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path4927"
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
id="path4929"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
<g
id="g4931"
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,327.582,-251.35321)">
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4933"
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
id="path4935"
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
id="path4937"
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z" />
</g>
<g
transform="matrix(0.64805599,0.12052062,-0.12052062,0.64805599,456.09293,-305.95371)"
id="g4939">
<path
d="m 388.57143,893.79076 -57.14285,-130 c 0,0 -30.0247,-58.84827 4.28571,-70.00001 27.07438,-8.79984 37.32196,9.59496 40,14.64286 27.54455,51.91936 84.64285,173.21429 84.64285,173.21429 l -0.71428,0 -71.07143,12.14286 z"
id="path4941"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
d="m 360.32021,827.78041 c -15.74169,-35.7991 -29.44655,-66.92657 -30.45523,-69.17214 -7.08929,-15.78239 -10.8761,-32.88254 -9.6176,-43.43026 1.39575,-11.69796 7.19746,-18.50389 18.22574,-21.38044 5.18218,-1.35169 8.54724,-1.76827 12.41155,-1.53649 4.43642,0.26609 6.95929,0.93715 11.03011,2.93391 3.93491,1.9301 8.0085,5.56248 10.68932,9.53159 3.68818,5.46055 26.56068,50.9623 49.57778,98.62829 16.60192,34.38082 37.06388,77.41994 36.89013,77.59369 -0.13286,0.13286 -69.01932,11.92114 -69.66286,11.92114 -0.27909,0 -12.00972,-26.24842 -29.08894,-65.08929 z"
id="path4943"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffccaa;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path
d="m 334.75785,756.75053 c -7.08929,-15.78239 -10.28437,-26.89033 -9.02587,-37.43805 1.39575,-11.69796 5.8085,-16.73613 16.83678,-19.61268 12.44766,-3.59459 20.03902,-1.91353 27.39013,8.75815 11.42622,25.66382 13.40166,29.05484 15.06365,35.48866 -0.13286,0.13286 -42.89663,15.49027 -44.57776,16.18518 -1.72922,0.71479 -4.94789,-2.09377 -5.68693,-3.38126 z"
id="path4945"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.92000002;fill:#ffe6d5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.31833494;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Lend)"
d="M 237.82329,186.65441 272.448,168.6762"
id="path4971"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path5363"
d="m 43.293248,279.41755 34.62471,-17.97821"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.31833494;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker6499)" />
<circle
style="opacity:0.92000002;fill:#ffdd55;fill-opacity:0.07734806;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path6880"
cx="158.49547"
cy="227.65926"
r="5.3374538" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.97016698px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#marker7356);marker-end:url(#marker7250)"
d="M 88.03522,236.61109 213.47708,179.47406"
id="path6882"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path6884"
d="M 25.998133,239.56832 257.65533,138.66416"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.97016698px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.50000095px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="139.81439"
y="246.73911"
id="text6886"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan6888"
x="139.81439"
y="246.73911">logical</tspan><tspan
sodipodi:role="line"
x="139.81439"
y="262.36411"
id="tspan6890">center</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.50000095px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-0.65095264"
y="247.5675"
id="text6892"
sodipodi:linespacing="125%"
transform="matrix(0.90931568,-0.41610697,0.41610692,0.90931568,0,0)"><tspan
sodipodi:role="line"
id="tspan6894"
x="-0.65095264"
y="247.5675">initial scale (1.0)</tspan></text>
<text
transform="matrix(0.90931568,-0.41610697,0.41610692,0.90931568,0,0)"
sodipodi:linespacing="125%"
id="text6896"
y="225.43965"
x="-16.939013"
style="font-style:normal;font-weight:normal;font-size:12.50000095px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="225.43965"
x="-16.939013"
id="tspan6898"
sodipodi:role="line">current scale (&gt; 1.0)</tspan></text>
<circle
r="5.3374538"
cy="226.45061"
cx="529.7337"
id="circle7474"
style="opacity:0.92000002;fill:#ffdd55;fill-opacity:0.07734806;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<text
sodipodi:linespacing="125%"
id="text7476"
y="245.53046"
x="511.05258"
style="font-style:normal;font-weight:normal;font-size:12.50000095px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="245.53046"
x="511.05258"
id="tspan7478"
sodipodi:role="line">logical</tspan><tspan
id="tspan7480"
y="261.15546"
x="511.05258"
sodipodi:role="line">center</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker6261)"
d="m 470.41432,259.01526 c 18.14735,33.09222 51.23956,27.22102 51.23956,27.22102"
id="path7554"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path7692"
d="M 592.09959,192.40501 C 567.10018,164.13044 536.11794,177.15603 536.11794,177.15603"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker7694)" />
<path
style="fill:#ff0000;fill-rule:evenodd;stroke:#dd0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 469.81386,258.48153 636.94289,169.34604"
id="path9270"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 540.33497,130.38263 523.25512,286.23628"
id="path9272"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="575.56219"
y="156.53615"
id="text9274"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan9276"
x="575.56219"
y="156.53615">delta</tspan><tspan
sodipodi:role="line"
x="575.56219"
y="172.16115"
id="tspan9278">angle</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 34 KiB

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 27 KiB

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