diff -pruN 1.28.1-1/.clang-format 1.30.0-1/.clang-format
--- 1.28.1-1/.clang-format	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/.clang-format	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,82 @@
+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;"]
diff -pruN 1.28.1-1/.clang-format-ignore 1.30.0-1/.clang-format-ignore
--- 1.28.1-1/.clang-format-ignore	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/.clang-format-ignore	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1 @@
+include/**/*
diff -pruN 1.28.1-1/.clang-tidy 1.30.0-1/.clang-tidy
--- 1.28.1-1/.clang-tidy	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/.clang-tidy	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,7 @@
+# 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: '*'
diff -pruN 1.28.1-1/.clang-tidy-ignore 1.30.0-1/.clang-tidy-ignore
--- 1.28.1-1/.clang-tidy-ignore	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/.clang-tidy-ignore	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1 @@
+include/*
diff -pruN 1.28.1-1/.git-blame-ignore-revs 1.30.0-1/.git-blame-ignore-revs
--- 1.28.1-1/.git-blame-ignore-revs	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/.git-blame-ignore-revs	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,21 @@
+# 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
diff -pruN 1.28.1-1/.gitlab-ci/ci.template 1.30.0-1/.gitlab-ci/ci.template
--- 1.28.1-1/.gitlab-ci/ci.template	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/.gitlab-ci/ci.template	2025-11-25 03:40:43.000000000 +0000
@@ -36,7 +36,7 @@
 # <distribution>:<version>@activity:
 #  e.g. fedora:31@build-default
 
-.templates_sha: &template_sha e195d80f35b45cc73668be3767b923fd76c70ed5
+.templates_sha: &template_sha c6aeb16f86e32525fa630fb99c66c4f3e62fc3cb
 
 include:
   - project: 'freedesktop/ci-templates'
@@ -50,9 +50,24 @@ include:
 
 workflow:
   rules:
-    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
-    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
+    # 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:
@@ -75,7 +90,7 @@ variables:
   # distribution                                                                #
   #                                                                             #
   # See the documentation here:                                                 #
-  # https://wayland.freedesktop.org/libinput/doc/latest/building_libinput.html  #
+  # https://wayland.freedesktop.org/libinput/doc/latest/building.html           #
   ###############################################################################
 {% for distro in distributions %}
   {{"%-17s" | format(distro.name.upper() + '_PACKAGES:')}} '{{ distro.packages|join(' ')}}'
@@ -101,6 +116,11 @@ variables:
   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
@@ -111,6 +131,16 @@ variables:
   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"
@@ -122,6 +152,10 @@ variables:
     reports:
       junit: $MESON_BUILDDIR/*junit*.xml
 
+.fdo-runner-tags:
+  tags:
+    - $FDO_RUNNER_JOB_PRIORITY_TAG_X86_64
+
 
 #################################################################
 #                                                               #
@@ -130,6 +164,8 @@ variables:
 #################################################################
 
 fail-if-fork-is-not-public:
+  extends:
+    - .fdo-runner-tags
   stage: sanity check
   script:
     - |
@@ -150,6 +186,7 @@ fail-if-fork-is-not-public:
 check-ci-script:
   extends:
     - .fdo.ci-fairy
+    - .fdo-runner-tags
   stage: sanity check
   script:
     - ci-fairy generate-template --verify && exit 0 || true
@@ -166,6 +203,7 @@ check-ci-script:
 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
@@ -190,6 +228,7 @@ check-commit:
 check-whitespace:
   extends:
     - .fdo.ci-fairy
+    - .fdo-runner-tags
   stage: sanity check
   script:
     - .gitlab-ci/whitespace-check.py $(git ls-files)
@@ -201,12 +240,13 @@ check-whitespace:
 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 || true
+    - pre-commit run --all-files
     - git diff --exit-code || (echo "ERROR - Code style errors found, please fix" && false)
 
 #################################################################
@@ -225,9 +265,10 @@ pre-commit-hooks:
     - .fdo.container-build@{{distro.name}}
 {% endif %}
     - .policy
+    - .fdo-runner-tags
 {% if distro.qemu_based %}
   tags:
-    - kvm
+    - $FDO_RUNNER_JOB_PRIORITY_TAG_X86_64_KVM
 {% endif %}
   stage: prep
   variables:
@@ -256,6 +297,7 @@ pre-commit-hooks:
   extends:
     - .policy
     - .fdo.ci-fairy
+    - .fdo-runner-tags
   stage: container_clean
   script:
     # Go to your Profile, Settings, Access Tokens
@@ -295,33 +337,40 @@ pre-commit-hooks:
   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-b2c@template:
+.build-in-vng@template:
   extends:
     - .policy
     - .default_artifacts
   tags:
-    - kvm
+    - $FDO_RUNNER_JOB_PRIORITY_TAG_X86_64_KVM
   variables:
     MESON_BUILDDIR: build_dir
-    B2C_KERNEL: {{ b2c.kernel }}
-    B2C_IMAGE: $FDO_DISTRIBUTION_IMAGE
-    B2C_COMMAND: .gitlab-ci/start-in-systemd.sh
+    VNG_KERNEL: {{ vng.kernel }}
   script:
     # first build in the host container
     - .gitlab-ci/meson-build.sh --skip-test
 
-    # pull b2c
-    - curl -L -o /app/boot2container https://gitlab.freedesktop.org/gfx-ci/boot2container/-/raw/{{b2c.version}}/vm2c.py
-    - chmod +x /app/boot2container
+    - mkdir -p $MESON_BUILDDIR
+    - curl -LO $VNG_KERNEL
+
+    - export -p > .vngenv
 
     # runs the test suite only
-    - /app/boot2container
+    - |
+      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
@@ -340,7 +389,7 @@ pre-commit-hooks:
   extends:
     - .policy
   tags:
-    - kvm
+    - $FDO_RUNNER_JOB_PRIORITY_TAG_X86_64_KVM
   variables:
     MESON_BUILDDIR: build_dir
   script:
@@ -386,7 +435,7 @@ pre-commit-hooks:
 # Set one or the other, not both.
 .test-suite-vm:
   extends:
-    - .build-in-b2c@template
+    - .build-in-vng@template
   stage: test-suite
   variables:
     # remove the global --no-suite=hardware
@@ -433,6 +482,7 @@ vm-valgrind-{{suite.name}}:
   stage: valgrind
   extends:
     - vm-{{suite.name}}
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -442,6 +492,7 @@ vm-valgrind-{{suite.name}}:
     - 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 %}
@@ -464,18 +515,16 @@ default-build-release@{{distro.name}}:{{
     MESON_ARGS: "-Dbuildtype=release"
     CFLAGS: "-Werror"
 
-scan-build@{{distro.name}}:{{version}}:
+clang-tidy@{{distro.name}}:{{version}}:
   extends:
     - .{{distro.name}}-build@template
   variables:
     NINJA_ARGS: ''
     MESON_TEST_ARGS: ''
+    CC: 'clang'
   script:
     - .gitlab-ci/meson-build.sh
-    - export SCANBUILD="$PWD/.gitlab-ci/scanbuild-wrapper.sh"
-    - ninja -C "$MESON_BUILDDIR" scan-build
-  after_script:
-    - .gitlab-ci/scanbuild-plist-to-junit.py "$MESON_BUILDDIR"/meson-logs/scanbuild/ > "$MESON_BUILDDIR"/junit-scan-build.xml
+    - 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
@@ -495,6 +544,34 @@ build-no-libwacom-nodeps@{{distro.name}}
   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
@@ -555,13 +632,6 @@ usr-bin-env-python@{{distro.name}}:{{ver
         /bin/false
       fi
 
-python-format@{{distro.name}}:{{version}}:
-  extends:
-    - .{{distro.name}}-build@template
-  script:
-    - black $(git grep -l '^#!/usr/bin/env python3')
-    - git diff --exit-code || (echo "Please run Black against all Python files" && false)
-
 # A job to check we're actually running all test suites in the CI
 check-test-suites:
   extends:
@@ -602,6 +672,7 @@ coverity:
   extends:
     - .fdo.distribution-image@debian
     - .policy
+    - .fdo-runner-tags
   stage: build
   variables:
     FDO_DISTRIBUTION_VERSION: 'stable'
@@ -690,32 +761,13 @@ coverity:
 #                                                               #
 #################################################################
 
-#
-# Verify that the merge request has the allow-collaboration checkbox ticked
-#
-
-check-merge-request:
-  extends:
-    - .fdo.ci-fairy
-    - .policy
-  stage: deploy
-  script:
-    - ci-fairy check-merge-request --require-allow-collaboration --junit-xml=results.xml
-  artifacts:
-    when: on_failure
-    reports:
-      junit: results.xml
-  allow_failure: true
-  rules:
-    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
-
-
 {% 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}}'
@@ -740,14 +792,12 @@ build rpm:
 wayland-web:
   stage: deploy
   trigger: wayland/wayland.freedesktop.org
-  except:
-    refs:
-      - schedules
   variables:
     MESON_ARGS: '-Ddocumentation=true -Ddebug-gui=false -Dlibwacom=false -Dtests=false'
     MESON_BUILDDIR: 'builddir'
-  only:
-    refs:
-      - main
-    variables:
-      - $CI_PROJECT_PATH == "libinput/libinput"
+  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
diff -pruN 1.28.1-1/.gitlab-ci/config.yml 1.30.0-1/.gitlab-ci/config.yml
--- 1.28.1-1/.gitlab-ci/config.yml	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/.gitlab-ci/config.yml	2025-11-25 03:40:43.000000000 +0000
@@ -3,14 +3,14 @@
 #
 
 # We're happy to rebuild all containers when one changes.
-.default_tag: &default_tag '2024-05-13.0'
+.default_tag: &default_tag '2025-11-17.0'
 
 distributions:
   - name: fedora
     tag: *default_tag
     versions:
-      - '40'
-      - '41'
+      - '42'
+      - '43'
     use_for_custom_build_tests: true
     use_for_qemu_tests: true
     packages:
@@ -36,7 +36,8 @@ distributions:
       - diffutils
       - wayland-protocols-devel
       - black           # for the Python black job, optional
-      - clang-analyzer  # for the scan-build 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
@@ -48,6 +49,8 @@ distributions:
       - jq
       - python3-click
       - python3-rich
+      - virtme-ng
+      - lua-devel
   - name: debian
     tag: *default_tag
     versions:
@@ -73,10 +76,11 @@ distributions:
       - libglib2.0-dev
       - libmtdev-dev
       - curl            # for the coverity job
+      - lua5.4-dev
   - name: ubuntu
     tag: *default_tag
     versions:
-      - '22.04'
+      - '25.10'
     packages:
       - git
       - gcc
@@ -97,6 +101,7 @@ distributions:
       - libgtk-3-dev
       - libglib2.0-dev
       - libmtdev-dev
+      - lua5.4-dev
   - name: arch
     tag: *default_tag
     versions:
@@ -109,17 +114,15 @@ distributions:
       - check
       - libsystemd
       - libevdev
-      - doxygen
-      - graphviz
-      - python-sphinx
-      - python-recommonmark
-      - python-sphinx_rtd_theme
       - python-pytest-xdist
       - libwacom
       - gtk4
       - mtdev
       - diffutils
-      - wayland-protocols
+      - lua
+    build:
+      extra_variables:
+        - "MESON_ARGS: '-Ddocumentation=false'" # python-recommonmark is no longer in the repos
   - name: alpine
     tag: *default_tag
     versions:
@@ -137,6 +140,7 @@ distributions:
       - gtk4.0-dev
       - mtdev-dev
       - bash
+      - lua5.4-dev
     build:
       extra_variables:
         - "MESON_ARGS: '-Ddocumentation=false' # alpine does not have python-recommonmark"
@@ -148,7 +152,7 @@ distributions:
     tag: *default_tag
     qemu_based: true
     versions:
-      - '13.2'
+      - '14.2'
     packages:
       - git
       - pkgconf
@@ -196,6 +200,13 @@ test_suites:
   - 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
@@ -221,7 +232,9 @@ test_suites:
   - name: pointer
     suites:
       - pointer
+  - name: lua
+    suites:
+      - lua
 
-b2c:
-  version: 24beb454c62fe59143d7ceb4f5a7d25a3c029559
-  kernel: https://gitlab.freedesktop.org/api/v4/projects/libevdev%2Fhid-tools/packages/generic/kernel-x86_64/v6.5/bzImage
+vng:
+  kernel: https://gitlab.freedesktop.org/api/v4/projects/libevdev%2Fhid-tools/packages/generic/kernel-x86_64/v6.14/bzImage
diff -pruN 1.28.1-1/.gitlab-ci/libinput.spec.in 1.30.0-1/.gitlab-ci/libinput.spec.in
--- 1.28.1-1/.gitlab-ci/libinput.spec.in	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/.gitlab-ci/libinput.spec.in	2025-11-25 03:40:43.000000000 +0000
@@ -100,6 +100,7 @@ intended to be run by users.
 %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
@@ -117,6 +118,7 @@ intended to be run by users.
 %{_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*
diff -pruN 1.28.1-1/.gitlab-ci/scanbuild-plist-to-junit.py 1.30.0-1/.gitlab-ci/scanbuild-plist-to-junit.py
--- 1.28.1-1/.gitlab-ci/scanbuild-plist-to-junit.py	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/.gitlab-ci/scanbuild-plist-to-junit.py	1970-01-01 00:00:00.000000000 +0000
@@ -1,131 +0,0 @@
-#!/usr/bin/env python3
-#
-# SPDX-License-Identifier: MIT
-#
-# Usage:
-#   $ scanbuild-plist-to-junit.py /path/to/meson-logs/scanbuild/ > junit-report.xml
-#
-# Converts the plist output from scan-build into a JUnit-compatible XML.
-#
-# For use with meson, use a wrapper script with this content:
-#   scan-build -v --status-bugs -plist-html "$@"
-# then build with
-#  SCANBUILD="/abs/path/to/wrapper.sh" ninja -C builddir scan-build
-#
-# For file context, $PWD has to be the root source directory.
-#
-# Note that the XML format is tailored towards being useful in the gitlab
-# CI, the JUnit format supports more features.
-#
-# This file is formatted with Python Black
-
-import argparse
-import plistlib
-import re
-import sys
-from pathlib import Path
-
-errors = []
-
-
-class Error(object):
-    pass
-
-
-parser = argparse.ArgumentParser(
-    description="This tool convers scan-build's plist format to JUnit XML"
-)
-parser.add_argument(
-    "directory", help="Path to a scan-build output directory", type=Path
-)
-args = parser.parse_args()
-
-if not args.directory.exists():
-    print(f"Invalid directory: {args.directory}", file=sys.stderr)
-    sys.exit(1)
-
-# Meson places scan-build runs into a timestamped directory. To make it
-# easier to invoke this script, we just glob everything on the assumption
-# that there's only one scanbuild/$timestamp/ directory anyway.
-for file in Path(args.directory).glob("**/*.plist"):
-    with open(file, "rb") as fd:
-        plist = plistlib.load(fd, fmt=plistlib.FMT_XML)
-        try:
-            sources = plist["files"]
-            for elem in plist["diagnostics"]:
-                e = Error()
-                e.type = elem["type"]  # Human-readable error type
-                e.description = elem["description"]  # Longer description
-                e.func = elem["issue_context"]  # function name
-                e.lineno = elem["location"]["line"]
-                filename = sources[elem["location"]["file"]]
-                # Remove the ../../../ prefix from the file
-                e.file = re.sub(r"^(\.\./)*", "", filename)
-                errors.append(e)
-        except KeyError:
-            print(
-                "Failed to access plist content, incompatible format?", file=sys.stderr
-            )
-            sys.exit(1)
-
-
-# Add a few lines of context for each error that we can print in the xml
-# output. Note that e.lineno is 1-indexed.
-#
-# If one of the files fail, we stop doing this, we're probably in the wrong
-# directory.
-try:
-    current_file = None
-    lines = []
-    for e in sorted(errors, key=lambda x: x.file):
-        if current_file != e.file:
-            current_file = e.file
-            lines = open(current_file).readlines()
-
-        # e.lineno is 1-indexed, lineno is our 0-indexed line number
-        lineno = e.lineno - 1
-        start = max(0, lineno - 4)
-        end = min(len(lines), lineno + 5)  # end is exclusive
-        e.context = [
-            f"{'>' if line == e.lineno else ' '} {line}: {content}"
-            for line, content in zip(range(start + 1, end), lines[start:end])
-        ]
-except FileNotFoundError:
-    pass
-
-print('<?xml version="1.0" encoding="utf-8"?>')
-print("<testsuites>")
-if errors:
-    suites = sorted(set([s.type for s in errors]))
-    # Use a counter to ensure test names are unique, otherwise the CI
-    # display ignores duplicates.
-    counter = 0
-    for suite in suites:
-        errs = [e for e in errors if e.type == suite]
-        # Note: the grouping by suites doesn't actually do anything in gitlab. Oh well
-        print(f'<testsuite name="{suite}" failures="{len(errs)}" tests="{len(errs)}">')
-        for error in errs:
-            print(
-                f"""\
-<testcase name="{counter}. {error.type} - {error.file}:{error.lineno}" classname="{error.file}">
-<failure message="{error.description}">
-<![CDATA[
-In function {error.func}(),
-{error.description}
-
-{error.file}:{error.lineno}
----
-{"".join(error.context)}
-]]>
-</failure>
-</testcase>"""
-            )
-            counter += 1
-        print("</testsuite>")
-else:
-    # In case of success, add one test case so that registers in the UI
-    # properly
-    print('<testsuite name="scanbuild" failures="0" tests="1">')
-    print('<testcase name="scanbuild" classname="scanbuild"/>')
-    print("</testsuite>")
-print("</testsuites>")
diff -pruN 1.28.1-1/.gitlab-ci/start-in-systemd.sh 1.30.0-1/.gitlab-ci/start-in-systemd.sh
--- 1.28.1-1/.gitlab-ci/start-in-systemd.sh	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/.gitlab-ci/start-in-systemd.sh	1970-01-01 00:00:00.000000000 +0000
@@ -1,69 +0,0 @@
-#!/usr/bin/env bash
-
-set -x
-
-systemd_target=basic.target
-post_command="/usr/bin/systemctl exit \$EXIT_STATUS"
-
-while [[ $# -gt 0 ]]; do
-	case $1 in
-		--debug-mode)
-			shift
-			systemd_target=multi-user.target
-			post_command="echo you can now log in as root (no password) and then turn off by running \'/usr/bin/systemctl exit \$EXIT_STATUS\'"
-			;;
-		*)
-			echo "Unknow commandline argument $1"
-			exit 1
-			;;
-	esac
-done
-
-WORKDIR=${FDO_DISTRIBUTION_WORKINGDIR:-$PWD}
-B2C_WORKDIR=${FDO_B2C_WORKDIR:-/app}
-
-# remove root password for debugging
-sed -i 's/root:!locked::/root:::/' /etc/shadow
-
-# create a libinput test suite service
-cat <<EOF > /etc/systemd/system/libinput-testsuite.service
-
-[Unit]
-Description=Libinput test suite
-After=$systemd_target
-
-[Service]
-Type=simple
-StandardOutput=journal+console
-EnvironmentFile=$B2C_WORKDIR/.b2c_env
-WorkingDirectory=$WORKDIR
-ExecStart=$WORKDIR/.gitlab-ci/meson-build.sh --skip-setup --skip-build --run-test
-
-# exit the container on termination
-ExecStopPost=$post_command
-
-[Install]
-WantedBy=default.target
-EOF
-
-cat /etc/systemd/system/libinput-testsuite.service
-
-# enable the service
-systemctl enable libinput-testsuite.service
-
-# disable some services we don't need in the CI
-systemctl mask network-online.target
-systemctl mask network-pre.target
-systemctl mask timers.target
-systemctl mask dnf-makecache.timer
-systemctl mask systemd-logind.service
-systemctl mask rpmdb-migrate.service
-systemctl mask systemd-network-generator.service
-systemctl mask cryptsetup-pre.target
-systemctl mask cryptsetup.target
-
-#change default target
-systemctl set-default $systemd_target
-
-# start the system
-exec /usr/sbin/init
diff -pruN 1.28.1-1/.gitlab-ci/whitespace-check.py 1.30.0-1/.gitlab-ci/whitespace-check.py
--- 1.28.1-1/.gitlab-ci/whitespace-check.py	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/.gitlab-ci/whitespace-check.py	2025-11-25 03:40:43.000000000 +0000
@@ -48,6 +48,17 @@ def test_trailing_whitespace(lines: list
     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(
@@ -78,6 +89,7 @@ def main():
         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)
diff -pruN 1.28.1-1/.gitlab-ci.yml 1.30.0-1/.gitlab-ci.yml
--- 1.28.1-1/.gitlab-ci.yml	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/.gitlab-ci.yml	2025-11-25 03:40:43.000000000 +0000
@@ -34,7 +34,7 @@
 # <distribution>:<version>@activity:
 #  e.g. fedora:31@build-default
 
-.templates_sha: &template_sha e195d80f35b45cc73668be3767b923fd76c70ed5
+.templates_sha: &template_sha c6aeb16f86e32525fa630fb99c66c4f3e62fc3cb
 
 include:
   - project: 'freedesktop/ci-templates'
@@ -56,9 +56,24 @@ include:
 
 workflow:
   rules:
-    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
-    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
+    # 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:
@@ -81,13 +96,13 @@ variables:
   # distribution                                                                #
   #                                                                             #
   # See the documentation here:                                                 #
-  # https://wayland.freedesktop.org/libinput/doc/latest/building_libinput.html  #
+  # https://wayland.freedesktop.org/libinput/doc/latest/building.html           #
   ###############################################################################
-  FEDORA_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 clang-analyzer jq rpmdevtools valgrind systemd-udev qemu-img qemu-system-x86-core qemu-system-aarch64-core jq python3-click python3-rich'
-  DEBIAN_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'
-  UBUNTU_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'
-  ARCH_PACKAGES:    'git gcc pkgconfig meson check libsystemd libevdev doxygen graphviz python-sphinx python-recommonmark python-sphinx_rtd_theme python-pytest-xdist libwacom gtk4 mtdev diffutils wayland-protocols'
-  ALPINE_PACKAGES:  'git gcc build-base pkgconfig meson check-dev eudev-dev libevdev-dev libwacom-dev cairo-dev gtk4.0-dev mtdev-dev bash'
+  FEDORA_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 clang clang-tools-extra jq rpmdevtools valgrind systemd-udev qemu-img qemu-system-x86-core qemu-system-aarch64-core jq python3-click python3-rich virtme-ng lua-devel'
+  DEBIAN_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 lua5.4-dev'
+  UBUNTU_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'
+  ARCH_PACKAGES:    'git gcc pkgconfig meson check libsystemd libevdev python-pytest-xdist libwacom gtk4 mtdev diffutils lua'
+  ALPINE_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'
   FREEBSD_PACKAGES: 'git pkgconf meson libepoll-shim libudev-devd libevdev libwacom gtk3 libmtdev bash wayland'
   ############################ end of package lists #############################
 
@@ -95,12 +110,12 @@ variables:
   # changing these will force rebuilding the associated image
   # Note: these tags have no meaning and are not tied to a particular
   # libinput version
-  FEDORA_TAG:  '2024-05-13.0'
-  DEBIAN_TAG:  '2024-05-13.0'
-  UBUNTU_TAG:  '2024-05-13.0'
-  ARCH_TAG:    '2024-05-13.0'
-  ALPINE_TAG:  '2024-05-13.0'
-  FREEBSD_TAG: '2024-05-13.0'
+  FEDORA_TAG:  '2025-11-17.0'
+  DEBIAN_TAG:  '2025-11-17.0'
+  UBUNTU_TAG:  '2025-11-17.0'
+  ARCH_TAG:    '2025-11-17.0'
+  ALPINE_TAG:  '2025-11-17.0'
+  FREEBSD_TAG: '2025-11-17.0'
 
   FDO_UPSTREAM_REPO: libinput/libinput
 
@@ -113,6 +128,11 @@ variables:
   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
@@ -123,6 +143,16 @@ variables:
   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"
@@ -134,6 +164,10 @@ variables:
     reports:
       junit: $MESON_BUILDDIR/*junit*.xml
 
+.fdo-runner-tags:
+  tags:
+    - $FDO_RUNNER_JOB_PRIORITY_TAG_X86_64
+
 
 #################################################################
 #                                                               #
@@ -142,6 +176,8 @@ variables:
 #################################################################
 
 fail-if-fork-is-not-public:
+  extends:
+    - .fdo-runner-tags
   stage: sanity check
   script:
     - |
@@ -162,6 +198,7 @@ fail-if-fork-is-not-public:
 check-ci-script:
   extends:
     - .fdo.ci-fairy
+    - .fdo-runner-tags
   stage: sanity check
   script:
     - ci-fairy generate-template --verify && exit 0 || true
@@ -178,6 +215,7 @@ check-ci-script:
 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
@@ -202,6 +240,7 @@ check-commit:
 check-whitespace:
   extends:
     - .fdo.ci-fairy
+    - .fdo-runner-tags
   stage: sanity check
   script:
     - .gitlab-ci/whitespace-check.py $(git ls-files)
@@ -213,12 +252,13 @@ check-whitespace:
 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 || true
+    - pre-commit run --all-files
     - git diff --exit-code || (echo "ERROR - Code style errors found, please fix" && false)
 
 #################################################################
@@ -227,25 +267,27 @@ pre-commit-hooks:
 #                                                               #
 #################################################################
 
-fedora:40@container-prep:
+fedora:42@container-prep:
   extends:
     - .fdo.container-build@fedora
     - .policy
+    - .fdo-runner-tags
   stage: prep
   variables:
     GIT_STRATEGY: none
-    FDO_DISTRIBUTION_VERSION: '40'
+    FDO_DISTRIBUTION_VERSION: '42'
     FDO_DISTRIBUTION_PACKAGES: $FEDORA_PACKAGES
     FDO_DISTRIBUTION_TAG: $FEDORA_TAG
 
-fedora:41@container-prep:
+fedora:43@container-prep:
   extends:
     - .fdo.container-build@fedora
     - .policy
+    - .fdo-runner-tags
   stage: prep
   variables:
     GIT_STRATEGY: none
-    FDO_DISTRIBUTION_VERSION: '41'
+    FDO_DISTRIBUTION_VERSION: '43'
     FDO_DISTRIBUTION_PACKAGES: $FEDORA_PACKAGES
     FDO_DISTRIBUTION_TAG: $FEDORA_TAG
 
@@ -253,6 +295,7 @@ debian:stable@container-prep:
   extends:
     - .fdo.container-build@debian
     - .policy
+    - .fdo-runner-tags
   stage: prep
   variables:
     GIT_STRATEGY: none
@@ -260,14 +303,15 @@ debian:stable@container-prep:
     FDO_DISTRIBUTION_PACKAGES: $DEBIAN_PACKAGES
     FDO_DISTRIBUTION_TAG: $DEBIAN_TAG
 
-ubuntu:22.04@container-prep:
+ubuntu:25.10@container-prep:
   extends:
     - .fdo.container-build@ubuntu
     - .policy
+    - .fdo-runner-tags
   stage: prep
   variables:
     GIT_STRATEGY: none
-    FDO_DISTRIBUTION_VERSION: '22.04'
+    FDO_DISTRIBUTION_VERSION: '25.10'
     FDO_DISTRIBUTION_PACKAGES: $UBUNTU_PACKAGES
     FDO_DISTRIBUTION_TAG: $UBUNTU_TAG
 
@@ -275,6 +319,7 @@ arch:rolling@container-prep:
   extends:
     - .fdo.container-build@arch
     - .policy
+    - .fdo-runner-tags
   stage: prep
   variables:
     GIT_STRATEGY: none
@@ -286,6 +331,7 @@ alpine:latest@container-prep:
   extends:
     - .fdo.container-build@alpine
     - .policy
+    - .fdo-runner-tags
   stage: prep
   variables:
     GIT_STRATEGY: none
@@ -293,16 +339,17 @@ alpine:latest@container-prep:
     FDO_DISTRIBUTION_PACKAGES: $ALPINE_PACKAGES
     FDO_DISTRIBUTION_TAG: $ALPINE_TAG
 
-freebsd:13.2@container-prep:
+freebsd:14.2@container-prep:
   extends:
     - .fdo.qemu-build@freebsd
     - .policy
+    - .fdo-runner-tags
   tags:
-    - kvm
+    - $FDO_RUNNER_JOB_PRIORITY_TAG_X86_64_KVM
   stage: prep
   variables:
     GIT_STRATEGY: none
-    FDO_DISTRIBUTION_VERSION: '13.2'
+    FDO_DISTRIBUTION_VERSION: '14.2'
     FDO_DISTRIBUTION_PACKAGES: $FREEBSD_PACKAGES
     FDO_DISTRIBUTION_TAG: $FREEBSD_TAG
 
@@ -324,6 +371,7 @@ freebsd:13.2@container-prep:
   extends:
     - .policy
     - .fdo.ci-fairy
+    - .fdo-runner-tags
   stage: container_clean
   script:
     # Go to your Profile, Settings, Access Tokens
@@ -338,24 +386,24 @@ freebsd:13.2@container-prep:
   only:
     - schedules
 
-fedora:40@container-clean:
+fedora:42@container-clean:
   extends:
     - .policy
     - .container-clean
   variables:
     GIT_STRATEGY: none
     CURRENT_CONTAINER_IMAGE: $CI_REGISTRY_IMAGE/fedora/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG
-    FDO_DISTRIBUTION_VERSION: '40'
+    FDO_DISTRIBUTION_VERSION: '42'
     FDO_DISTRIBUTION_TAG: $FEDORA_TAG
 
-fedora:41@container-clean:
+fedora:43@container-clean:
   extends:
     - .policy
     - .container-clean
   variables:
     GIT_STRATEGY: none
     CURRENT_CONTAINER_IMAGE: $CI_REGISTRY_IMAGE/fedora/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG
-    FDO_DISTRIBUTION_VERSION: '41'
+    FDO_DISTRIBUTION_VERSION: '43'
     FDO_DISTRIBUTION_TAG: $FEDORA_TAG
 
 debian:stable@container-clean:
@@ -368,14 +416,14 @@ debian:stable@container-clean:
     FDO_DISTRIBUTION_VERSION: 'stable'
     FDO_DISTRIBUTION_TAG: $DEBIAN_TAG
 
-ubuntu:22.04@container-clean:
+ubuntu:25.10@container-clean:
   extends:
     - .policy
     - .container-clean
   variables:
     GIT_STRATEGY: none
     CURRENT_CONTAINER_IMAGE: $CI_REGISTRY_IMAGE/ubuntu/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG
-    FDO_DISTRIBUTION_VERSION: '22.04'
+    FDO_DISTRIBUTION_VERSION: '25.10'
     FDO_DISTRIBUTION_TAG: $UBUNTU_TAG
 
 arch:rolling@container-clean:
@@ -398,14 +446,14 @@ alpine:latest@container-clean:
     FDO_DISTRIBUTION_VERSION: 'latest'
     FDO_DISTRIBUTION_TAG: $ALPINE_TAG
 
-freebsd:13.2@container-clean:
+freebsd:14.2@container-clean:
   extends:
     - .policy
     - .container-clean
   variables:
     GIT_STRATEGY: none
     CURRENT_CONTAINER_IMAGE: $CI_REGISTRY_IMAGE/freebsd/$FDO_DISTRIBUTION_VERSION:$FDO_DISTRIBUTION_TAG
-    FDO_DISTRIBUTION_VERSION: '13.2'
+    FDO_DISTRIBUTION_VERSION: '14.2'
     FDO_DISTRIBUTION_TAG: $FREEBSD_TAG
 
 
@@ -419,33 +467,40 @@ freebsd:13.2@container-clean:
   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-b2c@template:
+.build-in-vng@template:
   extends:
     - .policy
     - .default_artifacts
   tags:
-    - kvm
+    - $FDO_RUNNER_JOB_PRIORITY_TAG_X86_64_KVM
   variables:
     MESON_BUILDDIR: build_dir
-    B2C_KERNEL: https://gitlab.freedesktop.org/api/v4/projects/libevdev%2Fhid-tools/packages/generic/kernel-x86_64/v6.5/bzImage
-    B2C_IMAGE: $FDO_DISTRIBUTION_IMAGE
-    B2C_COMMAND: .gitlab-ci/start-in-systemd.sh
+    VNG_KERNEL: https://gitlab.freedesktop.org/api/v4/projects/libevdev%2Fhid-tools/packages/generic/kernel-x86_64/v6.14/bzImage
   script:
     # first build in the host container
     - .gitlab-ci/meson-build.sh --skip-test
 
-    # pull b2c
-    - curl -L -o /app/boot2container https://gitlab.freedesktop.org/gfx-ci/boot2container/-/raw/24beb454c62fe59143d7ceb4f5a7d25a3c029559/vm2c.py
-    - chmod +x /app/boot2container
+    - mkdir -p $MESON_BUILDDIR
+    - curl -LO $VNG_KERNEL
+
+    - export -p > .vngenv
 
     # runs the test suite only
-    - /app/boot2container
+    - |
+      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
@@ -464,7 +519,7 @@ freebsd:13.2@container-clean:
   extends:
     - .policy
   tags:
-    - kvm
+    - $FDO_RUNNER_JOB_PRIORITY_TAG_X86_64_KVM
   variables:
     MESON_BUILDDIR: build_dir
   script:
@@ -510,7 +565,7 @@ freebsd:13.2@container-clean:
 # Set one or the other, not both.
 .test-suite-vm:
   extends:
-    - .build-in-b2c@template
+    - .build-in-vng@template
   stage: test-suite
   variables:
     # remove the global --no-suite=hardware
@@ -522,20 +577,20 @@ freebsd:13.2@container-clean:
     - export MESON_TEST_ARGS="$MESON_TEST_ARGS $SUITES"
 
 
-.fedora:41@test-suite-vm:
+.fedora:43@test-suite-vm:
   extends:
     - .fdo.distribution-image@fedora
     - .test-suite-vm
   variables:
-    FDO_DISTRIBUTION_VERSION: 41
+    FDO_DISTRIBUTION_VERSION: 43
     FDO_DISTRIBUTION_TAG: $FEDORA_TAG
   needs:
-    - "fedora:41@container-prep"
+    - "fedora:43@container-prep"
 
 
 vm-touchpad:
   extends:
-    - .fedora:41@test-suite-vm
+    - .fedora:43@test-suite-vm
   variables:
     SUITE_NAMES: 'touchpad'
 
@@ -548,7 +603,7 @@ vm-touchpad-no-libwacom:
 
 vm-touchpad_palm:
   extends:
-    - .fedora:41@test-suite-vm
+    - .fedora:43@test-suite-vm
   variables:
     SUITE_NAMES: 'touchpad_palm'
 
@@ -561,7 +616,7 @@ vm-touchpad_palm-no-libwacom:
 
 vm-touchpad_dwt:
   extends:
-    - .fedora:41@test-suite-vm
+    - .fedora:43@test-suite-vm
   variables:
     SUITE_NAMES: 'touchpad_dwt'
 
@@ -574,7 +629,7 @@ vm-touchpad_dwt-no-libwacom:
 
 vm-tap:
   extends:
-    - .fedora:41@test-suite-vm
+    - .fedora:43@test-suite-vm
   variables:
     SUITE_NAMES: 'touchpad_tap'
 
@@ -587,7 +642,7 @@ vm-tap-no-libwacom:
 
 vm-tap-drag:
   extends:
-    - .fedora:41@test-suite-vm
+    - .fedora:43@test-suite-vm
   variables:
     SUITE_NAMES: 'touchpad_tap_drag'
 
@@ -600,7 +655,7 @@ vm-tap-drag-no-libwacom:
 
 vm-tap-palm:
   extends:
-    - .fedora:41@test-suite-vm
+    - .fedora:43@test-suite-vm
   variables:
     SUITE_NAMES: 'touchpad_tap_palm'
 
@@ -613,7 +668,7 @@ vm-tap-palm-no-libwacom:
 
 vm-touchpad-buttons:
   extends:
-    - .fedora:41@test-suite-vm
+    - .fedora:43@test-suite-vm
   variables:
     SUITE_NAMES: 'touchpad_buttons'
 
@@ -626,7 +681,7 @@ vm-touchpad-buttons-no-libwacom:
 
 vm-tablet:
   extends:
-    - .fedora:41@test-suite-vm
+    - .fedora:43@test-suite-vm
   variables:
     SUITE_NAMES: 'tablet'
 
@@ -639,7 +694,7 @@ vm-tablet-no-libwacom:
 
 vm-tablet_left_handed:
   extends:
-    - .fedora:41@test-suite-vm
+    - .fedora:43@test-suite-vm
   variables:
     SUITE_NAMES: 'tablet_left_handed'
 
@@ -650,9 +705,35 @@ vm-tablet_left_handed-no-libwacom:
   variables:
     MESON_ARGS: '-Dlibwacom=false'
 
+vm-tablet_proximity_tip:
+  extends:
+    - .fedora:43@test-suite-vm
+  variables:
+    SUITE_NAMES: 'tablet_proximity tablet_tip'
+
+vm-tablet_proximity_tip-no-libwacom:
+  extends:
+    - vm-tablet_proximity_tip
+  stage: test-suite-no-libwacom
+  variables:
+    MESON_ARGS: '-Dlibwacom=false'
+
+vm-tablet_eraser:
+  extends:
+    - .fedora:43@test-suite-vm
+  variables:
+    SUITE_NAMES: 'tablet_eraser'
+
+vm-tablet_eraser-no-libwacom:
+  extends:
+    - vm-tablet_eraser
+  stage: test-suite-no-libwacom
+  variables:
+    MESON_ARGS: '-Dlibwacom=false'
+
 vm-gestures:
   extends:
-    - .fedora:41@test-suite-vm
+    - .fedora:43@test-suite-vm
   variables:
     SUITE_NAMES: 'gestures'
 
@@ -665,7 +746,7 @@ vm-gestures-no-libwacom:
 
 vm-backends:
   extends:
-    - .fedora:41@test-suite-vm
+    - .fedora:43@test-suite-vm
   variables:
     SUITE_NAMES: 'path udev'
 
@@ -678,7 +759,7 @@ vm-backends-no-libwacom:
 
 vm-misc:
   extends:
-    - .fedora:41@test-suite-vm
+    - .fedora:43@test-suite-vm
   variables:
     SUITE_NAMES: 'log misc quirks device'
 
@@ -691,7 +772,7 @@ vm-misc-no-libwacom:
 
 vm-other devices:
   extends:
-    - .fedora:41@test-suite-vm
+    - .fedora:43@test-suite-vm
   variables:
     SUITE_NAMES: 'keyboard pad switch trackball trackpoint totem touch'
 
@@ -704,7 +785,7 @@ vm-other devices-no-libwacom:
 
 vm-pointer:
   extends:
-    - .fedora:41@test-suite-vm
+    - .fedora:43@test-suite-vm
   variables:
     SUITE_NAMES: 'pointer'
 
@@ -715,11 +796,25 @@ vm-pointer-no-libwacom:
   variables:
     MESON_ARGS: '-Dlibwacom=false'
 
+vm-lua:
+  extends:
+    - .fedora:43@test-suite-vm
+  variables:
+    SUITE_NAMES: 'lua'
+
+vm-lua-no-libwacom:
+  extends:
+    - vm-lua
+  stage: test-suite-no-libwacom
+  variables:
+    MESON_ARGS: '-Dlibwacom=false'
+
 
 vm-valgrind-touchpad:
   stage: valgrind
   extends:
     - vm-touchpad
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -732,6 +827,7 @@ vm-valgrind-touchpad_palm:
   stage: valgrind
   extends:
     - vm-touchpad_palm
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -744,6 +840,7 @@ vm-valgrind-touchpad_dwt:
   stage: valgrind
   extends:
     - vm-touchpad_dwt
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -756,6 +853,7 @@ vm-valgrind-tap:
   stage: valgrind
   extends:
     - vm-tap
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -768,6 +866,7 @@ vm-valgrind-tap-drag:
   stage: valgrind
   extends:
     - vm-tap-drag
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -780,6 +879,7 @@ vm-valgrind-tap-palm:
   stage: valgrind
   extends:
     - vm-tap-palm
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -792,6 +892,7 @@ vm-valgrind-touchpad-buttons:
   stage: valgrind
   extends:
     - vm-touchpad-buttons
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -804,6 +905,7 @@ vm-valgrind-tablet:
   stage: valgrind
   extends:
     - vm-tablet
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -816,6 +918,33 @@ vm-valgrind-tablet_left_handed:
   stage: valgrind
   extends:
     - vm-tablet_left_handed
+    - .policy-retry-on-failure
+  variables:
+    MESON_TEST_ARGS: '--setup=valgrind'
+    LITEST_JOBS: 0
+  retry:
+    max: 2
+  rules:
+    - if: $GITLAB_USER_LOGIN != "marge-bot"
+
+vm-valgrind-tablet_proximity_tip:
+  stage: valgrind
+  extends:
+    - vm-tablet_proximity_tip
+    - .policy-retry-on-failure
+  variables:
+    MESON_TEST_ARGS: '--setup=valgrind'
+    LITEST_JOBS: 0
+  retry:
+    max: 2
+  rules:
+    - if: $GITLAB_USER_LOGIN != "marge-bot"
+
+vm-valgrind-tablet_eraser:
+  stage: valgrind
+  extends:
+    - vm-tablet_eraser
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -828,6 +957,7 @@ vm-valgrind-gestures:
   stage: valgrind
   extends:
     - vm-gestures
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -840,6 +970,7 @@ vm-valgrind-backends:
   stage: valgrind
   extends:
     - vm-backends
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -852,6 +983,7 @@ vm-valgrind-misc:
   stage: valgrind
   extends:
     - vm-misc
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -864,6 +996,7 @@ vm-valgrind-other devices:
   stage: valgrind
   extends:
     - vm-other devices
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -876,6 +1009,20 @@ vm-valgrind-pointer:
   stage: valgrind
   extends:
     - vm-pointer
+    - .policy-retry-on-failure
+  variables:
+    MESON_TEST_ARGS: '--setup=valgrind'
+    LITEST_JOBS: 0
+  retry:
+    max: 2
+  rules:
+    - if: $GITLAB_USER_LOGIN != "marge-bot"
+
+vm-valgrind-lua:
+  stage: valgrind
+  extends:
+    - vm-lua
+    - .policy-retry-on-failure
   variables:
     MESON_TEST_ARGS: '--setup=valgrind'
     LITEST_JOBS: 0
@@ -885,17 +1032,18 @@ vm-valgrind-pointer:
     - if: $GITLAB_USER_LOGIN != "marge-bot"
 
 
+
 .fedora-build@template:
   extends:
     - .fdo.distribution-image@fedora
     - .build@template
   variables:
-    FDO_DISTRIBUTION_VERSION: '41'
+    FDO_DISTRIBUTION_VERSION: '43'
     FDO_DISTRIBUTION_TAG: $FEDORA_TAG
   needs:
-    - "fedora:41@container-prep"
+    - "fedora:43@container-prep"
 
-default-build-release@fedora:41:
+default-build-release@fedora:43:
   stage: distro
   extends:
     - .fedora-build@template
@@ -903,30 +1051,28 @@ default-build-release@fedora:41:
     MESON_ARGS: "-Dbuildtype=release"
     CFLAGS: "-Werror"
 
-scan-build@fedora:41:
+clang-tidy@fedora:43:
   extends:
     - .fedora-build@template
   variables:
     NINJA_ARGS: ''
     MESON_TEST_ARGS: ''
+    CC: 'clang'
   script:
     - .gitlab-ci/meson-build.sh
-    - export SCANBUILD="$PWD/.gitlab-ci/scanbuild-wrapper.sh"
-    - ninja -C "$MESON_BUILDDIR" scan-build
-  after_script:
-    - .gitlab-ci/scanbuild-plist-to-junit.py "$MESON_BUILDDIR"/meson-logs/scanbuild/ > "$MESON_BUILDDIR"/junit-scan-build.xml
+    - 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@fedora:41:
+build-no-libwacom@fedora:43:
   extends:
     - .fedora-build@template
   variables:
     MESON_ARGS: "-Dlibwacom=false"
 
-build-no-libwacom-nodeps@fedora:41:
+build-no-libwacom-nodeps@fedora:43:
   extends:
     - .fedora-build@template
   variables:
@@ -934,13 +1080,41 @@ build-no-libwacom-nodeps@fedora:41:
   before_script:
     - dnf remove -y libwacom libwacom-devel
 
-build-docs@fedora:41:
+build-no-mtdev@fedora:43:
+  extends:
+    - .fedora-build@template
+  variables:
+    MESON_ARGS: "-Dmtdev=false"
+
+build-no-mtdev-nodeps@fedora:43:
+  extends:
+    - .fedora-build@template
+  variables:
+    MESON_ARGS: "-Dmtdev=false"
+  before_script:
+    - dnf remove -y mtdev mtdev-devel
+
+build-no-lua@fedora:43:
+  extends:
+    - .fedora-build@template
+  variables:
+    MESON_ARGS: "-Dlua-plugins=disabled"
+
+build-no-lua-nodeps@fedora:43:
+  extends:
+    - .fedora-build@template
+  variables:
+    MESON_ARGS: "-Dlua-plugins=disabled"
+  before_script:
+    - dnf remove -y lua lua-devel
+
+build-docs@fedora:43:
   extends:
     - .fedora-build@template
   variables:
     MESON_ARGS: "-Ddocumentation=true"
 
-build-no-docs-nodeps@fedora:41:
+build-no-docs-nodeps@fedora:43:
   extends:
     - .fedora-build@template
   variables:
@@ -948,13 +1122,13 @@ build-no-docs-nodeps@fedora:41:
   before_script:
     - dnf remove -y doxygen graphviz
 
-build-no-debuggui@fedora:41:
+build-no-debuggui@fedora:43:
   extends:
     - .fedora-build@template
   variables:
     MESON_ARGS: "-Ddebug-gui=false"
 
-build-no-debuggui-nodeps@fedora:41:
+build-no-debuggui-nodeps@fedora:43:
   extends:
     - .fedora-build@template
   variables:
@@ -962,13 +1136,13 @@ build-no-debuggui-nodeps@fedora:41:
   before_script:
     - dnf remove -y gtk3-devel gtk4-devel
 
-build-no-tests@fedora:41:
+build-no-tests@fedora:43:
   extends:
     - .fedora-build@template
   variables:
     MESON_ARGS: "-Dtests=false"
 
-build-no-tests-nodeps@fedora:41:
+build-no-tests-nodeps@fedora:43:
   extends:
     - .fedora-build@template
   variables:
@@ -976,7 +1150,7 @@ build-no-tests-nodeps@fedora:41:
   before_script:
     - dnf remove -y check-devel
 
-valgrind@fedora:41:
+valgrind@fedora:43:
   extends:
     - .fedora-build@template
   variables:
@@ -984,7 +1158,7 @@ valgrind@fedora:41:
 
 # Python checks, only run on Fedora
 
-usr-bin-env-python@fedora:41:
+usr-bin-env-python@fedora:43:
   extends:
     - .fedora-build@template
   script:
@@ -994,13 +1168,6 @@ usr-bin-env-python@fedora:41:
         /bin/false
       fi
 
-python-format@fedora:41:
-  extends:
-    - .fedora-build@template
-  script:
-    - black $(git grep -l '^#!/usr/bin/env python3')
-    - git diff --exit-code || (echo "Please run Black against all Python files" && false)
-
 # A job to check we're actually running all test suites in the CI
 check-test-suites:
   extends:
@@ -1019,6 +1186,9 @@ check-test-suites:
         libinput-test-suite-touchpad_buttons
         libinput-test-suite-tablet
         libinput-test-suite-tablet_left_handed
+        libinput-test-suite-tablet_proximity
+        libinput-test-suite-tablet_tip
+        libinput-test-suite-tablet_eraser
         libinput-test-suite-gestures
         libinput-test-suite-path
         libinput-test-suite-udev
@@ -1034,6 +1204,7 @@ check-test-suites:
         libinput-test-suite-totem
         libinput-test-suite-touch
         libinput-test-suite-pointer
+        libinput-test-suite-lua
       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)
@@ -1059,6 +1230,7 @@ coverity:
   extends:
     - .fdo.distribution-image@debian
     - .policy
+    - .fdo-runner-tags
   stage: build
   variables:
     FDO_DISTRIBUTION_VERSION: 'stable'
@@ -1097,28 +1269,28 @@ coverity:
 #                                                               #
 #################################################################
 
-fedora:40@default-build:
+fedora:42@default-build:
   stage: distro
   extends:
     - .build@template
     - .fdo.distribution-image@fedora
   variables:
-    FDO_DISTRIBUTION_VERSION: '40'
+    FDO_DISTRIBUTION_VERSION: '42'
     FDO_DISTRIBUTION_TAG: $FEDORA_TAG
   needs:
-    - "fedora:40@container-prep"
+    - "fedora:42@container-prep"
 
 
-fedora:41@default-build:
+fedora:43@default-build:
   stage: distro
   extends:
     - .build@template
     - .fdo.distribution-image@fedora
   variables:
-    FDO_DISTRIBUTION_VERSION: '41'
+    FDO_DISTRIBUTION_VERSION: '43'
     FDO_DISTRIBUTION_TAG: $FEDORA_TAG
   needs:
-    - "fedora:41@container-prep"
+    - "fedora:43@container-prep"
 
 
 debian:stable@default-build:
@@ -1133,16 +1305,16 @@ debian:stable@default-build:
     - "debian:stable@container-prep"
 
 
-ubuntu:22.04@default-build:
+ubuntu:25.10@default-build:
   stage: distro
   extends:
     - .build@template
     - .fdo.distribution-image@ubuntu
   variables:
-    FDO_DISTRIBUTION_VERSION: '22.04'
+    FDO_DISTRIBUTION_VERSION: '25.10'
     FDO_DISTRIBUTION_TAG: $UBUNTU_TAG
   needs:
-    - "ubuntu:22.04@container-prep"
+    - "ubuntu:25.10@container-prep"
 
 
 arch:rolling@default-build:
@@ -1153,6 +1325,7 @@ arch:rolling@default-build:
   variables:
     FDO_DISTRIBUTION_VERSION: 'rolling'
     FDO_DISTRIBUTION_TAG: $ARCH_TAG
+    MESON_ARGS: '-Ddocumentation=false'
   needs:
     - "arch:rolling@container-prep"
 
@@ -1171,18 +1344,18 @@ alpine:latest@default-build:
     - "alpine:latest@container-prep"
 
 
-freebsd:13.2@default-build:
+freebsd:14.2@default-build:
   stage: distro
   extends:
     - .build-in-qemu@template
     - .fdo.distribution-image@freebsd
   variables:
-    FDO_DISTRIBUTION_VERSION: '13.2'
+    FDO_DISTRIBUTION_VERSION: '14.2'
     FDO_DISTRIBUTION_TAG: $FREEBSD_TAG
     MESON_ARGS: '-Dtests=false -Ddocumentation=false' # doxygen drags down too many deps
     MESON_TEST_ARGS: '' # test suite doesn't work on BSD yet
   needs:
-    - "freebsd:13.2@container-prep"
+    - "freebsd:14.2@container-prep"
 
 
 #################################################################
@@ -1191,36 +1364,17 @@ freebsd:13.2@default-build:
 #                                                               #
 #################################################################
 
-#
-# Verify that the merge request has the allow-collaboration checkbox ticked
-#
-
-check-merge-request:
-  extends:
-    - .fdo.ci-fairy
-    - .policy
-  stage: deploy
-  script:
-    - ci-fairy check-merge-request --require-allow-collaboration --junit-xml=results.xml
-  artifacts:
-    when: on_failure
-    reports:
-      junit: results.xml
-  allow_failure: true
-  rules:
-    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
-
-
 build rpm:
   extends:
     - .fdo.distribution-image@fedora
     - .policy
+    - .fdo-runner-tags
   stage: deploy
   variables:
-    FDO_DISTRIBUTION_VERSION: '41'
+    FDO_DISTRIBUTION_VERSION: '43'
     FDO_DISTRIBUTION_TAG: $FEDORA_TAG
   needs:
-    - "fedora:41@container-prep"
+    - "fedora:43@container-prep"
   script:
     - meson "$MESON_BUILDDIR"
     - VERSION=$(meson introspect "$MESON_BUILDDIR" --projectinfo | jq -r .version)
@@ -1238,14 +1392,12 @@ build rpm:
 wayland-web:
   stage: deploy
   trigger: wayland/wayland.freedesktop.org
-  except:
-    refs:
-      - schedules
   variables:
     MESON_ARGS: '-Ddocumentation=true -Ddebug-gui=false -Dlibwacom=false -Dtests=false'
     MESON_BUILDDIR: 'builddir'
-  only:
-    refs:
-      - main
-    variables:
-      - $CI_PROJECT_PATH == "libinput/libinput"
+  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
diff -pruN 1.28.1-1/.pre-commit-config.yaml 1.30.0-1/.pre-commit-config.yaml
--- 1.28.1-1/.pre-commit-config.yaml	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/.pre-commit-config.yaml	2025-11-25 03:40:43.000000000 +0000
@@ -7,9 +7,9 @@ repos:
   - id: no-commit-to-branch
     args: ['--branch', 'main']
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.8.1
+  rev: v0.12.2
   hooks:
-  - id: ruff
+  - id: ruff-check
     args: ['--ignore=E741,E501', '--extend-exclude=subprojects', '.']
   - id: ruff-format
     args: ['--check', '--diff']
@@ -24,3 +24,8 @@ repos:
     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]
diff -pruN 1.28.1-1/.triage-policies.yml 1.30.0-1/.triage-policies.yml
--- 1.28.1-1/.triage-policies.yml	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/.triage-policies.yml	2025-11-25 03:40:43.000000000 +0000
@@ -183,6 +183,16 @@ resource_rules:
             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
diff -pruN 1.28.1-1/completion/zsh/_libinput 1.30.0-1/completion/zsh/_libinput
--- 1.28.1-1/completion/zsh/_libinput	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/completion/zsh/_libinput	2025-11-25 03:40:43.000000000 +0000
@@ -49,13 +49,15 @@ __all_seats()
 		'--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-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]' \
@@ -91,6 +93,9 @@ __all_seats()
 		+ '(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]'
@@ -174,7 +179,7 @@ __all_seats()
 {
 	_arguments \
 		'--help[Show help message and exit]' \
-		'--format=dat[Specify the data format to be printed. The default is "summary"]'
+		'--format=dat[Specify the data format to be printed. The default is "summary"]' \
 		':device:_files -W /dev/input/ -P /dev/input/'
 }
 
diff -pruN 1.28.1-1/debian/changelog 1.30.0-1/debian/changelog
--- 1.28.1-1/debian/changelog	2025-04-03 12:47:30.000000000 +0000
+++ 1.30.0-1/debian/changelog	2025-11-25 15:03:20.000000000 +0000
@@ -1,3 +1,12 @@
+libinput (1.30.0-1) unstable; urgency=medium
+
+  * New upstream release.
+  * watch: Update to version 5.
+  * patches: Refreshed.
+  * symbols: Updated.
+
+ -- Timo Aaltonen <tjaalton@debian.org>  Tue, 25 Nov 2025 17:03:20 +0200
+
 libinput (1.28.1-1) unstable; urgency=medium
 
   * New upstream release.
diff -pruN 1.28.1-1/debian/libinput10.symbols 1.30.0-1/debian/libinput10.symbols
--- 1.28.1-1/debian/libinput10.symbols	2025-04-03 12:44:47.000000000 +0000
+++ 1.30.0-1/debian/libinput10.symbols	2025-11-25 15:00:35.000000000 +0000
@@ -16,7 +16,9 @@ libinput.so.10 libinput10 #MINVER#
  LIBINPUT_1.26@LIBINPUT_1.26 1.26.0
  LIBINPUT_1.27@LIBINPUT_1.27 1.27.1
  LIBINPUT_1.28@LIBINPUT_1.28 1.28.0
+ LIBINPUT_1.29@LIBINPUT_1.29 1.30.0
  LIBINPUT_1.2@LIBINPUT_1.2 1.2.0
+ LIBINPUT_1.30@LIBINPUT_1.30 1.30.0
  LIBINPUT_1.3@LIBINPUT_1.3 1.3.0
  LIBINPUT_1.4@LIBINPUT_1.4 1.4.0
  LIBINPUT_1.5@LIBINPUT_1.5 1.5.0
@@ -266,6 +268,9 @@ libinput.so.10 libinput10 #MINVER#
  libinput_path_add_device@LIBINPUT_0.12.0 0.15.0
  libinput_path_create_context@LIBINPUT_0.12.0 0.15.0
  libinput_path_remove_device@LIBINPUT_0.12.0 0.15.0
+ libinput_plugin_system_append_default_paths@LIBINPUT_1.30 1.30.0
+ libinput_plugin_system_append_path@LIBINPUT_1.30 1.30.0
+ libinput_plugin_system_load_plugins@LIBINPUT_1.30 1.30.0
  libinput_ref@LIBINPUT_0.12.0 0.15.0
  libinput_resume@LIBINPUT_0.12.0 0.15.0
  libinput_seat_get_context@LIBINPUT_0.12.0 0.15.0
@@ -289,6 +294,13 @@ libinput.so.10 libinput10 #MINVER#
  libinput_tablet_pad_mode_group_ref@LIBINPUT_1.4 1.4.0
  libinput_tablet_pad_mode_group_set_user_data@LIBINPUT_1.4 1.4.0
  libinput_tablet_pad_mode_group_unref@LIBINPUT_1.4 1.4.0
+ libinput_tablet_tool_config_eraser_button_get_button@LIBINPUT_1.29 1.30.0
+ libinput_tablet_tool_config_eraser_button_get_default_button@LIBINPUT_1.29 1.30.0
+ libinput_tablet_tool_config_eraser_button_get_default_mode@LIBINPUT_1.29 1.30.0
+ libinput_tablet_tool_config_eraser_button_get_mode@LIBINPUT_1.29 1.30.0
+ libinput_tablet_tool_config_eraser_button_get_modes@LIBINPUT_1.29 1.30.0
+ libinput_tablet_tool_config_eraser_button_set_button@LIBINPUT_1.29 1.30.0
+ libinput_tablet_tool_config_eraser_button_set_mode@LIBINPUT_1.29 1.30.0
  libinput_tablet_tool_config_pressure_range_get_default_maximum@LIBINPUT_1.26 1.26.0
  libinput_tablet_tool_config_pressure_range_get_default_minimum@LIBINPUT_1.26 1.26.0
  libinput_tablet_tool_config_pressure_range_get_maximum@LIBINPUT_1.26 1.26.0
diff -pruN 1.28.1-1/debian/patches/tools-remove-references-to-libinput_quir.patch 1.30.0-1/debian/patches/tools-remove-references-to-libinput_quir.patch
--- 1.28.1-1/debian/patches/tools-remove-references-to-libinput_quir.patch	2025-04-03 12:44:47.000000000 +0000
+++ 1.30.0-1/debian/patches/tools-remove-references-to-libinput_quir.patch	2025-11-25 14:32:17.000000000 +0000
@@ -10,18 +10,14 @@ https://reproducible-builds.org/docs/bui
 
 ---
 
-diff --git a/tools/libinput-quirks.c b/tools/libinput-quirks.c
-index e97eff6..7f3e26f 100644
 --- a/tools/libinput-quirks.c
 +++ b/tools/libinput-quirks.c
-@@ -166,14 +166,8 @@ main(int argc, char **argv)
+@@ -163,12 +163,8 @@ main(int argc, char **argv)
  
  	/* Overriding the data dir means no custom override file */
  	if (!data_path) {
--		char *builddir = builddir_lookup();
--		if (builddir) {
+-		if (builddir_lookup(NULL)) {
 -			data_path = LIBINPUT_QUIRKS_SRCDIR;
--			free(builddir);
 -		} else {
 -			data_path = LIBINPUT_QUIRKS_DIR;
 -			override_file = LIBINPUT_QUIRKS_OVERRIDE_FILE;
@@ -30,53 +26,39 @@ index e97eff6..7f3e26f 100644
 +		override_file = LIBINPUT_QUIRKS_OVERRIDE_FILE;
  	}
  
- 	quirks = quirks_init_subsystem(data_path,
-diff --git a/tools/libinput-record.c b/tools/libinput-record.c
-index 30b2900..1de63bc 100644
+ 	_unref_(quirks_context) *quirks =
 --- a/tools/libinput-record.c
 +++ b/tools/libinput-record.c
-@@ -1762,19 +1762,10 @@ print_device_quirks(struct record_device *dev)
- 	struct quirks_context *quirks;
- 	const char *data_path = LIBINPUT_QUIRKS_DIR;
- 	const char *override_file = LIBINPUT_QUIRKS_OVERRIDE_FILE;
--	char *builddir = NULL;
- 
+@@ -1781,12 +1781,6 @@ print_device_quirks(struct record_device
  	if (stat(dev->devnode, &st) < 0)
  		return;
  
--	if ((builddir = builddir_lookup())) {
+-	if (builddir_lookup(NULL)) {
 -		setenv("LIBINPUT_QUIRKS_DIR", LIBINPUT_QUIRKS_SRCDIR, 0);
 -		data_path = LIBINPUT_QUIRKS_SRCDIR;
 -		override_file = NULL;
 -	}
 -
--	free(builddir);
--
- 	quirks = quirks_init_subsystem(data_path,
- 				       override_file,
- 				       quirks_log_handler,
-diff --git a/tools/shared.c b/tools/shared.c
-index 7a73027..fcacb03 100644
+ 	_unref_(quirks_context) *quirks =
+ 		quirks_init_subsystem(data_path,
+ 				      override_file,
 --- a/tools/shared.c
 +++ b/tools/shared.c
-@@ -411,16 +411,6 @@ tools_open_device(const char **paths, bool verbose, bool *grab)
- 	return li;
+@@ -613,13 +613,6 @@ tools_open_device(const char **paths,
+ 	return steal(&li);
  }
  
 -static void
 -tools_setenv_quirks_dir(void)
 -{
--	char *builddir = builddir_lookup();
--	if (builddir) {
+-	if (builddir_lookup(NULL))
 -		setenv("LIBINPUT_QUIRKS_DIR", LIBINPUT_QUIRKS_SRCDIR, 0);
--		free(builddir);
--	}
 -}
 -
  struct libinput *
  tools_open_backend(enum tools_backend which,
  		   const char **seat_or_device,
-@@ -429,8 +419,6 @@ tools_open_backend(enum tools_backend which,
+@@ -630,8 +623,6 @@ tools_open_backend(enum tools_backend wh
  {
  	struct libinput *li;
  
@@ -84,4 +66,4 @@ index 7a73027..fcacb03 100644
 -
  	switch (which) {
  	case BACKEND_UDEV:
- 		li = tools_open_udev(seat_or_device[0], verbose, grab);
+ 		li = tools_open_udev(seat_or_device[0],
diff -pruN 1.28.1-1/debian/watch 1.30.0-1/debian/watch
--- 1.28.1-1/debian/watch	2025-04-03 12:44:47.000000000 +0000
+++ 1.30.0-1/debian/watch	2025-11-25 14:24:27.000000000 +0000
@@ -1,4 +1,4 @@
-#git=https://gitlab.freedesktop.org/libinput/libinput.git
-version=4
-opts="searchmode=plain" \
- https://gitlab.freedesktop.org/libinput/@PACKAGE@/tags?sort=updated_desc -/archive/v?\d[\d.]+/@PACKAGE@-@ANY_VERSION@\.tar\.gz
+Version: 5
+
+Template: Gitlab
+Dist: https://gitlab.freedesktop.org/libinput/libinput
diff -pruN 1.28.1-1/doc/user/absolute-coordinate-ranges.rst 1.30.0-1/doc/user/absolute-coordinate-ranges.rst
--- 1.28.1-1/doc/user/absolute-coordinate-ranges.rst	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/doc/user/absolute-coordinate-ranges.rst	2025-11-25 03:40:43.000000000 +0000
@@ -40,11 +40,6 @@ To fix the touchpad you need to:
 
 Detailed explanations are below.
 
-.. note:: ``libinput measure touchpad-size`` was introduced in libinput
-	  1.16. For earlier versions, use `libevdev <http://freedesktop.org/wiki/Software/libevdev/>`_'s
-	  ``touchpad-edge-detector`` tool.
-
-
 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
diff -pruN 1.28.1-1/doc/user/architecture.rst 1.30.0-1/doc/user/architecture.rst
--- 1.28.1-1/doc/user/architecture.rst	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/doc/user/architecture.rst	2025-11-25 03:40:43.000000000 +0000
@@ -13,6 +13,10 @@ for almost all API calls. General device
 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.
@@ -150,10 +154,8 @@ pointers to handle events. Four such dis
 implemented: touchpad, tablet, tablet pad, and the fallback dispatch which
 handles mice, keyboards and touchscreens.
 
-
 .. graphviz::
 
-
     digraph context
     {
       compound=true;
@@ -177,12 +179,59 @@ handles mice, keyboards and touchscreens
     }
 
 
+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 pipline"]
+
+      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
     {
@@ -194,22 +243,62 @@ events is performed within the dispatch
 
       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 -> fallback;
-      evdev -> touchpad;
-      evdev -> tablet;
-      evdev -> pad;
+      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"];
+    }
 
-The dispatch methods then look at the ``struct input_event`` and proceed to
-update the state. Note: the serialized nature of the kernel evdev protocol
-requires that the device updates the state with each event but to delay
-processing until the ``SYN_REPORT`` event is received.
+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:
 
diff -pruN 1.28.1-1/doc/user/configuration.rst 1.30.0-1/doc/user/configuration.rst
--- 1.28.1-1/doc/user/configuration.rst	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/doc/user/configuration.rst	2025-11-25 03:40:43.000000000 +0000
@@ -6,15 +6,21 @@ 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 is is the responsibility of the caller to expose
+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 exposes the options through X Input device
-properties and xorg.conf.d options. See the `libinput(4)
+`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.
 
 
@@ -205,6 +211,18 @@ before the maximum hardware-supported pr
 
 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
 ------------------------------------------------------------------------------
diff -pruN 1.28.1-1/doc/user/contributing.rst 1.30.0-1/doc/user/contributing.rst
--- 1.28.1-1/doc/user/contributing.rst	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/doc/user/contributing.rst	2025-11-25 03:40:43.000000000 +0000
@@ -181,7 +181,7 @@ sufficient to make some of the more conf
   with your username). git will call this repository ``gitlab``. ::
 
     cd /path/to/libinput.git
-    git remote add gitlab git@gitlab.freedesktop.org:USERNAME/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. ::
diff -pruN 1.28.1-1/doc/user/device-quirks.rst 1.30.0-1/doc/user/device-quirks.rst
--- 1.28.1-1/doc/user/device-quirks.rst	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/doc/user/device-quirks.rst	2025-11-25 03:40:43.000000000 +0000
@@ -155,6 +155,10 @@ 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
@@ -223,8 +227,8 @@ MatchBus
     A lower-case bus name. Currently supported are ``usb``, ``bluetooth``, ``ps2``,
     ``rmi``, ``i2c``, and ``spi``.
 MatchVendor, MatchProduct, MatchVersion
-    The hexadecmial 4-digit vendor ID, product ID or driver version as exported, without
-    a ``0x`` prefix.
+    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``.
diff -pruN 1.28.1-1/doc/user/dot/plugin-stack.gv 1.30.0-1/doc/user/dot/plugin-stack.gv
--- 1.28.1-1/doc/user/dot/plugin-stack.gv	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/doc/user/dot/plugin-stack.gv	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,31 @@
+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"];
+}
diff -pruN 1.28.1-1/doc/user/index.rst 1.30.0-1/doc/user/index.rst
--- 1.28.1-1/doc/user/index.rst	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/doc/user/index.rst	2025-11-25 03:40:43.000000000 +0000
@@ -12,6 +12,7 @@
   troubleshooting
   contributing
   development
+  lua-plugins
   API documentation <@HTTP_DOC_LINK@/api/>
 
 
@@ -23,7 +24,7 @@ libinput is a library that provides a fu
 and other applications that need to handle input devices provided by the
 kernel.
 
-libinput provides device detection, event handling and abstraction so
+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
@@ -36,7 +37,7 @@ driver than an application library. See
 Users and Developers
 --------------------
 
-Please use the side-bar to nagivate through the various documentation items.
+Please use the side-bar to navigate through the various documentation items.
 
 -----------------
 API documentation
diff -pruN 1.28.1-1/doc/user/lua-plugins.rst 1.30.0-1/doc/user/lua-plugins.rst
--- 1.28.1-1/doc/user/lua-plugins.rst	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/doc/user/lua-plugins.rst	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,661 @@
+.. _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
diff -pruN 1.28.1-1/doc/user/meson.build 1.30.0-1/doc/user/meson.build
--- 1.28.1-1/doc/user/meson.build	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/doc/user/meson.build	2025-11-25 03:40:43.000000000 +0000
@@ -100,6 +100,7 @@ src_rst = files(
 	'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',
@@ -123,6 +124,8 @@ src_rst = files(
 	'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',
@@ -153,6 +156,7 @@ src_rst = files(
 	'middle-button-emulation.rst',
 	'normalization-of-relative-motion.rst',
 	'palm-detection.rst',
+	'lua-plugins.rst',
 	'pointer-acceleration.rst',
 	'reporting-bugs.rst',
 	'scrolling.rst',
diff -pruN 1.28.1-1/doc/user/reporting-bugs.rst 1.30.0-1/doc/user/reporting-bugs.rst
--- 1.28.1-1/doc/user/reporting-bugs.rst	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/doc/user/reporting-bugs.rst	2025-11-25 03:40:43.000000000 +0000
@@ -113,8 +113,8 @@ When you file a bug, please attach the f
 - 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 the ``touchpad-edge-detector`` tool (provided by libevdev) and verify
-  that the ranges and sizes it prints match the touchpad (up to 5mm
+- 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:
diff -pruN 1.28.1-1/doc/user/svg/tablet-eraser-button.svg 1.30.0-1/doc/user/svg/tablet-eraser-button.svg
--- 1.28.1-1/doc/user/svg/tablet-eraser-button.svg	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/doc/user/svg/tablet-eraser-button.svg	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="104.05109mm"
+   height="64.88131mm"
+   viewBox="0 0 368.68495 229.89441"
+   id="svg4321"
+   version="1.1"
+   inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
+   sodipodi:docname="tablet-eraser-button.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:dc="http://purl.org/dc/elements/1.1/">
+  <defs
+     id="defs4323">
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4294"
+       id="linearGradient4300"
+       x1="465.81339"
+       y1="666.13727"
+       x2="454.82117"
+       y2="658.65521"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4294">
+      <stop
+         style="stop-color:#1a1a1a;stop-opacity:1;"
+         offset="0"
+         id="stop4296" />
+      <stop
+         style="stop-color:#808080;stop-opacity:1"
+         offset="1"
+         id="stop4298" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4294"
+       id="linearGradient3"
+       gradientUnits="userSpaceOnUse"
+       x1="465.81339"
+       y1="666.13727"
+       x2="454.82117"
+       y2="658.65521" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.8"
+     inkscape:cx="330.71429"
+     inkscape:cy="144.28571"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="3072"
+     inkscape:window-height="1659"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:showpageshadow="2"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#d1d1d1" />
+  <metadata
+     id="metadata4326">
+    <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>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-67.741956,-463.35919)">
+    <g
+       transform="matrix(0.35596319,-0.1450925,0.14027908,0.34415419,-98.897559,452.82709)"
+       id="g4304"
+       style="display:inline">
+      <path
+         style="display:inline;fill:#cccccc;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 387.83544,799.76093 c -1.1128,3.61694 -3.2211,13.05163 -1.08543,14.07769 2.13567,1.02606 7.81039,-3.72162 10.99756,-6.69095 z"
+         id="path4286"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="czcc" />
+      <path
+         style="display:inline;fill:#000000"
+         d="m 392.64431,804.79039 c -8.52094,-5.90399 -8.49394,-11.01546 0.22879,-43.30647 1.03999,-3.85 2.46829,-9.67602 3.17399,-12.9467 0.99731,-4.62219 2.39455,-7.29497 6.27321,-12 2.74456,-3.32932 5.25157,-6.2783 5.57113,-6.5533 40.78433,-60.97488 80.48307,-125.1652 118.27253,-184 9.86283,-15.675 26.59424,-42.225 37.18089,-59 10.58666,-16.775 34.01422,-53.9 52.06125,-82.5 18.04703,-28.6 35.04505,-55.31677 37.77338,-59.37059 l 4.9606,-7.3706 4.1828,0.57332 c 4.16371,0.5707 4.19706,0.54958 7.30887,-4.62941 3.75631,-6.2516 8.82067,-11.57582 12.2516,-12.88026 5.99391,-2.27888 14.03303,2.9506 14.03303,9.12854 0,3.90203 -2.51704,10.62127 -6.02878,16.09385 -1.63417,2.54664 -2.97122,4.85949 -2.97122,5.13969 0,0.28019 0.9,1.54715 2,2.81546 2.28453,2.63408 2.47267,4.21918 0.86833,7.31574 -1.28218,2.47476 -26.61383,45.18798 -55.85724,94.18426 -10.83283,18.15 -25.72943,43.1137 -33.10357,55.47489 -7.37413,12.3612 -13.69273,23.17153 -14.04131,24.02297 -0.34859,0.85144 -7.50972,12.78774 -15.91363,26.52511 -15.54138,25.40455 -32.24417,52.9052 -70.74345,116.47703 -40.26028,66.47968 -43.66308,72.46026 -49.21634,86.5 -1.74036,4.4 -3.92035,8.675 -4.8444,9.5 -0.92405,0.825 -4.36246,3.75 -7.6409,6.5 -3.27845,2.75 -9.57132,8.3067 -13.98415,12.34823 -10.62726,9.73304 -16.99729,13.87361 -22.52334,14.64034 -3.99187,0.55386 -5.03885,0.251 -9.27207,-2.6821 z"
+         id="path4283"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="ssssccssscsssssssssssssssssss" />
+      <path
+         style="fill:url(#linearGradient4300);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 450.89044,688.88586 c 8.71518,5.62513 45.74035,-59.18436 43.57923,-75.43494 l -7.07107,-6.56599 c -29.93081,25.86352 -47.78438,74.72281 -47.78438,74.72281 z"
+         id="path4292"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="scccs" />
+    </g>
+    <rect
+       transform="scale(1,-1)"
+       y="-693.18524"
+       x="67.81031"
+       height="16.036251"
+       width="168.54825"
+       id="rect4136"
+       style="fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.136709;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:0.546834, 0.136709;stroke-dashoffset:0;stroke-opacity:1" />
+    <rect
+       transform="scale(1,-1)"
+       y="-693.18524"
+       x="267.8103"
+       height="16.036251"
+       width="168.54825"
+       id="rect5709"
+       style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.136709;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:0.546834, 0.136709;stroke-dashoffset:0;stroke-opacity:1" />
+    <text
+       id="text5713"
+       y="559.10828"
+       x="296.20877"
+       style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;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="559.10828"
+         x="296.20877"
+         id="tspan5715"
+         sodipodi:role="line"
+         style="font-size:15px;line-height:1.25;font-family:sans-serif">Eraser</tspan></text>
+    <text
+       id="text1"
+       y="559.10828"
+       x="109.0436"
+       style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;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="559.10828"
+         x="109.0436"
+         id="tspan1"
+         sodipodi:role="line"
+         style="font-size:15px;line-height:1.25;font-family:sans-serif">Pen</tspan></text>
+    <g
+       transform="matrix(0.35596319,-0.1450925,0.14027908,0.34415419,117.39708,452.82709)"
+       id="g3"
+       style="display:inline">
+      <path
+         style="display:inline;fill:#cccccc;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 387.83544,799.76093 c -1.1128,3.61694 -3.2211,13.05163 -1.08543,14.07769 2.13567,1.02606 7.81039,-3.72162 10.99756,-6.69095 z"
+         id="path1"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="czcc" />
+      <path
+         style="display:inline;fill:#000000"
+         d="m 392.64431,804.79039 c -8.52094,-5.90399 -8.49394,-11.01546 0.22879,-43.30647 1.03999,-3.85 2.46829,-9.67602 3.17399,-12.9467 0.99731,-4.62219 2.39455,-7.29497 6.27321,-12 2.74456,-3.32932 5.25157,-6.2783 5.57113,-6.5533 40.78433,-60.97488 80.48307,-125.1652 118.27253,-184 9.86283,-15.675 26.59424,-42.225 37.18089,-59 10.58666,-16.775 34.01422,-53.9 52.06125,-82.5 18.04703,-28.6 35.04505,-55.31677 37.77338,-59.37059 l 4.9606,-7.3706 4.1828,0.57332 c 4.16371,0.5707 4.19706,0.54958 7.30887,-4.62941 3.75631,-6.2516 8.82067,-11.57582 12.2516,-12.88026 5.99391,-2.27888 14.03303,2.9506 14.03303,9.12854 0,3.90203 -2.51704,10.62127 -6.02878,16.09385 -1.63417,2.54664 -2.97122,4.85949 -2.97122,5.13969 0,0.28019 0.9,1.54715 2,2.81546 2.28453,2.63408 2.47267,4.21918 0.86833,7.31574 -1.28218,2.47476 -26.61383,45.18798 -55.85724,94.18426 -10.83283,18.15 -25.72943,43.1137 -33.10357,55.47489 -7.37413,12.3612 -13.69273,23.17153 -14.04131,24.02297 -0.34859,0.85144 -7.50972,12.78774 -15.91363,26.52511 -15.54138,25.40455 -32.24417,52.9052 -70.74345,116.47703 -40.26028,66.47968 -43.66308,72.46026 -49.21634,86.5 -1.74036,4.4 -3.92035,8.675 -4.8444,9.5 -0.92405,0.825 -4.36246,3.75 -7.6409,6.5 -3.27845,2.75 -9.57132,8.3067 -13.98415,12.34823 -10.62726,9.73304 -16.99729,13.87361 -22.52334,14.64034 -3.99187,0.55386 -5.03885,0.251 -9.27207,-2.6821 z"
+         id="path2"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="ssssccssscsssssssssssssssssss" />
+      <path
+         style="fill:url(#linearGradient3);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 450.89044,688.88586 c 8.71518,5.62513 45.74035,-59.18436 43.57923,-75.43494 l -7.07107,-6.56599 c -29.93081,25.86352 -47.78438,74.72281 -47.78438,74.72281 z"
+         id="path3"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="scccs" />
+    </g>
+    <g
+       transform="matrix(-0.21797128,-0.07136846,0.05329769,-0.25512499,403.9107,823.07273)"
+       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 h -0.71428 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.92;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>
+</svg>
diff -pruN 1.28.1-1/doc/user/svg/tablet-eraser-invert.svg 1.30.0-1/doc/user/svg/tablet-eraser-invert.svg
--- 1.28.1-1/doc/user/svg/tablet-eraser-invert.svg	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/doc/user/svg/tablet-eraser-invert.svg	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="104.05109mm"
+   height="64.88131mm"
+   viewBox="0 0 368.68495 229.89441"
+   id="svg4321"
+   version="1.1"
+   inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
+   sodipodi:docname="tablet-eraser-invert.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:dc="http://purl.org/dc/elements/1.1/">
+  <defs
+     id="defs4323">
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4294"
+       id="linearGradient4300"
+       x1="465.81339"
+       y1="666.13727"
+       x2="454.82117"
+       y2="658.65521"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4294">
+      <stop
+         style="stop-color:#1a1a1a;stop-opacity:1;"
+         offset="0"
+         id="stop4296" />
+      <stop
+         style="stop-color:#808080;stop-opacity:1"
+         offset="1"
+         id="stop4298" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4294"
+       id="linearGradient5721"
+       gradientUnits="userSpaceOnUse"
+       x1="465.81339"
+       y1="666.13727"
+       x2="454.82117"
+       y2="658.65521" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.8"
+     inkscape:cx="330.71429"
+     inkscape:cy="144.28571"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="3072"
+     inkscape:window-height="1659"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:showpageshadow="2"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#d1d1d1" />
+  <metadata
+     id="metadata4326">
+    <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>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-67.741956,-463.35919)">
+    <g
+       transform="matrix(0.35596319,-0.1450925,0.14027908,0.34415419,-98.897559,452.82709)"
+       id="g4304"
+       style="display:inline">
+      <path
+         style="display:inline;fill:#cccccc;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 387.83544,799.76093 c -1.1128,3.61694 -3.2211,13.05163 -1.08543,14.07769 2.13567,1.02606 7.81039,-3.72162 10.99756,-6.69095 z"
+         id="path4286"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="czcc" />
+      <path
+         style="display:inline;fill:#000000"
+         d="m 392.64431,804.79039 c -8.52094,-5.90399 -8.49394,-11.01546 0.22879,-43.30647 1.03999,-3.85 2.46829,-9.67602 3.17399,-12.9467 0.99731,-4.62219 2.39455,-7.29497 6.27321,-12 2.74456,-3.32932 5.25157,-6.2783 5.57113,-6.5533 40.78433,-60.97488 80.48307,-125.1652 118.27253,-184 9.86283,-15.675 26.59424,-42.225 37.18089,-59 10.58666,-16.775 34.01422,-53.9 52.06125,-82.5 18.04703,-28.6 35.04505,-55.31677 37.77338,-59.37059 l 4.9606,-7.3706 4.1828,0.57332 c 4.16371,0.5707 4.19706,0.54958 7.30887,-4.62941 3.75631,-6.2516 8.82067,-11.57582 12.2516,-12.88026 5.99391,-2.27888 14.03303,2.9506 14.03303,9.12854 0,3.90203 -2.51704,10.62127 -6.02878,16.09385 -1.63417,2.54664 -2.97122,4.85949 -2.97122,5.13969 0,0.28019 0.9,1.54715 2,2.81546 2.28453,2.63408 2.47267,4.21918 0.86833,7.31574 -1.28218,2.47476 -26.61383,45.18798 -55.85724,94.18426 -10.83283,18.15 -25.72943,43.1137 -33.10357,55.47489 -7.37413,12.3612 -13.69273,23.17153 -14.04131,24.02297 -0.34859,0.85144 -7.50972,12.78774 -15.91363,26.52511 -15.54138,25.40455 -32.24417,52.9052 -70.74345,116.47703 -40.26028,66.47968 -43.66308,72.46026 -49.21634,86.5 -1.74036,4.4 -3.92035,8.675 -4.8444,9.5 -0.92405,0.825 -4.36246,3.75 -7.6409,6.5 -3.27845,2.75 -9.57132,8.3067 -13.98415,12.34823 -10.62726,9.73304 -16.99729,13.87361 -22.52334,14.64034 -3.99187,0.55386 -5.03885,0.251 -9.27207,-2.6821 z"
+         id="path4283"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="ssssccssscsssssssssssssssssss" />
+      <path
+         style="fill:url(#linearGradient4300);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 450.89044,688.88586 c 8.71518,5.62513 45.74035,-59.18436 43.57923,-75.43494 l -7.07107,-6.56599 c -29.93081,25.86352 -47.78438,74.72281 -47.78438,74.72281 z"
+         id="path4292"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="scccs" />
+    </g>
+    <rect
+       transform="scale(1,-1)"
+       y="-693.18524"
+       x="67.81031"
+       height="16.036251"
+       width="168.54825"
+       id="rect4136"
+       style="fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.136709;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:0.546834, 0.136709;stroke-dashoffset:0;stroke-opacity:1" />
+    <g
+       transform="matrix(-0.36104723,0.13193379,-0.12755691,-0.34906957,644.73068,698.04111)"
+       id="g5701"
+       style="display:inline">
+      <path
+         style="display:inline;fill:#cccccc;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 387.83544,799.76093 c -1.1128,3.61694 -3.2211,13.05163 -1.08543,14.07769 2.13567,1.02606 7.81039,-3.72162 10.99756,-6.69095 z"
+         id="path5703"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="czcc" />
+      <path
+         style="display:inline;fill:#000000"
+         d="m 392.64431,804.79039 c -8.52094,-5.90399 -8.49394,-11.01546 0.22879,-43.30647 1.03999,-3.85 2.46829,-9.67602 3.17399,-12.9467 0.99731,-4.62219 2.39455,-7.29497 6.27321,-12 2.74456,-3.32932 5.25157,-6.2783 5.57113,-6.5533 40.78433,-60.97488 80.48307,-125.1652 118.27253,-184 9.86283,-15.675 26.59424,-42.225 37.18089,-59 10.58666,-16.775 34.01422,-53.9 52.06125,-82.5 18.04703,-28.6 35.04505,-55.31677 37.77338,-59.37059 l 4.9606,-7.3706 4.1828,0.57332 c 4.16371,0.5707 4.19706,0.54958 7.30887,-4.62941 3.75631,-6.2516 8.82067,-11.57582 12.2516,-12.88026 5.99391,-2.27888 14.03303,2.9506 14.03303,9.12854 0,3.90203 -2.51704,10.62127 -6.02878,16.09385 -1.63417,2.54664 -2.97122,4.85949 -2.97122,5.13969 0,0.28019 0.9,1.54715 2,2.81546 2.28453,2.63408 2.47267,4.21918 0.86833,7.31574 -1.28218,2.47476 -26.61383,45.18798 -55.85724,94.18426 -10.83283,18.15 -25.72943,43.1137 -33.10357,55.47489 -7.37413,12.3612 -13.69273,23.17153 -14.04131,24.02297 -0.34859,0.85144 -7.50972,12.78774 -15.91363,26.52511 -15.54138,25.40455 -32.24417,52.9052 -70.74345,116.47703 -40.26028,66.47968 -43.66308,72.46026 -49.21634,86.5 -1.74036,4.4 -3.92035,8.675 -4.8444,9.5 -0.92405,0.825 -4.36246,3.75 -7.6409,6.5 -3.27845,2.75 -9.57132,8.3067 -13.98415,12.34823 -10.62726,9.73304 -16.99729,13.87361 -22.52334,14.64034 -3.99187,0.55386 -5.03885,0.251 -9.27207,-2.6821 z"
+         id="path5705"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="ssssccssscsssssssssssssssssss" />
+      <path
+         style="fill:url(#linearGradient5721);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 450.89044,688.88586 c 8.71518,5.62513 45.74035,-59.18436 43.57923,-75.43494 l -7.07107,-6.56599 c -29.93081,25.86352 -47.78438,74.72281 -47.78438,74.72281 z"
+         id="path5707"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="scccs" />
+    </g>
+    <rect
+       transform="scale(1,-1)"
+       y="-693.18524"
+       x="267.8103"
+       height="16.036251"
+       width="168.54825"
+       id="rect5709"
+       style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.136709;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:0.546834, 0.136709;stroke-dashoffset:0;stroke-opacity:1" />
+    <text
+       id="text5713"
+       y="559.10828"
+       x="296.20877"
+       style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;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="559.10828"
+         x="296.20877"
+         id="tspan5715"
+         sodipodi:role="line"
+         style="font-size:15px;line-height:1.25;font-family:sans-serif">Eraser</tspan></text>
+    <text
+       id="text1"
+       y="559.10828"
+       x="109.0436"
+       style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;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="559.10828"
+         x="109.0436"
+         id="tspan1"
+         sodipodi:role="line"
+         style="font-size:15px;line-height:1.25;font-family:sans-serif">Pen</tspan></text>
+  </g>
+</svg>
diff -pruN 1.28.1-1/doc/user/tablet-support.rst 1.30.0-1/doc/user/tablet-support.rst
--- 1.28.1-1/doc/user/tablet-support.rst	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/doc/user/tablet-support.rst	2025-11-25 03:40:43.000000000 +0000
@@ -495,3 +495,50 @@ maximum provided in this call.
 
 The size of the tablet reported by **libinput_device_get_size()** always reflects
 the physical area, not the logical area.
+
+.. _tablet-eraser-button:
+
+------------------------------------------------------------------------------
+Tablet eraser buttons
+------------------------------------------------------------------------------
+
+Tablet tools come in a variety of forms but the most common one is a
+pen-like tool. Some of these pen-like tools have a virtual eraser at the
+tip of the tool - inverting the tool brings the eraser into proximity.
+
+.. figure:: tablet-eraser-invert.svg
+    :align: center
+
+    An pen-like tool used as pen and as eraser by inverting it
+
+Having an eraser as a separate tool is beneficial in many applications as the
+eraser tool can be assigned different functionality (colors, paint tools, etc.)
+that is easily available.
+
+However, a large proportion of tablet pens have an "eraser button". By
+pressing the button the pen switches to be an eraser tool.
+On the data level this is not done via a button event, instead the firmware
+will pretend the pen tool going out of proximity and the eraser coming
+into proximity immediately after - as if the tool was physically inverted.
+
+.. figure:: tablet-eraser-button.svg
+    :align: center
+
+    An pen-like tool used as pen and as eraser by pressing the eraser button
+
+Microsoft mandates this behavior (see
+`Windows Pen States <https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states>`_
+for details) and thus the overwhelming majority of devices will have
+an eraser button that virtually inverts the pen.
+
+Enforcing an eraser button means that users have one button less on the
+stylus that they would have otherwise. For some users the eraser button
+is in an inconvenient location, others don't want an eraser button at all.
+
+libinput provides an eraser button configuration that allows disabling the
+eraser button and turning it into a normal button event. If the eraser button
+is disabled, pressing that button will generate a normal tablet tool button
+event.
+
+This configuration is only available on pens with an eraser button, not on
+with an invert-type eraser.
diff -pruN 1.28.1-1/meson.build 1.30.0-1/meson.build
--- 1.28.1-1/meson.build	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/meson.build	2025-11-25 03:40:43.000000000 +0000
@@ -1,8 +1,8 @@
 project('libinput', 'c',
-	version : '1.28.1',
+	version : '1.30.0',
 	license : 'MIT/Expat',
 	default_options : [ 'c_std=gnu99', 'warning_level=2' ],
-	meson_version : '>= 0.56.0')
+	meson_version : '>= 0.64.0')
 
 libinput_version = meson.project_version().split('.')
 
@@ -47,6 +47,7 @@ libinput_so_version = '@0@.@1@.@2@'.form
 # Compiler setup
 cc = meson.get_compiler('c')
 cflags = [
+	'-Wno-error=deprecated-declarations',
 	'-Wno-unused-parameter',
 	'-Wmissing-prototypes',
 	'-Wstrict-prototypes',
@@ -134,6 +135,15 @@ if cc.has_header('xlocale.h')
 endif
 
 code = '''
+#include <time.h>
+
+void func() { auto foo = gmtime(NULL); foo->tm_sec = 0; }
+'''
+if cc.compiles(code, name: 'has C23 auto keyword')
+	config_h.set('HAVE_C23_AUTO', '1')
+endif
+
+code = '''
 #include <locale.h>
 void main(void) { newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); }
 '''
@@ -146,29 +156,63 @@ if not cc.has_header_symbol('sys/ptrace.
 	config_h.set('PTRACE_CONT', 'PT_CONTINUE')
 	config_h.set('PTRACE_DETACH', 'PT_DETACH')
 endif
-
-config_h.set10('HAVE_INSTALLED_TESTS', get_option('install-tests'))
+if get_option('install-tests')
+	config_h.set('HAVE_INSTALLED_TESTS', 1)
+endif
 
 # Dependencies
 pkgconfig = import('pkgconfig')
 dep_udev = dependency('libudev')
-dep_mtdev = dependency('mtdev', version : '>= 1.1.0')
-dep_libevdev = dependency('libevdev')
-config_h.set10('HAVE_LIBEVDEV_DISABLE_PROPERTY',
-		dep_libevdev.version().version_compare('>= 1.9.902'))
+dep_libevdev = dependency('libevdev', version: '>= 1.10.0')
 
 dep_lm = cc.find_library('m', required : false)
 dep_rt = cc.find_library('rt', required : false)
 
+dep_lua = dependency('lua-5.4', 'lua5.4', 'lua',
+		     version : '>= 5.4',
+		     required : get_option('lua-plugins'))
+have_lua = dep_lua.found()
+if have_lua
+	config_h.set('HAVE_LUA', 1)
+endif
+
+have_plugins =  dep_lua.found()
+if have_plugins
+	config_h.set('HAVE_PLUGINS', 1)
+endif
+
+autoload_plugins = get_option('autoload-plugins')
+if autoload_plugins
+	config_h.set('AUTOLOAD_PLUGINS', 1)
+endif
+
+summary({
+	'Plugins enabled' : have_plugins,
+	'Autoload plugins' : autoload_plugins,
+	'Lua Plugin support' : have_lua,
+	},
+	section : 'Plugins',
+	bool_yn : true)
+
 # Include directories
 includes_include = include_directories('include')
 includes_src = include_directories('src')
 
+############ mtdev configuration ############
+
+have_mtdev = get_option('mtdev')
+if have_mtdev
+	config_h.set('HAVE_MTDEV', 1)
+	dep_mtdev = dependency('mtdev', version : '>= 1.1.0')
+else
+	dep_mtdev = declare_dependency()
+endif
+
 ############ libwacom configuration ############
 
 have_libwacom = get_option('libwacom')
-config_h.set10('HAVE_LIBWACOM', have_libwacom)
 if have_libwacom
+	config_h.set('HAVE_LIBWACOM', 1)
 	dep_libwacom = dependency('libwacom', version : '>= 0.27')
 	if cc.has_header_symbol('libwacom/libwacom.h', 'WACOM_BUTTON_DIAL_MODESWITCH',
 				dependencies : dep_libwacom)
@@ -189,6 +233,7 @@ executable('libinput-device-group',
 	   dependencies : [dep_udev, dep_libwacom],
 	   include_directories : [includes_src, includes_include],
 	   install : true,
+	   install_tag : 'runtime',
 	   install_dir : dir_udev_callouts)
 executable('libinput-fuzz-extract',
 	   'udev/libinput-fuzz-extract.c',
@@ -197,16 +242,18 @@ executable('libinput-fuzz-extract',
 	   dependencies : [dep_udev, dep_libevdev, dep_lm],
 	   include_directories : [includes_src, includes_include],
 	   install : true,
+	   install_tag : 'runtime',
 	   install_dir : dir_udev_callouts)
 executable('libinput-fuzz-to-zero',
 	   'udev/libinput-fuzz-to-zero.c',
 	   dependencies : [dep_udev, dep_libevdev],
 	   include_directories : [includes_src, includes_include],
 	   install : true,
+	   install_tag : 'runtime',
 	   install_dir : dir_udev_callouts)
 
 udev_rules_config = configuration_data()
-udev_rules_config.set('UDEV_TEST_PATH', '')
+udev_rules_config.set('UDEV_TEST_PATH', dir_udev_callouts + '/')
 configure_file(input : 'udev/80-libinput-device-groups.rules.in',
 	       output : '80-libinput-device-groups.rules',
 	       install_dir : dir_udev_rules,
@@ -287,16 +334,18 @@ util_headers = [
 foreach h: util_headers
 	c = configuration_data()
 	c.set_quoted('FILE', h)
-	testfile = configure_file(input : 'test/test-util-includes.c',
+	testfile = configure_file(input : 'test/test-util-includes.c.in',
 			          output : 'test-util-includes-@0@.c'.format(h),
 				  configuration : c)
 	executable('test-build-@0@'.format(h),
 		   testfile,
 		   include_directories : [includes_src, includes_include],
+		   dependencies: [dep_libevdev],
 		   install : false)
 endforeach
 
 src_libinput_util = [
+	'src/util-files.c',
 	'src/util-list.c',
 	'src/util-ratelimit.c',
 	'src/util-strings.c',
@@ -323,7 +372,7 @@ src_libfilter = [
 		'src/filter-trackpoint-flat.c',
 ]
 libfilter = static_library('filter', src_libfilter,
-			   dependencies : [dep_udev, dep_libwacom],
+			   dependencies : [dep_udev, dep_libwacom, dep_libevdev],
 			   include_directories : includes_include)
 dep_libfilter = declare_dependency(link_with : libfilter)
 
@@ -350,22 +399,31 @@ libquirks = static_library('quirks', src
 dep_libquirks = declare_dependency(link_with : libquirks)
 
 # Create /etc/libinput
-if meson.version().version_compare('>= 0.60')
-	install_emptydir(dir_etc / 'libinput')
-else
-	install_subdir('libinput', install_dir : dir_etc)
-endif
+install_emptydir(dir_etc / 'libinput')
 
 ############ libinput.so ############
-config_h.set10('EVENT_DEBUGGING', get_option('internal-event-debugging'))
+if get_option('internal-event-debugging')
+	config_h.set('EVENT_DEBUGGING', 1)
+endif
+
+config_h.set_quoted('LIBINPUT_PLUGIN_LIBDIR', dir_lib / 'libinput' / 'plugins')
+config_h.set_quoted('LIBINPUT_PLUGIN_ETCDIR', dir_etc / 'libinput' / 'plugins')
 
 install_headers('src/libinput.h')
 src_libinput = src_libfilter + [
 	'src/libinput.c',
+	'src/libinput-plugin.c',
+	'src/libinput-plugin-button-debounce.c',
+	'src/libinput-plugin-mouse-wheel.c',
+	'src/libinput-plugin-mouse-wheel-lowres.c',
+	'src/libinput-plugin-tablet-double-tool.c',
+	'src/libinput-plugin-tablet-eraser-button.c',
+	'src/libinput-plugin-tablet-forced-tool.c',
+	'src/libinput-plugin-tablet-proximity-timer.c',
 	'src/libinput-private-config.c',
 	'src/evdev.c',
-	'src/evdev-debounce.c',
 	'src/evdev-fallback.c',
+	'src/evdev-plugin.c',
 	'src/evdev-totem.c',
 	'src/evdev-middle-button.c',
 	'src/evdev-mt-touchpad.c',
@@ -377,13 +435,22 @@ src_libinput = src_libfilter + [
 	'src/evdev-tablet.c',
 	'src/evdev-tablet-pad.c',
 	'src/evdev-tablet-pad-leds.c',
-	'src/evdev-wheel.c',
 	'src/path-seat.c',
 	'src/udev-seat.c',
 	'src/timer.c',
 	'src/util-libinput.c',
 ]
 
+if have_mtdev
+	src_libinput += ['src/libinput-plugin-mtdev.c']
+endif
+
+if dep_lua.found()
+	src_libinput += [
+		'src/libinput-plugin-lua.c',
+	]
+endif
+
 deps_libinput = [
 	dep_mtdev,
 	dep_udev,
@@ -393,7 +460,8 @@ deps_libinput = [
 	dep_rt,
 	dep_libwacom,
 	dep_libinput_util,
-	dep_libquirks
+	dep_libquirks,
+	dep_lua,
 ]
 
 libinput_version_h_config = configuration_data()
@@ -424,16 +492,18 @@ dep_libinput = declare_dependency(
 		link_with : lib_libinput,
 		dependencies : deps_libinput)
 
-if meson.version().version_compare('>= 0.54.0')
-	meson.override_dependency('libinput', dep_libinput)
-endif
+meson.override_dependency('libinput', dep_libinput)
 
 pkgconfig.generate(
 	filebase : 'libinput',
 	name : 'Libinput',
 	description : 'Input device library',
 	version : meson.project_version(),
-	libraries : lib_libinput
+	libraries : lib_libinput,
+	requires_private : dep_udev,
+	variables : [
+		'plugindir=${libdir}/libinput/plugins'
+	]
 )
 
 git_version_h = vcs_tag(command : ['git', 'describe'],
@@ -441,6 +511,8 @@ git_version_h = vcs_tag(command : ['git'
 			input : 'src/libinput-git-version.h.in',
 			output :'libinput-git-version.h')
 
+subdir('plugins')
+
 ############ documentation ############
 
 if get_option('documentation')
@@ -486,6 +558,7 @@ executable('libinput-debug-events',
 	   dependencies : deps_tools,
 	   include_directories : [includes_src, includes_include],
 	   install_dir : libinput_tool_path,
+	   install_tag : 'bin',
 	   install : true
 	   )
 
@@ -495,8 +568,17 @@ executable('libinput-debug-tablet',
 	   dependencies : deps_tools,
 	   include_directories : [includes_src, includes_include],
 	   install_dir : libinput_tool_path,
+	   install_tag : 'bin',
 	   install : true)
 
+libinput_debug_tablet_pad_sources = [ 'tools/libinput-debug-tablet-pad.c' ]
+executable('libinput-debug-tablet-pad',
+	   libinput_debug_tablet_pad_sources,
+	   dependencies : deps_tools,
+	   include_directories : [includes_src, includes_include],
+	   install_dir : libinput_tool_path,
+	   install_tag : 'bin',
+	   install : true)
 
 libinput_quirks_sources = [ 'tools/libinput-quirks.c' ]
 libinput_quirks = executable('libinput-quirks',
@@ -504,6 +586,7 @@ libinput_quirks = executable('libinput-q
 			     dependencies : [dep_libquirks, dep_tools_shared, dep_libinput],
 			     include_directories : [includes_src, includes_include],
 			     install_dir : libinput_tool_path,
+			     install_tag : 'bin',
 			     install : true
 			    )
 test('validate-quirks',
@@ -525,6 +608,7 @@ libinput_list_devices = executable('libi
 				   dependencies : deps_tools,
 				   include_directories : [includes_src, includes_include],
 				   install_dir : libinput_tool_path,
+				   install_tag : 'bin',
 				   install : true,
 				  )
 test('list-devices',
@@ -537,6 +621,7 @@ executable('libinput-measure',
 	   dependencies : deps_tools,
 	   include_directories : [includes_src, includes_include],
 	   install_dir : libinput_tool_path,
+	   install_tag : 'bin',
 	   install : true,
 	   )
 
@@ -546,21 +631,22 @@ executable('libinput-analyze',
 	   dependencies : deps_tools,
 	   include_directories : [includes_src, includes_include],
 	   install_dir : libinput_tool_path,
+	   install_tag : 'bin',
 	   install : true,
 	   )
 
 src_python_tools = files(
-	      'tools/libinput-analyze-buttons.py',
-	      'tools/libinput-analyze-per-slot-delta.py',
-	      'tools/libinput-analyze-recording.py',
-	      'tools/libinput-analyze-touch-down-state.py',
-	      'tools/libinput-list-kernel-devices.py',
-	      'tools/libinput-measure-fuzz.py',
-	      'tools/libinput-measure-touchpad-size.py',
-	      'tools/libinput-measure-touchpad-tap.py',
-	      'tools/libinput-measure-touchpad-pressure.py',
-	      'tools/libinput-measure-touch-size.py',
-	      'tools/libinput-replay.py'
+	'tools/libinput-analyze-buttons.py',
+	'tools/libinput-analyze-per-slot-delta.py',
+	'tools/libinput-analyze-recording.py',
+	'tools/libinput-analyze-touch-down-state.py',
+	'tools/libinput-list-kernel-devices.py',
+	'tools/libinput-measure-fuzz.py',
+	'tools/libinput-measure-touchpad-size.py',
+	'tools/libinput-measure-touchpad-tap.py',
+	'tools/libinput-measure-touchpad-pressure.py',
+	'tools/libinput-measure-touch-size.py',
+	'tools/libinput-replay.py'
 )
 
 foreach t : src_python_tools
@@ -577,23 +663,23 @@ executable('libinput-record',
 	   dependencies : deps_tools + [dep_udev],
 	   include_directories : [includes_src, includes_include],
 	   install_dir : libinput_tool_path,
+	   install_tag : 'bin',
 	   install : true,
 	   )
 
-config_h.set10('HAVE_DEBUG_GUI', get_option('debug-gui'))
 if get_option('debug-gui')
+	config_h.set('HAVE_DEBUG_GUI', 1)
 	dep_gtk = dependency('gtk4', version : '>= 4.0', required : false)
-	config_h.set10('HAVE_GTK4', dep_gtk.found())
-	if not dep_gtk.found()
+	if dep_gtk.found()
+		config_h.set('HAVE_GTK4', 1)
+	else
 		dep_gtk = dependency('gtk+-3.0', version : '>= 3.20')
-		config_h.set10('HAVE_GTK3', dep_gtk.found())
+		if dep_gtk.found()
+			config_h.set('HAVE_GTK3', 1)
+		endif
 	endif
 
-	if meson.version().version_compare('>= 0.58')
-		gtk_targets = dep_gtk.get_variable('targets')
-	else
-		gtk_targets = dep_gtk.get_pkgconfig_variable('targets')
-	endif
+	gtk_targets = dep_gtk.get_variable('targets')
 
 	have_gtk_wayland = gtk_targets.contains('wayland')
 	have_gtk_x11 = gtk_targets.contains('x11')
@@ -604,18 +690,15 @@ if get_option('debug-gui')
 	dep_wayland_client = dependency('wayland-client', required : false)
 	dep_wayland_protocols = dependency('wayland-protocols', required : false)
 
-	config_h.set10('HAVE_GTK_X11', have_gtk_x11 and dep_x11.found())
-	config_h.set10('HAVE_GTK_WAYLAND', false)
+	if have_gtk_x11 and dep_x11.found()
+		config_h.set('HAVE_GTK_X11', 1)
+	endif
 
 	debug_gui_sources = [ 'tools/libinput-debug-gui.c' ]
 
 	if have_gtk_wayland and dep_wayland_client.found() and dep_wayland_protocols.found()
 		wayland_scanner = find_program('wayland-scanner')
-		if meson.version().version_compare('>= 0.58')
-			wlproto_dir = dep_wayland_protocols.get_variable('pkgdatadir')
-		else
-			wlproto_dir = dep_wayland_protocols.get_pkgconfig_variable('pkgdatadir')
-		endif
+		wlproto_dir = dep_wayland_protocols.get_variable('pkgdatadir')
 
 		proto_name = 'pointer-constraints-unstable-v1'
 		input = files(wlproto_dir / 'unstable' / 'pointer-constraints' / '@0@.xml'.format(proto_name))
@@ -633,7 +716,7 @@ if get_option('debug-gui')
 		)
 
 		debug_gui_sources += [ wayland_headers, wayland_sources ]
-		config_h.set10('HAVE_GTK_WAYLAND', true)
+		config_h.set('HAVE_GTK_WAYLAND', 1)
 	endif
 
 	deps_debug_gui = [
@@ -649,6 +732,7 @@ if get_option('debug-gui')
 		   dependencies : deps_debug_gui,
 		   include_directories : [includes_src, includes_include],
 		   install_dir : libinput_tool_path,
+		   install_tag : 'bin',
 		   install : true
 		   )
 	src_man += files('tools/libinput-debug-gui.man')
@@ -660,6 +744,7 @@ libinput_tool = executable('libinput',
 			   libinput_sources,
 			   dependencies : deps_tools,
 			   include_directories : [includes_src, includes_include],
+			   install_tag : 'bin',
 			   install : true
 			  )
 
@@ -750,6 +835,7 @@ executable('libinput-test',
 	   dependencies : deps_tools,
 	   include_directories : [includes_src, includes_include],
 	   install_dir : libinput_tool_path,
+	   install_tag : 'tests',
 	   install : true,
 	   )
 
@@ -759,11 +845,15 @@ if get_option('tests')
 	dep_check = dependency('check', version : '>= 0.9.10', required: false)
 
 	gstack = find_program('gstack', required : false)
-	config_h.set10('HAVE_GSTACK', gstack.found())
+	if gstack.found()
+		config_h.set('HAVE_GSTACK', 1)
+	endif
 
 	# for inhibit support during test run
 	dep_libsystemd = dependency('libsystemd', version : '>= 221', required : false)
-	config_h.set10('HAVE_LIBSYSTEMD', dep_libsystemd.found())
+	if dep_libsystemd.found()
+		config_h.set('HAVE_LIBSYSTEMD', 1)
+	endif
 
 	litest_sources = [
 		'src/libinput-private-config.c',
@@ -810,13 +900,16 @@ if get_option('tests')
 		'test/litest-device-magic-trackpad.c',
 		'test/litest-device-mouse.c',
 		'test/litest-device-mouse-wheel-tilt.c',
+		'test/litest-device-mouse-wheel-hires-disabled.c',
+		'test/litest-device-mouse-ps2.c',
 		'test/litest-device-mouse-roccat.c',
 		'test/litest-device-mouse-low-dpi.c',
+		'test/litest-device-mouse-virtual.c',
 		'test/litest-device-mouse-wheel-click-angle.c',
 		'test/litest-device-mouse-wheel-click-count.c',
 		'test/litest-device-ms-nano-transceiver-mouse.c',
 		'test/litest-device-ms-surface-cover.c',
-		'test/litest-device-protocol-a-touch-screen.c',
+		'test/litest-device-ploopy-pavonis-stylus.c',
 		'test/litest-device-qemu-usb-tablet.c',
 		'test/litest-device-sony-vaio-keys.c',
 		'test/litest-device-synaptics-x220.c',
@@ -871,8 +964,15 @@ if get_option('tests')
 		'test/litest-device-yubikey.c',
 		'test/litest-runner.c',
 		'test/litest.c',
+		'test/litest-main.c',
 	]
 
+	if have_mtdev
+		litest_sources += [
+			'test/litest-device-protocol-a-touch-screen.c',
+		]
+	endif
+
 	dep_dl = cc.find_library('dl')
 	deps_litest = [
 		dep_libinput,
@@ -893,11 +993,9 @@ if get_option('tests')
 				   meson.current_build_dir() /
 				   '90-libinput-fuzz-override-litest.rules')
 
-	def_no_main = '-DLITEST_NO_MAIN'
 	if dep_check.found()
 		def_disable_backtrace = '-DLITEST_DISABLE_BACKTRACE_LOGGING'
 		defs_litest_selftest = [
-			def_no_main,
 			def_disable_backtrace,
 			'-Wno-unused',
 		]
@@ -935,7 +1033,6 @@ if get_option('tests')
 	test_utils = executable('libinput-test-utils',
 				test_utils_sources,
 				include_directories : [includes_src, includes_include],
-				c_args : [def_no_main],
 				dependencies : deps_litest,
 				install_dir : libinput_tool_path,
 				install : get_option('install-tests'))
@@ -964,6 +1061,10 @@ if get_option('tests')
 		'test/test-switch.c',
 		'test/test-quirks.c',
 	]
+	if have_plugins and have_lua
+		tests_sources += ['test/test-plugins-lua.c']
+	endif
+
 	libinput_test_runner_sources = litest_sources + tests_sources
 	libinput_test_runner = executable('libinput-test-suite',
 					  libinput_test_runner_sources,
@@ -988,7 +1089,10 @@ if get_option('tests')
 		'quirks',
 		'switch',
 		'tablet',
+		'tablet_eraser',
 		'tablet_left_handed',
+		'tablet_proximity',
+		'tablet_tip',
 		'totem',
 		'touch',
 		'touchpad',
@@ -1002,6 +1106,9 @@ if get_option('tests')
 		'trackpoint',
 		'udev',
 	]
+	if have_plugins and have_lua
+		collections += ['lua']
+	endif
 
 	foreach group : collections
 		test('libinput-test-suite-@0@'.format(group),
@@ -1009,7 +1116,7 @@ if get_option('tests')
 		     suite : ['all', 'valgrind', 'root', 'hardware'],
 		     args : ['--filter-group=@0@'.format(group)],
 		     is_parallel : false,
-		     timeout : 1200)
+		     timeout : 1100)
         endforeach
 
 	test('libinput-test-deviceless',
@@ -1029,7 +1136,7 @@ if get_option('tests')
 						'--error-exitcode=3',
 						'--suppressions=' + valgrind_suppressions_file ],
 				env :  valgrind_env,
-				timeout_multiplier : 100)
+				timeout_multiplier : 3)
 	else
 		message('valgrind not found, disabling valgrind test suite')
 	endif
@@ -1062,6 +1169,7 @@ src_man += files(
 	'tools/libinput-analyze-touch-down-state.man',
 	'tools/libinput-debug-events.man',
 	'tools/libinput-debug-tablet.man',
+	'tools/libinput-debug-tablet-pad.man',
 	'tools/libinput-list-devices.man',
 	'tools/libinput-list-kernel-devices.man',
 	'tools/libinput-measure.man',
diff -pruN 1.28.1-1/meson_options.txt 1.30.0-1/meson_options.txt
--- 1.28.1-1/meson_options.txt	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/meson_options.txt	2025-11-25 03:40:43.000000000 +0000
@@ -10,6 +10,10 @@ option('libwacom',
        type: 'boolean',
        value: true,
        description: 'Use libwacom for tablet identification (default=true)')
+option('mtdev',
+       type: 'boolean',
+       value: true,
+       description: 'Use mtdev for multitouch protocol A devices (default=true)')
 option('debug-gui',
        type: 'boolean',
        value: true,
@@ -38,3 +42,11 @@ option('internal-event-debugging',
 	type: 'boolean',
 	value: false,
 	description: 'Enable additional internal event debug tracing. This will print key values to the logs and thus must never be enabled in a release build')
+option('autoload-plugins',
+	type: 'boolean',
+	value: false,
+	description: 'Always load plugins from default plugin paths (only if the caller does not do so)')
+option('lua-plugins',
+	type: 'feature',
+	value: 'auto',
+	description: 'Enable support for Lua plugins')
diff -pruN 1.28.1-1/plugins/10-copilot-key-override.lua 1.30.0-1/plugins/10-copilot-key-override.lua
--- 1.28.1-1/plugins/10-copilot-key-override.lua	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/plugins/10-copilot-key-override.lua	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,71 @@
+-- SPDX-License-Identifier: MIT
+--
+-- This is an example libinput plugin
+--
+-- This plugin detects the Copilot key on the keyboard with
+-- the given VID/PID and replaces it with a different key (sequence).
+
+-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
+-- libinput:register({1})
+
+-- Replace this with your keyboard's VID/PID
+KEYBOARD_VID = 0x046d
+KEYBOARD_PID = 0x4088
+
+meta_is_down = false
+shift_is_down = false
+
+-- shift-A, because you can never have enough screaming
+replacement_sequence = { evdev.KEY_LEFTSHIFT, evdev.KEY_A }
+
+function frame(device, frame, _)
+    for _, v in ipairs(frame) do
+        if v.value ~= 2 then -- ignore key repeats
+            if v.usage == evdev.KEY_LEFTMETA then
+                meta_is_down = v.value == 1
+            elseif v.usage == evdev.KEY_LEFTSHIFT then
+                shift_is_down = v.value == 1
+            elseif v.usage == evdev.KEY_F23 and meta_is_down and shift_is_down then
+                -- We know from the MS requirements that F23 for copilot is
+                -- either last key (on press) or the first key (on release)
+                -- of the three-key sequence, and no other keys are
+                -- within this frame.
+                if v.value == 1 then
+                    -- Release our modifiers first
+                    device:prepend_frame({
+                        { usage = evdev.KEY_LEFTSHIFT, value = 0 },
+                        { usage = evdev.KEY_LEFTMETA, value = 0 },
+                    })
+                    -- Insert our replacement press sequence
+                    local replacement_frame = {}
+                    for _, rv in ipairs(replacement_sequence) do
+                        table.insert(replacement_frame, { usage = rv, value = 1 })
+                    end
+                    device:append_frame(replacement_frame)
+                else
+                    -- Insert our replacement release sequence
+                    local replacement_frame = {}
+                    for idx = #replacement_sequence, 1, -1 do
+                        table.insert(replacement_frame, { usage = replacement_sequence[idx], value = 0 })
+                    end
+                    device:append_frame(replacement_frame)
+
+                    -- we don't care about re-pressing shift/meta because the
+                    -- rest of the stack will filter the release for an
+                    -- unpressed key anyway.
+                end
+
+                return {} -- discard this frame
+            end
+        end
+    end
+end
+
+function device_new(device)
+    local info = device:info()
+    if  info.vid == KEYBOARD_VID and info.pid == KEYBOARD_PID then
+        device:connect("evdev-frame", frame)
+    end
+end
+
+libinput:connect("new-evdev-device", device_new)
diff -pruN 1.28.1-1/plugins/10-delay-motion.lua 1.30.0-1/plugins/10-delay-motion.lua
--- 1.28.1-1/plugins/10-delay-motion.lua	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/plugins/10-delay-motion.lua	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,64 @@
+-- SPDX-License-Identifier: MIT
+--
+-- This is an example libinput plugin
+--
+-- This plugin delays any event with relative motion by the given DELAY
+-- by storing it in a table and replaying it via a timer callback later.
+
+-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
+-- libinput:register({1})
+
+DELAY = 1500 * 1000 -- 1.5s
+next_timer_expiry = 0
+devices = {}
+
+function timer_expired(time_in_microseconds)
+    next_timer_expiry = 0
+    for device, frames in pairs(devices) do
+        while #frames > 0 and frames[1].time <= time_in_microseconds do
+            --- we don't have a current frame so it doesn't matter
+            --- whether we prepend or append
+            device:prepend_frame(frames[1].frame)
+            table.remove(frames, 1)
+        end
+        local next_frame = frames[1]
+        if next_frame and (next_timer_expiry == 0 or next_frame.time < next_timer_expiry) then
+            next_timer_expiry = next_frame.time
+        end
+    end
+    if next_timer_expiry ~= 0 then
+        libinput:timer_set_absolute(next_timer_expiry)
+    end
+end
+
+function frame(device, frame, timestamp)
+    for _, v in ipairs(frame) do
+        if v.usage == evdev.REL_X or v.usage == evdev.REL_Y then
+            local next_time = timestamp + DELAY
+            table.insert(devices[device], {
+                time = next_time,
+                frame = frame
+            })
+            if next_timer_expiry == 0 then
+                next_timer_expiry = next_time
+                libinput:timer_set_absolute(next_timer_expiry)
+            end
+            return {} -- discard frame
+        end
+    end
+    return nil
+end
+
+function device_new(device)
+    local usages = device:usages()
+    if usages[evdev.REL_X] then
+        devices[device] = {}
+        device:connect("evdev-frame", frame)
+        device:connect("device-removed", function(dev)
+            devices[dev] = nil
+        end)
+    end
+end
+
+libinput:connect("new-evdev-device", device_new)
+libinput:connect("timer-expired", timer_expired)
diff -pruN 1.28.1-1/plugins/10-disable-feature.lua 1.30.0-1/plugins/10-disable-feature.lua
--- 1.28.1-1/plugins/10-disable-feature.lua	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/plugins/10-disable-feature.lua	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,16 @@
+-- SPDX-License-Identifier: MIT
+--
+-- An example plugin to show how to disable an internal feature.
+--
+-- Typically one would expect the plugin to re-implement the feature
+-- in a more device-specific manner but that's not done here.
+
+-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
+-- libinput:register({1})
+libinput:connect("new-evdev-device", function(device)
+    local udev_info = device:udev_properties()
+    if udev_info["ID_INPUT_TOUCHPAD"] then
+        libinput:log_info("Disabling palm detection on " .. device:name())
+        device:disable_feature("touchpad-palm-detection")
+    end
+end)
diff -pruN 1.28.1-1/plugins/10-dwt.lua 1.30.0-1/plugins/10-dwt.lua
--- 1.28.1-1/plugins/10-dwt.lua	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/plugins/10-dwt.lua	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,40 @@
+-- SPDX-License-Identifier: MIT
+--
+-- This plugin implements a very simple version of disable-while-typing.
+-- It monitors all keyboard devices and if any of them send an event,
+-- any touchpad device is disabled for 2 seconds.
+-- And "disabled" means any event from that touchpad is simply
+-- discarded.
+--
+-- Install this file in /etc/libinput/plugins and
+--
+-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
+-- libinput:register({1})
+
+tp_enabled = true
+
+libinput:connect("timer-expired", function(now)
+    libinput:log_debug("touchpad enabled")
+    tp_enabled = true
+end)
+
+libinput:connect("new-evdev-device", function (device)
+    local props = device:udev_properties()
+    if props.ID_INPUT_KEYBOARD then
+        device:connect("evdev-frame", function (device, frame, timestamp)
+            libinput:timer_set_relative(2000000)
+            if tp_enabled then
+                libinput:log_debug("touchpad disabled")
+                tp_enabled = false
+            end
+        end)
+    elseif props.ID_INPUT_TOUCHPAD then
+        libinput:log_debug("Touchpad detected: " .. device:name())
+        device:connect("evdev-frame", function (device, frame, timestamp)
+            if not tp_enabled then
+                -- Returning an empty table discards the event.
+                return {}
+            end
+        end)
+    end
+end)
diff -pruN 1.28.1-1/plugins/10-example.lua 1.30.0-1/plugins/10-example.lua
--- 1.28.1-1/plugins/10-example.lua	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/plugins/10-example.lua	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,87 @@
+-- SPDX-License-Identifier: MIT
+--
+-- This is an example libinput plugin
+--
+-- This plugin swaps left and right buttons on any device that has both buttons.
+
+-- Let's create a plugin. A single Lua script may create more than one
+-- plugin instance but that's a bit of a nice use-case. Most scripts
+-- should be a single plugin.
+
+-- A plugin needs to be registered to activate. If it isn't, it is
+-- cleaned up immediately. In the register call we supply
+-- the list of plugin versions we support. Currently we only
+-- have version 1.
+--
+-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
+-- libinput:register({1})
+
+-- Note to the reader: it will be easier to understand this example
+-- if you read it bottom-up from here on.
+
+-- The callback for our "evdev-frame" signal.
+-- These frames are sent *before* libinput gets to handle them so
+-- any modifications will affect libinput.
+function frame(device, frame, time_in_microseconds)
+    -- Frame is a table in the form
+    -- { { usage: 123, value: 3 }, ... }
+    -- Let's use the evdev module to make it more readable, evdev.KEY_A
+    -- is simply the value (0x1 << 16) | 0x1 (see linux/input-event-codes.h)
+    for _, v in ipairs(frame) do
+        -- If we get a right button event, change it to left, and
+        -- vice versa. Because this happens before libinput (or the next
+        -- plugin in the precedence order) sees the
+        -- frame it doesn't know that the swap happened.
+        if v.usage == evdev.BTN_RIGHT then
+            v.usage = evdev.BTN_LEFT
+        elseif v.usage == evdev.BTN_LEFT then
+            v.usage = evdev.BTN_RIGHT
+        end
+    end
+    -- We changed the frame, let's return it. If we return nil
+    -- the original frame is being used as-is.
+    return frame
+end
+
+-- This is a timer callback. It is invoked after one second
+-- (see below) but only once - re-set the timer so
+-- it goes off every second.
+function timer_expired(time_in_microseconds)
+    libinput:timer_set_absolute(time_in_microseconds + 1000000)
+end
+
+-- Callback for the "new-evdev-device" signal, see below
+-- The argument is the EvdevDevice object, see the documentation.
+function device_new(device)
+    -- A table of evdev usages available on our device.
+    -- Using the evdev module makes it more readable but you can
+    -- use numbers (which is what evdev.EV_KEY and
+    -- friends resolve to anyway).
+    local usages = device:usages()
+    if usages[evdev.BTN_LEFT] and usages[evdev.BTN_RIGHT] then
+        -- The "evdev-frame" callback is invoked whenever the device
+        -- provided us with one evdev frame, i.e. a bunch of events up
+        -- to excluding EV_SYN SYN_REPORT.
+        device:connect("evdev-frame", frame)
+    end
+
+    -- The device has udev information, let's print it out. Right
+    -- now all we get are the ID_INPUT_ bits.
+    -- If this is empty we know libinput will ignore this device anyway
+    local udev_info = device:udev_properties()
+    for k, v in pairs(udev_info) do
+        libinput:log_debug(k .. "=" .. v)
+    end
+end
+
+-- Let's connect to the "new-evdev-device" signal. This function
+-- is invoked when libinput detects a new evdev device (but before
+-- that device is actually available to libinput as libinput device).
+-- This allows us to e.g. change properties on the device.
+libinput:connect("new-evdev-device", device_new)
+
+-- Set our timer to expire 1s from now (in microseconds).
+-- Timers are absolute, so they need to be added to the
+-- current time
+libinput:connect("timer-expired", timer_expired)
+libinput:timer_set_relative(1000000)
diff -pruN 1.28.1-1/plugins/10-logitech-mx-master-horiz-scroll.lua 1.30.0-1/plugins/10-logitech-mx-master-horiz-scroll.lua
--- 1.28.1-1/plugins/10-logitech-mx-master-horiz-scroll.lua	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/plugins/10-logitech-mx-master-horiz-scroll.lua	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,32 @@
+-- SPDX-License-Identifier: MIT
+--
+-- This plugin inverts the horizontal scroll direction of
+-- the Logitech MX Master mouse. OOTB the mouse scrolls
+-- in the opposite direction to all other mice out there.
+--
+-- This plugin is only needed when the mouse is connected
+-- to the Logitech Bolt receiver - on that receiver we cannot
+-- tell which device is connected.
+--
+-- For the Logitech Unifying receiver and Bluetooth please
+-- add the ModelInvertHorizontalScrolling=1 quirk
+-- in quirks/30-vendor-logitech.quirks.
+--
+--
+-- Install this file in /etc/libinput/plugins and
+--
+-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
+-- libinput:register({1})
+libinput:connect("new-evdev-device", function (device)
+    local info = device:info()
+    if info.vid == 0x046D and info.pid == 0xC548 then
+        device:connect("evdev-frame", function (device, frame, timestamp)
+            for _, event in ipairs(frame) do
+                if event.usage == evdev.REL_HWHEEL or event.usage == evdev.REL_HWHEEL_HI_RES then
+                    event.value = -event.value
+                end
+            end
+            return frame
+        end)
+    end
+end)
diff -pruN 1.28.1-1/plugins/10-pointer-go-faster.lua 1.30.0-1/plugins/10-pointer-go-faster.lua
--- 1.28.1-1/plugins/10-pointer-go-faster.lua	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/plugins/10-pointer-go-faster.lua	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,22 @@
+-- SPDX-License-Identifier: MIT
+--
+-- An example plugin to make the pointer go three times as fast
+--
+-- Install this file in /etc/libinput/plugins and
+--
+-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
+-- libinput:register({1})
+libinput:connect("new-evdev-device", function(device)
+    local usages = device:usages()
+    if usages[evdev.REL_X] then
+        device:connect("evdev-frame", function(device, frame, timestamp)
+            for _, v in ipairs(frame) do
+                if v.usage == evdev.REL_X or v.usage == evdev.REL_Y then
+                    -- Multiply the relative motion by 3
+                    v.value = v.value * 3
+                end
+            end
+            return frame
+        end)
+    end
+end)
diff -pruN 1.28.1-1/plugins/10-pointer-go-slower.lua 1.30.0-1/plugins/10-pointer-go-slower.lua
--- 1.28.1-1/plugins/10-pointer-go-slower.lua	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/plugins/10-pointer-go-slower.lua	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,56 @@
+-- SPDX-License-Identifier: MIT
+--
+-- An example plugin to make the pointer go three times as slow
+--
+-- Install this file in /etc/libinput/plugins and
+--
+-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
+-- libinput:register({1})
+remainders = {}
+
+function split(v)
+    if math.abs(v) >= 1.0 then
+        local i = math.floor(math.abs(v))
+        local r = math.abs(v) % 1.0
+        if v < 0.0 then
+            i = -i
+            r = -r
+        end
+        return i, r
+    else
+        return 0, v
+    end
+end
+
+function decelerate(device, x, y)
+    local remainder = remainders[device]
+    local rx, ry = 0, 0
+    if x ~= 0.0 then
+        rx, remainder.x = split(remainder.x + x/3.0)
+    end
+    if y ~= 0.0 then
+        ry, remainder.y = split(remainder.y + y/3.0)
+    end
+
+    return rx, ry
+end
+
+libinput:connect("new-evdev-device", function(device)
+    local usages = device:usages()
+    if usages[evdev.REL_X] then
+        remainders[device] = { x = 0.0, y = 0.0 }
+        device:connect("evdev-frame", function(device, frame, timestamp)
+            for _, v in ipairs(frame) do
+                if v.usage == evdev.REL_X then
+                    v.value, _ = decelerate(device, v.value, 0.0)
+                elseif v.usage == evdev.REL_Y then
+                    _, v.value = decelerate(device, 0.0, v.value)
+                end
+            end
+            return frame
+        end)
+        device:connect("device-removed", function(dev)
+            remainders[dev] = nil
+        end)
+    end
+end)
diff -pruN 1.28.1-1/plugins/10-wheel-to-button.lua 1.30.0-1/plugins/10-wheel-to-button.lua
--- 1.28.1-1/plugins/10-wheel-to-button.lua	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/plugins/10-wheel-to-button.lua	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,64 @@
+-- SPDX-License-Identifier: MIT
+--
+-- This is an example libinput plugin
+--
+-- This plugin maps a downwards mouse wheel to a button down event and
+-- an upwards wheel movement to a button up event.
+
+-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
+-- libinput:register({1})
+
+-- The button we want to press on wheel events
+local wheel_button = evdev.BTN_EXTRA
+local button_states = {}
+
+local function evdev_frame(device, frame, timestamp)
+    local events = {}
+    local modified = false
+
+    for _, v in ipairs(frame) do
+        if v.usage == evdev.REL_WHEEL then
+            -- REL_WHEEL is inverted, neg value -> down, pos value -> up
+            if v.value < 0 then
+                if not button_states[device] then
+                    table.insert(events, { usage = wheel_button, value = 1 })
+                    button_states[device] = true
+                end
+            else
+                if button_states[device] then
+                    table.insert(events, { usage = wheel_button, value = 0 })
+                    button_states[device] = false
+                end
+            end
+            modified = true
+        -- Because REL_WHEEL is no longer a wheel, the high-res
+        -- events are dropped
+        elseif v.usage == evdev.REL_WHEEL_HI_RES then
+            modified = true
+        else
+            table.insert(events, v)
+        end
+    end
+
+    if modified then
+        return events
+    else
+        return nil
+    end
+end
+
+local function device_new(device)
+    local usages = device:usages()
+    if usages[evdev.REL_WHEEL] then
+        button_states[device] = false
+        if not usages[wheel_button] then
+            device:enable_evdev_usage(wheel_button)
+        end
+        device:connect("evdev-frame", evdev_frame)
+        device:connect("device-removed", function(dev)
+            button_states[dev] = nil
+        end)
+    end
+end
+
+libinput:connect("new-evdev-device", device_new)
diff -pruN 1.28.1-1/plugins/meson.build 1.30.0-1/plugins/meson.build
--- 1.28.1-1/plugins/meson.build	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/plugins/meson.build	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,16 @@
+plugins = [
+	'10-example.lua',
+	'10-dwt.lua',
+	'10-logitech-mx-master-horiz-scroll.lua',
+	'10-pointer-go-faster.lua',
+	'10-pointer-go-slower.lua',
+	'10-delay-motion.lua',
+	'10-disable-feature.lua',
+	'10-copilot-key-override.lua',
+	'10-wheel-to-button.lua',
+]
+
+fs = import('fs')
+foreach plugin : plugins
+	fs.copyfile(plugin)
+endforeach
diff -pruN 1.28.1-1/quirks/30-vendor-contour.quirks 1.30.0-1/quirks/30-vendor-contour.quirks
--- 1.28.1-1/quirks/30-vendor-contour.quirks	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/quirks/30-vendor-contour.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -27,3 +27,9 @@ MatchVendor=0x0B33
 MatchProduct=0x0703
 MatchUdevType=mouse
 ModelBouncingKeys=1
+
+[Contour Design RollerMouse USB Receiver]
+MatchVendor=0x0B33
+MatchProduct=0x2000
+MatchUdevType=mouse
+ModelBouncingKeys=1
diff -pruN 1.28.1-1/quirks/30-vendor-logitech.quirks 1.30.0-1/quirks/30-vendor-logitech.quirks
--- 1.28.1-1/quirks/30-vendor-logitech.quirks	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/quirks/30-vendor-logitech.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -82,33 +82,45 @@ ModelInvertHorizontalScrolling=1
 MatchVendor=0x046D
 MatchProduct=0x4069
 ModelInvertHorizontalScrolling=1
+ModelScrollOnMiddleClick=1
 
 # MX Master 2S has a different PID on bluetooth
 [Logitech MX Master 2S Bluetooth]
 MatchVendor=0x046D
 MatchProduct=0xB019
 ModelInvertHorizontalScrolling=1
+ModelScrollOnMiddleClick=1
 
 [Logitech MX Master 3 USB]
 MatchVendor=0x046D
 MatchProduct=0x4082
 ModelInvertHorizontalScrolling=1
+ModelScrollOnMiddleClick=1
 
 # MX Master 3 has a different PID on bluetooth
 [Logitech MX Master 3 Bluetooth]
 MatchVendor=0x046D
 MatchProduct=0xB023
 ModelInvertHorizontalScrolling=1
+ModelScrollOnMiddleClick=1
 
 [Logitech MX Master 3S]
 MatchVendor=0x046D
 MatchProduct=0xB034
 ModelInvertHorizontalScrolling=1
+ModelScrollOnMiddleClick=1
 
 [Logitech MX Master 3B]
 MatchVendor=0x046D
 MatchProduct=0xB028
 ModelInvertHorizontalScrolling=1
+ModelScrollOnMiddleClick=1
+
+[Logitech MX Master 4]
+MatchVendor=0x046D
+MatchProduct=0xB042
+ModelInvertHorizontalScrolling=1
+ModelScrollOnMiddleClick=1
 
 # Don't add quirks for the Logitech Bolt Receiver:
 # MatchVendor=0x046D
diff -pruN 1.28.1-1/quirks/30-vendor-microsoft.quirks 1.30.0-1/quirks/30-vendor-microsoft.quirks
--- 1.28.1-1/quirks/30-vendor-microsoft.quirks	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/quirks/30-vendor-microsoft.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -19,6 +19,7 @@ AttrKeyboardIntegration=internal
 MatchName=*Microsoft Surface *Cover*
 MatchDMIModalias=dmi:*svnMicrosoftCorporation:*
 AttrKeyboardIntegration=internal
+AttrEventCode=-BTN_0;
 
 [Microsoft Surface Laptop Studio Touchpad]
 MatchVendor=0x045E
@@ -38,3 +39,9 @@ MatchBus=usb
 MatchVendor=0x045E
 MatchProduct=0x0800
 ModelBouncingKeys=1
+
+[Microsoft Surface Pro 10 with Flex Keyboard Touchpad]
+MatchVendor=0x045E
+MatchProduct=0x0C8D
+MatchUdevType=touchpad
+AttrEventCode=-BTN_RIGHT
diff -pruN 1.28.1-1/quirks/30-vendor-nulea.quirks 1.30.0-1/quirks/30-vendor-nulea.quirks
--- 1.28.1-1/quirks/30-vendor-nulea.quirks	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/quirks/30-vendor-nulea.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,9 @@
+# Nulea M501 does not name itself as a "Trackball"
+# In USB mode, it shares a generic dongle and name with other non-trackball
+# mice. As such, trackball quirks are enabled only for bluetooth modes.
+
+[Nulea M501 Bluetooth Trackball]
+MatchBus=bluetooth
+MatchVendor=0x000E
+MatchProduct=0x3412
+ModelTrackball=1
diff -pruN 1.28.1-1/quirks/30-vendor-oracle.quirks 1.30.0-1/quirks/30-vendor-oracle.quirks
--- 1.28.1-1/quirks/30-vendor-oracle.quirks	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/quirks/30-vendor-oracle.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -1,3 +1,3 @@
 [VirtualBox mouse integration]
 MatchName=*VirtualBox mouse integration*
-ModelBouncingKeys=1
+AttrIsVirtual=1
diff -pruN 1.28.1-1/quirks/30-vendor-qemu.quirks 1.30.0-1/quirks/30-vendor-qemu.quirks
--- 1.28.1-1/quirks/30-vendor-qemu.quirks	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/quirks/30-vendor-qemu.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -1,3 +1,3 @@
 [QEMU/KVM mouse integration]
 MatchName=*spice vdagent tablet*
-ModelBouncingKeys=1
+AttrIsVirtual=1
diff -pruN 1.28.1-1/quirks/30-vendor-razer.quirks 1.30.0-1/quirks/30-vendor-razer.quirks
--- 1.28.1-1/quirks/30-vendor-razer.quirks	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/quirks/30-vendor-razer.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -191,6 +191,13 @@ MatchVendor=0x1532
 MatchProduct=0x0259
 AttrKeyboardIntegration=internal
 
+[RazerBladeLate2020Base Keyboard]
+MatchUdevType=keyboard
+MatchBus=usb
+MatchVendor=0x1532
+MatchProduct=0x0268
+AttrKeyboardIntegration=internal
+
 [RazerBlade15AdvancedEarly2021 Keyboard]
 MatchUdevType=keyboard
 MatchBus=usb
@@ -302,3 +309,17 @@ MatchBus=usb
 MatchVendor=0x1532
 MatchProduct=0x02B8
 AttrKeyboardIntegration=internal
+
+[RazerBlade142025 Keyboard]
+MatchUdevType=keyboard
+MatchBus=usb
+MatchVendor=0x1532
+MatchProduct=0x02C5
+AttrKeyboardIntegration=internal
+
+[RazerBlade162025 Keyboard]
+MatchUdevType=keyboard
+MatchBus=usb
+MatchVendor=0x1532
+MatchProduct=0x02C6
+AttrKeyboardIntegration=internal
diff -pruN 1.28.1-1/quirks/30-vendor-vmware.quirks 1.30.0-1/quirks/30-vendor-vmware.quirks
--- 1.28.1-1/quirks/30-vendor-vmware.quirks	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/quirks/30-vendor-vmware.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -2,8 +2,8 @@
 
 [VMWare Virtual PS/2 Mouse]
 MatchName=*VirtualPS/2 VMware VMMouse*
-ModelBouncingKeys=1
+AttrIsVirtual=1
 
 [VMware VMware Virtual USB Mouse]
 MatchName=*VMware VMware Virtual USB Mouse*
-ModelBouncingKeys=1
+AttrIsVirtual=1
diff -pruN 1.28.1-1/quirks/50-system-asus.quirks 1.30.0-1/quirks/50-system-asus.quirks
--- 1.28.1-1/quirks/50-system-asus.quirks	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/quirks/50-system-asus.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -41,3 +41,17 @@ MatchVendor=0x0B05
 MatchProduct=0x1866
 MatchUdevType=keyboard
 AttrKeyboardIntegration=internal
+
+# don't disable volume buttons in tablet mode
+[Asus ROG Flow Z13 2025 volume buttons]
+MatchName=AT Translated Set 2 keyboard
+MatchDMIModalias=dmi:*svnASUSTeKCOMPUTERINC.:pnROGFlowZ13GZ302EA_GZ302EA:*
+ModelTabletModeNoSuspend=1
+
+# enable "disable touchpad while typing" to work with z13 keyboard
+[Asus ROG FLow Z13 2025 keyboard]
+MatchUdevType=keyboard
+MatchBus=usb
+MatchVendor=0x0B05
+MatchProduct=0x1A30
+AttrKeyboardIntegration=internal
diff -pruN 1.28.1-1/quirks/50-system-dell.quirks 1.30.0-1/quirks/50-system-dell.quirks
--- 1.28.1-1/quirks/50-system-dell.quirks	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/quirks/50-system-dell.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -89,6 +89,16 @@ MatchName=*AT Translated Set 2 keyboard*
 MatchDMIModalias=dmi:*:svnDellInc.:pnLatitude7275:*
 ModelTabletModeNoSuspend=1
 
+[Latitude 7285]
+MatchName=*AT Translated Set 2 keyboard*
+MatchDMIModalias=dmi:*:svnDellInc.:pnLatitude7285:*
+ModelTabletModeNoSuspend=1
+
+[Dell Pro Rugged Tablet]
+MatchName=*AT Translated Set 2 keyboard*
+MatchDMIModalias=dmi:*:svnDellInc.:pnDellProRugged*TabletRA*:*
+ModelTabletModeNoSuspend=1
+
 [Latitude 7480 Touchpad]
 MatchName=DLL07A0*
 MatchDMIModalias=dmi:**bvnDellInc.:*:pnLatitude7480*
@@ -165,3 +175,39 @@ MatchBus=i2c
 MatchVendor=0x27C6
 MatchProduct=0x0F62
 ModelPressurePad=1
+
+[Dell 14 Premium DA14250 touchpad]
+MatchBus=i2c
+MatchVendor=0x0488
+MatchProduct=0x108C
+ModelPressurePad=1
+
+[Dell 16 Premium DA16250 touchpad]
+MatchBus=i2c
+MatchVendor=0x0488
+MatchProduct=0x108D
+ModelPressurePad=1
+
+[Dell laptop 14_1 Synaptics touchpad]
+MatchBus=i2c
+MatchVendor=0x06CB
+MatchProduct=0xD01D
+ModelPressurePad=1
+
+[Dell laptop 16_1 Synaptics touchpad]
+MatchBus=i2c
+MatchVendor=0x06CB
+MatchProduct=0xD01A
+ModelPressurePad=1
+
+[Dell laptop 14 Sensel touchpad]
+MatchBus=i2c
+MatchVendor=0x2C2F
+MatchProduct=0x0034
+ModelPressurePad=1
+
+[Dell laptop 16 Sensel touchpad]
+MatchBus=i2c
+MatchVendor=0x2C2F
+MatchProduct=0x0033
+ModelPressurePad=1
diff -pruN 1.28.1-1/quirks/50-system-google.quirks 1.30.0-1/quirks/50-system-google.quirks
--- 1.28.1-1/quirks/50-system-google.quirks	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/quirks/50-system-google.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -178,3 +178,17 @@ ModelChromebook=1
 AttrPressureRange=0:-2
 AttrThumbPressureThreshold=45
 AttrPalmPressureThreshold=0
+
+[Google Chromebook Roric]
+MatchUdevType=touchpad
+MatchName=PNP0C50:00 093A:3307 Touchpad
+MatchDMIModalias=dmi:*svnGoogle:pnRoric*
+ModelChromebook=1
+AttrPressureRange=20:10
+
+[Google Chromebook Rull]
+MatchUdevType=touchpad
+MatchName=PNP0C50:00 093A:3307 Touchpad
+MatchDMIModalias=dmi:*svnGoogle:pnRull*
+ModelChromebook=1
+AttrPressureRange=20:10
diff -pruN 1.28.1-1/quirks/50-system-gpd.quirks 1.30.0-1/quirks/50-system-gpd.quirks
--- 1.28.1-1/quirks/50-system-gpd.quirks	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/quirks/50-system-gpd.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -3,3 +3,9 @@ MatchName=HTIX5288:00 093A:0255 Touchpad
 MatchUdevType=touchpad
 MatchDMIModalias=dmi:*svnGPD:*pnG1619-*
 AttrEventCode=-BTN_RIGHT
+
+[GPD MicroPC 2 Touchpad]
+MatchName=ALPS0001:00 36B6:C001 Touchpad
+MatchUdevType=touchpad
+MatchDMIModalias=dmi:*svnGPD:pnG1688-08:*
+AttrInputProp=-INPUT_PROP_BUTTONPAD
diff -pruN 1.28.1-1/quirks/50-system-hp.quirks 1.30.0-1/quirks/50-system-hp.quirks
--- 1.28.1-1/quirks/50-system-hp.quirks	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/quirks/50-system-hp.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -98,3 +98,24 @@ MatchBus=i2c
 MatchVendor=0x06CB
 MatchProduct=0xCFD2
 ModelPressurePad=1
+
+# The HP OmniBook Ultra Flip Laptop 14-fh0xxx's custom Intel ISH firmware
+# filters out events from its builtin keyboard and touchpad when the hinge is
+# opened little more than 180 degrees but toggles tablet-mode when it's opened
+# little less than 180 degrees.
+# Do not suspend the keyboard and touchpad to let use the device in flat
+# position and also give consistency with some keyboard keys controlled by the
+# Video Bus device (brightness down/up), the HP WMI hotkeys device (mic mute and
+# hp hubs launcher key) and the backlight getting on and off by the firmware at
+# the same time it enables disables the input.
+# This one is for the keyboard and...
+[HP OmniBook Ultra Flip Laptop 14-fh0xxx Keyboard]
+MatchName=AT Translated Set 2 keyboard
+MatchDMIModalias=dmi:*svnHP:pnHPOmniBookUltraFlipLaptop14-fh0xxx:*
+ModelTabletModeNoSuspend=1
+
+# ...this one is for the touchpad.
+[HP OmniBook Ultra Flip Laptop 14-fh0xxx Touchpad]
+MatchName=SYNA3580:00 06CB:CFD2 Touchpad
+MatchDMIModalias=dmi:*svnHP:pnHPOmniBookUltraFlipLaptop14-fh0xxx:*
+ModelTabletModeNoSuspend=1
diff -pruN 1.28.1-1/quirks/50-system-lenovo.quirks 1.30.0-1/quirks/50-system-lenovo.quirks
--- 1.28.1-1/quirks/50-system-lenovo.quirks	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/quirks/50-system-lenovo.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -360,6 +360,12 @@ MatchDMIModalias=dmi:*svnLENOVO:*:pvrThi
 AttrPalmPressureThreshold=300
 AttrPressureRange=10:8
 
+[Lenovo ThinkPad E16 Gen 3]
+MatchUdevType=pointingstick
+MatchName=*TPPS/2 Elan TrackPoint*
+MatchDMIModalias=dmi:*svnLENOVO:*:pvrThinkPadE16Gen3*
+AttrTrackpointMultiplier=0.5
+
 # White Backlit Keyboard
 [Lenovo Ideapad Gaming 3]
 MatchUdevType=keyboard
@@ -382,18 +388,18 @@ MatchUdevType=keyboard
 MatchDMIModalias=dmi:*svnLENOVO:*
 AttrKeyboardIntegration=internal
 
-# Some ThinkBook 14 G7+ ASP models come with pressure pads that were not
+# Some ThinkBook 14/16 G7+ ASP models come with pressure pads that were not
 # correctly declared as such.
-[Lenovo ThinkBook 14 G7+ ASP touchpad]
+[Lenovo ThinkBook G7+ ASP touchpad]
 MatchName=*GXTP5100*
-MatchDMIModalias=dmi:*svnLENOVO:*pvrThinkBook14G7+ASP*:*
+MatchDMIModalias=dmi:*svnLENOVO:*pvrThinkBook*G7+ASP*:*
 MatchUdevType=touchpad
 ModelPressurePad=1
 
-# The ThinkBook 14 G6+ IMH also has a similar issue as the G7+ mentioned above.
-[Lenovo ThinkBook 14 G6+ IMH]
+# The ThinkBook 14/16 G6+ IMH also has a similar issue as the G7+ mentioned above.
+[Lenovo ThinkBook G6+ IMH]
 MatchName=*GXTP5100*
-MatchDMIModalias=dmi:*svnLENOVO:*pvrThinkBook14G6+IMH*:*
+MatchDMIModalias=dmi:*svnLENOVO:*pvrThinkBook*G6+IMH*:*
 MatchUdevType=touchpad
 ModelPressurePad=1
 
@@ -411,9 +417,9 @@ MatchDMIModalias=dmi:*svnLENOVO:*pvrThin
 MatchUdevType=touchpad
 ModelPressurePad=1
 
-# The ThinkBook 14 G7+ IAH also has a similar issue as the ASP mentioned above.
-[Lenovo ThinkBook 14 G7+ IAH touchpad]
+# The ThinkBook 14/16 G7+ IAH also has a similar issue as the ASP mentioned above.
+[Lenovo ThinkBook G7+ IAH touchpad]
 MatchName=*GXTP5100*
-MatchDMIModalias=dmi:*svnLENOVO:*pvrThinkBook14G7+IAH*:*
+MatchDMIModalias=dmi:*svnLENOVO:*pvrThinkBook*G7+IAH*:*
 MatchUdevType=touchpad
 ModelPressurePad=1
diff -pruN 1.28.1-1/quirks/50-system-tongfang.quirks 1.30.0-1/quirks/50-system-tongfang.quirks
--- 1.28.1-1/quirks/50-system-tongfang.quirks	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/quirks/50-system-tongfang.quirks	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,5 @@
+[TongFang GX4 (X4SP4NAL) Touchpad]
+MatchName=UNIW0001:00 093A:0255 Touchpad
+MatchUdevType=touchpad
+MatchDMIModalias=dmi:*svnAiStone:pnX4SP4NAL:*
+AttrEventCode=-BTN_RIGHT
diff -pruN 1.28.1-1/src/builddir.h 1.30.0-1/src/builddir.h
--- 1.28.1-1/src/builddir.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/builddir.h	2025-11-25 03:40:43.000000000 +0000
@@ -26,14 +26,15 @@
 #pragma once
 
 #include <unistd.h>
+
 #include "util-strings.h"
 
 /**
  * Try to figure out the directory we're executing from and if it matches
- * the builddir, return that directory. Otherwise, return NULL.
+ * the builddir true and set builddir (if not NULL) to the given builddir.
  */
-static inline char *
-builddir_lookup(void)
+static inline bool
+builddir_lookup(char **builddir)
 {
 	char execdir[PATH_MAX];
 	char *pathsep;
@@ -42,11 +43,11 @@ builddir_lookup(void)
 	/* In the case of release builds, the builddir is
 	   the empty string */
 	if (streq(MESON_BUILD_ROOT, ""))
-		return NULL;
+		return false;
 
 	nread = readlink("/proc/self/exe", execdir, sizeof(execdir) - 1);
 	if (nread <= 0 || nread == sizeof(execdir) - 1)
-		return NULL;
+		return false;
 
 	/* readlink doesn't terminate the string and readlink says
 	   anything past sz is undefined */
@@ -54,11 +55,14 @@ builddir_lookup(void)
 
 	pathsep = strrchr(execdir, '/');
 	if (!pathsep)
-		return NULL;
+		return false;
 
 	*pathsep = '\0';
 	if (!streq(execdir, MESON_BUILD_ROOT))
-		return NULL;
+		return false;
+
+	if (builddir)
+		*builddir = safe_strdup(execdir);
 
-	return safe_strdup(execdir);
+	return true;
 }
diff -pruN 1.28.1-1/src/evdev-debounce.c 1.30.0-1/src/evdev-debounce.c
--- 1.28.1-1/src/evdev-debounce.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-debounce.c	1970-01-01 00:00:00.000000000 +0000
@@ -1,604 +0,0 @@
-/*
- * Copyright © 2017 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:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-#include "config.h"
-
-#include "evdev-fallback.h"
-
-/* Debounce cases to handle
-     P ... button press
-     R ... button release
-     ---|  timeout duration
-
-     'normal' .... event sent when it happens
-     'filtered' .. event is not sent (but may be sent later)
-     'delayed' ... event is sent with wall-clock delay
-
-   1) P---| R		P normal, R normal
-   2) R---| P		R normal, P normal
-   3) P---R--| P	P normal, R filtered, delayed, P normal
-   4) R---P--| R	R normal, P filtered, delayed, R normal
-   4.1) P---| R--P--|	P normal, R filtered
-   5) P--R-P-| R	P normal, R filtered, P filtered, R normal
-   6) R--P-R-| P	R normal, P filtered, R filtered, P normal
-   7) P--R--|
-          ---P-|	P normal, R filtered, P filtered
-   8) R--P--|
-          ---R-|	R normal, P filtered, R filtered
-
-   1, 2 are the normal click cases without debouncing taking effect
-   3, 4 are fast clicks where the second event is delivered with a delay
-   5, 6 are contact bounces, fast
-   7, 8 are contact bounces, slow
-
-   4.1 is a special case with the same event sequence as 4 but we want to
-   filter the *release* event out, it's a button losing contact while being
-   held down.
-
-   7 and 8 are cases where the first event happens within the first timeout
-   but the second event is outside that timeout (but within the timeout of
-   the second event). These cases are handled by restarting the timer on every
-   event that could be part of a bouncing sequence, which makes these cases
-   indistinguishable from 5 and 6.
-*/
-
-enum debounce_event {
-	DEBOUNCE_EVENT_PRESS = 50,
-	DEBOUNCE_EVENT_RELEASE,
-	DEBOUNCE_EVENT_TIMEOUT,
-	DEBOUNCE_EVENT_TIMEOUT_SHORT,
-	DEBOUNCE_EVENT_OTHERBUTTON,
-};
-
-static inline const char *
-debounce_state_to_str(enum debounce_state state)
-{
-	switch(state) {
-	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_UP);
-	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_DOWN);
-	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_DOWN_WAITING);
-	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_UP_DELAYING);
-	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_UP_DELAYING_SPURIOUS);
-	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_UP_DETECTING_SPURIOUS);
-	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_DOWN_DETECTING_SPURIOUS);
-	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_UP_WAITING);
-	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_DOWN_DELAYING);
-	CASE_RETURN_STRING(DEBOUNCE_STATE_DISABLED);
-	}
-
-	return NULL;
-}
-
-static inline const char*
-debounce_event_to_str(enum debounce_event event)
-{
-	switch(event) {
-	CASE_RETURN_STRING(DEBOUNCE_EVENT_PRESS);
-	CASE_RETURN_STRING(DEBOUNCE_EVENT_RELEASE);
-	CASE_RETURN_STRING(DEBOUNCE_EVENT_TIMEOUT);
-	CASE_RETURN_STRING(DEBOUNCE_EVENT_TIMEOUT_SHORT);
-	CASE_RETURN_STRING(DEBOUNCE_EVENT_OTHERBUTTON);
-	}
-	return NULL;
-}
-
-static inline void
-log_debounce_bug(struct fallback_dispatch *fallback, enum debounce_event event)
-{
-	evdev_log_bug_libinput(fallback->device,
-			       "invalid debounce event %s in state %s\n",
-			       debounce_event_to_str(event),
-			       debounce_state_to_str(fallback->debounce.state));
-
-}
-
-static inline void
-debounce_set_state(struct fallback_dispatch *fallback,
-		   enum debounce_state new_state)
-{
-	assert(new_state >= DEBOUNCE_STATE_IS_UP &&
-	       new_state <= DEBOUNCE_STATE_IS_DOWN_DELAYING);
-
-	fallback->debounce.state = new_state;
-}
-
-static inline void
-debounce_set_timer(struct fallback_dispatch *fallback,
-		   uint64_t time)
-{
-	const int DEBOUNCE_TIMEOUT_BOUNCE = ms2us(25);
-
-	libinput_timer_set(&fallback->debounce.timer,
-			   time + DEBOUNCE_TIMEOUT_BOUNCE);
-}
-
-static inline void
-debounce_set_timer_short(struct fallback_dispatch *fallback,
-			 uint64_t time)
-{
-	const int DEBOUNCE_TIMEOUT_SPURIOUS = ms2us(12);
-
-	libinput_timer_set(&fallback->debounce.timer_short,
-			   time + DEBOUNCE_TIMEOUT_SPURIOUS);
-}
-
-static inline void
-debounce_cancel_timer(struct fallback_dispatch *fallback)
-{
-	libinput_timer_cancel(&fallback->debounce.timer);
-}
-
-static inline void
-debounce_cancel_timer_short(struct fallback_dispatch *fallback)
-{
-	libinput_timer_cancel(&fallback->debounce.timer_short);
-}
-
-static inline void
-debounce_enable_spurious(struct fallback_dispatch *fallback)
-{
-	if (fallback->debounce.spurious_enabled)
-		evdev_log_bug_libinput(fallback->device,
-				       "tried to enable spurious debouncing twice\n");
-
-	fallback->debounce.spurious_enabled = true;
-	evdev_log_info(fallback->device,
-		       "Enabling spurious button debouncing, "
-		       "see %s/button-debouncing.html for details\n",
-		       HTTP_DOC_LINK);
-}
-
-static void
-debounce_notify_button(struct fallback_dispatch *fallback,
-		       enum libinput_button_state state)
-{
-	struct evdev_device *device = fallback->device;
-	unsigned int code = fallback->debounce.button_code;
-	uint64_t time = fallback->debounce.button_time;
-
-	code = evdev_to_left_handed(device, code);
-
-	fallback_notify_physical_button(fallback, device, time, code, state);
-}
-
-static void
-debounce_is_up_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time)
-{
-	switch (event) {
-	case DEBOUNCE_EVENT_PRESS:
-		fallback->debounce.button_time = time;
-		debounce_set_timer(fallback, time);
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN_WAITING);
-		debounce_notify_button(fallback,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
-		break;
-	case DEBOUNCE_EVENT_RELEASE:
-	case DEBOUNCE_EVENT_TIMEOUT:
-	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
-		log_debounce_bug(fallback, event);
-		break;
-	case DEBOUNCE_EVENT_OTHERBUTTON:
-		break;
-	}
-}
-
-static void
-debounce_is_down_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time)
-{
-	switch (event) {
-	case DEBOUNCE_EVENT_PRESS:
-		log_debounce_bug(fallback, event);
-		break;
-	case DEBOUNCE_EVENT_RELEASE:
-		fallback->debounce.button_time = time;
-		debounce_set_timer(fallback, time);
-		debounce_set_timer_short(fallback, time);
-		if (fallback->debounce.spurious_enabled) {
-			debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP_DELAYING_SPURIOUS);
-		} else {
-			debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP_DETECTING_SPURIOUS);
-			debounce_notify_button(fallback,
-					       LIBINPUT_BUTTON_STATE_RELEASED);
-		}
-		break;
-	case DEBOUNCE_EVENT_TIMEOUT:
-	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
-		log_debounce_bug(fallback, event);
-		break;
-	case DEBOUNCE_EVENT_OTHERBUTTON:
-		break;
-	}
-}
-
-static void
-debounce_is_down_waiting_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time)
-{
-	switch (event) {
-	case DEBOUNCE_EVENT_PRESS:
-		log_debounce_bug(fallback, event);
-		break;
-	case DEBOUNCE_EVENT_RELEASE:
-		debounce_set_timer(fallback, time);
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP_DELAYING);
-		/* Note: In the debouncing RPR case, we use the last
-		 * release's time stamp */
-		fallback->debounce.button_time = time;
-		break;
-	case DEBOUNCE_EVENT_TIMEOUT:
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN);
-		break;
-	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
-		log_debounce_bug(fallback, event);
-		break;
-	case DEBOUNCE_EVENT_OTHERBUTTON:
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN);
-		break;
-	}
-}
-
-static void
-debounce_is_up_delaying_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time)
-{
-	switch (event) {
-	case DEBOUNCE_EVENT_PRESS:
-		debounce_set_timer(fallback, time);
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN_WAITING);
-		break;
-	case DEBOUNCE_EVENT_RELEASE:
-	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
-		log_debounce_bug(fallback, event);
-		break;
-	case DEBOUNCE_EVENT_TIMEOUT:
-	case DEBOUNCE_EVENT_OTHERBUTTON:
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP);
-		debounce_notify_button(fallback,
-				       LIBINPUT_BUTTON_STATE_RELEASED);
-		break;
-	}
-}
-
-static void
-debounce_is_up_delaying_spurious_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time)
-{
-	switch (event) {
-	case DEBOUNCE_EVENT_PRESS:
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN);
-		debounce_cancel_timer(fallback);
-		debounce_cancel_timer_short(fallback);
-		break;
-	case DEBOUNCE_EVENT_RELEASE:
-	case DEBOUNCE_EVENT_TIMEOUT:
-		log_debounce_bug(fallback, event);
-		break;
-	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP_WAITING);
-		debounce_notify_button(fallback,
-				       LIBINPUT_BUTTON_STATE_RELEASED);
-		break;
-	case DEBOUNCE_EVENT_OTHERBUTTON:
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP);
-		debounce_notify_button(fallback,
-				       LIBINPUT_BUTTON_STATE_RELEASED);
-		break;
-	}
-}
-
-static void
-debounce_is_up_detecting_spurious_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time)
-{
-	switch (event) {
-	case DEBOUNCE_EVENT_PRESS:
-		debounce_set_timer(fallback, time);
-		debounce_set_timer_short(fallback, time);
-		/* Note: in a bouncing PRP case, we use the last press
-		 * event time */
-		fallback->debounce.button_time = time;
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN_DETECTING_SPURIOUS);
-		break;
-	case DEBOUNCE_EVENT_RELEASE:
-		log_debounce_bug(fallback, event);
-		break;
-	case DEBOUNCE_EVENT_TIMEOUT:
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP);
-		break;
-	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP_WAITING);
-		break;
-	case DEBOUNCE_EVENT_OTHERBUTTON:
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP);
-		break;
-	}
-}
-
-static void
-debounce_is_down_detecting_spurious_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time)
-{
-	switch (event) {
-	case DEBOUNCE_EVENT_PRESS:
-		log_debounce_bug(fallback, event);
-		break;
-	case DEBOUNCE_EVENT_RELEASE:
-		debounce_set_timer(fallback, time);
-		debounce_set_timer_short(fallback, time);
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP_DETECTING_SPURIOUS);
-		break;
-	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
-		debounce_cancel_timer(fallback);
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN);
-		debounce_enable_spurious(fallback);
-		debounce_notify_button(fallback,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
-		break;
-	case DEBOUNCE_EVENT_TIMEOUT:
-	case DEBOUNCE_EVENT_OTHERBUTTON:
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN);
-		debounce_notify_button(fallback,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
-		break;
-	}
-}
-
-static void
-debounce_is_up_waiting_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time)
-{
-	switch (event) {
-	case DEBOUNCE_EVENT_PRESS:
-		debounce_set_timer(fallback, time);
-		/* Note: in a debouncing PRP case, we use the last press'
-		 * time */
-		fallback->debounce.button_time = time;
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN_DELAYING);
-		break;
-	case DEBOUNCE_EVENT_RELEASE:
-	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
-		log_debounce_bug(fallback, event);
-		break;
-	case DEBOUNCE_EVENT_TIMEOUT:
-	case DEBOUNCE_EVENT_OTHERBUTTON:
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP);
-		break;
-	}
-}
-
-static void
-debounce_is_down_delaying_handle_event(struct fallback_dispatch *fallback, enum debounce_event event, uint64_t time)
-{
-	switch (event) {
-	case DEBOUNCE_EVENT_PRESS:
-		log_debounce_bug(fallback, event);
-		break;
-	case DEBOUNCE_EVENT_RELEASE:
-		debounce_set_timer(fallback, time);
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_UP_WAITING);
-		break;
-	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
-		log_debounce_bug(fallback, event);
-		break;
-	case DEBOUNCE_EVENT_TIMEOUT:
-	case DEBOUNCE_EVENT_OTHERBUTTON:
-		debounce_set_state(fallback, DEBOUNCE_STATE_IS_DOWN);
-		debounce_notify_button(fallback,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
-		break;
-	}
-}
-
-static void
-debounce_disabled_handle_event(struct fallback_dispatch *fallback,
-			enum debounce_event event,
-			uint64_t time)
-{
-	switch (event) {
-	case DEBOUNCE_EVENT_PRESS:
-		fallback->debounce.button_time = time;
-		debounce_notify_button(fallback,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
-		break;
-	case DEBOUNCE_EVENT_RELEASE:
-		fallback->debounce.button_time = time;
-		debounce_notify_button(fallback,
-				       LIBINPUT_BUTTON_STATE_RELEASED);
-		break;
-	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
-	case DEBOUNCE_EVENT_TIMEOUT:
-		log_debounce_bug(fallback, event);
-		break;
-	case DEBOUNCE_EVENT_OTHERBUTTON:
-		break;
-	}
-}
-
-static void
-debounce_handle_event(struct fallback_dispatch *fallback,
-		      enum debounce_event event,
-		      uint64_t time)
-{
-	enum debounce_state current = fallback->debounce.state;
-
-	if (event == DEBOUNCE_EVENT_OTHERBUTTON) {
-		debounce_cancel_timer(fallback);
-		debounce_cancel_timer_short(fallback);
-	}
-
-	switch(current) {
-	case DEBOUNCE_STATE_IS_UP:
-		debounce_is_up_handle_event(fallback, event, time);
-		break;
-	case DEBOUNCE_STATE_IS_DOWN:
-		debounce_is_down_handle_event(fallback, event, time);
-		break;
-	case DEBOUNCE_STATE_IS_DOWN_WAITING:
-		debounce_is_down_waiting_handle_event(fallback, event, time);
-		break;
-	case DEBOUNCE_STATE_IS_UP_DELAYING:
-		debounce_is_up_delaying_handle_event(fallback, event, time);
-		break;
-	case DEBOUNCE_STATE_IS_UP_DELAYING_SPURIOUS:
-		debounce_is_up_delaying_spurious_handle_event(fallback, event, time);
-		break;
-	case DEBOUNCE_STATE_IS_UP_DETECTING_SPURIOUS:
-		debounce_is_up_detecting_spurious_handle_event(fallback, event, time);
-		break;
-	case DEBOUNCE_STATE_IS_DOWN_DETECTING_SPURIOUS:
-		debounce_is_down_detecting_spurious_handle_event(fallback, event, time);
-		break;
-	case DEBOUNCE_STATE_IS_UP_WAITING:
-		debounce_is_up_waiting_handle_event(fallback, event, time);
-		break;
-	case DEBOUNCE_STATE_IS_DOWN_DELAYING:
-		debounce_is_down_delaying_handle_event(fallback, event, time);
-		break;
-	case DEBOUNCE_STATE_DISABLED:
-		debounce_disabled_handle_event(fallback, event, time);
-		break;
-	}
-
-	evdev_log_debug(fallback->device,
-			"debounce state: %s → %s → %s\n",
-			debounce_state_to_str(current),
-			debounce_event_to_str(event),
-			debounce_state_to_str(fallback->debounce.state));
-}
-
-void
-fallback_debounce_handle_state(struct fallback_dispatch *dispatch,
-			       uint64_t time)
-{
-	unsigned int changed[16] = {0}; /* event codes of changed buttons */
-	size_t nchanged = 0;
-	bool flushed = false;
-
-	for (unsigned int code = 0; code <= KEY_MAX; code++) {
-		if (get_key_type(code) != KEY_TYPE_BUTTON)
-			continue;
-
-		if (hw_key_has_changed(dispatch, code))
-			changed[nchanged++] = code;
-
-		/* If you manage to press more than 16 buttons in the same
-		 * frame, we just quietly ignore the rest of them */
-		if (nchanged == ARRAY_LENGTH(changed))
-			break;
-	}
-
-	/* If we have more than one button this frame or a different button,
-	 * flush the state machine with otherbutton */
-	if (nchanged > 1 ||
-	    changed[0] != dispatch->debounce.button_code) {
-		debounce_handle_event(dispatch,
-				      DEBOUNCE_EVENT_OTHERBUTTON,
-				      time);
-		flushed = true;
-	}
-
-	/* The state machine has some pre-conditions:
-	 * - the IS_DOWN and IS_UP states are neutral entry states without
-	 *   any timeouts
-	 * - a OTHERBUTTON event always flushes the state to IS_DOWN or
-	 *   IS_UP
-	 */
-
-	for (size_t i = 0; i < nchanged; i++) {
-		bool is_down = hw_is_key_down(dispatch, changed[i]);
-
-		if (flushed &&
-		    dispatch->debounce.state != DEBOUNCE_STATE_DISABLED) {
-			debounce_set_state(dispatch,
-					   !is_down ?
-						   DEBOUNCE_STATE_IS_DOWN :
-						   DEBOUNCE_STATE_IS_UP);
-			flushed = false;
-		}
-
-		dispatch->debounce.button_code = changed[i];
-		debounce_handle_event(dispatch,
-				      is_down ?
-					      DEBOUNCE_EVENT_PRESS :
-					      DEBOUNCE_EVENT_RELEASE,
-				      time);
-
-		/* if we have more than one event, we flush the state
-		 * machine immediately after the event itself */
-		if (nchanged > 1) {
-			debounce_handle_event(dispatch,
-					      DEBOUNCE_EVENT_OTHERBUTTON,
-					      time);
-			flushed = true;
-		}
-
-	}
-}
-
-static void
-debounce_timeout(uint64_t now, void *data)
-{
-	struct evdev_device *device = data;
-	struct fallback_dispatch *dispatch =
-		fallback_dispatch(device->dispatch);
-
-	debounce_handle_event(dispatch, DEBOUNCE_EVENT_TIMEOUT, now);
-}
-
-static void
-debounce_timeout_short(uint64_t now, void *data)
-{
-	struct evdev_device *device = data;
-	struct fallback_dispatch *dispatch =
-		fallback_dispatch(device->dispatch);
-
-	debounce_handle_event(dispatch, DEBOUNCE_EVENT_TIMEOUT_SHORT, now);
-}
-
-void
-fallback_init_debounce(struct fallback_dispatch *dispatch)
-{
-	struct evdev_device *device = dispatch->device;
-	char timer_name[64];
-
-	if (evdev_device_has_model_quirk(device, QUIRK_MODEL_BOUNCING_KEYS)) {
-		dispatch->debounce.state = DEBOUNCE_STATE_DISABLED;
-		return;
-	}
-
-	dispatch->debounce.state = DEBOUNCE_STATE_IS_UP;
-
-	snprintf(timer_name,
-		 sizeof(timer_name),
-		 "%s debounce short",
-		 evdev_device_get_sysname(device));
-	libinput_timer_init(&dispatch->debounce.timer_short,
-			    evdev_libinput_context(device),
-			    timer_name,
-			    debounce_timeout_short,
-			    device);
-
-	snprintf(timer_name,
-		 sizeof(timer_name),
-		 "%s debounce",
-		 evdev_device_get_sysname(device));
-	libinput_timer_init(&dispatch->debounce.timer,
-			    evdev_libinput_context(device),
-			    timer_name,
-			    debounce_timeout,
-			    device);
-}
diff -pruN 1.28.1-1/src/evdev-fallback.c 1.30.0-1/src/evdev-fallback.c
--- 1.28.1-1/src/evdev-fallback.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-fallback.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,25 +26,28 @@
 
 #include "config.h"
 
-#include <mtdev-plumbing.h>
+#include "util-input-event.h"
 
 #include "evdev-fallback.h"
-#include "util-input-event.h"
 
 static void
 fallback_keyboard_notify_key(struct fallback_dispatch *dispatch,
 			     struct evdev_device *device,
 			     uint64_t time,
-			     int key,
+			     evdev_usage_t usage,
 			     enum libinput_key_state state)
 {
 	int down_count;
 
-	down_count = evdev_update_key_down_count(device, key, state);
+	down_count = evdev_update_key_down_count(device, usage, state);
 
 	if ((state == LIBINPUT_KEY_STATE_PRESSED && down_count == 1) ||
-	    (state == LIBINPUT_KEY_STATE_RELEASED && down_count == 0))
-		keyboard_notify_key(&device->base, time, key, state);
+	    (state == LIBINPUT_KEY_STATE_RELEASED && down_count == 0)) {
+		keyboard_notify_key(&device->base,
+				    time,
+				    keycode_from_usage(usage),
+				    state);
+	}
 }
 
 static void
@@ -65,7 +68,7 @@ void
 fallback_notify_physical_button(struct fallback_dispatch *dispatch,
 				struct evdev_device *device,
 				uint64_t time,
-				int button,
+				evdev_usage_t button,
 				enum libinput_button_state state)
 {
 	evdev_pointer_notify_physical_button(device, time, button, state);
@@ -85,9 +88,8 @@ fallback_interface_get_switch_state(stru
 		abort();
 	}
 
-	return dispatch->tablet_mode.sw.state ?
-			LIBINPUT_SWITCH_STATE_ON :
-			LIBINPUT_SWITCH_STATE_OFF;
+	return dispatch->tablet_mode.sw.state ? LIBINPUT_SWITCH_STATE_ON
+					      : LIBINPUT_SWITCH_STATE_OFF;
 }
 
 static inline bool
@@ -98,7 +100,7 @@ post_button_scroll(struct evdev_device *
 	if (device->scroll.method != LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN)
 		return false;
 
-	switch(device->scroll.button_scroll_state) {
+	switch (device->scroll.button_scroll_state) {
 	case BUTTONSCROLL_IDLE:
 		return false;
 	case BUTTONSCROLL_BUTTON_DOWN:
@@ -110,17 +112,18 @@ post_button_scroll(struct evdev_device *
 	case BUTTONSCROLL_READY:
 		device->scroll.button_scroll_state = BUTTONSCROLL_SCROLLING;
 		_fallthrough_;
-	case BUTTONSCROLL_SCROLLING:
-		{
+	case BUTTONSCROLL_SCROLLING: {
 		const struct normalized_coords normalized =
-				filter_dispatch_scroll(device->pointer.filter,
-						       &raw,
-						       device,
-						       time);
-		evdev_post_scroll(device, time,
+			filter_dispatch_scroll(device->pointer.filter,
+					       &raw,
+					       device,
+					       time,
+					       FILTER_SCROLL_TYPE_CONTINUOUS);
+		evdev_post_scroll(device,
+				  time,
 				  LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
 				  &normalized);
-		}
+	}
 		return true;
 	}
 
@@ -187,13 +190,9 @@ fallback_flush_relative_motion(struct fa
 
 	if (device->pointer.filter) {
 		/* Apply pointer acceleration. */
-		accel = filter_dispatch(device->pointer.filter,
-					&raw,
-					device,
-					time);
+		accel = filter_dispatch(device->pointer.filter, &raw, device, time);
 	} else {
-		evdev_log_bug_libinput(device,
-				       "accel filter missing\n");
+		evdev_log_bug_libinput(device, "accel filter missing\n");
 		accel.x = accel.y = 0;
 	}
 
@@ -204,6 +203,123 @@ fallback_flush_relative_motion(struct fa
 }
 
 static void
+fallback_flush_wheels(struct fallback_dispatch *dispatch,
+		      struct evdev_device *device,
+		      uint64_t time)
+{
+	if (!libinput_device_has_capability(&device->base, LIBINPUT_DEVICE_CAP_POINTER))
+		return;
+
+	/* This mouse has a trackstick instead of a mouse wheel and sends
+	 * trackstick data via REL_WHEEL. Normalize it like normal x/y coordinates.
+	 */
+	if (device->model_flags & EVDEV_MODEL_LENOVO_SCROLLPOINT) {
+		const struct device_float_coords raw = {
+			.x = dispatch->wheel.lo_res.x,
+			.y = dispatch->wheel.lo_res.y * -1,
+		};
+		const struct normalized_coords normalized =
+			filter_dispatch_scroll(device->pointer.filter,
+					       &raw,
+					       device,
+					       time,
+					       FILTER_SCROLL_TYPE_WHEEL);
+		evdev_post_scroll(device,
+				  time,
+				  LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
+				  &normalized);
+		dispatch->wheel.hi_res.x = 0;
+		dispatch->wheel.hi_res.y = 0;
+		dispatch->wheel.lo_res.x = 0;
+		dispatch->wheel.lo_res.y = 0;
+
+		return;
+	}
+
+	/* High-resolution wheel events */
+	if (dispatch->wheel.hi_res.x != 0 || dispatch->wheel.hi_res.y != 0) {
+		const struct device_float_coords v120_unaccelerated = {
+			.x = dispatch->wheel.hi_res.x,
+			.y = -1 * dispatch->wheel.hi_res.y,
+		};
+		const struct normalized_coords v120_accelerated =
+			device->pointer.filter
+				? filter_dispatch_scroll(device->pointer.filter,
+							 &v120_unaccelerated,
+							 device,
+							 time,
+							 FILTER_SCROLL_TYPE_WHEEL)
+				: (const struct normalized_coords){
+					  .x = v120_unaccelerated.x,
+					  .y = v120_unaccelerated.y
+				  };
+		/* Truncate the fractional part when converting floating-point to
+		   integer. This is acceptable because the v120 unit maps one logical
+		   click to 120 units; values are effectively measured in 1/120 of a
+		   click, so truncation does not lose meaningful resolution. */
+		const struct wheel_v120 v120 = {
+			.x = v120_accelerated.x,
+			.y = v120_accelerated.y,
+		};
+		const struct normalized_coords wheel_degrees = {
+			.x = v120.x / 120.0 * device->scroll.wheel_click_angle.x,
+			.y = v120.y / 120.0 * device->scroll.wheel_click_angle.y,
+		};
+		if (v120.x != 0) {
+			evdev_notify_axis_wheel(
+				device,
+				time,
+				bit(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
+				&wheel_degrees,
+				&v120);
+		}
+		if (v120.y != 0) {
+			evdev_notify_axis_wheel(
+				device,
+				time,
+				bit(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
+				&wheel_degrees,
+				&v120);
+		}
+		dispatch->wheel.hi_res.x = 0;
+		dispatch->wheel.hi_res.y = 0;
+	}
+
+	/* Low-resolution wheel events */
+	if (dispatch->wheel.lo_res.x != 0 || dispatch->wheel.lo_res.y != 0) {
+		/* Do not accelerate low-resolution wheel events: they use different
+		   units than high-resolution events and should not be accelerated with
+		   the same function. */
+		const struct discrete_coords discrete = {
+			.x = dispatch->wheel.lo_res.x,
+			.y = -1 * dispatch->wheel.lo_res.y,
+		};
+		const struct normalized_coords wheel_degrees = {
+			.x = discrete.x * device->scroll.wheel_click_angle.x,
+			.y = discrete.y * device->scroll.wheel_click_angle.y,
+		};
+		if (discrete.x != 0) {
+			evdev_notify_axis_legacy_wheel(
+				device,
+				time,
+				bit(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
+				&wheel_degrees,
+				&discrete);
+		}
+		if (discrete.y != 0) {
+			evdev_notify_axis_legacy_wheel(
+				device,
+				time,
+				bit(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
+				&wheel_degrees,
+				&discrete);
+		}
+		dispatch->wheel.lo_res.x = 0;
+		dispatch->wheel.lo_res.y = 0;
+	}
+}
+
+static void
 fallback_flush_absolute_motion(struct fallback_dispatch *dispatch,
 			       struct evdev_device *device,
 			       uint64_t time)
@@ -237,8 +353,9 @@ fallback_flush_mt_down(struct fallback_d
 
 	slot = &dispatch->mt.slots[slot_idx];
 	if (slot->seat_slot != -1) {
-		evdev_log_bug_kernel(device,
-				     "driver sent multiple touch down for the same slot");
+		evdev_log_bug_kernel(
+			device,
+			"driver sent multiple touch down for the same slot");
 		return false;
 	}
 
@@ -253,8 +370,7 @@ fallback_flush_mt_down(struct fallback_d
 	slot->hysteresis_center = point;
 	evdev_transform_absolute(device, &point);
 
-	touch_notify_touch_down(base, time, slot_idx, seat_slot,
-				&point);
+	touch_notify_touch_down(base, time, slot_idx, seat_slot, &point);
 
 	return true;
 }
@@ -284,8 +400,7 @@ fallback_flush_mt_motion(struct fallback
 		return false;
 
 	evdev_transform_absolute(device, &point);
-	touch_notify_touch_motion(base, time, slot_idx, seat_slot,
-				  &point);
+	touch_notify_touch_motion(base, time, slot_idx, seat_slot, &point);
 
 	return true;
 }
@@ -360,8 +475,9 @@ fallback_flush_st_down(struct fallback_d
 		return false;
 
 	if (dispatch->abs.seat_slot != -1) {
-		evdev_log_bug_kernel(device,
-				     "driver sent multiple touch down for the same slot");
+		evdev_log_bug_kernel(
+			device,
+			"driver sent multiple touch down for the same slot");
 		return false;
 	}
 
@@ -456,92 +572,75 @@ fallback_flush_st_cancel(struct fallback
 static void
 fallback_process_touch_button(struct fallback_dispatch *dispatch,
 			      struct evdev_device *device,
-			      uint64_t time, int value)
+			      uint64_t time,
+			      int value)
 {
-	dispatch->pending_event |= (value) ?
-				 EVDEV_ABSOLUTE_TOUCH_DOWN :
-				 EVDEV_ABSOLUTE_TOUCH_UP;
+	dispatch->pending_event |=
+		(value) ? EVDEV_ABSOLUTE_TOUCH_DOWN : EVDEV_ABSOLUTE_TOUCH_UP;
 }
 
 static inline void
 fallback_process_key(struct fallback_dispatch *dispatch,
 		     struct evdev_device *device,
-		     struct input_event *e, uint64_t time)
+		     struct evdev_event *e,
+		     uint64_t time)
 {
-	enum key_type type;
-
 	/* ignore kernel key repeat */
 	if (e->value == 2)
 		return;
 
-	if (e->code == BTN_TOUCH) {
+	if (evdev_usage_eq(e->usage, EVDEV_BTN_TOUCH)) {
 		if (!device->is_mt)
-			fallback_process_touch_button(dispatch,
-						      device,
-						      time,
-						      e->value);
+			fallback_process_touch_button(dispatch, device, time, e->value);
 		return;
 	}
 
-	type = get_key_type(e->code);
+	bool is_button = evdev_usage_is_button(e->usage);
+	bool is_key = evdev_usage_is_key(e->usage);
 
 	/* Ignore key release events from the kernel for keys that libinput
 	 * never got a pressed event for or key presses for keys that we
 	 * think are still down */
-	switch (type) {
-	case KEY_TYPE_NONE:
-		break;
-	case KEY_TYPE_KEY:
-	case KEY_TYPE_BUTTON:
-		if ((e->value && hw_is_key_down(dispatch, e->code)) ||
-		    (e->value == 0 && !hw_is_key_down(dispatch, e->code)))
+	if (is_button || is_key) {
+		if ((e->value && hw_is_key_down(dispatch, e->usage)) ||
+		    (e->value == 0 && !hw_is_key_down(dispatch, e->usage)))
 			return;
 
 		dispatch->pending_event |= EVDEV_KEY;
-		break;
 	}
 
-	hw_set_key_down(dispatch, e->code, e->value);
+	hw_set_key_down(dispatch, e->usage, e->value);
 
-	switch (type) {
-	case KEY_TYPE_NONE:
-		break;
-	case KEY_TYPE_KEY:
-		fallback_keyboard_notify_key(
-			     dispatch,
-			     device,
-			     time,
-			     e->code,
-			     e->value ? LIBINPUT_KEY_STATE_PRESSED :
-					LIBINPUT_KEY_STATE_RELEASED);
-		break;
-	case KEY_TYPE_BUTTON:
-		break;
+	if (is_key) {
+		fallback_keyboard_notify_key(dispatch,
+					     device,
+					     time,
+					     e->usage,
+					     e->value ? LIBINPUT_KEY_STATE_PRESSED
+						      : LIBINPUT_KEY_STATE_RELEASED);
 	}
 }
 
 static void
 fallback_process_touch(struct fallback_dispatch *dispatch,
 		       struct evdev_device *device,
-		       struct input_event *e,
+		       struct evdev_event *e,
 		       uint64_t time)
 {
 	struct mt_slot *slot = &dispatch->mt.slots[dispatch->mt.slot];
 
-	if (e->code == ABS_MT_SLOT) {
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_ABS_MT_SLOT:
 		if ((size_t)e->value >= dispatch->mt.slots_len) {
 			evdev_log_bug_libinput(device,
-					 "exceeded slot count (%d of max %zd)\n",
-					 e->value,
-					 dispatch->mt.slots_len);
+					       "exceeded slot count (%d of max %zd)\n",
+					       e->value,
+					       dispatch->mt.slots_len);
 			e->value = dispatch->mt.slots_len - 1;
 		}
 		dispatch->mt.slot = e->value;
 		return;
-	}
-
-	switch (e->code) {
-	case ABS_MT_TRACKING_ID:
+	case EVDEV_ABS_MT_TRACKING_ID:
 		if (e->value >= 0) {
 			dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
 			slot->state = SLOT_STATE_BEGIN;
@@ -568,19 +667,19 @@ fallback_process_touch(struct fallback_d
 		}
 		slot->dirty = true;
 		break;
-	case ABS_MT_POSITION_X:
-		evdev_device_check_abs_axis_range(device, e->code, e->value);
+	case EVDEV_ABS_MT_POSITION_X:
+		evdev_device_check_abs_axis_range(device, e->usage, e->value);
 		dispatch->mt.slots[dispatch->mt.slot].point.x = e->value;
 		dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
 		slot->dirty = true;
 		break;
-	case ABS_MT_POSITION_Y:
-		evdev_device_check_abs_axis_range(device, e->code, e->value);
+	case EVDEV_ABS_MT_POSITION_Y:
+		evdev_device_check_abs_axis_range(device, e->usage, e->value);
 		dispatch->mt.slots[dispatch->mt.slot].point.y = e->value;
 		dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
 		slot->dirty = true;
 		break;
-	case ABS_MT_TOOL_TYPE:
+	case EVDEV_ABS_MT_TOOL_TYPE:
 		/* The transitions matter - we (may) need to send a touch
 		 * cancel event if we just switched to a palm touch. And the
 		 * kernel may switch back to finger but we keep the touch as
@@ -600,32 +699,34 @@ fallback_process_touch(struct fallback_d
 		dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
 		slot->dirty = true;
 		break;
+	default:
+		break;
 	}
 }
 
 static inline void
 fallback_process_absolute_motion(struct fallback_dispatch *dispatch,
 				 struct evdev_device *device,
-				 struct input_event *e)
+				 struct evdev_event *e)
 {
-	switch (e->code) {
-	case ABS_X:
-		evdev_device_check_abs_axis_range(device, e->code, e->value);
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_ABS_X:
+		evdev_device_check_abs_axis_range(device, e->usage, e->value);
 		dispatch->abs.point.x = e->value;
 		dispatch->pending_event |= EVDEV_ABSOLUTE_MOTION;
 		break;
-	case ABS_Y:
-		evdev_device_check_abs_axis_range(device, e->code, e->value);
+	case EVDEV_ABS_Y:
+		evdev_device_check_abs_axis_range(device, e->usage, e->value);
 		dispatch->abs.point.y = e->value;
 		dispatch->pending_event |= EVDEV_ABSOLUTE_MOTION;
 		break;
+	default:
+		break;
 	}
 }
 
 static void
-fallback_lid_keyboard_event(uint64_t time,
-			    struct libinput_event *event,
-			    void *data)
+fallback_lid_keyboard_event(uint64_t time, struct libinput_event *event, void *data)
 {
 	struct fallback_dispatch *dispatch = fallback_dispatch(data);
 
@@ -672,11 +773,10 @@ fallback_lid_toggle_keyboard_listener(st
 	libinput_device_remove_event_listener(&kbd->listener);
 
 	if (is_closed) {
-		libinput_device_add_event_listener(
-					&kbd->device->base,
-					&kbd->listener,
-					fallback_lid_keyboard_event,
-					dispatch);
+		libinput_device_add_event_listener(&kbd->device->base,
+						   &kbd->listener,
+						   fallback_lid_keyboard_event,
+						   dispatch);
 	} else {
 		libinput_device_init_event_listener(&kbd->listener);
 	}
@@ -692,16 +792,14 @@ fallback_lid_toggle_keyboard_listeners(s
 		if (!kbd->device)
 			continue;
 
-		fallback_lid_toggle_keyboard_listener(dispatch,
-						      kbd,
-						      is_closed);
+		fallback_lid_toggle_keyboard_listener(dispatch, kbd, is_closed);
 	}
 }
 
 static inline void
 fallback_process_switch(struct fallback_dispatch *dispatch,
 			struct evdev_device *device,
-			struct input_event *e,
+			struct evdev_event *e,
 			uint64_t time)
 {
 	enum libinput_switch_state state;
@@ -709,8 +807,8 @@ fallback_process_switch(struct fallback_
 
 	/* TODO: this should to move to handle_state */
 
-	switch (e->code) {
-	case SW_LID:
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_SW_LID:
 		is_closed = !!e->value;
 
 		fallback_lid_toggle_keyboard_listeners(dispatch, is_closed);
@@ -721,7 +819,7 @@ fallback_process_switch(struct fallback_
 		dispatch->lid.is_closed = is_closed;
 		fallback_lid_notify_toggle(dispatch, device, time);
 		break;
-	case SW_TABLET_MODE:
+	case EVDEV_SW_TABLET_MODE:
 		if (dispatch->tablet_mode.sw.state == e->value)
 			return;
 
@@ -735,51 +833,88 @@ fallback_process_switch(struct fallback_
 				     LIBINPUT_SWITCH_TABLET_MODE,
 				     state);
 		break;
+	default:
+		break;
 	}
 }
 
 static inline bool
 fallback_reject_relative(struct evdev_device *device,
-			 const struct input_event *e,
+			 const struct evdev_event *e,
 			 uint64_t time)
 {
-	if ((e->code == REL_X || e->code == REL_Y) &&
-	    (device->seat_caps & EVDEV_DEVICE_POINTER) == 0) {
-		evdev_log_bug_libinput_ratelimit(device,
-						 &device->nonpointer_rel_limit,
-						 "REL_X/Y from a non-pointer device\n");
-		return true;
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_REL_X:
+	case EVDEV_REL_Y:
+		if ((device->seat_caps & EVDEV_DEVICE_POINTER) == 0) {
+			evdev_log_bug_libinput_ratelimit(
+				device,
+				&device->nonpointer_rel_limit,
+				"REL_X/Y from a non-pointer device\n");
+			return true;
+		}
+		break;
+	default:
+		break;
 	}
-
 	return false;
 }
 
+static void
+fallback_rotate_wheel(struct fallback_dispatch *dispatch, struct evdev_event *e)
+{
+	/* Special case: if we're upside down (-ish),
+	 * swap the direction of the wheels so that user-down
+	 * means scroll down. This isn't done for any other angle
+	 * since it's not clear what the heuristics should be.*/
+	if (dispatch->rotation.angle >= 160.0 && dispatch->rotation.angle <= 220.0) {
+		e->value *= -1;
+	}
+}
+
 static inline void
 fallback_process_relative(struct fallback_dispatch *dispatch,
 			  struct evdev_device *device,
-			  struct input_event *e, uint64_t time)
+			  struct evdev_event *e,
+			  uint64_t time)
 {
 	if (fallback_reject_relative(device, e, time))
 		return;
 
-	switch (e->code) {
-	case REL_X:
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_REL_X:
 		dispatch->rel.x += e->value;
 		dispatch->pending_event |= EVDEV_RELATIVE_MOTION;
 		break;
-	case REL_Y:
+	case EVDEV_REL_Y:
 		dispatch->rel.y += e->value;
 		dispatch->pending_event |= EVDEV_RELATIVE_MOTION;
 		break;
+	case EVDEV_REL_WHEEL:
+		fallback_rotate_wheel(dispatch, e);
+		dispatch->wheel.lo_res.y += e->value;
+		break;
+	case EVDEV_REL_HWHEEL:
+		fallback_rotate_wheel(dispatch, e);
+		dispatch->wheel.lo_res.x += e->value;
+		break;
+	case EVDEV_REL_WHEEL_HI_RES:
+		fallback_rotate_wheel(dispatch, e);
+		dispatch->wheel.hi_res.y += e->value;
+		break;
+	case EVDEV_REL_HWHEEL_HI_RES:
+		fallback_rotate_wheel(dispatch, e);
+		dispatch->wheel.hi_res.x += e->value;
+		break;
+	default:
+		break;
 	}
-
-	fallback_wheel_process_relative(dispatch, device, e, time);
 }
 
 static inline void
 fallback_process_absolute(struct fallback_dispatch *dispatch,
 			  struct evdev_device *device,
-			  struct input_event *e,
+			  struct evdev_event *e,
 			  uint64_t time)
 {
 	if (device->is_mt) {
@@ -791,21 +926,22 @@ fallback_process_absolute(struct fallbac
 
 static inline bool
 fallback_any_button_down(struct fallback_dispatch *dispatch,
-		      struct evdev_device *device)
+			 struct evdev_device *device)
 {
-	unsigned int button;
-
-	for (button = BTN_LEFT; button < BTN_JOYSTICK; button++) {
-		if (libevdev_has_event_code(device->evdev, EV_KEY, button) &&
-		    hw_is_key_down(dispatch, button))
+	for (evdev_usage_t usage = evdev_usage_from(EVDEV_BTN_LEFT);
+	     evdev_usage_lt(usage, EVDEV_BTN_JOYSTICK);
+	     usage = evdev_usage_next(usage)) {
+		if (libevdev_has_event_code(device->evdev,
+					    EV_KEY,
+					    evdev_usage_code(usage)) &&
+		    hw_is_key_down(dispatch, usage))
 			return true;
 	}
 	return false;
 }
 
 static inline bool
-fallback_arbitrate_touch(struct fallback_dispatch *dispatch,
-			 struct mt_slot *slot)
+fallback_arbitrate_touch(struct fallback_dispatch *dispatch, struct mt_slot *slot)
 {
 	bool discard = false;
 	struct device_coords point = slot->point;
@@ -849,8 +985,7 @@ fallback_flush_mt_events(struct fallback
 		} else if (slot->palm_state == PALM_NONE) {
 			switch (slot->state) {
 			case SLOT_STATE_BEGIN:
-				if (!fallback_arbitrate_touch(dispatch,
-							     slot)) {
+				if (!fallback_arbitrate_touch(dispatch, slot)) {
 					sent = fallback_flush_mt_down(dispatch,
 								      device,
 								      i,
@@ -864,10 +999,7 @@ fallback_flush_mt_events(struct fallback
 								time);
 				break;
 			case SLOT_STATE_END:
-				sent = fallback_flush_mt_up(dispatch,
-							    device,
-							    i,
-							    time);
+				sent = fallback_flush_mt_up(dispatch, device, i, time);
 				break;
 			case SLOT_STATE_NONE:
 				break;
@@ -912,14 +1044,10 @@ fallback_handle_state(struct fallback_di
 			need_touch_frame = true;
 	} else if (dispatch->pending_event & EVDEV_ABSOLUTE_MOTION) {
 		if (device->seat_caps & EVDEV_DEVICE_TOUCH) {
-			if (fallback_flush_st_motion(dispatch,
-						     device,
-						     time))
+			if (fallback_flush_st_motion(dispatch, device, time))
 				need_touch_frame = true;
 		} else if (device->seat_caps & EVDEV_DEVICE_POINTER) {
-			fallback_flush_absolute_motion(dispatch,
-						       device,
-						       time);
+			fallback_flush_absolute_motion(dispatch, device, time);
 		}
 	}
 
@@ -930,31 +1058,36 @@ fallback_handle_state(struct fallback_di
 
 	/* Multitouch devices */
 	if (dispatch->pending_event & EVDEV_ABSOLUTE_MT)
-		need_touch_frame = fallback_flush_mt_events(dispatch,
-							    device,
-							    time);
+		need_touch_frame = fallback_flush_mt_events(dispatch, device, time);
 
 	if (need_touch_frame)
 		touch_notify_frame(&device->base, time);
 
-	fallback_wheel_handle_state(dispatch, device, time);
+	fallback_flush_wheels(dispatch, device, time);
 
 	/* Buttons and keys */
 	if (dispatch->pending_event & EVDEV_KEY) {
-		bool want_debounce = false;
-		for (unsigned int code = 0; code <= KEY_MAX; code++) {
-			if (!hw_key_has_changed(dispatch, code))
+		for (evdev_usage_t usage = evdev_usage_from(EVDEV_KEY_RESERVED);
+		     evdev_usage_le(usage, EVDEV_KEY_MAX);
+		     usage = evdev_usage_next(usage)) {
+			if (!hw_key_has_changed(dispatch, usage))
 				continue;
 
-			if (get_key_type(code) == KEY_TYPE_BUTTON) {
-				want_debounce = true;
-				break;
+			if (evdev_usage_is_button(usage)) {
+				enum libinput_button_state state =
+					hw_is_key_down(dispatch, usage)
+						? LIBINPUT_BUTTON_STATE_PRESSED
+						: LIBINPUT_BUTTON_STATE_RELEASED;
+				evdev_usage_t button =
+					evdev_to_left_handed(device, usage);
+				fallback_notify_physical_button(dispatch,
+								device,
+								time,
+								button,
+								state);
 			}
 		}
 
-		if (want_debounce)
-			fallback_debounce_handle_state(dispatch, time);
-
 		hw_key_update_last_state(dispatch);
 	}
 
@@ -962,17 +1095,18 @@ fallback_handle_state(struct fallback_di
 }
 
 static void
-fallback_interface_process(struct evdev_dispatch *evdev_dispatch,
-			   struct evdev_device *device,
-			   struct input_event *event,
-			   uint64_t time)
+fallback_interface_process_event(struct evdev_dispatch *evdev_dispatch,
+				 struct evdev_device *device,
+				 struct evdev_event *event,
+				 uint64_t time)
 {
 	struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
 	static bool warned = false;
 
 	if (dispatch->arbitration.in_arbitration) {
 		if (!warned) {
-			evdev_log_debug(device, "dropping events due to touch arbitration\n");
+			evdev_log_debug(device,
+					"dropping events due to touch arbitration\n");
 			warned = true;
 		}
 		return;
@@ -980,7 +1114,8 @@ fallback_interface_process(struct evdev_
 
 	warned = false;
 
-	switch (event->type) {
+	uint16_t type = evdev_event_type(event);
+	switch (type) {
 	case EV_REL:
 		fallback_process_relative(dispatch, device, event, time);
 		break;
@@ -1000,6 +1135,20 @@ fallback_interface_process(struct evdev_
 }
 
 static void
+fallback_interface_process(struct evdev_dispatch *dispatch,
+			   struct evdev_device *device,
+			   struct evdev_frame *frame,
+			   uint64_t time)
+{
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+
+	for (size_t i = 0; i < nevents; i++) {
+		fallback_interface_process_event(dispatch, device, &events[i], time);
+	}
+}
+
+static void
 cancel_touches(struct fallback_dispatch *dispatch,
 	       struct evdev_device *device,
 	       const struct device_coord_rect *rect,
@@ -1012,9 +1161,7 @@ cancel_touches(struct fallback_dispatch
 	point = dispatch->abs.point;
 	evdev_transform_absolute(device, &point);
 	if (!rect || point_in_rect(&point, rect))
-		need_frame = fallback_flush_st_cancel(dispatch,
-						      device,
-						      time);
+		need_frame = fallback_flush_st_cancel(dispatch, device, time);
 
 	for (idx = 0; idx < dispatch->mt.slots_len; idx++) {
 		struct mt_slot *slot = &dispatch->mt.slots[idx];
@@ -1038,10 +1185,10 @@ release_pressed_keys(struct fallback_dis
 		     struct evdev_device *device,
 		     uint64_t time)
 {
-	int code;
-
-	for (code = 0; code < KEY_CNT; code++) {
-		int count = get_key_down_count(device, code);
+	for (evdev_usage_t usage = evdev_usage_from(EVDEV_KEY_RESERVED);
+	     evdev_usage_le(usage, EVDEV_KEY_MAX);
+	     usage = evdev_usage_next(usage)) {
+		int count = get_key_down_count(device, usage);
 
 		if (count == 0)
 			continue;
@@ -1049,40 +1196,33 @@ release_pressed_keys(struct fallback_dis
 		if (count > 1) {
 			evdev_log_bug_libinput(device,
 					       "key %d is down %d times.\n",
-					       code,
+					       evdev_usage_code(usage),
 					       count);
 		}
 
-		switch (get_key_type(code)) {
-		case KEY_TYPE_NONE:
-			break;
-		case KEY_TYPE_KEY:
-			fallback_keyboard_notify_key(
-				dispatch,
-				device,
-				time,
-				code,
-				LIBINPUT_KEY_STATE_RELEASED);
-			break;
-		case KEY_TYPE_BUTTON:
+		if (evdev_usage_is_key(usage)) {
+			fallback_keyboard_notify_key(dispatch,
+						     device,
+						     time,
+						     usage,
+						     LIBINPUT_KEY_STATE_RELEASED);
+		} else if (evdev_usage_is_button(usage)) {
 			/* Note: the left-handed configuration is nonzero for
 			 * the mapped button (not the physical button), in
 			 * get_key_down_count(). We must not map this to left-handed
 			 * again, see #881.
 			 */
-			evdev_pointer_notify_button(
-				device,
-				time,
-				code,
-				LIBINPUT_BUTTON_STATE_RELEASED);
-			break;
+			evdev_pointer_notify_button(device,
+						    time,
+						    usage,
+						    LIBINPUT_BUTTON_STATE_RELEASED);
 		}
 
-		count = get_key_down_count(device, code);
+		count = get_key_down_count(device, usage);
 		if (count != 0) {
 			evdev_log_bug_libinput(device,
 					       "releasing key %d failed.\n",
-					       code);
+					       evdev_usage_enum(usage));
 			break;
 		}
 	}
@@ -1119,16 +1259,13 @@ fallback_interface_remove(struct evdev_d
 	struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
 	struct evdev_paired_keyboard *kbd;
 
-	libinput_timer_cancel(&dispatch->wheel.scroll_timer);
 	libinput_timer_cancel(&dispatch->debounce.timer);
 	libinput_timer_cancel(&dispatch->debounce.timer_short);
 	libinput_timer_cancel(&dispatch->arbitration.arbitration_timer);
 
 	libinput_device_remove_event_listener(&dispatch->tablet_mode.other.listener);
 
-	list_for_each_safe(kbd,
-			   &dispatch->lid.paired_keyboard_list,
-			   link) {
+	list_for_each_safe(kbd, &dispatch->lid.paired_keyboard_list, link) {
 		evdev_paired_keyboard_destroy(kbd);
 	}
 }
@@ -1143,9 +1280,8 @@ fallback_interface_sync_initial_state(st
 	if (device->tags & EVDEV_TAG_LID_SWITCH) {
 		struct libevdev *evdev = device->evdev;
 
-		dispatch->lid.is_closed = libevdev_get_event_value(evdev,
-								   EV_SW,
-								   SW_LID);
+		dispatch->lid.is_closed =
+			libevdev_get_event_value(evdev, EV_SW, SW_LID);
 		dispatch->lid.is_closed_client_state = false;
 
 		/* For the initial state sync, we depend on whether the lid switch
@@ -1172,8 +1308,8 @@ fallback_interface_sync_initial_state(st
 static void
 fallback_interface_update_rect(struct evdev_dispatch *evdev_dispatch,
 			       struct evdev_device *device,
-				const struct phys_rect *phys_rect,
-				uint64_t time)
+			       const struct phys_rect *phys_rect,
+			       uint64_t time)
 {
 	struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
 	struct device_coord_rect rect;
@@ -1195,7 +1331,7 @@ fallback_interface_toggle_touch(struct e
 				uint64_t time)
 {
 	struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
-	struct device_coord_rect rect = {0};
+	struct device_coord_rect rect = { 0 };
 	const char *state = NULL;
 
 	if (which == dispatch->arbitration.state)
@@ -1239,7 +1375,6 @@ fallback_interface_destroy(struct evdev_
 {
 	struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
 
-	libinput_timer_destroy(&dispatch->wheel.scroll_timer);
 	libinput_timer_destroy(&dispatch->arbitration.arbitration_timer);
 	libinput_timer_destroy(&dispatch->debounce.timer);
 	libinput_timer_destroy(&dispatch->debounce.timer_short);
@@ -1252,8 +1387,7 @@ static void
 fallback_lid_pair_keyboard(struct evdev_device *lid_switch,
 			   struct evdev_device *keyboard)
 {
-	struct fallback_dispatch *dispatch =
-		fallback_dispatch(lid_switch->dispatch);
+	struct fallback_dispatch *dispatch = fallback_dispatch(lid_switch->dispatch);
 	struct evdev_paired_keyboard *kbd;
 	size_t count = 0;
 
@@ -1291,8 +1425,7 @@ fallback_lid_pair_keyboard(struct evdev_
 }
 
 static void
-fallback_resume(struct fallback_dispatch *dispatch,
-		struct evdev_device *device)
+fallback_resume(struct fallback_dispatch *dispatch, struct evdev_device *device)
 {
 	if (dispatch->base.sendevents.current_mode ==
 	    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED)
@@ -1302,8 +1435,7 @@ fallback_resume(struct fallback_dispatch
 }
 
 static void
-fallback_suspend(struct fallback_dispatch *dispatch,
-		 struct evdev_device *device)
+fallback_suspend(struct fallback_dispatch *dispatch, struct evdev_device *device)
 {
 	evdev_device_suspend(device);
 }
@@ -1321,8 +1453,7 @@ fallback_tablet_mode_switch_event(uint64
 		return;
 
 	swev = libinput_event_get_switch_event(event);
-	if (libinput_event_switch_get_switch(swev) !=
-	    LIBINPUT_SWITCH_TABLET_MODE)
+	if (libinput_event_switch_get_switch(swev) != LIBINPUT_SWITCH_TABLET_MODE)
 		return;
 
 	switch (libinput_event_switch_get_switch_state(swev)) {
@@ -1341,23 +1472,21 @@ static void
 fallback_pair_tablet_mode(struct evdev_device *keyboard,
 			  struct evdev_device *tablet_mode_switch)
 {
-	struct fallback_dispatch *dispatch =
-		fallback_dispatch(keyboard->dispatch);
+	struct fallback_dispatch *dispatch = fallback_dispatch(keyboard->dispatch);
 
-	if ((keyboard->tags & EVDEV_TAG_EXTERNAL_KEYBOARD))
+	if (keyboard->tags & EVDEV_TAG_EXTERNAL_KEYBOARD)
 		return;
 
-	if ((keyboard->tags & EVDEV_TAG_TRACKPOINT)) {
+	if (keyboard->tags & EVDEV_TAG_TRACKPOINT) {
 		if (keyboard->tags & EVDEV_TAG_EXTERNAL_MOUSE)
 			return;
-	/* This filters out all internal keyboard-like devices (Video
-	 * Switch) */
+		/* This filters out all internal keyboard-like devices (Video
+		 * Switch) */
 	} else if ((keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD) == 0) {
 		return;
 	}
 
-	if (evdev_device_has_model_quirk(keyboard,
-					 QUIRK_MODEL_TABLET_MODE_NO_SUSPEND))
+	if (evdev_device_has_model_quirk(keyboard, QUIRK_MODEL_TABLET_MODE_NO_SUSPEND))
 		return;
 
 	if ((tablet_mode_switch->tags & EVDEV_TAG_TABLET_MODE_SWITCH) == 0)
@@ -1372,14 +1501,14 @@ fallback_pair_tablet_mode(struct evdev_d
 			tablet_mode_switch->devname);
 
 	libinput_device_add_event_listener(&tablet_mode_switch->base,
-				&dispatch->tablet_mode.other.listener,
-				fallback_tablet_mode_switch_event,
-				dispatch);
+					   &dispatch->tablet_mode.other.listener,
+					   fallback_tablet_mode_switch_event,
+					   dispatch);
 	dispatch->tablet_mode.other.sw_device = tablet_mode_switch;
 
 	if (evdev_device_switch_get_state(tablet_mode_switch,
-					  LIBINPUT_SWITCH_TABLET_MODE)
-		    == LIBINPUT_SWITCH_STATE_ON) {
+					  LIBINPUT_SWITCH_TABLET_MODE) ==
+	    LIBINPUT_SWITCH_STATE_ON) {
 		evdev_log_debug(keyboard, "tablet-mode: suspending device\n");
 		fallback_suspend(dispatch, keyboard);
 	}
@@ -1397,13 +1526,10 @@ static void
 fallback_interface_device_removed(struct evdev_device *device,
 				  struct evdev_device *removed_device)
 {
-	struct fallback_dispatch *dispatch =
-			fallback_dispatch(device->dispatch);
+	struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch);
 	struct evdev_paired_keyboard *kbd;
 
-	list_for_each_safe(kbd,
-			   &dispatch->lid.paired_keyboard_list,
-			   link) {
+	list_for_each_safe(kbd, &dispatch->lid.paired_keyboard_list, link) {
 		if (!kbd->device)
 			continue;
 
@@ -1415,9 +1541,9 @@ fallback_interface_device_removed(struct
 
 	if (removed_device == dispatch->tablet_mode.other.sw_device) {
 		libinput_device_remove_event_listener(
-				&dispatch->tablet_mode.other.listener);
+			&dispatch->tablet_mode.other.listener);
 		libinput_device_init_event_listener(
-				&dispatch->tablet_mode.other.listener);
+			&dispatch->tablet_mode.other.listener);
 		dispatch->tablet_mode.other.sw_device = NULL;
 	}
 }
@@ -1430,7 +1556,7 @@ static struct evdev_dispatch_interface f
 	.device_added = fallback_interface_device_added,
 	.device_removed = fallback_interface_device_removed,
 	.device_suspended = fallback_interface_device_removed, /* treat as remove */
-	.device_resumed = fallback_interface_device_added,   /* treat as add */
+	.device_resumed = fallback_interface_device_added,     /* treat as add */
 	.post_added = fallback_interface_sync_initial_state,
 	.touch_arbitration_toggle = fallback_interface_toggle_touch,
 	.touch_arbitration_update_rect = fallback_interface_update_rect,
@@ -1457,7 +1583,7 @@ fallback_change_scroll_method(struct evd
 	struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch);
 
 	if (device->scroll.want_method == device->scroll.method &&
-	    device->scroll.want_button == device->scroll.button &&
+	    evdev_usage_cmp(device->scroll.want_button, device->scroll.button) == 0 &&
 	    device->scroll.want_lock_enabled == device->scroll.lock_enabled)
 		return;
 
@@ -1479,7 +1605,7 @@ fallback_rotation_config_is_available(st
 
 static enum libinput_config_status
 fallback_rotation_config_set_angle(struct libinput_device *libinput_device,
-				unsigned int degrees_cw)
+				   unsigned int degrees_cw)
 {
 	struct evdev_device *device = evdev_device(libinput_device);
 	struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch);
@@ -1506,8 +1632,7 @@ fallback_rotation_config_get_default_ang
 }
 
 static void
-fallback_init_rotation(struct fallback_dispatch *dispatch,
-		       struct evdev_device *device)
+fallback_init_rotation(struct fallback_dispatch *dispatch, struct evdev_device *device)
 {
 	if (device->tags & EVDEV_TAG_TRACKPOINT)
 		return;
@@ -1515,7 +1640,8 @@ fallback_init_rotation(struct fallback_d
 	dispatch->rotation.config.is_available = fallback_rotation_config_is_available;
 	dispatch->rotation.config.set_angle = fallback_rotation_config_set_angle;
 	dispatch->rotation.config.get_angle = fallback_rotation_config_get_angle;
-	dispatch->rotation.config.get_default_angle = fallback_rotation_config_get_default_angle;
+	dispatch->rotation.config.get_default_angle =
+		fallback_rotation_config_get_default_angle;
 	matrix_init_identity(&dispatch->rotation.matrix);
 	device->base.config.rotation = &dispatch->rotation.config;
 }
@@ -1533,51 +1659,35 @@ fallback_dispatch_init_slots(struct fall
 	if (evdev_is_fake_mt_device(device) ||
 	    !libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) ||
 	    !libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y))
-		 return 0;
+		return 0;
 
 	/* We only handle the slotted Protocol B in libinput.
 	   Devices with ABS_MT_POSITION_* but not ABS_MT_SLOT
 	   require mtdev for conversion. */
-	if (evdev_need_mtdev(device)) {
-		device->mtdev = mtdev_new_open(device->fd);
-		if (!device->mtdev)
-			return -1;
-
-		/* pick 10 slots as default for type A
-		   devices. */
-		num_slots = 10;
-		active_slot = device->mtdev->caps.slot.value;
-	} else {
-		num_slots = libevdev_get_num_slots(device->evdev);
-		active_slot = libevdev_get_current_slot(evdev);
-	}
+	if (!libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT))
+		return 0;
 
+	num_slots = libevdev_get_num_slots(device->evdev);
+	active_slot = libevdev_get_current_slot(evdev);
 	slots = zalloc(num_slots * sizeof(struct mt_slot));
 
 	for (slot = 0; slot < num_slots; ++slot) {
 		slots[slot].seat_slot = -1;
-
-		if (evdev_need_mtdev(device))
-			continue;
-
-		slots[slot].point.x = libevdev_get_slot_value(evdev,
-							      slot,
-							      ABS_MT_POSITION_X);
-		slots[slot].point.y = libevdev_get_slot_value(evdev,
-							      slot,
-							      ABS_MT_POSITION_Y);
+		slots[slot].point.x =
+			libevdev_get_slot_value(evdev, slot, ABS_MT_POSITION_X);
+		slots[slot].point.y =
+			libevdev_get_slot_value(evdev, slot, ABS_MT_POSITION_Y);
 	}
 	dispatch->mt.slots = slots;
 	dispatch->mt.slots_len = num_slots;
 	dispatch->mt.slot = active_slot;
-	dispatch->mt.has_palm = libevdev_has_event_code(evdev,
-							EV_ABS,
-							ABS_MT_TOOL_TYPE);
+	dispatch->mt.has_palm =
+		libevdev_has_event_code(evdev, EV_ABS, ABS_MT_TOOL_TYPE);
 
 	if (device->abs.absinfo_x->fuzz || device->abs.absinfo_y->fuzz) {
 		dispatch->mt.want_hysteresis = true;
-		dispatch->mt.hysteresis_margin.x = device->abs.absinfo_x->fuzz/2;
-		dispatch->mt.hysteresis_margin.y = device->abs.absinfo_y->fuzz/2;
+		dispatch->mt.hysteresis_margin.x = device->abs.absinfo_x->fuzz / 2;
+		dispatch->mt.hysteresis_margin.y = device->abs.absinfo_y->fuzz / 2;
 	}
 
 	return 0;
@@ -1619,9 +1729,7 @@ fallback_dispatch_init_switch(struct fal
 	}
 
 	if (device->tags & EVDEV_TAG_TABLET_MODE_SWITCH) {
-		val = libevdev_get_event_value(device->evdev,
-					       EV_SW,
-					       SW_TABLET_MODE);
+		val = libevdev_get_event_value(device->evdev, EV_SW, SW_TABLET_MODE);
 		dispatch->tablet_mode.sw.state = val;
 	}
 
@@ -1647,8 +1755,8 @@ fallback_init_arbitration(struct fallbac
 
 	snprintf(timer_name,
 		 sizeof(timer_name),
-		  "%s arbitration",
-		  evdev_device_get_sysname(device));
+		 "%s arbitration",
+		 evdev_device_get_sysname(device));
 	libinput_timer_init(&dispatch->arbitration.arbitration_timer,
 			    evdev_libinput_context(device),
 			    timer_name,
@@ -1680,12 +1788,10 @@ fallback_dispatch_create(struct libinput
 	fallback_dispatch_init_switch(dispatch, device);
 
 	if (device->left_handed.want_enabled)
-		evdev_init_left_handed(device,
-				       fallback_change_to_left_handed);
+		evdev_init_left_handed(device, fallback_change_to_left_handed);
 
-	if (device->scroll.want_button)
-		evdev_init_button_scroll(device,
-					 fallback_change_scroll_method);
+	if (evdev_usage_enum(device->scroll.want_button))
+		evdev_init_button_scroll(device, fallback_change_scroll_method);
 
 	if (device->scroll.natural_scrolling_enabled)
 		evdev_init_natural_scroll(device);
@@ -1701,19 +1807,14 @@ fallback_dispatch_create(struct libinput
 	 * option */
 	if (libevdev_has_event_code(device->evdev, EV_KEY, BTN_LEFT) &&
 	    libevdev_has_event_code(device->evdev, EV_KEY, BTN_RIGHT)) {
-		bool has_middle = libevdev_has_event_code(device->evdev,
-							  EV_KEY,
-							  BTN_MIDDLE);
+		bool has_middle =
+			libevdev_has_event_code(device->evdev, EV_KEY, BTN_MIDDLE);
 		bool want_config = has_middle;
 		bool enable_by_default = !has_middle;
 
-		evdev_init_middlebutton(device,
-					enable_by_default,
-					want_config);
+		evdev_init_middlebutton(device, enable_by_default, want_config);
 	}
 
-	fallback_init_wheel(dispatch, device);
-	fallback_init_debounce(dispatch);
 	fallback_init_arbitration(dispatch, device);
 
 	return &dispatch->base;
diff -pruN 1.28.1-1/src/evdev-fallback.h 1.30.0-1/src/evdev-fallback.h
--- 1.28.1-1/src/evdev-fallback.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-fallback.h	2025-11-25 03:40:43.000000000 +0000
@@ -29,6 +29,8 @@
 #ifndef EVDEV_FALLBACK_H
 #define EVDEV_FALLBACK_H
 
+#include "util-input-event.h"
+
 #include "evdev.h"
 
 enum debounce_state {
@@ -59,20 +61,6 @@ enum palm_state {
 	PALM_WAS_PALM, /* this touch sequence was a palm but isn't now */
 };
 
-enum wheel_state {
-	WHEEL_STATE_NONE,
-	WHEEL_STATE_ACCUMULATING_SCROLL,
-	WHEEL_STATE_SCROLLING,
-};
-
-enum wheel_direction {
-	WHEEL_DIR_UNKNOW,
-	WHEEL_DIR_VPOS,
-	WHEEL_DIR_VNEG,
-	WHEEL_DIR_HPOS,
-	WHEEL_DIR_HNEG,
-};
-
 struct mt_slot {
 	bool dirty;
 	enum mt_slot_state state;
@@ -111,13 +99,8 @@ struct fallback_dispatch {
 	struct device_coords rel;
 
 	struct {
-		enum wheel_state state;
 		struct device_coords lo_res;
 		struct device_coords hi_res;
-		bool emulate_hi_res_wheel;
-		bool hi_res_event_received;
-		struct libinput_timer scroll_timer;
-		enum wheel_direction dir;
 	} wheel;
 
 	struct {
@@ -141,7 +124,7 @@ struct fallback_dispatch {
 	enum evdev_event_type pending_event;
 
 	struct {
-		unsigned int button_code;
+		evdev_usage_t button_usage;
 		uint64_t button_time;
 		struct libinput_timer timer;
 		struct libinput_timer timer_short;
@@ -174,7 +157,7 @@ struct fallback_dispatch {
 	} arbitration;
 };
 
-static inline struct fallback_dispatch*
+static inline struct fallback_dispatch *
 fallback_dispatch(struct evdev_dispatch *dispatch)
 {
 	evdev_verify_dispatch_type(dispatch, DISPATCH_FALLBACK);
@@ -182,65 +165,30 @@ fallback_dispatch(struct evdev_dispatch
 	return container_of(dispatch, struct fallback_dispatch, base);
 }
 
-enum key_type {
-	KEY_TYPE_NONE,
-	KEY_TYPE_KEY,
-	KEY_TYPE_BUTTON,
-};
-
-static inline enum key_type
-get_key_type(uint16_t code)
-{
-	switch (code) {
-	case BTN_TOOL_PEN:
-	case BTN_TOOL_RUBBER:
-	case BTN_TOOL_BRUSH:
-	case BTN_TOOL_PENCIL:
-	case BTN_TOOL_AIRBRUSH:
-	case BTN_TOOL_MOUSE:
-	case BTN_TOOL_LENS:
-	case BTN_TOOL_QUINTTAP:
-	case BTN_TOOL_DOUBLETAP:
-	case BTN_TOOL_TRIPLETAP:
-	case BTN_TOOL_QUADTAP:
-	case BTN_TOOL_FINGER:
-	case BTN_TOUCH:
-		return KEY_TYPE_NONE;
-	}
-
-	if (code >= KEY_ESC && code <= KEY_MICMUTE)
-		return KEY_TYPE_KEY;
-	if (code >= BTN_MISC && code <= BTN_GEAR_UP)
-		return KEY_TYPE_BUTTON;
-	if (code >= KEY_OK && code <= KEY_LIGHTS_TOGGLE)
-		return KEY_TYPE_KEY;
-	if (code >= BTN_DPAD_UP && code <= BTN_DPAD_RIGHT)
-		return KEY_TYPE_BUTTON;
-	if (code >= KEY_ALS_TOGGLE && code < BTN_TRIGGER_HAPPY)
-		return KEY_TYPE_KEY;
-	if (code >= BTN_TRIGGER_HAPPY && code <= BTN_TRIGGER_HAPPY40)
-		return KEY_TYPE_BUTTON;
-	return KEY_TYPE_NONE;
-}
-
 static inline void
-hw_set_key_down(struct fallback_dispatch *dispatch, int code, int pressed)
+hw_set_key_down(struct fallback_dispatch *dispatch, evdev_usage_t usage, int pressed)
 {
+	assert(evdev_usage_type(usage) == EV_KEY);
+
+	unsigned int code = evdev_usage_code(usage);
 	long_set_bit_state(dispatch->hw_key_mask, code, pressed);
 }
 
 static inline bool
-hw_key_has_changed(struct fallback_dispatch *dispatch, int code)
+hw_key_has_changed(struct fallback_dispatch *dispatch, evdev_usage_t usage)
 {
+	assert(evdev_usage_type(usage) == EV_KEY);
+
+	unsigned int code = evdev_usage_code(usage);
 	return long_bit_is_set(dispatch->hw_key_mask, code) !=
-		long_bit_is_set(dispatch->last_hw_key_mask, code);
+	       long_bit_is_set(dispatch->last_hw_key_mask, code);
 }
 
 static inline void
 hw_key_update_last_state(struct fallback_dispatch *dispatch)
 {
 	static_assert(sizeof(dispatch->hw_key_mask) ==
-		      sizeof(dispatch->last_hw_key_mask),
+			      sizeof(dispatch->last_hw_key_mask),
 		      "Mismatching key mask size");
 
 	memcpy(dispatch->last_hw_key_mask,
@@ -249,39 +197,28 @@ hw_key_update_last_state(struct fallback
 }
 
 static inline bool
-hw_is_key_down(struct fallback_dispatch *dispatch, int code)
+hw_is_key_down(struct fallback_dispatch *dispatch, evdev_usage_t usage)
 {
+	assert(evdev_usage_type(usage) == EV_KEY);
+	unsigned int code = evdev_usage_code(usage);
 	return long_bit_is_set(dispatch->hw_key_mask, code);
 }
 
 static inline int
-get_key_down_count(struct evdev_device *device, int code)
+get_key_down_count(struct evdev_device *device, evdev_usage_t usage)
 {
+	assert(evdev_usage_type(usage) == EV_KEY);
+	unsigned int code = evdev_usage_code(usage);
 	return device->key_count[code];
 }
 
-void fallback_init_debounce(struct fallback_dispatch *dispatch);
-void fallback_debounce_handle_state(struct fallback_dispatch *dispatch,
-				    uint64_t time);
+void
+fallback_debounce_handle_state(struct fallback_dispatch *dispatch, uint64_t time);
 void
 fallback_notify_physical_button(struct fallback_dispatch *dispatch,
 				struct evdev_device *device,
 				uint64_t time,
-				int button,
+				evdev_usage_t button,
 				enum libinput_button_state state);
 
-void
-fallback_init_wheel(struct fallback_dispatch *dispatch,
-		    struct evdev_device *device);
-
-void
-fallback_wheel_process_relative(struct fallback_dispatch *dispatch,
-				struct evdev_device *device,
-				struct input_event *e, uint64_t time);
-
-void
-fallback_wheel_handle_state(struct fallback_dispatch *dispatch,
-			    struct evdev_device *device,
-			    uint64_t time);
-
 #endif
diff -pruN 1.28.1-1/src/evdev-frame.h 1.30.0-1/src/evdev-frame.h
--- 1.28.1-1/src/evdev-frame.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/evdev-frame.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,758 @@
+
+/*
+ * Copyright © 2025 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:
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include <linux/input.h>
+#include <stdbool.h>
+
+#include "util-bits.h"
+#include "util-input-event.h"
+#include "util-mem.h"
+#include "util-newtype.h"
+
+#define _evbit(t_, c_) ((t_) << 16 | (c_))
+
+/**
+ * This is an enum to have the compiler help us a bit.
+ * The enum doesn't need to contain all event codes, only
+ * the ones we use in libinput - add to here as required.
+ *
+ * The order doesn't matter either since each enum value
+ * is just the type | code value anyway, keep it in somewhat
+ * logical groups where possible.
+ */
+enum evdev_usage {
+	EVDEV_SYN_REPORT = _evbit(EV_SYN, SYN_REPORT),
+
+	EVDEV_KEY_RESERVED = _evbit(EV_KEY, KEY_RESERVED),
+	EVDEV_KEY_ESC = _evbit(EV_KEY, KEY_ESC),
+	EVDEV_KEY_A = _evbit(EV_KEY, KEY_A),
+	EVDEV_KEY_CAPSLOCK = _evbit(EV_KEY, KEY_CAPSLOCK),
+	EVDEV_KEY_KP7 = _evbit(EV_KEY, KEY_KP7),
+	EVDEV_KEY_KPDOT = _evbit(EV_KEY, KEY_KPDOT),
+	EVDEV_KEY_MICMUTE = _evbit(EV_KEY, KEY_MICMUTE),
+	EVDEV_KEY_OK = _evbit(EV_KEY, KEY_OK),
+	EVDEV_KEY_LIGHTS_TOGGLE = _evbit(EV_KEY, KEY_LIGHTS_TOGGLE),
+	EVDEV_KEY_ALS_TOGGLE = _evbit(EV_KEY, KEY_ALS_TOGGLE),
+	EVDEV_KEY_MAX = _evbit(EV_KEY, KEY_MAX),
+
+	EVDEV_BTN_LEFT = _evbit(EV_KEY, BTN_LEFT),
+	EVDEV_BTN_RIGHT = _evbit(EV_KEY, BTN_RIGHT),
+	EVDEV_BTN_MIDDLE = _evbit(EV_KEY, BTN_MIDDLE),
+	EVDEV_BTN_SIDE = _evbit(EV_KEY, BTN_SIDE),
+	EVDEV_BTN_EXTRA = _evbit(EV_KEY, BTN_EXTRA),
+	EVDEV_BTN_FORWARD = _evbit(EV_KEY, BTN_FORWARD),
+	EVDEV_BTN_BACK = _evbit(EV_KEY, BTN_BACK),
+	EVDEV_BTN_TASK = _evbit(EV_KEY, BTN_TASK),
+
+	EVDEV_BTN_JOYSTICK = _evbit(EV_KEY, BTN_JOYSTICK),
+
+	EVDEV_BTN_0 = _evbit(EV_KEY, BTN_0),
+	EVDEV_BTN_1 = _evbit(EV_KEY, BTN_1),
+	EVDEV_BTN_2 = _evbit(EV_KEY, BTN_2),
+
+	EVDEV_BTN_STYLUS = _evbit(EV_KEY, BTN_STYLUS),
+	EVDEV_BTN_STYLUS2 = _evbit(EV_KEY, BTN_STYLUS2),
+	EVDEV_BTN_STYLUS3 = _evbit(EV_KEY, BTN_STYLUS3),
+
+	EVDEV_BTN_TOUCH = _evbit(EV_KEY, BTN_TOUCH),
+	EVDEV_BTN_TOOL_PEN = _evbit(EV_KEY, BTN_TOOL_PEN),
+	EVDEV_BTN_TOOL_RUBBER = _evbit(EV_KEY, BTN_TOOL_RUBBER),
+	EVDEV_BTN_TOOL_BRUSH = _evbit(EV_KEY, BTN_TOOL_BRUSH),
+	EVDEV_BTN_TOOL_PENCIL = _evbit(EV_KEY, BTN_TOOL_PENCIL),
+	EVDEV_BTN_TOOL_AIRBRUSH = _evbit(EV_KEY, BTN_TOOL_AIRBRUSH),
+	EVDEV_BTN_TOOL_MOUSE = _evbit(EV_KEY, BTN_TOOL_MOUSE),
+	EVDEV_BTN_TOOL_LENS = _evbit(EV_KEY, BTN_TOOL_LENS),
+	EVDEV_BTN_TOOL_QUINTTAP = _evbit(EV_KEY, BTN_TOOL_QUINTTAP),
+	EVDEV_BTN_TOOL_DOUBLETAP = _evbit(EV_KEY, BTN_TOOL_DOUBLETAP),
+	EVDEV_BTN_TOOL_TRIPLETAP = _evbit(EV_KEY, BTN_TOOL_TRIPLETAP),
+	EVDEV_BTN_TOOL_QUADTAP = _evbit(EV_KEY, BTN_TOOL_QUADTAP),
+	EVDEV_BTN_TOOL_FINGER = _evbit(EV_KEY, BTN_TOOL_FINGER),
+	EVDEV_BTN_MISC = _evbit(EV_KEY, BTN_MISC),
+	EVDEV_BTN_DIGI = _evbit(EV_KEY, BTN_DIGI),
+	EVDEV_BTN_WHEEL = _evbit(EV_KEY, BTN_WHEEL),
+	EVDEV_BTN_GEAR_UP = _evbit(EV_KEY, BTN_GEAR_UP),
+	EVDEV_BTN_DPAD_UP = _evbit(EV_KEY, BTN_DPAD_UP),
+	EVDEV_BTN_DPAD_RIGHT = _evbit(EV_KEY, BTN_DPAD_RIGHT),
+	EVDEV_BTN_TRIGGER_HAPPY = _evbit(EV_KEY, BTN_TRIGGER_HAPPY),
+	EVDEV_BTN_TRIGGER_HAPPY40 = _evbit(EV_KEY, BTN_TRIGGER_HAPPY40),
+
+	EVDEV_REL_X = _evbit(EV_REL, REL_X),
+	EVDEV_REL_Y = _evbit(EV_REL, REL_Y),
+	EVDEV_REL_WHEEL = _evbit(EV_REL, REL_WHEEL),
+	EVDEV_REL_WHEEL_HI_RES = _evbit(EV_REL, REL_WHEEL_HI_RES),
+	EVDEV_REL_HWHEEL = _evbit(EV_REL, REL_HWHEEL),
+	EVDEV_REL_HWHEEL_HI_RES = _evbit(EV_REL, REL_HWHEEL_HI_RES),
+	EVDEV_REL_DIAL = _evbit(EV_REL, REL_DIAL),
+	EVDEV_REL_MAX = _evbit(EV_REL, REL_MAX),
+
+	EVDEV_ABS_X = _evbit(EV_ABS, ABS_X),
+	EVDEV_ABS_Y = _evbit(EV_ABS, ABS_Y),
+	EVDEV_ABS_Z = _evbit(EV_ABS, ABS_Z),
+	EVDEV_ABS_RX = _evbit(EV_ABS, ABS_RX),
+	EVDEV_ABS_RY = _evbit(EV_ABS, ABS_RY),
+	EVDEV_ABS_RZ = _evbit(EV_ABS, ABS_RZ),
+	EVDEV_ABS_PRESSURE = _evbit(EV_ABS, ABS_PRESSURE),
+	EVDEV_ABS_DISTANCE = _evbit(EV_ABS, ABS_DISTANCE),
+	EVDEV_ABS_THROTTLE = _evbit(EV_ABS, ABS_THROTTLE),
+	EVDEV_ABS_RUDDER = _evbit(EV_ABS, ABS_RUDDER),
+	EVDEV_ABS_WHEEL = _evbit(EV_ABS, ABS_WHEEL),
+	EVDEV_ABS_MISC = _evbit(EV_ABS, ABS_MISC),
+	EVDEV_ABS_TILT_X = _evbit(EV_ABS, ABS_TILT_X),
+	EVDEV_ABS_TILT_Y = _evbit(EV_ABS, ABS_TILT_Y),
+
+	EVDEV_ABS_MT_SLOT = _evbit(EV_ABS, ABS_MT_SLOT),
+	EVDEV_ABS_MT_POSITION_X = _evbit(EV_ABS, ABS_MT_POSITION_X),
+	EVDEV_ABS_MT_POSITION_Y = _evbit(EV_ABS, ABS_MT_POSITION_Y),
+	EVDEV_ABS_MT_TOOL_TYPE = _evbit(EV_ABS, ABS_MT_TOOL_TYPE),
+	EVDEV_ABS_MT_TRACKING_ID = _evbit(EV_ABS, ABS_MT_TRACKING_ID),
+	EVDEV_ABS_MT_TOUCH_MAJOR = _evbit(EV_ABS, ABS_MT_TOUCH_MAJOR),
+	EVDEV_ABS_MT_TOUCH_MINOR = _evbit(EV_ABS, ABS_MT_TOUCH_MINOR),
+	EVDEV_ABS_MT_ORIENTATION = _evbit(EV_ABS, ABS_MT_ORIENTATION),
+	EVDEV_ABS_MT_PRESSURE = _evbit(EV_ABS, ABS_MT_PRESSURE),
+	EVDEV_ABS_MT_DISTANCE = _evbit(EV_ABS, ABS_MT_DISTANCE),
+	EVDEV_ABS_MAX = _evbit(EV_ABS, ABS_MAX),
+
+	EVDEV_SW_LID = _evbit(EV_SW, SW_LID),
+	EVDEV_SW_TABLET_MODE = _evbit(EV_SW, SW_TABLET_MODE),
+	EVDEV_SW_MAX = _evbit(EV_SW, SW_MAX),
+
+	EVDEV_MSC_SCAN = _evbit(EV_MSC, MSC_SCAN),
+	EVDEV_MSC_SERIAL = _evbit(EV_MSC, MSC_SERIAL),
+	EVDEV_MSC_TIMESTAMP = _evbit(EV_MSC, MSC_TIMESTAMP),
+};
+
+/**
+ * Declares evdev_usage_t as uint32_t wrapper that we
+ * use for passing event codes around.
+ *
+ * This way we can't accidentally mix up a code vs
+ * type or a random integer with what needs to be a usage.
+ */
+DECLARE_NEWTYPE(evdev_usage, uint32_t);
+
+static inline const char *
+evdev_usage_name(evdev_usage_t usage)
+{
+	switch (evdev_usage_as_uint32_t(usage)) {
+	CASE_RETURN_STRING(EVDEV_SYN_REPORT);
+
+	CASE_RETURN_STRING(EVDEV_KEY_RESERVED);
+	CASE_RETURN_STRING(EVDEV_KEY_ESC);
+	CASE_RETURN_STRING(EVDEV_KEY_MICMUTE);
+	CASE_RETURN_STRING(EVDEV_KEY_OK);
+	CASE_RETURN_STRING(EVDEV_KEY_LIGHTS_TOGGLE);
+	CASE_RETURN_STRING(EVDEV_KEY_ALS_TOGGLE);
+	CASE_RETURN_STRING(EVDEV_KEY_MAX);
+
+	CASE_RETURN_STRING(EVDEV_BTN_LEFT);
+	CASE_RETURN_STRING(EVDEV_BTN_RIGHT);
+	CASE_RETURN_STRING(EVDEV_BTN_MIDDLE);
+	CASE_RETURN_STRING(EVDEV_BTN_SIDE);
+	CASE_RETURN_STRING(EVDEV_BTN_EXTRA);
+	CASE_RETURN_STRING(EVDEV_BTN_FORWARD);
+	CASE_RETURN_STRING(EVDEV_BTN_BACK);
+	CASE_RETURN_STRING(EVDEV_BTN_TASK);
+
+	CASE_RETURN_STRING(EVDEV_BTN_JOYSTICK);
+
+	CASE_RETURN_STRING(EVDEV_BTN_0);
+	CASE_RETURN_STRING(EVDEV_BTN_1);
+	CASE_RETURN_STRING(EVDEV_BTN_2);
+
+	CASE_RETURN_STRING(EVDEV_BTN_STYLUS);
+	CASE_RETURN_STRING(EVDEV_BTN_STYLUS2);
+	CASE_RETURN_STRING(EVDEV_BTN_STYLUS3);
+
+	CASE_RETURN_STRING(EVDEV_BTN_TOUCH);
+	CASE_RETURN_STRING(EVDEV_BTN_TOOL_PEN);
+	CASE_RETURN_STRING(EVDEV_BTN_TOOL_RUBBER);
+	CASE_RETURN_STRING(EVDEV_BTN_TOOL_BRUSH);
+	CASE_RETURN_STRING(EVDEV_BTN_TOOL_PENCIL);
+	CASE_RETURN_STRING(EVDEV_BTN_TOOL_AIRBRUSH);
+	CASE_RETURN_STRING(EVDEV_BTN_TOOL_MOUSE);
+	CASE_RETURN_STRING(EVDEV_BTN_TOOL_LENS);
+	CASE_RETURN_STRING(EVDEV_BTN_TOOL_QUINTTAP);
+	CASE_RETURN_STRING(EVDEV_BTN_TOOL_DOUBLETAP);
+	CASE_RETURN_STRING(EVDEV_BTN_TOOL_TRIPLETAP);
+	CASE_RETURN_STRING(EVDEV_BTN_TOOL_QUADTAP);
+	CASE_RETURN_STRING(EVDEV_BTN_TOOL_FINGER);
+	/* CASE_RETURN_STRING(EVDEV_BTN_MISC); - alias of BTN_0 */
+	/* CASE_RETURN_STRING(EVDEV_BTN_DIGI); - alias of BTN_TOOL_PEN */
+	CASE_RETURN_STRING(EVDEV_BTN_WHEEL);
+	CASE_RETURN_STRING(EVDEV_BTN_GEAR_UP);
+	CASE_RETURN_STRING(EVDEV_BTN_DPAD_UP);
+	CASE_RETURN_STRING(EVDEV_BTN_DPAD_RIGHT);
+	CASE_RETURN_STRING(EVDEV_BTN_TRIGGER_HAPPY);
+	CASE_RETURN_STRING(EVDEV_BTN_TRIGGER_HAPPY40);
+
+	CASE_RETURN_STRING(EVDEV_REL_X);
+	CASE_RETURN_STRING(EVDEV_REL_Y);
+	CASE_RETURN_STRING(EVDEV_REL_WHEEL);
+	CASE_RETURN_STRING(EVDEV_REL_WHEEL_HI_RES);
+	CASE_RETURN_STRING(EVDEV_REL_HWHEEL);
+	CASE_RETURN_STRING(EVDEV_REL_HWHEEL_HI_RES);
+	CASE_RETURN_STRING(EVDEV_REL_DIAL);
+	CASE_RETURN_STRING(EVDEV_REL_MAX);
+
+	CASE_RETURN_STRING(EVDEV_ABS_X);
+	CASE_RETURN_STRING(EVDEV_ABS_Y);
+	CASE_RETURN_STRING(EVDEV_ABS_Z);
+	CASE_RETURN_STRING(EVDEV_ABS_RX);
+	CASE_RETURN_STRING(EVDEV_ABS_RY);
+	CASE_RETURN_STRING(EVDEV_ABS_RZ);
+	CASE_RETURN_STRING(EVDEV_ABS_PRESSURE);
+	CASE_RETURN_STRING(EVDEV_ABS_DISTANCE);
+	CASE_RETURN_STRING(EVDEV_ABS_THROTTLE);
+	CASE_RETURN_STRING(EVDEV_ABS_RUDDER);
+	CASE_RETURN_STRING(EVDEV_ABS_WHEEL);
+	CASE_RETURN_STRING(EVDEV_ABS_MISC);
+	CASE_RETURN_STRING(EVDEV_ABS_TILT_X);
+	CASE_RETURN_STRING(EVDEV_ABS_TILT_Y);
+
+	CASE_RETURN_STRING(EVDEV_ABS_MT_SLOT);
+	CASE_RETURN_STRING(EVDEV_ABS_MT_POSITION_X);
+	CASE_RETURN_STRING(EVDEV_ABS_MT_POSITION_Y);
+	CASE_RETURN_STRING(EVDEV_ABS_MT_TOOL_TYPE);
+	CASE_RETURN_STRING(EVDEV_ABS_MT_TRACKING_ID);
+	CASE_RETURN_STRING(EVDEV_ABS_MT_TOUCH_MAJOR);
+	CASE_RETURN_STRING(EVDEV_ABS_MT_TOUCH_MINOR);
+	CASE_RETURN_STRING(EVDEV_ABS_MT_ORIENTATION);
+	CASE_RETURN_STRING(EVDEV_ABS_MT_PRESSURE);
+	CASE_RETURN_STRING(EVDEV_ABS_MT_DISTANCE);
+	CASE_RETURN_STRING(EVDEV_ABS_MAX);
+
+	CASE_RETURN_STRING(EVDEV_SW_LID);
+	CASE_RETURN_STRING(EVDEV_SW_TABLET_MODE);
+	CASE_RETURN_STRING(EVDEV_SW_MAX);
+
+	CASE_RETURN_STRING(EVDEV_MSC_SCAN);
+	CASE_RETURN_STRING(EVDEV_MSC_SERIAL);
+	CASE_RETURN_STRING(EVDEV_MSC_TIMESTAMP);
+	}
+
+	return NULL;
+}
+
+static inline evdev_usage_t
+evdev_usage_from(enum evdev_usage usage)
+{
+	return evdev_usage_from_uint32_t((uint32_t)usage);
+}
+
+static inline enum evdev_usage
+evdev_usage_enum(evdev_usage_t usage)
+{
+	return (enum evdev_usage)evdev_usage_as_uint32_t(usage);
+}
+
+static inline evdev_usage_t
+evdev_usage_from_code(unsigned int type, unsigned int code)
+{
+	return evdev_usage_from_uint32_t(_evbit(type, code));
+}
+
+static inline uint16_t
+evdev_usage_type(evdev_usage_t usage)
+{
+	return evdev_usage_as_uint32_t(usage) >> 16;
+}
+
+static inline uint16_t
+evdev_usage_code(evdev_usage_t usage)
+{
+	return evdev_usage_as_uint32_t(usage) & 0xFFFF;
+}
+
+static inline const char *
+evdev_usage_code_name(evdev_usage_t usage)
+{
+	return libevdev_event_code_get_name(evdev_usage_type(usage),
+					    evdev_usage_code(usage));
+}
+
+static inline const char *
+evdev_usage_type_name(evdev_usage_t usage)
+{
+	return libevdev_event_type_get_name(evdev_usage_type(usage));
+}
+
+static inline evdev_usage_t
+evdev_usage_next(evdev_usage_t usage)
+{
+	return evdev_usage_from_code(evdev_usage_type(usage),
+				     evdev_usage_code(usage) + 1);
+}
+
+/**
+ * Returns true if the usage is a real button, i.e. BTN_FOO
+ * excluding the various BTN_TOOL and BTN_TOUCH usages.
+ */
+static inline bool
+evdev_usage_is_button(evdev_usage_t usage)
+{
+	switch (evdev_usage_as_uint32_t(usage)) {
+	case EVDEV_BTN_TOOL_PEN:
+	case EVDEV_BTN_TOOL_RUBBER:
+	case EVDEV_BTN_TOOL_BRUSH:
+	case EVDEV_BTN_TOOL_PENCIL:
+	case EVDEV_BTN_TOOL_AIRBRUSH:
+	case EVDEV_BTN_TOOL_MOUSE:
+	case EVDEV_BTN_TOOL_LENS:
+	case EVDEV_BTN_TOOL_QUINTTAP:
+	case EVDEV_BTN_TOOL_DOUBLETAP:
+	case EVDEV_BTN_TOOL_TRIPLETAP:
+	case EVDEV_BTN_TOOL_QUADTAP:
+	case EVDEV_BTN_TOOL_FINGER:
+	case EVDEV_BTN_TOUCH:
+		return false;
+	case BTN_STYLUS:
+	case BTN_STYLUS2:
+	case BTN_STYLUS3:
+		return true;
+	case EVDEV_BTN_MISC ... EVDEV_BTN_DIGI - 1:
+	case EVDEV_BTN_WHEEL ... EVDEV_BTN_GEAR_UP:
+	case EVDEV_BTN_DPAD_UP ... EVDEV_BTN_DPAD_RIGHT:
+	case EVDEV_BTN_TRIGGER_HAPPY ... EVDEV_BTN_TRIGGER_HAPPY40:
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * Returns true if the usage is a real key, i.e. KEY_FOO
+ * excluding the various BTN_ ranges
+ */
+static inline bool
+evdev_usage_is_key(evdev_usage_t usage)
+{
+	switch (evdev_usage_as_uint32_t(usage)) {
+	case EVDEV_KEY_ESC ... EVDEV_KEY_MICMUTE:
+	case EVDEV_KEY_OK ... EVDEV_KEY_LIGHTS_TOGGLE:
+	case EVDEV_KEY_ALS_TOGGLE ... EVDEV_BTN_TRIGGER_HAPPY - 1:
+		return true;
+	}
+
+	return false;
+}
+
+struct evdev_event {
+	/* this may be a value outside the known usages above but it's just an int */
+	evdev_usage_t usage;
+	int32_t value;
+};
+
+static inline uint16_t
+evdev_event_type(const struct evdev_event *e)
+{
+	return evdev_usage_type(e->usage);
+}
+
+static inline uint16_t
+evdev_event_code(const struct evdev_event *e)
+{
+	return evdev_usage_code(e->usage);
+}
+
+static inline const char *
+evdev_event_get_type_name(const struct evdev_event *e)
+{
+	return evdev_usage_type_name(e->usage);
+}
+
+static inline const char *
+evdev_event_get_code_name(const struct evdev_event *e)
+{
+	return evdev_usage_code_name(e->usage);
+}
+
+static inline struct input_event
+evdev_event_to_input_event(const struct evdev_event *e, uint64_t time)
+{
+	struct timeval tv = us2tv(time);
+	return (struct input_event){
+		.type = evdev_event_type(e),
+		.code = evdev_event_code(e),
+		.value = e->value,
+		.input_event_sec = tv.tv_sec,
+		.input_event_usec = tv.tv_usec,
+	};
+}
+
+static inline struct evdev_event
+evdev_event_from_input_event(const struct input_event *e, uint64_t *time)
+{
+	if (time)
+		*time = input_event_time(e);
+	return (struct evdev_event){
+		.usage = evdev_usage_from_code(e->type, e->code),
+		.value = e->value,
+	};
+}
+
+/**
+ * A wrapper around a SYN_REPORT-terminated set of input events.
+ *
+ * This struct always has a count of >= 1 (the SYN_REPORT)
+ * and the timestamp of the SYN_REPORT is always that of the
+ * most recently appended event (if nonzero)
+ *
+ * The event frame is of a fixed size given in
+ * evdev_frame_new() and cannot be resized via helpers.
+ *
+ * The struct should be considered opaque, use the helpers
+ * to access the various fields.
+ */
+struct evdev_frame {
+	int refcount;
+	size_t max_size;
+	size_t count;
+	uint64_t time;
+	struct evdev_event events[];
+};
+
+static inline struct evdev_frame *
+evdev_frame_ref(struct evdev_frame *frame)
+{
+	assert(frame->refcount > 0);
+	++frame->refcount;
+	return frame;
+}
+
+static inline struct evdev_frame *
+evdev_frame_unref(struct evdev_frame *frame)
+{
+	if (frame) {
+		assert(frame->refcount > 0);
+		if (--frame->refcount == 0) {
+			frame->max_size = 0;
+			frame->count = 0;
+			free(frame);
+		}
+	}
+	return NULL;
+}
+
+DEFINE_UNREF_CLEANUP_FUNC(evdev_frame);
+
+static inline bool
+evdev_frame_is_empty(const struct evdev_frame *frame)
+{
+	return frame->count == 1;
+}
+
+static inline size_t
+evdev_frame_get_count(const struct evdev_frame *frame)
+{
+	return frame->count;
+}
+
+static inline struct evdev_event *
+evdev_frame_get_events(struct evdev_frame *frame, size_t *nevents)
+{
+	if (nevents)
+		*nevents = frame->count;
+
+	return frame->events;
+}
+
+/**
+ * Set the timestamp for all events in this event frame.
+ */
+static inline void
+evdev_frame_set_time(struct evdev_frame *frame, uint64_t time)
+{
+	frame->time = time;
+}
+
+static inline uint64_t
+evdev_frame_get_time(const struct evdev_frame *frame)
+{
+	return frame->time;
+}
+
+static inline int
+evdev_frame_reset(struct evdev_frame *frame)
+{
+	memset(frame->events, 0, frame->max_size * sizeof(*frame->events));
+	frame->count = 1; /* SYN_REPORT is always there */
+
+	return 0;
+}
+
+static inline struct evdev_frame *
+evdev_frame_new(size_t max_size)
+{
+	struct evdev_frame *frame =
+		zalloc(max_size * sizeof(*frame->events) + sizeof(*frame));
+
+	frame->refcount = 1;
+	frame->max_size = max_size;
+	frame->count = 1; /* SYN_REPORT is always there */
+
+	return frame;
+}
+
+/**
+ * Append events to the event frame. nevents must be larger than 0
+ * and specifies the number of elements in events. If any events in
+ * the given events is a EV_SYN/SYN_REPORT event, that event is the last
+ * one appended even if nevents states a higher number of events (roughly
+ * equivalent to having a \0 inside a string).
+ *
+ * This function guarantees the frame is terminated with a SYN_REPORT event.
+ * Appending SYN_REPORTS to a frame does not increase the count of events in the
+ * frame - the new SYN_REPORT will simply replace the existing SYN_REPORT.
+ *
+ * The timestamp of the SYN_REPORT (if any) is used for this event
+ * frame. If the appended sequence does not contain a SYN_REPORT, the highest
+ * timestamp of any event appended is used. This timestamp will overwrite the
+ * frame's timestamp even if the timestamp of the frame is higher.
+ *
+ * If all to-be-appended events (including the SYN_REPORT) have a timestamp of
+ * 0, the existing frame's timestamp is left as-is.
+ *
+ * The caller SHOULD terminate the events with a SYN_REPORT event with a
+ * valid timestamp to ensure correct behavior.
+ *
+ * Returns 0 on success, or a negative errno on failure
+ */
+static inline int
+evdev_frame_append(struct evdev_frame *frame,
+		   const struct evdev_event *events,
+		   size_t nevents)
+{
+	assert(nevents > 0);
+
+	for (size_t i = 0; i < nevents; i++) {
+		if (evdev_usage_eq(events[i].usage, EVDEV_SYN_REPORT)) {
+			nevents = i;
+			break;
+		}
+	}
+
+	if (nevents > 0) {
+		if (frame->count + nevents > frame->max_size)
+			return -ENOMEM;
+
+		memcpy(frame->events + frame->count - 1,
+		       events,
+		       nevents * sizeof(*events));
+		frame->count += nevents;
+	}
+
+	return 0;
+}
+
+static inline int
+evdev_frame_append_one(struct evdev_frame *frame, evdev_usage_t usage, int32_t value)
+{
+	if (evdev_usage_eq(usage, EVDEV_SYN_REPORT))
+		return 0;
+
+	if (frame->count >= frame->max_size)
+		return -ENOMEM;
+
+	struct evdev_event *e = &frame->events[frame->count - 1];
+	*e = (struct evdev_event){ .usage = usage, .value = value };
+	frame->count++;
+	return 0;
+}
+
+static inline int
+evdev_frame_append_input_event(struct evdev_frame *frame,
+			       const struct input_event *event)
+{
+	struct evdev_event e = evdev_event_from_input_event(event, NULL);
+	if (evdev_usage_as_uint32_t(e.usage) == EVDEV_SYN_REPORT) {
+		uint64_t time = input_event_time(event);
+		evdev_frame_set_time(frame, time);
+	}
+	return evdev_frame_append(frame, &e, 1);
+}
+
+/**
+ * Behaves like evdev_frame_append() but resets the frame before appending.
+ *
+ * On error the frame is left as-is.
+ *
+ * Returns 0 on success, or a negative errno on failure
+ */
+static inline int
+evdev_frame_set(struct evdev_frame *frame,
+		const struct evdev_event *events,
+		size_t nevents)
+{
+	assert(nevents > 0);
+
+	size_t count = nevents;
+
+	for (size_t i = 0; i < nevents; i++) {
+		if (evdev_usage_as_uint32_t(events[i].usage) == EVDEV_SYN_REPORT) {
+			count = i;
+			break;
+		}
+	}
+
+	if (count > frame->max_size - 1)
+		return -ENOMEM;
+
+	evdev_frame_reset(frame);
+	return evdev_frame_append(frame, events, nevents);
+}
+
+static inline struct evdev_frame *
+evdev_frame_clone(struct evdev_frame *frame)
+{
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+	struct evdev_frame *clone = evdev_frame_new(nevents);
+
+	evdev_frame_append(clone, events, nevents);
+	evdev_frame_set_time(clone, evdev_frame_get_time(frame));
+
+	return clone;
+}
+
+struct evdev_mask {
+	bitmask_t ev;
+	bitmask_t rel;
+	bitmask_t sw;
+	infmask_t key; /* < BTN_MISC */
+	infmask_t btn; /* >= BTN_MISC */
+	infmask_t abs;
+};
+
+static_assert(sizeof(bitmask_t) * 8 >= EV_MAX, "bitmask size too small");
+static_assert(sizeof(bitmask_t) * 8 >= EV_REL, "bitmask size too small");
+static_assert(sizeof(bitmask_t) * 8 >= EV_SW, "bitmask size too small");
+
+static inline void
+evdev_mask_reset(struct evdev_mask *mask)
+{
+	mask->ev = bitmask_new();
+	mask->rel = bitmask_new();
+	mask->sw = bitmask_new();
+	infmask_reset(&mask->key);
+	infmask_reset(&mask->btn);
+	infmask_reset(&mask->abs);
+}
+
+static inline struct evdev_mask *
+evdev_mask_new(void)
+{
+	struct evdev_mask *mask = zalloc(sizeof(*mask));
+	evdev_mask_reset(mask);
+	return mask;
+}
+
+static inline void
+evdev_mask_destroy(struct evdev_mask *mask)
+{
+	if (mask) {
+		evdev_mask_reset(mask);
+		free(mask);
+	}
+}
+
+DEFINE_DESTROY_CLEANUP_FUNC(evdev_mask);
+
+static inline void
+evdev_mask_set_usage(struct evdev_mask *mask, evdev_usage_t usage)
+{
+	unsigned int type = evdev_usage_type(usage);
+	unsigned int code = evdev_usage_code(usage);
+
+	if (type >= EV_MAX)
+		return;
+
+	bitmask_set_bit(&mask->ev, type);
+
+	switch (type) {
+	case EV_ABS:
+		if (code <= ABS_MAX)
+			infmask_set_bit(&mask->abs, code);
+		break;
+	case EV_KEY:
+		if (code < BTN_MISC)
+			infmask_set_bit(&mask->key, code);
+		else if (code <= KEY_MAX)
+			infmask_set_bit(&mask->btn, code - BTN_MISC);
+		break;
+	case EV_REL:
+		if (code <= REL_MAX)
+			bitmask_set_bit(&mask->rel, code);
+		break;
+	case EV_SW:
+		if (code <= SW_MAX)
+			bitmask_set_bit(&mask->sw, code);
+		break;
+	}
+}
+
+static inline void
+evdev_mask_set_enum(struct evdev_mask *mask, enum evdev_usage usage)
+{
+	evdev_mask_set_usage(mask, evdev_usage_from(usage));
+}
+
+static inline bool
+evdev_mask_is_set(const struct evdev_mask *mask, evdev_usage_t usage)
+{
+	unsigned int type = evdev_usage_type(usage);
+	unsigned int code = evdev_usage_code(usage);
+
+	if (type >= EV_MAX)
+		return false;
+
+	if (!bitmask_bit_is_set(mask->ev, type))
+		return false;
+
+	bool isset = false;
+	switch (type) {
+	case EV_ABS:
+		isset = infmask_bit_is_set(&mask->abs, code);
+		break;
+	case EV_KEY:
+		if (code < BTN_MISC)
+			isset = infmask_bit_is_set(&mask->key, code);
+		else
+			isset = infmask_bit_is_set(&mask->btn, code - BTN_MISC);
+		break;
+	case EV_REL:
+		isset = bitmask_bit_is_set(mask->rel, code);
+		break;
+	case EV_SW:
+		isset = bitmask_bit_is_set(mask->sw, code);
+		break;
+	default:
+		break;
+	}
+
+	return isset;
+}
diff -pruN 1.28.1-1/src/evdev-middle-button.c 1.30.0-1/src/evdev-middle-button.c
--- 1.28.1-1/src/evdev-middle-button.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-middle-button.c	2025-11-25 03:40:43.000000000 +0000
@@ -42,7 +42,7 @@
  * as-is.
  */
 
-static inline const char*
+static inline const char *
 middlebutton_state_to_str(enum evdev_middlebutton_state state)
 {
 	switch (state) {
@@ -61,7 +61,7 @@ middlebutton_state_to_str(enum evdev_mid
 	return NULL;
 }
 
-static inline const char*
+static inline const char *
 middlebutton_event_to_str(enum evdev_middlebutton_event event)
 {
 	switch (event) {
@@ -90,8 +90,7 @@ middlebutton_state_error(struct evdev_de
 static void
 middlebutton_timer_set(struct evdev_device *device, uint64_t now)
 {
-	libinput_timer_set(&device->middlebutton.timer,
-			   now + MIDDLEBUTTON_TIMEOUT);
+	libinput_timer_set(&device->middlebutton.timer, now + MIDDLEBUTTON_TIMEOUT);
 }
 
 static void
@@ -129,13 +128,10 @@ middlebutton_set_state(struct evdev_devi
 static void
 middlebutton_post_event(struct evdev_device *device,
 			uint64_t now,
-			int button,
+			evdev_usage_t button,
 			enum libinput_button_state state)
 {
-	evdev_pointer_notify_button(device,
-				    now,
-				    button,
-				    state);
+	evdev_pointer_notify_button(device, now, button, state);
 }
 
 static int
@@ -174,18 +170,18 @@ evdev_middlebutton_ldown_handle_event(st
 		middlebutton_state_error(device, event);
 		break;
 	case MIDDLEBUTTON_EVENT_R_DOWN:
-		middlebutton_post_event(device, time,
-					BTN_MIDDLE,
+		middlebutton_post_event(device,
+					time,
+					evdev_usage_from(EVDEV_BTN_MIDDLE),
 					LIBINPUT_BUTTON_STATE_PRESSED);
 		middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
 		break;
 	case MIDDLEBUTTON_EVENT_OTHER:
-		middlebutton_post_event(device, time,
-					BTN_LEFT,
+		middlebutton_post_event(device,
+					time,
+					evdev_usage_from(EVDEV_BTN_LEFT),
 					LIBINPUT_BUTTON_STATE_PRESSED);
-		middlebutton_set_state(device,
-				       MIDDLEBUTTON_PASSTHROUGH,
-				       time);
+		middlebutton_set_state(device, MIDDLEBUTTON_PASSTHROUGH, time);
 		return 0;
 	case MIDDLEBUTTON_EVENT_R_UP:
 		middlebutton_state_error(device, event);
@@ -193,21 +189,20 @@ evdev_middlebutton_ldown_handle_event(st
 	case MIDDLEBUTTON_EVENT_L_UP:
 		middlebutton_post_event(device,
 					device->middlebutton.first_event_time,
-					BTN_LEFT,
+					evdev_usage_from(EVDEV_BTN_LEFT),
 					LIBINPUT_BUTTON_STATE_PRESSED);
-		middlebutton_post_event(device, time,
-					BTN_LEFT,
+		middlebutton_post_event(device,
+					time,
+					evdev_usage_from(EVDEV_BTN_LEFT),
 					LIBINPUT_BUTTON_STATE_RELEASED);
 		middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
 		break;
 	case MIDDLEBUTTON_EVENT_TIMEOUT:
 		middlebutton_post_event(device,
 					device->middlebutton.first_event_time,
-					BTN_LEFT,
+					evdev_usage_from(EVDEV_BTN_LEFT),
 					LIBINPUT_BUTTON_STATE_PRESSED);
-		middlebutton_set_state(device,
-				       MIDDLEBUTTON_PASSTHROUGH,
-				       time);
+		middlebutton_set_state(device, MIDDLEBUTTON_PASSTHROUGH, time);
 		break;
 	case MIDDLEBUTTON_EVENT_ALL_UP:
 		middlebutton_state_error(device, event);
@@ -224,8 +219,9 @@ evdev_middlebutton_rdown_handle_event(st
 {
 	switch (event) {
 	case MIDDLEBUTTON_EVENT_L_DOWN:
-		middlebutton_post_event(device, time,
-					BTN_MIDDLE,
+		middlebutton_post_event(device,
+					time,
+					evdev_usage_from(EVDEV_BTN_MIDDLE),
 					LIBINPUT_BUTTON_STATE_PRESSED);
 		middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
 		break;
@@ -235,19 +231,18 @@ evdev_middlebutton_rdown_handle_event(st
 	case MIDDLEBUTTON_EVENT_OTHER:
 		middlebutton_post_event(device,
 					device->middlebutton.first_event_time,
-					BTN_RIGHT,
+					evdev_usage_from(EVDEV_BTN_RIGHT),
 					LIBINPUT_BUTTON_STATE_PRESSED);
-		middlebutton_set_state(device,
-				       MIDDLEBUTTON_PASSTHROUGH,
-				       time);
+		middlebutton_set_state(device, MIDDLEBUTTON_PASSTHROUGH, time);
 		return 0;
 	case MIDDLEBUTTON_EVENT_R_UP:
 		middlebutton_post_event(device,
 					device->middlebutton.first_event_time,
-					BTN_RIGHT,
+					evdev_usage_from(EVDEV_BTN_RIGHT),
 					LIBINPUT_BUTTON_STATE_PRESSED);
-		middlebutton_post_event(device, time,
-					BTN_RIGHT,
+		middlebutton_post_event(device,
+					time,
+					evdev_usage_from(EVDEV_BTN_RIGHT),
 					LIBINPUT_BUTTON_STATE_RELEASED);
 		middlebutton_set_state(device, MIDDLEBUTTON_IDLE, time);
 		break;
@@ -257,11 +252,9 @@ evdev_middlebutton_rdown_handle_event(st
 	case MIDDLEBUTTON_EVENT_TIMEOUT:
 		middlebutton_post_event(device,
 					device->middlebutton.first_event_time,
-					BTN_RIGHT,
+					evdev_usage_from(EVDEV_BTN_RIGHT),
 					LIBINPUT_BUTTON_STATE_PRESSED);
-		middlebutton_set_state(device,
-				       MIDDLEBUTTON_PASSTHROUGH,
-				       time);
+		middlebutton_set_state(device, MIDDLEBUTTON_PASSTHROUGH, time);
 		break;
 	case MIDDLEBUTTON_EVENT_ALL_UP:
 		middlebutton_state_error(device, event);
@@ -282,26 +275,25 @@ evdev_middlebutton_middle_handle_event(s
 		middlebutton_state_error(device, event);
 		break;
 	case MIDDLEBUTTON_EVENT_OTHER:
-		middlebutton_post_event(device, time,
-					BTN_MIDDLE,
+		middlebutton_post_event(device,
+					time,
+					evdev_usage_from(EVDEV_BTN_MIDDLE),
 					LIBINPUT_BUTTON_STATE_RELEASED);
 		middlebutton_set_state(device, MIDDLEBUTTON_IGNORE_LR, time);
 		return 0;
 	case MIDDLEBUTTON_EVENT_R_UP:
-		middlebutton_post_event(device, time,
-					BTN_MIDDLE,
+		middlebutton_post_event(device,
+					time,
+					evdev_usage_from(EVDEV_BTN_MIDDLE),
 					LIBINPUT_BUTTON_STATE_RELEASED);
-		middlebutton_set_state(device,
-				       MIDDLEBUTTON_LEFT_UP_PENDING,
-				       time);
+		middlebutton_set_state(device, MIDDLEBUTTON_LEFT_UP_PENDING, time);
 		break;
 	case MIDDLEBUTTON_EVENT_L_UP:
-		middlebutton_post_event(device, time,
-					BTN_MIDDLE,
+		middlebutton_post_event(device,
+					time,
+					evdev_usage_from(EVDEV_BTN_MIDDLE),
 					LIBINPUT_BUTTON_STATE_RELEASED);
-		middlebutton_set_state(device,
-				       MIDDLEBUTTON_RIGHT_UP_PENDING,
-				       time);
+		middlebutton_set_state(device, MIDDLEBUTTON_RIGHT_UP_PENDING, time);
 		break;
 	case MIDDLEBUTTON_EVENT_TIMEOUT:
 		middlebutton_state_error(device, event);
@@ -324,8 +316,9 @@ evdev_middlebutton_lup_pending_handle_ev
 		middlebutton_state_error(device, event);
 		break;
 	case MIDDLEBUTTON_EVENT_R_DOWN:
-		middlebutton_post_event(device, time,
-					BTN_MIDDLE,
+		middlebutton_post_event(device,
+					time,
+					evdev_usage_from(EVDEV_BTN_MIDDLE),
 					LIBINPUT_BUTTON_STATE_PRESSED);
 		middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
 		break;
@@ -356,8 +349,9 @@ evdev_middlebutton_rup_pending_handle_ev
 {
 	switch (event) {
 	case MIDDLEBUTTON_EVENT_L_DOWN:
-		middlebutton_post_event(device, time,
-					BTN_MIDDLE,
+		middlebutton_post_event(device,
+					time,
+					evdev_usage_from(EVDEV_BTN_MIDDLE),
 					LIBINPUT_BUTTON_STATE_PRESSED);
 		middlebutton_set_state(device, MIDDLEBUTTON_MIDDLE, time);
 		break;
@@ -451,9 +445,7 @@ evdev_middlebutton_ignore_l_handle_event
 	case MIDDLEBUTTON_EVENT_R_UP:
 		return 0;
 	case MIDDLEBUTTON_EVENT_L_UP:
-		middlebutton_set_state(device,
-				       MIDDLEBUTTON_PASSTHROUGH,
-				       time);
+		middlebutton_set_state(device, MIDDLEBUTTON_PASSTHROUGH, time);
 		break;
 	case MIDDLEBUTTON_EVENT_TIMEOUT:
 	case MIDDLEBUTTON_EVENT_ALL_UP:
@@ -477,9 +469,7 @@ evdev_middlebutton_ignore_r_handle_event
 	case MIDDLEBUTTON_EVENT_OTHER:
 		return 0;
 	case MIDDLEBUTTON_EVENT_R_UP:
-		middlebutton_set_state(device,
-				       MIDDLEBUTTON_PASSTHROUGH,
-				       time);
+		middlebutton_set_state(device, MIDDLEBUTTON_PASSTHROUGH, time);
 		break;
 	case MIDDLEBUTTON_EVENT_L_UP:
 		return 0;
@@ -515,34 +505,22 @@ evdev_middlebutton_handle_event(struct e
 		rc = evdev_middlebutton_middle_handle_event(device, time, event);
 		break;
 	case MIDDLEBUTTON_LEFT_UP_PENDING:
-		rc = evdev_middlebutton_lup_pending_handle_event(device,
-								 time,
-								 event);
+		rc = evdev_middlebutton_lup_pending_handle_event(device, time, event);
 		break;
 	case MIDDLEBUTTON_RIGHT_UP_PENDING:
-		rc = evdev_middlebutton_rup_pending_handle_event(device,
-								 time,
-								 event);
+		rc = evdev_middlebutton_rup_pending_handle_event(device, time, event);
 		break;
 	case MIDDLEBUTTON_PASSTHROUGH:
-		rc = evdev_middlebutton_passthrough_handle_event(device,
-								 time,
-								 event);
+		rc = evdev_middlebutton_passthrough_handle_event(device, time, event);
 		break;
 	case MIDDLEBUTTON_IGNORE_LR:
-		rc = evdev_middlebutton_ignore_lr_handle_event(device,
-							       time,
-							       event);
+		rc = evdev_middlebutton_ignore_lr_handle_event(device, time, event);
 		break;
 	case MIDDLEBUTTON_IGNORE_L:
-		rc = evdev_middlebutton_ignore_l_handle_event(device,
-							      time,
-							      event);
+		rc = evdev_middlebutton_ignore_l_handle_event(device, time, event);
 		break;
 	case MIDDLEBUTTON_IGNORE_R:
-		rc = evdev_middlebutton_ignore_r_handle_event(device,
-							      time,
-							      event);
+		rc = evdev_middlebutton_ignore_r_handle_event(device, time, event);
 		break;
 	default:
 		evdev_log_bug_libinput(device,
@@ -564,8 +542,7 @@ evdev_middlebutton_handle_event(struct e
 static inline void
 evdev_middlebutton_apply_config(struct evdev_device *device)
 {
-	if (device->middlebutton.want_enabled ==
-	    device->middlebutton.enabled)
+	if (device->middlebutton.want_enabled == device->middlebutton.enabled)
 		return;
 
 	if (device->middlebutton.button_mask != 0)
@@ -577,26 +554,26 @@ evdev_middlebutton_apply_config(struct e
 bool
 evdev_middlebutton_filter_button(struct evdev_device *device,
 				 uint64_t time,
-				 int button,
+				 evdev_usage_t button,
 				 enum libinput_button_state state)
 {
 	enum evdev_middlebutton_event event;
 	bool is_press = state == LIBINPUT_BUTTON_STATE_PRESSED;
 	int rc;
-	unsigned int btnbit = (button - BTN_LEFT);
+	unsigned int btnbit = (evdev_usage_enum(button) - EVDEV_BTN_LEFT);
 	uint32_t old_mask = 0;
 
 	if (!device->middlebutton.enabled)
 		return false;
 
-	switch (button) {
-	case BTN_LEFT:
+	switch (evdev_usage_enum(button)) {
+	case EVDEV_BTN_LEFT:
 		if (is_press)
 			event = MIDDLEBUTTON_EVENT_L_DOWN;
 		else
 			event = MIDDLEBUTTON_EVENT_L_UP;
 		break;
-	case BTN_RIGHT:
+	case EVDEV_BTN_RIGHT:
 		if (is_press)
 			event = MIDDLEBUTTON_EVENT_R_DOWN;
 		else
@@ -605,18 +582,17 @@ evdev_middlebutton_filter_button(struct
 
 	/* BTN_MIDDLE counts as "other" and resets middle button
 	 * emulation */
-	case BTN_MIDDLE:
+	case EVDEV_BTN_MIDDLE:
 	default:
 		event = MIDDLEBUTTON_EVENT_OTHER;
 		break;
 	}
 
-	if (button < BTN_LEFT ||
+	if (evdev_usage_lt(button, EVDEV_BTN_LEFT) ||
 	    btnbit >= sizeof(device->middlebutton.button_mask) * 8) {
 		evdev_log_bug_libinput(device,
 				       "Button mask too small for %s\n",
-				       libevdev_event_code_get_name(EV_KEY,
-								    button));
+				       evdev_usage_code_name(button));
 		return true;
 	}
 
@@ -680,9 +656,9 @@ evdev_middlebutton_get(struct libinput_d
 {
 	struct evdev_device *evdev = evdev_device(device);
 
-	return evdev->middlebutton.want_enabled ?
-			LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED :
-			LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
+	return evdev->middlebutton.want_enabled
+		       ? LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED
+		       : LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
 }
 
 enum libinput_config_middle_emulation_state
@@ -690,15 +666,13 @@ evdev_middlebutton_get_default(struct li
 {
 	struct evdev_device *evdev = evdev_device(device);
 
-	return evdev->middlebutton.enabled_default ?
-			LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED :
-			LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
+	return evdev->middlebutton.enabled_default
+		       ? LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED
+		       : LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
 }
 
 void
-evdev_init_middlebutton(struct evdev_device *device,
-			bool enable,
-			bool want_config)
+evdev_init_middlebutton(struct evdev_device *device, bool enable, bool want_config)
 {
 	char timer_name[64];
 
diff -pruN 1.28.1-1/src/evdev-mt-touchpad-buttons.c 1.30.0-1/src/evdev-mt-touchpad-buttons.c
--- 1.28.1-1/src/evdev-mt-touchpad-buttons.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-mt-touchpad-buttons.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,10 +26,11 @@
 #include <limits.h>
 #include <math.h>
 #include <string.h>
-#include "linux/input.h"
 
 #include "util-input-event.h"
+
 #include "evdev-mt-touchpad.h"
+#include "linux/input.h"
 
 #define DEFAULT_BUTTON_ENTER_TIMEOUT ms2us(100)
 #define DEFAULT_BUTTON_LEAVE_TIMEOUT ms2us(300)
@@ -43,10 +44,10 @@
  * The state machine only affects the soft button area code.
  */
 
-static inline const char*
+static inline const char *
 button_state_to_str(enum button_state state)
 {
-	switch(state) {
+	switch (state) {
 	CASE_RETURN_STRING(BUTTON_STATE_NONE);
 	CASE_RETURN_STRING(BUTTON_STATE_AREA);
 	CASE_RETURN_STRING(BUTTON_STATE_BOTTOM);
@@ -58,10 +59,10 @@ button_state_to_str(enum button_state st
 	return NULL;
 }
 
-static inline const char*
+static inline const char *
 button_event_to_str(enum button_event event)
 {
-	switch(event) {
+	switch (event) {
 	CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_R);
 	CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_M);
 	CASE_RETURN_STRING(BUTTON_EVENT_IN_BOTTOM_L);
@@ -78,23 +79,20 @@ button_event_to_str(enum button_event ev
 }
 
 static inline bool
-is_inside_bottom_button_area(const struct tp_dispatch *tp,
-			     const struct tp_touch *t)
+is_inside_bottom_button_area(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
 	return t->point.y >= tp->buttons.bottom_area.top_edge;
 }
 
 static inline bool
-is_inside_bottom_right_area(const struct tp_dispatch *tp,
-			    const struct tp_touch *t)
+is_inside_bottom_right_area(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
 	return is_inside_bottom_button_area(tp, t) &&
 	       t->point.x > tp->buttons.bottom_area.rightbutton_left_edge;
 }
 
 static inline bool
-is_inside_bottom_middle_area(const struct tp_dispatch *tp,
-			   const struct tp_touch *t)
+is_inside_bottom_middle_area(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
 	return is_inside_bottom_button_area(tp, t) &&
 	       !is_inside_bottom_right_area(tp, t) &&
@@ -102,23 +100,20 @@ is_inside_bottom_middle_area(const struc
 }
 
 static inline bool
-is_inside_top_button_area(const struct tp_dispatch *tp,
-			  const struct tp_touch *t)
+is_inside_top_button_area(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
 	return t->point.y <= tp->buttons.top_area.bottom_edge;
 }
 
 static inline bool
-is_inside_top_right_area(const struct tp_dispatch *tp,
-			 const struct tp_touch *t)
+is_inside_top_right_area(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
 	return is_inside_top_button_area(tp, t) &&
 	       t->point.x > tp->buttons.top_area.rightbutton_left_edge;
 }
 
 static inline bool
-is_inside_top_middle_area(const struct tp_dispatch *tp,
-			  const struct tp_touch *t)
+is_inside_top_middle_area(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
 	return is_inside_top_button_area(tp, t) &&
 	       t->point.x >= tp->buttons.top_area.leftbutton_right_edge &&
@@ -126,21 +121,15 @@ is_inside_top_middle_area(const struct t
 }
 
 static void
-tp_button_set_enter_timer(struct tp_dispatch *tp,
-			  struct tp_touch *t,
-			  uint64_t time)
+tp_button_set_enter_timer(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
-	libinput_timer_set(&t->button.timer,
-			   time + DEFAULT_BUTTON_ENTER_TIMEOUT);
+	libinput_timer_set(&t->button.timer, time + DEFAULT_BUTTON_ENTER_TIMEOUT);
 }
 
 static void
-tp_button_set_leave_timer(struct tp_dispatch *tp,
-			  struct tp_touch *t,
-			  uint64_t time)
+tp_button_set_leave_timer(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
-	libinput_timer_set(&t->button.timer,
-			   time + DEFAULT_BUTTON_LEAVE_TIMEOUT);
+	libinput_timer_set(&t->button.timer, time + DEFAULT_BUTTON_LEAVE_TIMEOUT);
 }
 
 /*
@@ -252,8 +241,7 @@ tp_button_release_other_bottom_touches(s
 	tp_for_each_touch(tp, t) {
 		uint64_t tdelta;
 
-		if (t->button.state != BUTTON_STATE_BOTTOM ||
-		    t->button.has_moved)
+		if (t->button.state != BUTTON_STATE_BOTTOM || t->button.has_moved)
 			continue;
 
 		if (other_start_time > t->button.initial_time)
@@ -279,11 +267,7 @@ tp_button_bottom_handle_event(struct tp_
 	case BUTTON_EVENT_IN_BOTTOM_M:
 	case BUTTON_EVENT_IN_BOTTOM_L:
 		if (event != t->button.current)
-			tp_button_set_state(tp,
-					    t,
-					    BUTTON_STATE_BOTTOM,
-					    event,
-					    time);
+			tp_button_set_state(tp, t, BUTTON_STATE_BOTTOM, event, time);
 		break;
 	case BUTTON_EVENT_IN_TOP_R:
 	case BUTTON_EVENT_IN_TOP_M:
@@ -296,8 +280,7 @@ tp_button_bottom_handle_event(struct tp_
 		 * simultaneously with this finger, release those fingers
 		 * because they're part of a gesture.
 		 */
-		tp_button_release_other_bottom_touches(tp,
-						       t->button.initial_time);
+		tp_button_release_other_bottom_touches(tp, t->button.initial_time);
 		break;
 	case BUTTON_EVENT_UP:
 		tp_button_set_state(tp, t, BUTTON_STATE_NONE, event, time);
@@ -325,11 +308,7 @@ tp_button_top_handle_event(struct tp_dis
 	case BUTTON_EVENT_IN_TOP_M:
 	case BUTTON_EVENT_IN_TOP_L:
 		if (event != t->button.current)
-			tp_button_set_state(tp,
-					    t,
-					    BUTTON_STATE_TOP_NEW,
-					    event,
-					    time);
+			tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW, event, time);
 		break;
 	case BUTTON_EVENT_IN_AREA:
 		tp_button_set_state(tp, t, BUTTON_STATE_TOP_TO_IGNORE, event, time);
@@ -350,7 +329,7 @@ tp_button_top_new_handle_event(struct tp
 			       enum button_event event,
 			       uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case BUTTON_EVENT_IN_BOTTOM_R:
 	case BUTTON_EVENT_IN_BOTTOM_M:
 	case BUTTON_EVENT_IN_BOTTOM_L:
@@ -360,11 +339,7 @@ tp_button_top_new_handle_event(struct tp
 	case BUTTON_EVENT_IN_TOP_M:
 	case BUTTON_EVENT_IN_TOP_L:
 		if (event != t->button.current)
-			tp_button_set_state(tp,
-					    t,
-					    BUTTON_STATE_TOP_NEW,
-					    event,
-					    time);
+			tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW, event, time);
 		break;
 	case BUTTON_EVENT_IN_AREA:
 		tp_button_set_state(tp, t, BUTTON_STATE_AREA, event, time);
@@ -389,22 +364,14 @@ tp_button_top_to_ignore_handle_event(str
 				     enum button_event event,
 				     uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case BUTTON_EVENT_IN_TOP_R:
 	case BUTTON_EVENT_IN_TOP_M:
 	case BUTTON_EVENT_IN_TOP_L:
 		if (event == t->button.current)
-			tp_button_set_state(tp,
-					    t,
-					    BUTTON_STATE_TOP,
-					    event,
-					    time);
+			tp_button_set_state(tp, t, BUTTON_STATE_TOP, event, time);
 		else
-			tp_button_set_state(tp,
-					    t,
-					    BUTTON_STATE_TOP_NEW,
-					    event,
-					    time);
+			tp_button_set_state(tp, t, BUTTON_STATE_TOP_NEW, event, time);
 		break;
 	case BUTTON_EVENT_IN_BOTTOM_R:
 	case BUTTON_EVENT_IN_BOTTOM_M:
@@ -459,7 +426,7 @@ tp_button_handle_event(struct tp_dispatc
 {
 	enum button_state current = t->button.state;
 
-	switch(t->button.state) {
+	switch (t->button.state) {
 	case BUTTON_STATE_NONE:
 		tp_button_none_handle_event(tp, t, event, time);
 		break;
@@ -484,12 +451,13 @@ tp_button_handle_event(struct tp_dispatc
 	}
 
 	if (current != t->button.state)
-		evdev_log_debug(tp->device,
-				"button state: touch %d from %-20s event %-24s to %-20s\n",
-				t->index,
-				button_state_to_str(current),
-				button_event_to_str(event),
-				button_state_to_str(t->button.state));
+		evdev_log_debug(
+			tp->device,
+			"button state: touch %d from %-20s event %-24s to %-20s\n",
+			t->index,
+			button_state_to_str(current),
+			button_event_to_str(event),
+			button_state_to_str(t->button.state));
 }
 
 static inline void
@@ -523,8 +491,7 @@ tp_button_check_for_movement(struct tp_d
 	if (vector_length > 5.0 /* mm */) {
 		t->button.has_moved = true;
 
-		tp_button_release_other_bottom_touches(tp,
-						       t->button.initial_time);
+		tp_button_release_other_bottom_touches(tp, t->button.initial_time);
 	}
 }
 
@@ -588,17 +555,15 @@ tp_button_handle_timeout(uint64_t now, v
 }
 
 void
-tp_process_button(struct tp_dispatch *tp,
-		  const struct input_event *e,
-		  uint64_t time)
+tp_process_button(struct tp_dispatch *tp, const struct evdev_event *e, uint64_t time)
 {
-	uint32_t mask = bit(e->code - BTN_LEFT);
+	uint32_t mask = bit(evdev_usage_enum(e->usage) - EVDEV_BTN_LEFT);
 
 	/* Ignore other buttons on clickpads */
-	if (tp->buttons.is_clickpad && e->code != BTN_LEFT) {
+	if (tp->buttons.is_clickpad && evdev_usage_ne(e->usage, EVDEV_BTN_LEFT)) {
 		evdev_log_bug_kernel(tp->device,
 				     "received %s button event on a clickpad\n",
-				     libevdev_event_code_get_name(EV_KEY, e->code));
+				     evdev_event_get_code_name(e));
 		return;
 	}
 
@@ -612,8 +577,7 @@ tp_process_button(struct tp_dispatch *tp
 }
 
 void
-tp_release_all_buttons(struct tp_dispatch *tp,
-		       uint64_t time)
+tp_release_all_buttons(struct tp_dispatch *tp, uint64_t time)
 {
 	if (tp->buttons.state) {
 		tp->buttons.state = 0;
@@ -622,8 +586,7 @@ tp_release_all_buttons(struct tp_dispatc
 }
 
 static void
-tp_init_softbuttons(struct tp_dispatch *tp,
-		    struct evdev_device *device)
+tp_init_softbuttons(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	double width, height;
 	struct device_coords edges;
@@ -658,13 +621,12 @@ tp_init_softbuttons(struct tp_dispatch *
 	 * On touchpads with visible markings we reduce the size of the
 	 * middle button since users have a visual guide.
 	 */
-	if (evdev_device_has_model_quirk(device,
-					 QUIRK_MODEL_TOUCHPAD_VISIBLE_MARKER)) {
-		mm.x = width/2 - 5; /* 10mm wide */
+	if (evdev_device_has_model_quirk(device, QUIRK_MODEL_TOUCHPAD_VISIBLE_MARKER)) {
+		mm.x = width / 2 - 5; /* 10mm wide */
 		edges = evdev_device_mm_to_units(device, &mm);
 		mb_le = edges.x;
 
-		mm.x = width/2 + 5; /* 10mm wide */
+		mm.x = width / 2 + 5; /* 10mm wide */
 		edges = evdev_device_mm_to_units(device, &mm);
 		mb_re = edges.x;
 	} else {
@@ -717,7 +679,7 @@ static inline uint32_t
 tp_button_config_click_get_methods(struct libinput_device *device)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 	uint32_t methods = LIBINPUT_CONFIG_CLICK_METHOD_NONE;
 
 	if (tp->buttons.is_clickpad) {
@@ -762,7 +724,7 @@ tp_button_config_click_set_method(struct
 				  enum libinput_config_click_method method)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 
 	tp->buttons.click_method = method;
 	tp_switch_click_method(tp);
@@ -774,7 +736,7 @@ static enum libinput_config_click_method
 tp_button_config_click_get_method(struct libinput_device *device)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 
 	return tp->buttons.click_method;
 }
@@ -805,7 +767,7 @@ static enum libinput_config_click_method
 tp_button_config_click_get_default_method(struct libinput_device *device)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 
 	return tp_click_get_default_method(tp);
 }
@@ -813,19 +775,16 @@ tp_button_config_click_get_default_metho
 void
 tp_clickpad_middlebutton_apply_config(struct evdev_device *device)
 {
-	struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch;
 
-	if (!tp->buttons.is_clickpad ||
-	    tp->buttons.state != 0)
+	if (!tp->buttons.is_clickpad || tp->buttons.state != 0)
 		return;
 
-	if (device->middlebutton.want_enabled ==
-	    device->middlebutton.enabled)
+	if (device->middlebutton.want_enabled == device->middlebutton.enabled)
 		return;
 
 	device->middlebutton.enabled = device->middlebutton.want_enabled;
-	if (tp->buttons.click_method ==
-	    LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS)
+	if (tp->buttons.click_method == LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS)
 		tp_init_softbuttons(tp, device);
 }
 
@@ -837,7 +796,7 @@ tp_clickpad_middlebutton_is_available(st
 
 static enum libinput_config_status
 tp_clickpad_middlebutton_set(struct libinput_device *device,
-		     enum libinput_config_middle_emulation_state enable)
+			     enum libinput_config_middle_emulation_state enable)
 {
 	struct evdev_device *evdev = evdev_device(device);
 
@@ -885,11 +844,9 @@ tp_init_clickpad_middlebutton_emulation(
 }
 
 static inline void
-tp_init_middlebutton_emulation(struct tp_dispatch *tp,
-			       struct evdev_device *device)
+tp_init_middlebutton_emulation(struct tp_dispatch *tp, struct evdev_device *device)
 {
-	bool enable_by_default,
-	     want_config_option;
+	bool enable_by_default, want_config_option;
 
 	/* On clickpads we provide the config option but disable by default.
 	   When enabled, the middle software button disappears */
@@ -913,9 +870,7 @@ tp_init_middlebutton_emulation(struct tp
 	} else
 		return;
 
-	evdev_init_middlebutton(tp->device,
-				enable_by_default,
-				want_config_option);
+	evdev_init_middlebutton(tp->device, enable_by_default, want_config_option);
 }
 
 static bool
@@ -937,22 +892,22 @@ tp_guess_clickpad(const struct tp_dispat
 	 */
 	if (!is_clickpad && has_left && !has_right &&
 	    (tp->device->model_flags & EVDEV_MODEL_APPLE_TOUCHPAD_ONEBUTTON) == 0) {
-		evdev_log_bug_kernel(device,
-				     "missing right button, assuming it is a clickpad.\n");
+		evdev_log_bug_kernel(
+			device,
+			"missing right button, assuming it is a clickpad.\n");
 		is_clickpad = true;
 	}
 
 	if (has_middle || has_right) {
 		if (is_clickpad)
-			evdev_log_bug_kernel(device,
-					     "clickpad advertising right button. "
-					     "See %s/clickpad-with-right-button.html for details\n",
-					     HTTP_DOC_LINK);
-	} else if (has_left &&
-		   !is_clickpad &&
+			evdev_log_bug_kernel(
+				device,
+				"clickpad advertising right button. "
+				"See %s/clickpad-with-right-button.html for details\n",
+				HTTP_DOC_LINK);
+	} else if (has_left && !is_clickpad &&
 		   libevdev_get_id_vendor(device->evdev) != VENDOR_ID_APPLE) {
-			evdev_log_bug_kernel(device,
-					     "non clickpad without right button?\n");
+		evdev_log_bug_kernel(device, "non clickpad without right button?\n");
 	}
 
 	return is_clickpad;
@@ -1004,8 +959,7 @@ tp_button_config_get_default_clickfinger
 }
 
 void
-tp_init_buttons(struct tp_dispatch *tp,
-		struct evdev_device *device)
+tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	struct tp_touch *t;
 	const struct input_absinfo *absinfo_x, *absinfo_y;
@@ -1013,23 +967,27 @@ tp_init_buttons(struct tp_dispatch *tp,
 
 	tp->buttons.is_clickpad = tp_guess_clickpad(tp, device);
 
-	tp->buttons.has_topbuttons = libevdev_has_property(device->evdev,
-						        INPUT_PROP_TOPBUTTONPAD);
+	tp->buttons.has_topbuttons =
+		libevdev_has_property(device->evdev, INPUT_PROP_TOPBUTTONPAD);
 
 	absinfo_x = device->abs.absinfo_x;
 	absinfo_y = device->abs.absinfo_y;
 
 	/* pinned-finger motion threshold, see tp_unpin_finger. */
-	tp->buttons.motion_dist.x_scale_coeff = 1.0/absinfo_x->resolution;
-	tp->buttons.motion_dist.y_scale_coeff = 1.0/absinfo_y->resolution;
+	tp->buttons.motion_dist.x_scale_coeff = 1.0 / absinfo_x->resolution;
+	tp->buttons.motion_dist.y_scale_coeff = 1.0 / absinfo_y->resolution;
 
 	tp->buttons.config_method.get_methods = tp_button_config_click_get_methods;
 	tp->buttons.config_method.set_method = tp_button_config_click_set_method;
 	tp->buttons.config_method.get_method = tp_button_config_click_get_method;
-	tp->buttons.config_method.get_default_method = tp_button_config_click_get_default_method;
-	tp->buttons.config_method.set_clickfinger_map = tp_button_config_set_clickfinger_map;
-	tp->buttons.config_method.get_clickfinger_map = tp_button_config_get_clickfinger_map;
-	tp->buttons.config_method.get_default_clickfinger_map = tp_button_config_get_default_clickfinger_map;
+	tp->buttons.config_method.get_default_method =
+		tp_button_config_click_get_default_method;
+	tp->buttons.config_method.set_clickfinger_map =
+		tp_button_config_set_clickfinger_map;
+	tp->buttons.config_method.get_clickfinger_map =
+		tp_button_config_get_clickfinger_map;
+	tp->buttons.config_method.get_default_clickfinger_map =
+		tp_button_config_get_default_clickfinger_map;
 
 	tp->device->base.config.click_method = &tp->buttons.config_method;
 
@@ -1057,7 +1015,8 @@ tp_init_buttons(struct tp_dispatch *tp,
 		libinput_timer_init(&t->button.timer,
 				    tp_libinput_context(tp),
 				    timer_name,
-				    tp_button_handle_timeout, t);
+				    tp_button_handle_timeout,
+				    t);
 	}
 }
 
@@ -1079,20 +1038,21 @@ tp_post_physical_buttons(struct tp_dispa
 
 	current = tp->buttons.state;
 	old = tp->buttons.old_state;
-	button = BTN_LEFT;
+	button = EVDEV_BTN_LEFT;
 
 	while (current || old) {
 		enum libinput_button_state state;
 
 		if ((current & 0x1) ^ (old & 0x1)) {
-			uint32_t b;
+			evdev_usage_t b;
 
 			if (!!(current & 0x1))
 				state = LIBINPUT_BUTTON_STATE_PRESSED;
 			else
 				state = LIBINPUT_BUTTON_STATE_RELEASED;
 
-			b = evdev_to_left_handed(tp->device, button);
+			b = evdev_to_left_handed(tp->device,
+						 evdev_usage_from_uint32_t(button));
 			evdev_pointer_notify_physical_button(tp->device,
 							     time,
 							     b,
@@ -1150,29 +1110,27 @@ tp_clickfinger_within_distance(struct tp
 	 *   and the other one isn't
 	 */
 
-	if (tp->device->abs.dimensions.y/yres < 50)
+	if (tp->device->abs.dimensions.y / yres < 50)
 		goto out;
 
 	bottom_threshold = tp->device->abs.absinfo_y->maximum - 20 * yres;
-	if ((t1->point.y > bottom_threshold) !=
-		    (t2->point.y > bottom_threshold))
+	if ((t1->point.y > bottom_threshold) != (t2->point.y > bottom_threshold))
 		within_distance = 0;
 
 out:
 	return within_distance;
 }
 
-static uint32_t
+static evdev_usage_t
 tp_clickfinger_set_button(struct tp_dispatch *tp)
 {
 	uint32_t button;
 	unsigned int nfingers = 0;
 	struct tp_touch *t;
-	struct tp_touch *first = NULL,
-			*second = NULL;
-	int32_t button_map[2][3] = {
-		{ BTN_LEFT, BTN_RIGHT, BTN_MIDDLE },
-		{ BTN_LEFT, BTN_MIDDLE, BTN_RIGHT },
+	struct tp_touch *first = NULL, *second = NULL;
+	uint32_t button_map[2][3] = {
+		{ EVDEV_BTN_LEFT, EVDEV_BTN_RIGHT, EVDEV_BTN_MIDDLE },
+		{ EVDEV_BTN_LEFT, EVDEV_BTN_MIDDLE, EVDEV_BTN_RIGHT },
 	};
 
 	tp_for_each_touch(tp, t) {
@@ -1209,39 +1167,36 @@ out:
 	switch (nfingers) {
 	case 1:
 	case 2:
-	case 3: button = button_map[tp->buttons.map][nfingers-1]; break;
+	case 3:
+		button = button_map[tp->buttons.map][nfingers - 1];
+		break;
 	default:
 		button = 0;
 		break;
 	}
 
-	return button;
+	return evdev_usage_from_uint32_t(button);
 }
 
 static int
 tp_notify_clickpadbutton(struct tp_dispatch *tp,
 			 uint64_t time,
-			 uint32_t button,
+			 evdev_usage_t button,
 			 uint32_t is_topbutton,
 			 enum libinput_button_state state)
 {
 	/* If we've a trackpoint, send top buttons through the trackpoint */
 	if (tp->buttons.trackpoint) {
 		if (is_topbutton) {
-			struct evdev_dispatch *dispatch = tp->buttons.trackpoint->dispatch;
-			struct input_event event, syn_report;
-			int value;
-
-			value = (state == LIBINPUT_BUTTON_STATE_PRESSED) ? 1 : 0;
-			event = input_event_init(time, EV_KEY, button, value);
-			syn_report = input_event_init(time, EV_SYN, SYN_REPORT, 0);
-			dispatch->interface->process(dispatch,
-						     tp->buttons.trackpoint,
-						     &event,
-						     time);
+			_unref_(evdev_frame) *frame = evdev_frame_new(2);
+			struct evdev_dispatch *dispatch =
+				tp->buttons.trackpoint->dispatch;
+			int value = (state == LIBINPUT_BUTTON_STATE_PRESSED) ? 1 : 0;
+			evdev_frame_set_time(frame, time);
+			evdev_frame_append_one(frame, button, value);
 			dispatch->interface->process(dispatch,
 						     tp->buttons.trackpoint,
-						     &syn_report,
+						     frame,
 						     time);
 			return 1;
 		}
@@ -1263,7 +1218,7 @@ tp_notify_clickpadbutton(struct tp_dispa
 		button = tp_clickfinger_set_button(tp);
 		tp->buttons.active = button;
 
-		if (!button)
+		if (evdev_usage_eq(button, 0))
 			return 0;
 	}
 
@@ -1274,7 +1229,8 @@ tp_notify_clickpadbutton(struct tp_dispa
 static int
 tp_post_clickpadbutton_buttons(struct tp_dispatch *tp, uint64_t time)
 {
-	uint32_t current, old, button, is_top;
+	uint32_t current, old, is_top;
+	evdev_usage_t button;
 	enum libinput_button_state state;
 	enum { AREA = 0x01, LEFT = 0x02, MIDDLE = 0x04, RIGHT = 0x08 };
 	bool want_left_handed = true;
@@ -1330,24 +1286,24 @@ tp_post_clickpadbutton_buttons(struct tp
 			}
 		}
 
-		if (area == 0 &&
-		    tp->buttons.click_method != LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) {
+		if (area == 0 && tp->buttons.click_method !=
+					 LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) {
 			/* No touches, wait for a touch before processing */
 			tp->buttons.click_pending = true;
 			return 0;
 		}
 
-		if ((tp->device->middlebutton.enabled || is_top) &&
-		    (area & LEFT) && (area & RIGHT)) {
-			button = BTN_MIDDLE;
+		if ((tp->device->middlebutton.enabled || is_top) && (area & LEFT) &&
+		    (area & RIGHT)) {
+			button = evdev_usage_from(EVDEV_BTN_MIDDLE);
 		} else if (area & MIDDLE) {
-			button = BTN_MIDDLE;
+			button = evdev_usage_from(EVDEV_BTN_MIDDLE);
 		} else if (area & RIGHT) {
-			button = BTN_RIGHT;
+			button = evdev_usage_from(EVDEV_BTN_RIGHT);
 		} else if (area & LEFT) {
-			button = BTN_LEFT;
+			button = evdev_usage_from(EVDEV_BTN_LEFT);
 		} else { /* main or no area (for clickfinger) is always BTN_LEFT */
-			button = BTN_LEFT;
+			button = evdev_usage_from(EVDEV_BTN_LEFT);
 			want_left_handed = false;
 		}
 
@@ -1363,19 +1319,15 @@ tp_post_clickpadbutton_buttons(struct tp
 	} else {
 		button = tp->buttons.active;
 		is_top = tp->buttons.active_is_topbutton;
-		tp->buttons.active = 0;
+		tp->buttons.active = evdev_usage_from_uint32_t(0);
 		tp->buttons.active_is_topbutton = 0;
 		state = LIBINPUT_BUTTON_STATE_RELEASED;
 	}
 
 	tp->buttons.click_pending = false;
 
-	if (button)
-		return tp_notify_clickpadbutton(tp,
-						time,
-						button,
-						is_top,
-						state);
+	if (evdev_usage_ne(button, 0))
+		return tp_notify_clickpadbutton(tp, time, button, is_top, state);
 	return 0;
 }
 
@@ -1390,8 +1342,7 @@ tp_post_button_events(struct tp_dispatch
 }
 
 bool
-tp_button_touch_active(const struct tp_dispatch *tp,
-		       const struct tp_touch *t)
+tp_button_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
 	return t->button.state == BUTTON_STATE_AREA || t->button.has_moved;
 }
@@ -1400,6 +1351,5 @@ bool
 tp_button_is_inside_softbutton_area(const struct tp_dispatch *tp,
 				    const struct tp_touch *t)
 {
-	return is_inside_top_button_area(tp, t) ||
-	       is_inside_bottom_button_area(tp, t);
+	return is_inside_top_button_area(tp, t) || is_inside_bottom_button_area(tp, t);
 }
diff -pruN 1.28.1-1/src/evdev-mt-touchpad-edge-scroll.c 1.30.0-1/src/evdev-mt-touchpad-edge-scroll.c
--- 1.28.1-1/src/evdev-mt-touchpad-edge-scroll.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-mt-touchpad-edge-scroll.c	2025-11-25 03:40:43.000000000 +0000
@@ -43,7 +43,7 @@ enum scroll_event {
 	SCROLL_EVENT_POSTED,
 };
 
-static inline const char*
+static inline const char *
 edge_state_to_str(enum tp_edge_scroll_touch_state state)
 {
 
@@ -56,7 +56,7 @@ edge_state_to_str(enum tp_edge_scroll_to
 	return NULL;
 }
 
-static inline const char*
+static inline const char *
 edge_event_to_str(enum scroll_event event)
 {
 	switch (event) {
@@ -87,21 +87,17 @@ tp_touch_get_edge(const struct tp_dispat
 }
 
 static inline void
-tp_edge_scroll_set_timer(struct tp_dispatch *tp,
-			 struct tp_touch *t,
-			 uint64_t time)
+tp_edge_scroll_set_timer(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
 	const int DEFAULT_SCROLL_LOCK_TIMEOUT = ms2us(300);
 	/* if we use software buttons, we disable timeout-based
 	 * edge scrolling. A finger resting on the button areas is
 	 * likely there to trigger a button event.
 	 */
-	if (tp->buttons.click_method ==
-	    LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS)
+	if (tp->buttons.click_method == LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS)
 		return;
 
-	libinput_timer_set(&t->scroll.timer,
-			   time + DEFAULT_SCROLL_LOCK_TIMEOUT);
+	libinput_timer_set(&t->scroll.timer, time + DEFAULT_SCROLL_LOCK_TIMEOUT);
 }
 
 static void
@@ -155,10 +151,11 @@ tp_edge_scroll_handle_none(struct tp_dis
 	case SCROLL_EVENT_RELEASE:
 	case SCROLL_EVENT_TIMEOUT:
 	case SCROLL_EVENT_POSTED:
-		evdev_log_bug_libinput(tp->device,
-				 "edge-scroll: touch %d: unexpected scroll event %d in none state\n",
-				 t->index,
-				 event);
+		evdev_log_bug_libinput(
+			tp->device,
+			"edge-scroll: touch %d: unexpected scroll event %d in none state\n",
+			t->index,
+			event);
 		break;
 	}
 }
@@ -171,10 +168,11 @@ tp_edge_scroll_handle_edge_new(struct tp
 {
 	switch (event) {
 	case SCROLL_EVENT_TOUCH:
-		evdev_log_bug_libinput(tp->device,
-			       "edge-scroll: touch %d: unexpected scroll event %d in edge new state\n",
-			       t->index,
-			       event);
+		evdev_log_bug_libinput(
+			tp->device,
+			"edge-scroll: touch %d: unexpected scroll event %d in edge new state\n",
+			t->index,
+			event);
 		break;
 	case SCROLL_EVENT_MOTION:
 		t->scroll.edge &= tp_touch_get_edge(tp, t);
@@ -185,17 +183,11 @@ tp_edge_scroll_handle_edge_new(struct tp
 						 time);
 		break;
 	case SCROLL_EVENT_RELEASE:
-		tp_edge_scroll_set_state(tp,
-					 t,
-					 EDGE_SCROLL_TOUCH_STATE_NONE,
-					 time);
+		tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_NONE, time);
 		break;
 	case SCROLL_EVENT_TIMEOUT:
 	case SCROLL_EVENT_POSTED:
-		tp_edge_scroll_set_state(tp,
-					 t,
-					 EDGE_SCROLL_TOUCH_STATE_EDGE,
-					 time);
+		tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_EDGE, time);
 		break;
 	}
 }
@@ -209,10 +201,11 @@ tp_edge_scroll_handle_edge(struct tp_dis
 	switch (event) {
 	case SCROLL_EVENT_TOUCH:
 	case SCROLL_EVENT_TIMEOUT:
-		evdev_log_bug_libinput(tp->device,
-				 "edge-scroll: touch %d: unexpected scroll event %d in edge state\n",
-				 t->index,
-				 event);
+		evdev_log_bug_libinput(
+			tp->device,
+			"edge-scroll: touch %d: unexpected scroll event %d in edge state\n",
+			t->index,
+			event);
 		break;
 	case SCROLL_EVENT_MOTION:
 		/* If started at the bottom right, decide in which dir to scroll */
@@ -226,10 +219,7 @@ tp_edge_scroll_handle_edge(struct tp_dis
 		}
 		break;
 	case SCROLL_EVENT_RELEASE:
-		tp_edge_scroll_set_state(tp,
-					 t,
-					 EDGE_SCROLL_TOUCH_STATE_NONE,
-					 time);
+		tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_NONE, time);
 		break;
 	case SCROLL_EVENT_POSTED:
 		break;
@@ -247,16 +237,13 @@ tp_edge_scroll_handle_area(struct tp_dis
 	case SCROLL_EVENT_TIMEOUT:
 	case SCROLL_EVENT_POSTED:
 		evdev_log_bug_libinput(tp->device,
-				 "unexpected scroll event %d in area state\n",
-				 event);
+				       "unexpected scroll event %d in area state\n",
+				       event);
 		break;
 	case SCROLL_EVENT_MOTION:
 		break;
 	case SCROLL_EVENT_RELEASE:
-		tp_edge_scroll_set_state(tp,
-					 t,
-					 EDGE_SCROLL_TOUCH_STATE_NONE,
-					 time);
+		tp_edge_scroll_set_state(tp, t, EDGE_SCROLL_TOUCH_STATE_NONE, time);
 		break;
 	}
 }
@@ -319,7 +306,7 @@ tp_edge_scroll_init(struct tp_dispatch *
 	   when software buttons were a thing, e.g. Lenovo *20 series)
 	 */
 	if (!tp->buttons.is_clickpad)
-	    want_horiz_scroll = (height >= 40);
+		want_horiz_scroll = (height >= 40);
 
 	/* 7mm edge size */
 	mm.x = width - 7;
@@ -345,7 +332,8 @@ tp_edge_scroll_init(struct tp_dispatch *
 		libinput_timer_init(&t->scroll.timer,
 				    tp_libinput_context(tp),
 				    timer_name,
-				    tp_edge_scroll_handle_timeout, t);
+				    tp_edge_scroll_handle_timeout,
+				    t);
 	}
 }
 
@@ -368,11 +356,9 @@ tp_edge_scroll_handle_state(struct tp_di
 	if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE) {
 		tp_for_each_touch(tp, t) {
 			if (t->state == TOUCH_BEGIN)
-				t->scroll.edge_state =
-					EDGE_SCROLL_TOUCH_STATE_AREA;
+				t->scroll.edge_state = EDGE_SCROLL_TOUCH_STATE_AREA;
 			else if (t->state == TOUCH_END)
-				t->scroll.edge_state =
-					EDGE_SCROLL_TOUCH_STATE_NONE;
+				t->scroll.edge_state = EDGE_SCROLL_TOUCH_STATE_NONE;
 		}
 		return;
 	}
@@ -386,16 +372,10 @@ tp_edge_scroll_handle_state(struct tp_di
 		case TOUCH_HOVERING:
 			break;
 		case TOUCH_BEGIN:
-			tp_edge_scroll_handle_event(tp,
-						    t,
-						    SCROLL_EVENT_TOUCH,
-						    time);
+			tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_TOUCH, time);
 			break;
 		case TOUCH_UPDATE:
-			tp_edge_scroll_handle_event(tp,
-						    t,
-						    SCROLL_EVENT_MOTION,
-						    time);
+			tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_MOTION, time);
 			break;
 		case TOUCH_MAYBE_END:
 			/* This shouldn't happen we transfer to TOUCH_END
@@ -406,10 +386,7 @@ tp_edge_scroll_handle_state(struct tp_di
 					t->state);
 			_fallthrough_;
 		case TOUCH_END:
-			tp_edge_scroll_handle_event(tp,
-						    t,
-						    SCROLL_EVENT_RELEASE,
-						    time);
+			tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_RELEASE, time);
 			break;
 		}
 	}
@@ -435,31 +412,30 @@ tp_edge_scroll_post_events(struct tp_dis
 			continue;
 
 		/* only scroll with the finger in the previous edge */
-		if (t->scroll.edge &&
-		    (tp_touch_get_edge(tp, t) & t->scroll.edge) == 0)
+		if (t->scroll.edge && (tp_touch_get_edge(tp, t) & t->scroll.edge) == 0)
 			continue;
 
 		switch (t->scroll.edge) {
-			case EDGE_NONE:
-				if (t->scroll.direction != -1) {
-					/* Send stop scroll event */
-					evdev_notify_axis_finger(device,
-								 time,
-								 bit(t->scroll.direction),
-								 &zero);
-					t->scroll.direction = -1;
-				}
-				continue;
-			case EDGE_RIGHT:
-				axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
-				delta = &normalized.y;
-				break;
-			case EDGE_BOTTOM:
-				axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
-				delta = &normalized.x;
-				break;
-			default: /* EDGE_RIGHT | EDGE_BOTTOM */
-				continue; /* Don't know direction yet, skip */
+		case EDGE_NONE:
+			if (t->scroll.direction != -1) {
+				/* Send stop scroll event */
+				evdev_notify_axis_finger(device,
+							 time,
+							 bit(t->scroll.direction),
+							 &zero);
+				t->scroll.direction = -1;
+			}
+			continue;
+		case EDGE_RIGHT:
+			axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
+			delta = &normalized.y;
+			break;
+		case EDGE_BOTTOM:
+			axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
+			delta = &normalized.x;
+			break;
+		default:          /* EDGE_RIGHT | EDGE_BOTTOM */
+			continue; /* Don't know direction yet, skip */
 		}
 
 		raw = tp_get_delta(t);
@@ -472,14 +448,14 @@ tp_edge_scroll_post_events(struct tp_dis
 		case EDGE_SCROLL_TOUCH_STATE_NONE:
 		case EDGE_SCROLL_TOUCH_STATE_AREA:
 			evdev_log_bug_libinput(device,
-					 "unexpected scroll state %d\n",
-					 t->scroll.edge_state);
+					       "unexpected scroll state %d\n",
+					       t->scroll.edge_state);
 			break;
 		case EDGE_SCROLL_TOUCH_STATE_EDGE_NEW:
 			tmp = normalized;
-			normalized = tp_normalize_delta(tp,
-					device_delta(t->point,
-						     t->scroll.initial));
+			normalized = tp_normalize_delta(
+				tp,
+				device_delta(t->point, t->scroll.initial));
 			if (fabs(*delta) < DEFAULT_SCROLL_THRESHOLD)
 				normalized = zero;
 			else
@@ -492,9 +468,7 @@ tp_edge_scroll_post_events(struct tp_dis
 		if (*delta == 0.0)
 			continue;
 
-		evdev_notify_axis_finger(device, time,
-					 bit(axis),
-					 &normalized);
+		evdev_notify_axis_finger(device, time, bit(axis), &normalized);
 		t->scroll.direction = axis;
 
 		tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_POSTED, time);
@@ -526,8 +500,7 @@ tp_edge_scroll_stop_events(struct tp_dis
 }
 
 int
-tp_edge_scroll_touch_active(const struct tp_dispatch *tp,
-			    const struct tp_touch *t)
+tp_edge_scroll_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
 	return t->scroll.edge_state == EDGE_SCROLL_TOUCH_STATE_AREA;
 }
diff -pruN 1.28.1-1/src/evdev-mt-touchpad-gestures.c 1.30.0-1/src/evdev-mt-touchpad-gestures.c
--- 1.28.1-1/src/evdev-mt-touchpad-gestures.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-mt-touchpad-gestures.c	2025-11-25 03:40:43.000000000 +0000
@@ -68,7 +68,7 @@ enum gesture_event {
  * Any changes in this file must be represented in the diagram.
  */
 
-static inline const char*
+static inline const char *
 gesture_state_to_str(enum tp_gesture_state state)
 {
 	switch (state) {
@@ -90,10 +90,10 @@ gesture_state_to_str(enum tp_gesture_sta
 	return NULL;
 }
 
-static inline const char*
+static inline const char *
 gesture_event_to_str(enum gesture_event event)
 {
-	switch(event) {
+	switch (event) {
 	CASE_RETURN_STRING(GESTURE_EVENT_RESET);
 	CASE_RETURN_STRING(GESTURE_EVENT_END);
 	CASE_RETURN_STRING(GESTURE_EVENT_CANCEL);
@@ -117,7 +117,7 @@ tp_get_touches_delta(struct tp_dispatch
 {
 	struct tp_touch *t;
 	unsigned int i, nactive = 0;
-	struct device_float_coords delta = {0.0, 0.0};
+	struct device_float_coords delta = { 0.0, 0.0 };
 
 	for (i = 0; i < tp->num_slots; i++) {
 		t = &tp->touches[i];
@@ -149,7 +149,7 @@ tp_get_touches_delta(struct tp_dispatch
 static void
 tp_gesture_init_scroll(struct tp_dispatch *tp)
 {
-	struct phys_coords zero = {0.0, 0.0};
+	struct phys_coords zero = { 0.0, 0.0 };
 	tp->scroll.active.h = false;
 	tp->scroll.active.v = false;
 	tp->scroll.duration.h = 0;
@@ -214,10 +214,7 @@ tp_gesture_post_pointer_motion(struct tp
 		struct device_float_coords unaccel;
 
 		unaccel = tp_scale_to_xaxis(tp, raw);
-		pointer_notify_motion(&tp->device->base,
-				      time,
-				      &delta,
-				      &unaccel);
+		pointer_notify_motion(&tp->device->base, time, &delta, &unaccel);
 	}
 }
 
@@ -262,10 +259,8 @@ tp_gesture_same_directions(int dir1, int
 	 * The ((dira & 0x80) && (dirb & 0x01)) checks are to check for bit 0
 	 * and 7 being set as they also represent neighboring directions.
 	 */
-	return ((dir1 | (dir1 >> 1)) & dir2) ||
-		((dir2 | (dir2 >> 1)) & dir1) ||
-		((dir1 & 0x80) && (dir2 & 0x01)) ||
-		((dir2 & 0x80) && (dir1 & 0x01));
+	return ((dir1 | (dir1 >> 1)) & dir2) || ((dir2 | (dir2 >> 1)) & dir1) ||
+	       ((dir1 & 0x80) && (dir2 & 0x01)) || ((dir2 & 0x80) && (dir1 & 0x01));
 }
 
 static struct phys_coords
@@ -347,16 +342,15 @@ tp_gesture_set_scroll_buildup(struct tp_
 
 static void
 tp_gesture_apply_scroll_constraints(struct tp_dispatch *tp,
-				  struct device_float_coords *raw,
-				  struct normalized_coords *delta,
-				  uint64_t time)
+				    struct device_float_coords *raw,
+				    struct normalized_coords *delta,
+				    uint64_t time)
 {
 	uint64_t tdelta = 0;
 	struct phys_coords delta_mm, vector;
 	double vector_decay, vector_length, slope;
 
-	const uint64_t ACTIVE_THRESHOLD = ms2us(100),
-		       INACTIVE_THRESHOLD = ms2us(50),
+	const uint64_t ACTIVE_THRESHOLD = ms2us(100), INACTIVE_THRESHOLD = ms2us(50),
 		       EVENT_TIMEOUT = ms2us(100);
 
 	/* Both axes active == true means free scrolling is enabled */
@@ -380,12 +374,9 @@ tp_gesture_apply_scroll_constraints(stru
 	 */
 	if (tdelta > 0) {
 		double recent, later;
-		recent = ((EVENT_TIMEOUT / 2.0) - tdelta) /
-			 (EVENT_TIMEOUT / 2.0);
-		later = (EVENT_TIMEOUT - tdelta) /
-			(EVENT_TIMEOUT * 2.0);
-		vector_decay = tdelta <= (0.33 * EVENT_TIMEOUT) ?
-			       recent : later;
+		recent = ((EVENT_TIMEOUT / 2.0) - tdelta) / (EVENT_TIMEOUT / 2.0);
+		later = (EVENT_TIMEOUT - tdelta) / (EVENT_TIMEOUT * 2.0);
+		vector_decay = tdelta <= (0.33 * EVENT_TIMEOUT) ? recent : later;
 	} else {
 		vector_decay = 0.0;
 	}
@@ -426,7 +417,7 @@ tp_gesture_apply_scroll_constraints(stru
 				tp->scroll.duration.h = 0;
 		}
 	}
-	if (slope < DEGREE_60  && vector_length > MIN_VECTOR) {
+	if (slope < DEGREE_60 && vector_length > MIN_VECTOR) {
 		tp->scroll.duration.h += tdelta;
 		if (tp->scroll.duration.h > ACTIVE_THRESHOLD)
 			tp->scroll.duration.h = ACTIVE_THRESHOLD;
@@ -489,8 +480,7 @@ tp_gesture_is_quick_hold(struct tp_dispa
 	 * make the hold to stop kinetic scrolling user interaction feel more
 	 * natural.
 	 */
-	return (tp->gesture.finger_count == 1) ||
-	       (tp->gesture.finger_count == 2);
+	return (tp->gesture.finger_count == 1) || (tp->gesture.finger_count == 2);
 }
 
 static bool
@@ -537,9 +527,8 @@ tp_gesture_set_hold_timer(struct tp_disp
 		return;
 
 	if (tp_gesture_use_hold_timer(tp)) {
-		timeout = tp_gesture_is_quick_hold(tp) ?
-			  QUICK_GESTURE_HOLD_TIMEOUT :
-			  DEFAULT_GESTURE_HOLD_TIMEOUT;
+		timeout = tp_gesture_is_quick_hold(tp) ? QUICK_GESTURE_HOLD_TIMEOUT
+						       : DEFAULT_GESTURE_HOLD_TIMEOUT;
 
 		libinput_timer_set(&tp->gesture.hold_timer, time + timeout);
 	}
@@ -550,7 +539,7 @@ tp_gesture_handle_event_on_state_none(st
 				      enum gesture_event event,
 				      uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case GESTURE_EVENT_RESET:
 	case GESTURE_EVENT_END:
 	case GESTURE_EVENT_CANCEL:
@@ -561,7 +550,8 @@ tp_gesture_handle_event_on_state_none(st
 		 * 3fg pinch/hold. Those are niche enough to not worry about
 		 * for now.
 		 */
-		if (!tp->tap.enabled && tp->drag_3fg.nfingers == tp->gesture.finger_count) {
+		if (!tp->tap.enabled &&
+		    tp->drag_3fg.nfingers == tp->gesture.finger_count) {
 			tp->gesture.state = GESTURE_STATE_3FG_DRAG_START;
 		} else {
 			tp_gesture_set_hold_timer(tp, time);
@@ -593,7 +583,7 @@ tp_gesture_handle_event_on_state_unknown
 					 enum gesture_event event,
 					 uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case GESTURE_EVENT_RESET:
 	case GESTURE_EVENT_END:
 	case GESTURE_EVENT_CANCEL:
@@ -605,7 +595,8 @@ tp_gesture_handle_event_on_state_unknown
 	case GESTURE_EVENT_HOLD_TIMEOUT:
 	case GESTURE_EVENT_TAP_TIMEOUT:
 		tp->gesture.state = GESTURE_STATE_HOLD;
-		gesture_notify_hold_begin(&tp->device->base, time,
+		gesture_notify_hold_begin(&tp->device->base,
+					  time,
 					  tp->gesture.finger_count);
 		break;
 	case GESTURE_EVENT_POINTER_MOTION_START:
@@ -645,15 +636,17 @@ tp_gesture_handle_event_on_state_hold(st
 				      enum gesture_event event,
 				      uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case GESTURE_EVENT_RESET:
 		log_gesture_bug(tp, event);
 		break;
 	case GESTURE_EVENT_END:
 	case GESTURE_EVENT_CANCEL: {
 		bool cancelled = event == GESTURE_EVENT_CANCEL;
-		gesture_notify_hold_end(&tp->device->base, time,
-					tp->gesture.finger_count, cancelled);
+		gesture_notify_hold_end(&tp->device->base,
+					time,
+					tp->gesture.finger_count,
+					cancelled);
 		libinput_timer_cancel(&tp->gesture.hold_timer);
 		tp->gesture.state = GESTURE_STATE_NONE;
 		break;
@@ -701,15 +694,17 @@ tp_gesture_handle_event_on_state_hold_an
 						 enum gesture_event event,
 						 uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case GESTURE_EVENT_RESET:
 		log_gesture_bug(tp, event);
 		break;
 	case GESTURE_EVENT_END:
 	case GESTURE_EVENT_CANCEL: {
 		bool cancelled = event == GESTURE_EVENT_CANCEL;
-		gesture_notify_hold_end(&tp->device->base, time,
-					tp->gesture.finger_count, cancelled);
+		gesture_notify_hold_end(&tp->device->base,
+					time,
+					tp->gesture.finger_count,
+					cancelled);
 		libinput_timer_cancel(&tp->gesture.hold_timer);
 		tp->gesture.state = GESTURE_STATE_NONE;
 		break;
@@ -744,7 +739,7 @@ tp_gesture_handle_event_on_state_pointer
 	struct phys_coords first_moved;
 	double first_mm;
 
-	switch(event) {
+	switch (event) {
 	case GESTURE_EVENT_RESET:
 	case GESTURE_EVENT_END:
 	case GESTURE_EVENT_CANCEL:
@@ -762,8 +757,9 @@ tp_gesture_handle_event_on_state_pointer
 
 		if (first_mm < HOLD_AND_MOTION_THRESHOLD) {
 			tp->gesture.state = GESTURE_STATE_HOLD_AND_MOTION;
-			gesture_notify_hold_begin(&tp->device->base, time,
-					          tp->gesture.finger_count);
+			gesture_notify_hold_begin(&tp->device->base,
+						  time,
+						  tp->gesture.finger_count);
 		}
 		break;
 	case GESTURE_EVENT_FINGER_SWITCH_TIMEOUT:
@@ -785,7 +781,7 @@ tp_gesture_handle_event_on_state_scroll_
 					      enum gesture_event event,
 					      uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case GESTURE_EVENT_RESET:
 	case GESTURE_EVENT_END:
 	case GESTURE_EVENT_CANCEL:
@@ -818,7 +814,7 @@ tp_gesture_handle_event_on_state_scroll(
 					enum gesture_event event,
 					uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case GESTURE_EVENT_RESET:
 	case GESTURE_EVENT_END:
 	case GESTURE_EVENT_CANCEL:
@@ -853,7 +849,7 @@ tp_gesture_handle_event_on_state_pinch_s
 					     enum gesture_event event,
 					     uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case GESTURE_EVENT_RESET:
 	case GESTURE_EVENT_END:
 	case GESTURE_EVENT_CANCEL:
@@ -881,14 +877,15 @@ tp_gesture_handle_event_on_state_pinch(s
 				       enum gesture_event event,
 				       uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case GESTURE_EVENT_RESET:
 		log_gesture_bug(tp, event);
 		break;
 	case GESTURE_EVENT_END:
 	case GESTURE_EVENT_CANCEL: {
 		bool cancelled = event == GESTURE_EVENT_CANCEL;
-		gesture_notify_pinch_end(&tp->device->base, time,
+		gesture_notify_pinch_end(&tp->device->base,
+					 time,
 					 tp->gesture.finger_count,
 					 tp->gesture.prev_scale,
 					 cancelled);
@@ -919,7 +916,7 @@ tp_gesture_handle_event_on_state_swipe_s
 					     enum gesture_event event,
 					     uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case GESTURE_EVENT_RESET:
 	case GESTURE_EVENT_END:
 	case GESTURE_EVENT_CANCEL:
@@ -948,7 +945,7 @@ tp_gesture_handle_event_on_state_swipe(s
 				       enum gesture_event event,
 				       uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case GESTURE_EVENT_RESET:
 		log_gesture_bug(tp, event);
 		break;
@@ -986,7 +983,7 @@ tp_gesture_handle_event_on_state_3fg_dra
 						enum gesture_event event,
 						uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case GESTURE_EVENT_RESET:
 	case GESTURE_EVENT_END:
 	case GESTURE_EVENT_CANCEL:
@@ -1022,7 +1019,7 @@ tp_gesture_handle_event_on_state_3fg_dra
 					  enum gesture_event event,
 					  uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case GESTURE_EVENT_RESET:
 		log_gesture_bug(tp, event);
 		break;
@@ -1030,7 +1027,7 @@ tp_gesture_handle_event_on_state_3fg_dra
 		/* If the gesture is cancelled we release the button immediately */
 		evdev_pointer_notify_button(tp->device,
 					    tp->gesture.drag_3fg_release_time,
-					    BTN_LEFT,
+					    evdev_usage_from(EVDEV_BTN_LEFT),
 					    LIBINPUT_BUTTON_STATE_RELEASED);
 		tp->gesture.state = GESTURE_STATE_NONE;
 		break;
@@ -1043,9 +1040,9 @@ tp_gesture_handle_event_on_state_3fg_dra
 	case GESTURE_EVENT_FINGER_SWITCH_TIMEOUT:
 		if (tp->gesture.finger_count_pending < 2) {
 			evdev_pointer_notify_button(tp->device,
-					    tp->gesture.drag_3fg_release_time,
-					    BTN_LEFT,
-					    LIBINPUT_BUTTON_STATE_RELEASED);
+						    tp->gesture.drag_3fg_release_time,
+						    evdev_usage_from(EVDEV_BTN_LEFT),
+						    LIBINPUT_BUTTON_STATE_RELEASED);
 			tp->gesture.state = GESTURE_STATE_NONE;
 		}
 		break;
@@ -1070,7 +1067,7 @@ tp_gesture_handle_event_on_state_3fg_dra
 						   enum gesture_event event,
 						   uint64_t time)
 {
-	switch(event) {
+	switch (event) {
 	case GESTURE_EVENT_RESET:
 		log_gesture_bug(tp, event);
 		break;
@@ -1082,7 +1079,7 @@ tp_gesture_handle_event_on_state_3fg_dra
 		libinput_timer_cancel(&tp->gesture.finger_count_switch_timer);
 		evdev_pointer_notify_button(tp->device,
 					    tp->gesture.drag_3fg_release_time,
-					    BTN_LEFT,
+					    evdev_usage_from(EVDEV_BTN_LEFT),
 					    LIBINPUT_BUTTON_STATE_RELEASED);
 		tp->gesture.state = GESTURE_STATE_NONE;
 		break;
@@ -1100,7 +1097,7 @@ tp_gesture_handle_event_on_state_3fg_dra
 		libinput_timer_cancel(&tp->gesture.drag_3fg_timer);
 		evdev_pointer_notify_button(tp->device,
 					    tp->gesture.drag_3fg_release_time,
-					    BTN_LEFT,
+					    evdev_usage_from(EVDEV_BTN_LEFT),
 					    LIBINPUT_BUTTON_STATE_RELEASED);
 		tp->gesture.state = GESTURE_STATE_POINTER_MOTION;
 		break;
@@ -1114,7 +1111,7 @@ tp_gesture_handle_event_on_state_3fg_dra
 		libinput_timer_cancel(&tp->gesture.drag_3fg_timer);
 		evdev_pointer_notify_button(tp->device,
 					    tp->gesture.drag_3fg_release_time,
-					    BTN_LEFT,
+					    evdev_usage_from(EVDEV_BTN_LEFT),
 					    LIBINPUT_BUTTON_STATE_RELEASED);
 		tp->gesture.state = GESTURE_STATE_SCROLL_START;
 		break;
@@ -1128,15 +1125,13 @@ tp_gesture_handle_event_on_state_3fg_dra
 }
 
 static void
-tp_gesture_handle_event(struct tp_dispatch *tp,
-			enum gesture_event event,
-			uint64_t time)
+tp_gesture_handle_event(struct tp_dispatch *tp, enum gesture_event event, uint64_t time)
 {
 	enum tp_gesture_state oldstate;
 
 	oldstate = tp->gesture.state;
 
-	switch(tp->gesture.state) {
+	switch (tp->gesture.state) {
 	case GESTURE_STATE_NONE:
 		tp_gesture_handle_event_on_state_none(tp, event, time);
 		break;
@@ -1224,8 +1219,7 @@ static void
 tp_gesture_detect_motion_gestures(struct tp_dispatch *tp, uint64_t time)
 {
 	struct tp_touch *first = tp->gesture.touches[0],
-			*second = tp->gesture.touches[1],
-			*thumb;
+			*second = tp->gesture.touches[1], *thumb;
 	uint32_t dir1, dir2;
 	struct device_coords delta;
 	struct phys_coords first_moved, second_moved, distance_mm;
@@ -1243,8 +1237,7 @@ tp_gesture_detect_motion_gestures(struct
 
 		is_hold_and_motion = (first_mm < HOLD_AND_MOTION_THRESHOLD);
 
-		if (tp->gesture.state == GESTURE_STATE_HOLD &&
-		    is_hold_and_motion) {
+		if (tp->gesture.state == GESTURE_STATE_HOLD && is_hold_and_motion) {
 			tp_gesture_handle_event(tp,
 						GESTURE_EVENT_HOLD_AND_MOTION_START,
 						time);
@@ -1255,9 +1248,7 @@ tp_gesture_detect_motion_gestures(struct
 		    is_hold_and_motion)
 			return;
 
-		tp_gesture_handle_event(tp,
-					GESTURE_EVENT_POINTER_MOTION_START,
-					time);
+		tp_gesture_handle_event(tp, GESTURE_EVENT_POINTER_MOTION_START, time);
 		return;
 	}
 
@@ -1289,8 +1280,7 @@ tp_gesture_detect_motion_gestures(struct
 
 	/* If both touches are within 7mm vertically and 40mm horizontally
 	 * past the timeout, assume scroll/swipe */
-	if ((!tp->gesture.enabled ||
-	     (distance_mm.x < 40.0 && distance_mm.y < 7.0)) &&
+	if ((!tp->gesture.enabled || (distance_mm.x < 40.0 && distance_mm.y < 7.0)) &&
 	    time > (tp->gesture.initial_time + DEFAULT_GESTURE_SWIPE_TIMEOUT)) {
 		if (tp->gesture.finger_count == 2)
 			tp_gesture_handle_event(tp, GESTURE_EVENT_SCROLL_START, time);
@@ -1304,8 +1294,8 @@ tp_gesture_detect_motion_gestures(struct
 
 	/* If 3fg dragging touches are within a 60x10mm box, start
 	 * dragging immediately */
-	if (tp->gesture.finger_count == tp->drag_3fg.nfingers &&
-	    distance_mm.x < 60.0 && distance_mm.y < 10.0) {
+	if (tp->gesture.finger_count == tp->drag_3fg.nfingers && distance_mm.x < 60.0 &&
+	    distance_mm.y < 10.0) {
 		tp_gesture_handle_event(tp, GESTURE_EVENT_3FG_DRAG_START, time);
 		return;
 	}
@@ -1353,10 +1343,8 @@ tp_gesture_detect_motion_gestures(struct
 		/* If more than 2 fingers are involved, and the thumb moves
 		 * while the fingers stay still, assume a pinch if eligible.
 		 */
-		if (finger_mm < min_move &&
-		    tp->gesture.finger_count > 2 &&
-		    tp->gesture.enabled &&
-		    tp->thumb.pinch_eligible) {
+		if (finger_mm < min_move && tp->gesture.finger_count > 2 &&
+		    tp->gesture.enabled && tp->thumb.pinch_eligible) {
 			tp_gesture_handle_event(tp, GESTURE_EVENT_PINCH_START, time);
 			return;
 		}
@@ -1447,9 +1435,7 @@ tp_gesture_handle_state_none(struct tp_d
 		first->gesture.initial = first->point;
 		tp->gesture.touches[0] = first;
 
-		tp_gesture_handle_event(tp,
-					GESTURE_EVENT_FINGER_DETECTED,
-					time);
+		tp_gesture_handle_event(tp, GESTURE_EVENT_FINGER_DETECTED, time);
 		return;
 	}
 
@@ -1487,7 +1473,6 @@ tp_gesture_handle_state_none(struct tp_d
 
 		if (first == second)
 			return;
-
 	}
 
 	tp->gesture.initial_time = time;
@@ -1500,7 +1485,8 @@ tp_gesture_handle_state_none(struct tp_d
 }
 
 static void
-tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time,
+tp_gesture_handle_state_unknown(struct tp_dispatch *tp,
+				uint64_t time,
 				bool ignore_motion)
 {
 	if (!ignore_motion)
@@ -1508,8 +1494,7 @@ tp_gesture_handle_state_unknown(struct t
 }
 
 static void
-tp_gesture_handle_state_hold(struct tp_dispatch *tp, uint64_t time,
-			     bool ignore_motion)
+tp_gesture_handle_state_hold(struct tp_dispatch *tp, uint64_t time, bool ignore_motion)
 {
 	if (!ignore_motion)
 		tp_gesture_detect_motion_gestures(tp, time);
@@ -1605,10 +1590,12 @@ tp_gesture_handle_state_swipe_start(stru
 
 	if (!normalized_is_zero(delta) || !device_float_is_zero(raw)) {
 		const struct normalized_coords zero = { 0.0, 0.0 };
-		gesture_notify_swipe(&tp->device->base, time,
+		gesture_notify_swipe(&tp->device->base,
+				     time,
 				     LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
 				     tp->gesture.finger_count,
-				     &zero, &zero);
+				     &zero,
+				     &zero);
 		tp->gesture.state = GESTURE_STATE_SWIPE;
 	}
 }
@@ -1624,10 +1611,12 @@ tp_gesture_handle_state_swipe(struct tp_
 
 	if (!normalized_is_zero(delta) || !device_float_is_zero(raw)) {
 		unaccel = tp_filter_motion_unaccelerated(tp, &raw, time);
-		gesture_notify_swipe(&tp->device->base, time,
+		gesture_notify_swipe(&tp->device->base,
+				     time,
 				     LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
 				     tp->gesture.finger_count,
-				     &delta, &unaccel);
+				     &delta,
+				     &unaccel);
 	}
 }
 
@@ -1659,10 +1648,14 @@ tp_gesture_handle_state_pinch_start(stru
 	    scale == tp->gesture.prev_scale && angle_delta == 0.0)
 		return;
 
-        gesture_notify_pinch(&tp->device->base, time,
-                             LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
-                             tp->gesture.finger_count,
-                             &zero, &zero, 1.0, 0.0);
+	gesture_notify_pinch(&tp->device->base,
+			     time,
+			     LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
+			     tp->gesture.finger_count,
+			     &zero,
+			     &zero,
+			     1.0,
+			     0.0);
 
 	tp->gesture.prev_scale = scale;
 	tp->gesture.state = GESTURE_STATE_PINCH;
@@ -1696,10 +1689,14 @@ tp_gesture_handle_state_pinch(struct tp_
 		return;
 
 	unaccel = tp_filter_motion_unaccelerated(tp, &fdelta, time);
-	gesture_notify_pinch(&tp->device->base, time,
+	gesture_notify_pinch(&tp->device->base,
+			     time,
 			     LIBINPUT_EVENT_GESTURE_PINCH_UPDATE,
 			     tp->gesture.finger_count,
-			     &delta, &unaccel, scale, angle_delta);
+			     &delta,
+			     &unaccel,
+			     scale,
+			     angle_delta);
 
 	tp->gesture.prev_scale = scale;
 }
@@ -1709,7 +1706,7 @@ tp_gesture_handle_state_3fg_drag_start(s
 {
 	evdev_pointer_notify_button(tp->device,
 				    time,
-				    BTN_LEFT,
+				    evdev_usage_from(EVDEV_BTN_LEFT),
 				    LIBINPUT_BUTTON_STATE_PRESSED);
 	/* FIXME: immediately send a motion event? */
 	tp->gesture.state = GESTURE_STATE_3FG_DRAG;
@@ -1718,16 +1715,8 @@ tp_gesture_handle_state_3fg_drag_start(s
 static void
 tp_gesture_handle_state_3fg_drag(struct tp_dispatch *tp, uint64_t time)
 {
-	if (!(tp->queued & TOUCHPAD_EVENT_MOTION))
-		return;
-
-	struct device_float_coords raw = tp_get_average_touches_delta(tp);
-	struct normalized_coords delta = tp_filter_motion(tp, &raw, time);
-
-	if (!normalized_is_zero(delta) || !device_float_is_zero(raw)) {
-		if (tp->queued & TOUCHPAD_EVENT_MOTION)
-			tp_gesture_post_pointer_motion(tp, time);
-	}
+	if (tp->queued & TOUCHPAD_EVENT_MOTION)
+		tp_gesture_post_pointer_motion(tp, time);
 }
 
 static void
@@ -1739,11 +1728,10 @@ tp_gesture_handle_state_3fg_drag_release
 }
 
 static void
-tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time,
-			bool ignore_motion)
+tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time, bool ignore_motion)
 {
 	enum tp_gesture_state oldstate = tp->gesture.state;
-	enum tp_gesture_state transitions[16] = {0};
+	enum tp_gesture_state transitions[16] = { 0 };
 	enum tp_gesture_state *transition_state = transitions;
 
 #define REMEMBER_TRANSITION(_ts, _state) { \
@@ -1815,20 +1803,19 @@ tp_gesture_handle_state(struct tp_dispat
 #undef REMEMBER_TRANSITION
 
 	if (oldstate != tp->gesture.state) {
-		char buf[1024] = {0};
-		size_t remaining = sizeof(buf);
-		size_t slen = 0;
-		for (enum tp_gesture_state *s = transitions + 1; s < transition_state; s++) {
-			int n = snprintf(&buf[slen], remaining, " → %s", gesture_state_to_str(*s));
-			slen += n;
-			remaining -= n;
-		}
+		_autostrvfree_ char **states = NULL;
+		states = strv_append_strdup(states, gesture_state_to_str(oldstate));
+		for (enum tp_gesture_state *s = transitions + 1; s < transition_state;
+		     s++) {
+			states = strv_append_strdup(states, gesture_state_to_str(*s));
+		}
+		states = strv_append_strdup(states,
+					    gesture_state_to_str(tp->gesture.state));
+		_autofree_ char *str = strv_join(states, " → ");
 		evdev_log_debug(tp->device,
-				"gesture: [%dfg] state %s%s → %s\n",
+				"gesture: [%dfg] state %s\n",
 				tp->gesture.finger_count,
-				gesture_state_to_str(oldstate),
-				buf,
-				gesture_state_to_str(tp->gesture.state));
+				str);
 	}
 }
 
@@ -1852,8 +1839,7 @@ tp_gesture_thumb_moved(struct tp_dispatc
 }
 
 void
-tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time,
-		       bool ignore_motion)
+tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time, bool ignore_motion)
 {
 	if (tp->gesture.finger_count == 0)
 		return;
@@ -1862,9 +1848,8 @@ tp_gesture_post_events(struct tp_dispatc
 	 * physical button is down, don't allow gestures unless the button
 	 * is held down by a *thumb*, specifically.
 	 */
-	if (tp_tap_dragging(tp) ||
-	    (tp->buttons.is_clickpad && tp->buttons.state &&
-	     tp->thumb.state == THUMB_STATE_FINGER)) {
+	if (tp_tap_dragging(tp) || (tp->buttons.is_clickpad && tp->buttons.state &&
+				    tp->thumb.state == THUMB_STATE_FINGER)) {
 		if (tp->gesture.state != GESTURE_STATE_POINTER_MOTION) {
 			tp_gesture_cancel(tp, time);
 			tp_gesture_handle_event(tp,
@@ -1896,9 +1881,7 @@ tp_gesture_stop_twofinger_scroll(struct
 	if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG)
 		return;
 
-	evdev_stop_scroll(tp->device,
-			  time,
-			  LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
+	evdev_stop_scroll(tp->device, time, LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
 }
 
 static void
@@ -2029,7 +2012,7 @@ tp_gesture_update_finger_state(struct tp
 			tp_gesture_stop(tp, time);
 			tp->gesture.finger_count = 0;
 			tp->gesture.finger_count_pending = 0;
-		/* Immediately switch to new mode to avoid initial latency */
+			/* Immediately switch to new mode to avoid initial latency */
 		} else if (!tp_gesture_debounce_finger_changes(tp)) {
 			tp->gesture.finger_count = active_touches;
 			tp->gesture.finger_count_pending = 0;
@@ -2038,18 +2021,16 @@ tp_gesture_update_finger_state(struct tp
 			 */
 			if (tp->gesture.state == GESTURE_STATE_UNKNOWN ||
 			    tp->gesture.state == GESTURE_STATE_POINTER_MOTION) {
-				tp_gesture_handle_event(tp,
-							GESTURE_EVENT_RESET,
-							time);
+				tp_gesture_handle_event(tp, GESTURE_EVENT_RESET, time);
 			}
-		/* Else debounce finger changes */
+			/* Else debounce finger changes */
 		} else if (active_touches != tp->gesture.finger_count_pending) {
 			tp->gesture.finger_count_pending = active_touches;
 			libinput_timer_set(&tp->gesture.finger_count_switch_timer,
 					   time + DEFAULT_GESTURE_SWITCH_TIMEOUT);
 		}
 	} else {
-		 tp->gesture.finger_count_pending = 0;
+		tp->gesture.finger_count_pending = 0;
 	}
 }
 
@@ -2080,8 +2061,8 @@ tp_gesture_is_hold_enabled(struct libinp
 	struct evdev_dispatch *dispatch = evdev_device(device)->dispatch;
 	struct tp_dispatch *tp = tp_dispatch(dispatch);
 
-	return tp->gesture.hold_enabled ? LIBINPUT_CONFIG_HOLD_ENABLED :
-					  LIBINPUT_CONFIG_HOLD_DISABLED;
+	return tp->gesture.hold_enabled ? LIBINPUT_CONFIG_HOLD_ENABLED
+					: LIBINPUT_CONFIG_HOLD_DISABLED;
 }
 
 static enum libinput_config_hold_state
@@ -2090,9 +2071,8 @@ tp_gesture_get_hold_default(struct libin
 	struct evdev_dispatch *dispatch = evdev_device(device)->dispatch;
 	struct tp_dispatch *tp = tp_dispatch(dispatch);
 
-	return tp_gesture_are_gestures_enabled(tp) ?
-	       LIBINPUT_CONFIG_HOLD_ENABLED :
-	       LIBINPUT_CONFIG_HOLD_DISABLED;
+	return tp_gesture_are_gestures_enabled(tp) ? LIBINPUT_CONFIG_HOLD_ENABLED
+						   : LIBINPUT_CONFIG_HOLD_DISABLED;
 }
 
 static int
@@ -2180,7 +2160,9 @@ tp_3fg_drag_apply_config(struct evdev_de
 
 	tp->drag_3fg.nfingers = tp->drag_3fg.want_nfingers;
 
-	evdev_log_debug(device, "touchpad-3fg-drag: drag is now for %zd fingers\n", tp->drag_3fg.nfingers);
+	evdev_log_debug(device,
+			"touchpad-3fg-drag: drag is now for %zd fingers\n",
+			tp->drag_3fg.nfingers);
 }
 
 void
@@ -2227,7 +2209,8 @@ tp_init_gesture(struct tp_dispatch *tp)
 	libinput_timer_init(&tp->gesture.finger_count_switch_timer,
 			    tp_libinput_context(tp),
 			    timer_name,
-			    tp_gesture_finger_count_switch_timeout, tp);
+			    tp_gesture_finger_count_switch_timeout,
+			    tp);
 
 	snprintf(timer_name,
 		 sizeof(timer_name),
@@ -2236,7 +2219,8 @@ tp_init_gesture(struct tp_dispatch *tp)
 	libinput_timer_init(&tp->gesture.hold_timer,
 			    tp_libinput_context(tp),
 			    timer_name,
-			    tp_gesture_hold_timeout, tp);
+			    tp_gesture_hold_timeout,
+			    tp);
 	snprintf(timer_name,
 		 sizeof(timer_name),
 		 "%s drag_3fg",
@@ -2244,7 +2228,8 @@ tp_init_gesture(struct tp_dispatch *tp)
 	libinput_timer_init(&tp->gesture.drag_3fg_timer,
 			    tp_libinput_context(tp),
 			    timer_name,
-			    tp_gesture_3fg_drag_timeout, tp);
+			    tp_gesture_3fg_drag_timeout,
+			    tp);
 }
 
 void
diff -pruN 1.28.1-1/src/evdev-mt-touchpad-tap.c 1.30.0-1/src/evdev-mt-touchpad-tap.c
--- 1.28.1-1/src/evdev-mt-touchpad-tap.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-mt-touchpad-tap.c	2025-11-25 03:40:43.000000000 +0000
@@ -55,10 +55,10 @@ enum tap_event {
  * Any changes in this file must be represented in the diagram.
  */
 
-static inline const char*
+static inline const char *
 tap_state_to_str(enum tp_tap_state state)
 {
-	switch(state) {
+	switch (state) {
 	CASE_RETURN_STRING(TAP_STATE_IDLE);
 	CASE_RETURN_STRING(TAP_STATE_HOLD);
 	CASE_RETURN_STRING(TAP_STATE_TOUCH);
@@ -92,10 +92,10 @@ tap_state_to_str(enum tp_tap_state state
 	return NULL;
 }
 
-static inline const char*
+static inline const char *
 tap_event_to_str(enum tap_event event)
 {
-	switch(event) {
+	switch (event) {
 	CASE_RETURN_STRING(TAP_EVENT_TOUCH);
 	CASE_RETURN_STRING(TAP_EVENT_MOTION);
 	CASE_RETURN_STRING(TAP_EVENT_RELEASE);
@@ -116,7 +116,6 @@ log_tap_bug(struct tp_dispatch *tp, stru
 			       t->index,
 			       tap_event_to_str(event),
 			       tap_state_to_str(tp->tap.state));
-
 }
 
 static void
@@ -126,9 +125,9 @@ tp_tap_notify(struct tp_dispatch *tp,
 	      enum libinput_button_state state)
 {
 	int32_t button;
-	int32_t button_map[2][3] = {
-		{ BTN_LEFT, BTN_RIGHT, BTN_MIDDLE },
-		{ BTN_LEFT, BTN_MIDDLE, BTN_RIGHT },
+	uint32_t button_map[2][3] = {
+		{ EVDEV_BTN_LEFT, EVDEV_BTN_RIGHT, EVDEV_BTN_MIDDLE },
+		{ EVDEV_BTN_LEFT, EVDEV_BTN_MIDDLE, EVDEV_BTN_RIGHT },
 	};
 
 	assert(tp->tap.map < ARRAY_LENGTH(button_map));
@@ -147,7 +146,7 @@ tp_tap_notify(struct tp_dispatch *tp,
 
 	evdev_pointer_notify_button(tp->device,
 				    time,
-				    button,
+				    evdev_usage_from_uint32_t(button),
 				    state);
 }
 
@@ -158,20 +157,18 @@ tp_tap_set_timer(struct tp_dispatch *tp,
 }
 
 static void
-tp_tap_set_drag_timer(struct tp_dispatch *tp, uint64_t time,
-		      int nfingers_tapped)
+tp_tap_set_drag_timer(struct tp_dispatch *tp, uint64_t time, int nfingers_tapped)
 {
-	libinput_timer_set(&tp->tap.timer,
-			   time + DEFAULT_DRAG_TIMEOUT_PERIOD_BASE +
-			   (nfingers_tapped *
-			    DEFAULT_DRAG_TIMEOUT_PERIOD_PERFINGER));
+	libinput_timer_set(
+		&tp->tap.timer,
+		time + DEFAULT_DRAG_TIMEOUT_PERIOD_BASE +
+			(nfingers_tapped * DEFAULT_DRAG_TIMEOUT_PERIOD_PERFINGER));
 }
 
 static void
 tp_tap_set_draglock_timer(struct tp_dispatch *tp, uint64_t time)
 {
-	libinput_timer_set(&tp->tap.timer,
-			   time + DEFAULT_DRAGLOCK_TIMEOUT_PERIOD);
+	libinput_timer_set(&tp->tap.timer, time + DEFAULT_DRAGLOCK_TIMEOUT_PERIOD);
 }
 
 static void
@@ -191,7 +188,8 @@ tp_tap_move_to_dead(struct tp_dispatch *
 static void
 tp_tap_idle_handle_event(struct tp_dispatch *tp,
 			 struct tp_touch *t,
-			 enum tap_event event, uint64_t time)
+			 enum tap_event event,
+			 uint64_t time)
 {
 	switch (event) {
 	case TAP_EVENT_TOUCH:
@@ -223,7 +221,8 @@ tp_tap_idle_handle_event(struct tp_dispa
 static void
 tp_tap_touch_handle_event(struct tp_dispatch *tp,
 			  struct tp_touch *t,
-			  enum tap_event event, uint64_t time)
+			  enum tap_event event,
+			  uint64_t time)
 {
 
 	switch (event) {
@@ -242,10 +241,7 @@ tp_tap_touch_handle_event(struct tp_disp
 			tp->tap.saved_release_time = time;
 			tp_tap_set_drag_timer(tp, time, 1);
 		} else {
-			tp_tap_notify(tp,
-				      time,
-				      1,
-				      LIBINPUT_BUTTON_STATE_RELEASED);
+			tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
 			tp->tap.state = TAP_STATE_IDLE;
 		}
 		break;
@@ -279,7 +275,8 @@ tp_tap_touch_handle_event(struct tp_disp
 static void
 tp_tap_hold_handle_event(struct tp_dispatch *tp,
 			 struct tp_touch *t,
-			 enum tap_event event, uint64_t time)
+			 enum tap_event event,
+			 uint64_t time)
 {
 
 	switch (event) {
@@ -316,7 +313,8 @@ tp_tap_hold_handle_event(struct tp_dispa
 static void
 tp_tap_tapped_handle_event(struct tp_dispatch *tp,
 			   struct tp_touch *t,
-			   enum tap_event event, uint64_t time,
+			   enum tap_event event,
+			   uint64_t time,
 			   int nfingers_tapped)
 {
 	switch (event) {
@@ -364,7 +362,8 @@ tp_tap_tapped_handle_event(struct tp_dis
 static void
 tp_tap_touch2_handle_event(struct tp_dispatch *tp,
 			   struct tp_touch *t,
-			   enum tap_event event, uint64_t time)
+			   enum tap_event event,
+			   uint64_t time)
 {
 
 	switch (event) {
@@ -401,7 +400,8 @@ tp_tap_touch2_handle_event(struct tp_dis
 static void
 tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp,
 				struct tp_touch *t,
-				enum tap_event event, uint64_t time)
+				enum tap_event event,
+				uint64_t time)
 {
 
 	switch (event) {
@@ -435,7 +435,8 @@ tp_tap_touch2_hold_handle_event(struct t
 static void
 tp_tap_touch2_release_handle_event(struct tp_dispatch *tp,
 				   struct tp_touch *t,
-				   enum tap_event event, uint64_t time)
+				   enum tap_event event,
+				   uint64_t time)
 {
 
 	switch (event) {
@@ -503,7 +504,8 @@ tp_tap_touch2_release_handle_event(struc
 static void
 tp_tap_touch3_handle_event(struct tp_dispatch *tp,
 			   struct tp_touch *t,
-			   enum tap_event event, uint64_t time)
+			   enum tap_event event,
+			   uint64_t time)
 {
 
 	switch (event) {
@@ -540,7 +542,8 @@ tp_tap_touch3_handle_event(struct tp_dis
 static void
 tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp,
 				struct tp_touch *t,
-				enum tap_event event, uint64_t time)
+				enum tap_event event,
+				uint64_t time)
 {
 
 	switch (event) {
@@ -572,7 +575,8 @@ tp_tap_touch3_hold_handle_event(struct t
 static void
 tp_tap_touch3_release_handle_event(struct tp_dispatch *tp,
 				   struct tp_touch *t,
-				   enum tap_event event, uint64_t time)
+				   enum tap_event event,
+				   uint64_t time)
 {
 
 	switch (event) {
@@ -639,7 +643,8 @@ tp_tap_touch3_release_handle_event(struc
 static void
 tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp,
 				    struct tp_touch *t,
-				    enum tap_event event, uint64_t time)
+				    enum tap_event event,
+				    uint64_t time)
 {
 
 	switch (event) {
@@ -736,7 +741,8 @@ tp_tap_touch3_release2_handle_event(stru
 static void
 tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp,
 					  struct tp_touch *t,
-					  enum tap_event event, uint64_t time,
+					  enum tap_event event,
+					  uint64_t time,
 					  int nfingers_tapped)
 {
 	switch (event) {
@@ -801,7 +807,8 @@ tp_tap_dragging_or_doubletap_handle_even
 static void
 tp_tap_dragging_handle_event(struct tp_dispatch *tp,
 			     struct tp_touch *t,
-			     enum tap_event event, uint64_t time,
+			     enum tap_event event,
+			     uint64_t time,
 			     int nfingers_tapped)
 {
 
@@ -825,7 +832,8 @@ tp_tap_dragging_handle_event(struct tp_d
 			};
 			assert(nfingers_tapped >= 1 && nfingers_tapped <= 3);
 			tp->tap.state = dest[nfingers_tapped - 1];
-			if (tp->tap.drag_lock == LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT)
+			if (tp->tap.drag_lock ==
+			    LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT)
 				tp_tap_set_draglock_timer(tp, time);
 		} else {
 			tp_tap_notify(tp,
@@ -863,7 +871,8 @@ tp_tap_dragging_handle_event(struct tp_d
 static void
 tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp,
 				  struct tp_touch *t,
-				  enum tap_event event, uint64_t time,
+				  enum tap_event event,
+				  uint64_t time,
 				  int nfingers_tapped)
 {
 
@@ -909,7 +918,8 @@ tp_tap_dragging_wait_handle_event(struct
 static void
 tp_tap_dragging_tap_handle_event(struct tp_dispatch *tp,
 				 struct tp_touch *t,
-				 enum tap_event event, uint64_t time,
+				 enum tap_event event,
+				 uint64_t time,
 				 int nfingers_tapped)
 {
 
@@ -968,7 +978,8 @@ tp_tap_dragging_tap_handle_event(struct
 static void
 tp_tap_dragging2_handle_event(struct tp_dispatch *tp,
 			      struct tp_touch *t,
-			      enum tap_event event, uint64_t time,
+			      enum tap_event event,
+			      uint64_t time,
 			      int nfingers_tapped)
 {
 
@@ -1055,7 +1066,7 @@ tp_tap_handle_event(struct tp_dispatch *
 
 	current = tp->tap.state;
 
-	switch(tp->tap.state) {
+	switch (tp->tap.state) {
 	case TAP_STATE_IDLE:
 		tp_tap_idle_handle_event(tp, t, event, time);
 		break;
@@ -1096,16 +1107,13 @@ tp_tap_handle_event(struct tp_dispatch *
 		tp_tap_touch3_release2_handle_event(tp, t, event, time);
 		break;
 	case TAP_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP:
-		tp_tap_dragging_or_doubletap_handle_event(tp, t, event, time,
-							  1);
+		tp_tap_dragging_or_doubletap_handle_event(tp, t, event, time, 1);
 		break;
 	case TAP_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP:
-		tp_tap_dragging_or_doubletap_handle_event(tp, t, event, time,
-							  2);
+		tp_tap_dragging_or_doubletap_handle_event(tp, t, event, time, 2);
 		break;
 	case TAP_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP:
-		tp_tap_dragging_or_doubletap_handle_event(tp, t, event, time,
-							  3);
+		tp_tap_dragging_or_doubletap_handle_event(tp, t, event, time, 3);
 		break;
 	case TAP_STATE_1FGTAP_DRAGGING:
 		tp_tap_dragging_handle_event(tp, t, event, time, 1);
@@ -1153,17 +1161,16 @@ tp_tap_handle_event(struct tp_dispatch *
 
 	if (current != tp->tap.state)
 		evdev_log_debug(tp->device,
-			  "tap: touch %d (%s), tap state %s → %s → %s\n",
-			  t ? (int)t->index : -1,
-			  t ? touch_state_to_str(t->state) : "",
-			  tap_state_to_str(current),
-			  tap_event_to_str(event),
-			  tap_state_to_str(tp->tap.state));
+				"tap: touch %d (%s), tap state %s → %s → %s\n",
+				t ? (int)t->index : -1,
+				t ? touch_state_to_str(t->state) : "",
+				tap_state_to_str(current),
+				tap_event_to_str(event),
+				tap_state_to_str(tp->tap.state));
 }
 
 static bool
-tp_tap_exceeds_motion_threshold(struct tp_dispatch *tp,
-				struct tp_touch *t)
+tp_tap_exceeds_motion_threshold(struct tp_dispatch *tp, struct tp_touch *t)
 {
 	struct phys_coords mm =
 		tp_phys_delta(tp, device_delta(t->point, t->tap.initial));
@@ -1217,8 +1224,7 @@ tp_tap_handle_state(struct tp_dispatch *
 		if (!t->dirty || t->state == TOUCH_NONE)
 			continue;
 
-		if (tp->buttons.is_clickpad &&
-		    tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS)
+		if (tp->buttons.is_clickpad && tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS)
 			t->tap.state = TAP_TOUCH_STATE_DEAD;
 
 		/* If a touch was considered thumb for tapping once, we
@@ -1232,10 +1238,7 @@ tp_tap_handle_state(struct tp_dispatch *
 		 */
 		if (t->tap.is_palm) {
 			if (t->state == TOUCH_END)
-				tp_tap_handle_event(tp,
-						    t,
-						    TAP_EVENT_PALM_UP,
-						    time);
+				tp_tap_handle_event(tp, t, TAP_EVENT_PALM_UP, time);
 			continue;
 		}
 
@@ -1271,8 +1274,7 @@ tp_tap_handle_state(struct tp_dispatch *
 				tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time);
 			}
 			t->tap.state = TAP_TOUCH_STATE_IDLE;
-		} else if (tp->tap.state != TAP_STATE_IDLE &&
-			   tp_thumb_ignored(tp, t)) {
+		} else if (tp->tap.state != TAP_STATE_IDLE && tp_thumb_ignored(tp, t)) {
 			tp_tap_handle_event(tp, t, TAP_EVENT_THUMB, time);
 		} else if (tp->tap.state != TAP_STATE_IDLE &&
 			   tp_tap_exceeds_motion_threshold(tp, t)) {
@@ -1313,7 +1315,6 @@ tp_tap_handle_state(struct tp_dispatch *
 
 	default:
 		break;
-
 	}
 
 	assert(tp->tap.nfingers_down <= tp->nfingers_down);
@@ -1348,8 +1349,7 @@ tp_tap_handle_timeout(uint64_t time, voi
 	tp_tap_handle_event(tp, NULL, TAP_EVENT_TIMEOUT, time);
 
 	tp_for_each_touch(tp, t) {
-		if (t->state == TOUCH_NONE ||
-		    t->tap.state == TAP_TOUCH_STATE_IDLE)
+		if (t->state == TOUCH_NONE || t->tap.state == TAP_TOUCH_STATE_IDLE)
 			continue;
 
 		t->tap.state = TAP_TOUCH_STATE_DEAD;
@@ -1357,7 +1357,10 @@ tp_tap_handle_timeout(uint64_t time, voi
 }
 
 static void
-tp_tap_enabled_update(struct tp_dispatch *tp, bool suspended, bool enabled, uint64_t time)
+tp_tap_enabled_update(struct tp_dispatch *tp,
+		      bool suspended,
+		      bool enabled,
+		      uint64_t time)
 {
 	bool was_enabled = tp_tap_enabled(tp);
 
@@ -1402,8 +1405,9 @@ tp_tap_config_set_enabled(struct libinpu
 	struct evdev_dispatch *dispatch = evdev_device(device)->dispatch;
 	struct tp_dispatch *tp = tp_dispatch(dispatch);
 
-	tp_tap_enabled_update(tp, tp->tap.suspended,
-			      (enabled == LIBINPUT_CONFIG_TAP_ENABLED),
+	tp_tap_enabled_update(tp,
+			      tp->tap.suspended,
+			      enabled == LIBINPUT_CONFIG_TAP_ENABLED,
 			      libinput_now(device->seat->libinput));
 
 	return LIBINPUT_CONFIG_STATUS_SUCCESS;
@@ -1415,8 +1419,8 @@ tp_tap_config_is_enabled(struct libinput
 	struct evdev_dispatch *dispatch = evdev_device(device)->dispatch;
 	struct tp_dispatch *tp = tp_dispatch(dispatch);
 
-	return tp->tap.enabled ? LIBINPUT_CONFIG_TAP_ENABLED :
-				 LIBINPUT_CONFIG_TAP_DISABLED;
+	return tp->tap.enabled ? LIBINPUT_CONFIG_TAP_ENABLED
+			       : LIBINPUT_CONFIG_TAP_DISABLED;
 }
 
 static enum libinput_config_tap_state
@@ -1562,10 +1566,12 @@ tp_init_tap(struct tp_dispatch *tp)
 	tp->tap.config.get_default_map = tp_tap_config_get_default_map;
 	tp->tap.config.set_drag_enabled = tp_tap_config_set_drag_enabled;
 	tp->tap.config.get_drag_enabled = tp_tap_config_get_drag_enabled;
-	tp->tap.config.get_default_drag_enabled = tp_tap_config_get_default_drag_enabled;
+	tp->tap.config.get_default_drag_enabled =
+		tp_tap_config_get_default_drag_enabled;
 	tp->tap.config.set_draglock_enabled = tp_tap_config_set_draglock_enabled;
 	tp->tap.config.get_draglock_enabled = tp_tap_config_get_draglock_enabled;
-	tp->tap.config.get_default_draglock_enabled = tp_tap_config_get_default_draglock_enabled;
+	tp->tap.config.get_default_draglock_enabled =
+		tp_tap_config_get_default_draglock_enabled;
 	tp->device->base.config.tap = &tp->tap.config;
 
 	tp->tap.state = TAP_STATE_IDLE;
@@ -1582,7 +1588,8 @@ tp_init_tap(struct tp_dispatch *tp)
 	libinput_timer_init(&tp->tap.timer,
 			    tp_libinput_context(tp),
 			    timer_name,
-			    tp_tap_handle_timeout, tp);
+			    tp_tap_handle_timeout,
+			    tp);
 }
 
 void
diff -pruN 1.28.1-1/src/evdev-mt-touchpad-thumb.c 1.30.0-1/src/evdev-mt-touchpad-thumb.c
--- 1.28.1-1/src/evdev-mt-touchpad-thumb.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-mt-touchpad-thumb.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,6 +23,7 @@
  */
 
 #include "config.h"
+
 #include "evdev-mt-touchpad.h"
 
 /* distance between fingers to assume it is not a scroll */
@@ -30,10 +31,10 @@
 #define SCROLL_MM_Y 25
 #define THUMB_TIMEOUT ms2us(100)
 
-static inline const char*
+static inline const char *
 thumb_state_to_str(enum tp_thumb_state state)
 {
-	switch(state){
+	switch (state) {
 	CASE_RETURN_STRING(THUMB_STATE_FINGER);
 	CASE_RETURN_STRING(THUMB_STATE_JAILED);
 	CASE_RETURN_STRING(THUMB_STATE_PINCH);
@@ -82,28 +83,23 @@ tp_thumb_lift(struct tp_dispatch *tp)
 }
 
 static bool
-tp_thumb_in_exclusion_area(const struct tp_dispatch *tp,
-			   const struct tp_touch *t)
+tp_thumb_in_exclusion_area(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
 	return (t->point.y > tp->thumb.lower_thumb_line &&
 		tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE);
-
 }
 
 static bool
-tp_thumb_detect_pressure_size(const struct tp_dispatch *tp,
-			      const struct tp_touch *t)
+tp_thumb_detect_pressure_size(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
 	bool is_thumb = false;
 
-	if (tp->thumb.use_pressure &&
-	    t->pressure > tp->thumb.pressure_threshold &&
+	if (tp->thumb.use_pressure && t->pressure > tp->thumb.pressure_threshold &&
 	    tp_thumb_in_exclusion_area(tp, t)) {
 		is_thumb = true;
 	}
 
-	if (tp->thumb.use_size &&
-	    (t->major > tp->thumb.size_threshold) &&
+	if (tp->thumb.use_size && (t->major > tp->thumb.size_threshold) &&
 	    (t->minor < (tp->thumb.size_threshold * 0.6))) {
 		is_thumb = true;
 	}
@@ -119,7 +115,7 @@ tp_thumb_needs_jail(const struct tp_disp
 		return false;
 
 	if (!tp_thumb_in_exclusion_area(tp, t) &&
-           (tp->thumb.use_size || tp->thumb.use_pressure) &&
+	    (tp->thumb.use_size || tp->thumb.use_pressure) &&
 	    !tp_thumb_detect_pressure_size(tp, t))
 		return false;
 
@@ -132,8 +128,7 @@ tp_thumb_needs_jail(const struct tp_disp
 bool
 tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
-	return (tp->thumb.detect_thumbs &&
-		tp->thumb.index == t->index &&
+	return (tp->thumb.detect_thumbs && tp->thumb.index == t->index &&
 		(tp->thumb.state == THUMB_STATE_JAILED ||
 		 tp->thumb.state == THUMB_STATE_PINCH ||
 		 tp->thumb.state == THUMB_STATE_SUPPRESSED ||
@@ -142,22 +137,18 @@ tp_thumb_ignored(const struct tp_dispatc
 }
 
 bool
-tp_thumb_ignored_for_tap(const struct tp_dispatch *tp,
-			 const struct tp_touch *t)
+tp_thumb_ignored_for_tap(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
-	return (tp->thumb.detect_thumbs &&
-		tp->thumb.index == t->index &&
+	return (tp->thumb.detect_thumbs && tp->thumb.index == t->index &&
 		(tp->thumb.state == THUMB_STATE_PINCH ||
 		 tp->thumb.state == THUMB_STATE_SUPPRESSED ||
 		 tp->thumb.state == THUMB_STATE_DEAD));
 }
 
 bool
-tp_thumb_ignored_for_gesture(const struct tp_dispatch *tp,
-			     const struct tp_touch *t)
+tp_thumb_ignored_for_gesture(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
-	return (tp->thumb.detect_thumbs &&
-		tp->thumb.index == t->index &&
+	return (tp->thumb.detect_thumbs && tp->thumb.index == t->index &&
 		(tp->thumb.state == THUMB_STATE_JAILED ||
 		 tp->thumb.state == THUMB_STATE_SUPPRESSED ||
 		 tp->thumb.state == THUMB_STATE_REVIVED_JAILED ||
@@ -167,10 +158,9 @@ tp_thumb_ignored_for_gesture(const struc
 void
 tp_thumb_suppress(struct tp_dispatch *tp, struct tp_touch *t)
 {
-	if(tp->thumb.state == THUMB_STATE_FINGER ||
-	   tp->thumb.state == THUMB_STATE_JAILED ||
-	   tp->thumb.state == THUMB_STATE_PINCH ||
-	   tp->thumb.index != t->index) {
+	if (tp->thumb.state == THUMB_STATE_FINGER ||
+	    tp->thumb.state == THUMB_STATE_JAILED ||
+	    tp->thumb.state == THUMB_STATE_PINCH || tp->thumb.index != t->index) {
 		tp_thumb_set_state(tp, t, THUMB_STATE_SUPPRESSED);
 		return;
 	}
@@ -181,9 +171,8 @@ tp_thumb_suppress(struct tp_dispatch *tp
 static void
 tp_thumb_pinch(struct tp_dispatch *tp, struct tp_touch *t)
 {
-	if(tp->thumb.state == THUMB_STATE_FINGER ||
-	   tp->thumb.state == THUMB_STATE_JAILED ||
-	   tp->thumb.index != t->index)
+	if (tp->thumb.state == THUMB_STATE_FINGER ||
+	    tp->thumb.state == THUMB_STATE_JAILED || tp->thumb.index != t->index)
 		tp_thumb_set_state(tp, t, THUMB_STATE_PINCH);
 	else if (tp->thumb.state != THUMB_STATE_PINCH)
 		tp_thumb_suppress(tp, t);
@@ -192,21 +181,19 @@ tp_thumb_pinch(struct tp_dispatch *tp, s
 static void
 tp_thumb_revive(struct tp_dispatch *tp, struct tp_touch *t)
 {
-	if((tp->thumb.state != THUMB_STATE_SUPPRESSED &&
-	    tp->thumb.state != THUMB_STATE_PINCH) ||
-	   (tp->thumb.index != t->index))
+	if ((tp->thumb.state != THUMB_STATE_SUPPRESSED &&
+	     tp->thumb.state != THUMB_STATE_PINCH) ||
+	    (tp->thumb.index != t->index))
 		return;
 
-	if(tp_thumb_needs_jail(tp, t))
+	if (tp_thumb_needs_jail(tp, t))
 		tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED_JAILED);
 	else
 		tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED);
 }
 
 void
-tp_thumb_update_touch(struct tp_dispatch *tp,
-		      struct tp_touch *t,
-		      uint64_t time)
+tp_thumb_update_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
 	if (!tp->thumb.detect_thumbs)
 		return;
@@ -214,11 +201,10 @@ tp_thumb_update_touch(struct tp_dispatch
 	/* Once any active touch exceeds the speed threshold, don't
 	 * try to detect pinches until all touches lift.
 	 */
-	if (t->speed.exceeded_count >= 10 &&
-	    tp->thumb.pinch_eligible &&
+	if (t->speed.exceeded_count >= 10 && tp->thumb.pinch_eligible &&
 	    tp->gesture.state == GESTURE_STATE_NONE) {
 		tp->thumb.pinch_eligible = false;
-		if(tp->thumb.state == THUMB_STATE_PINCH) {
+		if (tp->thumb.state == THUMB_STATE_PINCH) {
 			struct tp_touch *thumb;
 			tp_for_each_touch(tp, thumb) {
 				if (thumb->index != tp->thumb.index)
@@ -258,8 +244,7 @@ tp_thumb_update_touch(struct tp_dispatch
 	/* If a touch breaks the speed threshold, or leaves the thumb area
 	 * (upper or lower, depending on HW detection), it "escapes" jail.
 	 */
-	if (tp->thumb.state == THUMB_STATE_JAILED &&
-	    !(tp_thumb_needs_jail(tp, t)))
+	if (tp->thumb.state == THUMB_STATE_JAILED && !(tp_thumb_needs_jail(tp, t)))
 		tp_thumb_set_state(tp, t, THUMB_STATE_FINGER);
 	if (tp->thumb.state == THUMB_STATE_REVIVED_JAILED &&
 	    !(tp_thumb_needs_jail(tp, t)))
@@ -270,10 +255,7 @@ void
 tp_thumb_update_multifinger(struct tp_dispatch *tp)
 {
 	struct tp_touch *t;
-	struct tp_touch *first = NULL,
-			*second = NULL,
-			*newest = NULL,
-			*oldest = NULL;
+	struct tp_touch *first = NULL, *second = NULL, *newest = NULL, *oldest = NULL;
 	struct device_coords distance;
 	struct phys_coords mm;
 
@@ -283,15 +265,14 @@ tp_thumb_update_multifinger(struct tp_di
 	 * count overall, and the newest and oldest touches.
 	 */
 	tp_for_each_touch(tp, t) {
-		if (t->state == TOUCH_NONE ||
-		    t->state == TOUCH_HOVERING)
+		if (t->state == TOUCH_NONE || t->state == TOUCH_HOVERING)
 			continue;
 
 		if (t->state == TOUCH_BEGIN)
 			newest = t;
 
-		speed_exceeded_count = max(speed_exceeded_count,
-		                           t->speed.exceeded_count);
+		speed_exceeded_count =
+			max(speed_exceeded_count, t->speed.exceeded_count);
 
 		if (!oldest || t->initial_time < oldest->initial_time) {
 			oldest = t;
@@ -308,7 +289,7 @@ tp_thumb_update_multifinger(struct tp_di
 			continue;
 		}
 
-		if (!second || t->point.y > second->point.y ) {
+		if (!second || t->point.y > second->point.y) {
 			second = t;
 		}
 	}
@@ -325,9 +306,7 @@ tp_thumb_update_multifinger(struct tp_di
 	 * 2-finger scroll. Also account for a thumb dropping onto the touchpad
 	 * while scrolling or swiping.
 	 */
-	if (newest &&
-	    tp->thumb.state == THUMB_STATE_FINGER &&
-	    tp->nfingers_down >= 2 &&
+	if (newest && tp->thumb.state == THUMB_STATE_FINGER && tp->nfingers_down >= 2 &&
 	    speed_exceeded_count > 5 &&
 	    (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG ||
 	     (mm.x > SCROLL_MM_X || mm.y > SCROLL_MM_Y))) {
@@ -358,8 +337,7 @@ tp_thumb_update_multifinger(struct tp_di
 	 * the behavior of the other touches.)
 	 */
 
-	if (newest &&
-	    (newest->initial_time - oldest->initial_time) < THUMB_TIMEOUT &&
+	if (newest && (newest->initial_time - oldest->initial_time) < THUMB_TIMEOUT &&
 	    first->point.y < tp->thumb.lower_thumb_line) {
 		tp_thumb_lift(tp);
 		return;
@@ -368,7 +346,7 @@ tp_thumb_update_multifinger(struct tp_di
 	/* If we're past the THUMB_TIMEOUT, and the touches are relatively far
 	 * apart, then the new touch is unlikely to be a tap or clickfinger.
 	 * Proceed with pre-1.14.901 thumb detection.
-	*/
+	 */
 
 	if (mm.y > SCROLL_MM_Y) {
 		if (tp->thumb.pinch_eligible)
@@ -388,8 +366,6 @@ tp_init_thumb(struct tp_dispatch *tp)
 	struct device_coords edges;
 	struct phys_coords mm = { 0.0, 0.0 };
 	uint32_t threshold;
-	struct quirks_context *quirks;
-	struct quirks *q;
 
 	tp->thumb.detect_thumbs = false;
 
@@ -418,9 +394,7 @@ tp_init_thumb(struct tp_dispatch *tp)
 	edges = evdev_device_mm_to_units(device, &mm);
 	tp->thumb.lower_thumb_line = edges.y;
 
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
-
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
 	if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_PRESSURE)) {
 		if (quirks_get_uint32(q,
 				      QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD,
@@ -431,9 +405,7 @@ tp_init_thumb(struct tp_dispatch *tp)
 	}
 
 	if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_TOUCH_MAJOR)) {
-		if (quirks_get_uint32(q,
-				      QUIRK_ATTR_THUMB_SIZE_THRESHOLD,
-				      &threshold)) {
+		if (quirks_get_uint32(q, QUIRK_ATTR_THUMB_SIZE_THRESHOLD, &threshold)) {
 			tp->thumb.use_size = true;
 			tp->thumb.size_threshold = threshold;
 		}
@@ -441,15 +413,13 @@ tp_init_thumb(struct tp_dispatch *tp)
 
 	tp_thumb_reset(tp);
 
-	quirks_unref(q);
-
 	evdev_log_debug(device,
 			"thumb: enabled thumb detection (area%s%s)\n",
 			tp->thumb.use_pressure ? ", pressure" : "",
 			tp->thumb.use_size ? ", size" : "");
 }
 
-struct tp_touch*
+struct tp_touch *
 tp_thumb_get_touch(struct tp_dispatch *tp)
 {
 	struct tp_touch *thumb;
diff -pruN 1.28.1-1/src/evdev-mt-touchpad.c 1.30.0-1/src/evdev-mt-touchpad.c
--- 1.28.1-1/src/evdev-mt-touchpad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-mt-touchpad.c	2025-11-25 03:40:43.000000000 +0000
@@ -24,18 +24,20 @@
 #include "config.h"
 
 #include <assert.h>
+#include <limits.h>
 #include <math.h>
 #include <stdbool.h>
-#include <limits.h>
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 #include <libwacom/libwacom.h>
 #endif
 
-#include "quirks.h"
-#include "evdev-mt-touchpad.h"
 #include "util-input-event.h"
 
+#include "evdev-mt-touchpad.h"
+#include "libinput-feature.h"
+#include "quirks.h"
+
 #define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT ms2us(300)
 #define DEFAULT_TRACKPOINT_EVENT_TIMEOUT ms2us(40)
 #define DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_1 ms2us(200)
@@ -48,12 +50,11 @@ enum notify {
 	DO_NOTIFY,
 };
 
-static inline struct tp_history_point*
+static inline struct tp_history_point *
 tp_motion_history_offset(struct tp_touch *t, int offset)
 {
-	int offset_index =
-		(t->history.index - offset + TOUCHPAD_HISTORY_LENGTH) %
-		TOUCHPAD_HISTORY_LENGTH;
+	int offset_index = (t->history.index - offset + TOUCHPAD_HISTORY_LENGTH) %
+			   TOUCHPAD_HISTORY_LENGTH;
 
 	return &t->history.samples[offset_index];
 }
@@ -72,8 +73,7 @@ tp_filter_motion(struct tp_dispatch *tp,
 	/* Convert to device units with x/y in the same resolution */
 	raw = tp_scale_to_xaxis(tp, *unaccelerated);
 
-	return filter_dispatch(tp->device->pointer.filter,
-			       &raw, tp, time);
+	return filter_dispatch(tp->device->pointer.filter, &raw, tp, time);
 }
 
 struct normalized_coords
@@ -90,8 +90,7 @@ tp_filter_motion_unaccelerated(struct tp
 	/* Convert to device units with x/y in the same resolution */
 	raw = tp_scale_to_xaxis(tp, *unaccelerated);
 
-	return filter_dispatch_constant(tp->device->pointer.filter,
-					&raw, tp, time);
+	return filter_dispatch_constant(tp->device->pointer.filter, &raw, tp, time);
 }
 
 struct normalized_coords
@@ -109,13 +108,14 @@ tp_filter_scroll(struct tp_dispatch *tp,
 	raw = tp_scale_to_xaxis(tp, *unaccelerated);
 
 	return filter_dispatch_scroll(tp->device->pointer.filter,
-				      &raw, tp, time);
+				      &raw,
+				      tp,
+				      time,
+				      FILTER_SCROLL_TYPE_FINGER);
 }
 
 static inline void
-tp_calculate_motion_speed(struct tp_dispatch *tp,
-			  struct tp_touch *t,
-			  uint64_t time)
+tp_calculate_motion_speed(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
 	const struct tp_history_point *last;
 	struct device_coords delta;
@@ -150,8 +150,8 @@ tp_calculate_motion_speed(struct tp_disp
 	mm = evdev_device_unit_delta_to_mm(tp->device, &delta);
 
 	distance = length_in_mm(mm);
-	speed = distance/(time - last->time); /* mm/us */
-	speed *= 1000000; /* mm/s */
+	speed = distance / (time - last->time); /* mm/us */
+	speed *= 1000000;                       /* mm/s */
 
 	t->speed.last_speed = speed;
 }
@@ -181,16 +181,13 @@ tp_motion_history_push(struct tp_touch *
  * This only looks at x changes, y changes are ignored.
  */
 static inline void
-tp_detect_wobbling(struct tp_dispatch *tp,
-		   struct tp_touch *t,
-		   uint64_t time)
+tp_detect_wobbling(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
 	int dx, dy;
 	uint64_t dtime;
-	const struct device_coords* prev_point;
+	const struct device_coords *prev_point;
 
-	if (tp->nfingers_down != 1 ||
-	    tp->nfingers_down != tp->old_nfingers_down)
+	if (tp->nfingers_down != 1 || tp->nfingers_down != tp->old_nfingers_down)
 		return;
 
 	if (tp->hysteresis.enabled || t->history.count == 0)
@@ -214,7 +211,7 @@ tp_detect_wobbling(struct tp_dispatch *t
 	}
 
 	t->hysteresis.x_motion_history >>= 1;
-	if (dx > 0) { /* right move */
+	if (dx > 0) {                          /* right move */
 		static const char r_l_r = 0x5; /* {Right, Left, Right} */
 
 		t->hysteresis.x_motion_history |= bit(2);
@@ -229,8 +226,7 @@ tp_detect_wobbling(struct tp_dispatch *t
 }
 
 static inline void
-tp_motion_hysteresis(struct tp_dispatch *tp,
-		     struct tp_touch *t)
+tp_motion_hysteresis(struct tp_dispatch *tp, struct tp_touch *t)
 {
 	if (!tp->hysteresis.enabled)
 		return;
@@ -265,8 +261,7 @@ tp_get_touch(struct tp_dispatch *tp, uns
 static inline unsigned int
 tp_fake_finger_count(struct tp_dispatch *tp)
 {
-	unsigned int fake_touches =
-		tp->fake_touches & ~(FAKE_FINGER_OVERFLOW|0x1);
+	unsigned int fake_touches = tp->fake_touches & ~(FAKE_FINGER_OVERFLOW | 0x1);
 
 	/* Only one of BTN_TOOL_DOUBLETAP/TRIPLETAP/... may be set at any
 	 * time */
@@ -289,30 +284,28 @@ tp_fake_finger_is_touching(struct tp_dis
 }
 
 static inline void
-tp_fake_finger_set(struct tp_dispatch *tp,
-		   unsigned int code,
-		   bool is_press)
+tp_fake_finger_set(struct tp_dispatch *tp, evdev_usage_t usage, bool is_press)
 {
 	unsigned int shift;
 
-	switch (code) {
-	case BTN_TOUCH:
+	switch (evdev_usage_enum(usage)) {
+	case EVDEV_BTN_TOUCH:
 		if (!is_press)
 			tp->fake_touches &= ~FAKE_FINGER_OVERFLOW;
 		shift = 0;
 		break;
-	case BTN_TOOL_FINGER:
+	case EVDEV_BTN_TOOL_FINGER:
 		shift = 1;
 		break;
-	case BTN_TOOL_DOUBLETAP:
-	case BTN_TOOL_TRIPLETAP:
-	case BTN_TOOL_QUADTAP:
-		shift = code - BTN_TOOL_DOUBLETAP + 2;
+	case EVDEV_BTN_TOOL_DOUBLETAP:
+	case EVDEV_BTN_TOOL_TRIPLETAP:
+	case EVDEV_BTN_TOOL_QUADTAP:
+		shift = evdev_usage_enum(usage) - EVDEV_BTN_TOOL_DOUBLETAP + 2;
 		break;
 	/* when QUINTTAP is released we're either switching to 6 fingers
 	   (flag stays in place until BTN_TOUCH is released) or
 	   one of DOUBLE/TRIPLE/QUADTAP (will clear the flag on press) */
-	case BTN_TOOL_QUINTTAP:
+	case EVDEV_BTN_TOOL_QUINTTAP:
 		if (is_press)
 			tp->fake_touches |= FAKE_FINGER_OVERFLOW;
 		return;
@@ -332,8 +325,7 @@ tp_fake_finger_set(struct tp_dispatch *t
 static inline void
 tp_new_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
-	if (t->state == TOUCH_BEGIN ||
-	    t->state == TOUCH_UPDATE ||
+	if (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE ||
 	    t->state == TOUCH_HOVERING)
 		return;
 
@@ -390,9 +382,7 @@ tp_begin_touch(struct tp_dispatch *tp, s
  * need.
  */
 static inline void
-tp_maybe_end_touch(struct tp_dispatch *tp,
-		   struct tp_touch *t,
-		   uint64_t time)
+tp_maybe_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
 	switch (t->state) {
 	case TOUCH_NONE:
@@ -425,8 +415,7 @@ tp_maybe_end_touch(struct tp_dispatch *t
  * state.
  */
 static inline void
-tp_recover_ended_touch(struct tp_dispatch *tp,
-		       struct tp_touch *t)
+tp_recover_ended_touch(struct tp_dispatch *tp, struct tp_touch *t)
 {
 	t->dirty = true;
 	t->state = TOUCH_UPDATE;
@@ -493,20 +482,20 @@ tp_get_delta(struct tp_touch *t)
 }
 
 static inline int32_t
-rotated(struct tp_dispatch *tp, unsigned int code, int value)
+rotated(struct tp_dispatch *tp, evdev_usage_t usage, int value)
 {
 	const struct input_absinfo *absinfo;
 
 	if (!tp->left_handed.rotate)
 		return value;
 
-	switch (code) {
-	case ABS_X:
-	case ABS_MT_POSITION_X:
+	switch (evdev_usage_enum(usage)) {
+	case EVDEV_ABS_X:
+	case EVDEV_ABS_MT_POSITION_X:
 		absinfo = tp->device->abs.absinfo_x;
 		break;
-	case ABS_Y:
-	case ABS_MT_POSITION_Y:
+	case EVDEV_ABS_Y:
+	case EVDEV_ABS_MT_POSITION_Y:
 		absinfo = tp->device->abs.absinfo_y;
 		break;
 	default:
@@ -516,33 +505,27 @@ rotated(struct tp_dispatch *tp, unsigned
 }
 
 static void
-tp_process_absolute(struct tp_dispatch *tp,
-		    const struct input_event *e,
-		    uint64_t time)
+tp_process_absolute(struct tp_dispatch *tp, const struct evdev_event *e, uint64_t time)
 {
 	struct tp_touch *t = tp_current_touch(tp);
 
-	switch(e->code) {
-	case ABS_MT_POSITION_X:
-		evdev_device_check_abs_axis_range(tp->device,
-						  e->code,
-						  e->value);
-		t->point.x = rotated(tp, e->code, e->value);
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_ABS_MT_POSITION_X:
+		evdev_device_check_abs_axis_range(tp->device, e->usage, e->value);
+		t->point.x = rotated(tp, e->usage, e->value);
 		t->dirty = true;
 		tp->queued |= TOUCHPAD_EVENT_MOTION;
 		break;
-	case ABS_MT_POSITION_Y:
-		evdev_device_check_abs_axis_range(tp->device,
-						  e->code,
-						  e->value);
-		t->point.y = rotated(tp, e->code, e->value);
+	case EVDEV_ABS_MT_POSITION_Y:
+		evdev_device_check_abs_axis_range(tp->device, e->usage, e->value);
+		t->point.y = rotated(tp, e->usage, e->value);
 		t->dirty = true;
 		tp->queued |= TOUCHPAD_EVENT_MOTION;
 		break;
-	case ABS_MT_SLOT:
+	case EVDEV_ABS_MT_SLOT:
 		tp->slot = e->value;
 		break;
-	case ABS_MT_TRACKING_ID:
+	case EVDEV_ABS_MT_TRACKING_ID:
 		if (e->value != -1) {
 			tp->nactive_slots += 1;
 			tp_new_touch(tp, t, time);
@@ -551,64 +534,63 @@ tp_process_absolute(struct tp_dispatch *
 			tp_end_sequence(tp, t, time);
 		}
 		break;
-	case ABS_MT_PRESSURE:
+	case EVDEV_ABS_MT_PRESSURE:
 		t->pressure = e->value;
 		t->dirty = true;
 		tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
 		break;
-	case ABS_MT_TOOL_TYPE:
+	case EVDEV_ABS_MT_TOOL_TYPE:
 		t->is_tool_palm = e->value == MT_TOOL_PALM;
 		t->dirty = true;
 		tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
 		break;
-	case ABS_MT_TOUCH_MAJOR:
+	case EVDEV_ABS_MT_TOUCH_MAJOR:
 		t->major = e->value;
 		t->dirty = true;
 		tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
 		break;
-	case ABS_MT_TOUCH_MINOR:
+	case EVDEV_ABS_MT_TOUCH_MINOR:
 		t->minor = e->value;
 		t->dirty = true;
 		tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
 		break;
+	default:
+		break;
 	}
 }
 
 static void
 tp_process_absolute_st(struct tp_dispatch *tp,
-		       const struct input_event *e,
+		       const struct evdev_event *e,
 		       uint64_t time)
 {
 	struct tp_touch *t = tp_current_touch(tp);
 
-	switch(e->code) {
-	case ABS_X:
-		evdev_device_check_abs_axis_range(tp->device,
-						  e->code,
-						  e->value);
-		t->point.x = rotated(tp, e->code, e->value);
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_ABS_X:
+		evdev_device_check_abs_axis_range(tp->device, e->usage, e->value);
+		t->point.x = rotated(tp, e->usage, e->value);
 		t->dirty = true;
 		tp->queued |= TOUCHPAD_EVENT_MOTION;
 		break;
-	case ABS_Y:
-		evdev_device_check_abs_axis_range(tp->device,
-						  e->code,
-						  e->value);
-		t->point.y = rotated(tp, e->code, e->value);
+	case EVDEV_ABS_Y:
+		evdev_device_check_abs_axis_range(tp->device, e->usage, e->value);
+		t->point.y = rotated(tp, e->usage, e->value);
 		t->dirty = true;
 		tp->queued |= TOUCHPAD_EVENT_MOTION;
 		break;
-	case ABS_PRESSURE:
+	case EVDEV_ABS_PRESSURE:
 		t->pressure = e->value;
 		t->dirty = true;
 		tp->queued |= TOUCHPAD_EVENT_OTHERAXIS;
 		break;
+	default:
+		break;
 	}
 }
 
 static inline void
-tp_restore_synaptics_touches(struct tp_dispatch *tp,
-			     uint64_t time)
+tp_restore_synaptics_touches(struct tp_dispatch *tp, uint64_t time)
 {
 	unsigned int i;
 	unsigned int nfake_touches;
@@ -642,8 +624,7 @@ tp_restore_synaptics_touches(struct tp_d
 }
 
 static void
-tp_process_fake_touches(struct tp_dispatch *tp,
-			uint64_t time)
+tp_process_fake_touches(struct tp_dispatch *tp, uint64_t time)
 {
 	struct tp_touch *t;
 	unsigned int nfake_touches;
@@ -653,8 +634,7 @@ tp_process_fake_touches(struct tp_dispat
 	if (nfake_touches == FAKE_FINGER_OVERFLOW)
 		return;
 
-	if (tp->device->model_flags &
-	    EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD)
+	if (tp->device->model_flags & EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD)
 		tp_restore_synaptics_touches(tp, time);
 
 	/* ALPS serial touchpads always set 3 slots in the kernel, even
@@ -667,22 +647,9 @@ tp_process_fake_touches(struct tp_dispat
 	 *
 	 * All touchpad devices have at least one slot so we only do this
 	 * for 2 touches or higher.
-	 *
-	 * There's an bug in libevdev < 1.9.0 affecting slots after a
-	 * SYN_DROPPED. Where a user release one or more touches during
-	 * SYN_DROPPED and places new ones on the touchpad, we may end up
-	 * with fake touches but no active slots.
-	 * So let's check for nactive_slots > 0 to make sure we don't lose
-	 * all fingers. That's a workaround only, this must be fixed in
-	 * libevdev.
-	 *
-	 * For a long explanation of what happens, see
-	 * https://gitlab.freedesktop.org/libevdev/libevdev/merge_requests/19
 	 */
 	if (tp->device->model_flags & EVDEV_MODEL_ALPS_SERIAL_TOUCHPAD &&
-	    nfake_touches > 1 && tp->has_mt &&
-	    tp->nactive_slots > 0 &&
-	    nfake_touches > tp->nactive_slots &&
+	    nfake_touches > 1 && tp->has_mt && nfake_touches > tp->nactive_slots &&
 	    tp->nactive_slots < tp->num_slots) {
 		evdev_log_bug_kernel(tp->device,
 				     "Wrong slot count (%d), reducing to %d\n",
@@ -709,87 +676,73 @@ tp_process_fake_touches(struct tp_dispat
 
 static void
 tp_process_trackpoint_button(struct tp_dispatch *tp,
-			     const struct input_event *e,
+			     const struct evdev_event *e,
 			     uint64_t time)
 {
 	struct evdev_dispatch *dispatch;
-	struct input_event event;
-	struct input_event syn_report = {
-		 .input_event_sec = 0,
-		 .input_event_usec = 0,
-		 .type = EV_SYN,
-		 .code = SYN_REPORT,
-		 .value = 0
-	};
+	evdev_usage_t button;
 
 	if (!tp->buttons.trackpoint)
 		return;
 
 	dispatch = tp->buttons.trackpoint->dispatch;
 
-	event = *e;
-	syn_report.input_event_sec = e->input_event_sec;
-	syn_report.input_event_usec = e->input_event_usec;
-
-	switch (event.code) {
-	case BTN_0:
-		event.code = BTN_LEFT;
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_BTN_0:
+		button = evdev_usage_from(EVDEV_BTN_LEFT);
 		break;
-	case BTN_1:
-		event.code = BTN_RIGHT;
+	case EVDEV_BTN_1:
+		button = evdev_usage_from(EVDEV_BTN_RIGHT);
 		break;
-	case BTN_2:
-		event.code = BTN_MIDDLE;
+	case EVDEV_BTN_2:
+		button = evdev_usage_from(EVDEV_BTN_MIDDLE);
 		break;
 	default:
 		return;
 	}
 
-	dispatch->interface->process(dispatch,
-				     tp->buttons.trackpoint,
-				     &event, time);
-	dispatch->interface->process(dispatch,
-				     tp->buttons.trackpoint,
-				     &syn_report, time);
+	_unref_(evdev_frame) *frame = evdev_frame_new(2);
+	evdev_frame_append_one(frame, button, e->value);
+	evdev_frame_set_time(frame, time);
+
+	dispatch->interface->process(dispatch, tp->buttons.trackpoint, frame, time);
 }
 
 static void
-tp_process_key(struct tp_dispatch *tp,
-	       const struct input_event *e,
-	       uint64_t time)
+tp_process_key(struct tp_dispatch *tp, const struct evdev_event *e, uint64_t time)
 {
 	/* ignore kernel key repeat */
 	if (e->value == 2)
 		return;
 
-	switch (e->code) {
-		case BTN_LEFT:
-		case BTN_MIDDLE:
-		case BTN_RIGHT:
-			tp_process_button(tp, e, time);
-			break;
-		case BTN_TOUCH:
-		case BTN_TOOL_FINGER:
-		case BTN_TOOL_DOUBLETAP:
-		case BTN_TOOL_TRIPLETAP:
-		case BTN_TOOL_QUADTAP:
-		case BTN_TOOL_QUINTTAP:
-			tp_fake_finger_set(tp, e->code, !!e->value);
-			break;
-		case BTN_0:
-		case BTN_1:
-		case BTN_2:
-			tp_process_trackpoint_button(tp, e, time);
-			break;
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_BTN_LEFT:
+	case EVDEV_BTN_MIDDLE:
+	case EVDEV_BTN_RIGHT:
+		tp_process_button(tp, e, time);
+		break;
+	case EVDEV_BTN_TOUCH:
+	case EVDEV_BTN_TOOL_FINGER:
+	case EVDEV_BTN_TOOL_DOUBLETAP:
+	case EVDEV_BTN_TOOL_TRIPLETAP:
+	case EVDEV_BTN_TOOL_QUADTAP:
+	case EVDEV_BTN_TOOL_QUINTTAP:
+		tp_fake_finger_set(tp, e->usage, !!e->value);
+		break;
+	case EVDEV_BTN_0:
+	case EVDEV_BTN_1:
+	case EVDEV_BTN_2:
+		tp_process_trackpoint_button(tp, e, time);
+		break;
+	default:
+		break;
 	}
 }
 
 static void
-tp_process_msc(struct tp_dispatch *tp,
-	       const struct input_event *e,
-	       uint64_t time)
+tp_process_msc(struct tp_dispatch *tp, const struct evdev_event *e, uint64_t time)
 {
-	if (e->code != MSC_TIMESTAMP)
+	if (evdev_usage_eq(e->usage, EVDEV_MSC_TIMESTAMP))
 		return;
 
 	tp->quirks.msc_timestamp.now = e->value;
@@ -832,22 +785,18 @@ bool
 tp_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
 	return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
-		t->palm.state == PALM_NONE &&
-		!t->pinned.is_pinned &&
-		!tp_thumb_ignored(tp, t) &&
-		tp_button_touch_active(tp, t) &&
-		tp_edge_scroll_touch_active(tp, t);
+	       t->palm.state == PALM_NONE && !t->pinned.is_pinned &&
+	       !tp_thumb_ignored(tp, t) && tp_button_touch_active(tp, t) &&
+	       tp_edge_scroll_touch_active(tp, t);
 }
 
 bool
 tp_touch_active_for_gesture(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
 	return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
-		t->palm.state == PALM_NONE &&
-		!t->pinned.is_pinned &&
-		!tp_thumb_ignored_for_gesture(tp, t) &&
-		tp_button_touch_active(tp, t) &&
-		tp_edge_scroll_touch_active(tp, t);
+	       t->palm.state == PALM_NONE && !t->pinned.is_pinned &&
+	       !tp_thumb_ignored_for_gesture(tp, t) && tp_button_touch_active(tp, t) &&
+	       tp_edge_scroll_touch_active(tp, t);
 }
 
 static inline bool
@@ -866,8 +815,7 @@ tp_palm_was_in_top_edge(const struct tp_
 static inline bool
 tp_palm_in_side_edge(const struct tp_dispatch *tp, const struct tp_touch *t)
 {
-	return t->point.x < tp->palm.left_edge ||
-	       t->point.x > tp->palm.right_edge;
+	return t->point.x < tp->palm.left_edge || t->point.x > tp->palm.right_edge;
 }
 
 static inline bool
@@ -883,21 +831,16 @@ tp_palm_in_edge(const struct tp_dispatch
 }
 
 static bool
-tp_palm_detect_dwt_triggered(struct tp_dispatch *tp,
-			     struct tp_touch *t,
-			     uint64_t time)
+tp_palm_detect_dwt_triggered(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
-	if (tp->dwt.dwt_enabled &&
-	    tp->dwt.keyboard_active &&
-	    t->state == TOUCH_BEGIN) {
+	if (tp->dwt.dwt_enabled && tp->dwt.keyboard_active && t->state == TOUCH_BEGIN) {
 		t->palm.state = PALM_TYPING;
 		t->palm.first = t->point;
 		return true;
 	}
 
-	if (!tp->dwt.keyboard_active &&
-		   t->state == TOUCH_UPDATE &&
-		   t->palm.state == PALM_TYPING) {
+	if (!tp->dwt.keyboard_active && t->state == TOUCH_UPDATE &&
+	    t->palm.state == PALM_TYPING) {
 		/* If a touch has started before the first or after the last
 		   key press, release it on timeout. Benefit: a palm rested
 		   while typing on the touchpad will be ignored, but a touch
@@ -907,9 +850,10 @@ tp_palm_detect_dwt_triggered(struct tp_d
 		if (t->palm.time == 0 ||
 		    t->palm.time > tp->dwt.keyboard_last_press_time) {
 			t->palm.state = PALM_NONE;
-			evdev_log_debug(tp->device,
-					"palm: touch %d released, timeout after typing\n",
-					t->index);
+			evdev_log_debug(
+				tp->device,
+				"palm: touch %d released, timeout after typing\n",
+				t->index);
 		}
 	}
 
@@ -924,22 +868,22 @@ tp_palm_detect_trackpoint_triggered(stru
 	if (!tp->palm.monitor_trackpoint)
 		return false;
 
-	if (t->palm.state == PALM_NONE &&
-	    t->state == TOUCH_BEGIN &&
+	if (t->palm.state == PALM_NONE && t->state == TOUCH_BEGIN &&
 	    tp->palm.trackpoint_active) {
 		t->palm.state = PALM_TRACKPOINT;
 		return true;
 	}
 
-	if (t->palm.state == PALM_TRACKPOINT &&
-		   t->state == TOUCH_UPDATE &&
-		   !tp->palm.trackpoint_active) {
+	if (t->palm.state == PALM_TRACKPOINT && t->state == TOUCH_UPDATE &&
+	    !tp->palm.trackpoint_active) {
 
 		if (t->palm.time == 0 ||
 		    t->palm.time > tp->palm.trackpoint_last_event_time) {
 			t->palm.state = PALM_NONE;
-			evdev_log_debug(tp->device,
-				       "palm: touch %d released, timeout after trackpoint\n", t->index);
+			evdev_log_debug(
+				tp->device,
+				"palm: touch %d released, timeout after trackpoint\n",
+				t->index);
 		}
 	}
 
@@ -947,22 +891,17 @@ tp_palm_detect_trackpoint_triggered(stru
 }
 
 static bool
-tp_palm_detect_tool_triggered(struct tp_dispatch *tp,
-			      struct tp_touch *t,
-			      uint64_t time)
+tp_palm_detect_tool_triggered(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
 	if (!tp->palm.use_mt_tool)
 		return false;
 
-	if (t->palm.state != PALM_NONE &&
-	    t->palm.state != PALM_TOOL_PALM)
+	if (t->palm.state != PALM_NONE && t->palm.state != PALM_TOOL_PALM)
 		return false;
 
-	if (t->palm.state == PALM_NONE &&
-	    t->is_tool_palm)
+	if (t->palm.state == PALM_NONE && t->is_tool_palm)
 		t->palm.state = PALM_TOOL_PALM;
-	else if (t->palm.state == PALM_TOOL_PALM &&
-		 !t->is_tool_palm)
+	else if (t->palm.state == PALM_TOOL_PALM && !t->is_tool_palm)
 		t->palm.state = PALM_NONE;
 
 	return t->palm.state == PALM_TOOL_PALM;
@@ -980,9 +919,9 @@ tp_palm_detect_move_out_of_edge(struct t
 
 	if (time < t->palm.time + PALM_TIMEOUT && !tp_palm_in_edge(tp, t)) {
 		if (tp_palm_was_in_side_edge(tp, t))
-			directions = NE|E|SE|SW|W|NW;
+			directions = NE | E | SE | SW | W | NW;
 		else if (tp_palm_was_in_top_edge(tp, t))
-			directions = S|SE|SW;
+			directions = S | SE | SW;
 
 		if (directions) {
 			delta = device_delta(t->point, t->palm.first);
@@ -1015,8 +954,7 @@ tp_palm_detect_multifinger(struct tp_dis
 		if (other == t)
 			continue;
 
-		if (tp_touch_active(tp, other) &&
-		    other->palm.state == PALM_NONE) {
+		if (tp_touch_active(tp, other) && other->palm.state == PALM_NONE) {
 			return true;
 		}
 	}
@@ -1037,8 +975,7 @@ tp_palm_detect_touch_size_triggered(stru
 	if (t->palm.state != PALM_NONE && t->palm.state != PALM_TOUCH_SIZE)
 		return false;
 
-	if (t->major > tp->palm.size_threshold ||
-	    t->minor > tp->palm.size_threshold) {
+	if (t->major > tp->palm.size_threshold || t->minor > tp->palm.size_threshold) {
 		if (t->palm.state != PALM_TOUCH_SIZE)
 			evdev_log_debug(tp->device,
 					"palm: touch %d size exceeded\n",
@@ -1051,26 +988,27 @@ tp_palm_detect_touch_size_triggered(stru
 }
 
 static inline bool
-tp_palm_detect_edge(struct tp_dispatch *tp,
-		    struct tp_touch *t,
-		    uint64_t time)
+tp_palm_detect_edge(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
 	if (t->palm.state == PALM_EDGE) {
 		if (tp_palm_detect_multifinger(tp, t, time)) {
 			t->palm.state = PALM_NONE;
-			evdev_log_debug(tp->device,
-				  "palm: touch %d released, multiple fingers\n",
-				  t->index);
-
-		/* If labelled a touch as palm, we unlabel as palm when
-		   we move out of the palm edge zone within the timeout, provided
-		   the direction is within 45 degrees of the horizontal.
-		 */
+			evdev_log_debug(
+				tp->device,
+				"palm: touch %d is no longer palm, multiple fingers\n",
+				t->index);
+
+			/* If labelled a touch as palm, we unlabel as palm when
+			   we move out of the palm edge zone within the timeout,
+			   provided the direction is within 45 degrees of the
+			   horizontal.
+			 */
 		} else if (tp_palm_detect_move_out_of_edge(tp, t, time)) {
 			t->palm.state = PALM_NONE;
-			evdev_log_debug(tp->device,
-				  "palm: touch %d released, out of edge zone\n",
-				  t->index);
+			evdev_log_debug(
+				tp->device,
+				"palm: touch %d is no longer palm, out of edge zone\n",
+				t->index);
 		}
 		return false;
 	}
@@ -1102,8 +1040,7 @@ tp_palm_detect_pressure_triggered(struct
 	if (!tp->palm.use_pressure)
 		return false;
 
-	if (t->palm.state != PALM_NONE &&
-	    t->palm.state != PALM_PRESSURE)
+	if (t->palm.state != PALM_NONE && t->palm.state != PALM_PRESSURE)
 		return false;
 
 	if (t->pressure > tp->palm.pressure_threshold)
@@ -1195,10 +1132,10 @@ out:
 		break;
 	}
 	evdev_log_debug(tp->device,
-		  "palm: touch %d (%s), palm detected (%s)\n",
-		  t->index,
-		  touch_state_to_str(t->state),
-		  palm_state);
+			"palm: touch %d (%s), palm detected (%s)\n",
+			t->index,
+			touch_state_to_str(t->state),
+			palm_state);
 }
 
 static void
@@ -1229,11 +1166,11 @@ tp_unhover_pressure(struct tp_dispatch *
 					tp_motion_history_reset(t);
 					tp_begin_touch(tp, t, time);
 				}
-			/* don't unhover for pressure if we have too many
-			 * fake fingers down, see comment below. Except
-			 * for single-finger touches where the real touch
-			 * decides for the rest.
-			 */
+				/* don't unhover for pressure if we have too many
+				 * fake fingers down, see comment below. Except
+				 * for single-finger touches where the real touch
+				 * decides for the rest.
+				 */
 			} else if (nfake_touches <= tp->num_slots ||
 				   tp->num_slots == 1) {
 				if (t->pressure < tp->pressure.low) {
@@ -1245,13 +1182,11 @@ tp_unhover_pressure(struct tp_dispatch *
 			}
 		}
 
-		if (t->state == TOUCH_BEGIN ||
-		    t->state == TOUCH_UPDATE)
+		if (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE)
 			real_fingers_down++;
 	}
 
-	if (nfake_touches <= tp->num_slots ||
-	    tp->nfingers_down == 0)
+	if (nfake_touches <= tp->num_slots || tp->nfingers_down == 0)
 		return;
 
 	/* if we have more fake fingers down than slots, we assume
@@ -1271,20 +1206,17 @@ tp_unhover_pressure(struct tp_dispatch *
 		}
 	}
 
-	if (tp->nfingers_down > nfake_touches ||
-	    real_fingers_down == 0) {
+	if (tp->nfingers_down > nfake_touches || real_fingers_down == 0) {
 		for (i = tp->ntouches - 1; i >= 0; i--) {
 			t = tp_get_touch(tp, i);
 
-			if (t->state == TOUCH_HOVERING ||
-			    t->state == TOUCH_NONE ||
+			if (t->state == TOUCH_HOVERING || t->state == TOUCH_NONE ||
 			    t->state == TOUCH_MAYBE_END)
 				continue;
 
 			tp_maybe_end_touch(tp, t, time);
 
-			if (real_fingers_down > 0  &&
-			    tp->nfingers_down == nfake_touches)
+			if (real_fingers_down > 0 && tp->nfingers_down == nfake_touches)
 				break;
 		}
 	}
@@ -1294,8 +1226,7 @@ static void
 tp_unhover_size(struct tp_dispatch *tp, uint64_t time)
 {
 	struct tp_touch *t;
-	int low = tp->touch_size.low,
-	    high = tp->touch_size.high;
+	int low = tp->touch_size.low, high = tp->touch_size.high;
 	int i;
 
 	/* We require 5 slots for size handling, so we don't need to care
@@ -1354,8 +1285,7 @@ tp_unhover_fake_touches(struct tp_dispat
 	 * touches, switch each hovering touch to BEGIN
 	 * until nfingers_down matches nfake_touches
 	 */
-	if (tp_fake_finger_is_touching(tp) &&
-	    tp->nfingers_down < nfake_touches) {
+	if (tp_fake_finger_is_touching(tp) && tp->nfingers_down < nfake_touches) {
 		tp_for_each_touch(tp, t) {
 			if (t->state == TOUCH_HOVERING) {
 				tp_begin_touch(tp, t, time);
@@ -1370,13 +1300,11 @@ tp_unhover_fake_touches(struct tp_dispat
 	 * have too many touches also end some of them. This is done in
 	 * reverse order.
 	 */
-	if (tp->nfingers_down > nfake_touches ||
-	    !tp_fake_finger_is_touching(tp)) {
+	if (tp->nfingers_down > nfake_touches || !tp_fake_finger_is_touching(tp)) {
 		for (i = tp->ntouches - 1; i >= 0; i--) {
 			t = tp_get_touch(tp, i);
 
-			if (t->state == TOUCH_HOVERING ||
-			    t->state == TOUCH_NONE)
+			if (t->state == TOUCH_HOVERING || t->state == TOUCH_NONE)
 				continue;
 
 			tp_maybe_end_touch(tp, t, time);
@@ -1397,7 +1325,6 @@ tp_unhover_touches(struct tp_dispatch *t
 		tp_unhover_size(tp, time);
 	else
 		tp_unhover_fake_touches(tp, time);
-
 }
 
 static inline void
@@ -1407,8 +1334,7 @@ tp_position_fake_touches(struct tp_dispa
 	struct tp_touch *topmost = NULL;
 	unsigned int start, i;
 
-	if (tp_fake_finger_count(tp) <= tp->num_slots ||
-	    tp->nfingers_down == 0)
+	if (tp_fake_finger_count(tp) <= tp->num_slots || tp->nfingers_down == 0)
 		return;
 
 	/* We have at least one fake touch down. Find the top-most real
@@ -1417,8 +1343,7 @@ tp_position_fake_touches(struct tp_dispa
 	 */
 	for (i = 0; i < tp->num_slots; i++) {
 		t = tp_get_touch(tp, i);
-		if (t->state == TOUCH_END ||
-		    t->state == TOUCH_NONE)
+		if (t->state == TOUCH_END || t->state == TOUCH_NONE)
 			continue;
 
 		if (topmost == NULL || t->point.y < topmost->point.y)
@@ -1426,8 +1351,7 @@ tp_position_fake_touches(struct tp_dispa
 	}
 
 	if (!topmost) {
-		evdev_log_bug_libinput(tp->device,
-				       "Unable to find topmost touch\n");
+		evdev_log_bug_libinput(tp->device, "Unable to find topmost touch\n");
 		return;
 	}
 
@@ -1470,7 +1394,7 @@ tp_need_motion_history_reset(struct tp_d
 			tp->quirks.nonmotion_event_count = 0;
 		}
 
-		if ((tp->queued & (TOUCHPAD_EVENT_OTHERAXIS|TOUCHPAD_EVENT_MOTION)) ==
+		if ((tp->queued & (TOUCHPAD_EVENT_OTHERAXIS | TOUCHPAD_EVENT_MOTION)) ==
 		    TOUCHPAD_EVENT_OTHERAXIS)
 			tp->quirks.nonmotion_event_count++;
 	}
@@ -1479,9 +1403,7 @@ tp_need_motion_history_reset(struct tp_d
 }
 
 static bool
-tp_detect_jumps(const struct tp_dispatch *tp,
-		struct tp_touch *t,
-		uint64_t time)
+tp_detect_jumps(const struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
 	struct device_coords delta;
 	struct phys_coords mm;
@@ -1533,7 +1455,7 @@ tp_detect_jumps(const struct tp_dispatch
 	delta.x = abs(t->point.x - last->point.x);
 	delta.y = abs(t->point.y - last->point.y);
 	mm = evdev_device_unit_delta_to_mm(tp->device, &delta);
-	abs_distance = hypot(mm.x, mm.y) * reference_interval/tdelta;
+	abs_distance = hypot(mm.x, mm.y) * reference_interval / tdelta;
 	rel_distance = abs_distance - t->jumps.last_delta_mm;
 
 	/* Special case for the ALPS devices in the Lenovo ThinkPad E465,
@@ -1637,7 +1559,7 @@ tp_process_msc_timestamp(struct tp_dispa
 		return;
 	}
 
-	switch(m->state) {
+	switch (m->state) {
 	case JUMP_STATE_EXPECT_FIRST:
 		if (m->now > ms2us(20)) {
 			m->state = JUMP_STATE_IGNORE;
@@ -1664,11 +1586,12 @@ tp_process_msc_timestamp(struct tp_dispa
 			}
 			m->state = JUMP_STATE_IGNORE;
 
-			/* We need to restart the acceleration filter to forget its history.
-			 * The current point becomes the first point in the history there
-			 * (including timestamp) and that accelerates correctly.
-			 * This has a potential to be incorrect but since we only ever see
-			 * those jumps over the first three events it doesn't matter.
+			/* We need to restart the acceleration filter to forget its
+			 * history. The current point becomes the first point in the
+			 * history there (including timestamp) and that accelerates
+			 * correctly. This has a potential to be incorrect but since we
+			 * only ever see those jumps over the first three events it
+			 * doesn't matter.
 			 */
 			filter_restart(tp->device->pointer.filter, tp, time - tdelta);
 		}
@@ -1698,7 +1621,6 @@ tp_pre_process_state(struct tp_dispatch
 		if (t->state == TOUCH_END && t->history.count > 0)
 			t->point = tp_motion_history_offset(t, 0)->point;
 	}
-
 }
 
 static void
@@ -1731,8 +1653,8 @@ tp_process_state(struct tp_dispatch *tp,
 			if (t->speed.exceeded_count > 0)
 				t->speed.exceeded_count--;
 
-			speed_exceeded_count = max(speed_exceeded_count,
-						   t->speed.exceeded_count);
+			speed_exceeded_count =
+				max(speed_exceeded_count, t->speed.exceeded_count);
 
 			/* A touch that hasn't moved must be in the same
 			 * position, so let's add this to the motion
@@ -1744,11 +1666,12 @@ tp_process_state(struct tp_dispatch *tp,
 
 		if (tp_detect_jumps(tp, t, time)) {
 			if (!tp->semi_mt)
-				evdev_log_bug_kernel_ratelimit(tp->device,
-						&tp->jump.warning,
-					        "Touch jump detected and discarded.\n"
-					        "See %s/touchpad-jumping-cursors.html for details\n",
-					        HTTP_DOC_LINK);
+				evdev_log_bug_kernel_ratelimit(
+					tp->device,
+					&tp->jump.warning,
+					"Touch jump detected and discarded.\n"
+					"See %s/touchpad-jumping-cursors.html for details\n",
+					HTTP_DOC_LINK);
 			tp_motion_history_reset(t);
 		}
 
@@ -1774,11 +1697,11 @@ tp_process_state(struct tp_dispatch *tp,
 			if (t->speed.exceeded_count < 15)
 				t->speed.exceeded_count++;
 		} else if (t->speed.exceeded_count > 0) {
-				t->speed.exceeded_count--;
+			t->speed.exceeded_count--;
 		}
 
-		speed_exceeded_count = max(speed_exceeded_count,
-					   t->speed.exceeded_count);
+		speed_exceeded_count =
+			max(speed_exceeded_count, t->speed.exceeded_count);
 
 		tp_calculate_motion_speed(tp, t, time);
 
@@ -1790,9 +1713,7 @@ tp_process_state(struct tp_dispatch *tp,
 		}
 	}
 
-	if (tp->thumb.detect_thumbs &&
-	    have_new_touch &&
-	    tp->nfingers_down >= 2)
+	if (tp->thumb.detect_thumbs && have_new_touch && tp->nfingers_down >= 2)
 		tp_thumb_update_multifinger(tp);
 
 	if (restart_filter)
@@ -1807,8 +1728,7 @@ tp_process_state(struct tp_dispatch *tp,
 	 * We unpin fingers when they move more then a certain threshold to
 	 * to allow drag and drop.
 	 */
-	if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) &&
-	    tp->buttons.is_clickpad)
+	if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) && tp->buttons.is_clickpad)
 		tp_pin_fingers(tp);
 
 	tp_gesture_update_finger_state(tp, time);
@@ -1900,8 +1820,7 @@ tp_apply_rotation(struct evdev_device *d
 }
 
 static void
-tp_handle_state(struct tp_dispatch *tp,
-		uint64_t time)
+tp_handle_state(struct tp_dispatch *tp, uint64_t time)
 {
 	tp_pre_process_state(tp, time);
 	tp_process_state(tp, time);
@@ -1913,12 +1832,10 @@ tp_handle_state(struct tp_dispatch *tp,
 	tp_3fg_drag_apply_config(tp->device);
 }
 
-LIBINPUT_UNUSED
-static inline void
-tp_debug_touch_state(struct tp_dispatch *tp,
-		     struct evdev_device *device)
+_unused_ static inline void
+tp_debug_touch_state(struct tp_dispatch *tp, struct evdev_device *device)
 {
-	char buf[1024] = {0};
+	char buf[1024] = { 0 };
 	struct tp_touch *t;
 	size_t i = 0;
 
@@ -1938,14 +1855,15 @@ tp_debug_touch_state(struct tp_dispatch
 }
 
 static void
-tp_interface_process(struct evdev_dispatch *dispatch,
-		     struct evdev_device *device,
-		     struct input_event *e,
-		     uint64_t time)
+tp_interface_process_event(struct evdev_dispatch *dispatch,
+			   struct evdev_device *device,
+			   struct evdev_event *e,
+			   uint64_t time)
 {
 	struct tp_dispatch *tp = tp_dispatch(dispatch);
 
-	switch (e->type) {
+	uint16_t type = evdev_event_type(e);
+	switch (type) {
 	case EV_ABS:
 		if (tp->has_mt)
 			tp_process_absolute(tp, e, time);
@@ -1968,6 +1886,36 @@ tp_interface_process(struct evdev_dispat
 }
 
 static void
+tp_interface_process(struct evdev_dispatch *dispatch,
+		     struct evdev_device *device,
+		     struct evdev_frame *frame,
+		     uint64_t time)
+{
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+
+	struct evdev_event *ev_syn = &events[nevents - 1];
+	if (evdev_usage_enum(ev_syn->usage) == EVDEV_SYN_REPORT) {
+		/* A SYN_REPORT 1 event is a kernel-inserted SYN_REPORT.
+		 * This happens most commonly on key repeat but in the
+		 * touchpad code this causes issues with
+		 * timestamp deltas (see e.g. #1145).
+		 *
+		 * Let's drop this frame, hoping it wasn't important.
+		 */
+		if (ev_syn->value == 1) {
+			return;
+		}
+	} else {
+		evdev_log_bug_libinput(device, "Terminating event is not a SYN_REPORT");
+	}
+
+	for (size_t i = 0; i < nevents; i++) {
+		tp_interface_process_event(dispatch, device, &events[i], time);
+	}
+}
+
+static void
 tp_remove_sendevents(struct tp_dispatch *tp)
 {
 	struct evdev_paired_keyboard *kbd;
@@ -1975,22 +1923,18 @@ tp_remove_sendevents(struct tp_dispatch
 	libinput_timer_cancel(&tp->palm.trackpoint_timer);
 	libinput_timer_cancel(&tp->dwt.keyboard_timer);
 
-	if (tp->buttons.trackpoint &&
-	    tp->palm.monitor_trackpoint)
-		libinput_device_remove_event_listener(
-					&tp->palm.trackpoint_listener);
+	if (tp->buttons.trackpoint && tp->palm.monitor_trackpoint)
+		libinput_device_remove_event_listener(&tp->palm.trackpoint_listener);
 
 	list_for_each(kbd, &tp->dwt.paired_keyboard_list, link) {
 		libinput_device_remove_event_listener(&kbd->listener);
 	}
 
 	if (tp->lid_switch.lid_switch)
-		libinput_device_remove_event_listener(
-					&tp->lid_switch.listener);
+		libinput_device_remove_event_listener(&tp->lid_switch.listener);
 
 	if (tp->tablet_mode_switch.tablet_mode_switch)
-		libinput_device_remove_event_listener(
-					&tp->tablet_mode_switch.listener);
+		libinput_device_remove_event_listener(&tp->tablet_mode_switch.listener);
 }
 
 static void
@@ -2097,8 +2041,7 @@ out:
 }
 
 static void
-tp_interface_suspend(struct evdev_dispatch *dispatch,
-		     struct evdev_device *device)
+tp_interface_suspend(struct evdev_dispatch *dispatch, struct evdev_device *device)
 {
 	struct tp_dispatch *tp = tp_dispatch(dispatch);
 
@@ -2114,45 +2057,24 @@ tp_sync_touch(struct tp_dispatch *tp,
 	struct libevdev *evdev = device->evdev;
 	int tracking_id;
 
-	if (!libevdev_fetch_slot_value(evdev,
-				       slot,
-				       ABS_MT_POSITION_X,
-				       &t->point.x))
+	if (!libevdev_fetch_slot_value(evdev, slot, ABS_MT_POSITION_X, &t->point.x))
 		t->point.x = libevdev_get_event_value(evdev, EV_ABS, ABS_X);
-	if (!libevdev_fetch_slot_value(evdev,
-				       slot,
-				       ABS_MT_POSITION_Y,
-				       &t->point.y))
+	if (!libevdev_fetch_slot_value(evdev, slot, ABS_MT_POSITION_Y, &t->point.y))
 		t->point.y = libevdev_get_event_value(evdev, EV_ABS, ABS_Y);
 
-	if (!libevdev_fetch_slot_value(evdev,
-				       slot,
-				       ABS_MT_PRESSURE,
-				       &t->pressure))
-		t->pressure = libevdev_get_event_value(evdev,
-						       EV_ABS,
-						       ABS_PRESSURE);
-
-	libevdev_fetch_slot_value(evdev,
-				  slot,
-				  ABS_MT_TOUCH_MAJOR,
-				  &t->major);
-	libevdev_fetch_slot_value(evdev,
-				  slot,
-				  ABS_MT_TOUCH_MINOR,
-				  &t->minor);
-
-	if (libevdev_fetch_slot_value(evdev,
-				      slot,
-				      ABS_MT_TRACKING_ID,
-				      &tracking_id) &&
+	if (!libevdev_fetch_slot_value(evdev, slot, ABS_MT_PRESSURE, &t->pressure))
+		t->pressure = libevdev_get_event_value(evdev, EV_ABS, ABS_PRESSURE);
+
+	libevdev_fetch_slot_value(evdev, slot, ABS_MT_TOUCH_MAJOR, &t->major);
+	libevdev_fetch_slot_value(evdev, slot, ABS_MT_TOUCH_MINOR, &t->minor);
+
+	if (libevdev_fetch_slot_value(evdev, slot, ABS_MT_TRACKING_ID, &tracking_id) &&
 	    tracking_id != -1)
 		tp->nactive_slots++;
 }
 
 static void
-tp_sync_slots(struct tp_dispatch *tp,
-	      struct evdev_device *device)
+tp_sync_slots(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	/* Always sync the first touch so we get ABS_X/Y synced on
 	 * single-touch touchpads */
@@ -2233,8 +2155,7 @@ tp_keyboard_timeout(uint64_t now, void *
 	struct tp_dispatch *tp = data;
 
 	if (tp->dwt.dwt_enabled &&
-	    long_any_bit_set(tp->dwt.key_mask,
-			     ARRAY_LENGTH(tp->dwt.key_mask))) {
+	    long_any_bit_set(tp->dwt.key_mask, ARRAY_LENGTH(tp->dwt.key_mask))) {
 		libinput_timer_set(&tp->dwt.keyboard_timer,
 				   now + DEFAULT_KEYBOARD_ACTIVITY_TIMEOUT_2);
 		tp->dwt.keyboard_last_press_time = now;
@@ -2344,9 +2265,8 @@ tp_keyboard_event(uint64_t time, struct
 		 * trigger dwt because it's likely to be combination like
 		 * Ctrl+S or similar */
 
-		if (long_any_bit_set(tp->dwt.mod_mask,
-				     ARRAY_LENGTH(tp->dwt.mod_mask)))
-		    return;
+		if (long_any_bit_set(tp->dwt.mod_mask, ARRAY_LENGTH(tp->dwt.mod_mask)))
+			return;
 
 		tp_stop_actions(tp, time);
 		tp->dwt.keyboard_active = true;
@@ -2357,13 +2277,11 @@ tp_keyboard_event(uint64_t time, struct
 
 	tp->dwt.keyboard_last_press_time = time;
 	long_set_bit(tp->dwt.key_mask, key);
-	libinput_timer_set(&tp->dwt.keyboard_timer,
-			   time + timeout);
+	libinput_timer_set(&tp->dwt.keyboard_timer, time + timeout);
 }
 
 static bool
-tp_want_dwt(struct evdev_device *touchpad,
-	    struct evdev_device *keyboard)
+tp_want_dwt(struct evdev_device *touchpad, struct evdev_device *keyboard)
 {
 	unsigned int vendor_tp = evdev_device_get_id_vendor(touchpad);
 	unsigned int vendor_kbd = evdev_device_get_id_vendor(keyboard);
@@ -2384,10 +2302,9 @@ tp_want_dwt(struct evdev_device *touchpa
 }
 
 static void
-tp_dwt_pair_keyboard(struct evdev_device *touchpad,
-		     struct evdev_device *keyboard)
+tp_dwt_pair_keyboard(struct evdev_device *touchpad, struct evdev_device *keyboard)
 {
-	struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)touchpad->dispatch;
 	struct evdev_paired_keyboard *kbd;
 	size_t count = 0;
 
@@ -2410,7 +2327,8 @@ tp_dwt_pair_keyboard(struct evdev_device
 	kbd->device = keyboard;
 	libinput_device_add_event_listener(&keyboard->base,
 					   &kbd->listener,
-					   tp_keyboard_event, tp);
+					   tp_keyboard_event,
+					   tp);
 	list_insert(&tp->dwt.paired_keyboard_list, &kbd->link);
 	evdev_log_debug(touchpad,
 			"palm: dwt activated with %s<->%s\n",
@@ -2419,10 +2337,9 @@ tp_dwt_pair_keyboard(struct evdev_device
 }
 
 static void
-tp_pair_trackpoint(struct evdev_device *touchpad,
-			struct evdev_device *trackpoint)
+tp_pair_trackpoint(struct evdev_device *touchpad, struct evdev_device *trackpoint)
 {
-	struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)touchpad->dispatch;
 	unsigned int bus_trp = libevdev_get_id_bustype(trackpoint->evdev);
 	bool tp_is_internal, trp_is_internal;
 
@@ -2432,15 +2349,16 @@ tp_pair_trackpoint(struct evdev_device *
 	tp_is_internal = !!(touchpad->tags & EVDEV_TAG_INTERNAL_TOUCHPAD);
 	trp_is_internal = bus_trp != BUS_USB && bus_trp != BUS_BLUETOOTH;
 
-	if (tp->buttons.trackpoint == NULL &&
-	    tp_is_internal && trp_is_internal) {
+	if (tp->buttons.trackpoint == NULL && tp_is_internal && trp_is_internal) {
 		/* Don't send any pending releases to the new trackpoint */
 		tp->buttons.active_is_topbutton = false;
 		tp->buttons.trackpoint = trackpoint;
 		if (tp->palm.monitor_trackpoint)
-			libinput_device_add_event_listener(&trackpoint->base,
-						&tp->palm.trackpoint_listener,
-						tp_trackpoint_event, tp);
+			libinput_device_add_event_listener(
+				&trackpoint->base,
+				&tp->palm.trackpoint_listener,
+				tp_trackpoint_event,
+				tp);
 	}
 }
 
@@ -2470,9 +2388,7 @@ tp_lid_switch_event(uint64_t time, struc
 }
 
 static void
-tp_tablet_mode_switch_event(uint64_t time,
-			    struct libinput_event *event,
-			    void *data)
+tp_tablet_mode_switch_event(uint64_t time, struct libinput_event *event, void *data)
 {
 	struct tp_dispatch *tp = data;
 	struct libinput_event_switch *swev;
@@ -2481,8 +2397,7 @@ tp_tablet_mode_switch_event(uint64_t tim
 		return;
 
 	swev = libinput_event_get_switch_event(event);
-	if (libinput_event_switch_get_switch(swev) !=
-	    LIBINPUT_SWITCH_TABLET_MODE)
+	if (libinput_event_switch_get_switch(swev) != LIBINPUT_SWITCH_TABLET_MODE)
 		return;
 
 	switch (libinput_event_switch_get_switch_state(swev)) {
@@ -2498,10 +2413,9 @@ tp_tablet_mode_switch_event(uint64_t tim
 }
 
 static void
-tp_pair_lid_switch(struct evdev_device *touchpad,
-		   struct evdev_device *lid_switch)
+tp_pair_lid_switch(struct evdev_device *touchpad, struct evdev_device *lid_switch)
 {
-	struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)touchpad->dispatch;
 
 	if ((lid_switch->tags & EVDEV_TAG_LID_SWITCH) == 0)
 		return;
@@ -2517,7 +2431,8 @@ tp_pair_lid_switch(struct evdev_device *
 
 		libinput_device_add_event_listener(&lid_switch->base,
 						   &tp->lid_switch.listener,
-						   tp_lid_switch_event, tp);
+						   tp_lid_switch_event,
+						   tp);
 		tp->lid_switch.lid_switch = lid_switch;
 	}
 }
@@ -2526,7 +2441,7 @@ static void
 tp_pair_tablet_mode_switch(struct evdev_device *touchpad,
 			   struct evdev_device *tablet_mode_switch)
 {
-	struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)touchpad->dispatch;
 
 	if ((tablet_mode_switch->tags & EVDEV_TAG_TABLET_MODE_SWITCH) == 0)
 		return;
@@ -2537,8 +2452,7 @@ tp_pair_tablet_mode_switch(struct evdev_
 	if (touchpad->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD)
 		return;
 
-	if (evdev_device_has_model_quirk(touchpad,
-					 QUIRK_MODEL_TABLET_MODE_NO_SUSPEND))
+	if (evdev_device_has_model_quirk(touchpad, QUIRK_MODEL_TABLET_MODE_NO_SUSPEND))
 		return;
 
 	evdev_log_debug(touchpad,
@@ -2547,13 +2461,14 @@ tp_pair_tablet_mode_switch(struct evdev_
 			tablet_mode_switch->devname);
 
 	libinput_device_add_event_listener(&tablet_mode_switch->base,
-				&tp->tablet_mode_switch.listener,
-				tp_tablet_mode_switch_event, tp);
+					   &tp->tablet_mode_switch.listener,
+					   tp_tablet_mode_switch_event,
+					   tp);
 	tp->tablet_mode_switch.tablet_mode_switch = tablet_mode_switch;
 
 	if (evdev_device_switch_get_state(tablet_mode_switch,
-					  LIBINPUT_SWITCH_TABLET_MODE)
-		    == LIBINPUT_SWITCH_STATE_ON) {
+					  LIBINPUT_SWITCH_TABLET_MODE) ==
+	    LIBINPUT_SWITCH_STATE_ON) {
 		tp_suspend(tp, touchpad, SUSPEND_TABLET_MODE);
 	}
 }
@@ -2579,17 +2494,17 @@ tp_change_rotation(struct evdev_device *
 		struct evdev_dispatch *dispatch = tablet_device->dispatch;
 
 		if (dispatch->interface->left_handed_toggle)
-			dispatch->interface->left_handed_toggle(dispatch,
-								tablet_device,
-								tp->left_handed.want_rotate);
+			dispatch->interface->left_handed_toggle(
+				dispatch,
+				tablet_device,
+				tp->left_handed.want_rotate);
 	}
 }
 
 static void
-tp_pair_tablet(struct evdev_device *touchpad,
-	       struct evdev_device *tablet)
+tp_pair_tablet(struct evdev_device *touchpad, struct evdev_device *tablet)
 {
-	struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)touchpad->dispatch;
 
 	if (!tp->left_handed.must_rotate)
 		return;
@@ -2619,7 +2534,7 @@ static void
 tp_interface_device_added(struct evdev_device *device,
 			  struct evdev_device *added_device)
 {
-	struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch;
 
 	tp_pair_trackpoint(device, added_device);
 	tp_dwt_pair_keyboard(device, added_device);
@@ -2639,18 +2554,19 @@ static void
 tp_interface_device_removed(struct evdev_device *device,
 			    struct evdev_device *removed_device)
 {
-	struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch;
 	struct evdev_paired_keyboard *kbd;
 
 	if (removed_device == tp->buttons.trackpoint) {
 		/* Clear any pending releases for the trackpoint */
-		if (tp->buttons.active && tp->buttons.active_is_topbutton) {
-			tp->buttons.active = 0;
+		if (evdev_usage_ne(tp->buttons.active, 0) &&
+		    tp->buttons.active_is_topbutton) {
+			tp->buttons.active = evdev_usage_from_uint32_t(0);
 			tp->buttons.active_is_topbutton = false;
 		}
 		if (tp->palm.monitor_trackpoint)
 			libinput_device_remove_event_listener(
-						&tp->palm.trackpoint_listener);
+				&tp->palm.trackpoint_listener);
 		tp->buttons.trackpoint = NULL;
 	}
 
@@ -2662,21 +2578,19 @@ tp_interface_device_removed(struct evdev
 	}
 
 	if (removed_device == tp->lid_switch.lid_switch) {
-		libinput_device_remove_event_listener(
-					&tp->lid_switch.listener);
+		libinput_device_remove_event_listener(&tp->lid_switch.listener);
 		tp->lid_switch.lid_switch = NULL;
 		tp_resume(tp, device, SUSPEND_LID);
 	}
 
 	if (removed_device == tp->tablet_mode_switch.tablet_mode_switch) {
-		libinput_device_remove_event_listener(
-					&tp->tablet_mode_switch.listener);
+		libinput_device_remove_event_listener(&tp->tablet_mode_switch.listener);
 		tp->tablet_mode_switch.tablet_mode_switch = NULL;
 		tp_resume(tp, device, SUSPEND_TABLET_MODE);
 	}
 
 	if (tp->sendevents.current_mode ==
-		    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) {
+	    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) {
 		struct libinput_device *dev;
 		bool found = false;
 
@@ -2719,8 +2633,7 @@ evdev_tag_touchpad_external(struct evdev
 }
 
 static void
-evdev_tag_touchpad(struct evdev_device *device,
-		   struct udev_device *udev_device)
+evdev_tag_touchpad(struct evdev_device *device, struct udev_device *udev_device)
 {
 	int bustype, vendor;
 	const char *prop;
@@ -2738,9 +2651,7 @@ evdev_tag_touchpad(struct evdev_device *
 			return;
 		}
 
-		evdev_log_info(device,
-			       "tagged with unknown value %s\n",
-			       prop);
+		evdev_log_info(device, "tagged with unknown value %s\n", prop);
 	}
 
 	/* The hwdb is the authority on integration, these heuristics are
@@ -2775,7 +2686,7 @@ evdev_tag_touchpad(struct evdev_device *
 		evdev_tag_touchpad_external(device);
 
 	if ((device->tags &
-	    (EVDEV_TAG_EXTERNAL_TOUCHPAD|EVDEV_TAG_INTERNAL_TOUCHPAD)) == 0) {
+	     (EVDEV_TAG_EXTERNAL_TOUCHPAD | EVDEV_TAG_INTERNAL_TOUCHPAD)) == 0) {
 		evdev_log_bug_libinput(device,
 				       "Internal or external? Please file a bug.\n");
 		evdev_tag_touchpad_external(device);
@@ -2826,9 +2737,9 @@ tp_interface_toggle_touch(struct evdev_d
 
 /* Called when the tablet toggles to left-handed */
 static void
-touchpad_left_handed_toggled(struct evdev_dispatch *dispatch,
-			     struct evdev_device *device,
-			     bool left_handed_enabled)
+tp_interface_left_handed_toggled(struct evdev_dispatch *dispatch,
+				 struct evdev_device *device,
+				 bool left_handed_enabled)
 {
 	struct tp_dispatch *tp = tp_dispatch(dispatch);
 
@@ -2848,6 +2759,32 @@ touchpad_left_handed_toggled(struct evde
 	tp_change_rotation(device, DONT_NOTIFY);
 }
 
+static void
+tp_interface_disable_feature(struct evdev_dispatch *dispatch,
+			     enum libinput_feature feature)
+{
+	struct tp_dispatch *tp = tp_dispatch(dispatch);
+
+	switch (feature) {
+	case LIBINPUT_FEATURE_TOUCHPAD_JUMP_DETECTION:
+		tp->jump.detection_disabled = true;
+		break;
+	case LIBINPUT_FEATURE_TOUCHPAD_HYSTERESIS:
+		tp->hysteresis.enabled = false;
+		break;
+	case LIBINPUT_FEATURE_TOUCHPAD_PALM_DETECTION:
+		tp->palm.use_mt_tool = false;
+		tp->palm.use_pressure = false;
+		tp->palm.use_size = false;
+		tp->palm.right_edge = INT_MAX;
+		tp->palm.left_edge = INT_MIN;
+		tp->palm.upper_edge = INT_MIN;
+		break;
+	default:
+		return;
+	}
+}
+
 static struct evdev_dispatch_interface tp_interface = {
 	.process = tp_interface_process,
 	.suspend = tp_interface_suspend,
@@ -2856,18 +2793,17 @@ static struct evdev_dispatch_interface t
 	.device_added = tp_interface_device_added,
 	.device_removed = tp_interface_device_removed,
 	.device_suspended = tp_interface_device_removed, /* treat as remove */
-	.device_resumed = tp_interface_device_added,   /* treat as add */
+	.device_resumed = tp_interface_device_added,     /* treat as add */
 	.post_added = NULL,
 	.touch_arbitration_toggle = tp_interface_toggle_touch,
 	.touch_arbitration_update_rect = NULL,
 	.get_switch_state = NULL,
-	.left_handed_toggle = touchpad_left_handed_toggled,
+	.left_handed_toggle = tp_interface_left_handed_toggled,
+	.disable_feature = tp_interface_disable_feature,
 };
 
 static void
-tp_init_touch(struct tp_dispatch *tp,
-	      struct tp_touch *t,
-	      unsigned int index)
+tp_init_touch(struct tp_dispatch *tp, struct tp_touch *t, unsigned int index)
 {
 	t->tp = tp;
 	t->has_ended = true;
@@ -2885,8 +2821,7 @@ tp_disable_abs_mt(struct evdev_device *d
 }
 
 static bool
-tp_init_slots(struct tp_dispatch *tp,
-	      struct evdev_device *device)
+tp_init_slots(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	const struct input_absinfo *absinfo;
 	struct map {
@@ -2943,9 +2878,7 @@ tp_init_slots(struct tp_dispatch *tp,
 		tp_disable_abs_mt(device);
 
 	ARRAY_FOR_EACH(max_touches, m) {
-		if (libevdev_has_event_code(device->evdev,
-					    EV_KEY,
-					    m->code)) {
+		if (libevdev_has_event_code(device->evdev, EV_KEY, m->code)) {
 			n_btn_tool_touches = m->ntouches;
 			break;
 		}
@@ -2965,7 +2898,7 @@ tp_init_slots(struct tp_dispatch *tp,
 	 * performed.
 	 */
 	if (libevdev_get_event_value(device->evdev, EV_KEY, BTN_TOOL_FINGER))
-		tp_fake_finger_set(tp, BTN_TOOL_FINGER, 1);
+		tp_fake_finger_set(tp, evdev_usage_from(EVDEV_BTN_TOOL_FINGER), true);
 
 	return true;
 }
@@ -2993,16 +2926,16 @@ tp_init_accel(struct tp_dispatch *tp, en
 	 * and y resolution, so that a circle on the
 	 * touchpad does not turn into an ellipse on the screen.
 	 */
-	tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_x;
-	tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y;
-	tp->accel.xy_scale_coeff = 1.0 * res_x/res_y;
+	tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI / 25.4) / res_x;
+	tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI / 25.4) / res_y;
+	tp->accel.xy_scale_coeff = 1.0 * res_x / res_y;
 
 	if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT) {
 		filter = create_pointer_accelerator_filter_touchpad_flat(dpi);
 	} else if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM) {
 		filter = create_custom_accelerator_filter();
 	} else if (evdev_device_has_model_quirk(device, QUIRK_MODEL_LENOVO_X230) ||
-		 tp->device->model_flags & EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81) {
+		   tp->device->model_flags & EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81) {
 		filter = create_pointer_accelerator_filter_lenovo_x230(dpi, use_v_avg);
 	} else {
 		uint64_t eds_threshold = 0;
@@ -3088,17 +3021,17 @@ static uint32_t
 tp_scroll_config_scroll_method_get_methods(struct libinput_device *device)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 
 	return tp_scroll_get_methods(tp);
 }
 
 static enum libinput_config_status
 tp_scroll_config_scroll_method_set_method(struct libinput_device *device,
-		        enum libinput_config_scroll_method method)
+					  enum libinput_config_scroll_method method)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 	uint64_t time = libinput_now(tp_libinput_context(tp));
 
 	if (method == tp->scroll.method)
@@ -3116,7 +3049,7 @@ static enum libinput_config_scroll_metho
 tp_scroll_config_scroll_method_get_method(struct libinput_device *device)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 
 	return tp->scroll.method;
 }
@@ -3145,7 +3078,7 @@ static enum libinput_config_scroll_metho
 tp_scroll_config_scroll_method_get_default_method(struct libinput_device *device)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 
 	return tp_scroll_get_default_method(tp);
 }
@@ -3156,7 +3089,8 @@ tp_scroll_config_natural_get_default(str
 	struct evdev_device *dev = evdev_device(device);
 
 	return (evdev_device_has_model_quirk(dev, QUIRK_MODEL_APPLE_TOUCHPAD) ||
-		evdev_device_has_model_quirk(dev, QUIRK_MODEL_APPLE_TOUCHPAD_ONEBUTTON));
+		evdev_device_has_model_quirk(dev,
+					     QUIRK_MODEL_APPLE_TOUCHPAD_ONEBUTTON));
 }
 
 static void
@@ -3166,17 +3100,21 @@ tp_init_scroll(struct tp_dispatch *tp, s
 
 	evdev_init_natural_scroll(device);
 	/* Override natural scroll config for Apple touchpads */
-	device->scroll.config_natural.get_default_enabled = tp_scroll_config_natural_get_default;
-	device->scroll.natural_scrolling_enabled = tp_scroll_config_natural_get_default(&device->base);
+	device->scroll.config_natural.get_default_enabled =
+		tp_scroll_config_natural_get_default;
+	device->scroll.natural_scrolling_enabled =
+		tp_scroll_config_natural_get_default(&device->base);
 
-	tp->scroll.config_method.get_methods = tp_scroll_config_scroll_method_get_methods;
+	tp->scroll.config_method.get_methods =
+		tp_scroll_config_scroll_method_get_methods;
 	tp->scroll.config_method.set_method = tp_scroll_config_scroll_method_set_method;
 	tp->scroll.config_method.get_method = tp_scroll_config_scroll_method_get_method;
-	tp->scroll.config_method.get_default_method = tp_scroll_config_scroll_method_get_default_method;
+	tp->scroll.config_method.get_default_method =
+		tp_scroll_config_scroll_method_get_default_method;
 	tp->scroll.method = tp_scroll_get_default_method(tp);
 	tp->device->base.config.scroll_method = &tp->scroll.config_method;
 
-	 /* In mm for touchpads with valid resolution, see tp_init_accel() */
+	/* In mm for touchpads with valid resolution, see tp_init_accel() */
 	tp->device->scroll.threshold = 0.0;
 	tp->device->scroll.direction_lock_threshold = 5.0;
 }
@@ -3188,13 +3126,12 @@ tp_dwt_config_is_available(struct libinp
 }
 
 static enum libinput_config_status
-tp_dwt_config_set(struct libinput_device *device,
-	   enum libinput_config_dwt_state enable)
+tp_dwt_config_set(struct libinput_device *device, enum libinput_config_dwt_state enable)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 
-	switch(enable) {
+	switch (enable) {
 	case LIBINPUT_CONFIG_DWT_ENABLED:
 	case LIBINPUT_CONFIG_DWT_DISABLED:
 		break;
@@ -3211,11 +3148,10 @@ static enum libinput_config_dwt_state
 tp_dwt_config_get(struct libinput_device *device)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 
-	return tp->dwt.dwt_enabled ?
-		LIBINPUT_CONFIG_DWT_ENABLED :
-		LIBINPUT_CONFIG_DWT_DISABLED;
+	return tp->dwt.dwt_enabled ? LIBINPUT_CONFIG_DWT_ENABLED
+				   : LIBINPUT_CONFIG_DWT_DISABLED;
 }
 
 static bool
@@ -3228,11 +3164,10 @@ static enum libinput_config_dwt_state
 tp_dwt_config_get_default(struct libinput_device *device)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 
-	return tp_dwt_default_enabled(tp) ?
-		LIBINPUT_CONFIG_DWT_ENABLED :
-		LIBINPUT_CONFIG_DWT_DISABLED;
+	return tp_dwt_default_enabled(tp) ? LIBINPUT_CONFIG_DWT_ENABLED
+					  : LIBINPUT_CONFIG_DWT_DISABLED;
 }
 
 static int
@@ -3243,12 +3178,12 @@ tp_dwtp_config_is_available(struct libin
 
 static enum libinput_config_status
 tp_dwtp_config_set(struct libinput_device *device,
-	   enum libinput_config_dwtp_state enable)
+		   enum libinput_config_dwtp_state enable)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 
-	switch(enable) {
+	switch (enable) {
 	case LIBINPUT_CONFIG_DWTP_ENABLED:
 	case LIBINPUT_CONFIG_DWTP_DISABLED:
 		break;
@@ -3265,11 +3200,10 @@ static enum libinput_config_dwtp_state
 tp_dwtp_config_get(struct libinput_device *device)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 
-	return tp->palm.dwtp_enabled ?
-		LIBINPUT_CONFIG_DWTP_ENABLED :
-		LIBINPUT_CONFIG_DWTP_DISABLED;
+	return tp->palm.dwtp_enabled ? LIBINPUT_CONFIG_DWTP_ENABLED
+				     : LIBINPUT_CONFIG_DWTP_DISABLED;
 }
 
 static bool
@@ -3282,34 +3216,28 @@ static enum libinput_config_dwtp_state
 tp_dwtp_config_get_default(struct libinput_device *device)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 
-	return tp_dwtp_default_enabled(tp) ?
-		LIBINPUT_CONFIG_DWTP_ENABLED :
-		LIBINPUT_CONFIG_DWTP_DISABLED;
+	return tp_dwtp_default_enabled(tp) ? LIBINPUT_CONFIG_DWTP_ENABLED
+					   : LIBINPUT_CONFIG_DWTP_DISABLED;
 }
 
 static inline bool
 tp_is_tpkb_combo_below(struct evdev_device *device)
 {
-	struct quirks_context *quirks;
-	struct quirks *q;
 	char *prop;
 	enum tpkbcombo_layout layout = TPKBCOMBO_LAYOUT_UNKNOWN;
 	int rc = false;
 
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
 	if (!q)
 		return false;
 
 	if (quirks_get_string(q, QUIRK_ATTR_TPKBCOMBO_LAYOUT, &prop)) {
 		rc = parse_tpkbcombo_layout_poperty(prop, &layout) &&
-			layout == TPKBCOMBO_LAYOUT_BELOW;
+		     layout == TPKBCOMBO_LAYOUT_BELOW;
 	}
 
-	quirks_unref(q);
-
 	return rc;
 }
 
@@ -3320,8 +3248,7 @@ tp_is_tablet(struct evdev_device *device
 }
 
 static void
-tp_init_dwt(struct tp_dispatch *tp,
-	    struct evdev_device *device)
+tp_init_dwt(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD &&
 	    !tp_is_tpkb_combo_below(device))
@@ -3336,8 +3263,7 @@ tp_init_dwt(struct tp_dispatch *tp,
 }
 
 static void
-tp_init_dwtp(struct tp_dispatch *tp,
-	    struct evdev_device *device)
+tp_init_dwtp(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	tp->palm.dwtp_enabled = tp_dwtp_default_enabled(tp);
 
@@ -3352,8 +3278,7 @@ tp_init_dwtp(struct tp_dispatch *tp,
 }
 
 static inline void
-tp_init_palmdetect_edge(struct tp_dispatch *tp,
-			struct evdev_device *device)
+tp_init_palmdetect_edge(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	double width, height;
 	struct phys_coords mm = { 0.0, 0.0 };
@@ -3392,28 +3317,22 @@ tp_init_palmdetect_edge(struct tp_dispat
 }
 
 static int
-tp_read_palm_pressure_prop(struct tp_dispatch *tp,
-			   const struct evdev_device *device)
+tp_read_palm_pressure_prop(struct tp_dispatch *tp, const struct evdev_device *device)
 {
 	const int default_palm_threshold = 130;
 	uint32_t threshold = default_palm_threshold;
-	struct quirks_context *quirks;
-	struct quirks *q;
 
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
+	_unref_(quirks) *q = libinput_device_get_quirks(&tp->device->base);
 	if (!q)
 		return threshold;
 
 	quirks_get_uint32(q, QUIRK_ATTR_PALM_PRESSURE_THRESHOLD, &threshold);
-	quirks_unref(q);
 
 	return threshold;
 }
 
 static inline void
-tp_init_palmdetect_pressure(struct tp_dispatch *tp,
-			    struct evdev_device *device)
+tp_init_palmdetect_pressure(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_PRESSURE)) {
 		tp->palm.use_pressure = false;
@@ -3431,15 +3350,11 @@ tp_init_palmdetect_pressure(struct tp_di
 }
 
 static inline void
-tp_init_palmdetect_size(struct tp_dispatch *tp,
-			struct evdev_device *device)
+tp_init_palmdetect_size(struct tp_dispatch *tp, struct evdev_device *device)
 {
-	struct quirks_context *quirks;
-	struct quirks *q;
 	uint32_t threshold;
 
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
 	if (!q)
 		return;
 
@@ -3449,29 +3364,27 @@ tp_init_palmdetect_size(struct tp_dispat
 			tp->palm.size_threshold = threshold;
 		}
 	}
-	quirks_unref(q);
 }
 
 static inline void
-tp_init_palmdetect_arbitration(struct tp_dispatch *tp,
-			       struct evdev_device *device)
+tp_init_palmdetect_arbitration(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	char timer_name[64];
 
 	snprintf(timer_name,
 		 sizeof(timer_name),
-		  "%s arbitration",
-		  evdev_device_get_sysname(device));
+		 "%s arbitration",
+		 evdev_device_get_sysname(device));
 	libinput_timer_init(&tp->arbitration.arbitration_timer,
 			    tp_libinput_context(tp),
 			    timer_name,
-			    tp_arbitration_timeout, tp);
+			    tp_arbitration_timeout,
+			    tp);
 	tp->arbitration.state = ARBITRATION_NOT_ACTIVE;
 }
 
 static void
-tp_init_palmdetect(struct tp_dispatch *tp,
-		   struct evdev_device *device)
+tp_init_palmdetect(struct tp_dispatch *tp, struct evdev_device *device)
 {
 
 	tp->palm.right_edge = INT_MAX;
@@ -3481,16 +3394,13 @@ tp_init_palmdetect(struct tp_dispatch *t
 	tp_init_palmdetect_arbitration(tp, device);
 
 	if (device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD &&
-	    !tp_is_tpkb_combo_below(device) &&
-	    !tp_is_tablet(device))
+	    !tp_is_tpkb_combo_below(device) && !tp_is_tablet(device))
 		return;
 
 	if (!tp_is_tablet(device))
 		tp->palm.monitor_trackpoint = true;
 
-	if (libevdev_has_event_code(device->evdev,
-				    EV_ABS,
-				    ABS_MT_TOOL_TYPE))
+	if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_TOOL_TYPE))
 		tp->palm.use_mt_tool = true;
 
 	if (!tp_is_tablet(device))
@@ -3500,19 +3410,19 @@ tp_init_palmdetect(struct tp_dispatch *t
 }
 
 static void
-tp_init_sendevents(struct tp_dispatch *tp,
-		   struct evdev_device *device)
+tp_init_sendevents(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	char timer_name[64];
 
 	snprintf(timer_name,
 		 sizeof(timer_name),
-		  "%s trackpoint",
-		  evdev_device_get_sysname(device));
+		 "%s trackpoint",
+		 evdev_device_get_sysname(device));
 	libinput_timer_init(&tp->palm.trackpoint_timer,
 			    tp_libinput_context(tp),
 			    timer_name,
-			    tp_trackpoint_timeout, tp);
+			    tp_trackpoint_timeout,
+			    tp);
 
 	snprintf(timer_name,
 		 sizeof(timer_name),
@@ -3521,12 +3431,12 @@ tp_init_sendevents(struct tp_dispatch *t
 	libinput_timer_init(&tp->dwt.keyboard_timer,
 			    tp_libinput_context(tp),
 			    timer_name,
-			    tp_keyboard_timeout, tp);
+			    tp_keyboard_timeout,
+			    tp);
 }
 
 static bool
-tp_pass_sanity_check(struct tp_dispatch *tp,
-		     struct evdev_device *device)
+tp_pass_sanity_check(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	struct libevdev *evdev = device->evdev;
 
@@ -3542,17 +3452,15 @@ tp_pass_sanity_check(struct tp_dispatch
 	return true;
 
 error:
-	evdev_log_bug_kernel(device,
-			     "device failed touchpad sanity checks\n");
+	evdev_log_bug_kernel(device, "device failed touchpad sanity checks\n");
 	return false;
 }
 
 static void
-tp_init_default_resolution(struct tp_dispatch *tp,
-			   struct evdev_device *device)
+tp_init_default_resolution(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	const int touchpad_width_mm = 69, /* 1 under palm detection */
-		  touchpad_height_mm = 50;
+		touchpad_height_mm = 50;
 	int xres, yres;
 
 	if (!device->abs.is_fake_resolution)
@@ -3572,8 +3480,8 @@ tp_init_default_resolution(struct tp_dis
 		       touchpad_width_mm,
 		       touchpad_height_mm);
 
-	xres = device->abs.dimensions.x/touchpad_width_mm;
-	yres = device->abs.dimensions.y/touchpad_height_mm;
+	xres = device->abs.dimensions.x / touchpad_width_mm;
+	yres = device->abs.dimensions.y / touchpad_height_mm;
 	libevdev_set_abs_resolution(device->evdev, ABS_X, xres);
 	libevdev_set_abs_resolution(device->evdev, ABS_Y, yres);
 	libevdev_set_abs_resolution(device->evdev, ABS_MT_POSITION_X, xres);
@@ -3591,12 +3499,12 @@ tp_init_hysteresis(struct tp_dispatch *t
 	if (ax->fuzz)
 		xmargin = ax->fuzz;
 	else
-		xmargin = ax->resolution/4;
+		xmargin = ax->resolution / 4;
 
 	if (ay->fuzz)
 		ymargin = ay->fuzz;
 	else
-		ymargin = ay->resolution/4;
+		ymargin = ay->resolution / 4;
 
 	tp->hysteresis.margin.x = xmargin;
 	tp->hysteresis.margin.y = ymargin;
@@ -3609,13 +3517,10 @@ tp_init_hysteresis(struct tp_dispatch *t
 }
 
 static void
-tp_init_pressure(struct tp_dispatch *tp,
-		 struct evdev_device *device)
+tp_init_pressure(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	const struct input_absinfo *abs;
 	unsigned int code;
-	struct quirks_context *quirks;
-	struct quirks *q;
 	struct quirk_range r;
 	int hi, lo;
 
@@ -3628,16 +3533,15 @@ tp_init_pressure(struct tp_dispatch *tp,
 	abs = libevdev_get_abs_info(device->evdev, code);
 	assert(abs);
 
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
 	if (q && quirks_get_range(q, QUIRK_ATTR_PRESSURE_RANGE, &r)) {
 		hi = r.upper;
 		lo = r.lower;
 
 		if (hi == 0 && lo == 0) {
 			evdev_log_info(device,
-			       "pressure-based touch detection disabled\n");
-			goto out;
+				       "pressure-based touch detection disabled\n");
+			return;
 		}
 	} else {
 		double range = absinfo_range(abs);
@@ -3647,12 +3551,14 @@ tp_init_pressure(struct tp_dispatch *tp,
 		lo = abs->minimum + 0.10 * range;
 	}
 
-	if (hi > abs->maximum || hi < abs->minimum ||
-	    lo > abs->maximum || lo < abs->minimum) {
-		evdev_log_bug_libinput(device,
-			       "discarding out-of-bounds pressure range %d:%d\n",
-			       hi, lo);
-		goto out;
+	if (hi > abs->maximum || hi < abs->minimum || lo > abs->maximum ||
+	    lo < abs->minimum) {
+		evdev_log_bug_libinput(
+			device,
+			"discarding out-of-bounds pressure range %d:%d\n",
+			hi,
+			lo);
+		return;
 	}
 
 	tp->pressure.use_pressure = true;
@@ -3663,45 +3569,35 @@ tp_init_pressure(struct tp_dispatch *tp,
 			"using pressure-based touch detection (%d:%d)\n",
 			lo,
 			hi);
-out:
-	quirks_unref(q);
 }
 
 static bool
-tp_init_touch_size(struct tp_dispatch *tp,
-		   struct evdev_device *device)
+tp_init_touch_size(struct tp_dispatch *tp, struct evdev_device *device)
 {
-	struct quirks_context *quirks;
-	struct quirks *q;
 	struct quirk_range r;
 	int lo, hi;
-	int rc = false;
 
-	if (!libevdev_has_event_code(device->evdev,
-				     EV_ABS,
-				     ABS_MT_TOUCH_MAJOR)) {
+	if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_MT_TOUCH_MAJOR)) {
 		return false;
 	}
 
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
 	if (q && quirks_get_range(q, QUIRK_ATTR_TOUCH_SIZE_RANGE, &r)) {
 		hi = r.upper;
 		lo = r.lower;
 	} else {
-		goto out;
+		return false;
 	}
 
 	if (libevdev_get_num_slots(device->evdev) < 5) {
 		evdev_log_bug_libinput(device,
-			       "Expected 5+ slots for touch size detection\n");
-		goto out;
+				       "Expected 5+ slots for touch size detection\n");
+		return false;
 	}
 
 	if (hi == 0 && lo == 0) {
-		evdev_log_info(device,
-			       "touch size based touch detection disabled\n");
-		goto out;
+		evdev_log_info(device, "touch size based touch detection disabled\n");
+		return false;
 	}
 
 	/* Thresholds apply for both major or minor */
@@ -3709,19 +3605,12 @@ tp_init_touch_size(struct tp_dispatch *t
 	tp->touch_size.high = hi;
 	tp->touch_size.use_touch_size = true;
 
-	evdev_log_debug(device,
-			"using size-based touch detection (%d:%d)\n",
-			hi, lo);
-
-	rc = true;
-out:
-	quirks_unref(q);
-	return rc;
+	evdev_log_debug(device, "using size-based touch detection (%d:%d)\n", hi, lo);
+	return true;
 }
 
 static void
-tp_init_pressurepad(struct tp_dispatch *tp,
-		    struct evdev_device *device)
+tp_init_pressurepad(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	/* On traditional touchpads, the pressure value equals contact
 	 * size. On PressurePads, pressure is a real physical axis for the
@@ -3741,8 +3630,7 @@ tp_init_pressurepad(struct tp_dispatch *
 }
 
 static int
-tp_init(struct tp_dispatch *tp,
-	struct evdev_device *device)
+tp_init(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	bool use_touch_size = false;
 
@@ -3792,8 +3680,7 @@ tp_init(struct tp_dispatch *tp,
 	 * detection impossible. See
 	 * https://gitlab.freedesktop.org/libinput/libinput/-/issues/506
 	 */
-	if (evdev_device_has_model_quirk(device,
-					 QUIRK_MODEL_LENOVO_X1GEN6_TOUCHPAD))
+	if (evdev_device_has_model_quirk(device, QUIRK_MODEL_LENOVO_X1GEN6_TOUCHPAD))
 		tp->jump.detection_disabled = true;
 
 	device->seat_caps |= EVDEV_DEVICE_POINTER;
@@ -3816,8 +3703,7 @@ tp_sendevents_get_modes(struct libinput_
 }
 
 static void
-tp_suspend_conditional(struct tp_dispatch *tp,
-		       struct evdev_device *device)
+tp_suspend_conditional(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	struct libinput_device *dev;
 
@@ -3835,17 +3721,17 @@ tp_sendevents_set_mode(struct libinput_d
 		       enum libinput_config_send_events_mode mode)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *tp = (struct tp_dispatch *)evdev->dispatch;
 
 	/* DISABLED overrides any DISABLED_ON_ */
 	if ((mode & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED) &&
 	    (mode & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE))
-	    mode &= ~LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
+		mode &= ~LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
 
 	if (mode == tp->sendevents.current_mode)
 		return LIBINPUT_CONFIG_STATUS_SUCCESS;
 
-	switch(mode) {
+	switch (mode) {
 	case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
 		tp_resume(tp, evdev, SUSPEND_SENDEVENTS);
 		tp_resume(tp, evdev, SUSPEND_EXTERNAL_MOUSE);
@@ -3871,7 +3757,7 @@ static enum libinput_config_send_events_
 tp_sendevents_get_mode(struct libinput_device *device)
 {
 	struct evdev_device *evdev = evdev_device(device);
-	struct tp_dispatch *dispatch = (struct tp_dispatch*)evdev->dispatch;
+	struct tp_dispatch *dispatch = (struct tp_dispatch *)evdev->dispatch;
 
 	return dispatch->sendevents.current_mode;
 }
@@ -3904,11 +3790,10 @@ static bool
 tp_requires_rotation(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	bool rotate = false;
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct libinput *li = tp_libinput_context(tp);
 	WacomDeviceDatabase *db = NULL;
-	WacomDevice **devices = NULL,
-		    **d;
+	WacomDevice **devices = NULL, **d;
 	WacomDevice *dev;
 	uint32_t vid = evdev_device_get_id_vendor(device),
 		 pid = evdev_device_get_id_product(device);
@@ -3934,12 +3819,11 @@ tp_requires_rotation(struct tp_dispatch
 	if (!devices)
 		goto out;
 	d = devices;
-	while(*d) {
+	while (*d) {
 		const WacomMatch *paired;
 
 		paired = libwacom_get_paired_device(*d);
-		if (paired &&
-		    libwacom_match_get_vendor_id(paired) == vid &&
+		if (paired && libwacom_match_get_vendor_id(paired) == vid &&
 		    libwacom_match_get_product_id(paired) == pid) {
 			rotate = libwacom_is_reversible(dev);
 			break;
@@ -3960,8 +3844,7 @@ out:
 }
 
 static void
-tp_init_left_handed(struct tp_dispatch *tp,
-		    struct evdev_device *device)
+tp_init_left_handed(struct tp_dispatch *tp, struct evdev_device *device)
 {
 	bool want_left_handed = true;
 
@@ -3971,7 +3854,6 @@ tp_init_left_handed(struct tp_dispatch *
 		want_left_handed = false;
 	if (want_left_handed)
 		evdev_init_left_handed(device, tp_change_to_left_handed);
-
 }
 
 struct evdev_dispatch *
diff -pruN 1.28.1-1/src/evdev-mt-touchpad.h 1.30.0-1/src/evdev-mt-touchpad.h
--- 1.28.1-1/src/evdev-mt-touchpad.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-mt-touchpad.h	2025-11-25 03:40:43.000000000 +0000
@@ -36,12 +36,12 @@
 #define TP_MM_TO_DPI_NORMALIZED(mm) (DEFAULT_MOUSE_DPI/25.4 * mm)
 
 enum touchpad_event {
-	TOUCHPAD_EVENT_NONE		= 0,
-	TOUCHPAD_EVENT_MOTION		= bit(0),
-	TOUCHPAD_EVENT_BUTTON_PRESS	= bit(1),
-	TOUCHPAD_EVENT_BUTTON_RELEASE	= bit(2),
-	TOUCHPAD_EVENT_OTHERAXIS	= bit(3),
-	TOUCHPAD_EVENT_TIMESTAMP	= bit(4),
+	TOUCHPAD_EVENT_NONE = 0,
+	TOUCHPAD_EVENT_MOTION = bit(0),
+	TOUCHPAD_EVENT_BUTTON_PRESS = bit(1),
+	TOUCHPAD_EVENT_BUTTON_RELEASE = bit(2),
+	TOUCHPAD_EVENT_OTHERAXIS = bit(3),
+	TOUCHPAD_EVENT_TIMESTAMP = bit(4),
 };
 
 enum touch_state {
@@ -56,7 +56,7 @@ enum touch_state {
 static inline const char *
 touch_state_to_str(enum touch_state state)
 {
-	switch(state) {
+	switch (state) {
 	CASE_RETURN_STRING(TOUCH_NONE);
 	CASE_RETURN_STRING(TOUCH_HOVERING);
 	CASE_RETURN_STRING(TOUCH_BEGIN);
@@ -135,16 +135,16 @@ enum tp_tap_state {
 };
 
 enum tp_tap_touch_state {
-	TAP_TOUCH_STATE_IDLE = 16,	/**< not in touch */
-	TAP_TOUCH_STATE_TOUCH,		/**< touching, may tap */
-	TAP_TOUCH_STATE_DEAD,		/**< exceeded motion/timeout */
+	TAP_TOUCH_STATE_IDLE = 16, /**< not in touch */
+	TAP_TOUCH_STATE_TOUCH,     /**< touching, may tap */
+	TAP_TOUCH_STATE_DEAD,      /**< exceeded motion/timeout */
 };
 
 /* For edge scrolling, so we only care about right and bottom */
 enum tp_edge {
-	EDGE_NONE	= 0,
-	EDGE_RIGHT	= bit(0),
-	EDGE_BOTTOM	= bit(1),
+	EDGE_NONE = 0,
+	EDGE_RIGHT = bit(0),
+	EDGE_BOTTOM = bit(1),
 };
 
 enum tp_edge_scroll_touch_state {
@@ -191,7 +191,7 @@ struct tp_touch {
 	struct tp_dispatch *tp;
 	unsigned int index;
 	enum touch_state state;
-	bool has_ended;				/* TRACKING_ID == -1 */
+	bool has_ended; /* TRACKING_ID == -1 */
 	bool dirty;
 	struct device_coords point;
 	uint64_t initial_time;
@@ -267,7 +267,7 @@ struct tp_touch {
 	struct {
 		enum touch_palm_state state;
 		struct device_coords first; /* first coordinates if is_palm == true */
-		uint64_t time; /* first timestamp if is_palm == true */
+		uint64_t time;              /* first timestamp if is_palm == true */
 	} palm;
 
 	struct {
@@ -281,19 +281,19 @@ struct tp_touch {
 };
 
 enum suspend_trigger {
-	SUSPEND_NO_FLAG         = 0x0,
-	SUSPEND_EXTERNAL_MOUSE  = 0x1,
-	SUSPEND_SENDEVENTS      = 0x2,
-	SUSPEND_LID             = 0x4,
-	SUSPEND_TABLET_MODE     = 0x8,
+	SUSPEND_NO_FLAG = 0x0,
+	SUSPEND_EXTERNAL_MOUSE = 0x1,
+	SUSPEND_SENDEVENTS = 0x2,
+	SUSPEND_LID = 0x4,
+	SUSPEND_TABLET_MODE = 0x8,
 };
 
 struct tp_dispatch {
 	struct evdev_dispatch base;
 	struct evdev_device *device;
-	unsigned int nfingers_down;		/* number of fingers down */
-	unsigned int old_nfingers_down;		/* previous no fingers down */
-	unsigned int slot;			/* current slot */
+	unsigned int nfingers_down;     /* number of fingers down */
+	unsigned int old_nfingers_down; /* previous no fingers down */
+	unsigned int slot;              /* current slot */
 	bool has_mt;
 	bool semi_mt;
 
@@ -305,10 +305,10 @@ struct tp_dispatch {
 		struct libinput_timer arbitration_timer;
 	} arbitration;
 
-	unsigned int nactive_slots;		/* number of active slots */
-	unsigned int num_slots;			/* number of slots */
-	unsigned int ntouches;			/* no slots inc. fakes */
-	struct tp_touch *touches;		/* len == ntouches */
+	unsigned int nactive_slots; /* number of active slots */
+	unsigned int num_slots;     /* number of slots */
+	unsigned int ntouches;      /* no slots inc. fakes */
+	struct tp_touch *touches;   /* len == ntouches */
 	/* bit 0: BTN_TOUCH
 	 * bit 1: BTN_TOOL_FINGER
 	 * bit 2: BTN_TOOL_DOUBLETAP
@@ -331,7 +331,7 @@ struct tp_dispatch {
 
 	/* If touch size (either axis) goes above high -> touch down,
 	   if touch size (either axis) goes below low -> touch up */
-	struct  {
+	struct {
 		bool use_touch_size;
 		int high;
 		int low;
@@ -374,31 +374,31 @@ struct tp_dispatch {
 	} gesture;
 
 	struct {
-		bool is_clickpad;		/* true for clickpads */
+		bool is_clickpad; /* true for clickpads */
 		bool has_topbuttons;
-		bool use_clickfinger;		/* number of fingers decides button number */
+		bool use_clickfinger; /* number of fingers decides button number */
 		bool click_pending;
 		uint32_t state;
 		uint32_t old_state;
 		struct {
 			double x_scale_coeff;
 			double y_scale_coeff;
-		} motion_dist;			/* for pinned touches */
-		unsigned int active;		/* currently active button, for release event */
-		bool active_is_topbutton;	/* is active a top button? */
+		} motion_dist;        /* for pinned touches */
+		evdev_usage_t active; /* currently active button, for release event */
+		bool active_is_topbutton; /* is active a top button? */
 
 		/* Only used for clickpads. The software button areas are
 		 * always 2 horizontal stripes across the touchpad.
 		 * The buttons are split according to the edge settings.
 		 */
 		struct {
-			int32_t top_edge;	/* in device coordinates */
-			int32_t rightbutton_left_edge; /* in device coordinates */
+			int32_t top_edge;               /* in device coordinates */
+			int32_t rightbutton_left_edge;  /* in device coordinates */
 			int32_t middlebutton_left_edge; /* in device coordinates */
 		} bottom_area;
 
 		struct {
-			int32_t bottom_edge;	/* in device coordinates */
+			int32_t bottom_edge;           /* in device coordinates */
 			int32_t rightbutton_left_edge; /* in device coordinates */
 			int32_t leftbutton_right_edge; /* in device coordinates */
 		} top_area;
@@ -415,8 +415,8 @@ struct tp_dispatch {
 	struct {
 		struct libinput_device_config_scroll_method config_method;
 		enum libinput_config_scroll_method method;
-		int32_t right_edge;		/* in device coordinates */
-		int32_t bottom_edge;		/* in device coordinates */
+		int32_t right_edge;  /* in device coordinates */
+		int32_t bottom_edge; /* in device coordinates */
 		struct {
 			bool h, v;
 		} active;
@@ -436,8 +436,7 @@ struct tp_dispatch {
 		struct libinput_timer timer;
 		enum tp_tap_state state;
 		uint32_t buttons_pressed;
-		uint64_t saved_press_time,
-			 saved_release_time;
+		uint64_t saved_press_time, saved_release_time;
 
 		enum libinput_config_tap_button_map map;
 		enum libinput_config_tap_button_map want_map;
@@ -445,7 +444,8 @@ struct tp_dispatch {
 		bool drag_enabled;
 		enum libinput_config_drag_lock_state drag_lock;
 
-		unsigned int nfingers_down;	/* number of fingers down for tapping (excl. thumb/palm) */
+		unsigned int nfingers_down; /* number of fingers down for tapping (excl.
+					       thumb/palm) */
 	} tap;
 
 	struct {
@@ -458,9 +458,9 @@ struct tp_dispatch {
 		struct libinput_device_config_dwtp config;
 		bool dwtp_enabled;
 
-		int32_t right_edge;		/* in device coordinates */
-		int32_t left_edge;		/* in device coordinates */
-		int32_t upper_edge;		/* in device coordinates */
+		int32_t right_edge; /* in device coordinates */
+		int32_t left_edge;  /* in device coordinates */
+		int32_t upper_edge; /* in device coordinates */
 
 		bool trackpoint_active;
 		struct libinput_event_listener trackpoint_listener;
@@ -554,7 +554,7 @@ struct tp_dispatch {
 	} left_handed;
 };
 
-static inline struct tp_dispatch*
+static inline struct tp_dispatch *
 tp_dispatch(struct evdev_dispatch *dispatch)
 {
 	evdev_verify_dispatch_type(dispatch, DISPATCH_TOUCHPAD);
@@ -565,15 +565,14 @@ tp_dispatch(struct evdev_dispatch *dispa
 #define tp_for_each_touch(_tp, _t) \
 	for (unsigned int _i = 0; _i < (_tp)->ntouches && (_t = &(_tp)->touches[_i]); _i++)
 
-static inline struct libinput*
+static inline struct libinput *
 tp_libinput_context(const struct tp_dispatch *tp)
 {
 	return evdev_libinput_context(tp->device);
 }
 
 static inline struct normalized_coords
-tp_normalize_delta(const struct tp_dispatch *tp,
-		   struct device_float_coords delta)
+tp_normalize_delta(const struct tp_dispatch *tp, struct device_float_coords delta)
 {
 	struct normalized_coords normalized;
 
@@ -584,8 +583,7 @@ tp_normalize_delta(const struct tp_dispa
 }
 
 static inline struct phys_coords
-tp_phys_delta(const struct tp_dispatch *tp,
-	      struct device_float_coords delta)
+tp_phys_delta(const struct tp_dispatch *tp, struct device_float_coords delta)
 {
 	struct phys_coords mm;
 
@@ -600,8 +598,7 @@ tp_phys_delta(const struct tp_dispatch *
  * x-axis' resolution.
  */
 static inline struct device_float_coords
-tp_scale_to_xaxis(const struct tp_dispatch *tp,
-		  struct device_float_coords delta)
+tp_scale_to_xaxis(const struct tp_dispatch *tp, struct device_float_coords delta)
 {
 	struct device_float_coords raw;
 
@@ -633,8 +630,7 @@ bool
 tp_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t);
 
 bool
-tp_touch_active_for_gesture(const struct tp_dispatch *tp,
-			    const struct tp_touch *t);
+tp_touch_active_for_gesture(const struct tp_dispatch *tp, const struct tp_touch *t);
 
 int
 tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time);
@@ -663,13 +659,10 @@ void
 tp_remove_buttons(struct tp_dispatch *tp);
 
 void
-tp_process_button(struct tp_dispatch *tp,
-		  const struct input_event *e,
-		  uint64_t time);
+tp_process_button(struct tp_dispatch *tp, const struct evdev_event *e, uint64_t time);
 
 void
-tp_release_all_buttons(struct tp_dispatch *tp,
-		       uint64_t time);
+tp_release_all_buttons(struct tp_dispatch *tp, uint64_t time);
 
 int
 tp_post_button_events(struct tp_dispatch *tp, uint64_t time);
@@ -678,16 +671,14 @@ void
 tp_button_handle_state(struct tp_dispatch *tp, uint64_t time);
 
 bool
-tp_button_touch_active(const struct tp_dispatch *tp,
-		       const struct tp_touch *t);
+tp_button_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t);
 
 bool
 tp_button_is_inside_softbutton_area(const struct tp_dispatch *tp,
 				    const struct tp_touch *t);
 
 void
-tp_release_all_taps(struct tp_dispatch *tp,
-		    uint64_t now);
+tp_release_all_taps(struct tp_dispatch *tp, uint64_t now);
 
 void
 tp_tap_suspend(struct tp_dispatch *tp, uint64_t time);
@@ -717,8 +708,7 @@ void
 tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time);
 
 int
-tp_edge_scroll_touch_active(const struct tp_dispatch *tp,
-			    const struct tp_touch *t);
+tp_edge_scroll_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t);
 
 uint32_t
 tp_touch_get_edge(const struct tp_dispatch *tp, const struct tp_touch *t);
@@ -742,8 +732,7 @@ void
 tp_gesture_update_finger_state(struct tp_dispatch *tp, uint64_t time);
 
 void
-tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time,
-		       bool ignore_motion);
+tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time, bool ignore_motion);
 
 void
 tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time);
@@ -764,16 +753,13 @@ bool
 tp_thumb_ignored_for_gesture(const struct tp_dispatch *tp, const struct tp_touch *t);
 
 bool
-tp_thumb_ignored_for_tap(const struct tp_dispatch *tp,
-			 const struct tp_touch *t);
+tp_thumb_ignored_for_tap(const struct tp_dispatch *tp, const struct tp_touch *t);
 
 void
 tp_thumb_suppress(struct tp_dispatch *tp, struct tp_touch *t);
 
 void
-tp_thumb_update_touch(struct tp_dispatch *tp,
-		      struct tp_touch *t,
-		      uint64_t time);
+tp_thumb_update_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time);
 
 void
 tp_detect_thumb_while_moving(struct tp_dispatch *tp);
@@ -784,7 +770,7 @@ tp_thumb_update_multifinger(struct tp_di
 void
 tp_init_thumb(struct tp_dispatch *tp);
 
-struct tp_touch*
+struct tp_touch *
 tp_thumb_get_touch(struct tp_dispatch *tp);
 
 void
diff -pruN 1.28.1-1/src/evdev-plugin.c 1.30.0-1/src/evdev-plugin.c
--- 1.28.1-1/src/evdev-plugin.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/evdev-plugin.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "util-macros.h"
+#include "util-mem.h"
+
+#include "evdev-plugin.h"
+#include "evdev.h"
+
+static inline void
+evdev_process_frame(struct evdev_device *device,
+		    struct evdev_frame *frame,
+		    uint64_t time)
+{
+	struct evdev_dispatch *dispatch = device->dispatch;
+
+	libinput_timer_flush(evdev_libinput_context(device), time);
+
+	dispatch->interface->process(dispatch, device, frame, time);
+}
+
+static inline void
+evdev_device_dispatch_frame(struct libinput_plugin *plugin,
+			    struct libinput_device *libinput_device,
+			    struct evdev_frame *frame)
+{
+	struct evdev_device *device = evdev_device(libinput_device);
+	uint64_t time = evdev_frame_get_time(frame);
+	evdev_process_frame(device, frame, time);
+
+	/* Discard event to make the plugin system aware we're done */
+	evdev_frame_reset(frame);
+}
+
+static void
+evdev_plugin_device_added(struct libinput_plugin *plugin,
+			  struct libinput_device *device)
+
+{
+	libinput_plugin_enable_device_event_frame(plugin, device, true);
+}
+
+static const struct libinput_plugin_interface interface = {
+	.run = NULL,
+	.destroy = NULL,
+	.device_new = NULL,
+	.device_ignored = NULL,
+	.device_added = evdev_plugin_device_added,
+	.device_removed = NULL,
+	.evdev_frame = evdev_device_dispatch_frame,
+};
+
+void
+libinput_evdev_dispatch_plugin(struct libinput *libinput)
+{
+	_unref_(libinput_plugin) *p =
+		libinput_plugin_new(libinput, "evdev", &interface, NULL);
+}
diff -pruN 1.28.1-1/src/evdev-plugin.h 1.30.0-1/src/evdev-plugin.h
--- 1.28.1-1/src/evdev-plugin.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/evdev-plugin.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,32 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include "libinput-plugin.h"
+#include "libinput.h"
+
+void
+libinput_evdev_dispatch_plugin(struct libinput *libinput);
diff -pruN 1.28.1-1/src/evdev-tablet-pad-leds.c 1.30.0-1/src/evdev-tablet-pad-leds.c
--- 1.28.1-1/src/evdev-tablet-pad-leds.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-tablet-pad-leds.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,12 +23,12 @@
 
 #include "config.h"
 
-#include <limits.h>
 #include <fcntl.h>
+#include <limits.h>
 
 #include "evdev-tablet-pad.h"
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 #include <libwacom/libwacom.h>
 #endif
 
@@ -68,7 +68,7 @@ struct pad_mode_led {
 };
 
 static inline void
-pad_mode_toggle_button_destroy(struct pad_mode_toggle_button* button)
+pad_mode_toggle_button_destroy(struct pad_mode_toggle_button *button)
 {
 	list_remove(&button->link);
 	free(button);
@@ -77,7 +77,7 @@ pad_mode_toggle_button_destroy(struct pa
 static inline int
 pad_led_group_get_mode(struct pad_led_group *group)
 {
-	char buf[4] = {0};
+	char buf[4] = { 0 };
 	int rc;
 	unsigned int brightness;
 	struct pad_mode_led *led;
@@ -106,8 +106,7 @@ pad_led_group_get_mode(struct pad_led_gr
 }
 
 static inline void
-pad_led_destroy(struct libinput *libinput,
-		struct pad_mode_led *led)
+pad_led_destroy(struct libinput *libinput, struct pad_mode_led *led)
 {
 	list_remove(&led->link);
 	if (led->brightness_fd != -1)
@@ -115,7 +114,7 @@ pad_led_destroy(struct libinput *libinpu
 	free(led);
 }
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 static inline struct pad_mode_led *
 pad_led_new(struct libinput *libinput, const char *prefix, int group, int mode)
 {
@@ -130,12 +129,7 @@ pad_led_new(struct libinput *libinput, c
 
 	/* /sys/devices/..../input1235/input1235::wacom-0.1/brightness,
 	 * where 0 and 1 are group and mode index. */
-	rc = snprintf(path,
-		      sizeof(path),
-		      "%s%d.%d/brightness",
-		      prefix,
-		      group,
-		      mode);
+	rc = snprintf(path, sizeof(path), "%s%d.%d/brightness", prefix, group, mode);
 	if (rc == -1)
 		goto error;
 
@@ -174,9 +168,7 @@ pad_led_group_destroy(struct libinput_ta
 }
 
 static struct pad_led_group *
-pad_group_new(struct pad_dispatch *pad,
-		    unsigned int group_index,
-		    int num_modes)
+pad_group_new(struct pad_dispatch *pad, unsigned int group_index, int num_modes)
 {
 	struct pad_led_group *group;
 
@@ -206,7 +198,7 @@ pad_get_mode_group(struct pad_dispatch *
 	return NULL;
 }
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 static inline bool
 is_litest_device(struct evdev_device *device)
 {
@@ -258,8 +250,9 @@ pad_led_get_sysfs_base_path(struct evdev
 
 	/* For testing purposes only allow for a base path set through a
 	 * udev rule. We still expect the normal directory hierarchy inside */
-	test_path = udev_device_get_property_value(udev_device,
-						   "LIBINPUT_TEST_TABLET_PAD_SYSFS_PATH");
+	test_path =
+		udev_device_get_property_value(udev_device,
+					       "LIBINPUT_TEST_TABLET_PAD_SYSFS_PATH");
 	if (test_path) {
 		rc = snprintf(path_out, path_out_sz, "%s", test_path);
 		return rc != -1;
@@ -342,17 +335,17 @@ out:
 }
 
 static int
-pad_fetch_group_index(struct pad_dispatch *pad,
-		      WacomDevice *wacom,
-		      int button_index)
+pad_fetch_group_index(struct pad_dispatch *pad, WacomDevice *wacom, int button_index)
 {
 	char btn = 'A' + button_index;
 	WacomButtonFlags flags = libwacom_get_button_flag(wacom, btn);
 	int led_group = libwacom_get_button_led_group(wacom, btn);
 
 	if ((flags & WACOM_BUTTON_MODESWITCH) == 0) {
-		evdev_log_bug_libinput(pad->device,
-				       "Cannot fetch group index for non-mode toggle button %c\n", btn);
+		evdev_log_bug_libinput(
+			pad->device,
+			"Cannot fetch group index for non-mode toggle button %c\n",
+			btn);
 		return -1;
 	}
 
@@ -395,7 +388,7 @@ pad_fetch_group_index(struct pad_dispatc
 #endif
 	}
 
-	return  group_index;
+	return group_index;
 }
 
 static inline int
@@ -416,7 +409,7 @@ pad_find_button_group(struct pad_dispatc
 			continue;
 
 		if ((flags & WACOM_BUTTON_DIRECTION) ==
-			(button_flags & WACOM_BUTTON_DIRECTION))
+		    (button_flags & WACOM_BUTTON_DIRECTION))
 			return pad_fetch_group_index(pad, wacom, i);
 	}
 
@@ -428,11 +421,16 @@ pad_button_target_mode(WacomDevice *waco
 {
 #ifdef HAVE_LIBWACOM_BUTTON_MODESWITCH_MODE
 	switch (libwacom_get_button_modeswitch_mode(wacom, button)) {
-		case WACOM_MODE_SWITCH_NEXT: return MODE_NEXT;
-		case WACOM_MODE_SWITCH_0: return MODE_0;
-		case WACOM_MODE_SWITCH_1: return MODE_1;
-		case WACOM_MODE_SWITCH_2: return MODE_2;
-		case WACOM_MODE_SWITCH_3: return MODE_3;
+	case WACOM_MODE_SWITCH_NEXT:
+		return MODE_NEXT;
+	case WACOM_MODE_SWITCH_0:
+		return MODE_0;
+	case WACOM_MODE_SWITCH_1:
+		return MODE_1;
+	case WACOM_MODE_SWITCH_2:
+		return MODE_2;
+	case WACOM_MODE_SWITCH_3:
+		return MODE_3;
 	}
 #endif
 	return MODE_NEXT;
@@ -460,9 +458,10 @@ pad_init_leds_from_libwacom(struct pad_d
 		if ((flags & WACOM_BUTTON_MODESWITCH) == 0)
 			continue;
 
-		enum pad_toggle_button_target_mode target_mode = pad_button_target_mode(wacom, btn);
+		enum pad_toggle_button_target_mode target_mode =
+			pad_button_target_mode(wacom, btn);
 
-                int group_index = pad_fetch_group_index(pad, wacom, b);
+		int group_index = pad_fetch_group_index(pad, wacom, b);
 		switch (flags & WACOM_BUTTON_MODESWITCH) {
 		case WACOM_BUTTON_RING_MODESWITCH:
 			nmodes = libwacom_get_ring_num_modes(wacom);
@@ -492,22 +491,36 @@ pad_init_leds_from_libwacom(struct pad_d
 			break;
 #endif
 		default:
-			evdev_log_error(pad->device,
-					"unable to init pad mode group: button %c has multiple modeswitch flags 0x%x\n",
-					btn, flags);
+			evdev_log_error(
+				pad->device,
+				"unable to init pad mode group: button %c has multiple modeswitch flags 0x%x\n",
+				btn,
+				flags);
 			goto out;
 		}
 		have_status_led = libwacom_get_button_led_group(wacom, btn) >= 0;
 		if (nmodes > 1) {
-			struct libinput_tablet_pad_mode_group *group = pad_get_mode_group(pad, group_index);
+			struct libinput_tablet_pad_mode_group *group =
+				pad_get_mode_group(pad, group_index);
 			if (!group) {
-				rc = pad_add_mode_group(pad, device, group_index, nmodes, b, target_mode,
-							ring_mask, strip_mask, dial_mask,
+				rc = pad_add_mode_group(pad,
+							device,
+							group_index,
+							nmodes,
+							b,
+							target_mode,
+							ring_mask,
+							strip_mask,
+							dial_mask,
 							have_status_led);
 			} else {
-				struct pad_led_group *led_group = (struct pad_led_group *)group;
-				/* Multiple toggle buttons (Wacom MobileStudio Pro 16) */
-				rc = pad_led_group_add_toggle_button(led_group, b, target_mode);
+				struct pad_led_group *led_group =
+					(struct pad_led_group *)group;
+				/* Multiple toggle buttons (Wacom MobileStudio Pro 16)
+				 */
+				rc = pad_led_group_add_toggle_button(led_group,
+								     b,
+								     target_mode);
 				if (rc < 0)
 					goto out;
 			}
@@ -525,7 +538,7 @@ pad_init_leds_from_libwacom(struct pad_d
 		char btn = 'A' + b;
 		WacomButtonFlags flags = libwacom_get_button_flag(wacom, btn);
 
-		if ((flags & WACOM_BUTTON_MODESWITCH))
+		if (flags & WACOM_BUTTON_MODESWITCH)
 			continue;
 
 		int group_index = pad_find_button_group(pad, wacom, b, flags);
@@ -537,12 +550,14 @@ pad_init_leds_from_libwacom(struct pad_d
 			goto out;
 		}
 
-		struct libinput_tablet_pad_mode_group *group = pad_get_mode_group(pad, group_index);
+		struct libinput_tablet_pad_mode_group *group =
+			pad_get_mode_group(pad, group_index);
 		if (!group) {
-			evdev_log_bug_libinput(pad->device,
-					       "Failed to find group %d for button %i\n",
-					       group_index,
-					       b);
+			evdev_log_bug_libinput(
+				pad->device,
+				"Failed to find group %d for button %i\n",
+				group_index,
+				b);
 			rc = -EINVAL;
 			goto out;
 		}
@@ -589,9 +604,7 @@ pad_init_fallback_group(struct pad_dispa
 }
 
 int
-pad_init_leds(struct pad_dispatch *pad,
-	      struct evdev_device *device,
-	      WacomDevice *wacom)
+pad_init_leds(struct pad_dispatch *pad, struct evdev_device *device, WacomDevice *wacom)
 {
 	int rc = 1;
 
@@ -605,7 +618,7 @@ pad_init_leds(struct pad_dispatch *pad,
 	}
 
 	/* If libwacom fails, we init one fallback group anyway */
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	rc = pad_init_leds_from_libwacom(pad, device, wacom);
 #endif
 	if (rc != 0)
@@ -628,7 +641,7 @@ pad_button_update_mode(struct libinput_t
 		       unsigned int button_index,
 		       enum libinput_button_state state)
 {
-	struct pad_led_group *group = (struct pad_led_group*)g;
+	struct pad_led_group *group = (struct pad_led_group *)g;
 	int rc = -ENODEV;
 
 	if (state != LIBINPUT_BUTTON_STATE_PRESSED)
@@ -660,7 +673,7 @@ pad_button_update_mode(struct libinput_t
 int
 evdev_device_tablet_pad_get_num_mode_groups(struct evdev_device *device)
 {
-	struct pad_dispatch *pad = (struct pad_dispatch*)device->dispatch;
+	struct pad_dispatch *pad = (struct pad_dispatch *)device->dispatch;
 	struct libinput_tablet_pad_mode_group *group;
 	int num_groups = 0;
 
@@ -674,16 +687,14 @@ evdev_device_tablet_pad_get_num_mode_gro
 }
 
 struct libinput_tablet_pad_mode_group *
-evdev_device_tablet_pad_get_mode_group(struct evdev_device *device,
-				       unsigned int index)
+evdev_device_tablet_pad_get_mode_group(struct evdev_device *device, unsigned int index)
 {
-	struct pad_dispatch *pad = (struct pad_dispatch*)device->dispatch;
+	struct pad_dispatch *pad = (struct pad_dispatch *)device->dispatch;
 
 	if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
 		return NULL;
 
-	if (index >=
-	    (unsigned int)evdev_device_tablet_pad_get_num_mode_groups(device))
+	if (index >= (unsigned int)evdev_device_tablet_pad_get_num_mode_groups(device))
 		return NULL;
 
 	return pad_get_mode_group(pad, index);
diff -pruN 1.28.1-1/src/evdev-tablet-pad.c 1.30.0-1/src/evdev-tablet-pad.c
--- 1.28.1-1/src/evdev-tablet-pad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-tablet-pad.c	2025-11-25 03:40:43.000000000 +0000
@@ -22,24 +22,25 @@
  */
 
 #include "config.h"
-#include "evdev-tablet-pad.h"
-#include "util-input-event.h"
 
 #include <assert.h>
 #include <stdbool.h>
 #include <string.h>
 
-#if HAVE_LIBWACOM
+#include "util-input-event.h"
+
+#include "evdev-tablet-pad.h"
+
+#ifdef HAVE_LIBWACOM
 #include <libwacom/libwacom.h>
 #endif
 
-#define pad_set_status(pad_,s_) (pad_)->status |= (s_)
-#define pad_unset_status(pad_,s_) (pad_)->status &= ~(s_)
-#define pad_has_status(pad_,s_) (!!((pad_)->status & (s_)))
+#define pad_set_status(pad_, s_) (pad_)->status |= (s_)
+#define pad_unset_status(pad_, s_) (pad_)->status &= ~(s_)
+#define pad_has_status(pad_, s_) (!!((pad_)->status & (s_)))
 
 static void
-pad_get_buttons_pressed(struct pad_dispatch *pad,
-			struct button_state *buttons)
+pad_get_buttons_pressed(struct pad_dispatch *pad, struct button_state *buttons)
 {
 	struct button_state *state = &pad->button_state;
 	struct button_state *prev_state = &pad->prev_button_state;
@@ -50,8 +51,7 @@ pad_get_buttons_pressed(struct pad_dispa
 }
 
 static void
-pad_get_buttons_released(struct pad_dispatch *pad,
-			 struct button_state *buttons)
+pad_get_buttons_released(struct pad_dispatch *pad, struct button_state *buttons)
 {
 	struct button_state *state = &pad->button_state;
 	struct button_state *prev_state = &pad->prev_button_state;
@@ -62,8 +62,7 @@ pad_get_buttons_released(struct pad_disp
 }
 
 static inline bool
-pad_button_is_down(const struct pad_dispatch *pad,
-		   uint32_t button)
+pad_button_is_down(const struct pad_dispatch *pad, uint32_t button)
 {
 	return bit_is_set(pad->button_state.bits, button);
 }
@@ -82,17 +81,16 @@ pad_any_button_down(const struct pad_dis
 }
 
 static inline void
-pad_button_set_down(struct pad_dispatch *pad,
-		    uint32_t button,
-		    bool is_down)
+pad_button_set_down(struct pad_dispatch *pad, evdev_usage_t button, bool is_down)
 {
 	struct button_state *state = &pad->button_state;
+	unsigned int code = evdev_usage_code(button);
 
 	if (is_down) {
-		set_bit(state->bits, button);
+		set_bit(state->bits, code);
 		pad_set_status(pad, PAD_BUTTONS_PRESSED);
 	} else {
-		clear_bit(state->bits, button);
+		clear_bit(state->bits, code);
 		pad_set_status(pad, PAD_BUTTONS_RELEASED);
 	}
 }
@@ -100,35 +98,35 @@ pad_button_set_down(struct pad_dispatch
 static void
 pad_process_relative(struct pad_dispatch *pad,
 		     struct evdev_device *device,
-		     struct input_event *e,
+		     struct evdev_event *e,
 		     uint64_t time)
 {
-	switch (e->code) {
-	case REL_DIAL:
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_REL_DIAL:
 		pad->dials.dial1 = e->value * 120;
 		pad->changed_axes |= PAD_AXIS_DIAL1;
 		pad_set_status(pad, PAD_AXES_UPDATED);
 		break;
-	case REL_WHEEL:
+	case EVDEV_REL_WHEEL:
 		if (!pad->dials.has_hires_dial) {
 			pad->dials.dial1 = -1 * e->value * 120;
 			pad->changed_axes |= PAD_AXIS_DIAL1;
 			pad_set_status(pad, PAD_AXES_UPDATED);
 		}
 		break;
-	case REL_HWHEEL:
+	case EVDEV_REL_HWHEEL:
 		if (!pad->dials.has_hires_dial) {
 			pad->dials.dial2 = e->value * 120;
 			pad->changed_axes |= PAD_AXIS_DIAL2;
 			pad_set_status(pad, PAD_AXES_UPDATED);
 		}
 		break;
-	case REL_WHEEL_HI_RES:
+	case EVDEV_REL_WHEEL_HI_RES:
 		pad->dials.dial1 = -1 * e->value;
 		pad->changed_axes |= PAD_AXIS_DIAL1;
 		pad_set_status(pad, PAD_AXES_UPDATED);
 		break;
-	case REL_HWHEEL_HI_RES:
+	case EVDEV_REL_HWHEEL_HI_RES:
 		pad->dials.dial2 = e->value;
 		pad->changed_axes |= PAD_AXIS_DIAL2;
 		pad_set_status(pad, PAD_AXES_UPDATED);
@@ -136,7 +134,7 @@ pad_process_relative(struct pad_dispatch
 	default:
 		evdev_log_info(device,
 			       "Unhandled EV_REL event code %#x\n",
-			       e->code);
+			       evdev_usage_as_uint32_t(e->usage));
 		break;
 	}
 }
@@ -144,13 +142,14 @@ pad_process_relative(struct pad_dispatch
 static void
 pad_update_changed_axis(struct pad_dispatch *pad,
 			enum pad_axes axis,
-			const struct input_event *e)
+			const struct evdev_event *e)
 {
 	if (pad->changed_axes & axis) {
-		evdev_log_bug_kernel_ratelimit(pad->device,
-					       &pad->duplicate_abs_limit,
-					       "Multiple EV_ABS %s events in the same SYN_REPORT\n",
-					       libevdev_event_code_get_name(EV_ABS, e->code));
+		evdev_log_bug_kernel_ratelimit(
+			pad->device,
+			&pad->duplicate_abs_limit,
+			"Multiple EV_ABS %s events in the same SYN_REPORT\n",
+			evdev_event_get_code_name(e));
 
 		/* Special heuristics probably good enough:
 		 * if we get multiple EV_ABS in the same SYN_REPORT
@@ -172,17 +171,25 @@ pad_update_changed_axis(struct pad_dispa
 static void
 pad_process_absolute(struct pad_dispatch *pad,
 		     struct evdev_device *device,
-		     struct input_event *e,
+		     struct evdev_event *e,
 		     uint64_t time)
 {
 	enum pad_axes axis = PAD_AXIS_NONE;
 
-	switch (e->code) {
-	case ABS_WHEEL:		axis = PAD_AXIS_RING1; break;
-	case ABS_THROTTLE:	axis = PAD_AXIS_RING2; break;
-	case ABS_RX:		axis = PAD_AXIS_STRIP1; break;
-	case ABS_RY:		axis = PAD_AXIS_STRIP2; break;
-	case ABS_MISC:
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_ABS_WHEEL:
+		axis = PAD_AXIS_RING1;
+		break;
+	case EVDEV_ABS_THROTTLE:
+		axis = PAD_AXIS_RING2;
+		break;
+	case EVDEV_ABS_RX:
+		axis = PAD_AXIS_STRIP1;
+		break;
+	case EVDEV_ABS_RY:
+		axis = PAD_AXIS_STRIP2;
+		break;
+	case EVDEV_ABS_MISC:
 		/* The wacom driver always sends a 0 axis event on finger
 		   up, but we also get an ABS_MISC 15 on touch down and
 		   ABS_MISC 0 on touch up, on top of the actual event. This
@@ -202,7 +209,7 @@ pad_process_absolute(struct pad_dispatch
 	default:
 		evdev_log_info(device,
 			       "Unhandled EV_ABS event code %#x\n",
-			       e->code);
+			       evdev_usage_as_uint32_t(e->usage));
 		break;
 	}
 
@@ -233,8 +240,7 @@ normalize_wacom_strip(const struct input
 	/* strip axes don't use a proper value, they just shift the bit left
 	 * for each position. 0 isn't a real value either, it's only sent on
 	 * finger release */
-	double min = 0,
-	       max = log2(absinfo->maximum);
+	double min = 0, max = log2(absinfo->maximum);
 	double range = max - min;
 	double value = (log2(absinfo->value) - min) / range;
 
@@ -292,49 +298,58 @@ pad_handle_strip(struct pad_dispatch *pa
 }
 
 static inline struct libinput_tablet_pad_mode_group *
-pad_dial_get_mode_group(struct pad_dispatch *pad,
-			unsigned int dial)
+pad_dial_get_mode_group(struct pad_dispatch *pad, unsigned int dial)
 {
 	struct libinput_tablet_pad_mode_group *group;
 
 	list_for_each(group, &pad->modes.mode_group_list, link) {
+		assert(group != NULL); /* for clang-tidy */
 		if (libinput_tablet_pad_mode_group_has_dial(group, dial))
 			return group;
 	}
 
-	assert(!"Unable to find dial mode group");
+	evdev_log_bug_libinput_ratelimit(pad->device,
+					 &pad->modes.group_not_found,
+					 "Unable to find mode group for dial %d",
+					 dial);
 
 	return NULL;
 }
 
 static inline struct libinput_tablet_pad_mode_group *
-pad_ring_get_mode_group(struct pad_dispatch *pad,
-			unsigned int ring)
+pad_ring_get_mode_group(struct pad_dispatch *pad, unsigned int ring)
 {
 	struct libinput_tablet_pad_mode_group *group;
 
 	list_for_each(group, &pad->modes.mode_group_list, link) {
+		assert(group != NULL); /* for clang-tidy */
 		if (libinput_tablet_pad_mode_group_has_ring(group, ring))
 			return group;
 	}
 
-	assert(!"Unable to find ring mode group");
+	evdev_log_bug_libinput_ratelimit(pad->device,
+					 &pad->modes.group_not_found,
+					 "Unable to find mode group for ring %d",
+					 ring);
 
 	return NULL;
 }
 
 static inline struct libinput_tablet_pad_mode_group *
-pad_strip_get_mode_group(struct pad_dispatch *pad,
-			unsigned int strip)
+pad_strip_get_mode_group(struct pad_dispatch *pad, unsigned int strip)
 {
 	struct libinput_tablet_pad_mode_group *group;
 
 	list_for_each(group, &pad->modes.mode_group_list, link) {
+		assert(group != NULL); /* for clang-tidy */
 		if (libinput_tablet_pad_mode_group_has_strip(group, strip))
 			return group;
 	}
 
-	assert(!"Unable to find strip mode group");
+	evdev_log_bug_libinput_ratelimit(pad->device,
+					 &pad->modes.group_not_found,
+					 "Unable to find mode group for strip %d",
+					 strip);
 
 	return NULL;
 }
@@ -359,20 +374,14 @@ pad_check_notify_axes(struct pad_dispatc
 	 * so we can't set a source */
 	if (pad->changed_axes & PAD_AXIS_DIAL1) {
 		group = pad_dial_get_mode_group(pad, 0);
-		tablet_pad_notify_dial(base,
-				       time,
-				       0,
-				       pad->dials.dial1,
-				       group);
+		if (group)
+			tablet_pad_notify_dial(base, time, 0, pad->dials.dial1, group);
 	}
 
 	if (pad->changed_axes & PAD_AXIS_DIAL2) {
 		group = pad_dial_get_mode_group(pad, 1);
-		tablet_pad_notify_dial(base,
-				       time,
-				       1,
-				       pad->dials.dial2,
-				       group);
+		if (group)
+			tablet_pad_notify_dial(base, time, 1, pad->dials.dial2, group);
 	}
 
 	if (pad->changed_axes & PAD_AXIS_RING1) {
@@ -381,12 +390,13 @@ pad_check_notify_axes(struct pad_dispatc
 			value = -1.0;
 
 		group = pad_ring_get_mode_group(pad, 0);
-		tablet_pad_notify_ring(base,
-				       time,
-				       0,
-				       value,
-				       LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER,
-				       group);
+		if (group)
+			tablet_pad_notify_ring(base,
+					       time,
+					       0,
+					       value,
+					       LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER,
+					       group);
 	}
 
 	if (pad->changed_axes & PAD_AXIS_RING2) {
@@ -395,12 +405,13 @@ pad_check_notify_axes(struct pad_dispatc
 			value = -1.0;
 
 		group = pad_ring_get_mode_group(pad, 1);
-		tablet_pad_notify_ring(base,
-				       time,
-				       1,
-				       value,
-				       LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER,
-				       group);
+		if (group)
+			tablet_pad_notify_ring(base,
+					       time,
+					       1,
+					       value,
+					       LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER,
+					       group);
 	}
 
 	if (pad->changed_axes & PAD_AXIS_STRIP1) {
@@ -409,12 +420,13 @@ pad_check_notify_axes(struct pad_dispatc
 			value = -1.0;
 
 		group = pad_strip_get_mode_group(pad, 0);
-		tablet_pad_notify_strip(base,
-					time,
-					0,
-					value,
-					LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
-					group);
+		if (group)
+			tablet_pad_notify_strip(base,
+						time,
+						0,
+						value,
+						LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
+						group);
 	}
 
 	if (pad->changed_axes & PAD_AXIS_STRIP2) {
@@ -423,12 +435,13 @@ pad_check_notify_axes(struct pad_dispatc
 			value = -1.0;
 
 		group = pad_strip_get_mode_group(pad, 1);
-		tablet_pad_notify_strip(base,
-					time,
-					1,
-					value,
-					LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
-					group);
+		if (group)
+			tablet_pad_notify_strip(base,
+						time,
+						1,
+						value,
+						LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
+						group);
 	}
 
 	pad->changed_axes = PAD_AXIS_NONE;
@@ -438,22 +451,20 @@ pad_check_notify_axes(struct pad_dispatc
 static void
 pad_process_key(struct pad_dispatch *pad,
 		struct evdev_device *device,
-		struct input_event *e,
+		struct evdev_event *e,
 		uint64_t time)
 {
-	uint32_t button = e->code;
 	uint32_t is_press = e->value != 0;
 
 	/* ignore kernel key repeat */
 	if (e->value == 2)
 		return;
 
-	pad_button_set_down(pad, button, is_press);
+	pad_button_set_down(pad, e->usage, is_press);
 }
 
 static inline struct libinput_tablet_pad_mode_group *
-pad_button_get_mode_group(struct pad_dispatch *pad,
-			  unsigned int button)
+pad_button_get_mode_group(struct pad_dispatch *pad, unsigned int button)
 {
 	struct libinput_tablet_pad_mode_group *group;
 
@@ -503,11 +514,12 @@ pad_notify_button_mask(struct pad_dispat
 
 				group = pad_button_get_mode_group(pad, button);
 				pad_button_update_mode(group, button, state);
-				tablet_pad_notify_button(base,
-							 time,
-							 button,
-							 state,
-							 group);
+				tablet_pad_notify_button(
+					base,
+					time,
+					pad_button_from_uint32_t(button),
+					state,
+					group);
 			} else if (map_is_key(map)) {
 				uint32_t key = map_value(map);
 
@@ -541,7 +553,7 @@ pad_notify_buttons(struct pad_dispatch *
 static void
 pad_change_to_left_handed(struct evdev_device *device)
 {
-	struct pad_dispatch *pad = (struct pad_dispatch*)device->dispatch;
+	struct pad_dispatch *pad = (struct pad_dispatch *)device->dispatch;
 
 	if (device->left_handed.enabled == device->left_handed.want_enabled)
 		return;
@@ -553,9 +565,7 @@ pad_change_to_left_handed(struct evdev_d
 }
 
 static void
-pad_flush(struct pad_dispatch *pad,
-	  struct evdev_device *device,
-	  uint64_t time)
+pad_flush(struct pad_dispatch *pad, struct evdev_device *device, uint64_t time)
 {
 	if (pad_has_status(pad, PAD_AXES_UPDATED)) {
 		pad_check_notify_axes(pad, device, time);
@@ -563,40 +573,33 @@ pad_flush(struct pad_dispatch *pad,
 	}
 
 	if (pad_has_status(pad, PAD_BUTTONS_RELEASED)) {
-		pad_notify_buttons(pad,
-				   device,
-				   time,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+		pad_notify_buttons(pad, device, time, LIBINPUT_BUTTON_STATE_RELEASED);
 		pad_unset_status(pad, PAD_BUTTONS_RELEASED);
 
 		pad_change_to_left_handed(device);
 	}
 
 	if (pad_has_status(pad, PAD_BUTTONS_PRESSED)) {
-		pad_notify_buttons(pad,
-				   device,
-				   time,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+		pad_notify_buttons(pad, device, time, LIBINPUT_BUTTON_STATE_PRESSED);
 		pad_unset_status(pad, PAD_BUTTONS_PRESSED);
 	}
 
 	/* Update state */
-	memcpy(&pad->prev_button_state,
-	       &pad->button_state,
-	       sizeof(pad->button_state));
+	memcpy(&pad->prev_button_state, &pad->button_state, sizeof(pad->button_state));
 	pad->dials.dial1 = 0;
 	pad->dials.dial2 = 0;
 }
 
 static void
-pad_process(struct evdev_dispatch *dispatch,
-	    struct evdev_device *device,
-	    struct input_event *e,
-	    uint64_t time)
+pad_process_event(struct evdev_dispatch *dispatch,
+		  struct evdev_device *device,
+		  struct evdev_event *e,
+		  uint64_t time)
 {
 	struct pad_dispatch *pad = pad_dispatch(dispatch);
 
-	switch (e->type) {
+	uint16_t type = evdev_event_type(e);
+	switch (type) {
 	case EV_REL:
 		pad_process_relative(pad, device, e, time);
 		break;
@@ -616,23 +619,37 @@ pad_process(struct evdev_dispatch *dispa
 	default:
 		evdev_log_error(device,
 				"Unexpected event type %s (%#x)\n",
-				libevdev_event_type_get_name(e->type),
-				e->type);
+				libevdev_event_type_get_name(type),
+				evdev_usage_as_uint32_t(e->usage));
 		break;
 	}
 }
 
 static void
-pad_suspend(struct evdev_dispatch *dispatch,
-	    struct evdev_device *device)
+pad_process(struct evdev_dispatch *dispatch,
+	    struct evdev_device *device,
+	    struct evdev_frame *frame,
+	    uint64_t time)
+{
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+
+	for (size_t i = 0; i < nevents; i++) {
+		pad_process_event(dispatch, device, &events[i], time);
+	}
+}
+
+static void
+pad_suspend(struct evdev_dispatch *dispatch, struct evdev_device *device)
 {
 	struct pad_dispatch *pad = pad_dispatch(dispatch);
 	struct libinput *libinput = pad_libinput_context(pad);
-	unsigned int code;
 
-	for (code = KEY_ESC; code < KEY_CNT; code++) {
-		if (pad_button_is_down(pad, code))
-			pad_button_set_down(pad, code, false);
+	for (evdev_usage_t usage = evdev_usage_from(EVDEV_KEY_ESC);
+	     evdev_usage_le(usage, EVDEV_KEY_MAX);
+	     usage = evdev_usage_next(usage)) {
+		if (pad_button_is_down(pad, evdev_usage_code(usage)))
+			pad_button_set_down(pad, usage, false);
 	}
 
 	pad_flush(pad, device, libinput_now(libinput));
@@ -668,7 +685,7 @@ pad_init_buttons_from_libwacom(struct pa
 			       WacomDevice *tablet)
 {
 	bool rc = false;
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 
 	if (tablet) {
 		int num_buttons = libwacom_get_num_buttons(tablet);
@@ -691,8 +708,7 @@ pad_init_buttons_from_libwacom(struct pa
 }
 
 static void
-pad_init_buttons_from_kernel(struct pad_dispatch *pad,
-			       struct evdev_device *device)
+pad_init_buttons_from_kernel(struct pad_dispatch *pad, struct evdev_device *device)
 {
 	unsigned int code;
 	int map = 0;
@@ -757,17 +773,15 @@ pad_init_buttons(struct pad_dispatch *pa
 }
 
 static void
-pad_init_left_handed(struct evdev_device *device,
-		     WacomDevice *wacom)
+pad_init_left_handed(struct evdev_device *device, WacomDevice *wacom)
 {
 	bool has_left_handed = true;
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	has_left_handed = !wacom || libwacom_is_reversible(wacom);
 #endif
 	if (has_left_handed)
-		evdev_init_left_handed(device,
-				       pad_change_to_left_handed);
+		evdev_init_left_handed(device, pad_change_to_left_handed);
 }
 
 static int
@@ -776,7 +790,7 @@ pad_init(struct pad_dispatch *pad, struc
 	int rc = 1;
 	struct libinput *li = evdev_libinput_context(device);
 	WacomDevice *wacom = NULL;
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	WacomDeviceDatabase *db = libinput_libwacom_ref(li);
 	if (db) {
 		char event_path[64];
@@ -786,17 +800,19 @@ pad_init(struct pad_dispatch *pad, struc
 			 evdev_device_get_sysname(device));
 		wacom = libwacom_new_from_path(db, event_path, WFALLBACK_NONE, NULL);
 		if (!wacom) {
-			wacom = libwacom_new_from_usbid(db,
-							evdev_device_get_id_vendor(device),
-							evdev_device_get_id_product(device),
-							NULL);
+			wacom = libwacom_new_from_usbid(
+				db,
+				evdev_device_get_id_vendor(device),
+				evdev_device_get_id_product(device),
+				NULL);
 		}
 		if (!wacom) {
-			evdev_log_info(device,
-				       "device \"%s\" (%04x:%04x) is not known to libwacom\n",
-				       evdev_device_get_name(device),
-				       evdev_device_get_id_vendor(device),
-				       evdev_device_get_id_product(device));
+			evdev_log_info(
+				device,
+				"device \"%s\" (%04x:%04x) is not known to libwacom\n",
+				evdev_device_get_name(device),
+				evdev_device_get_id_vendor(device),
+				evdev_device_get_id_product(device));
 		}
 	}
 #endif
@@ -806,15 +822,18 @@ pad_init(struct pad_dispatch *pad, struc
 	pad->device = device;
 	pad->status = PAD_NONE;
 	pad->changed_axes = PAD_AXIS_NONE;
+	ratelimit_init(&pad->modes.group_not_found, h2us(1), 3);
 
-        /* We expect the kernel to either give us both axes as hires or neither.
+	/* We expect the kernel to either give us both axes as hires or neither.
 	 * Getting one is a kernel bug we don't need to care about */
-        pad->dials.has_hires_dial = libevdev_has_event_code(device->evdev, EV_REL, REL_WHEEL_HI_RES) ||
-				    libevdev_has_event_code(device->evdev, EV_REL, REL_HWHEEL_HI_RES);
+	pad->dials.has_hires_dial =
+		libevdev_has_event_code(device->evdev, EV_REL, REL_WHEEL_HI_RES) ||
+		libevdev_has_event_code(device->evdev, EV_REL, REL_HWHEEL_HI_RES);
 
 	if (libevdev_has_event_code(device->evdev, EV_REL, REL_WHEEL) &&
 	    libevdev_has_event_code(device->evdev, EV_REL, REL_DIAL)) {
-		log_bug_libinput(li, "Unsupported combination REL_DIAL and REL_WHEEL\n");
+		log_bug_libinput(li,
+				 "Unsupported combination REL_DIAL and REL_WHEEL\n");
 	}
 
 	pad_init_buttons(pad, device, wacom);
@@ -825,7 +844,7 @@ pad_init(struct pad_dispatch *pad, struc
 	/* at most 5 "Multiple EV_ABS events" log messages per hour */
 	ratelimit_init(&pad->duplicate_abs_limit, s2us(60 * 60), 5);
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	if (wacom)
 		libwacom_destroy(wacom);
 	if (db)
@@ -834,52 +853,6 @@ pad_init(struct pad_dispatch *pad, struc
 	return rc;
 }
 
-static uint32_t
-pad_sendevents_get_modes(struct libinput_device *device)
-{
-	return LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
-}
-
-static enum libinput_config_status
-pad_sendevents_set_mode(struct libinput_device *device,
-			enum libinput_config_send_events_mode mode)
-{
-	struct evdev_device *evdev = evdev_device(device);
-	struct pad_dispatch *pad = (struct pad_dispatch*)evdev->dispatch;
-
-	if (mode == pad->sendevents.current_mode)
-		return LIBINPUT_CONFIG_STATUS_SUCCESS;
-
-	switch(mode) {
-	case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
-		break;
-	case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
-		pad_suspend(evdev->dispatch, evdev);
-		break;
-	default:
-		return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
-	}
-
-	pad->sendevents.current_mode = mode;
-
-	return LIBINPUT_CONFIG_STATUS_SUCCESS;
-}
-
-static enum libinput_config_send_events_mode
-pad_sendevents_get_mode(struct libinput_device *device)
-{
-	struct evdev_device *evdev = evdev_device(device);
-	struct pad_dispatch *dispatch = (struct pad_dispatch*)evdev->dispatch;
-
-	return dispatch->sendevents.current_mode;
-}
-
-static enum libinput_config_send_events_mode
-pad_sendevents_get_default_mode(struct libinput_device *device)
-{
-	return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
-}
-
 struct evdev_dispatch *
 evdev_tablet_pad_create(struct evdev_device *device)
 {
@@ -892,12 +865,7 @@ evdev_tablet_pad_create(struct evdev_dev
 		return NULL;
 	}
 
-	device->base.config.sendevents = &pad->sendevents.config;
-	pad->sendevents.current_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
-	pad->sendevents.config.get_modes = pad_sendevents_get_modes;
-	pad->sendevents.config.set_mode = pad_sendevents_set_mode;
-	pad->sendevents.config.get_mode = pad_sendevents_get_mode;
-	pad->sendevents.config.get_default_mode = pad_sendevents_get_default_mode;
+	evdev_init_sendevents(device, &pad->base);
 
 	return &pad->base;
 }
@@ -914,7 +882,7 @@ evdev_device_tablet_pad_has_key(struct e
 int
 evdev_device_tablet_pad_get_num_buttons(struct evdev_device *device)
 {
-	struct pad_dispatch *pad = (struct pad_dispatch*)device->dispatch;
+	struct pad_dispatch *pad = (struct pad_dispatch *)device->dispatch;
 
 	if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
 		return -1;
@@ -933,9 +901,7 @@ evdev_device_tablet_pad_get_num_dials(st
 	if (libevdev_has_event_code(device->evdev, EV_REL, REL_WHEEL) ||
 	    libevdev_has_event_code(device->evdev, EV_REL, REL_DIAL)) {
 		ndials++;
-		if (libevdev_has_event_code(device->evdev,
-					    EV_REL,
-					    REL_HWHEEL))
+		if (libevdev_has_event_code(device->evdev, EV_REL, REL_HWHEEL))
 			ndials++;
 	}
 
@@ -952,9 +918,7 @@ evdev_device_tablet_pad_get_num_rings(st
 
 	if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_WHEEL)) {
 		nrings++;
-		if (libevdev_has_event_code(device->evdev,
-					    EV_ABS,
-					    ABS_THROTTLE))
+		if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_THROTTLE))
 			nrings++;
 	}
 
@@ -971,9 +935,7 @@ evdev_device_tablet_pad_get_num_strips(s
 
 	if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_RX)) {
 		nstrips++;
-		if (libevdev_has_event_code(device->evdev,
-					    EV_ABS,
-					    ABS_RY))
+		if (libevdev_has_event_code(device->evdev, EV_ABS, ABS_RY))
 			nstrips++;
 	}
 
diff -pruN 1.28.1-1/src/evdev-tablet-pad.h 1.30.0-1/src/evdev-tablet-pad.h
--- 1.28.1-1/src/evdev-tablet-pad.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-tablet-pad.h	2025-11-25 03:40:43.000000000 +0000
@@ -26,27 +26,27 @@
 
 #include "evdev.h"
 
-#if !HAVE_LIBWACOM
-typedef void * WacomDevice;
+#ifndef HAVE_LIBWACOM
+typedef void *WacomDevice;
 #endif
 
 #define LIBINPUT_BUTTONSET_AXIS_NONE 0
 
 enum pad_status {
-	PAD_NONE		= 0,
-	PAD_AXES_UPDATED	= bit(0),
-	PAD_BUTTONS_PRESSED	= bit(1),
-	PAD_BUTTONS_RELEASED	= bit(2),
+	PAD_NONE = 0,
+	PAD_AXES_UPDATED = bit(0),
+	PAD_BUTTONS_PRESSED = bit(1),
+	PAD_BUTTONS_RELEASED = bit(2),
 };
 
 enum pad_axes {
-	PAD_AXIS_NONE		= 0,
-	PAD_AXIS_RING1		= bit(0),
-	PAD_AXIS_RING2		= bit(1),
-	PAD_AXIS_STRIP1		= bit(2),
-	PAD_AXIS_STRIP2		= bit(3),
-	PAD_AXIS_DIAL1		= bit(4),
-	PAD_AXIS_DIAL2		= bit(5),
+	PAD_AXIS_NONE = 0,
+	PAD_AXIS_RING1 = bit(0),
+	PAD_AXIS_RING2 = bit(1),
+	PAD_AXIS_STRIP1 = bit(2),
+	PAD_AXIS_STRIP2 = bit(3),
+	PAD_AXIS_DIAL1 = bit(4),
+	PAD_AXIS_DIAL2 = bit(5),
 };
 
 struct button_state {
@@ -86,18 +86,14 @@ struct pad_dispatch {
 	} dials;
 
 	struct {
-		struct libinput_device_config_send_events config;
-		enum libinput_config_send_events_mode current_mode;
-	} sendevents;
-
-	struct {
 		struct list mode_group_list;
+		struct ratelimit group_not_found;
 	} modes;
 
 	struct ratelimit duplicate_abs_limit;
 };
 
-static inline struct pad_dispatch*
+static inline struct pad_dispatch *
 pad_dispatch(struct evdev_dispatch *dispatch)
 {
 	evdev_verify_dispatch_type(dispatch, DISPATCH_TABLET_PAD);
diff -pruN 1.28.1-1/src/evdev-tablet.c 1.30.0-1/src/evdev-tablet.c
--- 1.28.1-1/src/evdev-tablet.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-tablet.c	2025-11-25 03:40:43.000000000 +0000
@@ -22,15 +22,19 @@
  * DEALINGS IN THE SOFTWARE.
  */
 #include "config.h"
-#include "evdev-tablet.h"
-#include "util-input-event.h"
 
 #include <assert.h>
 #include <stdbool.h>
 #include <string.h>
 
-#if HAVE_LIBWACOM
+#include "util-input-event.h"
+
+#include "evdev-tablet.h"
+
+#ifdef HAVE_LIBWACOM
 #include <libwacom/libwacom.h>
+#else
+typedef void *WacomStylus;
 #endif
 
 enum notify {
@@ -38,22 +42,16 @@ enum notify {
 	DO_NOTIFY,
 };
 
-/* The tablet sends events every ~2ms , 50ms should be plenty enough to
-   detect out-of-range.
-   This value is higher during test suite runs */
-static int FORCED_PROXOUT_TIMEOUT = 50 * 1000; /* µs */
-
-#define tablet_set_status(tablet_,s_) (tablet_)->status |= (s_)
-#define tablet_unset_status(tablet_,s_) (tablet_)->status &= ~(s_)
-#define tablet_has_status(tablet_,s_) (!!((tablet_)->status & (s_)))
+#define tablet_set_status(tablet_, s_) (tablet_)->status |= (s_)
+#define tablet_unset_status(tablet_, s_) (tablet_)->status &= ~(s_)
+#define tablet_has_status(tablet_, s_) (!!((tablet_)->status & (s_)))
 
 static inline void
-tablet_get_pressed_buttons(struct tablet_dispatch *tablet,
-			   struct button_state *buttons)
+tablet_get_pressed_buttons(struct tablet_dispatch *tablet, struct button_state *buttons)
 {
 	size_t i;
 	const struct button_state *state = &tablet->button_state,
-			          *prev_state = &tablet->prev_button_state;
+				  *prev_state = &tablet->prev_button_state;
 
 	for (i = 0; i < sizeof(buttons->bits); i++)
 		buttons->bits[i] = state->bits[i] & ~(prev_state->bits[i]);
@@ -65,32 +63,17 @@ tablet_get_released_buttons(struct table
 {
 	size_t i;
 	const struct button_state *state = &tablet->button_state,
-			          *prev_state = &tablet->prev_button_state;
+				  *prev_state = &tablet->prev_button_state;
 
 	for (i = 0; i < sizeof(buttons->bits); i++)
-		buttons->bits[i] = prev_state->bits[i] &
-					~(state->bits[i]);
+		buttons->bits[i] = prev_state->bits[i] & ~(state->bits[i]);
 }
 
-static struct libinput_tablet_tool_pressure_threshold*
+static struct libinput_tablet_tool_pressure_threshold *
 tablet_tool_get_threshold(struct tablet_dispatch *tablet,
-			   struct libinput_tablet_tool *tool)
+			  struct libinput_tablet_tool *tool)
 {
-	ARRAY_FOR_EACH(tool->pressure.thresholds, threshold) {
-		if (threshold->tablet_id == tablet->tablet_id) {
-			return threshold;
-		}
-	}
-
-	/* If we ever get here, we failed detecting the proximity for this tablet
-	 * (or we have too many tablets). Return the first one which will
-	 * make things work incorrectly but we don't need to NULL-check
-	 * everything for an extremely unlikely situtation */
-	evdev_log_bug_libinput(tablet->device,
-			       "Failed to find tablet_id %d for pressure offsets\n",
-			       tablet->tablet_id);
-
-	return &tool->pressure.thresholds[0];
+	return &tool->pressure.threshold;
 }
 
 /* Merge the previous state with the current one so all buttons look like
@@ -121,16 +104,14 @@ tablet_history_reset(struct tablet_dispa
 }
 
 static inline void
-tablet_history_push(struct tablet_dispatch *tablet,
-		    const struct tablet_axes *axes)
+tablet_history_push(struct tablet_dispatch *tablet, const struct tablet_axes *axes)
 {
-	unsigned int index = (tablet->history.index + 1) %
-				tablet_history_size(tablet);
+	unsigned int index = (tablet->history.index + 1) % tablet_history_size(tablet);
 
 	tablet->history.samples[index] = *axes;
 	tablet->history.index = index;
-	tablet->history.count = min(tablet->history.count + 1,
-				    tablet_history_size(tablet));
+	tablet->history.count =
+		min(tablet->history.count + 1, tablet_history_size(tablet));
 
 	if (tablet->history.count < tablet_history_size(tablet))
 		tablet_history_push(tablet, axes);
@@ -140,7 +121,7 @@ tablet_history_push(struct tablet_dispat
  * Return a previous axis state, where index of 0 means "most recent", 1 is
  * "one before most recent", etc.
  */
-static inline const struct tablet_axes*
+static inline const struct tablet_axes *
 tablet_history_get(const struct tablet_dispatch *tablet, unsigned int index)
 {
 	size_t sz = tablet_history_size(tablet);
@@ -167,28 +148,16 @@ tablet_device_has_axis(struct tablet_dis
 	unsigned int code;
 
 	if (axis == LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z) {
-		has_axis = (libevdev_has_event_code(evdev,
-						    EV_KEY,
-						    BTN_TOOL_MOUSE) &&
-			    libevdev_has_event_code(evdev,
-						    EV_ABS,
-						    ABS_TILT_X) &&
-			    libevdev_has_event_code(evdev,
-						    EV_ABS,
-						    ABS_TILT_Y));
+		has_axis = (libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_MOUSE) &&
+			    libevdev_has_event_code(evdev, EV_ABS, ABS_TILT_X) &&
+			    libevdev_has_event_code(evdev, EV_ABS, ABS_TILT_Y));
 		code = axis_to_evcode(axis);
-		has_axis |= libevdev_has_event_code(evdev,
-						    EV_ABS,
-						    code);
+		has_axis |= libevdev_has_event_code(evdev, EV_ABS, code);
 	} else if (axis == LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL) {
-		has_axis = libevdev_has_event_code(evdev,
-						   EV_REL,
-						   REL_WHEEL);
+		has_axis = libevdev_has_event_code(evdev, EV_REL, REL_WHEEL);
 	} else {
 		code = axis_to_evcode(axis);
-		has_axis = libevdev_has_event_code(evdev,
-						   EV_ABS,
-						   code);
+		has_axis = libevdev_has_event_code(evdev, EV_ABS, code);
 	}
 
 	return has_axis;
@@ -197,7 +166,7 @@ tablet_device_has_axis(struct tablet_dis
 static inline bool
 tablet_filter_axis_fuzz(const struct tablet_dispatch *tablet,
 			const struct evdev_device *device,
-			const struct input_event *e,
+			const struct evdev_event *e,
 			enum libinput_tablet_tool_axis axis)
 {
 	int delta, fuzz;
@@ -207,14 +176,14 @@ tablet_filter_axis_fuzz(const struct tab
 	current = e->value;
 	delta = previous - current;
 
-	fuzz = libevdev_get_abs_fuzz(device->evdev, e->code);
+	fuzz = libevdev_get_abs_fuzz(device->evdev, evdev_usage_code(e->usage));
 
 	/* ABS_DISTANCE doesn't have have fuzz set and causes continuous
 	 * updates for the cursor/lens tools. Add a minimum fuzz of 2, same
 	 * as the xf86-input-wacom driver
 	 */
-	switch (e->code) {
-	case ABS_DISTANCE:
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_ABS_DISTANCE:
 		fuzz = max(2, fuzz);
 		break;
 	default:
@@ -227,25 +196,25 @@ tablet_filter_axis_fuzz(const struct tab
 static void
 tablet_process_absolute(struct tablet_dispatch *tablet,
 			struct evdev_device *device,
-			struct input_event *e,
+			struct evdev_event *e,
 			uint64_t time)
 {
 	enum libinput_tablet_tool_axis axis;
 
-	switch (e->code) {
-	case ABS_X:
-	case ABS_Y:
-	case ABS_Z:
-	case ABS_PRESSURE:
-	case ABS_TILT_X:
-	case ABS_TILT_Y:
-	case ABS_DISTANCE:
-	case ABS_WHEEL:
-		axis = evcode_to_axis(e->code);
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_ABS_X:
+	case EVDEV_ABS_Y:
+	case EVDEV_ABS_Z:
+	case EVDEV_ABS_PRESSURE:
+	case EVDEV_ABS_TILT_X:
+	case EVDEV_ABS_TILT_Y:
+	case EVDEV_ABS_DISTANCE:
+	case EVDEV_ABS_WHEEL:
+		axis = evdev_usage_to_axis(e->usage);
 		if (axis == LIBINPUT_TABLET_TOOL_AXIS_NONE) {
 			evdev_log_bug_libinput(device,
 					       "Invalid ABS event code %#x\n",
-					       e->code);
+					       evdev_usage_as_uint32_t(e->usage));
 			break;
 		}
 
@@ -259,24 +228,24 @@ tablet_process_absolute(struct tablet_di
 		break;
 	/* tool_id is the identifier for the tool we can use in libwacom
 	 * to identify it (if we have one anyway) */
-	case ABS_MISC:
+	case EVDEV_ABS_MISC:
 		tablet->current_tool.id = e->value;
 		break;
 	/* Intuos 3 strip data. Should only happen on the Pad device, not on
 	   the Pen device. */
-	case ABS_RX:
-	case ABS_RY:
+	case EVDEV_ABS_RX:
+	case EVDEV_ABS_RY:
 	/* Only on the 4D mouse (Intuos2), obsolete */
-	case ABS_RZ:
+	case EVDEV_ABS_RZ:
 	/* Only on the 4D mouse (Intuos2), obsolete.
 	   The 24HD sends ABS_THROTTLE on the Pad device for the second
 	   wheel but we shouldn't get here on kernel >= 3.17.
 	   */
-	case ABS_THROTTLE:
+	case EVDEV_ABS_THROTTLE:
 	default:
 		evdev_log_info(device,
 			       "Unhandled ABS event code %#x\n",
-			       e->code);
+			       evdev_usage_as_uint32_t(e->usage));
 		break;
 	}
 }
@@ -284,7 +253,7 @@ tablet_process_absolute(struct tablet_di
 static inline int
 axis_range_percentage(const struct input_absinfo *a, double percent)
 {
-	return (a->maximum - a->minimum) * percent/100.0 + a->minimum;
+	return (a->maximum - a->minimum) * percent / 100.0 + a->minimum;
 }
 
 static void
@@ -292,7 +261,9 @@ tablet_change_area(struct evdev_device *
 {
 	struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch);
 
-	if (memcmp(&tablet->area.rect, &tablet->area.want_rect, sizeof(tablet->area.rect)) == 0)
+	if (memcmp(&tablet->area.rect,
+		   &tablet->area.want_rect,
+		   sizeof(tablet->area.rect)) == 0)
 		return;
 
 	if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))
@@ -309,10 +280,14 @@ tablet_change_area(struct evdev_device *
 
 	const struct input_absinfo *absx = device->abs.absinfo_x;
 	const struct input_absinfo *absy = device->abs.absinfo_y;
-	tablet->area.x.minimum = axis_range_percentage(absx, tablet->area.rect.x1 * 100);
-	tablet->area.x.maximum = axis_range_percentage(absx, tablet->area.rect.x2 * 100);
-	tablet->area.y.minimum = axis_range_percentage(absy, tablet->area.rect.y1 * 100);
-	tablet->area.y.maximum = axis_range_percentage(absy, tablet->area.rect.y2 * 100);
+	tablet->area.x.minimum =
+		axis_range_percentage(absx, tablet->area.rect.x1 * 100);
+	tablet->area.x.maximum =
+		axis_range_percentage(absx, tablet->area.rect.x2 * 100);
+	tablet->area.y.minimum =
+		axis_range_percentage(absy, tablet->area.rect.y1 * 100);
+	tablet->area.y.maximum =
+		axis_range_percentage(absy, tablet->area.rect.y2 * 100);
 }
 
 static void
@@ -381,8 +356,7 @@ tablet_update_tool(struct tablet_dispatc
 		tablet->current_tool.type = tool;
 		tablet_set_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY);
 		tablet_unset_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY);
-	}
-	else if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) {
+	} else if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) {
 		tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY);
 	}
 }
@@ -430,10 +404,8 @@ adjust_tilt(const struct input_absinfo *
 	/* If resolution is nonzero, it's in units/radian. But require
 	 * a min/max less/greater than zero so we can assume 0 is the
 	 * center */
-	if (absinfo->resolution != 0 &&
-	    absinfo->maximum > 0 &&
-	    absinfo->minimum < 0) {
-		value = rad2deg((double)absinfo->value/absinfo->resolution);
+	if (absinfo->resolution != 0 && absinfo->maximum > 0 && absinfo->minimum < 0) {
+		value = rad2deg((double)absinfo->value / absinfo->resolution);
 	} else {
 		/* Wacom supports physical [-64, 64] degrees, so map to that by
 		 * default. If other tablets have a different physical range or
@@ -488,8 +460,7 @@ convert_to_degrees(const struct input_ab
 }
 
 static inline double
-normalize_wheel(struct tablet_dispatch *tablet,
-		int value)
+normalize_wheel(struct tablet_dispatch *tablet, int value)
 {
 	struct evdev_device *device = tablet->device;
 
@@ -508,13 +479,15 @@ is_inside_area(struct tablet_dispatch *t
 	assert(normalized_margin > 0.0);
 	assert(normalized_margin <= 1.0);
 
-	int xmargin = (tablet->area.x.maximum - tablet->area.x.minimum) * normalized_margin;
-	int ymargin = (tablet->area.y.maximum - tablet->area.y.minimum) * normalized_margin;
+	int xmargin =
+		(tablet->area.x.maximum - tablet->area.x.minimum) * normalized_margin;
+	int ymargin =
+		(tablet->area.y.maximum - tablet->area.y.minimum) * normalized_margin;
 
 	return (point->x >= tablet->area.x.minimum - xmargin &&
-	        point->x <= tablet->area.x.maximum + xmargin &&
-	        point->y >= tablet->area.y.minimum - ymargin &&
-	        point->y <= tablet->area.y.maximum + ymargin);
+		point->x <= tablet->area.x.maximum + xmargin &&
+		point->y >= tablet->area.y.minimum - ymargin &&
+		point->y <= tablet->area.y.maximum + ymargin);
 }
 
 static void
@@ -543,8 +516,7 @@ apply_tablet_area(struct tablet_dispatch
 }
 
 static inline void
-tablet_update_xy(struct tablet_dispatch *tablet,
-		 struct evdev_device *device)
+tablet_update_xy(struct tablet_dispatch *tablet, struct evdev_device *device)
 {
 	const struct input_absinfo *absinfo;
 	int value;
@@ -593,8 +565,7 @@ tablet_tool_process_delta(struct tablet_
 
 	/* When tool contact changes, we probably got a cursor jump. Don't
 	   try to calculate a delta for that event */
-	if (!tablet_has_status(tablet,
-			       TABLET_TOOL_ENTERING_PROXIMITY) &&
+	if (!tablet_has_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY) &&
 	    !tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT) &&
 	    !tablet_has_status(tablet, TABLET_TOOL_LEAVING_CONTACT) &&
 	    (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_X) ||
@@ -616,10 +587,7 @@ tablet_tool_process_delta(struct tablet_
 	if (device_float_is_zero(accel))
 		return zero;
 
-	return filter_dispatch(device->pointer.filter,
-			       &accel,
-			       tool,
-			       time);
+	return filter_dispatch(device->pointer.filter, &accel, tool, time);
 }
 
 static inline void
@@ -627,8 +595,8 @@ tablet_update_pressure(struct tablet_dis
 		       struct evdev_device *device,
 		       struct libinput_tablet_tool *tool)
 {
-	const struct input_absinfo *abs = libevdev_get_abs_info(device->evdev,
-								ABS_PRESSURE);
+	const struct input_absinfo *abs =
+		libevdev_get_abs_info(device->evdev, ABS_PRESSURE);
 	if (!abs)
 		return;
 
@@ -640,40 +608,35 @@ tablet_update_pressure(struct tablet_dis
 }
 
 static inline void
-tablet_update_distance(struct tablet_dispatch *tablet,
-		       struct evdev_device *device)
+tablet_update_distance(struct tablet_dispatch *tablet, struct evdev_device *device)
 {
 	const struct input_absinfo *absinfo;
 
 	if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_DISTANCE))
 		return;
 
-	if (bit_is_set(tablet->changed_axes,
-		       LIBINPUT_TABLET_TOOL_AXIS_DISTANCE)) {
+	if (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_DISTANCE)) {
 		absinfo = libevdev_get_abs_info(device->evdev, ABS_DISTANCE);
 		tablet->axes.distance = normalize_distance(absinfo);
 	}
 }
 
 static inline void
-tablet_update_slider(struct tablet_dispatch *tablet,
-		     struct evdev_device *device)
+tablet_update_slider(struct tablet_dispatch *tablet, struct evdev_device *device)
 {
 	const struct input_absinfo *absinfo;
 
 	if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_WHEEL))
 		return;
 
-	if (bit_is_set(tablet->changed_axes,
-		       LIBINPUT_TABLET_TOOL_AXIS_SLIDER)) {
+	if (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_SLIDER)) {
 		absinfo = libevdev_get_abs_info(device->evdev, ABS_WHEEL);
 		tablet->axes.slider = normalize_slider(absinfo);
 	}
 }
 
 static inline void
-tablet_update_tilt(struct tablet_dispatch *tablet,
-		   struct evdev_device *device)
+tablet_update_tilt(struct tablet_dispatch *tablet, struct evdev_device *device)
 {
 	const struct input_absinfo *absinfo;
 
@@ -683,10 +646,8 @@ tablet_update_tilt(struct tablet_dispatc
 
 	/* mouse rotation resets tilt to 0 so always fetch both axes if
 	 * either has changed */
-	if (bit_is_set(tablet->changed_axes,
-		       LIBINPUT_TABLET_TOOL_AXIS_TILT_X) ||
-	    bit_is_set(tablet->changed_axes,
-		       LIBINPUT_TABLET_TOOL_AXIS_TILT_Y)) {
+	if (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_X) ||
+	    bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_Y)) {
 
 		absinfo = libevdev_get_abs_info(device->evdev, ABS_TILT_X);
 		tablet->axes.tilt.x = adjust_tilt(absinfo);
@@ -710,10 +671,8 @@ tablet_update_artpen_rotation(struct tab
 	if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_Z))
 		return;
 
-	if (bit_is_set(tablet->changed_axes,
-		       LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z)) {
-		absinfo = libevdev_get_abs_info(device->evdev,
-						ABS_Z);
+	if (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z)) {
+		absinfo = libevdev_get_abs_info(device->evdev, ABS_Z);
 		/* artpen has 0 with buttons pointing east */
 		tablet->axes.rotation = convert_to_degrees(absinfo, 90);
 	}
@@ -723,17 +682,14 @@ static inline void
 tablet_update_mouse_rotation(struct tablet_dispatch *tablet,
 			     struct evdev_device *device)
 {
-	if (bit_is_set(tablet->changed_axes,
-		       LIBINPUT_TABLET_TOOL_AXIS_TILT_X) ||
-	    bit_is_set(tablet->changed_axes,
-		       LIBINPUT_TABLET_TOOL_AXIS_TILT_Y)) {
+	if (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_X) ||
+	    bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_Y)) {
 		convert_tilt_to_rotation(tablet);
 	}
 }
 
 static inline void
-tablet_update_rotation(struct tablet_dispatch *tablet,
-		       struct evdev_device *device)
+tablet_update_rotation(struct tablet_dispatch *tablet, struct evdev_device *device)
 {
 	/* We must check ROTATION_Z after TILT_X/Y so that the tilt axes are
 	 * already normalized and set if we have the mouse/lens tool */
@@ -759,16 +715,15 @@ tablet_update_rotation(struct tablet_dis
 }
 
 static inline void
-tablet_update_wheel(struct tablet_dispatch *tablet,
-		    struct evdev_device *device)
+tablet_update_wheel(struct tablet_dispatch *tablet, struct evdev_device *device)
 {
 	int a;
 
 	a = LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL;
 	if (bit_is_set(tablet->changed_axes, a)) {
 		/* tablet->axes.wheel_discrete is already set */
-		tablet->axes.wheel = normalize_wheel(tablet,
-						     tablet->axes.wheel_discrete);
+		tablet->axes.wheel =
+			normalize_wheel(tablet, tablet->axes.wheel_discrete);
 	} else {
 		tablet->axes.wheel = 0;
 		tablet->axes.wheel_discrete = 0;
@@ -776,8 +731,7 @@ tablet_update_wheel(struct tablet_dispat
 }
 
 static void
-tablet_smoothen_axes(const struct tablet_dispatch *tablet,
-		     struct tablet_axes *axes)
+tablet_smoothen_axes(const struct tablet_dispatch *tablet, struct tablet_axes *axes)
 {
 	size_t i;
 	size_t count = tablet_history_size(tablet);
@@ -793,11 +747,11 @@ tablet_smoothen_axes(const struct tablet
 		smooth.tilt.y += a->tilt.y;
 	}
 
-	axes->point.x = smooth.point.x/count;
-	axes->point.y = smooth.point.y/count;
+	axes->point.x = smooth.point.x / count;
+	axes->point.y = smooth.point.y / count;
 
-	axes->tilt.x = smooth.tilt.x/count;
-	axes->tilt.y = smooth.tilt.y/count;
+	axes->tilt.x = smooth.tilt.x / count;
+	axes->tilt.y = smooth.tilt.y / count;
 }
 
 static bool
@@ -807,8 +761,8 @@ tablet_check_notify_axes(struct tablet_d
 			 struct tablet_axes *axes_out,
 			 uint64_t time)
 {
-	struct tablet_axes axes = {0};
-	const char tmp[sizeof(tablet->changed_axes)] = {0};
+	struct tablet_axes axes = { 0 };
+	const char tmp[sizeof(tablet->changed_axes)] = { 0 };
 	bool rc = false;
 
 	if (memcmp(tmp, tablet->changed_axes, sizeof(tmp)) == 0) {
@@ -859,52 +813,66 @@ out:
 
 static void
 tablet_update_button(struct tablet_dispatch *tablet,
-		     uint32_t evcode,
+		     evdev_usage_t usage,
 		     uint32_t enable)
 {
-	switch (evcode) {
-	case BTN_LEFT:
-	case BTN_RIGHT:
-	case BTN_MIDDLE:
-	case BTN_SIDE:
-	case BTN_EXTRA:
-	case BTN_FORWARD:
-	case BTN_BACK:
-	case BTN_TASK:
-	case BTN_STYLUS:
-	case BTN_STYLUS2:
-	case BTN_STYLUS3:
+	switch (evdev_usage_enum(usage)) {
+	case EVDEV_BTN_LEFT:
+	case EVDEV_BTN_RIGHT:
+	case EVDEV_BTN_MIDDLE:
+	case EVDEV_BTN_SIDE:
+	case EVDEV_BTN_EXTRA:
+	case EVDEV_BTN_FORWARD:
+	case EVDEV_BTN_BACK:
+	case EVDEV_BTN_TASK:
+	case EVDEV_BTN_STYLUS:
+	case EVDEV_BTN_STYLUS2:
+	case EVDEV_BTN_STYLUS3:
 		break;
 	default:
 		evdev_log_info(tablet->device,
 			       "Unhandled button %s (%#x)\n",
-			       libevdev_event_code_get_name(EV_KEY, evcode),
-			       evcode);
+			       evdev_usage_code_name(usage),
+			       evdev_usage_as_uint32_t(usage));
 		return;
 	}
 
 	if (enable) {
-		set_bit(tablet->button_state.bits, evcode);
+		set_bit(tablet->button_state.bits, evdev_usage_code(usage));
 		tablet_set_status(tablet, TABLET_BUTTONS_PRESSED);
 	} else {
-		clear_bit(tablet->button_state.bits, evcode);
+		clear_bit(tablet->button_state.bits, evdev_usage_code(usage));
 		tablet_set_status(tablet, TABLET_BUTTONS_RELEASED);
 	}
 }
 
 static inline enum libinput_tablet_tool_type
-tablet_evcode_to_tool(int code)
+tablet_evdev_usage_to_tool(evdev_usage_t usage)
 {
 	enum libinput_tablet_tool_type type;
 
-	switch (code) {
-	case BTN_TOOL_PEN:	type = LIBINPUT_TABLET_TOOL_TYPE_PEN;		break;
-	case BTN_TOOL_RUBBER:	type = LIBINPUT_TABLET_TOOL_TYPE_ERASER;	break;
-	case BTN_TOOL_BRUSH:	type = LIBINPUT_TABLET_TOOL_TYPE_BRUSH;		break;
-	case BTN_TOOL_PENCIL:	type = LIBINPUT_TABLET_TOOL_TYPE_PENCIL;	break;
-	case BTN_TOOL_AIRBRUSH:	type = LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH;	break;
-	case BTN_TOOL_MOUSE:	type = LIBINPUT_TABLET_TOOL_TYPE_MOUSE;		break;
-	case BTN_TOOL_LENS:	type = LIBINPUT_TABLET_TOOL_TYPE_LENS;		break;
+	switch (evdev_usage_enum(usage)) {
+	case EVDEV_BTN_TOOL_PEN:
+		type = LIBINPUT_TABLET_TOOL_TYPE_PEN;
+		break;
+	case EVDEV_BTN_TOOL_RUBBER:
+		type = LIBINPUT_TABLET_TOOL_TYPE_ERASER;
+		break;
+	case EVDEV_BTN_TOOL_BRUSH:
+		type = LIBINPUT_TABLET_TOOL_TYPE_BRUSH;
+		break;
+	case EVDEV_BTN_TOOL_PENCIL:
+		type = LIBINPUT_TABLET_TOOL_TYPE_PENCIL;
+		break;
+	case EVDEV_BTN_TOOL_AIRBRUSH:
+		type = LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH;
+		break;
+	case EVDEV_BTN_TOOL_MOUSE:
+		type = LIBINPUT_TABLET_TOOL_TYPE_MOUSE;
+		break;
+	case EVDEV_BTN_TOOL_LENS:
+		type = LIBINPUT_TABLET_TOOL_TYPE_LENS;
+		break;
 	default:
 		abort();
 	}
@@ -915,7 +883,7 @@ tablet_evcode_to_tool(int code)
 static void
 tablet_process_key(struct tablet_dispatch *tablet,
 		   struct evdev_device *device,
-		   struct input_event *e,
+		   struct evdev_event *e,
 		   uint64_t time)
 {
 	enum libinput_tablet_tool_type type;
@@ -924,38 +892,36 @@ tablet_process_key(struct tablet_dispatc
 	if (e->value == 2)
 		return;
 
-	switch (e->code) {
-	case BTN_TOOL_FINGER:
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_BTN_TOOL_FINGER:
 		evdev_log_bug_libinput(device,
-			       "Invalid tool 'finger' on tablet interface\n");
+				       "Invalid tool 'finger' on tablet interface\n");
 		break;
-	case BTN_TOOL_PEN:
-	case BTN_TOOL_RUBBER:
-	case BTN_TOOL_BRUSH:
-	case BTN_TOOL_PENCIL:
-	case BTN_TOOL_AIRBRUSH:
-	case BTN_TOOL_MOUSE:
-	case BTN_TOOL_LENS:
-		type = tablet_evcode_to_tool(e->code);
+	case EVDEV_BTN_TOOL_PEN:
+	case EVDEV_BTN_TOOL_RUBBER:
+	case EVDEV_BTN_TOOL_BRUSH:
+	case EVDEV_BTN_TOOL_PENCIL:
+	case EVDEV_BTN_TOOL_AIRBRUSH:
+	case EVDEV_BTN_TOOL_MOUSE:
+	case EVDEV_BTN_TOOL_LENS:
+		type = tablet_evdev_usage_to_tool(e->usage);
 		tablet_set_status(tablet, TABLET_TOOL_UPDATED);
 		if (e->value)
 			tablet->tool_state |= bit(type);
 		else
 			tablet->tool_state &= ~bit(type);
 		break;
-	case BTN_TOUCH:
+	case EVDEV_BTN_TOUCH:
 		if (!bit_is_set(tablet->axis_caps,
 				LIBINPUT_TABLET_TOOL_AXIS_PRESSURE)) {
 			if (e->value)
-				tablet_set_status(tablet,
-						  TABLET_TOOL_ENTERING_CONTACT);
+				tablet_set_status(tablet, TABLET_TOOL_ENTERING_CONTACT);
 			else
-				tablet_set_status(tablet,
-						  TABLET_TOOL_LEAVING_CONTACT);
+				tablet_set_status(tablet, TABLET_TOOL_LEAVING_CONTACT);
 		}
 		break;
 	default:
-		tablet_update_button(tablet, e->code, e->value);
+		tablet_update_button(tablet, e->usage, e->value);
 		break;
 	}
 }
@@ -963,18 +929,18 @@ tablet_process_key(struct tablet_dispatc
 static void
 tablet_process_relative(struct tablet_dispatch *tablet,
 			struct evdev_device *device,
-			struct input_event *e,
+			struct evdev_event *e,
 			uint64_t time)
 {
 	enum libinput_tablet_tool_axis axis;
 
-	switch (e->code) {
-	case REL_WHEEL:
-		axis = rel_evcode_to_axis(e->code);
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_REL_WHEEL:
+		axis = evdev_usage_to_axis(e->usage);
 		if (axis == LIBINPUT_TABLET_TOOL_AXIS_NONE) {
 			evdev_log_bug_libinput(device,
 					       "Invalid ABS event code %#x\n",
-					       e->code);
+					       evdev_usage_as_uint32_t(e->usage));
 			break;
 		}
 		set_bit(tablet->changed_axes, axis);
@@ -984,8 +950,8 @@ tablet_process_relative(struct tablet_di
 	default:
 		evdev_log_info(device,
 			       "Unhandled relative axis %s (%#x)\n",
-			       libevdev_event_code_get_name(EV_REL, e->code),
-			       e->code);
+			       evdev_event_get_code_name(e),
+			       evdev_usage_as_uint32_t(e->usage));
 		return;
 	}
 }
@@ -993,22 +959,22 @@ tablet_process_relative(struct tablet_di
 static void
 tablet_process_misc(struct tablet_dispatch *tablet,
 		    struct evdev_device *device,
-		    struct input_event *e,
+		    struct evdev_event *e,
 		    uint64_t time)
 {
-	switch (e->code) {
-	case MSC_SERIAL:
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_MSC_SERIAL:
 		if (e->value != -1)
 			tablet->current_tool.serial = e->value;
 
 		break;
-	case MSC_SCAN:
+	case EVDEV_MSC_SCAN:
 		break;
 	default:
 		evdev_log_info(device,
 			       "Unhandled MSC event code %s (%#x)\n",
-			       libevdev_event_code_get_name(EV_MSC, e->code),
-			       e->code);
+			       evdev_event_get_code_name(e),
+			       evdev_usage_as_uint32_t(e->usage));
 		break;
 	}
 }
@@ -1034,21 +1000,15 @@ copy_button_cap(const struct tablet_disp
 
 static inline bool
 tool_set_bits_from_libwacom(const struct tablet_dispatch *tablet,
-			    struct libinput_tablet_tool *tool)
+			    struct libinput_tablet_tool *tool,
+			    const WacomStylus *s)
 {
 	bool rc = false;
-#if HAVE_LIBWACOM
-	WacomDeviceDatabase *db;
-	const WacomStylus *s = NULL;
+#ifdef HAVE_LIBWACOM
 	int code;
 	WacomStylusType type;
 	WacomAxisTypeFlags axes;
 
-	db = tablet_libinput_context(tablet)->libwacom.db;
-	if (!db)
-		return rc;
-
-	s = libwacom_stylus_get_for_id(db, tool->tool_id);
 	if (!s)
 		return rc;
 
@@ -1075,15 +1035,10 @@ tool_set_bits_from_libwacom(const struct
 	if (axes & WACOM_AXIS_TYPE_TILT) {
 		/* tilt on the puck is converted to rotation */
 		if (type == WSTYLUS_PUCK) {
-			set_bit(tool->axis_caps,
-				LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
+			set_bit(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
 		} else {
-			copy_axis_cap(tablet,
-				      tool,
-				      LIBINPUT_TABLET_TOOL_AXIS_TILT_X);
-			copy_axis_cap(tablet,
-				      tool,
-				      LIBINPUT_TABLET_TOOL_AXIS_TILT_Y);
+			copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_TILT_X);
+			copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_TILT_Y);
 		}
 	}
 	if (axes & WACOM_AXIS_TYPE_ROTATION_Z)
@@ -1102,14 +1057,15 @@ tool_set_bits_from_libwacom(const struct
 
 static void
 tool_set_bits(const struct tablet_dispatch *tablet,
-	      struct libinput_tablet_tool *tool)
+	      struct libinput_tablet_tool *tool,
+	      const WacomStylus *s)
 {
 	enum libinput_tablet_tool_type type = tool->type;
 
 	copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_X);
 	copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_Y);
 
-	if (tool_set_bits_from_libwacom(tablet, tool))
+	if (s && tool_set_bits_from_libwacom(tablet, tool, s))
 		return;
 
 	/* If we don't have libwacom, we simply copy any axis we have on the
@@ -1135,9 +1091,10 @@ tool_set_bits(const struct tablet_dispat
 		 * ABS_Z, otherwise we try to get the value from it later on
 		 * proximity in and go boom because the absinfo isn't there.
 		 */
-		if (libevdev_has_event_code(tablet->device->evdev, EV_ABS,
-					    ABS_Z))
-			copy_axis_cap(tablet, tool, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
+		if (libevdev_has_event_code(tablet->device->evdev, EV_ABS, ABS_Z))
+			copy_axis_cap(tablet,
+				      tool,
+				      LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
 		break;
 	case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
 	case LIBINPUT_TABLET_TOOL_TYPE_LENS:
@@ -1174,29 +1131,26 @@ tool_set_bits(const struct tablet_dispat
 }
 
 static bool
-tablet_get_quirked_pressure_thresholds(struct tablet_dispatch *tablet,
-				       int *hi,
-				       int *lo)
+tablet_get_quirked_pressure_thresholds(struct tablet_dispatch *tablet, int *hi, int *lo)
 {
 	struct evdev_device *device = tablet->device;
-	struct quirks_context *quirks = evdev_libinput_context(device)->quirks;
-	struct quirks *q = quirks_fetch_for_device(quirks, device->udev_device);
 	struct quirk_range r;
 	bool status = false;
 
-        /* Note: the quirk term "range" refers to the hi/lo settings, not the
-         * full available range for the pressure axis */
-        if (q && quirks_get_range(q, QUIRK_ATTR_PRESSURE_RANGE, &r)) {
+	/* Note: the quirk term "range" refers to the hi/lo settings, not the
+	 * full available range for the pressure axis */
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
+	if (q && quirks_get_range(q, QUIRK_ATTR_PRESSURE_RANGE, &r)) {
 		if (r.lower < r.upper) {
 			*hi = r.lower;
 			*lo = r.upper;
 			status = true;
 		} else {
-			evdev_log_info(device, "Invalid pressure range, using defaults\n");
+			evdev_log_info(device,
+				       "Invalid pressure range, using defaults\n");
 		}
 	}
 
-	quirks_unref(q);
 	return status;
 }
 
@@ -1213,40 +1167,12 @@ apply_pressure_range_configuration(struc
 	     tool->pressure.range.max == tool->pressure.wanted_range.max))
 		return;
 
-	double min = tool->pressure.wanted_range.min;
-	double max = tool->pressure.wanted_range.max;
-
-	struct input_absinfo abs = *libevdev_get_abs_info(device->evdev, ABS_PRESSURE);
-
-	int minimum = axis_range_percentage(&abs, min * 100.0);
-	int maximum = axis_range_percentage(&abs, max * 100.0);
-
-	abs.minimum = minimum;
-	abs.maximum = maximum;
-
-	/* Only use the quirk pressure range if we don't have a custom range */
-	int hi, lo;
-	if (tool->pressure.wanted_range.min != 0.0 ||
-	    tool->pressure.wanted_range.max != 1.0 ||
-	    !tablet_get_quirked_pressure_thresholds(tablet, &hi, &lo)) {
-		/* 5 and 1% of the pressure range */
-		hi = axis_range_percentage(&abs, 5);
-		lo = axis_range_percentage(&abs, 1);
-	}
-
-	struct libinput_tablet_tool_pressure_threshold *threshold =
-		tablet_tool_get_threshold(tablet, tool);
-	threshold->abs_pressure = abs;
-	threshold->threshold.upper = hi;
-	threshold->threshold.lower = lo;
 	tool->pressure.range.min = tool->pressure.wanted_range.min;
 	tool->pressure.range.max = tool->pressure.wanted_range.max;
 
-	/* Disable any heuristics */
-	if (tool->pressure.has_configured_range) {
-		threshold->has_offset = true;
-		threshold->heuristic_state = PRESSURE_HEURISTIC_STATE_DONE;
-	}
+	struct libinput *libinput = tablet_libinput_context(tablet);
+	libinput_plugin_system_notify_tablet_tool_configured(&libinput->plugin_system,
+							     tool);
 }
 
 static inline void
@@ -1258,7 +1184,7 @@ tool_init_pressure_thresholds(struct tab
 	const struct input_absinfo *pressure, *distance;
 
 	threshold->tablet_id = tablet->tablet_id;
-	threshold->offset = 0;
+	threshold->offset = pressure_offset_from_double(0.0);
 	threshold->has_offset = false;
 	threshold->threshold.upper = 1;
 	threshold->threshold.lower = 0;
@@ -1271,10 +1197,10 @@ tool_init_pressure_thresholds(struct tab
 
 	distance = libevdev_get_abs_info(device->evdev, ABS_DISTANCE);
 	if (distance) {
-		threshold->offset = pressure->minimum;
+		threshold->offset = pressure_offset_from_double(0.0);
 		threshold->heuristic_state = PRESSURE_HEURISTIC_STATE_DONE;
 	} else {
-		threshold->offset = pressure->maximum;
+		threshold->offset = pressure_offset_from_double(1.0);
 		threshold->heuristic_state = PRESSURE_HEURISTIC_STATE_PROXIN1;
 	}
 
@@ -1295,7 +1221,7 @@ pressure_range_set(struct libinput_table
 
 	tool->pressure.wanted_range.min = min;
 	tool->pressure.wanted_range.max = max;
-	tool->pressure.has_configured_range = true;
+	tool->pressure.has_configured_range = min != 0.0 || max != 1.0;
 
 	return LIBINPUT_CONFIG_STATUS_SUCCESS;
 }
@@ -1314,6 +1240,158 @@ pressure_range_get_default(struct libinp
 	*max = 1.0;
 }
 
+static void
+tablet_tool_apply_eraser_button(struct tablet_dispatch *tablet,
+				struct libinput_tablet_tool *tool)
+{
+	if (bitmask_is_empty(tool->eraser_button.available_modes))
+		return;
+
+	if (tool->eraser_button.mode == tool->eraser_button.want_mode &&
+	    tool->eraser_button.button == tool->eraser_button.want_button)
+		return;
+
+	if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))
+		return;
+
+	tool->eraser_button.mode = tool->eraser_button.want_mode;
+	tool->eraser_button.button = tool->eraser_button.want_button;
+
+	struct libinput *libinput = tablet_libinput_context(tablet);
+	libinput_plugin_system_notify_tablet_tool_configured(&libinput->plugin_system,
+							     tool);
+}
+
+static bitmask_t
+eraser_button_get_modes(struct libinput_tablet_tool *tool)
+{
+	return tool->eraser_button.available_modes;
+}
+
+static void
+eraser_button_toggle(struct libinput_tablet_tool *tool)
+{
+	struct libinput_device *libinput_device = tool->last_device;
+	struct evdev_device *device = evdev_device(libinput_device);
+	struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch);
+
+	tablet_tool_apply_eraser_button(tablet, tool);
+}
+
+static enum libinput_config_status
+eraser_button_set_mode(struct libinput_tablet_tool *tool,
+		       enum libinput_config_eraser_button_mode mode)
+{
+	if (mode != LIBINPUT_CONFIG_ERASER_BUTTON_DEFAULT &&
+	    !bitmask_all(tool->eraser_button.available_modes, bitmask_from_u32(mode)))
+		return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
+
+	tool->eraser_button.want_mode = mode;
+
+	eraser_button_toggle(tool);
+
+	return LIBINPUT_CONFIG_STATUS_SUCCESS;
+}
+
+static enum libinput_config_eraser_button_mode
+eraser_button_get_mode(struct libinput_tablet_tool *tool)
+{
+	return tool->eraser_button.mode;
+}
+
+static enum libinput_config_eraser_button_mode
+eraser_button_get_default_mode(struct libinput_tablet_tool *tool)
+{
+	return LIBINPUT_CONFIG_ERASER_BUTTON_DEFAULT;
+}
+
+static enum libinput_config_status
+eraser_button_set_button(struct libinput_tablet_tool *tool, uint32_t button)
+{
+	switch (button) {
+	case BTN_STYLUS:
+	case BTN_STYLUS2:
+	case BTN_STYLUS3:
+		break;
+	default:
+		log_bug_libinput(libinput_device_get_context(tool->last_device),
+				 "Unsupported eraser button 0x%x",
+				 button);
+		return LIBINPUT_CONFIG_STATUS_INVALID;
+	}
+
+	tool->eraser_button.want_button = button;
+
+	eraser_button_toggle(tool);
+
+	return LIBINPUT_CONFIG_STATUS_SUCCESS;
+}
+
+static unsigned int
+eraser_button_get_button(struct libinput_tablet_tool *tool)
+{
+	return tool->eraser_button.button;
+}
+
+static unsigned int
+eraser_button_get_default_button(struct libinput_tablet_tool *tool)
+{
+	/* Which button we want is complicated. Other than Wacom no-one supports
+	 * tool ids so we cannot know if an individual tool supports any of the
+	 * BTN_STYLUS. e.g. any Huion tablet that supports the Huion PW600
+	 * will have BTN_STYLUS3 - regardless if that tool is actually present.
+	 * So we default to BTN_STYLUS3 because there's no placeholder BTN_STYLUS4.
+	 * in the kernel.
+	 */
+	if (!libinput_tablet_tool_has_button(tool, BTN_STYLUS))
+		return BTN_STYLUS;
+	if (!libinput_tablet_tool_has_button(tool, BTN_STYLUS2))
+		return BTN_STYLUS2;
+
+	return BTN_STYLUS3;
+}
+
+static void
+tool_init_eraser_button(struct tablet_dispatch *tablet,
+			struct libinput_tablet_tool *tool,
+			const WacomStylus *s)
+{
+	/* We provide an eraser button config if:
+	 * - the tool is a pen
+	 * - we don't know about the stylus (that's a good indication the
+	 *   stylus doesn't have tool ids which means it'll follow the windows
+	 *   pen protocol)
+	 * - the tool does *not* have an eraser on the back end
+	 *
+	 * Because those are the only tools where the eraser button may
+	 * get changed to a real button (by udev-hid-bpf).
+	 */
+	if (libinput_tablet_tool_get_type(tool) != LIBINPUT_TABLET_TOOL_TYPE_PEN)
+		return;
+
+#ifdef HAVE_LIBWACOM
+	/* libwacom's API is a bit terrible here:
+	 * - has_eraser is true on styli that have a separate eraser, all
+	 *   those are INVERT so we can exclude them
+	 * - get_eraser_type() returns something on actual eraser tools
+	 *   but we don't have any separate erasers with buttons so
+	 *   we only need to exclude INVERT
+	 */
+	if (s && libwacom_stylus_has_eraser(s) &&
+	    libwacom_stylus_get_eraser_type(s) == WACOM_ERASER_INVERT) {
+		return;
+	}
+#endif
+	/* All other pens need eraser button handling because most of the time
+	 * we don't know if they have one (Huion, XP-Pen, ...) */
+	bitmask_t available_modes =
+		bitmask_from_masks(LIBINPUT_CONFIG_ERASER_BUTTON_BUTTON);
+
+	tool->eraser_button.available_modes = available_modes;
+	tool->eraser_button.want_button = eraser_button_get_default_button(tool);
+	tool->eraser_button.button = tool->eraser_button.want_button;
+}
+
 static struct libinput_tablet_tool *
 tablet_new_tool(struct tablet_dispatch *tablet,
 		enum libinput_tablet_tool_type type,
@@ -1321,26 +1399,54 @@ tablet_new_tool(struct tablet_dispatch *
 		uint32_t serial)
 {
 	struct libinput_tablet_tool *tool = zalloc(sizeof *tool);
+	const WacomStylus *s = NULL;
+#ifdef HAVE_LIBWACOM
+	WacomDeviceDatabase *db;
+
+	db = tablet_libinput_context(tablet)->libwacom.db;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+	if (db)
+		s = libwacom_stylus_get_for_id(db, tool_id);
+#pragma GCC diagnostic pop
+#endif
 
-	*tool = (struct libinput_tablet_tool) {
+	*tool = (struct libinput_tablet_tool){
 		.type = type,
 		.serial = serial,
 		.tool_id = tool_id,
 		.refcount = 1,
+		.last_device = NULL,
 
 		.pressure.range.min = 0.0,
 		.pressure.range.max = 0.0, /* to trigger configuration */
 		.pressure.wanted_range.min = 0.0,
 		.pressure.wanted_range.max = 1.0,
 
+		.eraser_button.available_modes = bitmask_new(),
+		.eraser_button.mode = LIBINPUT_CONFIG_ERASER_BUTTON_DEFAULT,
+		.eraser_button.want_mode = LIBINPUT_CONFIG_ERASER_BUTTON_DEFAULT,
+		.eraser_button.button = BTN_STYLUS2,
+		.eraser_button.want_button = BTN_STYLUS2,
+
 		.config.pressure_range.is_available = pressure_range_is_available,
 		.config.pressure_range.set = pressure_range_set,
 		.config.pressure_range.get = pressure_range_get,
 		.config.pressure_range.get_default = pressure_range_get_default,
+
+		.config.eraser_button.get_modes = eraser_button_get_modes,
+		.config.eraser_button.set_mode = eraser_button_set_mode,
+		.config.eraser_button.get_mode = eraser_button_get_mode,
+		.config.eraser_button.get_default_mode = eraser_button_get_default_mode,
+		.config.eraser_button.set_button = eraser_button_set_button,
+		.config.eraser_button.get_button = eraser_button_get_button,
+		.config.eraser_button.get_default_button =
+			eraser_button_get_default_button,
 	};
 
-	tool_init_pressure_thresholds(tablet, tool, &tool->pressure.thresholds[0]);
-	tool_set_bits(tablet, tool);
+	tool_init_pressure_thresholds(tablet, tool, &tool->pressure.threshold);
+	tool_set_bits(tablet, tool, s);
+	tool_init_eraser_button(tablet, tool, s);
 
 	return tool;
 }
@@ -1351,6 +1457,7 @@ tablet_get_tool(struct tablet_dispatch *
 		uint32_t tool_id,
 		uint32_t serial)
 {
+	struct evdev_device *device = tablet->device;
 	struct libinput *libinput = tablet_libinput_context(tablet);
 	struct libinput_tablet_tool *tool = NULL, *t;
 	struct list *tool_list;
@@ -1397,17 +1504,15 @@ tablet_get_tool(struct tablet_dispatch *
 	if (!tool) {
 		tool = tablet_new_tool(tablet, type, tool_id, serial);
 		list_insert(tool_list, &tool->link);
-	} else {
-		ARRAY_FOR_EACH(tool->pressure.thresholds, t) {
-			if (t->tablet_id == tablet->tablet_id)
-				break;
-			if (t->tablet_id == 0) {
-				tool_init_pressure_thresholds(tablet, tool, t);
-				break;
-			}
-		}
 	}
 
+	struct libinput_device *last = tool->last_device;
+	tool->last_device = libinput_device_ref(&device->base);
+	if (last)
+		libinput_device_unref(last);
+
+	tool->last_tablet_id = tablet->tablet_id;
+
 	return tool;
 }
 
@@ -1438,7 +1543,7 @@ tablet_notify_button_mask(struct tablet_
 				     tool,
 				     tip_state,
 				     &tablet->axes,
-				     i,
+				     button_code_from_uint32_t(i),
 				     state,
 				     &tablet->area.x,
 				     &tablet->area.y);
@@ -1459,12 +1564,7 @@ tablet_notify_buttons(struct tablet_disp
 	else
 		tablet_get_released_buttons(tablet, &buttons);
 
-	tablet_notify_button_mask(tablet,
-				  device,
-				  time,
-				  tool,
-				  &buttons,
-				  state);
+	tablet_notify_button_mask(tablet, device, time, tool, &buttons, state);
 }
 
 static void
@@ -1472,8 +1572,7 @@ sanitize_pressure_distance(struct tablet
 			   struct libinput_tablet_tool *tool)
 {
 	bool tool_in_contact;
-	const struct input_absinfo *distance,
-	                           *pressure;
+	const struct input_absinfo *distance, *pressure;
 
 	distance = libevdev_get_abs_info(tablet->device->evdev, ABS_DISTANCE);
 	/* Note: for pressure/distance sanitization we use the real pressure
@@ -1483,8 +1582,10 @@ sanitize_pressure_distance(struct tablet
 	if (!pressure || !distance)
 		return;
 
-	bool pressure_changed = bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE);
-	bool distance_changed = bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_DISTANCE);
+	bool pressure_changed =
+		bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE);
+	bool distance_changed =
+		bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_DISTANCE);
 
 	if (!pressure_changed && !distance_changed)
 		return;
@@ -1492,13 +1593,12 @@ sanitize_pressure_distance(struct tablet
 	/* Note: this is an arbitrary "in contact" decision rather than "tip
 	 * down". We use the lower threshold as minimum pressure value,
 	 * anything less than that gets filtered away */
-	struct libinput_tablet_tool_pressure_threshold* threshold =
+	struct libinput_tablet_tool_pressure_threshold *threshold =
 		tablet_tool_get_threshold(tablet, tool);
 	tool_in_contact = (pressure->value > threshold->threshold.lower);
 
 	/* Keep distance and pressure mutually exclusive */
-	if (distance &&
-	    distance->value > distance->minimum &&
+	if (distance && distance->value > distance->minimum &&
 	    pressure->value > pressure->minimum) {
 		if (tool_in_contact) {
 			clear_bit(tablet->changed_axes,
@@ -1525,33 +1625,35 @@ sanitize_mouse_lens_rotation(struct tabl
 	/* If we have a mouse/lens cursor and the tilt changed, the rotation
 	   changed. Mark this, calculate the angle later */
 	if ((tablet->current_tool.type == LIBINPUT_TABLET_TOOL_TYPE_MOUSE ||
-	    tablet->current_tool.type == LIBINPUT_TABLET_TOOL_TYPE_LENS) &&
+	     tablet->current_tool.type == LIBINPUT_TABLET_TOOL_TYPE_LENS) &&
 	    (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_X) ||
 	     bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_Y)))
 		set_bit(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
 }
 
 static void
-sanitize_tablet_axes(struct tablet_dispatch *tablet,
-		     struct libinput_tablet_tool *tool)
+sanitize_tablet_axes(struct tablet_dispatch *tablet, struct libinput_tablet_tool *tool)
 {
 	sanitize_pressure_distance(tablet, tool);
 	sanitize_mouse_lens_rotation(tablet);
 }
 
 static void
-set_pressure_offset(struct libinput_tablet_tool_pressure_threshold *threshold, int offset)
+set_pressure_offset(struct libinput_tablet_tool_pressure_threshold *threshold,
+		    pressure_offset_t offset_in_percent)
 {
-	threshold->offset = offset;
+	threshold->offset = offset_in_percent;
 	threshold->has_offset = true;
 
 	/* Adjust the tresholds accordingly - we use the same gap (4% in
 	 * device coordinates) between upper and lower as before which isn't
 	 * technically correct (our range shrunk) but it's easy to calculate.
 	 */
+	int units =
+		pressure_offset_to_absinfo(offset_in_percent, &threshold->abs_pressure);
 	int gap = threshold->threshold.upper - threshold->threshold.lower;
-	threshold->threshold.lower = offset;
-	threshold->threshold.upper = offset + gap;
+	threshold->threshold.lower = units;
+	threshold->threshold.upper = units + gap;
 }
 
 static void
@@ -1574,14 +1676,15 @@ update_pressure_offset(struct tablet_dis
 	 * If we are still pending the offset decision, only update the observed
 	 * offset value, don't actually set it to have an offset.
 	 */
-	int offset = pressure->value;
+	pressure_offset_t offset =
+		pressure_offset_from_absinfo(pressure, pressure->value);
 	struct libinput_tablet_tool_pressure_threshold *threshold =
 		tablet_tool_get_threshold(tablet, tool);
 	if (threshold->has_offset) {
-		if (offset < threshold->offset)
+		if (pressure_offset_cmp(offset, threshold->offset) < 0)
 			set_pressure_offset(threshold, offset);
 	} else if (threshold->heuristic_state != PRESSURE_HEURISTIC_STATE_DONE) {
-		threshold->offset = min(offset, threshold->offset);
+		threshold->offset = pressure_offset_min(offset, threshold->offset);
 	}
 }
 
@@ -1591,7 +1694,6 @@ detect_pressure_offset(struct tablet_dis
 		       struct libinput_tablet_tool *tool)
 {
 	const struct input_absinfo *pressure, *distance;
-	int offset;
 
 	if (tool->pressure.has_configured_range ||
 	    !bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE))
@@ -1608,24 +1710,26 @@ detect_pressure_offset(struct tablet_dis
 	if (!pressure)
 		return;
 
-	offset = pressure->value;
-	if (offset <= pressure->minimum)
+	int units = pressure->value;
+	if (units <= pressure->minimum)
 		return;
 
+	pressure_offset_t offset = pressure_offset_from_absinfo(pressure, units);
 	if (distance) {
 		/* If we're closer than 50% of the distance axis, skip pressure
 		 * offset detection, too likely to be wrong */
 		if (distance->value < axis_range_percentage(distance, 50))
 			return;
 	} else {
-                /* A device without distance will always have some pressure on
-                 * contact. Offset detection is delayed for a few proximity ins
-                 * in the hope we'll find the minimum value until then. That
-                 * offset is updated during motion events so by the time the
-                 * deciding prox-in arrives we should know the minimum offset.
-                 */
-                if (offset > pressure->minimum)
-			threshold->offset = min(offset, threshold->offset);
+		/* A device without distance will always have some pressure on
+		 * contact. Offset detection is delayed for a few proximity ins
+		 * in the hope we'll find the minimum value until then. That
+		 * offset is updated during motion events so by the time the
+		 * deciding prox-in arrives we should know the minimum offset.
+		 */
+		if (units > pressure->minimum)
+			threshold->offset =
+				pressure_offset_min(offset, threshold->offset);
 
 		switch (threshold->heuristic_state) {
 		case PRESSURE_HEURISTIC_STATE_PROXIN1:
@@ -1634,6 +1738,7 @@ detect_pressure_offset(struct tablet_dis
 			return;
 		case PRESSURE_HEURISTIC_STATE_DECIDE:
 			threshold->heuristic_state++;
+			units = pressure_offset_to_absinfo(threshold->offset, pressure);
 			offset = threshold->offset;
 			break;
 		case PRESSURE_HEURISTIC_STATE_DONE:
@@ -1641,25 +1746,28 @@ detect_pressure_offset(struct tablet_dis
 		}
 	}
 
-	if (offset <= pressure->minimum)
+	if (units <= pressure->minimum) {
 		return;
+	}
 
-	if (offset > axis_range_percentage(pressure, 50)) {
-		evdev_log_error(device,
-			 "Ignoring pressure offset greater than 50%% detected on tool %s (serial %#x). "
-			 "See %s/tablet-support.html\n",
-			 tablet_tool_type_to_string(tool->type),
-			 tool->serial,
-			 HTTP_DOC_LINK);
+	if (pressure_offset_gt(offset, 0.5)) {
+		evdev_log_error(
+			device,
+			"Ignoring pressure offset greater than 50%% detected on tool %s (serial %#x). "
+			"See %s/tablet-support.html\n",
+			tablet_tool_type_to_string(tool->type),
+			tool->serial,
+			HTTP_DOC_LINK);
 		return;
 	}
 
 	evdev_log_info(device,
-		 "Pressure offset detected on tool %s (serial %#x).  "
-		 "See %s/tablet-support.html\n",
-		 tablet_tool_type_to_string(tool->type),
-		 tool->serial,
-		 HTTP_DOC_LINK);
+		       "Pressure offset of %d%% detected on tool %s (serial %#x).  "
+		       "See %s/tablet-support.html\n",
+		       (int)(pressure_offset_as_double(offset) * 100),
+		       tablet_tool_type_to_string(tool->type),
+		       tool->serial,
+		       HTTP_DOC_LINK);
 
 	set_pressure_offset(threshold, offset);
 }
@@ -1677,17 +1785,14 @@ detect_tool_contact(struct tablet_dispat
 
 	/* if we have pressure, always use that for contact, not BTN_TOUCH */
 	if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_CONTACT))
-		evdev_log_bug_libinput(device,
-				       "Invalid status: entering contact\n");
+		evdev_log_bug_libinput(device, "Invalid status: entering contact\n");
 	if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_CONTACT) &&
 	    !tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY))
-		evdev_log_bug_libinput(device,
-				       "Invalid status: leaving contact\n");
+		evdev_log_bug_libinput(device, "Invalid status: leaving contact\n");
 
 	p = libevdev_get_abs_info(tablet->device->evdev, ABS_PRESSURE);
 	if (!p) {
-		evdev_log_bug_libinput(device,
-				       "Missing pressure axis\n");
+		evdev_log_bug_libinput(device, "Missing pressure axis\n");
 		return;
 	}
 	pressure = p->value;
@@ -1707,13 +1812,10 @@ static void
 tablet_mark_all_axes_changed(struct tablet_dispatch *tablet,
 			     struct libinput_tablet_tool *tool)
 {
-	static_assert(sizeof(tablet->changed_axes) ==
-			      sizeof(tool->axis_caps),
+	static_assert(sizeof(tablet->changed_axes) == sizeof(tool->axis_caps),
 		      "Mismatching array sizes");
 
-	memcpy(tablet->changed_axes,
-	       tool->axis_caps,
-	       sizeof(tablet->changed_axes));
+	memcpy(tablet->changed_axes, tool->axis_caps, sizeof(tablet->changed_axes));
 }
 
 static void
@@ -1737,10 +1839,8 @@ tablet_update_proximity_state(struct tab
 	if (dist < dist_max &&
 	    (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE) ||
 	     tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))) {
-		tablet_unset_status(tablet,
-				    TABLET_TOOL_OUT_OF_RANGE);
-		tablet_unset_status(tablet,
-				    TABLET_TOOL_OUT_OF_PROXIMITY);
+		tablet_unset_status(tablet, TABLET_TOOL_OUT_OF_RANGE);
+		tablet_unset_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY);
 		tablet_set_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY);
 		tablet_mark_all_axes_changed(tablet, tool);
 
@@ -1755,15 +1855,12 @@ tablet_update_proximity_state(struct tab
 	/* Still out of range/proximity */
 	if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE) ||
 	    tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY))
-	    return;
+		return;
 
 	/* Tool entered prox but is outside of permitted range */
-	if (tablet_has_status(tablet,
-			      TABLET_TOOL_ENTERING_PROXIMITY)) {
-		tablet_set_status(tablet,
-				  TABLET_TOOL_OUT_OF_RANGE);
-		tablet_unset_status(tablet,
-				    TABLET_TOOL_ENTERING_PROXIMITY);
+	if (tablet_has_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY)) {
+		tablet_set_status(tablet, TABLET_TOOL_OUT_OF_RANGE);
+		tablet_unset_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY);
 		return;
 	}
 
@@ -1777,7 +1874,7 @@ static struct phys_rect
 tablet_calculate_arbitration_rect(struct tablet_dispatch *tablet)
 {
 	struct evdev_device *device = tablet->device;
-	struct phys_rect r = {0};
+	struct phys_rect r = { 0 };
 	struct phys_coords mm;
 
 	mm = evdev_device_units_to_mm(device, &tablet->axes.point);
@@ -1820,7 +1917,7 @@ tablet_update_touch_device_rect(struct t
 				uint64_t time)
 {
 	struct evdev_dispatch *dispatch;
-	struct phys_rect rect = {0};
+	struct phys_rect rect = { 0 };
 
 	if (tablet->touch_device == NULL ||
 	    tablet->arbitration != ARBITRATION_IGNORE_RECT)
@@ -1866,10 +1963,10 @@ tablet_send_proximity_in(struct tablet_d
 
 static inline void
 tablet_send_proximity_out(struct tablet_dispatch *tablet,
-			 struct libinput_tablet_tool *tool,
-			 struct evdev_device *device,
-			 struct tablet_axes *axes,
-			 uint64_t time)
+			  struct libinput_tablet_tool *tool,
+			  struct evdev_device *device,
+			  struct tablet_axes *axes,
+			  uint64_t time)
 {
 	if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY) &&
 	    !tablet_has_status(tablet, TABLET_TOOL_OUTSIDE_AREA)) {
@@ -1946,8 +2043,7 @@ tablet_send_axes(struct tablet_dispatch
 	if (!tablet_has_status(tablet, TABLET_AXES_UPDATED))
 		return;
 
-	if (tablet_has_status(tablet,
-			      TABLET_TOOL_IN_CONTACT))
+	if (tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT))
 		tip_state = LIBINPUT_TABLET_TOOL_TIP_DOWN;
 	else
 		tip_state = LIBINPUT_TABLET_TOOL_TIP_UP;
@@ -1997,7 +2093,7 @@ tablet_send_events(struct tablet_dispatc
 		   struct evdev_device *device,
 		   uint64_t time)
 {
-	struct tablet_axes axes = {0};
+	struct tablet_axes axes = { 0 };
 
 	if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) {
 		/* Tool is leaving proximity, we can't rely on the last axis
@@ -2029,35 +2125,7 @@ tablet_send_events(struct tablet_dispatc
 	tablet_send_proximity_out(tablet, tool, device, &axes, time);
 }
 
-/**
- * Handling for the proximity out workaround. Some tablets only send
- * BTN_TOOL_PEN on the very first event, then leave it set even when the pen
- * leaves the detectable range. To libinput this looks like we always have
- * the pen in proximity.
- *
- * To avoid this, we set a timer on BTN_TOOL_PEN in. We expect the tablet to
- * continuously send events, and while it's doing so we keep updating the
- * timer. Once we go Xms without an event we assume proximity out and inject
- * a BTN_TOOL_PEN event into the sequence through the timer func.
- *
- * We need to remember that we did that, on the first event after the
- * timeout we need to emulate a BTN_TOOL_PEN event again to force proximity
- * in.
- *
- * Other tools never send the BTN_TOOL_PEN event. For those tools, we
- * piggyback along with the proximity out quirks by injecting
- * the event during the first event frame.
- */
-static inline void
-tablet_proximity_out_quirk_set_timer(struct tablet_dispatch *tablet,
-				     uint64_t time)
-{
-	if (tablet->quirks.need_to_force_prox_out)
-		libinput_timer_set(&tablet->quirks.prox_out_timer,
-				   time + FORCED_PROXOUT_TIMEOUT);
-}
-
-static bool
+static void
 tablet_update_tool_state(struct tablet_dispatch *tablet,
 			 struct evdev_device *device,
 			 uint64_t time)
@@ -2065,77 +2133,9 @@ tablet_update_tool_state(struct tablet_d
 	enum libinput_tablet_tool_type type;
 	uint32_t changed;
 	int state;
-	uint32_t doubled_up_new_tool_bit = 0;
-
-	/* we were already out of proximity but now got a tool update but
-	 * our tool state is zero - i.e. we got a valid prox out from the
-	 * device.
-	 */
-	if (tablet->quirks.proximity_out_forced &&
-	    tablet_has_status(tablet, TABLET_TOOL_UPDATED) &&
-	    !tablet->tool_state) {
-		tablet->quirks.need_to_force_prox_out = false;
-		tablet->quirks.proximity_out_forced = false;
-	}
-	/* We need to emulate a BTN_TOOL_PEN if we get an axis event (i.e.
-	 * stylus is def. in proximity) and:
-	 * - we forced a proximity out before, or
-	 * - on the very first event after init, because if we didn't get a
-	 *   BTN_TOOL_PEN and the state for the tool was 0, this device will
-	 *   never send the event.
-	 * We don't do this for pure button events because we discard those.
-	 *
-	 * But: on some devices the proximity out is delayed by the kernel,
-	 * so we get it after our forced prox-out has triggered. In that
-	 * case we need to just ignore the change.
-	 */
-	if (tablet_has_status(tablet, TABLET_AXES_UPDATED)) {
-		if (tablet->quirks.proximity_out_forced) {
-			if (!tablet_has_status(tablet, TABLET_TOOL_UPDATED)  &&
-			    !tablet->tool_state)
-				tablet->tool_state = bit(LIBINPUT_TABLET_TOOL_TYPE_PEN);
-			tablet->quirks.proximity_out_forced = false;
-		} else if (tablet->tool_state == 0 &&
-			    tablet->current_tool.type == LIBINPUT_TOOL_NONE) {
-			tablet->tool_state = bit(LIBINPUT_TABLET_TOOL_TYPE_PEN);
-			tablet->quirks.proximity_out_forced = false;
-		}
-	}
 
 	if (tablet->tool_state == tablet->prev_tool_state)
-		return false;
-
-	/* Kernel tools are supposed to be mutually exclusive, but we may have
-	 * two bits set due to firmware/kernel bugs.
-	 * Two cases that have been seen in the wild:
-	 * - BTN_TOOL_PEN on proximity in, followed by
-	 *   BTN_TOOL_RUBBER later, see #259
-	 *   -> We force a prox-out of the pen, trigger prox-in for eraser
-	 * - BTN_TOOL_RUBBER on proximity in, but BTN_TOOL_PEN when
-	 *   the tip is down, see #702.
-	 *   -> We ignore BTN_TOOL_PEN
-	 * In both cases the eraser is what we want, so we bias
-	 * towards that.
-	 */
-	if (tablet->tool_state & (tablet->tool_state - 1)) {
-		doubled_up_new_tool_bit = tablet->tool_state ^ tablet->prev_tool_state;
-
-		/* The new tool is the pen. Ignore it */
-		if (doubled_up_new_tool_bit == bit(LIBINPUT_TABLET_TOOL_TYPE_PEN)) {
-			tablet->tool_state &= ~bit(LIBINPUT_TABLET_TOOL_TYPE_PEN);
-			return false;
-		}
-
-		/* The new tool is some tool other than pen (usually eraser).
-		 * We set the current tool state to zero, thus setting
-		 * everything up for a prox out on the tool. Once that is set
-		 * up, we change the tool state to be the new one we just got.
-		 * When we re-process this function we now get the new tool
-		 * as prox in. Importantly, we basically rely on nothing else
-		 * happening in the meantime.
-		 */
-		tablet->tool_state = 0;
-	}
+		return;
 
 	changed = tablet->tool_state ^ tablet->prev_tool_state;
 	type = ffs(changed) - 1;
@@ -2143,31 +2143,7 @@ tablet_update_tool_state(struct tablet_d
 
 	tablet_update_tool(tablet, device, type, state);
 
-	/* The proximity timeout is only needed for BTN_TOOL_PEN, devices
-	 * that require it don't do erasers */
-	if (type == LIBINPUT_TABLET_TOOL_TYPE_PEN) {
-		if (state) {
-			tablet_proximity_out_quirk_set_timer(tablet, time);
-		} else {
-			/* If we get a BTN_TOOL_PEN 0 when *not* injecting
-			 * events it means the tablet will give us the right
-			 * events after all and we can disable our
-			 * timer-based proximity out.
-			 */
-			if (!tablet->quirks.proximity_out_in_progress)
-				tablet->quirks.need_to_force_prox_out = false;
-
-			libinput_timer_cancel(&tablet->quirks.prox_out_timer);
-		}
-	}
-
 	tablet->prev_tool_state = tablet->tool_state;
-
-	if (doubled_up_new_tool_bit) {
-		tablet->tool_state = doubled_up_new_tool_bit;
-		return true; /* need to re-process */
-	}
-	return false;
 }
 
 static struct libinput_tablet_tool *
@@ -2183,15 +2159,55 @@ tablet_get_current_tool(struct tablet_di
 }
 
 static void
-tablet_flush(struct tablet_dispatch *tablet,
-	     struct evdev_device *device,
-	     uint64_t time)
+update_pressure_range(struct tablet_dispatch *tablet,
+		      struct evdev_device *device,
+		      struct libinput_tablet_tool *tool)
+{
+	if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_PRESSURE))
+		return;
+
+	double min = tool->pressure.range.min;
+	double max = tool->pressure.range.max;
+
+	struct input_absinfo abs = *libevdev_get_abs_info(device->evdev, ABS_PRESSURE);
+
+	int minimum = axis_range_percentage(&abs, min * 100.0);
+	int maximum = axis_range_percentage(&abs, max * 100.0);
+
+	abs.minimum = minimum;
+	abs.maximum = maximum;
+
+	/* Only use the quirk pressure range if we don't have a custom range */
+	int hi, lo;
+	if (tool->pressure.range.min != 0.0 || tool->pressure.range.max != 1.0 ||
+	    !tablet_get_quirked_pressure_thresholds(tablet, &hi, &lo)) {
+		/* 5 and 1% of the pressure range */
+		hi = axis_range_percentage(&abs, 5);
+		lo = axis_range_percentage(&abs, 1);
+	}
+
+	struct libinput_tablet_tool_pressure_threshold *threshold =
+		tablet_tool_get_threshold(tablet, tool);
+	threshold->abs_pressure = abs;
+	threshold->threshold.upper = hi;
+	threshold->threshold.lower = lo;
+
+	/* Disable any heuristics */
+	if (tool->pressure.has_configured_range) {
+		threshold->has_offset = true;
+		threshold->heuristic_state = PRESSURE_HEURISTIC_STATE_DONE;
+		threshold->offset = pressure_offset_from_double(0.0);
+	} else if (threshold->has_offset) {
+		set_pressure_offset(threshold, threshold->offset);
+	}
+}
+
+static void
+tablet_flush(struct tablet_dispatch *tablet, struct evdev_device *device, uint64_t time)
 {
 	struct libinput_tablet_tool *tool;
-	bool process_tool_twice;
 
-reprocess:
-	process_tool_twice = tablet_update_tool_state(tablet, device, time);
+	tablet_update_tool_state(tablet, device, time);
 
 	tool = tablet_get_current_tool(tablet);
 	if (!tool)
@@ -2207,9 +2223,7 @@ reprocess:
 
 	if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) {
 		/* Release all stylus buttons */
-		memset(tablet->button_state.bits,
-		       0,
-		       sizeof(tablet->button_state.bits));
+		memset(tablet->button_state.bits, 0, sizeof(tablet->button_state.bits));
 		tablet_set_status(tablet, TABLET_BUTTONS_RELEASED);
 		if (tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT))
 			tablet_set_status(tablet, TABLET_TOOL_LEAVING_CONTACT);
@@ -2222,9 +2236,9 @@ reprocess:
 			 * implement so let's wait for someone to actually complain
 			 * about it.
 			 *
-			 * We allow a margin of 3% (6mm on a 200mm tablet) to be "within"
-			 * the area - there we clip to the area but do not ignore the
-			 * sequence.
+			 * We allow a margin of 3% (6mm on a 200mm tablet) to be
+			 * "within" the area - there we clip to the area but do not
+			 * ignore the sequence.
 			 */
 			const struct device_coords point = {
 				device->abs.absinfo_x->value,
@@ -2234,20 +2248,21 @@ reprocess:
 			const double margin = 0.03;
 			if (is_inside_area(tablet, &point, margin)) {
 				tablet_mark_all_axes_changed(tablet, tool);
+				update_pressure_range(tablet, device, tool);
 				update_pressure_offset(tablet, device, tool);
 				detect_pressure_offset(tablet, device, tool);
 				detect_tool_contact(tablet, device, tool);
 				sanitize_tablet_axes(tablet, tool);
 			} else {
 				tablet_set_status(tablet, TABLET_TOOL_OUTSIDE_AREA);
-				tablet_unset_status(tablet, TABLET_TOOL_ENTERING_PROXIMITY);
+				tablet_unset_status(tablet,
+						    TABLET_TOOL_ENTERING_PROXIMITY);
 			}
 		} else if (tablet_has_status(tablet, TABLET_AXES_UPDATED)) {
 			update_pressure_offset(tablet, device, tool);
 			detect_tool_contact(tablet, device, tool);
 			sanitize_tablet_axes(tablet, tool);
 		}
-
 	}
 
 	if (!tablet_has_status(tablet, TABLET_TOOL_OUTSIDE_AREA))
@@ -2264,10 +2279,8 @@ reprocess:
 		tablet_apply_rotation(device);
 		tablet_change_area(device);
 		tablet_history_reset(tablet);
+		tablet_tool_apply_eraser_button(tablet, tool);
 	}
-
-	if (process_tool_twice)
-		goto reprocess;
 }
 
 static inline void
@@ -2299,16 +2312,13 @@ tablet_toggle_touch_device(struct tablet
 			   uint64_t time)
 {
 	enum evdev_arbitration_state which;
-	struct phys_rect r = {0};
+	struct phys_rect r = { 0 };
 	struct phys_rect *rect = NULL;
 
-	if (tablet_has_status(tablet,
-			      TABLET_TOOL_OUT_OF_RANGE) ||
+	if (tablet_has_status(tablet, TABLET_TOOL_OUT_OF_RANGE) ||
 	    tablet_has_status(tablet, TABLET_NONE) ||
-	    tablet_has_status(tablet,
-			      TABLET_TOOL_LEAVING_PROXIMITY) ||
-	    tablet_has_status(tablet,
-			      TABLET_TOOL_OUT_OF_PROXIMITY)) {
+	    tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY) ||
+	    tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) {
 		which = ARBITRATION_NOT_ACTIVE;
 	} else if (tablet->axes.tilt.x == 0) {
 		which = ARBITRATION_IGNORE_ALL;
@@ -2322,16 +2332,13 @@ tablet_toggle_touch_device(struct tablet
 		return;
 	}
 
-	tablet_set_touch_device_enabled(tablet,
-					which,
-					rect,
-					time);
+	tablet_set_touch_device_enabled(tablet, which, rect, time);
 }
 
 static inline void
 tablet_reset_state(struct tablet_dispatch *tablet)
 {
-	struct button_state zero = {0};
+	struct button_state zero = { 0 };
 
 	/* Update state */
 	memcpy(&tablet->prev_button_state,
@@ -2346,58 +2353,15 @@ tablet_reset_state(struct tablet_dispatc
 }
 
 static void
-tablet_proximity_out_quirk_timer_func(uint64_t now, void *data)
-{
-	struct tablet_dispatch *tablet = data;
-	struct timeval tv = us2tv(now);
-	struct input_event events[2] = {
-		{ .input_event_sec = tv.tv_sec,
-		  .input_event_usec = tv.tv_usec,
-		  .type = EV_KEY,
-		  .code = BTN_TOOL_PEN,
-		  .value = 0 },
-		{ .input_event_sec = tv.tv_sec,
-		  .input_event_usec = tv.tv_usec,
-		  .type = EV_SYN,
-		  .code = SYN_REPORT,
-		  .value = 0 },
-	};
-
-	if (tablet_has_status(tablet, TABLET_TOOL_IN_CONTACT) ||
-	    tablet_has_status(tablet, TABLET_BUTTONS_DOWN)) {
-		tablet_proximity_out_quirk_set_timer(tablet, now);
-		return;
-	}
-
-	if (tablet->quirks.last_event_time > now - FORCED_PROXOUT_TIMEOUT) {
-		tablet_proximity_out_quirk_set_timer(tablet,
-						     tablet->quirks.last_event_time);
-		return;
-	}
-
-	evdev_log_debug(tablet->device, "tablet: forcing proximity after timeout\n");
-
-	tablet->quirks.proximity_out_in_progress = true;
-	ARRAY_FOR_EACH(events, e) {
-		tablet->base.interface->process(&tablet->base,
-						 tablet->device,
-						 e,
-						 now);
-	}
-	tablet->quirks.proximity_out_in_progress = false;
-
-	tablet->quirks.proximity_out_forced = true;
-}
-
-static void
-tablet_process(struct evdev_dispatch *dispatch,
-	       struct evdev_device *device,
-	       struct input_event *e,
-	       uint64_t time)
+tablet_process_event(struct evdev_dispatch *dispatch,
+		     struct evdev_device *device,
+		     struct evdev_event *e,
+		     uint64_t time)
 {
 	struct tablet_dispatch *tablet = tablet_dispatch(dispatch);
 
-	switch (e->type) {
+	uint16_t type = evdev_event_type(e);
+	switch (type) {
 	case EV_ABS:
 		tablet_process_absolute(tablet, device, e, time);
 		break;
@@ -2414,29 +2378,38 @@ tablet_process(struct evdev_dispatch *di
 		tablet_flush(tablet, device, time);
 		tablet_toggle_touch_device(tablet, device, time);
 		tablet_reset_state(tablet);
-		tablet->quirks.last_event_time = time;
 		break;
 	default:
 		evdev_log_error(device,
 				"Unexpected event type %s (%#x)\n",
-				libevdev_event_type_get_name(e->type),
-				e->type);
+				evdev_event_get_type_name(e),
+				evdev_event_type(e));
 		break;
 	}
 }
 
 static void
-tablet_suspend(struct evdev_dispatch *dispatch,
-	       struct evdev_device *device)
+tablet_process(struct evdev_dispatch *dispatch,
+	       struct evdev_device *device,
+	       struct evdev_frame *frame,
+	       uint64_t time)
+{
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+
+	for (size_t i = 0; i < nevents; i++) {
+		tablet_process_event(dispatch, device, &events[i], time);
+	}
+}
+
+static void
+tablet_suspend(struct evdev_dispatch *dispatch, struct evdev_device *device)
 {
 	struct tablet_dispatch *tablet = tablet_dispatch(dispatch);
 	struct libinput *li = tablet_libinput_context(tablet);
 	uint64_t now = libinput_now(li);
 
-	tablet_set_touch_device_enabled(tablet,
-					ARBITRATION_NOT_ACTIVE,
-					NULL,
-					now);
+	tablet_set_touch_device_enabled(tablet, ARBITRATION_NOT_ACTIVE, NULL, now);
 
 	if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) {
 		tablet_set_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY);
@@ -2445,23 +2418,34 @@ tablet_suspend(struct evdev_dispatch *di
 }
 
 static void
-tablet_destroy(struct evdev_dispatch *dispatch)
+tablet_remove(struct evdev_dispatch *dispatch)
 {
 	struct tablet_dispatch *tablet = tablet_dispatch(dispatch);
+	struct libinput_device *device = &tablet->device->base;
+	struct libinput *libinput = tablet_libinput_context(tablet);
 	struct libinput_tablet_tool *tool;
-	struct libinput *li = tablet_libinput_context(tablet);
 
-	libinput_timer_cancel(&tablet->quirks.prox_out_timer);
-	libinput_timer_destroy(&tablet->quirks.prox_out_timer);
+	list_for_each_safe(tool, &tablet->tool_list, link) {
+		if (tool->last_device == device) {
+			libinput_device_unref(tool->last_device);
+			tool->last_device = NULL;
+		}
+	}
 
-	list_for_each(tool, &li->tool_list, link) {
-		ARRAY_FOR_EACH(tool->pressure.thresholds, threshold) {
-			if (threshold->tablet_id == tablet->tablet_id) {
-				threshold->tablet_id = 0;
-				break;
-			}
+	list_for_each_safe(tool, &libinput->tool_list, link) {
+		if (tool->last_device == device) {
+			libinput_device_unref(tool->last_device);
+			tool->last_device = NULL;
 		}
 	}
+}
+
+static void
+tablet_destroy(struct evdev_dispatch *dispatch)
+{
+	struct tablet_dispatch *tablet = tablet_dispatch(dispatch);
+	struct libinput_tablet_tool *tool;
+	struct libinput *li = tablet_libinput_context(tablet);
 
 	list_for_each_safe(tool, &tablet->tool_list, link) {
 		libinput_tablet_tool_unref(tool);
@@ -2478,13 +2462,15 @@ tablet_setup_touch_arbitration(struct ev
 {
 	struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch);
 
-        /* We enable touch arbitration with the first touch screen/external
-         * touchpad we see. This may be wrong in some cases, so we have some
-         * heuristics in case we find a "better" device.
-         */
-        if (tablet->touch_device != NULL) {
-		struct libinput_device_group *group1 = libinput_device_get_device_group(&device->base);
-		struct libinput_device_group *group2 = libinput_device_get_device_group(&new_device->base);
+	/* We enable touch arbitration with the first touch screen/external
+	 * touchpad we see. This may be wrong in some cases, so we have some
+	 * heuristics in case we find a "better" device.
+	 */
+	if (tablet->touch_device != NULL) {
+		struct libinput_device_group *group1 =
+			libinput_device_get_device_group(&device->base);
+		struct libinput_device_group *group2 =
+			libinput_device_get_device_group(&new_device->base);
 
 		/* same phsical device? -> better, otherwise keep the one we have */
 		if (group1 != group2)
@@ -2510,12 +2496,13 @@ tablet_setup_touch_arbitration(struct ev
 }
 
 static void
-tablet_setup_rotation(struct evdev_device *device,
-		      struct evdev_device *new_device)
+tablet_setup_rotation(struct evdev_device *device, struct evdev_device *new_device)
 {
 	struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch);
-	struct libinput_device_group *group1 = libinput_device_get_device_group(&device->base);
-	struct libinput_device_group *group2 = libinput_device_get_device_group(&new_device->base);
+	struct libinput_device_group *group1 =
+		libinput_device_get_device_group(&device->base);
+	struct libinput_device_group *group2 =
+		libinput_device_get_device_group(&new_device->base);
 
 	if (tablet->rotation.touch_device == NULL && (group1 == group2)) {
 		evdev_log_debug(device,
@@ -2532,13 +2519,12 @@ tablet_setup_rotation(struct evdev_devic
 }
 
 static void
-tablet_device_added(struct evdev_device *device,
-		    struct evdev_device *added_device)
+tablet_device_added(struct evdev_device *device, struct evdev_device *added_device)
 {
 	bool is_touchscreen, is_ext_touchpad;
 
-	is_touchscreen = evdev_device_has_capability(added_device,
-						     LIBINPUT_DEVICE_CAP_TOUCH);
+	is_touchscreen =
+		evdev_device_has_capability(added_device, LIBINPUT_DEVICE_CAP_TOUCH);
 	is_ext_touchpad = evdev_device_has_capability(added_device,
 						      LIBINPUT_DEVICE_CAP_POINTER) &&
 			  (added_device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD);
@@ -2551,8 +2537,7 @@ tablet_device_added(struct evdev_device
 }
 
 static void
-tablet_device_removed(struct evdev_device *device,
-		      struct evdev_device *removed_device)
+tablet_device_removed(struct evdev_device *device, struct evdev_device *removed_device)
 {
 	struct tablet_dispatch *tablet = tablet_dispatch(device->dispatch);
 
@@ -2571,7 +2556,6 @@ tablet_check_initial_proximity(struct ev
 			       struct evdev_dispatch *dispatch)
 {
 	struct tablet_dispatch *tablet = tablet_dispatch(dispatch);
-	struct libinput *li = tablet_libinput_context(tablet);
 	int code, state;
 	enum libinput_tablet_tool_type tool;
 
@@ -2581,10 +2565,8 @@ tablet_check_initial_proximity(struct ev
 		code = tablet_tool_to_evcode(tool);
 
 		/* we only expect one tool to be in proximity at a time */
-		if (libevdev_fetch_event_value(device->evdev,
-						EV_KEY,
-						code,
-						&state) && state) {
+		if (libevdev_fetch_event_value(device->evdev, EV_KEY, code, &state) &&
+		    state) {
 			tablet->tool_state = bit(tool);
 			tablet->prev_tool_state = bit(tool);
 			break;
@@ -2595,13 +2577,9 @@ tablet_check_initial_proximity(struct ev
 		return;
 
 	tablet_update_tool(tablet, device, tool, state);
-	if (tablet->quirks.need_to_force_prox_out)
-		tablet_proximity_out_quirk_set_timer(tablet, libinput_now(li));
 
 	tablet->current_tool.id =
-		libevdev_get_event_value(device->evdev,
-					 EV_ABS,
-					 ABS_MISC);
+		libevdev_get_event_value(device->evdev, EV_ABS, ABS_MISC);
 
 	/* we can't fetch MSC_SERIAL from the kernel, so we set the serial
 	 * to 0 for now. On the first real event from the device we get the
@@ -2637,7 +2615,7 @@ tablet_left_handed_toggled(struct evdev_
 static struct evdev_dispatch_interface tablet_interface = {
 	.process = tablet_process,
 	.suspend = tablet_suspend,
-	.remove = NULL,
+	.remove = tablet_remove,
 	.destroy = tablet_destroy,
 	.device_added = tablet_device_added,
 	.device_removed = tablet_device_removed,
@@ -2655,7 +2633,8 @@ tablet_init_calibration(struct tablet_di
 			struct evdev_device *device,
 			bool is_display_tablet)
 {
-	if (is_display_tablet || libevdev_has_property(device->evdev, INPUT_PROP_DIRECT))
+	if (is_display_tablet ||
+	    libevdev_has_property(device->evdev, INPUT_PROP_DIRECT))
 		evdev_init_calibration(device, &tablet->calibration);
 }
 
@@ -2675,8 +2654,8 @@ tablet_area_set_rectangle(struct libinpu
 	if (rectangle->x1 >= rectangle->x2 || rectangle->y1 >= rectangle->y2)
 		return LIBINPUT_CONFIG_STATUS_INVALID;
 
-	if (rectangle->x1 < 0.0 || rectangle->x2 > 1.0 ||
-	    rectangle->y1 < 0.0 || rectangle->y2 > 1.0)
+	if (rectangle->x1 < 0.0 || rectangle->x2 > 1.0 || rectangle->y1 < 0.0 ||
+	    rectangle->y2 > 1.0)
 		return LIBINPUT_CONFIG_STATUS_INVALID;
 
 	tablet->area.want_rect = *rectangle;
@@ -2699,17 +2678,22 @@ static struct libinput_config_area_recta
 tablet_area_get_default_rectangle(struct libinput_device *device)
 {
 	struct libinput_config_area_rectangle rect = {
-		0.0, 0.0, 1.0, 1.0,
+		0.0,
+		0.0,
+		1.0,
+		1.0,
 	};
 	return rect;
 }
 
 static void
-tablet_init_area(struct tablet_dispatch *tablet,
-		 struct evdev_device *device)
+tablet_init_area(struct tablet_dispatch *tablet, struct evdev_device *device)
 {
-	tablet->area.rect = (struct libinput_config_area_rectangle) {
-		0.0, 0.0, 1.0, 1.0,
+	tablet->area.rect = (struct libinput_config_area_rectangle){
+		0.0,
+		0.0,
+		1.0,
+		1.0,
 	};
 	tablet->area.want_rect = tablet->area.rect;
 	tablet->area.x = *device->abs.absinfo_x;
@@ -2720,7 +2704,8 @@ tablet_init_area(struct tablet_dispatch
 		tablet->area.config.has_rectangle = tablet_area_has_rectangle;
 		tablet->area.config.set_rectangle = tablet_area_set_rectangle;
 		tablet->area.config.get_rectangle = tablet_area_get_rectangle;
-		tablet->area.config.get_default_rectangle = tablet_area_get_default_rectangle;
+		tablet->area.config.get_default_rectangle =
+			tablet_area_get_default_rectangle;
 	}
 }
 
@@ -2756,7 +2741,7 @@ tablet_accel_config_get_profiles(struct
 
 static enum libinput_config_status
 tablet_accel_config_set_profile(struct libinput_device *libinput_device,
-			    enum libinput_config_accel_profile profile)
+				enum libinput_config_accel_profile profile)
 {
 	return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
 }
@@ -2782,8 +2767,7 @@ tablet_init_accel(struct tablet_dispatch
 	x = device->abs.absinfo_x;
 	y = device->abs.absinfo_y;
 
-	filter = create_pointer_accelerator_filter_tablet(x->resolution,
-							  y->resolution);
+	filter = create_pointer_accelerator_filter_tablet(x->resolution, y->resolution);
 	if (!filter)
 		return -1;
 
@@ -2794,31 +2778,31 @@ tablet_init_accel(struct tablet_dispatch
 	device->pointer.config.get_profiles = tablet_accel_config_get_profiles;
 	device->pointer.config.set_profile = tablet_accel_config_set_profile;
 	device->pointer.config.get_profile = tablet_accel_config_get_profile;
-	device->pointer.config.get_default_profile = tablet_accel_config_get_default_profile;
+	device->pointer.config.get_default_profile =
+		tablet_accel_config_get_default_profile;
 
 	return 0;
 }
 
 static void
-tablet_init_left_handed(struct evdev_device *device,
-			WacomDevice *wacom)
+tablet_init_left_handed(struct evdev_device *device, WacomDevice *wacom)
 {
 	bool has_left_handed = true;
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	has_left_handed = !wacom || libwacom_is_reversible(wacom);
 #endif
 	if (has_left_handed)
-		evdev_init_left_handed(device,
-				       tablet_change_to_left_handed);
+		evdev_init_left_handed(device, tablet_change_to_left_handed);
 }
 
 static inline bool
 tablet_is_display_tablet(WacomDevice *wacom)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	return !wacom ||
-		(libwacom_get_integration_flags(wacom) & (WACOM_DEVICE_INTEGRATED_SYSTEM|WACOM_DEVICE_INTEGRATED_DISPLAY));
+	       (libwacom_get_integration_flags(wacom) &
+		(WACOM_DEVICE_INTEGRATED_SYSTEM | WACOM_DEVICE_INTEGRATED_DISPLAY));
 #else
 	return true;
 #endif
@@ -2827,7 +2811,7 @@ tablet_is_display_tablet(WacomDevice *wa
 static inline bool
 tablet_is_aes(struct evdev_device *device, WacomDevice *wacom)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	int vid = evdev_device_get_id_vendor(device);
 	/* Wacom-specific check for whether smoothing is required:
 	 * libwacom keeps all the AES pens in a single group, so any device
@@ -2837,7 +2821,10 @@ tablet_is_aes(struct evdev_device *devic
 	 */
 	if (wacom && vid == VENDOR_ID_WACOM) {
 		int nstyli;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 		const int *stylus_ids = libwacom_get_supported_styli(wacom, &nstyli);
+#pragma GCC diagnostic pop
 		for (int i = 0; i < nstyli; i++) {
 			if (stylus_ids[i] == 0x11) {
 				return true;
@@ -2852,27 +2839,23 @@ tablet_is_aes(struct evdev_device *devic
 static void
 tablet_init_smoothing(struct evdev_device *device,
 		      struct tablet_dispatch *tablet,
-		      bool is_aes)
+		      bool is_aes,
+		      bool is_virtual)
 {
 	size_t history_size = ARRAY_LENGTH(tablet->history.samples);
-	struct quirks_context *quirks = NULL;
-	struct quirks *q = NULL;
 	bool use_smoothing = true;
 
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
-
-	/* By default, always enable smoothing except on AES devices.
+	/* By default, always enable smoothing except on AES or uinput devices.
 	 * AttrTabletSmoothing can override this, if necessary.
 	 */
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
 	if (!q || !quirks_get_bool(q, QUIRK_ATTR_TABLET_SMOOTHING, &use_smoothing))
-		use_smoothing = !is_aes;
+		use_smoothing = !is_aes && !is_virtual;
 
 	/* Setting the history size to 1 means we never do any actual smoothing. */
 	if (!use_smoothing)
 		history_size = 1;
 
-	quirks_unref(q);
 	tablet->history.size = history_size;
 }
 
@@ -2884,7 +2867,7 @@ tablet_reject_device(struct evdev_device
 	bool has_xy, has_pen, has_btn_stylus, has_size;
 
 	has_xy = libevdev_has_event_code(evdev, EV_ABS, ABS_X) &&
-	         libevdev_has_event_code(evdev, EV_ABS, ABS_Y);
+		 libevdev_has_event_code(evdev, EV_ABS, ABS_Y);
 	has_pen = libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_PEN);
 	has_btn_stylus = libevdev_has_event_code(evdev, EV_KEY, BTN_STYLUS);
 	has_size = evdev_device_get_size(device, &w, &h) == 0;
@@ -2903,8 +2886,7 @@ tablet_reject_device(struct evdev_device
 }
 
 static void
-tablet_fix_tilt(struct tablet_dispatch *tablet,
-		struct evdev_device *device)
+tablet_fix_tilt(struct tablet_dispatch *tablet, struct evdev_device *device)
 {
 	struct libevdev *evdev = device->evdev;
 
@@ -2954,8 +2936,7 @@ tablet_fix_tilt(struct tablet_dispatch *
 }
 
 static int
-tablet_init(struct tablet_dispatch *tablet,
-	    struct evdev_device *device)
+tablet_init(struct tablet_dispatch *tablet, struct evdev_device *device)
 {
 	static unsigned int tablet_ids = 0;
 	struct libinput *li = evdev_libinput_context(device);
@@ -2963,7 +2944,7 @@ tablet_init(struct tablet_dispatch *tabl
 	enum libinput_tablet_tool_axis axis;
 	int rc = -1;
 	WacomDevice *wacom = NULL;
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	WacomDeviceDatabase *db = libinput_libwacom_ref(li);
 	if (db) {
 		char event_path[64];
@@ -2973,17 +2954,19 @@ tablet_init(struct tablet_dispatch *tabl
 			 evdev_device_get_sysname(device));
 		wacom = libwacom_new_from_path(db, event_path, WFALLBACK_NONE, NULL);
 		if (!wacom) {
-			wacom = libwacom_new_from_usbid(db,
-							evdev_device_get_id_vendor(device),
-							evdev_device_get_id_product(device),
-							NULL);
+			wacom = libwacom_new_from_usbid(
+				db,
+				evdev_device_get_id_vendor(device),
+				evdev_device_get_id_product(device),
+				NULL);
 		}
 		if (!wacom) {
-			evdev_log_info(device,
-				       "device \"%s\" (%04x:%04x) is not known to libwacom\n",
-				       evdev_device_get_name(device),
-				       evdev_device_get_id_vendor(device),
-				       evdev_device_get_id_product(device));
+			evdev_log_info(
+				device,
+				"device \"%s\" (%04x:%04x) is not known to libwacom\n",
+				evdev_device_get_name(device),
+				evdev_device_get_id_vendor(device),
+				evdev_device_get_id_product(device));
 		}
 	}
 #endif
@@ -3000,11 +2983,11 @@ tablet_init(struct tablet_dispatch *tabl
 		goto out;
 
 	bool is_aes = tablet_is_aes(device, wacom);
+	bool is_virtual = evdev_device_is_virtual(device);
 	bool is_display_tablet = tablet_is_display_tablet(wacom);
 
 	if (!libevdev_has_event_code(evdev, EV_KEY, BTN_TOOL_PEN)) {
 		libevdev_enable_event_code(evdev, EV_KEY, BTN_TOOL_PEN, NULL);
-		tablet->quirks.proximity_out_forced = true;
 	}
 
 	/* Our rotation code only works with Wacoms, let's wait until
@@ -3024,10 +3007,9 @@ tablet_init(struct tablet_dispatch *tabl
 
 	evdev_init_sendevents(device, &tablet->base);
 	tablet_init_left_handed(device, wacom);
-	tablet_init_smoothing(device, tablet, is_aes);
+	tablet_init_smoothing(device, tablet, is_aes, is_virtual);
 
-	for (axis = LIBINPUT_TABLET_TOOL_AXIS_X;
-	     axis <= LIBINPUT_TABLET_TOOL_AXIS_MAX;
+	for (axis = LIBINPUT_TABLET_TOOL_AXIS_X; axis <= LIBINPUT_TABLET_TOOL_AXIS_MAX;
 	     axis++) {
 		if (tablet_device_has_axis(tablet, axis))
 			set_bit(tablet->axis_caps, axis);
@@ -3035,19 +3017,9 @@ tablet_init(struct tablet_dispatch *tabl
 
 	tablet_set_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY);
 
-	/* We always enable the proximity out quirk, but disable it once a
-	   device gives us the right event sequence */
-	tablet->quirks.need_to_force_prox_out = true;
-
-	libinput_timer_init(&tablet->quirks.prox_out_timer,
-			    li,
-			    "proxout",
-			    tablet_proximity_out_quirk_timer_func,
-			    tablet);
-
 	rc = 0;
 out:
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	if (wacom)
 		libwacom_destroy(wacom);
 	if (db)
@@ -3064,10 +3036,6 @@ evdev_tablet_create(struct evdev_device
 
 	libinput_libwacom_ref(li);
 
-	/* Stop false positives caused by the forced proximity code */
-	if (getenv("LIBINPUT_RUNNING_TEST_SUITE"))
-		FORCED_PROXOUT_TIMEOUT = 150 * 1000; /* µs */
-
 	tablet = zalloc(sizeof *tablet);
 
 	if (tablet_init(tablet, device) != 0) {
diff -pruN 1.28.1-1/src/evdev-tablet.h 1.30.0-1/src/evdev-tablet.h
--- 1.28.1-1/src/evdev-tablet.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-tablet.h	2025-11-25 03:40:43.000000000 +0000
@@ -27,8 +27,8 @@
 
 #include "evdev.h"
 
-#if !HAVE_LIBWACOM
-typedef void * WacomDevice;
+#ifndef HAVE_LIBWACOM
+typedef void *WacomDevice;
 #endif
 
 #define LIBINPUT_TABLET_TOOL_AXIS_NONE 0
@@ -38,20 +38,20 @@ typedef void * WacomDevice;
 #define TABLET_HISTORY_LENGTH 4
 
 enum tablet_status {
-	TABLET_NONE			= 0,
-	TABLET_AXES_UPDATED		= bit(0),
-	TABLET_BUTTONS_PRESSED		= bit(1),
-	TABLET_BUTTONS_DOWN		= bit(2),
-	TABLET_BUTTONS_RELEASED		= bit(3),
-	TABLET_TOOL_UPDATED		= bit(4),
-	TABLET_TOOL_IN_CONTACT		= bit(5),
-	TABLET_TOOL_LEAVING_PROXIMITY	= bit(6),
-	TABLET_TOOL_OUT_OF_PROXIMITY	= bit(7),
-	TABLET_TOOL_ENTERING_PROXIMITY	= bit(8),
-	TABLET_TOOL_ENTERING_CONTACT	= bit(9),
-	TABLET_TOOL_LEAVING_CONTACT	= bit(10),
-	TABLET_TOOL_OUT_OF_RANGE	= bit(11),
-	TABLET_TOOL_OUTSIDE_AREA        = bit(12),
+	TABLET_NONE = 0,
+	TABLET_AXES_UPDATED = bit(0),
+	TABLET_BUTTONS_PRESSED = bit(1),
+	TABLET_BUTTONS_DOWN = bit(2),
+	TABLET_BUTTONS_RELEASED = bit(3),
+	TABLET_TOOL_UPDATED = bit(4),
+	TABLET_TOOL_IN_CONTACT = bit(5),
+	TABLET_TOOL_LEAVING_PROXIMITY = bit(6),
+	TABLET_TOOL_OUT_OF_PROXIMITY = bit(7),
+	TABLET_TOOL_ENTERING_PROXIMITY = bit(8),
+	TABLET_TOOL_ENTERING_CONTACT = bit(9),
+	TABLET_TOOL_LEAVING_CONTACT = bit(10),
+	TABLET_TOOL_OUT_OF_RANGE = bit(11),
+	TABLET_TOOL_OUTSIDE_AREA = bit(12),
 };
 
 struct button_state {
@@ -116,19 +116,9 @@ struct tablet_dispatch {
 		bool rotate;
 		bool want_rotate;
 	} rotation;
-
-	struct {
-		bool need_to_force_prox_out;
-		struct libinput_timer prox_out_timer;
-		bool proximity_out_forced;
-		uint64_t last_event_time;
-
-		/* true while injecting BTN_TOOL_PEN events */
-		bool proximity_out_in_progress;
-	} quirks;
 };
 
-static inline struct tablet_dispatch*
+static inline struct tablet_dispatch *
 tablet_dispatch(struct evdev_dispatch *dispatch)
 {
 	evdev_verify_dispatch_type(dispatch, DISPATCH_TABLET);
@@ -137,50 +127,36 @@ tablet_dispatch(struct evdev_dispatch *d
 }
 
 static inline enum libinput_tablet_tool_axis
-evcode_to_axis(const uint32_t evcode)
+evdev_usage_to_axis(evdev_usage_t usage)
 {
 	enum libinput_tablet_tool_axis axis;
 
-	switch (evcode) {
-	case ABS_X:
+	switch (evdev_usage_enum(usage)) {
+	case EVDEV_ABS_X:
 		axis = LIBINPUT_TABLET_TOOL_AXIS_X;
 		break;
-	case ABS_Y:
+	case EVDEV_ABS_Y:
 		axis = LIBINPUT_TABLET_TOOL_AXIS_Y;
 		break;
-	case ABS_Z:
+	case EVDEV_ABS_Z:
 		axis = LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z;
 		break;
-	case ABS_DISTANCE:
+	case EVDEV_ABS_DISTANCE:
 		axis = LIBINPUT_TABLET_TOOL_AXIS_DISTANCE;
 		break;
-	case ABS_PRESSURE:
+	case EVDEV_ABS_PRESSURE:
 		axis = LIBINPUT_TABLET_TOOL_AXIS_PRESSURE;
 		break;
-	case ABS_TILT_X:
+	case EVDEV_ABS_TILT_X:
 		axis = LIBINPUT_TABLET_TOOL_AXIS_TILT_X;
 		break;
-	case ABS_TILT_Y:
+	case EVDEV_ABS_TILT_Y:
 		axis = LIBINPUT_TABLET_TOOL_AXIS_TILT_Y;
 		break;
-	case ABS_WHEEL:
+	case EVDEV_ABS_WHEEL:
 		axis = LIBINPUT_TABLET_TOOL_AXIS_SLIDER;
 		break;
-	default:
-		axis = LIBINPUT_TABLET_TOOL_AXIS_NONE;
-		break;
-	}
-
-	return axis;
-}
-
-static inline enum libinput_tablet_tool_axis
-rel_evcode_to_axis(const uint32_t evcode)
-{
-	enum libinput_tablet_tool_axis axis;
-
-	switch (evcode) {
-	case REL_WHEEL:
+	case EVDEV_REL_WHEEL:
 		axis = LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL;
 		break;
 	default:
@@ -240,13 +216,27 @@ tablet_tool_to_evcode(enum libinput_tabl
 	int code;
 
 	switch (type) {
-	case LIBINPUT_TABLET_TOOL_TYPE_PEN:	  code = BTN_TOOL_PEN;		break;
-	case LIBINPUT_TABLET_TOOL_TYPE_ERASER:	  code = BTN_TOOL_RUBBER;	break;
-	case LIBINPUT_TABLET_TOOL_TYPE_BRUSH:	  code = BTN_TOOL_BRUSH;	break;
-	case LIBINPUT_TABLET_TOOL_TYPE_PENCIL:	  code = BTN_TOOL_PENCIL;	break;
-	case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH:  code = BTN_TOOL_AIRBRUSH;	break;
-	case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:	  code = BTN_TOOL_MOUSE;	break;
-	case LIBINPUT_TABLET_TOOL_TYPE_LENS:	  code = BTN_TOOL_LENS;		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_PEN:
+		code = BTN_TOOL_PEN;
+		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_ERASER:
+		code = BTN_TOOL_RUBBER;
+		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_BRUSH:
+		code = BTN_TOOL_BRUSH;
+		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_PENCIL:
+		code = BTN_TOOL_PENCIL;
+		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH:
+		code = BTN_TOOL_AIRBRUSH;
+		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
+		code = BTN_TOOL_MOUSE;
+		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_LENS:
+		code = BTN_TOOL_LENS;
+		break;
 	default:
 		abort();
 	}
@@ -260,13 +250,27 @@ tablet_tool_type_to_string(enum libinput
 	const char *str;
 
 	switch (type) {
-	case LIBINPUT_TABLET_TOOL_TYPE_PEN:	  str = "pen";		break;
-	case LIBINPUT_TABLET_TOOL_TYPE_ERASER:	  str = "eraser";	break;
-	case LIBINPUT_TABLET_TOOL_TYPE_BRUSH:	  str = "brush";	break;
-	case LIBINPUT_TABLET_TOOL_TYPE_PENCIL:	  str = "pencil";	break;
-	case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH:  str = "airbrush";	break;
-	case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:	  str = "mouse";	break;
-	case LIBINPUT_TABLET_TOOL_TYPE_LENS:	  str = "lens";		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_PEN:
+		str = "pen";
+		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_ERASER:
+		str = "eraser";
+		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_BRUSH:
+		str = "brush";
+		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_PENCIL:
+		str = "pencil";
+		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH:
+		str = "airbrush";
+		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
+		str = "mouse";
+		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_LENS:
+		str = "lens";
+		break;
 	default:
 		abort();
 	}
diff -pruN 1.28.1-1/src/evdev-totem.c 1.30.0-1/src/evdev-totem.c
--- 1.28.1-1/src/evdev-totem.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-totem.c	2025-11-25 03:40:43.000000000 +0000
@@ -22,6 +22,7 @@
  */
 
 #include "config.h"
+
 #include "evdev.h"
 
 enum totem_slot_state {
@@ -59,7 +60,7 @@ struct totem_dispatch {
 	enum evdev_arbitration_state arbitration_state;
 };
 
-static inline struct totem_dispatch*
+static inline struct totem_dispatch *
 totem_dispatch(struct evdev_dispatch *totem)
 {
 	evdev_verify_dispatch_type(totem, DISPATCH_TOTEM);
@@ -81,20 +82,18 @@ totem_new_tool(struct totem_dispatch *to
 
 	tool = zalloc(sizeof *tool);
 
-	*tool = (struct libinput_tablet_tool) {
+	*tool = (struct libinput_tablet_tool){
 		.type = LIBINPUT_TABLET_TOOL_TYPE_TOTEM,
 		.serial = 0,
 		.tool_id = 0,
 		.refcount = 1,
 	};
 
-	ARRAY_FOR_EACH(tool->pressure.thresholds, t) {
-		t->tablet_id = 0;
-		t->offset = 0;
-		t->has_offset = false;
-		t->threshold.lower = 0;
-		t->threshold.upper = 1;
-	}
+	tool->pressure.threshold.tablet_id = 0;
+	tool->pressure.threshold.offset = pressure_offset_from_double(0.0);
+	tool->pressure.threshold.has_offset = false;
+	tool->pressure.threshold.threshold.lower = 0;
+	tool->pressure.threshold.threshold.upper = 1;
 
 	set_bit(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_X);
 	set_bit(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_Y);
@@ -148,30 +147,32 @@ totem_set_touch_device_enabled(struct to
 	dispatch = touch_device->dispatch;
 
 	if (enable_touch_device) {
-	    if (dispatch->interface->touch_arbitration_toggle)
-		dispatch->interface->touch_arbitration_toggle(dispatch,
-							      touch_device,
-							      state,
-							      rect,
-							      time);
+		if (dispatch->interface->touch_arbitration_toggle)
+			dispatch->interface->touch_arbitration_toggle(dispatch,
+								      touch_device,
+								      state,
+								      rect,
+								      time);
 	} else {
 		switch (totem->arbitration_state) {
 		case ARBITRATION_IGNORE_ALL:
 			abort();
 		case ARBITRATION_NOT_ACTIVE:
 			if (dispatch->interface->touch_arbitration_toggle)
-				dispatch->interface->touch_arbitration_toggle(dispatch,
-									      touch_device,
-									      state,
-									      rect,
-									      time);
+				dispatch->interface->touch_arbitration_toggle(
+					dispatch,
+					touch_device,
+					state,
+					rect,
+					time);
 			break;
 		case ARBITRATION_IGNORE_RECT:
 			if (dispatch->interface->touch_arbitration_update_rect)
-				dispatch->interface->touch_arbitration_update_rect(dispatch,
-										   touch_device,
-										   rect,
-										   time);
+				dispatch->interface->touch_arbitration_update_rect(
+					dispatch,
+					touch_device,
+					rect,
+					time);
 			break;
 		}
 	}
@@ -181,21 +182,21 @@ totem_set_touch_device_enabled(struct to
 static void
 totem_process_key(struct totem_dispatch *totem,
 		  struct evdev_device *device,
-		  struct input_event *e,
+		  struct evdev_event *e,
 		  uint64_t time)
 {
 	/* ignore kernel key repeat */
 	if (e->value == 2)
 		return;
 
-	switch(e->code) {
-	case BTN_0:
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_BTN_0:
 		totem->button_state_now = !!e->value;
 		break;
 	default:
 		evdev_log_info(device,
 			       "Unhandled KEY event code %#x\n",
-			       e->code);
+			       evdev_usage_as_uint32_t(e->usage));
 		break;
 	}
 }
@@ -203,13 +204,13 @@ totem_process_key(struct totem_dispatch
 static void
 totem_process_abs(struct totem_dispatch *totem,
 		  struct evdev_device *device,
-		  struct input_event *e,
+		  struct evdev_event *e,
 		  uint64_t time)
 {
 	struct totem_slot *slot = &totem->slots[totem->slot];
 
-	switch(e->code) {
-	case ABS_MT_SLOT:
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_ABS_MT_SLOT:
 		if ((size_t)e->value >= totem->nslots) {
 			evdev_log_bug_libinput(device,
 					       "exceeded slot count (%d of max %zd)\n",
@@ -219,7 +220,7 @@ totem_process_abs(struct totem_dispatch
 		}
 		totem->slot = e->value;
 		return;
-	case ABS_MT_TRACKING_ID:
+	case EVDEV_ABS_MT_TRACKING_ID:
 		/* If the totem is already down on init, we currently
 		   ignore it */
 		if (e->value >= 0)
@@ -227,35 +228,32 @@ totem_process_abs(struct totem_dispatch
 		else if (slot->state != SLOT_STATE_NONE)
 			slot->state = SLOT_STATE_END;
 		break;
-	case ABS_MT_POSITION_X:
+	case EVDEV_ABS_MT_POSITION_X:
 		set_bit(slot->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_X);
 		break;
-	case ABS_MT_POSITION_Y:
+	case EVDEV_ABS_MT_POSITION_Y:
 		set_bit(slot->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_Y);
 		break;
-	case ABS_MT_TOUCH_MAJOR:
-		set_bit(slot->changed_axes,
-			LIBINPUT_TABLET_TOOL_AXIS_SIZE_MAJOR);
-		break;
-	case ABS_MT_TOUCH_MINOR:
-		set_bit(slot->changed_axes,
-			LIBINPUT_TABLET_TOOL_AXIS_SIZE_MINOR);
-		break;
-	case ABS_MT_ORIENTATION:
-		set_bit(slot->changed_axes,
-			LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
+	case EVDEV_ABS_MT_TOUCH_MAJOR:
+		set_bit(slot->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_SIZE_MAJOR);
+		break;
+	case EVDEV_ABS_MT_TOUCH_MINOR:
+		set_bit(slot->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_SIZE_MINOR);
+		break;
+	case EVDEV_ABS_MT_ORIENTATION:
+		set_bit(slot->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
 		break;
-	case ABS_MT_TOOL_TYPE:
+	case EVDEV_ABS_MT_TOOL_TYPE:
 		if (e->value != MT_TOOL_DIAL) {
 			evdev_log_info(device,
 				       "Unexpected tool type %#x, changing to dial\n",
-				       e->code);
+				       evdev_usage_as_uint32_t(e->usage));
 		}
 		break;
 	default:
 		evdev_log_info(device,
 			       "Unhandled ABS event code %#x\n",
-			       e->code);
+			       evdev_usage_as_uint32_t(e->usage));
 		break;
 	}
 }
@@ -268,8 +266,8 @@ totem_slot_fetch_axes(struct totem_dispa
 		      uint64_t time)
 {
 	struct evdev_device *device = totem->device;
-	const char tmp[sizeof(slot->changed_axes)] = {0};
-	struct tablet_axes axes = {0};
+	const char tmp[sizeof(slot->changed_axes)] = { 0 };
+	struct tablet_axes axes = { 0 };
 	struct device_float_coords delta;
 	bool rc = false;
 
@@ -288,8 +286,7 @@ totem_slot_fetch_axes(struct totem_dispa
 							     ABS_MT_POSITION_Y);
 	}
 
-	if (bit_is_set(slot->changed_axes,
-		       LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z)) {
+	if (bit_is_set(slot->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z)) {
 		int angle = libevdev_get_slot_value(device->evdev,
 						    slot->index,
 						    ABS_MT_ORIENTATION);
@@ -297,10 +294,8 @@ totem_slot_fetch_axes(struct totem_dispa
 		slot->axes.rotation = (360 - angle) % 360;
 	}
 
-	if (bit_is_set(slot->changed_axes,
-		       LIBINPUT_TABLET_TOOL_AXIS_SIZE_MAJOR) ||
-	    bit_is_set(slot->changed_axes,
-		       LIBINPUT_TABLET_TOOL_AXIS_SIZE_MINOR)) {
+	if (bit_is_set(slot->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_SIZE_MAJOR) ||
+	    bit_is_set(slot->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_SIZE_MINOR)) {
 		int major, minor;
 		unsigned int rmajor, rminor;
 
@@ -312,8 +307,8 @@ totem_slot_fetch_axes(struct totem_dispa
 						ABS_MT_TOUCH_MINOR);
 		rmajor = libevdev_get_abs_resolution(device->evdev, ABS_MT_TOUCH_MAJOR);
 		rminor = libevdev_get_abs_resolution(device->evdev, ABS_MT_TOUCH_MINOR);
-		slot->axes.size.major = (double)major/rmajor;
-		slot->axes.size.minor = (double)minor/rminor;
+		slot->axes.size.major = (double)major / rmajor;
+		slot->axes.size.minor = (double)minor / rminor;
 	}
 
 	axes.point = slot->axes.point;
@@ -328,42 +323,34 @@ totem_slot_fetch_axes(struct totem_dispa
 out:
 	*axes_out = axes;
 	return rc;
-
 }
 
 static void
 totem_slot_mark_all_axes_changed(struct totem_dispatch *totem,
-			    struct totem_slot *slot,
-			    struct libinput_tablet_tool *tool)
+				 struct totem_slot *slot,
+				 struct libinput_tablet_tool *tool)
 {
-	static_assert(sizeof(slot->changed_axes) ==
-			      sizeof(tool->axis_caps),
+	static_assert(sizeof(slot->changed_axes) == sizeof(tool->axis_caps),
 		      "Mismatching array sizes");
 
-	memcpy(slot->changed_axes,
-	       tool->axis_caps,
-	       sizeof(slot->changed_axes));
+	memcpy(slot->changed_axes, tool->axis_caps, sizeof(slot->changed_axes));
 }
 
 static inline void
-totem_slot_reset_changed_axes(struct totem_dispatch *totem,
-			      struct totem_slot *slot)
+totem_slot_reset_changed_axes(struct totem_dispatch *totem, struct totem_slot *slot)
 {
 	memset(slot->changed_axes, 0, sizeof(slot->changed_axes));
 }
 
 static inline void
-slot_axes_initialize(struct totem_dispatch *totem,
-		     struct totem_slot *slot)
+slot_axes_initialize(struct totem_dispatch *totem, struct totem_slot *slot)
 {
 	struct evdev_device *device = totem->device;
 
-	slot->axes.point.x = libevdev_get_slot_value(device->evdev,
-						     slot->index,
-						     ABS_MT_POSITION_X);
-	slot->axes.point.y = libevdev_get_slot_value(device->evdev,
-						     slot->index,
-						     ABS_MT_POSITION_Y);
+	slot->axes.point.x =
+		libevdev_get_slot_value(device->evdev, slot->index, ABS_MT_POSITION_X);
+	slot->axes.point.y =
+		libevdev_get_slot_value(device->evdev, slot->index, ABS_MT_POSITION_Y);
 	slot->last_point.x = slot->axes.point.x;
 	slot->last_point.y = slot->axes.point.y;
 }
@@ -460,7 +447,7 @@ totem_handle_slot_state(struct totem_dis
 				     slot->tool,
 				     tip_state,
 				     &axes,
-				     BTN_0,
+				     button_code_from_uint32_t(BTN_0),
 				     btn_state,
 				     device->abs.absinfo_x,
 				     device->abs.absinfo_y);
@@ -468,7 +455,7 @@ totem_handle_slot_state(struct totem_dis
 		totem->button_state_previous = totem->button_state_now;
 	}
 
-	switch(slot->state) {
+	switch (slot->state) {
 	case SLOT_STATE_BEGIN:
 	case SLOT_STATE_UPDATE:
 		break;
@@ -489,8 +476,8 @@ totem_handle_slot_state(struct totem_dis
 					LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT,
 					slot->changed_axes,
 					&axes,
-				        device->abs.absinfo_x,
-				        device->abs.absinfo_y);
+					device->abs.absinfo_x,
+					device->abs.absinfo_y);
 
 		slot->state = SLOT_STATE_NONE;
 		break;
@@ -506,17 +493,14 @@ totem_handle_slot_state(struct totem_dis
 }
 
 static enum totem_slot_state
-totem_handle_state(struct totem_dispatch *totem,
-		   uint64_t time)
+totem_handle_state(struct totem_dispatch *totem, uint64_t time)
 {
 	enum totem_slot_state global_state = SLOT_STATE_NONE;
 
 	for (size_t i = 0; i < totem->nslots; i++) {
 		enum totem_slot_state s;
 
-		s = totem_handle_slot_state(totem,
-					    &totem->slots[i],
-					    time);
+		s = totem_handle_slot_state(totem, &totem->slots[i], time);
 
 		/* If one slot is active, the totem is active */
 		if (s != SLOT_STATE_NONE)
@@ -527,16 +511,17 @@ totem_handle_state(struct totem_dispatch
 }
 
 static void
-totem_interface_process(struct evdev_dispatch *dispatch,
-			struct evdev_device *device,
-			struct input_event *e,
-			uint64_t time)
+totem_process_event(struct evdev_dispatch *dispatch,
+		    struct evdev_device *device,
+		    struct evdev_event *e,
+		    uint64_t time)
 {
 	struct totem_dispatch *totem = totem_dispatch(dispatch);
 	enum totem_slot_state global_state;
 	bool enable_touch;
 
-	switch(e->type) {
+	uint16_t type = evdev_event_type(e);
+	switch (type) {
 	case EV_ABS:
 		totem_process_abs(totem, device, e, time);
 		break;
@@ -549,22 +534,33 @@ totem_interface_process(struct evdev_dis
 	case EV_SYN:
 		global_state = totem_handle_state(totem, time);
 		enable_touch = (global_state == SLOT_STATE_NONE);
-		totem_set_touch_device_enabled(totem,
-					       enable_touch,
-					       time);
+		totem_set_touch_device_enabled(totem, enable_touch, time);
 		break;
 	default:
 		evdev_log_error(device,
-				"Unexpected event type %s (%#x)\n",
-				libevdev_event_type_get_name(e->type),
-				e->type);
+				"Unexpected event %s (%#x)\n",
+				evdev_event_get_code_name(e),
+				evdev_usage_as_uint32_t(e->usage));
 		break;
 	}
 }
 
 static void
-totem_interface_suspend(struct evdev_dispatch *dispatch,
-			struct evdev_device *device)
+totem_interface_process(struct evdev_dispatch *dispatch,
+			struct evdev_device *device,
+			struct evdev_frame *frame,
+			uint64_t time)
+{
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+
+	for (size_t i = 0; i < nevents; i++) {
+		totem_process_event(dispatch, device, &events[i], time);
+	}
+}
+
+static void
+totem_interface_suspend(struct evdev_dispatch *dispatch, struct evdev_device *device)
 {
 	struct totem_dispatch *totem = totem_dispatch(dispatch);
 	uint64_t now = libinput_now(evdev_libinput_context(device));
@@ -592,7 +588,7 @@ totem_interface_suspend(struct evdev_dis
 					     slot->tool,
 					     tip_state,
 					     &axes,
-					     BTN_0,
+					     button_code_from_uint32_t(BTN_0),
 					     LIBINPUT_BUTTON_STATE_RELEASED,
 					     device->abs.absinfo_x,
 					     device->abs.absinfo_y);
@@ -617,8 +613,8 @@ totem_interface_suspend(struct evdev_dis
 					LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT,
 					slot->changed_axes,
 					&axes,
-				        device->abs.absinfo_x,
-				        device->abs.absinfo_y);
+					device->abs.absinfo_x,
+					device->abs.absinfo_y);
 	}
 	totem_set_touch_device_enabled(totem, true, now);
 }
@@ -640,10 +636,10 @@ totem_interface_device_added(struct evde
 	struct libinput_device_group *g1, *g2;
 
 	if ((evdev_device_get_id_vendor(added_device) !=
-	    evdev_device_get_id_vendor(device)) ||
+	     evdev_device_get_id_vendor(device)) ||
 	    (evdev_device_get_id_product(added_device) !=
 	     evdev_device_get_id_product(device)))
-	    return;
+		return;
 
 	/* virtual devices don't have device groups, so check for that
 	   libinput replay */
@@ -653,14 +649,17 @@ totem_interface_device_added(struct evde
 		return;
 
 	if (totem->touch_device != NULL) {
-		evdev_log_bug_libinput(device,
-				       "already has a paired touch device, ignoring (%s)\n",
-				       added_device->devname);
+		evdev_log_bug_libinput(
+			device,
+			"already has a paired touch device, ignoring (%s)\n",
+			added_device->devname);
 		return;
 	}
 
 	totem->touch_device = added_device;
-	evdev_log_info(device, "%s: is the totem touch device\n", added_device->devname);
+	evdev_log_info(device,
+		       "%s: is the totem touch device\n",
+		       added_device->devname);
 }
 
 static void
@@ -672,7 +671,8 @@ totem_interface_device_removed(struct ev
 	if (totem->touch_device != removed_device)
 		return;
 
-	totem_set_touch_device_enabled(totem, true,
+	totem_set_touch_device_enabled(totem,
+				       true,
 				       libinput_now(evdev_libinput_context(device)));
 	totem->touch_device = NULL;
 }
@@ -690,9 +690,8 @@ totem_interface_initial_proximity(struct
 		struct tablet_axes axes;
 		int tracking_id;
 
-		tracking_id = libevdev_get_slot_value(device->evdev,
-						      i,
-						      ABS_MT_TRACKING_ID);
+		tracking_id =
+			libevdev_get_slot_value(device->evdev, i, ABS_MT_TRACKING_ID);
 		if (tracking_id == -1)
 			continue;
 
@@ -706,8 +705,8 @@ totem_interface_initial_proximity(struct
 					LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN,
 					slot->changed_axes,
 					&axes,
-				        device->abs.absinfo_x,
-				        device->abs.absinfo_y);
+					device->abs.absinfo_x,
+					device->abs.absinfo_y);
 		totem_slot_reset_changed_axes(totem, slot);
 		tablet_notify_tip(&device->base,
 				  now,
@@ -732,7 +731,7 @@ static struct evdev_dispatch_interface t
 	.device_added = totem_interface_device_added,
 	.device_removed = totem_interface_device_removed,
 	.device_suspended = totem_interface_device_removed, /* treat as remove */
-	.device_resumed = totem_interface_device_added, /* treat as add */
+	.device_resumed = totem_interface_device_added,     /* treat as add */
 	.post_added = totem_interface_initial_proximity,
 	.touch_arbitration_toggle = NULL,
 	.touch_arbitration_update_rect = NULL,
@@ -747,10 +746,11 @@ totem_reject_device(struct evdev_device
 	double w, h;
 
 	has_xy = libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) &&
-	         libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y);
+		 libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y);
 	has_slot = libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT);
-	has_tool_dial = libevdev_has_event_code(evdev, EV_ABS, ABS_MT_TOOL_TYPE) &&
-			libevdev_get_abs_maximum(evdev, ABS_MT_TOOL_TYPE) >= MT_TOOL_DIAL;
+	has_tool_dial =
+		libevdev_has_event_code(evdev, EV_ABS, ABS_MT_TOOL_TYPE) &&
+		libevdev_get_abs_maximum(evdev, ABS_MT_TOOL_TYPE) >= MT_TOOL_DIAL;
 	has_size = evdev_device_get_size(device, &w, &h) == 0;
 	has_touch_size =
 		libevdev_get_abs_resolution(device->evdev, ABS_MT_TOUCH_MAJOR) > 0 ||
@@ -778,7 +778,7 @@ totem_accel_config_get_profiles(struct l
 
 static enum libinput_config_status
 totem_accel_config_set_profile(struct libinput_device *libinput_device,
-			    enum libinput_config_accel_profile profile)
+			       enum libinput_config_accel_profile profile)
 {
 	return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
 }
@@ -805,8 +805,7 @@ totem_init_accel(struct totem_dispatch *
 	y = device->abs.absinfo_y;
 
 	/* same filter as the tablet */
-	filter = create_pointer_accelerator_filter_tablet(x->resolution,
-							  y->resolution);
+	filter = create_pointer_accelerator_filter_tablet(x->resolution, y->resolution);
 	if (!filter)
 		return -1;
 
@@ -817,7 +816,8 @@ totem_init_accel(struct totem_dispatch *
 	device->pointer.config.get_profiles = totem_accel_config_get_profiles;
 	device->pointer.config.set_profile = totem_accel_config_set_profile;
 	device->pointer.config.get_profile = totem_accel_config_get_profile;
-	device->pointer.config.get_default_profile = totem_accel_config_get_default_profile;
+	device->pointer.config.get_default_profile =
+		totem_accel_config_get_default_profile;
 
 	return 0;
 }
diff -pruN 1.28.1-1/src/evdev-wheel.c 1.30.0-1/src/evdev-wheel.c
--- 1.28.1-1/src/evdev-wheel.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev-wheel.c	1970-01-01 00:00:00.000000000 +0000
@@ -1,459 +0,0 @@
-/*
- * Copyright © 2010 Intel Corporation
- * Copyright © 2013 Jonas Ådahl
- * Copyright © 2013-2017 Red Hat, Inc.
- * Copyright © 2017 James Ye <jye836@gmail.com>
- * Copyright © 2021 José Expósito
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-#include "config.h"
-
-#include "evdev-fallback.h"
-#include "util-input-event.h"
-
-#define ACC_V120_THRESHOLD 60
-#define WHEEL_SCROLL_TIMEOUT ms2us(500)
-
-enum wheel_event {
-	WHEEL_EVENT_SCROLL_ACCUMULATED,
-	WHEEL_EVENT_SCROLL,
-	WHEEL_EVENT_SCROLL_TIMEOUT,
-	WHEEL_EVENT_SCROLL_DIR_CHANGED,
-};
-
-static inline const char *
-wheel_state_to_str(enum wheel_state state)
-{
-	switch(state) {
-	CASE_RETURN_STRING(WHEEL_STATE_NONE);
-	CASE_RETURN_STRING(WHEEL_STATE_ACCUMULATING_SCROLL);
-	CASE_RETURN_STRING(WHEEL_STATE_SCROLLING);
-	}
-	return NULL;
-}
-
-static inline const char*
-wheel_event_to_str(enum wheel_event event)
-{
-	switch(event) {
-	CASE_RETURN_STRING(WHEEL_EVENT_SCROLL_ACCUMULATED);
-	CASE_RETURN_STRING(WHEEL_EVENT_SCROLL);
-	CASE_RETURN_STRING(WHEEL_EVENT_SCROLL_TIMEOUT);
-	CASE_RETURN_STRING(WHEEL_EVENT_SCROLL_DIR_CHANGED);
-	}
-	return NULL;
-}
-
-static inline void
-log_wheel_bug(struct fallback_dispatch *dispatch, enum wheel_event event)
-{
-	evdev_log_bug_libinput(dispatch->device,
-			       "invalid wheel event %s in state %s\n",
-			       wheel_event_to_str(event),
-			       wheel_state_to_str(dispatch->wheel.state));
-}
-
-static inline void
-wheel_set_scroll_timer(struct fallback_dispatch *dispatch, uint64_t time)
-{
-	libinput_timer_set(&dispatch->wheel.scroll_timer,
-			   time + WHEEL_SCROLL_TIMEOUT);
-}
-
-static inline void
-wheel_cancel_scroll_timer(struct fallback_dispatch *dispatch)
-{
-	libinput_timer_cancel(&dispatch->wheel.scroll_timer);
-}
-
-static void
-wheel_handle_event_on_state_none(struct fallback_dispatch *dispatch,
-				 enum wheel_event event,
-				 uint64_t time)
-{
-	switch (event) {
-	case WHEEL_EVENT_SCROLL:
-		dispatch->wheel.state = WHEEL_STATE_ACCUMULATING_SCROLL;
-		break;
-	case WHEEL_EVENT_SCROLL_DIR_CHANGED:
-		break;
-	case WHEEL_EVENT_SCROLL_ACCUMULATED:
-	case WHEEL_EVENT_SCROLL_TIMEOUT:
-		log_wheel_bug(dispatch, event);
-		break;
-	}
-}
-
-static void
-wheel_handle_event_on_state_accumulating_scroll(struct fallback_dispatch *dispatch,
-						enum wheel_event event,
-						uint64_t time)
-{
-	switch (event) {
-	case WHEEL_EVENT_SCROLL_ACCUMULATED:
-		dispatch->wheel.state = WHEEL_STATE_SCROLLING;
-		wheel_set_scroll_timer(dispatch, time);
-		break;
-	case WHEEL_EVENT_SCROLL:
-		/* Ignore scroll while accumulating deltas */
-		break;
-	case WHEEL_EVENT_SCROLL_DIR_CHANGED:
-		dispatch->wheel.state = WHEEL_STATE_NONE;
-		break;
-	case WHEEL_EVENT_SCROLL_TIMEOUT:
-		log_wheel_bug(dispatch, event);
-		break;
-	}
-}
-
-static void
-wheel_handle_event_on_state_scrolling(struct fallback_dispatch *dispatch,
-				      enum wheel_event event,
-				      uint64_t time)
-{
-	switch (event) {
-	case WHEEL_EVENT_SCROLL:
-		wheel_cancel_scroll_timer(dispatch);
-		wheel_set_scroll_timer(dispatch, time);
-		break;
-	case WHEEL_EVENT_SCROLL_TIMEOUT:
-		dispatch->wheel.state = WHEEL_STATE_NONE;
-		break;
-	case WHEEL_EVENT_SCROLL_DIR_CHANGED:
-		wheel_cancel_scroll_timer(dispatch);
-		dispatch->wheel.state = WHEEL_STATE_NONE;
-		break;
-	case WHEEL_EVENT_SCROLL_ACCUMULATED:
-		log_wheel_bug(dispatch, event);
-		break;
-	}
-}
-
-static void
-wheel_handle_event(struct fallback_dispatch *dispatch,
-		   enum wheel_event event,
-		   uint64_t time)
-{
-	enum wheel_state oldstate = dispatch->wheel.state;
-
-	switch (oldstate) {
-	case WHEEL_STATE_NONE:
-		wheel_handle_event_on_state_none(dispatch, event, time);
-		break;
-	case WHEEL_STATE_ACCUMULATING_SCROLL:
-		wheel_handle_event_on_state_accumulating_scroll(dispatch,
-								event,
-								time);
-		break;
-	case WHEEL_STATE_SCROLLING:
-		wheel_handle_event_on_state_scrolling(dispatch, event, time);
-		break;
-	}
-
-	if (oldstate != dispatch->wheel.state) {
-		evdev_log_debug(dispatch->device,
-				"wheel state %s → %s → %s\n",
-				wheel_state_to_str(oldstate),
-				wheel_event_to_str(event),
-				wheel_state_to_str(dispatch->wheel.state));
-	}
-}
-
-static void
-wheel_flush_scroll(struct fallback_dispatch *dispatch,
-		   struct evdev_device *device,
-		   uint64_t time)
-{
-	struct normalized_coords wheel_degrees = { 0.0, 0.0 };
-	struct discrete_coords discrete = { 0.0, 0.0 };
-	struct wheel_v120 v120 = { 0.0, 0.0 };
-
-	/* This mouse has a trackstick instead of a mouse wheel and sends
-	 * trackstick data via REL_WHEEL. Normalize it like normal x/y coordinates.
-	 */
-	if (device->model_flags & EVDEV_MODEL_LENOVO_SCROLLPOINT) {
-		const struct device_float_coords raw = {
-			.x = dispatch->wheel.lo_res.x,
-			.y = dispatch->wheel.lo_res.y * -1,
-		};
-		const struct normalized_coords normalized =
-				filter_dispatch_scroll(device->pointer.filter,
-						       &raw,
-						       device,
-						       time);
-		evdev_post_scroll(device,
-				  time,
-				  LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
-				  &normalized);
-		dispatch->wheel.hi_res.x = 0;
-		dispatch->wheel.hi_res.y = 0;
-		dispatch->wheel.lo_res.x = 0;
-		dispatch->wheel.lo_res.y = 0;
-
-		return;
-	}
-
-	if (dispatch->wheel.hi_res.y != 0) {
-		int value = dispatch->wheel.hi_res.y;
-
-		v120.y = -1 * value;
-		wheel_degrees.y = -1 * value/120.0 * device->scroll.wheel_click_angle.y;
-		evdev_notify_axis_wheel(
-			device,
-			time,
-			bit(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
-			&wheel_degrees,
-			&v120);
-		dispatch->wheel.hi_res.y = 0;
-	}
-
-	if (dispatch->wheel.lo_res.y != 0) {
-		int value = dispatch->wheel.lo_res.y;
-
-		wheel_degrees.y = -1 * value * device->scroll.wheel_click_angle.y;
-		discrete.y = -1 * value;
-		evdev_notify_axis_legacy_wheel(
-			device,
-			time,
-			bit(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
-			&wheel_degrees,
-			&discrete);
-		dispatch->wheel.lo_res.y = 0;
-	}
-
-	if (dispatch->wheel.hi_res.x != 0) {
-		int value = dispatch->wheel.hi_res.x;
-
-		v120.x = value;
-		wheel_degrees.x = value/120.0 * device->scroll.wheel_click_angle.x;
-		evdev_notify_axis_wheel(
-			device,
-			time,
-			bit(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
-			&wheel_degrees,
-			&v120);
-		dispatch->wheel.hi_res.x = 0;
-	}
-
-	if (dispatch->wheel.lo_res.x != 0) {
-		int value = dispatch->wheel.lo_res.x;
-
-		wheel_degrees.x = value * device->scroll.wheel_click_angle.x;
-		discrete.x = value;
-		evdev_notify_axis_legacy_wheel(
-			device,
-			time,
-			bit(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
-			&wheel_degrees,
-			&discrete);
-		dispatch->wheel.lo_res.x = 0;
-	}
-}
-
-static void
-wheel_handle_state_none(struct fallback_dispatch *dispatch,
-			struct evdev_device *device,
-			uint64_t time)
-{
-
-}
-
-static void
-wheel_handle_state_accumulating_scroll(struct fallback_dispatch *dispatch,
-				       struct evdev_device *device,
-				       uint64_t time)
-{
-	if (abs(dispatch->wheel.hi_res.x) >= ACC_V120_THRESHOLD ||
-	    abs(dispatch->wheel.hi_res.y) >= ACC_V120_THRESHOLD) {
-		wheel_handle_event(dispatch,
-				   WHEEL_EVENT_SCROLL_ACCUMULATED,
-				   time);
-		wheel_flush_scroll(dispatch, device, time);
-	}
-}
-
-static void
-wheel_handle_state_scrolling(struct fallback_dispatch *dispatch,
-			     struct evdev_device *device,
-			     uint64_t time)
-{
-	wheel_flush_scroll(dispatch, device, time);
-}
-
-static void
-wheel_handle_direction_change(struct fallback_dispatch *dispatch,
-			      struct input_event *e,
-			      uint64_t time)
-{
-	enum wheel_direction new_dir = WHEEL_DIR_UNKNOW;
-
-	switch (e->code) {
-	case REL_WHEEL_HI_RES:
-		new_dir = (e->value > 0) ? WHEEL_DIR_VPOS : WHEEL_DIR_VNEG;
-		break;
-	case REL_HWHEEL_HI_RES:
-		new_dir = (e->value > 0) ? WHEEL_DIR_HPOS : WHEEL_DIR_HNEG;
-		break;
-	}
-
-	if (new_dir != WHEEL_DIR_UNKNOW && new_dir != dispatch->wheel.dir) {
-		dispatch->wheel.dir = new_dir;
-		wheel_handle_event(dispatch,
-				   WHEEL_EVENT_SCROLL_DIR_CHANGED,
-				   time);
-	}
-}
-
-static void
-fallback_rotate_wheel(struct fallback_dispatch *dispatch,
-		      struct evdev_device *device,
-		      struct input_event *e)
-{
-	/* Special case: if we're upside down (-ish),
-	 * swap the direction of the wheels so that user-down
-	 * means scroll down. This isn't done for any other angle
-	 * since it's not clear what the heuristics should be.*/
-	if (dispatch->rotation.angle >= 160.0 &&
-	    dispatch->rotation.angle <= 220.0) {
-		e->value *= -1;
-	}
-}
-
-void
-fallback_wheel_process_relative(struct fallback_dispatch *dispatch,
-				struct evdev_device *device,
-				struct input_event *e, uint64_t time)
-{
-	switch (e->code) {
-	case REL_WHEEL:
-		fallback_rotate_wheel(dispatch, device, e);
-		dispatch->wheel.lo_res.y += e->value;
-		if (dispatch->wheel.emulate_hi_res_wheel)
-			dispatch->wheel.hi_res.y += e->value * 120;
-		dispatch->pending_event |= EVDEV_WHEEL;
-		wheel_handle_event(dispatch, WHEEL_EVENT_SCROLL, time);
-		break;
-	case REL_HWHEEL:
-		fallback_rotate_wheel(dispatch, device, e);
-		dispatch->wheel.lo_res.x += e->value;
-		if (dispatch->wheel.emulate_hi_res_wheel)
-			dispatch->wheel.hi_res.x += e->value * 120;
-		dispatch->pending_event |= EVDEV_WHEEL;
-		wheel_handle_event(dispatch, WHEEL_EVENT_SCROLL, time);
-		break;
-	case REL_WHEEL_HI_RES:
-		fallback_rotate_wheel(dispatch, device, e);
-		dispatch->wheel.hi_res.y += e->value;
-		dispatch->wheel.hi_res_event_received = true;
-		dispatch->pending_event |= EVDEV_WHEEL;
-		wheel_handle_direction_change(dispatch, e, time);
-		wheel_handle_event(dispatch, WHEEL_EVENT_SCROLL, time);
-		break;
-	case REL_HWHEEL_HI_RES:
-		fallback_rotate_wheel(dispatch, device, e);
-		dispatch->wheel.hi_res.x += e->value;
-		dispatch->wheel.hi_res_event_received = true;
-		dispatch->pending_event |= EVDEV_WHEEL;
-		wheel_handle_direction_change(dispatch, e, time);
-		wheel_handle_event(dispatch, WHEEL_EVENT_SCROLL, time);
-		break;
-	}
-}
-
-void
-fallback_wheel_handle_state(struct fallback_dispatch *dispatch,
-			    struct evdev_device *device,
-			    uint64_t time)
-{
-	if (!(device->seat_caps & EVDEV_DEVICE_POINTER))
-		return;
-
-	if (!dispatch->wheel.emulate_hi_res_wheel &&
-	    !dispatch->wheel.hi_res_event_received &&
-	    (dispatch->wheel.lo_res.x != 0 || dispatch->wheel.lo_res.y != 0)) {
-		evdev_log_bug_kernel(device,
-				     "device supports high-resolution scroll but only low-resolution events have been received.\n"
-				     "See %s/incorrectly-enabled-hires.html for details\n",
-				     HTTP_DOC_LINK);
-		dispatch->wheel.emulate_hi_res_wheel = true;
-		dispatch->wheel.hi_res.x = dispatch->wheel.lo_res.x * 120;
-		dispatch->wheel.hi_res.y = dispatch->wheel.lo_res.y * 120;
-	}
-
-	switch (dispatch->wheel.state) {
-	case WHEEL_STATE_NONE:
-		wheel_handle_state_none(dispatch, device, time);
-		break;
-	case WHEEL_STATE_ACCUMULATING_SCROLL:
-		wheel_handle_state_accumulating_scroll(dispatch, device, time);
-		break;
-	case WHEEL_STATE_SCROLLING:
-		wheel_handle_state_scrolling(dispatch, device, time);
-		break;
-	}
-}
-
-static void
-wheel_init_scroll_timer(uint64_t now, void *data)
-{
-	struct evdev_device *device = data;
-	struct fallback_dispatch *dispatch =
-		fallback_dispatch(device->dispatch);
-
-	wheel_handle_event(dispatch, WHEEL_EVENT_SCROLL_TIMEOUT, now);
-}
-
-void
-fallback_init_wheel(struct fallback_dispatch *dispatch,
-		    struct evdev_device *device)
-{
-	char timer_name[64];
-
-	dispatch->wheel.state = WHEEL_STATE_NONE;
-	dispatch->wheel.dir = WHEEL_DIR_UNKNOW;
-
-	/* On kernel < 5.0 we need to emulate high-resolution
-	   wheel scroll events */
-	if ((libevdev_has_event_code(device->evdev,
-				     EV_REL,
-				     REL_WHEEL) &&
-	     !libevdev_has_event_code(device->evdev,
-				      EV_REL,
-				      REL_WHEEL_HI_RES)) ||
-	    (libevdev_has_event_code(device->evdev,
-				     EV_REL,
-				     REL_HWHEEL) &&
-	     !libevdev_has_event_code(device->evdev,
-				      EV_REL,
-				      REL_HWHEEL_HI_RES)))
-		dispatch->wheel.emulate_hi_res_wheel = true;
-
-	snprintf(timer_name,
-		 sizeof(timer_name),
-		 "%s wheel scroll",
-		 evdev_device_get_sysname(device));
-	libinput_timer_init(&dispatch->wheel.scroll_timer,
-			    evdev_libinput_context(device),
-			    timer_name,
-			    wheel_init_scroll_timer,
-			    device);
-}
diff -pruN 1.28.1-1/src/evdev.c 1.30.0-1/src/evdev.c
--- 1.28.1-1/src/evdev.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,26 +26,29 @@
 
 #include "config.h"
 
+#include <assert.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <math.h>
 #include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
-#include "linux/input.h"
 #include <unistd.h>
-#include <fcntl.h>
-#include <mtdev-plumbing.h>
-#include <assert.h>
-#include <math.h>
 
-#include "libinput.h"
+#include "util-input-event.h"
+#include "util-udev.h"
+
+#include "evdev-frame.h"
 #include "evdev.h"
 #include "filter.h"
+#include "libinput-plugin.h"
 #include "libinput-private.h"
+#include "libinput.h"
+#include "linux/input.h"
 #include "quirks.h"
-#include "util-input-event.h"
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 #include <libwacom/libwacom.h>
 #endif
 
@@ -53,18 +56,19 @@
 #define DEFAULT_BUTTON_SCROLL_TIMEOUT ms2us(200)
 
 enum evdev_device_udev_tags {
-	EVDEV_UDEV_TAG_INPUT		= bit(0),
-	EVDEV_UDEV_TAG_KEYBOARD		= bit(1),
-	EVDEV_UDEV_TAG_MOUSE		= bit(2),
-	EVDEV_UDEV_TAG_TOUCHPAD		= bit(3),
-	EVDEV_UDEV_TAG_TOUCHSCREEN	= bit(4),
-	EVDEV_UDEV_TAG_TABLET		= bit(5),
-	EVDEV_UDEV_TAG_JOYSTICK		= bit(6),
-	EVDEV_UDEV_TAG_ACCELEROMETER	= bit(7),
-	EVDEV_UDEV_TAG_TABLET_PAD	= bit(8),
-	EVDEV_UDEV_TAG_POINTINGSTICK	= bit(9),
-	EVDEV_UDEV_TAG_TRACKBALL	= bit(10),
-	EVDEV_UDEV_TAG_SWITCH		= bit(11),
+	EVDEV_UDEV_TAG_NONE = 0,
+	EVDEV_UDEV_TAG_INPUT = bit(0),
+	EVDEV_UDEV_TAG_KEYBOARD = bit(1),
+	EVDEV_UDEV_TAG_MOUSE = bit(2),
+	EVDEV_UDEV_TAG_TOUCHPAD = bit(3),
+	EVDEV_UDEV_TAG_TOUCHSCREEN = bit(4),
+	EVDEV_UDEV_TAG_TABLET = bit(5),
+	EVDEV_UDEV_TAG_JOYSTICK = bit(6),
+	EVDEV_UDEV_TAG_ACCELEROMETER = bit(7),
+	EVDEV_UDEV_TAG_TABLET_PAD = bit(8),
+	EVDEV_UDEV_TAG_POINTINGSTICK = bit(9),
+	EVDEV_UDEV_TAG_TRACKBALL = bit(10),
+	EVDEV_UDEV_TAG_SWITCH = bit(11),
 };
 
 struct evdev_udev_tag_match {
@@ -73,32 +77,24 @@ struct evdev_udev_tag_match {
 };
 
 static const struct evdev_udev_tag_match evdev_udev_tag_matches[] = {
-	{"ID_INPUT",			EVDEV_UDEV_TAG_INPUT},
-	{"ID_INPUT_KEYBOARD",		EVDEV_UDEV_TAG_KEYBOARD},
-	{"ID_INPUT_KEY",		EVDEV_UDEV_TAG_KEYBOARD},
-	{"ID_INPUT_MOUSE",		EVDEV_UDEV_TAG_MOUSE},
-	{"ID_INPUT_TOUCHPAD",		EVDEV_UDEV_TAG_TOUCHPAD},
-	{"ID_INPUT_TOUCHSCREEN",	EVDEV_UDEV_TAG_TOUCHSCREEN},
-	{"ID_INPUT_TABLET",		EVDEV_UDEV_TAG_TABLET},
-	{"ID_INPUT_TABLET_PAD",		EVDEV_UDEV_TAG_TABLET_PAD},
-	{"ID_INPUT_JOYSTICK",		EVDEV_UDEV_TAG_JOYSTICK},
-	{"ID_INPUT_ACCELEROMETER",	EVDEV_UDEV_TAG_ACCELEROMETER},
-	{"ID_INPUT_POINTINGSTICK",	EVDEV_UDEV_TAG_POINTINGSTICK},
-	{"ID_INPUT_TRACKBALL",		EVDEV_UDEV_TAG_TRACKBALL},
-	{"ID_INPUT_SWITCH",		EVDEV_UDEV_TAG_SWITCH},
+	{ "ID_INPUT", EVDEV_UDEV_TAG_INPUT },
+	{ "ID_INPUT_KEYBOARD", EVDEV_UDEV_TAG_KEYBOARD },
+	{ "ID_INPUT_KEY", EVDEV_UDEV_TAG_KEYBOARD },
+	{ "ID_INPUT_MOUSE", EVDEV_UDEV_TAG_MOUSE },
+	{ "ID_INPUT_TOUCHPAD", EVDEV_UDEV_TAG_TOUCHPAD },
+	{ "ID_INPUT_TOUCHSCREEN", EVDEV_UDEV_TAG_TOUCHSCREEN },
+	{ "ID_INPUT_TABLET", EVDEV_UDEV_TAG_TABLET },
+	{ "ID_INPUT_TABLET_PAD", EVDEV_UDEV_TAG_TABLET_PAD },
+	{ "ID_INPUT_JOYSTICK", EVDEV_UDEV_TAG_JOYSTICK },
+	{ "ID_INPUT_ACCELEROMETER", EVDEV_UDEV_TAG_ACCELEROMETER },
+	{ "ID_INPUT_POINTINGSTICK", EVDEV_UDEV_TAG_POINTINGSTICK },
+	{ "ID_INPUT_TRACKBALL", EVDEV_UDEV_TAG_TRACKBALL },
+	{ "ID_INPUT_SWITCH", EVDEV_UDEV_TAG_SWITCH },
 };
 
 static const unsigned int well_known_keyboard_keys[] = {
-	KEY_LEFTCTRL,
-	KEY_CAPSLOCK,
-	KEY_NUMLOCK,
-	KEY_INSERT,
-	KEY_MUTE,
-	KEY_CALC,
-	KEY_FILE,
-	KEY_MAIL,
-	KEY_PLAYPAUSE,
-	KEY_BRIGHTNESSDOWN,
+	KEY_LEFTCTRL, KEY_CAPSLOCK, KEY_NUMLOCK, KEY_INSERT,    KEY_MUTE,
+	KEY_CALC,     KEY_FILE,     KEY_MAIL,    KEY_PLAYPAUSE, KEY_BRIGHTNESSDOWN,
 };
 
 static inline bool
@@ -126,11 +122,14 @@ parse_udev_flag(struct evdev_device *dev
 
 int
 evdev_update_key_down_count(struct evdev_device *device,
-			    int code,
+			    evdev_usage_t usage,
 			    int pressed)
 {
 	int key_count = 0;
-	assert(code >= 0 && code < KEY_CNT);
+	assert(evdev_usage_ge(usage, EVDEV_KEY_RESERVED) &&
+	       evdev_usage_le(usage, EVDEV_KEY_MAX));
+
+	unsigned int code = evdev_usage_code(usage);
 
 	if (pressed) {
 		key_count = ++device->key_count[code];
@@ -138,10 +137,12 @@ evdev_update_key_down_count(struct evdev
 		if (device->key_count[code] > 0) {
 			key_count = --device->key_count[code];
 		} else {
-			evdev_log_bug_libinput(device,
-					       "releasing key %s with count %d\n",
-					       libevdev_event_code_get_name(EV_KEY, code),
-					       device->key_count[code]);
+			evdev_log_bug_libinput(
+				device,
+				"releasing key %s (%#x) with count %d\n",
+				libevdev_event_code_get_name(EV_KEY, code),
+				evdev_usage_as_uint32_t(usage),
+				device->key_count[code]);
 		}
 	}
 
@@ -155,8 +156,7 @@ evdev_update_key_down_count(struct evdev
 }
 
 enum libinput_switch_state
-evdev_device_switch_get_state(struct evdev_device *device,
-			      enum libinput_switch sw)
+evdev_device_switch_get_state(struct evdev_device *device, enum libinput_switch sw)
 {
 	struct evdev_dispatch *dispatch = device->dispatch;
 
@@ -168,25 +168,19 @@ evdev_device_switch_get_state(struct evd
 void
 evdev_pointer_notify_physical_button(struct evdev_device *device,
 				     uint64_t time,
-				     int button,
+				     evdev_usage_t button,
 				     enum libinput_button_state state)
 {
-	if (evdev_middlebutton_filter_button(device,
-					     time,
-					     button,
-					     state))
-			return;
+	if (evdev_middlebutton_filter_button(device, time, button, state))
+		return;
 
-	evdev_pointer_notify_button(device,
-				    time,
-				    (unsigned int)button,
-				    state);
+	evdev_pointer_notify_button(device, time, button, state);
 }
 
 static void
 evdev_pointer_post_button(struct evdev_device *device,
 			  uint64_t time,
-			  unsigned int button,
+			  evdev_usage_t button,
 			  enum libinput_button_state state)
 {
 	int down_count;
@@ -195,7 +189,10 @@ evdev_pointer_post_button(struct evdev_d
 
 	if ((state == LIBINPUT_BUTTON_STATE_PRESSED && down_count == 1) ||
 	    (state == LIBINPUT_BUTTON_STATE_RELEASED && down_count == 0)) {
-		pointer_notify_button(&device->base, time, button, state);
+		pointer_notify_button(&device->base,
+				      time,
+				      button_code_from_usage(button),
+				      state);
 
 		if (state == LIBINPUT_BUTTON_STATE_RELEASED) {
 			if (device->left_handed.change_to_enabled)
@@ -205,7 +202,6 @@ evdev_pointer_post_button(struct evdev_d
 				device->scroll.change_scroll_method(device);
 		}
 	}
-
 }
 
 static void
@@ -217,8 +213,7 @@ evdev_button_scroll_timeout(uint64_t tim
 }
 
 static void
-evdev_button_scroll_button(struct evdev_device *device,
-			   uint64_t time, int is_press)
+evdev_button_scroll_button(struct evdev_device *device, uint64_t time, int is_press)
 {
 	/* Where the button lock is enabled, we wrap the buttons into
 	   their own little state machine and filter out the events.
@@ -249,9 +244,10 @@ evdev_button_scroll_button(struct evdev_
 	}
 
 	if (is_press) {
-		if (device->scroll.button < BTN_MOUSE + 5) {
-			/* For mouse buttons 1-5 (0x110 to 0x114) we apply a timeout before scrolling
-			 * since the button could also be used for regular clicking. */
+		if (evdev_usage_lt(device->scroll.button, EVDEV_BTN_LEFT + 5)) {
+			/* For mouse buttons 1-5 (0x110 to 0x114) we apply a timeout
+			 * before scrolling since the button could also be used for
+			 * regular clicking. */
 			enum timer_flags flags = TIMER_FLAG_NONE;
 
 			device->scroll.button_scroll_state = BUTTONSCROLL_BUTTON_DOWN;
@@ -264,28 +260,29 @@ evdev_button_scroll_button(struct evdev_
 			 * for a negative timer to be set.
 			 */
 			if (device->middlebutton.enabled &&
-				(device->scroll.button == BTN_LEFT ||
-				device->scroll.button == BTN_RIGHT)) {
+			    (evdev_usage_eq(device->scroll.button, EVDEV_BTN_LEFT) ||
+			     evdev_usage_eq(device->scroll.button, EVDEV_BTN_RIGHT))) {
 				flags = TIMER_FLAG_ALLOW_NEGATIVE;
 			}
 
 			libinput_timer_set_flags(&device->scroll.timer,
-						time + DEFAULT_BUTTON_SCROLL_TIMEOUT,
-						flags);
+						 time + DEFAULT_BUTTON_SCROLL_TIMEOUT,
+						 flags);
 		} else {
-			/* For extra mouse buttons numbered 6 or more (0x115+) we assume it is
-			 * dedicated exclusively to scrolling, so we don't apply the timeout
-			 * in order to provide immediate scrolling responsiveness. */
+			/* For extra mouse buttons numbered 6 or more (0x115+) we assume
+			 * it is dedicated exclusively to scrolling, so we don't apply
+			 * the timeout in order to provide immediate scrolling
+			 * responsiveness. */
 			device->scroll.button_scroll_state = BUTTONSCROLL_READY;
 		}
 		device->scroll.button_down_time = time;
 		evdev_log_debug(device, "btnscroll: down\n");
 	} else {
 		libinput_timer_cancel(&device->scroll.timer);
-		switch(device->scroll.button_scroll_state) {
+		switch (device->scroll.button_scroll_state) {
 		case BUTTONSCROLL_IDLE:
 			evdev_log_bug_libinput(device,
-				       "invalid state IDLE for button up\n");
+					       "invalid state IDLE for button up\n");
 			break;
 		case BUTTONSCROLL_BUTTON_DOWN:
 		case BUTTONSCROLL_READY:
@@ -295,16 +292,18 @@ evdev_button_scroll_button(struct evdev_
 			 * without scroll events, emit the
 			 * button press/release events. */
 			evdev_pointer_post_button(device,
-					device->scroll.button_down_time,
-					device->scroll.button,
-					LIBINPUT_BUTTON_STATE_PRESSED);
-			evdev_pointer_post_button(device, time,
-					device->scroll.button,
-					LIBINPUT_BUTTON_STATE_RELEASED);
+						  device->scroll.button_down_time,
+						  device->scroll.button,
+						  LIBINPUT_BUTTON_STATE_PRESSED);
+			evdev_pointer_post_button(device,
+						  time,
+						  device->scroll.button,
+						  LIBINPUT_BUTTON_STATE_RELEASED);
 			break;
 		case BUTTONSCROLL_SCROLLING:
 			evdev_log_debug(device, "btnscroll: up\n");
-			evdev_stop_scroll(device, time,
+			evdev_stop_scroll(device,
+					  time,
 					  LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
 			break;
 		}
@@ -316,11 +315,11 @@ evdev_button_scroll_button(struct evdev_
 void
 evdev_pointer_notify_button(struct evdev_device *device,
 			    uint64_t time,
-			    unsigned int button,
+			    evdev_usage_t button,
 			    enum libinput_button_state state)
 {
 	if (device->scroll.method == LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN &&
-	    button == device->scroll.button) {
+	    evdev_usage_cmp(button, device->scroll.button) == 0) {
 		evdev_button_scroll_button(device, time, state);
 		return;
 	}
@@ -361,8 +360,7 @@ evdev_device_led_update(struct evdev_dev
 }
 
 void
-evdev_transform_absolute(struct evdev_device *device,
-			 struct device_coords *point)
+evdev_transform_absolute(struct evdev_device *device, struct device_coords *point)
 {
 	if (!device->abs.apply_calibration)
 		return;
@@ -371,8 +369,7 @@ evdev_transform_absolute(struct evdev_de
 }
 
 void
-evdev_transform_relative(struct evdev_device *device,
-			 struct device_coords *point)
+evdev_transform_relative(struct evdev_device *device, struct device_coords *point)
 {
 	struct matrix rel_matrix;
 
@@ -384,17 +381,13 @@ evdev_transform_relative(struct evdev_de
 }
 
 double
-evdev_device_transform_x(struct evdev_device *device,
-			 double x,
-			 uint32_t width)
+evdev_device_transform_x(struct evdev_device *device, double x, uint32_t width)
 {
 	return absinfo_scale_axis(device->abs.absinfo_x, x, width);
 }
 
 double
-evdev_device_transform_y(struct evdev_device *device,
-			 double y,
-			 uint32_t height)
+evdev_device_transform_y(struct evdev_device *device, double y, uint32_t height)
 {
 	return absinfo_scale_axis(device->abs.absinfo_y, y, height);
 }
@@ -421,11 +414,7 @@ evdev_notify_axis_legacy_wheel(struct ev
 		discrete.y *= -1;
 	}
 
-	pointer_notify_axis_legacy_wheel(&device->base,
-					 time,
-					 axes,
-					 &delta,
-					 &discrete);
+	pointer_notify_axis_legacy_wheel(&device->base, time, axes, &delta, &discrete);
 }
 
 void
@@ -450,18 +439,14 @@ evdev_notify_axis_wheel(struct evdev_dev
 		v120.y *= -1;
 	}
 
-	pointer_notify_axis_wheel(&device->base,
-				  time,
-				  axes,
-				  &delta,
-				  &v120);
+	pointer_notify_axis_wheel(&device->base, time, axes, &delta, &v120);
 }
 
 void
 evdev_notify_axis_finger(struct evdev_device *device,
-			uint64_t time,
-			uint32_t axes,
-			const struct normalized_coords *delta_in)
+			 uint64_t time,
+			 uint32_t axes,
+			 const struct normalized_coords *delta_in)
 {
 	struct normalized_coords delta = *delta_in;
 
@@ -470,10 +455,7 @@ evdev_notify_axis_finger(struct evdev_de
 		delta.y *= -1;
 	}
 
-	pointer_notify_axis_finger(&device->base,
-				  time,
-				  axes,
-				  &delta);
+	pointer_notify_axis_finger(&device->base, time, axes, &delta);
 }
 
 void
@@ -489,15 +471,11 @@ evdev_notify_axis_continous(struct evdev
 		delta.y *= -1;
 	}
 
-	pointer_notify_axis_continuous(&device->base,
-				       time,
-				       axes,
-				       &delta);
+	pointer_notify_axis_continuous(&device->base, time, axes, &delta);
 }
 
 static void
-evdev_tag_external_mouse(struct evdev_device *device,
-			 struct udev_device *udev_device)
+evdev_tag_external_mouse(struct evdev_device *device, struct udev_device *udev_device)
 {
 	int bustype;
 
@@ -507,37 +485,27 @@ evdev_tag_external_mouse(struct evdev_de
 }
 
 static void
-evdev_tag_trackpoint(struct evdev_device *device,
-		     struct udev_device *udev_device)
+evdev_tag_trackpoint(struct evdev_device *device, struct udev_device *udev_device)
 {
-	struct quirks_context *quirks;
-	struct quirks *q;
 	char *prop;
 
-	if (!libevdev_has_property(device->evdev,
-				  INPUT_PROP_POINTING_STICK) &&
+	if (!libevdev_has_property(device->evdev, INPUT_PROP_POINTING_STICK) &&
 	    !parse_udev_flag(device, udev_device, "ID_INPUT_POINTINGSTICK"))
 		return;
 
 	device->tags |= EVDEV_TAG_TRACKPOINT;
 
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
 	if (q && quirks_get_string(q, QUIRK_ATTR_TRACKPOINT_INTEGRATION, &prop)) {
 		if (streq(prop, "internal")) {
 			/* noop, this is the default anyway */
 		} else if (streq(prop, "external")) {
 			device->tags |= EVDEV_TAG_EXTERNAL_MOUSE;
-			evdev_log_info(device,
-				       "is an external pointing stick\n");
+			evdev_log_info(device, "is an external pointing stick\n");
 		} else {
-			evdev_log_info(device,
-				       "tagged with unknown value %s\n",
-				       prop);
+			evdev_log_info(device, "tagged with unknown value %s\n", prop);
 		}
 	}
-
-	quirks_unref(q);
 }
 
 static inline void
@@ -555,11 +523,8 @@ evdev_tag_keyboard_external(struct evdev
 }
 
 static void
-evdev_tag_keyboard(struct evdev_device *device,
-		   struct udev_device *udev_device)
+evdev_tag_keyboard(struct evdev_device *device, struct udev_device *udev_device)
 {
-	struct quirks_context *quirks;
-	struct quirks *q;
 	char *prop;
 	int code;
 
@@ -567,28 +532,21 @@ evdev_tag_keyboard(struct evdev_device *
 		return;
 
 	for (code = KEY_Q; code <= KEY_P; code++) {
-		if (!libevdev_has_event_code(device->evdev,
-					     EV_KEY,
-					     code))
+		if (!libevdev_has_event_code(device->evdev, EV_KEY, code))
 			return;
 	}
 
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
 	if (q && quirks_get_string(q, QUIRK_ATTR_KEYBOARD_INTEGRATION, &prop)) {
 		if (streq(prop, "internal")) {
 			evdev_tag_keyboard_internal(device);
 		} else if (streq(prop, "external")) {
 			evdev_tag_keyboard_external(device);
 		} else {
-			evdev_log_info(device,
-				       "tagged with unknown value %s\n",
-				       prop);
+			evdev_log_info(device, "tagged with unknown value %s\n", prop);
 		}
 	}
 
-	quirks_unref(q);
-
 	device->tags |= EVDEV_TAG_KEYBOARD;
 }
 
@@ -618,8 +576,7 @@ evdev_calibration_set_matrix(struct libi
 }
 
 static int
-evdev_calibration_get_matrix(struct libinput_device *libinput_device,
-			     float matrix[6])
+evdev_calibration_get_matrix(struct libinput_device *libinput_device, float matrix[6])
 {
 	struct evdev_device *device = evdev_device(libinput_device);
 
@@ -655,7 +612,7 @@ evdev_sendevents_set_mode(struct libinpu
 	if (mode == dispatch->sendevents.current_mode)
 		return LIBINPUT_CONFIG_STATUS_SUCCESS;
 
-	switch(mode) {
+	switch (mode) {
 	case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
 		evdev_device_resume(evdev);
 		break;
@@ -783,12 +740,11 @@ evdev_scroll_get_default_method(struct l
 }
 
 static enum libinput_config_status
-evdev_scroll_set_button(struct libinput_device *device,
-			uint32_t button)
+evdev_scroll_set_button(struct libinput_device *device, uint32_t button)
 {
 	struct evdev_device *evdev = evdev_device(device);
 
-	evdev->scroll.want_button = button;
+	evdev->scroll.want_button = evdev_usage_from_code(EV_KEY, button);
 	evdev->scroll.change_scroll_method(evdev);
 
 	return LIBINPUT_CONFIG_STATUS_SUCCESS;
@@ -801,7 +757,7 @@ evdev_scroll_get_button(struct libinput_
 
 	/* return the wanted configuration, even if it hasn't taken
 	 * effect yet! */
-	return evdev->scroll.want_button;
+	return evdev_usage_code(evdev->scroll.want_button);
 }
 
 static uint32_t
@@ -864,8 +820,7 @@ evdev_scroll_get_default_button_lock(str
 }
 
 void
-evdev_set_button_scroll_lock_enabled(struct evdev_device *device,
-				     bool enabled)
+evdev_set_button_scroll_lock_enabled(struct evdev_device *device, bool enabled)
 {
 	if (enabled)
 		device->scroll.lock_state = BUTTONSCROLL_LOCK_IDLE;
@@ -886,7 +841,8 @@ evdev_init_button_scroll(struct evdev_de
 	libinput_timer_init(&device->scroll.timer,
 			    evdev_libinput_context(device),
 			    timer_name,
-			    evdev_button_scroll_timeout, device);
+			    evdev_button_scroll_timeout,
+			    device);
 	device->scroll.config.get_methods = evdev_scroll_get_methods;
 	device->scroll.config.set_method = evdev_scroll_set_method;
 	device->scroll.config.get_method = evdev_scroll_get_method;
@@ -896,11 +852,15 @@ evdev_init_button_scroll(struct evdev_de
 	device->scroll.config.get_default_button = evdev_scroll_get_default_button;
 	device->scroll.config.set_button_lock = evdev_scroll_set_button_lock;
 	device->scroll.config.get_button_lock = evdev_scroll_get_button_lock;
-	device->scroll.config.get_default_button_lock = evdev_scroll_get_default_button_lock;
+	device->scroll.config.get_default_button_lock =
+		evdev_scroll_get_default_button_lock;
 	device->base.config.scroll_method = &device->scroll.config;
-	device->scroll.method = evdev_scroll_get_default_method((struct libinput_device *)device);
+	device->scroll.method =
+		evdev_scroll_get_default_method((struct libinput_device *)device);
 	device->scroll.want_method = device->scroll.method;
-	device->scroll.button = evdev_scroll_get_default_button((struct libinput_device *)device);
+	device->scroll.button = evdev_usage_from_code(
+		EV_KEY,
+		evdev_scroll_get_default_button((struct libinput_device *)device));
 	device->scroll.want_button = device->scroll.button;
 	device->scroll.change_scroll_method = change_scroll_method;
 }
@@ -918,8 +878,7 @@ evdev_init_calibration(struct evdev_devi
 }
 
 void
-evdev_init_sendevents(struct evdev_device *device,
-		      struct evdev_dispatch *dispatch)
+evdev_init_sendevents(struct evdev_device *device, struct evdev_dispatch *dispatch)
 {
 	device->base.config.sendevents = &dispatch->sendevents.config;
 
@@ -927,7 +886,8 @@ evdev_init_sendevents(struct evdev_devic
 	dispatch->sendevents.config.get_modes = evdev_sendevents_get_modes;
 	dispatch->sendevents.config.set_mode = evdev_sendevents_set_mode;
 	dispatch->sendevents.config.get_mode = evdev_sendevents_get_mode;
-	dispatch->sendevents.config.get_default_mode = evdev_sendevents_get_default_mode;
+	dispatch->sendevents.config.get_default_mode =
+		evdev_sendevents_get_default_mode;
 }
 
 static int
@@ -937,8 +897,7 @@ evdev_scroll_config_natural_has(struct l
 }
 
 static enum libinput_config_status
-evdev_scroll_config_natural_set(struct libinput_device *device,
-				int enabled)
+evdev_scroll_config_natural_set(struct libinput_device *device, int enabled)
 {
 	struct evdev_device *dev = evdev_device(device);
 
@@ -968,21 +927,12 @@ evdev_init_natural_scroll(struct evdev_d
 	device->scroll.config_natural.has = evdev_scroll_config_natural_has;
 	device->scroll.config_natural.set_enabled = evdev_scroll_config_natural_set;
 	device->scroll.config_natural.get_enabled = evdev_scroll_config_natural_get;
-	device->scroll.config_natural.get_default_enabled = evdev_scroll_config_natural_get_default;
+	device->scroll.config_natural.get_default_enabled =
+		evdev_scroll_config_natural_get_default;
 	device->scroll.natural_scrolling_enabled = false;
 	device->base.config.natural_scroll = &device->scroll.config_natural;
 }
 
-int
-evdev_need_mtdev(struct evdev_device *device)
-{
-	struct libevdev *evdev = device->evdev;
-
-	return (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) &&
-		libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y) &&
-		!libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT));
-}
-
 /* Fake MT devices have the ABS_MT_SLOT bit set because of
    the limited ABS_* range - they aren't MT devices, they
    just have too many ABS_ axes */
@@ -992,19 +942,16 @@ evdev_is_fake_mt_device(struct evdev_dev
 	struct libevdev *evdev = device->evdev;
 
 	return libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT) &&
-		libevdev_get_num_slots(evdev) == -1;
+	       libevdev_get_num_slots(evdev) == -1;
 }
 
 enum switch_reliability
 evdev_read_switch_reliability_prop(struct evdev_device *device)
 {
 	enum switch_reliability r;
-	struct quirks_context *quirks;
-	struct quirks *q;
 	char *prop;
 
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
 	if (!q || !quirks_get_string(q, QUIRK_ATTR_LID_SWITCH_RELIABILITY, &prop)) {
 		r = RELIABILITY_RELIABLE;
 	} else if (!parse_switch_reliability_property(prop, &r)) {
@@ -1017,99 +964,53 @@ evdev_read_switch_reliability_prop(struc
 		evdev_log_info(device, "will write switch open events\n");
 	}
 
-	quirks_unref(q);
-
 	return r;
 }
 
-LIBINPUT_UNUSED
 static inline void
-evdev_print_event(struct evdev_device *device,
-		  const struct input_event *e)
-{
-	static uint32_t offset = 0;
-	static uint32_t last_time = 0;
-	uint32_t time = us2ms(input_event_time(e));
-
-	if (offset == 0) {
-		offset = time;
-		last_time = time - offset;
-	}
-
-	time -= offset;
-
-	if (libevdev_event_is_code(e, EV_SYN, SYN_REPORT)) {
-		evdev_log_debug(device,
-			  "%u.%03u ----------------- EV_SYN ----------------- +%ums\n",
-			  time / 1000,
-			  time % 1000,
-			  time - last_time);
-
-		last_time = time;
-	} else {
-		evdev_log_debug(device,
-			  "%u.%03u %-16s %-20s %4d\n",
-			  time / 1000,
-			  time % 1000,
-			  libevdev_event_type_get_name(e->type),
-			  libevdev_event_code_get_name(e->type, e->code),
-			  e->value);
-	}
+evdev_device_dispatch_frame(struct libinput *libinput,
+			    struct evdev_device *device,
+			    struct evdev_frame *frame)
+{
+	libinput_plugin_system_notify_evdev_frame(&libinput->plugin_system,
+						  &device->base,
+						  frame);
 }
 
 static inline void
-evdev_process_event(struct evdev_device *device, struct input_event *e)
+libinput_device_dispatch_frame(struct libinput_device *device,
+			       struct evdev_frame *frame)
 {
-	struct evdev_dispatch *dispatch = device->dispatch;
-	uint64_t time = input_event_time(e);
-
-#if EVENT_DEBUGGING
-	evdev_print_event(device, e);
-#endif
-
-	libinput_timer_flush(evdev_libinput_context(device), time);
+	struct libinput *libinput = libinput_device_get_context(device);
+	struct evdev_device *dev = evdev_device(device);
 
-	dispatch->interface->process(dispatch, device, e, time);
-}
-
-static inline void
-evdev_device_dispatch_one(struct evdev_device *device,
-			  struct input_event *ev)
-{
-	if (!device->mtdev) {
-		evdev_process_event(device, ev);
-	} else {
-		mtdev_put_event(device->mtdev, ev);
-		if (libevdev_event_is_code(ev, EV_SYN, SYN_REPORT)) {
-			while (!mtdev_empty(device->mtdev)) {
-				struct input_event e;
-				mtdev_get_event(device->mtdev, &e);
-				evdev_process_event(device, &e);
-			}
-		}
-	}
+	evdev_device_dispatch_frame(libinput, dev, frame);
 }
 
 static int
-evdev_sync_device(struct evdev_device *device)
+evdev_sync_device(struct libinput *libinput, struct evdev_device *device)
 {
 	struct input_event ev;
 	int rc;
+	const size_t maxevents = 256;
+	_unref_(evdev_frame) *frame = evdev_frame_new(maxevents);
 
 	do {
-		rc = libevdev_next_event(device->evdev,
-					 LIBEVDEV_READ_FLAG_SYNC, &ev);
+		rc = libevdev_next_event(device->evdev, LIBEVDEV_READ_FLAG_SYNC, &ev);
 		if (rc < 0)
 			break;
-		evdev_device_dispatch_one(device, &ev);
+
+		/* No ENOMEM check here because >maxevents really should never happen */
+		evdev_frame_append_input_event(frame, &ev);
 	} while (rc == LIBEVDEV_READ_STATUS_SYNC);
 
+	evdev_device_dispatch_frame(libinput, device, frame);
+
 	return rc == -EAGAIN ? 0 : rc;
 }
 
 static inline void
-evdev_note_time_delay(struct evdev_device *device,
-		      const struct input_event *ev)
+evdev_note_time_delay(struct evdev_device *device, const struct input_event *ev)
 {
 	struct libinput *libinput = evdev_libinput_context(device);
 	uint32_t tdelta;
@@ -1121,16 +1022,16 @@ evdev_note_time_delay(struct evdev_devic
 	 * where there is no steady event flow and thus SYN_DROPPED may not
 	 * get hit by the kernel despite us being too slow.
 	 */
-	if (libinput->dispatch_time == 0 ||
-	    eventtime > libinput->dispatch_time)
+	if (libinput->dispatch_time == 0 || eventtime > libinput->dispatch_time)
 		return;
 
 	tdelta = us2ms(libinput->dispatch_time - eventtime);
 	if (tdelta > 20) {
-		evdev_log_bug_client_ratelimit(device,
-					       &device->delay_warning_limit,
-					       "event processing lagging behind by %dms, your system is too slow\n",
-					       tdelta);
+		evdev_log_bug_client_ratelimit(
+			device,
+			&device->delay_warning_limit,
+			"event processing lagging behind by %dms, your system is too slow\n",
+			tdelta);
 	}
 }
 
@@ -1142,25 +1043,33 @@ evdev_device_dispatch(void *data)
 	struct input_event ev;
 	int rc;
 	bool once = false;
+	_unref_(evdev_frame) *frame = evdev_frame_new(64);
 
 	/* If the compositor is repainting, this function is called only once
 	 * per frame and we have to process all the events available on the
 	 * fd, otherwise there will be input lag. */
 	do {
-		rc = libevdev_next_event(device->evdev,
-					 LIBEVDEV_READ_FLAG_NORMAL, &ev);
+		rc = libevdev_next_event(device->evdev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
 		if (rc == LIBEVDEV_READ_STATUS_SYNC) {
-			evdev_log_info_ratelimit(device,
-						 &device->syn_drop_limit,
-						 "SYN_DROPPED event - some input events have been lost.\n");
+			evdev_log_info_ratelimit(
+				device,
+				&device->syn_drop_limit,
+				"SYN_DROPPED event - some input events have been lost.\n");
 
 			/* send one more sync event so we handle all
 			   currently pending events before we sync up
 			   to the current state */
 			ev.code = SYN_REPORT;
-			evdev_device_dispatch_one(device, &ev);
 
-			rc = evdev_sync_device(device);
+			if (evdev_frame_append_input_event(frame, &ev) == -ENOMEM) {
+				evdev_log_bug_libinput(
+					device,
+					"event frame overflow, discarding events.\n");
+			}
+			evdev_device_dispatch_frame(libinput, device, frame);
+			evdev_frame_reset(frame);
+
+			rc = evdev_sync_device(libinput, device);
 			if (rc == 0)
 				rc = LIBEVDEV_READ_STATUS_SUCCESS;
 		} else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) {
@@ -1168,13 +1077,30 @@ evdev_device_dispatch(void *data)
 				evdev_note_time_delay(device, &ev);
 				once = true;
 			}
-			evdev_device_dispatch_one(device, &ev);
+
+			if (evdev_frame_append_input_event(frame, &ev) == -ENOMEM) {
+				evdev_log_bug_libinput(
+					device,
+					"event frame overflow, discarding events.\n");
+			}
+			if (ev.type == EV_SYN && ev.code == SYN_REPORT) {
+				evdev_device_dispatch_frame(libinput, device, frame);
+				evdev_frame_reset(frame);
+			}
 		} else if (rc == -ENODEV) {
 			evdev_device_remove(device);
 			return;
 		}
 	} while (rc == LIBEVDEV_READ_STATUS_SUCCESS);
 
+	/* This should never happen, the kernel flushes only on SYN_REPORT */
+	if (evdev_frame_get_count(frame) > 1) {
+		evdev_log_bug_kernel(
+			device,
+			"event frame missing SYN_REPORT, forcing frame.\n");
+		evdev_device_dispatch_frame(libinput, device, frame);
+	}
+
 	if (rc != -EAGAIN && rc != -EINTR) {
 		libinput_remove_source(libinput, device->source);
 		device->source = NULL;
@@ -1182,8 +1108,7 @@ evdev_device_dispatch(void *data)
 }
 
 static inline bool
-evdev_init_accel(struct evdev_device *device,
-		 enum libinput_config_accel_profile which)
+evdev_init_accel(struct evdev_device *device, enum libinput_config_accel_profile which)
 {
 	struct motion_filter *filter = NULL;
 
@@ -1191,21 +1116,25 @@ evdev_init_accel(struct evdev_device *de
 		filter = create_custom_accelerator_filter();
 	else if (device->tags & EVDEV_TAG_TRACKPOINT) {
 		if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT)
-			filter = create_pointer_accelerator_filter_trackpoint_flat(device->trackpoint_multiplier);
+			filter = create_pointer_accelerator_filter_trackpoint_flat(
+				device->trackpoint_multiplier);
 		else
-			filter = create_pointer_accelerator_filter_trackpoint(device->trackpoint_multiplier,
-									      device->use_velocity_averaging);
+			filter = create_pointer_accelerator_filter_trackpoint(
+				device->trackpoint_multiplier,
+				device->use_velocity_averaging);
 	} else {
 		if (which == LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT)
 			filter = create_pointer_accelerator_filter_flat(device->dpi);
 		else if (device->dpi < DEFAULT_MOUSE_DPI)
-			filter = create_pointer_accelerator_filter_linear_low_dpi(device->dpi,
-										  device->use_velocity_averaging);
+			filter = create_pointer_accelerator_filter_linear_low_dpi(
+				device->dpi,
+				device->use_velocity_averaging);
 	}
 
 	if (!filter)
-		filter = create_pointer_accelerator_filter_linear(device->dpi,
-								  device->use_velocity_averaging);
+		filter = create_pointer_accelerator_filter_linear(
+			device->dpi,
+			device->use_velocity_averaging);
 
 	if (!filter)
 		return false;
@@ -1311,7 +1240,8 @@ static enum libinput_config_status
 evdev_set_accel_config(struct libinput_device *libinput_device,
 		       struct libinput_config_accel *accel_config)
 {
-	assert(evdev_accel_config_get_profile(libinput_device) == accel_config->profile);
+	assert(evdev_accel_config_get_profile(libinput_device) ==
+	       accel_config->profile);
 
 	struct evdev_device *dev = evdev_device(libinput_device);
 
@@ -1333,11 +1263,13 @@ evdev_device_init_pointer_acceleration(s
 		device->pointer.config.available = evdev_accel_config_available;
 		device->pointer.config.set_speed = evdev_accel_config_set_speed;
 		device->pointer.config.get_speed = evdev_accel_config_get_speed;
-		device->pointer.config.get_default_speed = evdev_accel_config_get_default_speed;
+		device->pointer.config.get_default_speed =
+			evdev_accel_config_get_default_speed;
 		device->pointer.config.get_profiles = evdev_accel_config_get_profiles;
 		device->pointer.config.set_profile = evdev_accel_config_set_profile;
 		device->pointer.config.get_profile = evdev_accel_config_get_profile;
-		device->pointer.config.get_default_profile = evdev_accel_config_get_default_profile;
+		device->pointer.config.get_default_profile =
+			evdev_accel_config_get_default_profile;
 		device->pointer.config.set_accel_config = evdev_set_accel_config;
 		device->base.config.accel = &device->pointer.config;
 
@@ -1365,9 +1297,9 @@ evdev_read_wheel_click_prop(struct evdev
 	}
 
 	evdev_log_error(device,
-		  "mouse wheel click angle is present but invalid, "
-		  "using %d degrees instead\n",
-		  DEFAULT_WHEEL_CLICK_ANGLE);
+			"mouse wheel click angle is present but invalid, "
+			"using %d degrees instead\n",
+			DEFAULT_WHEEL_CLICK_ANGLE);
 
 	return false;
 }
@@ -1385,14 +1317,14 @@ evdev_read_wheel_click_count_prop(struct
 
 	val = parse_mouse_wheel_click_angle_property(prop);
 	if (val) {
-		*angle = 360.0/val;
+		*angle = 360.0 / val;
 		return true;
 	}
 
 	evdev_log_error(device,
-		  "mouse wheel click count is present but invalid, "
-		  "using %d degrees for angle instead instead\n",
-		  DEFAULT_WHEEL_CLICK_ANGLE);
+			"mouse wheel click count is present but invalid, "
+			"using %d degrees for angle instead instead\n",
+			DEFAULT_WHEEL_CLICK_ANGLE);
 	*angle = DEFAULT_WHEEL_CLICK_ANGLE;
 
 	return false;
@@ -1410,13 +1342,13 @@ evdev_read_wheel_click_props(struct evde
 	/* CLICK_COUNT overrides CLICK_ANGLE */
 	if (evdev_read_wheel_click_count_prop(device, wheel_count, &angles.y) ||
 	    evdev_read_wheel_click_prop(device, wheel_angle, &angles.y)) {
-		evdev_log_debug(device,
-				"wheel: vert click angle: %.2f\n", angles.y);
+		evdev_log_debug(device, "wheel: vert click angle: %.2f\n", angles.y);
 	}
 	if (evdev_read_wheel_click_count_prop(device, hwheel_count, &angles.x) ||
 	    evdev_read_wheel_click_prop(device, hwheel_angle, &angles.x)) {
 		evdev_log_debug(device,
-				"wheel: horizontal click angle: %.2f\n", angles.x);
+				"wheel: horizontal click angle: %.2f\n",
+				angles.x);
 	} else {
 		angles.x = angles.y;
 	}
@@ -1427,18 +1359,14 @@ evdev_read_wheel_click_props(struct evde
 static inline double
 evdev_get_trackpoint_multiplier(struct evdev_device *device)
 {
-	struct quirks_context *quirks;
-	struct quirks *q;
 	double multiplier = 1.0;
 
 	if (!(device->tags & EVDEV_TAG_TRACKPOINT))
 		return 1.0;
 
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
 	if (q) {
 		quirks_get_double(q, QUIRK_ATTR_TRACKPOINT_MULTIPLIER, &multiplier);
-		quirks_unref(q);
 	}
 
 	if (multiplier <= 0.0) {
@@ -1449,9 +1377,7 @@ evdev_get_trackpoint_multiplier(struct e
 	}
 
 	if (multiplier != 1.0)
-		evdev_log_info(device,
-			       "trackpoint multiplier is %.2f\n",
-			       multiplier);
+		evdev_log_info(device, "trackpoint multiplier is %.2f\n", multiplier);
 
 	return multiplier;
 }
@@ -1459,22 +1385,16 @@ evdev_get_trackpoint_multiplier(struct e
 static inline bool
 evdev_need_velocity_averaging(struct evdev_device *device)
 {
-	struct quirks_context *quirks;
-	struct quirks *q;
 	bool use_velocity_averaging = false; /* default off unless we have quirk */
-
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
 	if (q) {
 		quirks_get_bool(q,
 				QUIRK_ATTR_USE_VELOCITY_AVERAGING,
 				&use_velocity_averaging);
-		quirks_unref(q);
 	}
 
 	if (use_velocity_averaging)
-		evdev_log_info(device,
-			       "velocity averaging is turned on\n");
+		evdev_log_info(device, "velocity averaging is turned on\n");
 
 	return use_velocity_averaging;
 }
@@ -1488,8 +1408,7 @@ evdev_read_dpi_prop(struct evdev_device
 	if (device->tags & EVDEV_TAG_TRACKPOINT)
 		return DEFAULT_MOUSE_DPI;
 
-	mouse_dpi = udev_device_get_property_value(device->udev_device,
-						   "MOUSE_DPI");
+	mouse_dpi = udev_device_get_property_value(device->udev_device, "MOUSE_DPI");
 	if (mouse_dpi) {
 		dpi = parse_mouse_dpi_property(mouse_dpi);
 		if (!dpi) {
@@ -1499,9 +1418,7 @@ evdev_read_dpi_prop(struct evdev_device
 					DEFAULT_MOUSE_DPI);
 			dpi = DEFAULT_MOUSE_DPI;
 		}
-		evdev_log_info(device,
-			       "device set to %d DPI\n",
-			       dpi);
+		evdev_log_info(device, "device set to %d DPI\n", dpi);
 	}
 
 	return dpi;
@@ -1528,12 +1445,8 @@ evdev_read_model_flags(struct evdev_devi
 	const struct model_map *m = model_map;
 	uint32_t model_flags = 0;
 	uint32_t all_model_flags = 0;
-	struct quirks_context *quirks;
-	struct quirks *q;
-
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
 
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
 	while (q && m->quirk) {
 		bool is_set;
 
@@ -1558,11 +1471,7 @@ evdev_read_model_flags(struct evdev_devi
 		m++;
 	}
 
-	quirks_unref(q);
-
-	if (parse_udev_flag(device,
-			    device->udev_device,
-			    "ID_INPUT_TRACKBALL")) {
+	if (parse_udev_flag(device, device->udev_device, "ID_INPUT_TRACKBALL")) {
 		evdev_log_debug(device, "tagged as trackball\n");
 		model_flags |= EVDEV_MODEL_TRACKBALL;
 	}
@@ -1579,8 +1488,7 @@ evdev_read_model_flags(struct evdev_devi
 		model_flags |= EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81;
 	}
 
-	if (parse_udev_flag(device, device->udev_device,
-			    "LIBINPUT_TEST_DEVICE")) {
+	if (parse_udev_flag(device, device->udev_device, "LIBINPUT_TEST_DEVICE")) {
 		evdev_log_debug(device, "is a test device\n");
 		model_flags |= EVDEV_MODEL_TEST_DEVICE;
 	}
@@ -1589,17 +1497,12 @@ evdev_read_model_flags(struct evdev_devi
 }
 
 static inline bool
-evdev_read_attr_res_prop(struct evdev_device *device,
-			 size_t *xres,
-			 size_t *yres)
+evdev_read_attr_res_prop(struct evdev_device *device, size_t *xres, size_t *yres)
 {
-	struct quirks_context *quirks;
-	struct quirks *q;
 	struct quirk_dimensions dim;
 	bool rc = false;
 
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
 	if (!q)
 		return false;
 
@@ -1609,23 +1512,16 @@ evdev_read_attr_res_prop(struct evdev_de
 		*yres = dim.y;
 	}
 
-	quirks_unref(q);
-
 	return rc;
 }
 
 static inline bool
-evdev_read_attr_size_prop(struct evdev_device *device,
-			  size_t *size_x,
-			  size_t *size_y)
+evdev_read_attr_size_prop(struct evdev_device *device, size_t *size_x, size_t *size_y)
 {
-	struct quirks_context *quirks;
-	struct quirks *q;
 	struct quirk_dimensions dim;
 	bool rc = false;
 
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
 	if (!q)
 		return false;
 
@@ -1635,8 +1531,6 @@ evdev_read_attr_size_prop(struct evdev_d
 		*size_y = dim.y;
 	}
 
-	quirks_unref(q);
-
 	return rc;
 }
 
@@ -1649,10 +1543,9 @@ evdev_fix_abs_resolution(struct evdev_de
 	struct libevdev *evdev = device->evdev;
 	const struct input_absinfo *absx, *absy;
 	size_t widthmm = 0, heightmm = 0;
-	size_t xres = EVDEV_FAKE_RESOLUTION,
-	       yres = EVDEV_FAKE_RESOLUTION;
+	size_t xres = EVDEV_FAKE_RESOLUTION, yres = EVDEV_FAKE_RESOLUTION;
 
-	if (!(xcode == ABS_X && ycode == ABS_Y)  &&
+	if (!(xcode == ABS_X && ycode == ABS_Y) &&
 	    !(xcode == ABS_MT_POSITION_X && ycode == ABS_MT_POSITION_Y)) {
 		evdev_log_bug_libinput(device,
 				       "invalid x/y code combination %d/%d\n",
@@ -1674,8 +1567,8 @@ evdev_fix_abs_resolution(struct evdev_de
 	 */
 	if (!evdev_read_attr_res_prop(device, &xres, &yres) &&
 	    evdev_read_attr_size_prop(device, &widthmm, &heightmm)) {
-		xres = absinfo_range(absx)/widthmm;
-		yres = absinfo_range(absy)/heightmm;
+		xres = absinfo_range(absx) / widthmm;
+		yres = absinfo_range(absy) / heightmm;
 	}
 
 	/* libevdev_set_abs_resolution() changes the absinfo we already
@@ -1687,19 +1580,17 @@ evdev_fix_abs_resolution(struct evdev_de
 }
 
 static enum evdev_device_udev_tags
-evdev_device_get_udev_tags(struct evdev_device *device,
-			   struct udev_device *udev_device)
+evdev_device_get_udev_tags(struct evdev_device *device, struct udev_device *udev_device)
 {
-	enum evdev_device_udev_tags tags = 0;
+	enum evdev_device_udev_tags tags = EVDEV_UDEV_TAG_NONE;
 	int i;
 
 	for (i = 0; i < 2 && udev_device; i++) {
 		unsigned j;
 		for (j = 0; j < ARRAY_LENGTH(evdev_udev_tag_matches); j++) {
-			const struct evdev_udev_tag_match match = evdev_udev_tag_matches[j];
-			if (parse_udev_flag(device,
-					    udev_device,
-					    match.name))
+			const struct evdev_udev_tag_match match =
+				evdev_udev_tag_matches[j];
+			if (parse_udev_flag(device, udev_device, match.name))
 				tags |= match.tag;
 		}
 		udev_device = udev_device_get_parent(udev_device);
@@ -1722,10 +1613,14 @@ evdev_fix_android_mt(struct evdev_device
 	    evdev_is_fake_mt_device(device))
 		return;
 
-	libevdev_enable_event_code(evdev, EV_ABS, ABS_X,
-		      libevdev_get_abs_info(evdev, ABS_MT_POSITION_X));
-	libevdev_enable_event_code(evdev, EV_ABS, ABS_Y,
-		      libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y));
+	libevdev_enable_event_code(evdev,
+				   EV_ABS,
+				   ABS_X,
+				   libevdev_get_abs_info(evdev, ABS_MT_POSITION_X));
+	libevdev_enable_event_code(evdev,
+				   EV_ABS,
+				   ABS_Y,
+				   libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y));
 }
 
 static inline bool
@@ -1744,18 +1639,17 @@ evdev_check_min_max(struct evdev_device
 		 * them, simply disable the axes so we won't get events,
 		 * we don't know what to do with them anyway.
 		 */
-		if (absinfo->minimum == 0 &&
-		    code >= ABS_MISC && code < ABS_MT_SLOT) {
-			evdev_log_info(device,
-				       "disabling EV_ABS %#x on device (min == max == 0)\n",
-				       code);
-			libevdev_disable_event_code(device->evdev,
-						    EV_ABS,
-						    code);
+		if (absinfo->minimum == 0 && code >= ABS_MISC && code < ABS_MT_SLOT) {
+			evdev_log_info(
+				device,
+				"disabling EV_ABS %#x on device (min == max == 0)\n",
+				code);
+			libevdev_disable_event_code(device->evdev, EV_ABS, code);
 		} else {
-			evdev_log_bug_kernel(device,
-					     "device has min == max on %s\n",
-					     libevdev_event_code_get_name(EV_ABS, code));
+			evdev_log_bug_kernel(
+				device,
+				"device has min == max on %s\n",
+				libevdev_event_code_get_name(EV_ABS, code));
 			return false;
 		}
 	}
@@ -1780,7 +1674,7 @@ evdev_reject_device(struct evdev_device
 
 	if (!evdev_is_fake_mt_device(device) &&
 	    libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) ^
-	    libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y))
+		    libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y))
 		return true;
 
 	if (libevdev_has_event_code(evdev, EV_ABS, ABS_X)) {
@@ -1788,8 +1682,9 @@ evdev_reject_device(struct evdev_device
 		absy = libevdev_get_abs_info(evdev, ABS_Y);
 		if ((absx->resolution == 0 && absy->resolution != 0) ||
 		    (absx->resolution != 0 && absy->resolution == 0)) {
-			evdev_log_bug_kernel(device,
-				       "kernel has only x or y resolution, not both.\n");
+			evdev_log_bug_kernel(
+				device,
+				"kernel has only x or y resolution, not both.\n");
 			return true;
 		}
 	}
@@ -1800,8 +1695,9 @@ evdev_reject_device(struct evdev_device
 		absy = libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y);
 		if ((absx->resolution == 0 && absy->resolution != 0) ||
 		    (absx->resolution != 0 && absy->resolution == 0)) {
-			evdev_log_bug_kernel(device,
-				       "kernel has only x or y MT resolution, not both.\n");
+			evdev_log_bug_kernel(
+				device,
+				"kernel has only x or y MT resolution, not both.\n");
 			return true;
 		}
 	}
@@ -1830,12 +1726,12 @@ evdev_extract_abs_axes(struct evdev_devi
 
 	if (!libevdev_has_event_code(evdev, EV_ABS, ABS_X) ||
 	    !libevdev_has_event_code(evdev, EV_ABS, ABS_Y))
-		 return;
+		return;
 
 	if (evdev_fix_abs_resolution(device, ABS_X, ABS_Y))
 		device->abs.is_fake_resolution = true;
 
-	if (udev_tags & (EVDEV_UDEV_TAG_TOUCHPAD|EVDEV_UDEV_TAG_TOUCHSCREEN)) {
+	if (udev_tags & (EVDEV_UDEV_TAG_TOUCHPAD | EVDEV_UDEV_TAG_TOUCHSCREEN)) {
 		fuzz = evdev_read_fuzz_prop(device, ABS_X);
 		libevdev_set_abs_fuzz(evdev, ABS_X, fuzz);
 		fuzz = evdev_read_fuzz_prop(device, ABS_Y);
@@ -1850,17 +1746,15 @@ evdev_extract_abs_axes(struct evdev_devi
 	if (evdev_is_fake_mt_device(device) ||
 	    !libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) ||
 	    !libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y))
-		 return;
+		return;
 
-	if (evdev_fix_abs_resolution(device,
-				     ABS_MT_POSITION_X,
-				     ABS_MT_POSITION_Y))
+	if (evdev_fix_abs_resolution(device, ABS_MT_POSITION_X, ABS_MT_POSITION_Y))
 		device->abs.is_fake_resolution = true;
 
 	if ((fuzz = evdev_read_fuzz_prop(device, ABS_MT_POSITION_X)))
-	    libevdev_set_abs_fuzz(evdev, ABS_MT_POSITION_X, fuzz);
+		libevdev_set_abs_fuzz(evdev, ABS_MT_POSITION_X, fuzz);
 	if ((fuzz = evdev_read_fuzz_prop(device, ABS_MT_POSITION_Y)))
-	    libevdev_set_abs_fuzz(evdev, ABS_MT_POSITION_Y, fuzz);
+		libevdev_set_abs_fuzz(evdev, ABS_MT_POSITION_Y, fuzz);
 
 	device->abs.absinfo_x = libevdev_get_abs_info(evdev, ABS_MT_POSITION_X);
 	device->abs.absinfo_y = libevdev_get_abs_info(evdev, ABS_MT_POSITION_Y);
@@ -1941,17 +1835,17 @@ evdev_device_is_joystick_or_gamepad(stru
 	unsigned int num_keys = 0;
 
 	for (code = KEY_ESC; code <= KEY_MICMUTE; code++) {
-		if (libevdev_has_event_code(evdev, EV_KEY, code) )
+		if (libevdev_has_event_code(evdev, EV_KEY, code))
 			num_keys++;
 	}
 
 	for (code = KEY_OK; code <= KEY_LIGHTS_TOGGLE; code++) {
-		if (libevdev_has_event_code(evdev, EV_KEY, code) )
+		if (libevdev_has_event_code(evdev, EV_KEY, code))
 			num_keys++;
 	}
 
 	for (code = KEY_ALS_TOGGLE; code < BTN_TRIGGER_HAPPY; code++) {
-		if (libevdev_has_event_code(evdev, EV_KEY, code) )
+		if (libevdev_has_event_code(evdev, EV_KEY, code))
 			num_keys++;
 	}
 
@@ -1962,41 +1856,17 @@ evdev_device_is_joystick_or_gamepad(stru
 }
 
 static struct evdev_dispatch *
-evdev_configure_device(struct evdev_device *device)
+evdev_configure_device(struct evdev_device *device,
+		       enum evdev_device_udev_tags udev_tags)
 {
 	struct libevdev *evdev = device->evdev;
-	enum evdev_device_udev_tags udev_tags;
 	unsigned int tablet_tags;
 	struct evdev_dispatch *dispatch;
 
-	udev_tags = evdev_device_get_udev_tags(device, device->udev_device);
-
-	if ((udev_tags & EVDEV_UDEV_TAG_INPUT) == 0 ||
-	    (udev_tags & ~EVDEV_UDEV_TAG_INPUT) == 0) {
-		evdev_log_info(device,
-			       "not tagged as supported input device\n");
-		return NULL;
-	}
-
-	evdev_log_info(device,
-		 "is tagged by udev as:%s%s%s%s%s%s%s%s%s%s%s\n",
-		 udev_tags & EVDEV_UDEV_TAG_KEYBOARD ? " Keyboard" : "",
-		 udev_tags & EVDEV_UDEV_TAG_MOUSE ? " Mouse" : "",
-		 udev_tags & EVDEV_UDEV_TAG_TOUCHPAD ? " Touchpad" : "",
-		 udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN ? " Touchscreen" : "",
-		 udev_tags & EVDEV_UDEV_TAG_TABLET ? " Tablet" : "",
-		 udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK ? " Pointingstick" : "",
-		 udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "",
-		 udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : "",
-		 udev_tags & EVDEV_UDEV_TAG_TABLET_PAD ? " TabletPad" : "",
-		 udev_tags & EVDEV_UDEV_TAG_TRACKBALL ? " Trackball" : "",
-		 udev_tags & EVDEV_UDEV_TAG_SWITCH ? " Switch" : "");
-
 	/* Ignore pure accelerometers, but accept devices that are
 	 * accelerometers with other axes */
-	if (udev_tags == (EVDEV_UDEV_TAG_INPUT|EVDEV_UDEV_TAG_ACCELEROMETER)) {
-		evdev_log_info(device,
-			 "device is an accelerometer, ignoring\n");
+	if (udev_tags == (EVDEV_UDEV_TAG_INPUT | EVDEV_UDEV_TAG_ACCELEROMETER)) {
+		evdev_log_info(device, "device is an accelerometer, ignoring\n");
 		return NULL;
 	}
 
@@ -2005,8 +1875,7 @@ evdev_configure_device(struct evdev_devi
 	}
 
 	if (evdev_device_is_joystick_or_gamepad(device)) {
-		evdev_log_info(device,
-			       "device is a joystick or a gamepad, ignoring\n");
+		evdev_log_info(device, "device is a joystick or a gamepad, ignoring\n");
 		return NULL;
 	}
 
@@ -2025,8 +1894,7 @@ evdev_configure_device(struct evdev_devi
 			udev_tags &= ~EVDEV_UDEV_TAG_TOUCHSCREEN;
 	}
 
-	if (evdev_device_has_model_quirk(device,
-					 QUIRK_MODEL_DELL_CANVAS_TOTEM)) {
+	if (evdev_device_has_model_quirk(device, QUIRK_MODEL_DELL_CANVAS_TOTEM)) {
 		dispatch = evdev_totem_create(device);
 		device->seat_caps |= EVDEV_DEVICE_TABLET;
 		evdev_log_info(device, "device is a totem\n");
@@ -2036,8 +1904,7 @@ evdev_configure_device(struct evdev_devi
 	/* libwacom assigns touchpad (or touchscreen) _and_ tablet to the
 	   tablet touch bits, so make sure we don't initialize the tablet
 	   interface for the touch device */
-	tablet_tags = EVDEV_UDEV_TAG_TABLET |
-		      EVDEV_UDEV_TAG_TOUCHPAD |
+	tablet_tags = EVDEV_UDEV_TAG_TABLET | EVDEV_UDEV_TAG_TOUCHPAD |
 		      EVDEV_UDEV_TAG_TOUCHSCREEN;
 
 	/* libwacom assigns tablet _and_ tablet_pad to the pad devices */
@@ -2046,7 +1913,6 @@ evdev_configure_device(struct evdev_devi
 		device->seat_caps |= EVDEV_DEVICE_TABLET_PAD;
 		evdev_log_info(device, "device is a tablet pad\n");
 		return dispatch;
-
 	}
 
 	if ((udev_tags & tablet_tags) == EVDEV_UDEV_TAG_TABLET) {
@@ -2071,7 +1937,8 @@ evdev_configure_device(struct evdev_devi
 		evdev_tag_external_mouse(device, device->udev_device);
 		evdev_tag_trackpoint(device, device->udev_device);
 		if (device->tags & EVDEV_TAG_TRACKPOINT)
-			device->trackpoint_multiplier = evdev_get_trackpoint_multiplier(device);
+			device->trackpoint_multiplier =
+				evdev_get_trackpoint_multiplier(device);
 		else
 			device->dpi = evdev_read_dpi_prop(device);
 		/* whether velocity should be averaged, false by default */
@@ -2088,7 +1955,7 @@ evdev_configure_device(struct evdev_devi
 		/* want button scrolling config option */
 		if (libevdev_has_event_code(evdev, EV_REL, REL_X) ||
 		    libevdev_has_event_code(evdev, EV_REL, REL_Y))
-			device->scroll.want_button = 1;
+			device->scroll.want_button = evdev_usage_from_code(EV_KEY, 1);
 	}
 
 	if (udev_tags & EVDEV_UDEV_TAG_KEYBOARD) {
@@ -2117,33 +1984,35 @@ evdev_configure_device(struct evdev_devi
 		}
 
 		if (libevdev_has_event_code(evdev, EV_SW, SW_TABLET_MODE)) {
-		    if (evdev_device_has_model_quirk(device,
-				 QUIRK_MODEL_TABLET_MODE_SWITCH_UNRELIABLE)) {
-			    evdev_log_info(device,
-				"device is an unreliable tablet mode switch, filtering events.\n");
-			    libevdev_disable_event_code(device->evdev,
-							EV_SW,
-							SW_TABLET_MODE);
-		    } else {
-			    device->tags |= EVDEV_TAG_TABLET_MODE_SWITCH;
-			    device->seat_caps |= EVDEV_DEVICE_SWITCH;
-		    }
+			if (evdev_device_has_model_quirk(
+				    device,
+				    QUIRK_MODEL_TABLET_MODE_SWITCH_UNRELIABLE)) {
+				evdev_log_info(
+					device,
+					"device is an unreliable tablet mode switch, filtering events.\n");
+				libevdev_disable_event_code(device->evdev,
+							    EV_SW,
+							    SW_TABLET_MODE);
+			} else {
+				device->tags |= EVDEV_TAG_TABLET_MODE_SWITCH;
+				device->seat_caps |= EVDEV_DEVICE_SWITCH;
+			}
 		}
 
 		if (device->seat_caps & EVDEV_DEVICE_SWITCH)
-		    evdev_log_info(device, "device is a switch device\n");
+			evdev_log_info(device, "device is a switch device\n");
 	}
 
 	if (device->seat_caps & EVDEV_DEVICE_POINTER &&
 	    libevdev_has_event_code(evdev, EV_REL, REL_X) &&
 	    libevdev_has_event_code(evdev, EV_REL, REL_Y) &&
 	    !evdev_init_accel(device, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE)) {
-		evdev_log_error(device,
-				"failed to initialize pointer acceleration\n");
+		evdev_log_error(device, "failed to initialize pointer acceleration\n");
 		return NULL;
 	}
 
-	if (evdev_device_has_model_quirk(device, QUIRK_MODEL_INVERT_HORIZONTAL_SCROLLING)) {
+	if (evdev_device_has_model_quirk(device,
+					 QUIRK_MODEL_INVERT_HORIZONTAL_SCROLLING)) {
 		device->scroll.invert_horizontal_scrolling = true;
 	}
 
@@ -2169,16 +2038,14 @@ evdev_notify_added_device(struct evdev_d
 			device->dispatch->interface->device_added(device, d);
 
 		/* Notify new device if existing device d is suspended */
-		if (d->is_suspended &&
-		    device->dispatch->interface->device_suspended)
+		if (d->is_suspended && device->dispatch->interface->device_suspended)
 			device->dispatch->interface->device_suspended(device, d);
 	}
 
 	notify_added_device(&device->base);
 
 	if (device->dispatch->interface->post_added)
-		device->dispatch->interface->post_added(device,
-							device->dispatch);
+		device->dispatch->interface->post_added(device, device->dispatch);
 }
 
 static bool
@@ -2205,15 +2072,14 @@ out:
 }
 
 static bool
-evdev_set_device_group(struct evdev_device *device,
-		       struct udev_device *udev_device)
+evdev_set_device_group(struct evdev_device *device, struct udev_device *udev_device)
 {
 	struct libinput *libinput = evdev_libinput_context(device);
 	struct libinput_device_group *group = NULL;
 	const char *udev_group;
 
-	udev_group = udev_device_get_property_value(udev_device,
-						    "LIBINPUT_DEVICE_GROUP");
+	udev_group =
+		udev_device_get_property_value(udev_device, "LIBINPUT_DEVICE_GROUP");
 	if (udev_group)
 		group = libinput_device_group_find_group(libinput, udev_group);
 
@@ -2244,10 +2110,9 @@ evdev_drain_fd(int fd)
 static inline void
 evdev_pre_configure_model_quirks(struct evdev_device *device)
 {
-	struct quirks_context *quirks;
-	struct quirks *q;
 	const struct quirk_tuples *t;
 	char *prop;
+	bool is_virtual;
 
 	/* Touchpad claims to have 4 slots but only ever sends 2
 	 * https://bugs.freedesktop.org/show_bug.cgi?id=98100 */
@@ -2257,10 +2122,8 @@ evdev_pre_configure_model_quirks(struct
 	/* Generally we don't care about MSC_TIMESTAMP and it can cause
 	 * unnecessary wakeups but on some devices we need to watch it for
 	 * pointer jumps */
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
-	if (!q ||
-	    !quirks_get_string(q, QUIRK_ATTR_MSC_TIMESTAMP, &prop) ||
+	_unref_(quirks) *q = libinput_device_get_quirks(&device->base);
+	if (!q || !quirks_get_string(q, QUIRK_ATTR_MSC_TIMESTAMP, &prop) ||
 	    !streq(prop, "watch")) {
 		libevdev_disable_event_code(device->evdev, EV_MSC, MSC_TIMESTAMP);
 	}
@@ -2280,13 +2143,15 @@ evdev_pre_configure_model_quirks(struct
 				if (enable)
 					libevdev_enable_event_type(device->evdev, type);
 				else
-					libevdev_disable_event_type(device->evdev, type);
+					libevdev_disable_event_type(device->evdev,
+								    type);
 			} else {
 				if (enable)
-					libevdev_enable_event_code(device->evdev,
-								   type,
-								   code,
-								   type == EV_ABS ?  &absinfo : NULL);
+					libevdev_enable_event_code(
+						device->evdev,
+						type,
+						code,
+						type == EV_ABS ? &absinfo : NULL);
 				else
 					libevdev_disable_event_code(device->evdev,
 								    type,
@@ -2307,17 +2172,10 @@ evdev_pre_configure_model_quirks(struct
 			unsigned int p = t->tuples[idx].first;
 			bool enable = t->tuples[idx].second;
 
-			if (enable) {
+			if (enable)
 				libevdev_enable_property(device->evdev, p);
-			}
-			else {
-#if HAVE_LIBEVDEV_DISABLE_PROPERTY
+			else
 				libevdev_disable_property(device->evdev, p);
-#else
-				evdev_log_error(device,
-						"quirks: a quirk for this device requires newer libevdev than installed\n");
-#endif
-			}
 			evdev_log_debug(device,
 					"quirks: %s %s (%#x)\n",
 					enable ? "enabling" : "disabling",
@@ -2326,7 +2184,12 @@ evdev_pre_configure_model_quirks(struct
 		}
 	}
 
-	quirks_unref(q);
+	if (!quirks_get_bool(q, QUIRK_ATTR_IS_VIRTUAL, &is_virtual)) {
+		is_virtual = !getenv("LIBINPUT_RUNNING_TEST_SUITE") &&
+			     udev_device_is_virtual(device->udev_device);
+	}
+	if (is_virtual)
+		device->tags |= EVDEV_TAG_VIRTUAL;
 }
 
 static void
@@ -2369,15 +2232,13 @@ udev_device_should_be_ignored(struct ude
 {
 	const char *value;
 
-	value = udev_device_get_property_value(udev_device,
-					       "LIBINPUT_IGNORE_DEVICE");
+	value = udev_device_get_property_value(udev_device, "LIBINPUT_IGNORE_DEVICE");
 
 	return value && !streq(value, "0");
 }
 
 struct evdev_device *
-evdev_device_create(struct libinput_seat *seat,
-		    struct udev_device *udev_device)
+evdev_device_create(struct libinput_seat *seat, struct udev_device *udev_device)
 {
 	struct libinput *libinput = seat->libinput;
 	struct evdev_device *device = NULL;
@@ -2385,7 +2246,7 @@ evdev_device_create(struct libinput_seat
 	int fd = -1;
 	int unhandled_device = 0;
 	const char *devnode = udev_device_get_devnode(udev_device);
-	char *sysname = str_sanitize(udev_device_get_sysname(udev_device));
+	_autofree_ char *sysname = str_sanitize(udev_device_get_sysname(udev_device));
 
 	if (!devnode) {
 		log_info(libinput, "%s: no device node associated\n", sysname);
@@ -2399,9 +2260,8 @@ evdev_device_create(struct libinput_seat
 
 	/* Use non-blocking mode so that we can loop on read on
 	 * evdev_device_data() until all events on the fd are
-	 * read.  mtdev_get() also expects this. */
-	fd = open_restricted(libinput, devnode,
-			     O_RDWR | O_NONBLOCK | O_CLOEXEC);
+	 * read. */
+	fd = open_restricted(libinput, devnode, O_RDWR | O_NONBLOCK | O_CLOEXEC);
 	if (fd < 0) {
 		log_info(libinput,
 			 "%s: opening input device '%s' failed (%s).\n",
@@ -2415,8 +2275,7 @@ evdev_device_create(struct libinput_seat
 		goto err;
 
 	device = zalloc(sizeof *device);
-	device->sysname = sysname;
-	sysname = NULL;
+	device->sysname = steal(&sysname);
 
 	libinput_device_init(&device->base, seat);
 	libinput_seat_ref(seat);
@@ -2432,9 +2291,8 @@ evdev_device_create(struct libinput_seat
 					 libevdev_log_func,
 					 LIBEVDEV_LOG_ERROR,
 					 libinput);
-	device->seat_caps = 0;
+	device->seat_caps = EVDEV_DEVICE_NO_CAPABILITIES;
 	device->is_mt = 0;
-	device->mtdev = NULL;
 	device->udev_device = udev_device_ref(udev_device);
 	device->dispatch = NULL;
 	device->fd = fd;
@@ -2442,11 +2300,10 @@ evdev_device_create(struct libinput_seat
 	/* the log_prefix_name is used as part of a printf format string and
 	 * must not contain % directives, see evdev_log_msg */
 	device->log_prefix_name = str_sanitize(device->devname);
-	device->scroll.threshold = 5.0; /* Default may be overridden */
+	device->scroll.threshold = 5.0;                /* Default may be overridden */
 	device->scroll.direction_lock_threshold = 5.0; /* Default may be overridden */
 	device->scroll.direction = 0;
-	device->scroll.wheel_click_angle =
-		evdev_read_wheel_click_props(device);
+	device->scroll.wheel_click_angle = evdev_read_wheel_click_props(device);
 	device->model_flags = evdev_read_model_flags(device);
 	device->dpi = DEFAULT_MOUSE_DPI;
 
@@ -2463,36 +2320,68 @@ evdev_device_create(struct libinput_seat
 
 	evdev_pre_configure_model_quirks(device);
 
-	device->dispatch = evdev_configure_device(device);
-	if (device->dispatch == NULL || device->seat_caps == 0)
+	enum evdev_device_udev_tags udev_tags =
+		evdev_device_get_udev_tags(device, device->udev_device);
+	if ((udev_tags & EVDEV_UDEV_TAG_INPUT) == 0 ||
+	    (udev_tags & ~EVDEV_UDEV_TAG_INPUT) == 0) {
+		evdev_log_info(device, "not tagged as supported input device\n");
 		goto err;
+	}
 
-	device->source =
-		libinput_add_fd(libinput, fd, evdev_device_dispatch, device);
+	evdev_log_info(device,
+		       "is tagged by udev as:%s%s%s%s%s%s%s%s%s%s%s\n",
+		       udev_tags & EVDEV_UDEV_TAG_KEYBOARD ? " Keyboard" : "",
+		       udev_tags & EVDEV_UDEV_TAG_MOUSE ? " Mouse" : "",
+		       udev_tags & EVDEV_UDEV_TAG_TOUCHPAD ? " Touchpad" : "",
+		       udev_tags & EVDEV_UDEV_TAG_TOUCHSCREEN ? " Touchscreen" : "",
+		       udev_tags & EVDEV_UDEV_TAG_TABLET ? " Tablet" : "",
+		       udev_tags & EVDEV_UDEV_TAG_POINTINGSTICK ? " Pointingstick" : "",
+		       udev_tags & EVDEV_UDEV_TAG_JOYSTICK ? " Joystick" : "",
+		       udev_tags & EVDEV_UDEV_TAG_ACCELEROMETER ? " Accelerometer" : "",
+		       udev_tags & EVDEV_UDEV_TAG_TABLET_PAD ? " TabletPad" : "",
+		       udev_tags & EVDEV_UDEV_TAG_TRACKBALL ? " Trackball" : "",
+		       udev_tags & EVDEV_UDEV_TAG_SWITCH ? " Switch" : "");
+
+	libinput_plugin_system_notify_device_new(&libinput->plugin_system,
+						 &device->base,
+						 device->evdev,
+						 device->udev_device);
+
+	device->dispatch = evdev_configure_device(device, udev_tags);
+	if (device->dispatch == NULL ||
+	    device->seat_caps == EVDEV_DEVICE_NO_CAPABILITIES)
+		goto err_notify;
+
+	device->source = libinput_add_fd(libinput, fd, evdev_device_dispatch, device);
 	if (!device->source)
-		goto err;
+		goto err_notify;
 
 	if (!evdev_set_device_group(device, udev_device))
-		goto err;
+		goto err_notify;
 
 	list_insert(seat->devices_list.prev, &device->base.link);
 
+	device->base.inject_evdev_frame = libinput_device_dispatch_frame;
+
 	evdev_notify_added_device(device);
 
 	return device;
 
+err_notify:
+	libinput_plugin_system_notify_device_ignored(&libinput->plugin_system,
+						     &device->base);
+
 err:
 	if (fd >= 0) {
 		close_restricted(libinput, fd);
 		if (device) {
-			unhandled_device = device->seat_caps == 0;
+			unhandled_device =
+				device->seat_caps == EVDEV_DEVICE_NO_CAPABILITIES;
 			evdev_device_destroy(device);
 		}
 	}
 
-	free(sysname);
-
-	return unhandled_device ? EVDEV_UNHANDLED_DEVICE :  NULL;
+	return unhandled_device ? EVDEV_UNHANDLED_DEVICE : NULL;
 }
 
 const char *
@@ -2546,12 +2435,9 @@ evdev_device_set_default_calibration(str
 }
 
 void
-evdev_device_calibrate(struct evdev_device *device,
-		       const float calibration[6])
+evdev_device_calibrate(struct evdev_device *device, const float calibration[6])
 {
-	struct matrix scale,
-		      translate,
-		      transform;
+	struct matrix scale, translate, transform;
 	double sx, sy;
 
 	matrix_from_farray6(&transform, calibration);
@@ -2600,9 +2486,9 @@ evdev_device_calibrate(struct evdev_devi
 
 	/* Normalize */
 	matrix_init_translate(&translate,
-			      -device->abs.absinfo_x->minimum/sx,
-			      -device->abs.absinfo_y->minimum/sy);
-	matrix_init_scale(&scale, 1.0/sx, 1.0/sy);
+			      -device->abs.absinfo_x->minimum / sx,
+			      -device->abs.absinfo_y->minimum / sy);
+	matrix_init_scale(&scale, 1.0 / sx, 1.0 / sy);
 	matrix_mult(&scale, &translate, &scale);
 
 	/* store final matrix in device */
@@ -2671,15 +2557,17 @@ evdev_read_fuzz_prop(struct evdev_device
 		return fuzz;
 
 	if (prop) {
-		evdev_log_bug_libinput(device,
-				       "kernel fuzz of %d even with LIBINPUT_FUZZ_%02x present\n",
-				       abs->fuzz,
-				       code);
+		evdev_log_bug_libinput(
+			device,
+			"kernel fuzz of %d even with LIBINPUT_FUZZ_%02x present\n",
+			abs->fuzz,
+			code);
 	} else {
-		evdev_log_bug_libinput(device,
-				       "kernel fuzz of %d but LIBINPUT_FUZZ_%02x is missing\n",
-				       abs->fuzz,
-				       code);
+		evdev_log_bug_libinput(
+			device,
+			"kernel fuzz of %d but LIBINPUT_FUZZ_%02x is missing\n",
+			abs->fuzz,
+			code);
 	}
 
 	return 0;
@@ -2710,9 +2598,7 @@ evdev_device_has_capability(struct evdev
 }
 
 int
-evdev_device_get_size(const struct evdev_device *device,
-		      double *width,
-		      double *height)
+evdev_device_get_size(const struct evdev_device *device, double *width, double *height)
 {
 	const struct input_absinfo *x, *y;
 
@@ -2723,8 +2609,8 @@ evdev_device_get_size(const struct evdev
 	    (y && y->minimum == 0 && y->maximum == 1))
 		return -1;
 
-	if (!x || !y || device->abs.is_fake_resolution ||
-	    !x->resolution || !y->resolution)
+	if (!x || !y || device->abs.is_fake_resolution || !x->resolution ||
+	    !y->resolution)
 		return -1;
 
 	*width = absinfo_convert_to_mm(x, x->maximum);
@@ -2761,21 +2647,16 @@ evdev_device_get_touch_count(struct evde
 
 	ntouches = libevdev_get_num_slots(device->evdev);
 	if (ntouches == -1) {
-		/* mtdev devices have multitouch but we don't know
-		 * how many. Otherwise, any touch device with num_slots of
+		/* any touch device with num_slots of
 		 * -1 is a single-touch device */
-		if (device->mtdev)
-			ntouches = 0;
-		else
-			ntouches = 1;
+		ntouches = 1;
 	}
 
 	return ntouches;
 }
 
 int
-evdev_device_has_switch(struct evdev_device *device,
-			enum libinput_switch sw)
+evdev_device_has_switch(struct evdev_device *device, enum libinput_switch sw)
 {
 	unsigned int code;
 
@@ -2797,8 +2678,7 @@ evdev_device_has_switch(struct evdev_dev
 }
 
 static inline bool
-evdev_is_scrolling(const struct evdev_device *device,
-		   enum libinput_pointer_axis axis)
+evdev_is_scrolling(const struct evdev_device *device, enum libinput_pointer_axis axis)
 {
 	assert(axis == LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL ||
 	       axis == LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
@@ -2807,8 +2687,7 @@ evdev_is_scrolling(const struct evdev_de
 }
 
 static inline void
-evdev_start_scrolling(struct evdev_device *device,
-		      enum libinput_pointer_axis axis)
+evdev_start_scrolling(struct evdev_device *device, enum libinput_pointer_axis axis)
 {
 	assert(axis == LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL ||
 	       axis == LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
@@ -2825,39 +2704,34 @@ evdev_post_scroll(struct evdev_device *d
 	const struct normalized_coords *trigger;
 	struct normalized_coords event;
 
-	if (!evdev_is_scrolling(device,
-				LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
+	if (!evdev_is_scrolling(device, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
 		device->scroll.buildup.y += delta->y;
-	if (!evdev_is_scrolling(device,
-				LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
+	if (!evdev_is_scrolling(device, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
 		device->scroll.buildup.x += delta->x;
 
 	trigger = &device->scroll.buildup;
 
 	/* If we're not scrolling yet, use a distance trigger: moving
 	   past a certain distance starts scrolling */
-	if (!evdev_is_scrolling(device,
-				LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL) &&
-	    !evdev_is_scrolling(device,
-				LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
+	if (!evdev_is_scrolling(device, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL) &&
+	    !evdev_is_scrolling(device, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
 		if (fabs(trigger->y) >= device->scroll.threshold)
 			evdev_start_scrolling(device,
 					      LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
 		if (fabs(trigger->x) >= device->scroll.threshold)
 			evdev_start_scrolling(device,
 					      LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
-	/* We're already scrolling in one direction. Require some
-	   trigger speed to start scrolling in the other direction */
-	} else if (!evdev_is_scrolling(device,
-			       LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
+		/* We're already scrolling in one direction. Require some
+		   trigger speed to start scrolling in the other direction */
+	} else if (!evdev_is_scrolling(device, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
 		if (fabs(delta->y) >= device->scroll.direction_lock_threshold)
 			evdev_start_scrolling(device,
-				      LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+					      LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
 	} else if (!evdev_is_scrolling(device,
-				LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
+				       LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
 		if (fabs(delta->x) >= device->scroll.direction_lock_threshold)
 			evdev_start_scrolling(device,
-				      LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
+					      LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
 	}
 
 	event = *delta;
@@ -2865,12 +2739,10 @@ evdev_post_scroll(struct evdev_device *d
 	/* We use the trigger to enable, but the delta from this event for
 	 * the actual scroll movement. Otherwise we get a jump once
 	 * scrolling engages */
-	if (!evdev_is_scrolling(device,
-			       LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
+	if (!evdev_is_scrolling(device, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
 		event.y = 0.0;
 
-	if (!evdev_is_scrolling(device,
-			       LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
+	if (!evdev_is_scrolling(device, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
 		event.x = 0.0;
 
 	if (!normalized_is_zero(event)) {
@@ -2980,19 +2852,13 @@ evdev_device_suspend(struct evdev_device
 	evdev_notify_suspended_device(device);
 
 	if (device->dispatch->interface->suspend)
-		device->dispatch->interface->suspend(device->dispatch,
-						     device);
+		device->dispatch->interface->suspend(device->dispatch, device);
 
 	if (device->source) {
 		libinput_remove_source(libinput, device->source);
 		device->source = NULL;
 	}
 
-	if (device->mtdev) {
-		mtdev_close_delete(device->mtdev);
-		device->mtdev = NULL;
-	}
-
 	if (device->fd != -1) {
 		close_restricted(libinput, device->fd);
 		device->fd = -1;
@@ -3018,8 +2884,7 @@ evdev_device_resume(struct evdev_device
 	if (!devnode)
 		return -ENODEV;
 
-	fd = open_restricted(libinput, devnode,
-			     O_RDWR | O_NONBLOCK | O_CLOEXEC);
+	fd = open_restricted(libinput, devnode, O_RDWR | O_NONBLOCK | O_CLOEXEC);
 
 	if (fd < 0)
 		return -errno;
@@ -3033,32 +2898,21 @@ evdev_device_resume(struct evdev_device
 
 	device->fd = fd;
 
-	if (evdev_need_mtdev(device)) {
-		device->mtdev = mtdev_new_open(device->fd);
-		if (!device->mtdev)
-			return -ENODEV;
-	}
-
 	libevdev_change_fd(device->evdev, fd);
 	libevdev_set_clock_id(device->evdev, CLOCK_MONOTONIC);
 
 	/* re-sync libevdev's view of the device, but discard the actual
 	   events. Our device is in a neutral state already */
-	libevdev_next_event(device->evdev,
-			    LIBEVDEV_READ_FLAG_FORCE_SYNC,
-			    &ev);
+	libevdev_next_event(device->evdev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev);
 	do {
 		status = libevdev_next_event(device->evdev,
 					     LIBEVDEV_READ_FLAG_SYNC,
 					     &ev);
 	} while (status == LIBEVDEV_READ_STATUS_SYNC);
 
-	device->source =
-		libinput_add_fd(libinput, fd, evdev_device_dispatch, device);
-	if (!device->source) {
-		mtdev_close_delete(device->mtdev);
+	device->source = libinput_add_fd(libinput, fd, evdev_device_dispatch, device);
+	if (!device->source)
 		return -ENOMEM;
-	}
 
 	evdev_notify_resumed_device(device);
 
diff -pruN 1.28.1-1/src/evdev.h 1.30.0-1/src/evdev.h
--- 1.28.1-1/src/evdev.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/evdev.h	2025-11-25 03:40:43.000000000 +0000
@@ -28,53 +28,58 @@
 
 #include "config.h"
 
-#include <stdbool.h>
-#include <stdarg.h>
-#include "linux/input.h"
 #include <libevdev/libevdev.h>
+#include <stdarg.h>
+#include <stdbool.h>
 
-#include "libinput-private.h"
-#include "timer.h"
+#include "util-input-event.h"
+
+#include "evdev-frame.h"
 #include "filter.h"
+#include "libinput-feature.h"
+#include "libinput-private.h"
+#include "linux/input.h"
 #include "quirks.h"
-#include "util-input-event.h"
+#include "timer.h"
 
 /* The fake resolution value for abs devices without resolution */
 #define EVDEV_FAKE_RESOLUTION 1
 
 enum evdev_event_type {
-	EVDEV_NONE,
-	EVDEV_ABSOLUTE_TOUCH_DOWN	= bit(0),
-	EVDEV_ABSOLUTE_MOTION		= bit(1),
-	EVDEV_ABSOLUTE_TOUCH_UP		= bit(2),
-	EVDEV_ABSOLUTE_MT		= bit(3),
-	EVDEV_WHEEL			= bit(4),
-	EVDEV_KEY			= bit(5),
-	EVDEV_RELATIVE_MOTION		= bit(6),
-	EVDEV_BUTTON			= bit(7),
+	EVDEV_NONE = 0,
+	EVDEV_ABSOLUTE_TOUCH_DOWN = bit(0),
+	EVDEV_ABSOLUTE_MOTION = bit(1),
+	EVDEV_ABSOLUTE_TOUCH_UP = bit(2),
+	EVDEV_ABSOLUTE_MT = bit(3),
+	EVDEV_KEY = bit(4),
+	EVDEV_RELATIVE_MOTION = bit(5),
+	EVDEV_BUTTON = bit(6),
 };
 
 enum evdev_device_seat_capability {
-	EVDEV_DEVICE_POINTER		= bit(0),
-	EVDEV_DEVICE_KEYBOARD		= bit(1),
-	EVDEV_DEVICE_TOUCH		= bit(2),
-	EVDEV_DEVICE_TABLET		= bit(3),
-	EVDEV_DEVICE_TABLET_PAD		= bit(4),
-	EVDEV_DEVICE_GESTURE		= bit(5),
-	EVDEV_DEVICE_SWITCH		= bit(6),
+	EVDEV_DEVICE_NO_CAPABILITIES = 0,
+	EVDEV_DEVICE_POINTER = bit(0),
+	EVDEV_DEVICE_KEYBOARD = bit(1),
+	EVDEV_DEVICE_TOUCH = bit(2),
+	EVDEV_DEVICE_TABLET = bit(3),
+	EVDEV_DEVICE_TABLET_PAD = bit(4),
+	EVDEV_DEVICE_GESTURE = bit(5),
+	EVDEV_DEVICE_SWITCH = bit(6),
 };
 
 enum evdev_device_tags {
-	EVDEV_TAG_EXTERNAL_MOUSE	= bit(0),
-	EVDEV_TAG_INTERNAL_TOUCHPAD	= bit(1),
-	EVDEV_TAG_EXTERNAL_TOUCHPAD	= bit(2),
-	EVDEV_TAG_TRACKPOINT		= bit(3),
-	EVDEV_TAG_KEYBOARD		= bit(4),
-	EVDEV_TAG_LID_SWITCH		= bit(5),
-	EVDEV_TAG_INTERNAL_KEYBOARD	= bit(6),
-	EVDEV_TAG_EXTERNAL_KEYBOARD	= bit(7),
-	EVDEV_TAG_TABLET_MODE_SWITCH	= bit(8),
-	EVDEV_TAG_TABLET_TOUCHPAD	= bit(9),
+	EVDEV_TAG_NONE = 0,
+	EVDEV_TAG_EXTERNAL_MOUSE = bit(0),
+	EVDEV_TAG_INTERNAL_TOUCHPAD = bit(1),
+	EVDEV_TAG_EXTERNAL_TOUCHPAD = bit(2),
+	EVDEV_TAG_TRACKPOINT = bit(3),
+	EVDEV_TAG_KEYBOARD = bit(4),
+	EVDEV_TAG_LID_SWITCH = bit(5),
+	EVDEV_TAG_INTERNAL_KEYBOARD = bit(6),
+	EVDEV_TAG_EXTERNAL_KEYBOARD = bit(7),
+	EVDEV_TAG_TABLET_MODE_SWITCH = bit(8),
+	EVDEV_TAG_TABLET_TOUCHPAD = bit(9),
+	EVDEV_TAG_VIRTUAL = bit(10),
 };
 
 enum evdev_middlebutton_state {
@@ -107,24 +112,24 @@ enum evdev_middlebutton_event {
  */
 enum evdev_device_model {
 	EVDEV_MODEL_DEFAULT = 0,
-	EVDEV_MODEL_WACOM_TOUCHPAD		= bit(1),
-	EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD	= bit(2),
-	EVDEV_MODEL_ALPS_SERIAL_TOUCHPAD	= bit(3),
-	EVDEV_MODEL_LENOVO_T450_TOUCHPAD	= bit(4),
-	EVDEV_MODEL_APPLE_TOUCHPAD_ONEBUTTON	= bit(5),
-	EVDEV_MODEL_LENOVO_SCROLLPOINT		= bit(6),
+	EVDEV_MODEL_WACOM_TOUCHPAD = bit(1),
+	EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD = bit(2),
+	EVDEV_MODEL_ALPS_SERIAL_TOUCHPAD = bit(3),
+	EVDEV_MODEL_LENOVO_T450_TOUCHPAD = bit(4),
+	EVDEV_MODEL_APPLE_TOUCHPAD_ONEBUTTON = bit(5),
+	EVDEV_MODEL_LENOVO_SCROLLPOINT = bit(6),
 
 	/* udev tags, not true quirks */
-	EVDEV_MODEL_TEST_DEVICE			= bit(20),
-	EVDEV_MODEL_TRACKBALL			= bit(21),
-	EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81	= bit(22),
+	EVDEV_MODEL_TEST_DEVICE = bit(20),
+	EVDEV_MODEL_TRACKBALL = bit(21),
+	EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81 = bit(22),
 };
 
 enum evdev_button_scroll_state {
 	BUTTONSCROLL_IDLE,
-	BUTTONSCROLL_BUTTON_DOWN,	/* button is down */
-	BUTTONSCROLL_READY,		/* ready for scroll events */
-	BUTTONSCROLL_SCROLLING,		/* have sent scroll events */
+	BUTTONSCROLL_BUTTON_DOWN, /* button is down */
+	BUTTONSCROLL_READY,       /* ready for scroll events */
+	BUTTONSCROLL_SCROLLING,   /* have sent scroll events */
 };
 
 enum evdev_button_scroll_lock_state {
@@ -178,14 +183,16 @@ struct evdev_device {
 	enum evdev_device_tags tags;
 	bool is_mt;
 	bool is_suspended;
-	int dpi; /* HW resolution */
+	int dpi;                      /* HW resolution */
 	double trackpoint_multiplier; /* trackpoint constant multiplier */
-	bool use_velocity_averaging; /* whether averaging should be applied on velocity calculation */
+	bool use_velocity_averaging;  /* whether averaging should be applied on velocity
+					 calculation */
 	struct ratelimit syn_drop_limit; /* ratelimit for SYN_DROPPED logging */
-	struct ratelimit delay_warning_limit; /* ratelimit for delayd processing logging */
-	struct ratelimit nonpointer_rel_limit; /* ratelimit for REL_* events from non-pointer devices */
+	struct ratelimit
+		delay_warning_limit; /* ratelimit for delayd processing logging */
+	struct ratelimit nonpointer_rel_limit; /* ratelimit for REL_* events from
+						  non-pointer devices */
 	uint32_t model_flags;
-	struct mtdev *mtdev;
 
 	struct {
 		const struct input_absinfo *absinfo_x, *absinfo_y;
@@ -193,8 +200,9 @@ struct evdev_device {
 
 		int apply_calibration;
 		struct matrix calibration;
-		struct matrix default_calibration; /* from LIBINPUT_CALIBRATION_MATRIX */
-		struct matrix usermatrix; /* as supplied by the caller */
+		struct matrix
+			default_calibration; /* from LIBINPUT_CALIBRATION_MATRIX */
+		struct matrix usermatrix;    /* as supplied by the caller */
 
 		struct device_coords dimensions;
 
@@ -209,13 +217,13 @@ struct evdev_device {
 		struct libinput_device_config_scroll_method config;
 		/* Currently enabled method, button */
 		enum libinput_config_scroll_method method;
-		uint32_t button;
+		evdev_usage_t button;
 		uint64_t button_down_time;
 
 		/* set during device init, used at runtime to delay changes
 		 * until all buttons are up */
 		enum libinput_config_scroll_method want_method;
-		uint32_t want_button;
+		evdev_usage_t want_button;
 		/* Checks if buttons are down and commits the setting */
 		void (*change_scroll_method)(struct evdev_device *device);
 		enum evdev_button_scroll_state button_scroll_state;
@@ -288,12 +296,11 @@ struct evdev_dispatch_interface {
 	/* Process an evdev input event. */
 	void (*process)(struct evdev_dispatch *dispatch,
 			struct evdev_device *device,
-			struct input_event *event,
+			struct evdev_frame *frame,
 			uint64_t time);
 
 	/* Device is being suspended */
-	void (*suspend)(struct evdev_dispatch *dispatch,
-			struct evdev_device *device);
+	void (*suspend)(struct evdev_dispatch *dispatch, struct evdev_device *device);
 
 	/* Device is being removed (may be NULL) */
 	void (*remove)(struct evdev_dispatch *dispatch);
@@ -340,13 +347,15 @@ struct evdev_dispatch_interface {
 					      uint64_t now);
 
 	/* Return the state of the given switch */
-	enum libinput_switch_state
-		(*get_switch_state)(struct evdev_dispatch *dispatch,
-				    enum libinput_switch which);
+	enum libinput_switch_state (*get_switch_state)(struct evdev_dispatch *dispatch,
+						       enum libinput_switch which);
 
 	void (*left_handed_toggle)(struct evdev_dispatch *dispatch,
 				   struct evdev_device *device,
 				   bool left_handed_enabled);
+
+	void (*disable_feature)(struct evdev_dispatch *dispatch,
+				enum libinput_feature feature);
 };
 
 enum evdev_dispatch_type {
@@ -376,8 +385,7 @@ evdev_verify_dispatch_type(struct evdev_
 }
 
 struct evdev_device *
-evdev_device_create(struct libinput_seat *seat,
-		    struct udev_device *device);
+evdev_device_create(struct libinput_seat *seat, struct udev_device *device);
 
 static inline struct libinput *
 evdev_libinput_context(const struct evdev_device *device)
@@ -385,35 +393,29 @@ evdev_libinput_context(const struct evde
 	return device->base.seat->libinput;
 }
 
-static inline bool
-evdev_device_has_model_quirk(struct evdev_device *device,
-			     enum quirk model_quirk)
+static inline void
+evdev_device_disable_feature(struct evdev_device *device, enum libinput_feature feature)
 {
-	struct quirks_context *quirks;
-	struct quirks *q;
-	bool result = false;
-
-	assert(quirk_get_name(model_quirk) != NULL);
-
-	quirks = evdev_libinput_context(device)->quirks;
-	q = quirks_fetch_for_device(quirks, device->udev_device);
-	quirks_get_bool(q, model_quirk, &result);
-	quirks_unref(q);
+	if (device->dispatch && device->dispatch->interface &&
+	    device->dispatch->interface->disable_feature)
+		device->dispatch->interface->disable_feature(device->dispatch, feature);
+}
 
-	return result;
+static inline bool
+evdev_device_has_model_quirk(struct evdev_device *device, enum quirk model_quirk)
+{
+	return libinput_device_has_model_quirk(&device->base, model_quirk);
 }
 
 void
-evdev_transform_absolute(struct evdev_device *device,
-			 struct device_coords *point);
+evdev_transform_absolute(struct evdev_device *device, struct device_coords *point);
 
 void
-evdev_transform_relative(struct evdev_device *device,
-			 struct device_coords *point);
+evdev_transform_relative(struct evdev_device *device, struct device_coords *point);
 
 void
 evdev_init_calibration(struct evdev_device *device,
-		        struct libinput_device_config_calibration *calibration);
+		       struct libinput_device_config_calibration *calibration);
 
 void
 evdev_read_calibration_prop(struct evdev_device *device);
@@ -425,8 +427,7 @@ enum switch_reliability
 evdev_read_switch_reliability_prop(struct evdev_device *device);
 
 void
-evdev_init_sendevents(struct evdev_device *device,
-		      struct evdev_dispatch *dispatch);
+evdev_init_sendevents(struct evdev_device *device, struct evdev_dispatch *dispatch);
 
 void
 evdev_device_init_pointer_acceleration(struct evdev_device *device,
@@ -456,9 +457,6 @@ evdev_totem_create(struct evdev_device *
 bool
 evdev_is_fake_mt_device(struct evdev_device *device);
 
-int
-evdev_need_mtdev(struct evdev_device *device);
-
 void
 evdev_device_led_update(struct evdev_device *device, enum libinput_led leds);
 
@@ -490,17 +488,14 @@ void
 evdev_device_set_default_calibration(struct evdev_device *device,
 				     const float calibration[6]);
 void
-evdev_device_calibrate(struct evdev_device *device,
-		       const float calibration[6]);
+evdev_device_calibrate(struct evdev_device *device, const float calibration[6]);
 
 bool
 evdev_device_has_capability(struct evdev_device *device,
 			    enum libinput_device_capability capability);
 
 int
-evdev_device_get_size(const struct evdev_device *device,
-		      double *w,
-		      double *h);
+evdev_device_get_size(const struct evdev_device *device, double *w, double *h);
 
 int
 evdev_device_has_button(struct evdev_device *device, uint32_t code);
@@ -512,12 +507,10 @@ int
 evdev_device_get_touch_count(struct evdev_device *device);
 
 int
-evdev_device_has_switch(struct evdev_device *device,
-			enum libinput_switch sw);
+evdev_device_has_switch(struct evdev_device *device, enum libinput_switch sw);
 
 int
-evdev_device_tablet_pad_has_key(struct evdev_device *device,
-				uint32_t code);
+evdev_device_tablet_pad_has_key(struct evdev_device *device, uint32_t code);
 
 int
 evdev_device_tablet_pad_get_num_buttons(struct evdev_device *device);
@@ -535,22 +528,16 @@ int
 evdev_device_tablet_pad_get_num_mode_groups(struct evdev_device *device);
 
 struct libinput_tablet_pad_mode_group *
-evdev_device_tablet_pad_get_mode_group(struct evdev_device *device,
-				       unsigned int index);
+evdev_device_tablet_pad_get_mode_group(struct evdev_device *device, unsigned int index);
 
 enum libinput_switch_state
-evdev_device_switch_get_state(struct evdev_device *device,
-			      enum libinput_switch sw);
+evdev_device_switch_get_state(struct evdev_device *device, enum libinput_switch sw);
 
 double
-evdev_device_transform_x(struct evdev_device *device,
-			 double x,
-			 uint32_t width);
+evdev_device_transform_x(struct evdev_device *device, double x, uint32_t width);
 
 double
-evdev_device_transform_y(struct evdev_device *device,
-			 double y,
-			 uint32_t height);
+evdev_device_transform_y(struct evdev_device *device, double y, uint32_t height);
 void
 evdev_device_suspend(struct evdev_device *device);
 
@@ -566,12 +553,12 @@ evdev_notify_resumed_device(struct evdev
 void
 evdev_pointer_notify_button(struct evdev_device *device,
 			    uint64_t time,
-			    unsigned int button,
+			    evdev_usage_t button,
 			    enum libinput_button_state state);
 void
 evdev_pointer_notify_physical_button(struct evdev_device *device,
 				     uint64_t time,
-				     int button,
+				     evdev_usage_t button,
 				     enum libinput_button_state state);
 
 void
@@ -582,12 +569,11 @@ evdev_init_button_scroll(struct evdev_de
 			 void (*change_scroll_method)(struct evdev_device *));
 
 void
-evdev_set_button_scroll_lock_enabled(struct evdev_device *device,
-				     bool enabled);
+evdev_set_button_scroll_lock_enabled(struct evdev_device *device, bool enabled);
 
 int
 evdev_update_key_down_count(struct evdev_device *device,
-			    int code,
+			    evdev_usage_t code,
 			    int pressed);
 
 void
@@ -604,9 +590,9 @@ evdev_notify_axis_wheel(struct evdev_dev
 			const struct wheel_v120 *v120_in);
 void
 evdev_notify_axis_finger(struct evdev_device *device,
-			uint64_t time,
-			uint32_t axes,
-			const struct normalized_coords *delta_in);
+			 uint64_t time,
+			 uint32_t axes,
+			 const struct normalized_coords *delta_in);
 void
 evdev_notify_axis_continous(struct evdev_device *device,
 			    uint64_t time,
@@ -633,13 +619,11 @@ evdev_device_destroy(struct evdev_device
 bool
 evdev_middlebutton_filter_button(struct evdev_device *device,
 				 uint64_t time,
-				 int button,
+				 evdev_usage_t button,
 				 enum libinput_button_state state);
 
 void
-evdev_init_middlebutton(struct evdev_device *device,
-			bool enabled,
-			bool want_config);
+evdev_init_middlebutton(struct evdev_device *device, bool enabled, bool want_config);
 
 enum libinput_config_middle_emulation_state
 evdev_middlebutton_get(struct libinput_device *device);
@@ -665,15 +649,14 @@ void
 evdev_init_left_handed(struct evdev_device *device,
 		       void (*change_to_left_handed)(struct evdev_device *));
 
-static inline uint32_t
-evdev_to_left_handed(struct evdev_device *device,
-		     uint32_t button)
+static inline evdev_usage_t
+evdev_to_left_handed(struct evdev_device *device, evdev_usage_t button)
 {
 	if (device->left_handed.enabled) {
-		if (button == BTN_LEFT)
-			return BTN_RIGHT;
-		else if (button == BTN_RIGHT)
-			return BTN_LEFT;
+		if (evdev_usage_eq(button, EVDEV_BTN_LEFT))
+			return evdev_usage_from(EVDEV_BTN_RIGHT);
+		else if (evdev_usage_eq(button, EVDEV_BTN_RIGHT))
+			return evdev_usage_from(EVDEV_BTN_LEFT);
 	}
 	return button;
 }
@@ -731,8 +714,8 @@ evdev_hysteresis(const struct device_coo
 	 * the ratio of finger_distance to margin_distance:
 	 *   dx²/a² + dy²/b² = normalized_finger_distance²
 	 */
-	normalized_finger_distance = sqrt((double)dx2 / (a * a) +
-					  (double)dy2 / (b * b));
+	normalized_finger_distance =
+		sqrt((double)dx2 / (a * a) + (double)dy2 / (b * b));
 
 	/* Which means anything less than 1 is within the elliptical margin */
 	if (normalized_finger_distance < 1.0)
@@ -749,9 +732,8 @@ evdev_hysteresis(const struct device_coo
 	if (dx) {
 		double gradient = (double)dy / dx;
 		lag_x = margin_distance / sqrt(gradient * gradient + 1);
-		lag_y = sqrt((margin_distance + lag_x) *
-			     (margin_distance - lag_x));
-	} else {  /* Infinite gradient */
+		lag_y = sqrt((margin_distance + lag_x) * (margin_distance - lag_x));
+	} else { /* Infinite gradient */
 		lag_x = 0.0;
 		lag_y = margin_distance;
 	}
@@ -776,7 +758,7 @@ evdev_log_msg(struct evdev_device *devic
 	va_list args;
 	char buf[1024];
 
-	if (!is_logged(evdev_libinput_context(device), priority))
+	if (!log_is_logged(evdev_libinput_context(device), priority))
 		return;
 
 	/* Anything info and above is user-visible, use the device name */
@@ -784,8 +766,9 @@ evdev_log_msg(struct evdev_device *devic
 		 sizeof(buf),
 		 "%-7s - %s%s%s",
 		 evdev_device_get_sysname(device),
-		 (priority > LIBINPUT_LOG_PRIORITY_DEBUG) ?  device->log_prefix_name : "",
-		 (priority > LIBINPUT_LOG_PRIORITY_DEBUG) ?  ": " : "",
+		 (priority > LIBINPUT_LOG_PRIORITY_DEBUG) ? device->log_prefix_name
+							  : "",
+		 (priority > LIBINPUT_LOG_PRIORITY_DEBUG) ? ": " : "",
 		 format);
 
 	va_start(args, format);
@@ -794,7 +777,6 @@ evdev_log_msg(struct evdev_device *devic
 	log_msg_va(evdev_libinput_context(device), priority, buf, args);
 #pragma GCC diagnostic pop
 	va_end(args);
-
 }
 
 LIBINPUT_ATTRIBUTE_PRINTF(4, 5)
@@ -810,7 +792,7 @@ evdev_log_msg_ratelimit(struct evdev_dev
 
 	enum ratelimit_state state;
 
-	if (!is_logged(evdev_libinput_context(device), priority))
+	if (!log_is_logged(evdev_libinput_context(device), priority))
 		return;
 
 	state = ratelimit_test(ratelimit);
@@ -822,8 +804,9 @@ evdev_log_msg_ratelimit(struct evdev_dev
 		 sizeof(buf),
 		 "%-7s - %s%s%s",
 		 evdev_device_get_sysname(device),
-		 (priority > LIBINPUT_LOG_PRIORITY_DEBUG) ?  device->log_prefix_name : "",
-		 (priority > LIBINPUT_LOG_PRIORITY_DEBUG) ?  ": " : "",
+		 (priority > LIBINPUT_LOG_PRIORITY_DEBUG) ? device->log_prefix_name
+							  : "",
+		 (priority > LIBINPUT_LOG_PRIORITY_DEBUG) ? ": " : "",
 		 format);
 
 	va_start(args, format);
@@ -842,7 +825,6 @@ evdev_log_msg_ratelimit(struct evdev_dev
 			      ratelimit->burst,
 			      ht.value,
 			      ht.unit);
-
 	}
 }
 
@@ -870,14 +852,13 @@ evdev_log_msg_ratelimit(struct evdev_dev
  * Convert the pair of delta coordinates in device space to mm.
  */
 static inline struct phys_coords
-evdev_device_unit_delta_to_mm(const struct evdev_device* device,
+evdev_device_unit_delta_to_mm(const struct evdev_device *device,
 			      const struct device_coords *units)
 {
-	struct phys_coords mm = { 0,  0 };
+	struct phys_coords mm = { 0, 0 };
 	const struct input_absinfo *absx, *absy;
 
-	if (device->abs.absinfo_x == NULL ||
-	    device->abs.absinfo_y == NULL) {
+	if (device->abs.absinfo_x == NULL || device->abs.absinfo_y == NULL) {
 		log_bug_libinput(evdev_libinput_context(device),
 				 "%s: is not an abs device\n",
 				 device->devname);
@@ -887,8 +868,8 @@ evdev_device_unit_delta_to_mm(const stru
 	absx = device->abs.absinfo_x;
 	absy = device->abs.absinfo_y;
 
-	mm.x = 1.0 * units->x/absx->resolution;
-	mm.y = 1.0 * units->y/absy->resolution;
+	mm.x = 1.0 * units->x / absx->resolution;
+	mm.y = 1.0 * units->y / absy->resolution;
 
 	return mm;
 }
@@ -898,14 +879,13 @@ evdev_device_unit_delta_to_mm(const stru
  * axis min into account, i.e. a unit of min is equivalent to 0 mm.
  */
 static inline struct phys_coords
-evdev_device_units_to_mm(const struct evdev_device* device,
+evdev_device_units_to_mm(const struct evdev_device *device,
 			 const struct device_coords *units)
 {
-	struct phys_coords mm = { 0,  0 };
+	struct phys_coords mm = { 0, 0 };
 	const struct input_absinfo *absx, *absy;
 
-	if (device->abs.absinfo_x == NULL ||
-	    device->abs.absinfo_y == NULL) {
+	if (device->abs.absinfo_x == NULL || device->abs.absinfo_y == NULL) {
 		log_bug_libinput(evdev_libinput_context(device),
 				 "%s: is not an abs device\n",
 				 device->devname);
@@ -915,8 +895,8 @@ evdev_device_units_to_mm(const struct ev
 	absx = device->abs.absinfo_x;
 	absy = device->abs.absinfo_y;
 
-	mm.x = (units->x - absx->minimum)/absx->resolution;
-	mm.y = (units->y - absy->minimum)/absy->resolution;
+	mm.x = (units->x - absx->minimum) / absx->resolution;
+	mm.y = (units->y - absy->minimum) / absy->resolution;
 
 	return mm;
 }
@@ -929,11 +909,10 @@ static inline struct device_coords
 evdev_device_mm_to_units(const struct evdev_device *device,
 			 const struct phys_coords *mm)
 {
-	struct device_coords units = { 0,  0 };
+	struct device_coords units = { 0, 0 };
 	const struct input_absinfo *absx, *absy;
 
-	if (device->abs.absinfo_x == NULL ||
-	    device->abs.absinfo_y == NULL) {
+	if (device->abs.absinfo_x == NULL || device->abs.absinfo_y == NULL) {
 		log_bug_libinput(evdev_libinput_context(device),
 				 "%s: is not an abs device\n",
 				 device->devname);
@@ -950,14 +929,12 @@ evdev_device_mm_to_units(const struct ev
 }
 
 static inline struct device_coord_rect
-evdev_phys_rect_to_units(const struct evdev_device *device,
-			 const struct phys_rect *mm)
+evdev_phys_rect_to_units(const struct evdev_device *device, const struct phys_rect *mm)
 {
-	struct device_coord_rect units = {0};
+	struct device_coord_rect units = { 0 };
 	const struct input_absinfo *absx, *absy;
 
-	if (device->abs.absinfo_x == NULL ||
-	    device->abs.absinfo_y == NULL) {
+	if (device->abs.absinfo_x == NULL || device->abs.absinfo_y == NULL) {
 		log_bug_libinput(evdev_libinput_context(device),
 				 "%s: is not an abs device\n",
 				 device->devname);
@@ -992,26 +969,24 @@ evdev_device_init_abs_range_warnings(str
 	device->abs.warning_range.max.y = y->maximum + 0.05 * height;
 
 	/* One warning every 5 min is enough */
-	ratelimit_init(&device->abs.warning_range.range_warn_limit,
-		       s2us(3000),
-		       1);
+	ratelimit_init(&device->abs.warning_range.range_warn_limit, s2us(3000), 1);
 }
 
 static inline void
 evdev_device_check_abs_axis_range(struct evdev_device *device,
-				  unsigned int code,
+				  evdev_usage_t usage,
 				  int value)
 {
 	int min, max;
 
-	switch(code) {
-	case ABS_X:
-	case ABS_MT_POSITION_X:
+	switch (evdev_usage_enum(usage)) {
+	case EVDEV_ABS_X:
+	case EVDEV_ABS_MT_POSITION_X:
 		min = device->abs.warning_range.min.x;
 		max = device->abs.warning_range.max.x;
 		break;
-	case ABS_Y:
-	case ABS_MT_POSITION_Y:
+	case EVDEV_ABS_Y:
+	case EVDEV_ABS_MT_POSITION_Y:
 		min = device->abs.warning_range.min.y;
 		max = device->abs.warning_range.max.y;
 		break;
@@ -1020,12 +995,16 @@ evdev_device_check_abs_axis_range(struct
 	}
 
 	if (value < min || value > max) {
-		log_info_ratelimit(evdev_libinput_context(device),
-				   &device->abs.warning_range.range_warn_limit,
-				   "Axis %#x value %d is outside expected range [%d, %d]\n"
-				   "See %s/absolute_coordinate_ranges.html for details\n",
-				   code, value, min, max,
-				   HTTP_DOC_LINK);
+		log_info_ratelimit(
+			evdev_libinput_context(device),
+			&device->abs.warning_range.range_warn_limit,
+			"Axis %#x value %d is outside expected range [%d, %d]\n"
+			"See %s/absolute-coordinate-ranges.html for details\n",
+			evdev_usage_enum(usage),
+			value,
+			min,
+			max,
+			HTTP_DOC_LINK);
 	}
 }
 
@@ -1044,4 +1023,32 @@ evdev_paired_keyboard_destroy(struct evd
 	free(kbd);
 }
 
+static inline bool
+evdev_device_is_virtual(struct evdev_device *device)
+{
+	return device->tags & EVDEV_TAG_VIRTUAL;
+}
+
+static inline keycode_t
+keycode_from_usage(evdev_usage_t usage)
+{
+	uint32_t type = evdev_usage_type(usage);
+
+	assert(type == EV_KEY);
+
+	uint32_t code = evdev_usage_code(usage);
+	return keycode_from_uint32_t(code);
+}
+
+static inline button_code_t
+button_code_from_usage(evdev_usage_t usage)
+{
+	uint32_t type = evdev_usage_type(usage);
+
+	assert(type == EV_KEY);
+
+	uint32_t code = evdev_usage_code(usage);
+	return button_code_from_uint32_t(code);
+}
+
 #endif /* EVDEV_H */
diff -pruN 1.28.1-1/src/filter-custom.c 1.30.0-1/src/filter-custom.c
--- 1.28.1-1/src/filter-custom.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/filter-custom.c	2025-11-25 03:40:43.000000000 +0000
@@ -22,18 +22,19 @@
 #include "config.h"
 
 #include <assert.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdint.h>
 
-#include "filter.h"
 #include "filter-private.h"
+#include "filter.h"
 
 #define MOTION_TIMEOUT ms2us(1000)
 #define FIRST_MOTION_TIME_INTERVAL ms2us(7) /* random but good enough interval for very first event */
 
 struct custom_accel_function {
 	uint64_t last_time;
+	uint64_t last_delta_time;
 	double step;
 	size_t npoints;
 	double points[];
@@ -55,8 +56,10 @@ create_custom_accel_function(double step
 			return NULL;
 	}
 
-	struct custom_accel_function *cf = zalloc(sizeof(*cf) + npoints * sizeof(*points));
+	struct custom_accel_function *cf =
+		zalloc(sizeof(*cf) + npoints * sizeof(*points));
 	cf->last_time = 0;
+	cf->last_delta_time = FIRST_MOTION_TIME_INTERVAL;
 	cf->step = step;
 	cf->npoints = npoints;
 	memcpy(cf->points, points, sizeof(*points) * npoints);
@@ -109,20 +112,31 @@ custom_accel_function_calculate_speed(st
 
 	/* calculate speed based on time passed since last event */
 	double distance = hypot(unaccelerated->x, unaccelerated->y);
+	/* delta_time can be zero when:
+	 * - the fallback acceleration function is used for multiple movement types
+	 *   (for example, pointer motion and wheel scrolling simultaneously)
+	 * - two different methods produce the same movement at the same time
+	 *   (for example, button-scrolling and wheel-scrolling)
+	 *
+	 * Reusing the last delta_time is a graceful fallback even if there are
+	 * duplicate events or event-ordering bugs.
+	 */
+	uint64_t delta_time =
+		(time > cf->last_time) ? time - cf->last_time : cf->last_delta_time;
 	/* handle first event in a motion */
-	if (time - cf->last_time > MOTION_TIMEOUT)
-		cf->last_time = time - FIRST_MOTION_TIME_INTERVAL;
+	if (delta_time > MOTION_TIMEOUT)
+		delta_time = FIRST_MOTION_TIME_INTERVAL;
 
-	double dt = us2ms_f(time - cf->last_time);
-	double speed = distance / dt; /* speed is in device-units per ms */
+	/* speed is in device-units per ms */
+	double speed = distance / us2ms_f(delta_time);
 	cf->last_time = time;
+	cf->last_delta_time = delta_time;
 
 	return speed;
 }
 
 static double
-custom_accel_function_profile(struct custom_accel_function *cf,
-			      double speed_in)
+custom_accel_function_profile(struct custom_accel_function *cf, double speed_in)
 {
 	size_t npoints = cf->npoints;
 	double step = cf->step;
@@ -249,9 +263,7 @@ custom_accelerator_filter(enum libinput_
 }
 
 static void
-custom_accelerator_restart(struct motion_filter *filter,
-			   void *data,
-			   uint64_t time)
+custom_accelerator_restart(struct motion_filter *filter, void *data, uint64_t time)
 {
 	/* noop, this function has no effect in the custom interface */
 }
@@ -259,8 +271,7 @@ custom_accelerator_restart(struct motion
 static void
 custom_accelerator_destroy(struct motion_filter *filter)
 {
-	struct custom_accelerator *f =
-		(struct custom_accelerator *)filter;
+	struct custom_accelerator *f = (struct custom_accelerator *)filter;
 
 	/* destroy all custom movement functions */
 	custom_accel_function_destroy(f->funcs.fallback);
@@ -270,8 +281,7 @@ custom_accelerator_destroy(struct motion
 }
 
 static bool
-custom_accelerator_set_speed(struct motion_filter *filter,
-			     double speed_adjustment)
+custom_accelerator_set_speed(struct motion_filter *filter, double speed_adjustment)
 {
 	assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0);
 
@@ -284,17 +294,15 @@ static bool
 custom_accelerator_set_accel_config(struct motion_filter *filter,
 				    struct libinput_config_accel *config)
 {
-	struct custom_accelerator *f =
-		(struct custom_accelerator *)filter;
+	struct custom_accelerator *f = (struct custom_accelerator *)filter;
 
-	struct custom_accel_function *fallback = NULL,
-				     *motion = NULL,
-				     *scroll = NULL;
+	struct custom_accel_function *fallback = NULL, *motion = NULL, *scroll = NULL;
 
 	if (config->custom.fallback) {
-		fallback = create_custom_accel_function(config->custom.fallback->step,
-							config->custom.fallback->points,
-							config->custom.fallback->npoints);
+		fallback =
+			create_custom_accel_function(config->custom.fallback->step,
+						     config->custom.fallback->points,
+						     config->custom.fallback->npoints);
 		if (!fallback)
 			goto out;
 	}
@@ -364,9 +372,7 @@ custom_accel_profile_motion(struct motio
 			    double speed_in,
 			    uint64_t time)
 {
-	return custom_accelerator_profile(LIBINPUT_ACCEL_TYPE_MOTION,
-					  filter,
-					  speed_in);
+	return custom_accelerator_profile(LIBINPUT_ACCEL_TYPE_MOTION, filter, speed_in);
 }
 
 static struct normalized_coords
@@ -387,16 +393,15 @@ custom_accel_profile_scroll(struct motio
 			    double speed_in,
 			    uint64_t time)
 {
-	return custom_accelerator_profile(LIBINPUT_ACCEL_TYPE_SCROLL,
-					  filter,
-					  speed_in);
+	return custom_accelerator_profile(LIBINPUT_ACCEL_TYPE_SCROLL, filter, speed_in);
 }
 
 static struct normalized_coords
 custom_accelerator_filter_scroll(struct motion_filter *filter,
 				 const struct device_float_coords *unaccelerated,
 				 void *data,
-				 uint64_t time)
+				 uint64_t time,
+				 enum filter_scroll_type type)
 {
 	return custom_accelerator_filter(LIBINPUT_ACCEL_TYPE_SCROLL,
 					 filter,
@@ -423,7 +428,7 @@ create_custom_accelerator_filter(void)
 	/* the unit function by default, speed in = speed out,
 	   i.e. no acceleration */
 	const double default_step = 1.0;
-	const double default_points[2] = {0.0, 1.0};
+	const double default_points[2] = { 0.0, 1.0 };
 
 	/* initialize default acceleration, used as fallback */
 	f->funcs.fallback = create_custom_accel_function(default_step,
diff -pruN 1.28.1-1/src/filter-flat.c 1.30.0-1/src/filter-flat.c
--- 1.28.1-1/src/filter-flat.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/filter-flat.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,13 +26,13 @@
 #include "config.h"
 
 #include <assert.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdint.h>
 
+#include "filter-private.h"
 #include "filter.h"
 #include "libinput-util.h"
-#include "filter-private.h"
 
 struct pointer_accelerator_flat {
 	struct motion_filter base;
@@ -44,7 +44,8 @@ struct pointer_accelerator_flat {
 static struct normalized_coords
 accelerator_filter_flat(struct motion_filter *filter,
 			const struct device_float_coords *unaccelerated,
-			void *data, uint64_t time)
+			void *data,
+			uint64_t time)
 {
 	struct pointer_accelerator_flat *accel_filter =
 		(struct pointer_accelerator_flat *)filter;
@@ -61,9 +62,10 @@ accelerator_filter_flat(struct motion_fi
 }
 
 static struct normalized_coords
-accelerator_filter_noop_flat(struct motion_filter *filter,
-			     const struct device_float_coords *unaccelerated,
-			     void *data, uint64_t time)
+accelerator_filter_constant_flat(struct motion_filter *filter,
+				 const struct device_float_coords *unaccelerated,
+				 void *data,
+				 uint64_t time)
 {
 	/* We map the unaccelerated flat filter to have the same behavior as
 	 * the "accelerated" flat filter.
@@ -79,9 +81,29 @@ accelerator_filter_noop_flat(struct moti
 	return accelerator_filter_flat(filter, unaccelerated, data, time);
 }
 
+static struct normalized_coords
+accelerator_filter_scroll_flat(struct motion_filter *filter,
+			       const struct device_float_coords *unaccelerated,
+			       void *data,
+			       uint64_t time,
+			       enum filter_scroll_type type)
+{
+	/* Scroll wheels were not historically accelerated and have different
+	 * units than button scrolling. Maintain the status quo and do not
+	 * accelerate wheel events.
+	 */
+	if (type == FILTER_SCROLL_TYPE_WHEEL) {
+		return (struct normalized_coords){
+			.x = unaccelerated->x,
+			.y = unaccelerated->y,
+		};
+	}
+
+	return accelerator_filter_constant_flat(filter, unaccelerated, data, time);
+}
+
 static bool
-accelerator_set_speed_flat(struct motion_filter *filter,
-			   double speed_adjustment)
+accelerator_set_speed_flat(struct motion_filter *filter, double speed_adjustment)
 {
 	struct pointer_accelerator_flat *accel_filter =
 		(struct pointer_accelerator_flat *)filter;
@@ -103,7 +125,7 @@ static void
 accelerator_destroy_flat(struct motion_filter *filter)
 {
 	struct pointer_accelerator_flat *accel =
-		(struct pointer_accelerator_flat *) filter;
+		(struct pointer_accelerator_flat *)filter;
 
 	free(accel);
 }
@@ -111,8 +133,8 @@ accelerator_destroy_flat(struct motion_f
 static const struct motion_filter_interface accelerator_interface_flat = {
 	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT,
 	.filter = accelerator_filter_flat,
-	.filter_constant = accelerator_filter_noop_flat,
-	.filter_scroll = accelerator_filter_noop_flat,
+	.filter_constant = accelerator_filter_constant_flat,
+	.filter_scroll = accelerator_filter_scroll_flat,
 	.restart = NULL,
 	.destroy = accelerator_destroy_flat,
 	.set_speed = accelerator_set_speed_flat,
diff -pruN 1.28.1-1/src/filter-low-dpi.c 1.30.0-1/src/filter-low-dpi.c
--- 1.28.1-1/src/filter-low-dpi.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/filter-low-dpi.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,13 +26,13 @@
 #include "config.h"
 
 #include <assert.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdint.h>
 
+#include "filter-private.h"
 #include "filter.h"
 #include "libinput-util.h"
-#include "filter-private.h"
 
 /*
  * Default parameters for pointer acceleration profiles.
@@ -48,14 +48,14 @@ struct pointer_accelerator_low_dpi {
 
 	accel_profile_func_t profile;
 
-	double velocity;	/* units/us */
-	double last_velocity;	/* units/us */
+	double velocity;      /* units/us */
+	double last_velocity; /* units/us */
 
 	struct pointer_trackers trackers;
 
-	double threshold;	/* units/us */
-	double accel;		/* unitless factor */
-	double incline;		/* incline of the function */
+	double threshold; /* units/us */
+	double accel;     /* unitless factor */
+	double incline;   /* incline of the function */
 
 	int dpi;
 };
@@ -79,10 +79,10 @@ pointer_accel_profile_linear_low_dpi(str
 	struct pointer_accelerator_low_dpi *accel_filter =
 		(struct pointer_accelerator_low_dpi *)filter;
 
-	double max_accel = accel_filter->accel; /* unitless factor */
+	double max_accel = accel_filter->accel;     /* unitless factor */
 	double threshold = accel_filter->threshold; /* units/us */
 	const double incline = accel_filter->incline;
-	double dpi_factor = accel_filter->dpi/(double)DEFAULT_MOUSE_DPI;
+	double dpi_factor = accel_filter->dpi / (double)DEFAULT_MOUSE_DPI;
 	double factor; /* unitless */
 
 	/* dpi_factor is always < 1.0, increase max_accel, reduce
@@ -128,16 +128,15 @@ calculate_acceleration_factor(struct poi
 static struct normalized_coords
 accelerator_filter_low_dpi(struct motion_filter *filter,
 			   const struct device_float_coords *unaccelerated,
-			   void *data, uint64_t time)
+			   void *data,
+			   uint64_t time)
 {
 	struct pointer_accelerator_low_dpi *accel =
-		(struct pointer_accelerator_low_dpi *) filter;
+		(struct pointer_accelerator_low_dpi *)filter;
 
 	/* Accelerate for device units and return device units */
-	double accel_factor = calculate_acceleration_factor(accel,
-							    unaccelerated,
-							    data,
-							    time);
+	double accel_factor =
+		calculate_acceleration_factor(accel, unaccelerated, data, time);
 	const struct normalized_coords normalized = {
 		.x = accel_factor * unaccelerated->x,
 		.y = accel_factor * unaccelerated->y,
@@ -146,9 +145,10 @@ accelerator_filter_low_dpi(struct motion
 }
 
 static struct normalized_coords
-accelerator_filter_noop(struct motion_filter *filter,
-			const struct device_float_coords *unaccelerated,
-			void *data, uint64_t time)
+accelerator_filter_constant(struct motion_filter *filter,
+			    const struct device_float_coords *unaccelerated,
+			    void *data,
+			    uint64_t time)
 {
 	const struct normalized_coords normalized = {
 		.x = unaccelerated->x,
@@ -157,13 +157,21 @@ accelerator_filter_noop(struct motion_fi
 	return normalized;
 }
 
+static struct normalized_coords
+accelerator_filter_scroll(struct motion_filter *filter,
+			  const struct device_float_coords *unaccelerated,
+			  void *data,
+			  uint64_t time,
+			  enum filter_scroll_type type)
+{
+	return accelerator_filter_constant(filter, unaccelerated, data, time);
+}
+
 static void
-accelerator_restart(struct motion_filter *filter,
-		    void *data,
-		    uint64_t time)
+accelerator_restart(struct motion_filter *filter, void *data, uint64_t time)
 {
 	struct pointer_accelerator_low_dpi *accel =
-		(struct pointer_accelerator_low_dpi *) filter;
+		(struct pointer_accelerator_low_dpi *)filter;
 
 	trackers_reset(&accel->trackers, time);
 }
@@ -172,15 +180,14 @@ static void
 accelerator_destroy(struct motion_filter *filter)
 {
 	struct pointer_accelerator_low_dpi *accel =
-		(struct pointer_accelerator_low_dpi *) filter;
+		(struct pointer_accelerator_low_dpi *)filter;
 
 	trackers_free(&accel->trackers);
 	free(accel);
 }
 
 static bool
-accelerator_set_speed(struct motion_filter *filter,
-		      double speed_adjustment)
+accelerator_set_speed(struct motion_filter *filter, double speed_adjustment)
 {
 	struct pointer_accelerator_low_dpi *accel_filter =
 		(struct pointer_accelerator_low_dpi *)filter;
@@ -191,8 +198,7 @@ accelerator_set_speed(struct motion_filt
 	   don't read more into them other than "they mostly worked ok" */
 
 	/* delay when accel kicks in */
-	accel_filter->threshold = DEFAULT_THRESHOLD -
-					v_ms2us(0.25) * speed_adjustment;
+	accel_filter->threshold = DEFAULT_THRESHOLD - v_ms2us(0.25) * speed_adjustment;
 	if (accel_filter->threshold < MINIMUM_THRESHOLD)
 		accel_filter->threshold = MINIMUM_THRESHOLD;
 
@@ -209,8 +215,8 @@ accelerator_set_speed(struct motion_filt
 static const struct motion_filter_interface accelerator_interface_low_dpi = {
 	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
 	.filter = accelerator_filter_low_dpi,
-	.filter_constant = accelerator_filter_noop,
-	.filter_scroll = accelerator_filter_noop,
+	.filter_constant = accelerator_filter_constant,
+	.filter_scroll = accelerator_filter_scroll,
 	.restart = accelerator_restart,
 	.destroy = accelerator_destroy,
 	.set_speed = accelerator_set_speed,
diff -pruN 1.28.1-1/src/filter-mouse.c 1.30.0-1/src/filter-mouse.c
--- 1.28.1-1/src/filter-mouse.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/filter-mouse.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,13 +26,13 @@
 #include "config.h"
 
 #include <assert.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdint.h>
 
+#include "filter-private.h"
 #include "filter.h"
 #include "libinput-util.h"
-#include "filter-private.h"
 
 /*
  * Default parameters for pointer acceleration profiles.
@@ -48,14 +48,14 @@ struct pointer_accelerator {
 
 	accel_profile_func_t profile;
 
-	double velocity;	/* units/us */
-	double last_velocity;	/* units/us */
+	double velocity;      /* units/us */
+	double last_velocity; /* units/us */
 
 	struct pointer_trackers trackers;
 
-	double threshold;	/* 1000dpi units/us */
-	double accel;		/* unitless factor */
-	double incline;		/* incline of the function */
+	double threshold; /* 1000dpi units/us */
+	double accel;     /* unitless factor */
+	double incline;   /* incline of the function */
 
 	int dpi;
 };
@@ -88,12 +88,13 @@ calculate_acceleration_factor(struct poi
 	trackers_feed(&accel->trackers, &unaccel, time);
 	velocity = trackers_velocity(&accel->trackers, time);
 	/* This will call into our pointer_accel_profile_linear() profile func */
-	accel_factor = calculate_acceleration_simpsons(&accel->base,
-						       accel->profile,
-						       data,
-						       velocity, /* normalized coords */
-						       accel->last_velocity, /* normalized coords */
-						       time);
+	accel_factor = calculate_acceleration_simpsons(
+		&accel->base,
+		accel->profile,
+		data,
+		velocity,             /* normalized coords */
+		accel->last_velocity, /* normalized coords */
+		time);
 	accel->last_velocity = velocity;
 
 	return accel_factor;
@@ -102,18 +103,16 @@ calculate_acceleration_factor(struct poi
 static struct normalized_coords
 accelerator_filter_linear(struct motion_filter *filter,
 			  const struct device_float_coords *unaccelerated,
-			  void *data, uint64_t time)
+			  void *data,
+			  uint64_t time)
 {
-	struct pointer_accelerator *accel =
-		(struct pointer_accelerator *) filter;
+	struct pointer_accelerator *accel = (struct pointer_accelerator *)filter;
 
 	/* Accelerate for normalized units and return normalized units */
-	const struct normalized_coords normalized = normalize_for_dpi(unaccelerated,
-								      accel->dpi);
-	double accel_factor = calculate_acceleration_factor(accel,
-							    &normalized,
-							    data,
-							    time);
+	const struct normalized_coords normalized =
+		normalize_for_dpi(unaccelerated, accel->dpi);
+	double accel_factor =
+		calculate_acceleration_factor(accel, &normalized, data, time);
 	struct normalized_coords accelerated = {
 		.x = normalized.x * accel_factor,
 		.y = normalized.y * accel_factor,
@@ -134,23 +133,41 @@ accelerator_filter_linear(struct motion_
  * motion
  */
 static struct normalized_coords
-accelerator_filter_noop(struct motion_filter *filter,
-			const struct device_float_coords *unaccelerated,
-			void *data, uint64_t time)
+accelerator_filter_constant(struct motion_filter *filter,
+			    const struct device_float_coords *unaccelerated,
+			    void *data,
+			    uint64_t time)
 {
-	struct pointer_accelerator *accel =
-		(struct pointer_accelerator *) filter;
+	struct pointer_accelerator *accel = (struct pointer_accelerator *)filter;
 
 	return normalize_for_dpi(unaccelerated, accel->dpi);
 }
 
+static struct normalized_coords
+accelerator_filter_scroll(struct motion_filter *filter,
+			  const struct device_float_coords *unaccelerated,
+			  void *data,
+			  uint64_t time,
+			  enum filter_scroll_type type)
+{
+	/* Scroll wheels were not historically accelerated and have different
+	 * units than button scrolling. Maintain the status quo and do not
+	 * accelerate wheel events.
+	 */
+	if (type == FILTER_SCROLL_TYPE_WHEEL) {
+		return (struct normalized_coords){
+			.x = unaccelerated->x,
+			.y = unaccelerated->y,
+		};
+	}
+
+	return accelerator_filter_constant(filter, unaccelerated, data, time);
+}
+
 static void
-accelerator_restart(struct motion_filter *filter,
-		    void *data,
-		    uint64_t time)
+accelerator_restart(struct motion_filter *filter, void *data, uint64_t time)
 {
-	struct pointer_accelerator *accel =
-		(struct pointer_accelerator *) filter;
+	struct pointer_accelerator *accel = (struct pointer_accelerator *)filter;
 
 	trackers_reset(&accel->trackers, time);
 }
@@ -158,19 +175,16 @@ accelerator_restart(struct motion_filter
 static void
 accelerator_destroy(struct motion_filter *filter)
 {
-	struct pointer_accelerator *accel =
-		(struct pointer_accelerator *) filter;
+	struct pointer_accelerator *accel = (struct pointer_accelerator *)filter;
 
 	trackers_free(&accel->trackers);
 	free(accel);
 }
 
 static bool
-accelerator_set_speed(struct motion_filter *filter,
-		      double speed_adjustment)
+accelerator_set_speed(struct motion_filter *filter, double speed_adjustment)
 {
-	struct pointer_accelerator *accel_filter =
-		(struct pointer_accelerator *)filter;
+	struct pointer_accelerator *accel_filter = (struct pointer_accelerator *)filter;
 
 	assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0);
 
@@ -178,8 +192,7 @@ accelerator_set_speed(struct motion_filt
 	   don't read more into them other than "they mostly worked ok" */
 
 	/* delay when accel kicks in */
-	accel_filter->threshold = DEFAULT_THRESHOLD -
-					v_ms2us(0.25) * speed_adjustment;
+	accel_filter->threshold = DEFAULT_THRESHOLD - v_ms2us(0.25) * speed_adjustment;
 	if (accel_filter->threshold < MINIMUM_THRESHOLD)
 		accel_filter->threshold = MINIMUM_THRESHOLD;
 
@@ -199,9 +212,8 @@ pointer_accel_profile_linear(struct moti
 			     double speed_in, /* in normalized units */
 			     uint64_t time)
 {
-	struct pointer_accelerator *accel_filter =
-		(struct pointer_accelerator *)filter;
-	const double max_accel = accel_filter->accel; /* unitless factor */
+	struct pointer_accelerator *accel_filter = (struct pointer_accelerator *)filter;
+	const double max_accel = accel_filter->accel;     /* unitless factor */
 	const double threshold = accel_filter->threshold; /* 1000dpi units/us */
 	const double incline = accel_filter->incline;
 	double factor; /* unitless */
@@ -223,7 +235,7 @@ pointer_accel_profile_linear(struct moti
 	   The two inclines are linear functions in the form
 		   y = ax + b
 		   where y is speed_out
-		         x is speed_in
+			 x is speed_in
 			 a is the incline of acceleration
 			 b is minimum acceleration factor
 
@@ -242,21 +254,21 @@ pointer_accel_profile_linear(struct moti
 	*/
 	if (v_us2ms(speed_in) < 0.07) {
 		factor = 10 * v_us2ms(speed_in) + 0.3;
-	/* up to the threshold, we keep factor 1, i.e. 1:1 movement */
+		/* up to the threshold, we keep factor 1, i.e. 1:1 movement */
 	} else if (speed_in < threshold) {
 		factor = 1;
 
 	} else {
-	/* Acceleration function above the threshold:
-		y = ax' + b
-		where T is threshold
-		      x is speed_in
-		      x' is speed
-	        and
-			y(T) == 1
-		hence 1 = ax' + 1
-			=> x' := (x - T)
-	 */
+		/* Acceleration function above the threshold:
+			y = ax' + b
+			where T is threshold
+			      x is speed_in
+			      x' is speed
+			and
+				y(T) == 1
+			hence 1 = ax' + 1
+				=> x' := (x - T)
+		 */
 		factor = incline * v_us2ms(speed_in - threshold) + 1;
 	}
 
@@ -269,8 +281,8 @@ pointer_accel_profile_linear(struct moti
 static const struct motion_filter_interface accelerator_interface = {
 	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
 	.filter = accelerator_filter_linear,
-	.filter_constant = accelerator_filter_noop,
-	.filter_scroll = accelerator_filter_noop,
+	.filter_constant = accelerator_filter_constant,
+	.filter_scroll = accelerator_filter_scroll,
 	.restart = accelerator_restart,
 	.destroy = accelerator_destroy,
 	.set_speed = accelerator_set_speed,
diff -pruN 1.28.1-1/src/filter-private.h 1.30.0-1/src/filter-private.h
--- 1.28.1-1/src/filter-private.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/filter-private.h	2025-11-25 03:40:43.000000000 +0000
@@ -31,23 +31,24 @@
 struct motion_filter_interface {
 	enum libinput_config_accel_profile type;
 	struct normalized_coords (*filter)(
-			   struct motion_filter *filter,
-			   const struct device_float_coords *unaccelerated,
-			   void *data, uint64_t time);
+		struct motion_filter *filter,
+		const struct device_float_coords *unaccelerated,
+		void *data,
+		uint64_t time);
 	struct normalized_coords (*filter_constant)(
-			   struct motion_filter *filter,
-			   const struct device_float_coords *unaccelerated,
-			   void *data, uint64_t time);
+		struct motion_filter *filter,
+		const struct device_float_coords *unaccelerated,
+		void *data,
+		uint64_t time);
 	struct normalized_coords (*filter_scroll)(
-			   struct motion_filter *filter,
-			   const struct device_float_coords *unaccelerated,
-			   void *data, uint64_t time);
-	void (*restart)(struct motion_filter *filter,
-			void *data,
-			uint64_t time);
+		struct motion_filter *filter,
+		const struct device_float_coords *unaccelerated,
+		void *data,
+		uint64_t time,
+		enum filter_scroll_type type);
+	void (*restart)(struct motion_filter *filter, void *data, uint64_t time);
 	void (*destroy)(struct motion_filter *filter);
-	bool (*set_speed)(struct motion_filter *filter,
-			  double speed_adjustment);
+	bool (*set_speed)(struct motion_filter *filter, double speed_adjustment);
 	bool (*set_accel_config)(struct motion_filter *filter,
 				 struct libinput_config_accel *accel_config);
 };
@@ -59,7 +60,7 @@ struct motion_filter {
 
 struct pointer_tracker {
 	struct device_float_coords delta; /* delta to most recent event */
-	uint64_t time;  /* us */
+	uint64_t time;                    /* us */
 	uint32_t dir;
 };
 
@@ -93,12 +94,13 @@ struct pointer_trackers {
 	struct pointer_delta_smoothener *smoothener;
 };
 
-void trackers_init(struct pointer_trackers *trackers, int ntrackers);
-void trackers_free(struct pointer_trackers *trackers);
+void
+trackers_init(struct pointer_trackers *trackers, int ntrackers);
+void
+trackers_free(struct pointer_trackers *trackers);
 
 void
-trackers_reset(struct pointer_trackers *trackers,
-	       uint64_t time);
+trackers_reset(struct pointer_trackers *trackers, uint64_t time);
 void
 trackers_feed(struct pointer_trackers *trackers,
 	      const struct device_float_coords *delta,
@@ -135,7 +137,7 @@ v_us2s(double units_per_us)
 static inline double
 v_ms2us(double units_per_ms)
 {
-	return units_per_ms/1000.0;
+	return units_per_ms / 1000.0;
 }
 
 static inline struct normalized_coords
@@ -143,8 +145,8 @@ normalize_for_dpi(const struct device_fl
 {
 	struct normalized_coords norm;
 
-	norm.x = coords->x * DEFAULT_MOUSE_DPI/dpi;
-	norm.y = coords->y * DEFAULT_MOUSE_DPI/dpi;
+	norm.x = coords->x * DEFAULT_MOUSE_DPI / dpi;
+	norm.y = coords->y * DEFAULT_MOUSE_DPI / dpi;
 
 	return norm;
 }
diff -pruN 1.28.1-1/src/filter-tablet.c 1.30.0-1/src/filter-tablet.c
--- 1.28.1-1/src/filter-tablet.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/filter-tablet.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,21 +26,21 @@
 #include "config.h"
 
 #include <assert.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdint.h>
 
+#include "filter-private.h"
 #include "filter.h"
 #include "libinput-util.h"
-#include "filter-private.h"
 
 struct tablet_accelerator_flat {
 	struct motion_filter base;
 
 	double factor;
 	int xres, yres;
-	double xres_scale, /* 1000dpi : tablet res */
-	       yres_scale; /* 1000dpi : tablet res */
+	double xres_scale,  /* 1000dpi : tablet res */
+		yres_scale; /* 1000dpi : tablet res */
 };
 
 static inline struct normalized_coords
@@ -81,25 +81,26 @@ tablet_accelerator_filter_flat_pen(struc
 	 * is almost identical to the tablet mapped to screen in absolute
 	 * mode. Tested on a Intuos5, other tablets may vary.
 	 */
-       const double DPI_CONVERSION = 96.0/25.4 * 2.5; /* unitless factor */
-       struct normalized_coords mm;
+	const double DPI_CONVERSION = 96.0 / 25.4 * 2.5; /* unitless factor */
+	struct normalized_coords mm;
 
-       mm.x = 1.0 * units->x/filter->xres;
-       mm.y = 1.0 * units->y/filter->yres;
-       accelerated.x = mm.x * filter->factor * DPI_CONVERSION;
-       accelerated.y = mm.y * filter->factor * DPI_CONVERSION;
+	mm.x = 1.0 * units->x / filter->xres;
+	mm.y = 1.0 * units->y / filter->yres;
+	accelerated.x = mm.x * filter->factor * DPI_CONVERSION;
+	accelerated.y = mm.y * filter->factor * DPI_CONVERSION;
 
-       return accelerated;
+	return accelerated;
 }
 
 static struct normalized_coords
 tablet_accelerator_filter_flat(struct motion_filter *filter,
 			       const struct device_float_coords *units,
-			       void *data, uint64_t time)
+			       void *data,
+			       uint64_t time)
 {
 	struct tablet_accelerator_flat *accel_filter =
 		(struct tablet_accelerator_flat *)filter;
-	struct libinput_tablet_tool *tool = (struct libinput_tablet_tool*)data;
+	struct libinput_tablet_tool *tool = (struct libinput_tablet_tool *)data;
 	enum libinput_tablet_tool_type type;
 	struct normalized_coords accel;
 
@@ -108,12 +109,10 @@ tablet_accelerator_filter_flat(struct mo
 	switch (type) {
 	case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
 	case LIBINPUT_TABLET_TOOL_TYPE_LENS:
-		accel = tablet_accelerator_filter_flat_mouse(accel_filter,
-							     units);
+		accel = tablet_accelerator_filter_flat_mouse(accel_filter, units);
 		break;
 	default:
-		accel = tablet_accelerator_filter_flat_pen(accel_filter,
-							   units);
+		accel = tablet_accelerator_filter_flat_pen(accel_filter, units);
 		break;
 	}
 
@@ -121,8 +120,7 @@ tablet_accelerator_filter_flat(struct mo
 }
 
 static bool
-tablet_accelerator_set_speed(struct motion_filter *filter,
-			     double speed_adjustment)
+tablet_accelerator_set_speed(struct motion_filter *filter, double speed_adjustment)
 {
 	struct tablet_accelerator_flat *accel_filter =
 		(struct tablet_accelerator_flat *)filter;
@@ -162,8 +160,8 @@ create_tablet_filter_flat(int xres, int
 	filter->factor = 1.0;
 	filter->xres = xres;
 	filter->yres = yres;
-	filter->xres_scale = DEFAULT_MOUSE_DPI/(25.4 * xres);
-	filter->yres_scale = DEFAULT_MOUSE_DPI/(25.4 * yres);
+	filter->xres_scale = DEFAULT_MOUSE_DPI / (25.4 * xres);
+	filter->yres_scale = DEFAULT_MOUSE_DPI / (25.4 * yres);
 
 	return filter;
 }
diff -pruN 1.28.1-1/src/filter-touchpad-flat.c 1.30.0-1/src/filter-touchpad-flat.c
--- 1.28.1-1/src/filter-touchpad-flat.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/filter-touchpad-flat.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,13 +26,13 @@
 #include "config.h"
 
 #include <assert.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdint.h>
 
+#include "filter-private.h"
 #include "filter.h"
 #include "libinput-util.h"
-#include "filter-private.h"
 
 #define TP_MAGIC_SLOWDOWN_FLAT 0.2968
 
@@ -46,7 +46,8 @@ struct touchpad_accelerator_flat {
 static struct normalized_coords
 accelerator_filter_touchpad_flat(struct motion_filter *filter,
 				 const struct device_float_coords *unaccelerated,
-				 void *data, uint64_t time)
+				 void *data,
+				 uint64_t time)
 {
 	struct touchpad_accelerator_flat *accel =
 		(struct touchpad_accelerator_flat *)filter;
@@ -64,9 +65,11 @@ accelerator_filter_touchpad_flat(struct
 }
 
 static struct normalized_coords
-accelerator_filter_noop_touchpad_flat(struct motion_filter *filter,
-				      const struct device_float_coords *unaccelerated,
-				      void *data, uint64_t time)
+accelerator_filter_constant_touchpad_flat(
+	struct motion_filter *filter,
+	const struct device_float_coords *unaccelerated,
+	void *data,
+	uint64_t time)
 {
 	/* We map the unaccelerated flat filter to have the same behavior as
 	 * the "accelerated" flat filter.
@@ -82,6 +85,30 @@ accelerator_filter_noop_touchpad_flat(st
 	return accelerator_filter_touchpad_flat(filter, unaccelerated, data, time);
 }
 
+static struct normalized_coords
+accelerator_filter_scroll_touchpad_flat(struct motion_filter *filter,
+					const struct device_float_coords *unaccelerated,
+					void *data,
+					uint64_t time,
+					enum filter_scroll_type type)
+{
+	/* Scroll wheels were not historically accelerated and have different
+	 * units than button scrolling. Maintain the status quo and do not
+	 * accelerate wheel events.
+	 */
+	if (type == FILTER_SCROLL_TYPE_WHEEL) {
+		return (struct normalized_coords){
+			.x = unaccelerated->x,
+			.y = unaccelerated->y,
+		};
+	}
+
+	return accelerator_filter_constant_touchpad_flat(filter,
+							 unaccelerated,
+							 data,
+							 time);
+}
+
 static bool
 accelerator_set_speed_touchpad_flat(struct motion_filter *filter,
 				    double speed_adjustment)
@@ -101,7 +128,7 @@ static void
 accelerator_destroy_touchpad_flat(struct motion_filter *filter)
 {
 	struct touchpad_accelerator_flat *accel =
-		(struct touchpad_accelerator_flat *) filter;
+		(struct touchpad_accelerator_flat *)filter;
 
 	free(accel);
 }
@@ -109,8 +136,8 @@ accelerator_destroy_touchpad_flat(struct
 static const struct motion_filter_interface accelerator_interface_touchpad_flat = {
 	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT,
 	.filter = accelerator_filter_touchpad_flat,
-	.filter_constant = accelerator_filter_noop_touchpad_flat,
-	.filter_scroll = accelerator_filter_noop_touchpad_flat,
+	.filter_constant = accelerator_filter_constant_touchpad_flat,
+	.filter_scroll = accelerator_filter_scroll_touchpad_flat,
 	.restart = NULL,
 	.destroy = accelerator_destroy_touchpad_flat,
 	.set_speed = accelerator_set_speed_touchpad_flat,
diff -pruN 1.28.1-1/src/filter-touchpad-x230.c 1.30.0-1/src/filter-touchpad-x230.c
--- 1.28.1-1/src/filter-touchpad-x230.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/filter-touchpad-x230.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,12 +26,12 @@
 #include "config.h"
 
 #include <assert.h>
-#include <stdlib.h>
 #include <stdint.h>
+#include <stdlib.h>
 
+#include "filter-private.h"
 #include "filter.h"
 #include "libinput-util.h"
-#include "filter-private.h"
 
 /* Trackpoint acceleration for the Lenovo x230. DO NOT TOUCH.
  * This code is only invoked on the X230 and is quite flimsy,
@@ -62,14 +62,14 @@ struct pointer_accelerator_x230 {
 
 	accel_profile_func_t profile;
 
-	double velocity;	/* units/us */
-	double last_velocity;	/* units/us */
+	double velocity;      /* units/us */
+	double last_velocity; /* units/us */
 
 	struct pointer_trackers trackers;
 
-	double threshold;	/* units/us */
-	double accel;		/* unitless factor */
-	double incline;		/* incline of the function */
+	double threshold; /* units/us */
+	double accel;     /* unitless factor */
+	double incline;   /* incline of the function */
 
 	int dpi;
 };
@@ -86,7 +86,9 @@ struct pointer_accelerator_x230 {
  */
 static double
 acceleration_profile(struct pointer_accelerator_x230 *accel,
-		     void *data, double velocity, uint64_t time)
+		     void *data,
+		     double velocity,
+		     uint64_t time)
 {
 	return accel->profile(&accel->base, data, velocity, time);
 }
@@ -116,10 +118,9 @@ calculate_acceleration(struct pointer_ac
 	 * the previous motion and the most recent. */
 	factor = acceleration_profile(accel, data, velocity, time);
 	factor += acceleration_profile(accel, data, last_velocity, time);
-	factor += 4.0 *
-		acceleration_profile(accel, data,
-				     (last_velocity + velocity) / 2,
-				     time);
+	factor +=
+		4.0 *
+		acceleration_profile(accel, data, (last_velocity + velocity) / 2, time);
 
 	factor = factor / 6.0;
 
@@ -129,10 +130,11 @@ calculate_acceleration(struct pointer_ac
 static struct normalized_coords
 accelerator_filter_x230(struct motion_filter *filter,
 			const struct device_float_coords *raw,
-			void *data, uint64_t time)
+			void *data,
+			uint64_t time)
 {
 	struct pointer_accelerator_x230 *accel =
-		(struct pointer_accelerator_x230 *) filter;
+		(struct pointer_accelerator_x230 *)filter;
 	double accel_factor; /* unitless factor */
 	struct normalized_coords accelerated;
 	struct device_float_coords delta_normalized;
@@ -168,13 +170,13 @@ accelerator_filter_x230(struct motion_fi
 static struct normalized_coords
 accelerator_filter_constant_x230(struct motion_filter *filter,
 				 const struct device_float_coords *unaccelerated,
-				 void *data, uint64_t time)
+				 void *data,
+				 uint64_t time)
 {
 	struct pointer_accelerator_x230 *accel =
-		(struct pointer_accelerator_x230 *) filter;
+		(struct pointer_accelerator_x230 *)filter;
 	struct normalized_coords normalized;
-	const double factor =
-		X230_MAGIC_SLOWDOWN/X230_TP_MAGIC_LOW_RES_FACTOR;
+	const double factor = X230_MAGIC_SLOWDOWN / X230_TP_MAGIC_LOW_RES_FACTOR;
 
 	normalized = normalize_for_dpi(unaccelerated, accel->dpi);
 	normalized.x = factor * normalized.x;
@@ -183,13 +185,32 @@ accelerator_filter_constant_x230(struct
 	return normalized;
 }
 
+static struct normalized_coords
+accelerator_filter_scroll_x230(struct motion_filter *filter,
+			       const struct device_float_coords *unaccelerated,
+			       void *data,
+			       uint64_t time,
+			       enum filter_scroll_type type)
+{
+	/* Scroll wheels were not historically accelerated and have different
+	 * units than button scrolling. Maintain the status quo and do not
+	 * accelerate wheel events.
+	 */
+	if (type == FILTER_SCROLL_TYPE_WHEEL) {
+		return (struct normalized_coords){
+			.x = unaccelerated->x,
+			.y = unaccelerated->y,
+		};
+	}
+
+	return accelerator_filter_constant_x230(filter, unaccelerated, data, time);
+}
+
 static void
-accelerator_restart_x230(struct motion_filter *filter,
-			 void *data,
-			 uint64_t time)
+accelerator_restart_x230(struct motion_filter *filter, void *data, uint64_t time)
 {
 	struct pointer_accelerator_x230 *accel =
-		(struct pointer_accelerator_x230 *) filter;
+		(struct pointer_accelerator_x230 *)filter;
 	unsigned int offset;
 	struct pointer_tracker *tracker;
 
@@ -210,15 +231,14 @@ static void
 accelerator_destroy_x230(struct motion_filter *filter)
 {
 	struct pointer_accelerator_x230 *accel =
-		(struct pointer_accelerator_x230 *) filter;
+		(struct pointer_accelerator_x230 *)filter;
 
 	free(accel->trackers.trackers);
 	free(accel);
 }
 
 static bool
-accelerator_set_speed_x230(struct motion_filter *filter,
-			   double speed_adjustment)
+accelerator_set_speed_x230(struct motion_filter *filter, double speed_adjustment)
 {
 	struct pointer_accelerator_x230 *accel_filter =
 		(struct pointer_accelerator_x230 *)filter;
@@ -229,8 +249,7 @@ accelerator_set_speed_x230(struct motion
 	   don't read more into them other than "they mostly worked ok" */
 
 	/* delay when accel kicks in */
-	accel_filter->threshold = DEFAULT_THRESHOLD -
-					v_ms2us(0.25) * speed_adjustment;
+	accel_filter->threshold = DEFAULT_THRESHOLD - v_ms2us(0.25) * speed_adjustment;
 	if (accel_filter->threshold < MINIMUM_THRESHOLD)
 		accel_filter->threshold = MINIMUM_THRESHOLD;
 
@@ -246,9 +265,9 @@ accelerator_set_speed_x230(struct motion
 
 double
 touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
-				      void *data,
-				      double speed_in, /* 1000dpi-units/µs */
-				      uint64_t time)
+				   void *data,
+				   double speed_in, /* 1000dpi-units/µs */
+				   uint64_t time)
 {
 	/* Those touchpads presents an actual lower resolution that what is
 	 * advertised. We see some jumps from the cursor due to the big steps
@@ -262,9 +281,9 @@ touchpad_lenovo_x230_accel_profile(struc
 
 	double f1, f2; /* unitless */
 	const double max_accel = accel_filter->accel *
-				  X230_TP_MAGIC_LOW_RES_FACTOR; /* unitless factor */
-	const double threshold = accel_filter->threshold /
-				  X230_TP_MAGIC_LOW_RES_FACTOR; /* units/us */
+				 X230_TP_MAGIC_LOW_RES_FACTOR; /* unitless factor */
+	const double threshold =
+		accel_filter->threshold / X230_TP_MAGIC_LOW_RES_FACTOR; /* units/us */
 	const double incline = accel_filter->incline * X230_TP_MAGIC_LOW_RES_FACTOR;
 
 	/* Note: the magic values in this function are obtained by
@@ -287,7 +306,7 @@ static const struct motion_filter_interf
 	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
 	.filter = accelerator_filter_x230,
 	.filter_constant = accelerator_filter_constant_x230,
-	.filter_scroll = accelerator_filter_constant_x230,
+	.filter_scroll = accelerator_filter_scroll_x230,
 	.restart = accelerator_restart_x230,
 	.destroy = accelerator_destroy_x230,
 	.set_speed = accelerator_set_speed_x230,
@@ -311,7 +330,7 @@ create_pointer_accelerator_filter_lenovo
 
 	filter->threshold = X230_THRESHOLD;
 	filter->accel = X230_ACCELERATION; /* unitless factor */
-	filter->incline = X230_INCLINE; /* incline of the acceleration function */
+	filter->incline = X230_INCLINE;    /* incline of the acceleration function */
 	filter->dpi = dpi;
 
 	return &filter->base;
diff -pruN 1.28.1-1/src/filter-touchpad.c 1.30.0-1/src/filter-touchpad.c
--- 1.28.1-1/src/filter-touchpad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/filter-touchpad.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,13 +26,13 @@
 #include "config.h"
 
 #include <assert.h>
-#include <stdlib.h>
-#include <stdint.h>
 #include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
 
+#include "filter-private.h"
 #include "filter.h"
 #include "libinput-util.h"
-#include "filter-private.h"
 
 /* Once normalized, touchpads see the same acceleration as mice. that is
  * technically correct but subjectively wrong, we expect a touchpad to be a
@@ -45,17 +45,17 @@ struct touchpad_accelerator {
 
 	accel_profile_func_t profile;
 
-	double velocity;	/* units/us */
-	double last_velocity;	/* units/us */
+	double velocity;      /* units/us */
+	double last_velocity; /* units/us */
 
 	struct pointer_trackers trackers;
 
-	double threshold;	/* mm/s */
-	double accel;		/* unitless factor */
+	double threshold; /* mm/s */
+	double accel;     /* unitless factor */
 
 	int dpi;
 
-	double speed_factor;    /* factor based on speed setting */
+	double speed_factor; /* factor based on speed setting */
 };
 
 /**
@@ -93,17 +93,15 @@ calculate_acceleration_factor(struct tou
 static struct normalized_coords
 accelerator_filter_touchpad(struct motion_filter *filter,
 			    const struct device_float_coords *unaccelerated,
-			    void *data, uint64_t time)
+			    void *data,
+			    uint64_t time)
 {
-	struct touchpad_accelerator *accel =
-		(struct touchpad_accelerator *) filter;
+	struct touchpad_accelerator *accel = (struct touchpad_accelerator *)filter;
 
 	/* Accelerate for device units, normalize afterwards */
-	double accel_factor = calculate_acceleration_factor(accel,
-							    unaccelerated,
-							    data,
-							    time);
-	const struct device_float_coords accelerated =  {
+	double accel_factor =
+		calculate_acceleration_factor(accel, unaccelerated, data, time);
+	const struct device_float_coords accelerated = {
 		.x = unaccelerated->x * accel_factor,
 		.y = unaccelerated->y * accel_factor,
 	};
@@ -126,8 +124,7 @@ speed_factor(double s)
 }
 
 static bool
-touchpad_accelerator_set_speed(struct motion_filter *filter,
-		      double speed_adjustment)
+touchpad_accelerator_set_speed(struct motion_filter *filter, double speed_adjustment)
 {
 	struct touchpad_accelerator *accel_filter =
 		(struct touchpad_accelerator *)filter;
@@ -143,10 +140,10 @@ touchpad_accelerator_set_speed(struct mo
 static struct normalized_coords
 touchpad_constant_filter(struct motion_filter *filter,
 			 const struct device_float_coords *unaccelerated,
-			 void *data, uint64_t time)
+			 void *data,
+			 uint64_t time)
 {
-	struct touchpad_accelerator *accel =
-		(struct touchpad_accelerator *)filter;
+	struct touchpad_accelerator *accel = (struct touchpad_accelerator *)filter;
 	struct normalized_coords normalized;
 	/* We need to use the same baseline here as the accelerated code,
 	 * otherwise our unaccelerated speed is different to the accelerated
@@ -165,13 +162,31 @@ touchpad_constant_filter(struct motion_f
 	return normalized;
 }
 
+static struct normalized_coords
+touchpad_scroll_filter(struct motion_filter *filter,
+		       const struct device_float_coords *unaccelerated,
+		       void *data,
+		       uint64_t time,
+		       enum filter_scroll_type type)
+{
+	/* Scroll wheels were not historically accelerated and have different
+	 * units than button scrolling. Maintain the status quo and do not
+	 * accelerate wheel events.
+	 */
+	if (type == FILTER_SCROLL_TYPE_WHEEL) {
+		return (struct normalized_coords){
+			.x = unaccelerated->x,
+			.y = unaccelerated->y,
+		};
+	}
+
+	return touchpad_constant_filter(filter, unaccelerated, data, time);
+}
+
 static void
-touchpad_accelerator_restart(struct motion_filter *filter,
-			     void *data,
-			     uint64_t time)
+touchpad_accelerator_restart(struct motion_filter *filter, void *data, uint64_t time)
 {
-	struct touchpad_accelerator *accel =
-		(struct touchpad_accelerator *) filter;
+	struct touchpad_accelerator *accel = (struct touchpad_accelerator *)filter;
 
 	trackers_reset(&accel->trackers, time);
 }
@@ -179,8 +194,7 @@ touchpad_accelerator_restart(struct moti
 static void
 touchpad_accelerator_destroy(struct motion_filter *filter)
 {
-	struct touchpad_accelerator *accel =
-		(struct touchpad_accelerator *) filter;
+	struct touchpad_accelerator *accel = (struct touchpad_accelerator *)filter;
 
 	trackers_free(&accel->trackers);
 	free(accel);
@@ -199,7 +213,7 @@ touchpad_accel_profile_linear(struct mot
 	double factor; /* unitless */
 
 	/* Convert to mm/s because that's something one can understand */
-	speed_in = v_us2s(speed_in) * 25.4/accel_filter->dpi;
+	speed_in = v_us2s(speed_in) * 25.4 / accel_filter->dpi;
 
 	/*
 	   Our acceleration function calculates a factor to accelerate input
@@ -220,7 +234,7 @@ touchpad_accel_profile_linear(struct mot
 	   The first incline is a linear function in the form
 		   y = ax + b
 		   where y is speed_out
-		         x is speed_in
+			 x is speed_in
 			 a is the incline of acceleration
 			 b is minimum acceleration factor
 	   for speeds up to the lower threshold, we decelerate, down to 30%
@@ -248,26 +262,27 @@ touchpad_accel_profile_linear(struct mot
 
 	if (speed_in < 7.0) {
 		factor = min(baseline, 0.1 * speed_in + 0.3);
-	/* up to the threshold, we keep factor 1, i.e. 1:1 movement */
+		/* up to the threshold, we keep factor 1, i.e. 1:1 movement */
 	} else if (speed_in < threshold) {
 		factor = baseline;
 	} else {
 
-	/* Acceleration function above the threshold is a curve up
-	   to four times the threshold, because why not.
+		/* Acceleration function above the threshold is a curve up
+		   to four times the threshold, because why not.
 
-	   Don't assume anything about the specific numbers though, this was
-	   all just trial and error by tweaking numbers here and there, then
-	   the formula was optimized doing basic maths.
+		   Don't assume anything about the specific numbers though, this was
+		   all just trial and error by tweaking numbers here and there, then
+		   the formula was optimized doing basic maths.
 
-	   You could replace this with some other random formula that gives
-	   the same numbers and it would be just as correct.
+		   You could replace this with some other random formula that gives
+		   the same numbers and it would be just as correct.
 
-	 */
+		 */
 		const double upper_threshold = threshold * 4.0;
 		speed_in = min(speed_in, upper_threshold);
 
-		factor = 0.0025 * (speed_in/threshold) * (speed_in - threshold) + baseline;
+		factor = 0.0025 * (speed_in / threshold) * (speed_in - threshold) +
+			 baseline;
 	}
 
 	factor *= accel_filter->speed_factor;
@@ -278,7 +293,7 @@ static const struct motion_filter_interf
 	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
 	.filter = accelerator_filter_touchpad,
 	.filter_constant = touchpad_constant_filter,
-	.filter_scroll = touchpad_constant_filter,
+	.filter_scroll = touchpad_scroll_filter,
 	.restart = touchpad_accelerator_restart,
 	.destroy = touchpad_accelerator_destroy,
 	.set_speed = touchpad_accelerator_set_speed,
@@ -286,9 +301,9 @@ static const struct motion_filter_interf
 
 struct motion_filter *
 create_pointer_accelerator_filter_touchpad(int dpi,
-	uint64_t event_delta_smooth_threshold,
-	uint64_t event_delta_smooth_value,
-	bool use_velocity_averaging)
+					   uint64_t event_delta_smooth_threshold,
+					   uint64_t event_delta_smooth_value,
+					   bool use_velocity_averaging)
 {
 	struct touchpad_accelerator *filter;
 
@@ -302,7 +317,9 @@ create_pointer_accelerator_filter_touchp
 
 	filter->base.interface = &accelerator_interface_touchpad;
 	filter->profile = touchpad_accel_profile_linear;
-	filter->trackers.smoothener = pointer_delta_smoothener_create(event_delta_smooth_threshold, event_delta_smooth_value);
+	filter->trackers.smoothener =
+		pointer_delta_smoothener_create(event_delta_smooth_threshold,
+						event_delta_smooth_value);
 
 	return &filter->base;
 }
diff -pruN 1.28.1-1/src/filter-trackpoint-flat.c 1.30.0-1/src/filter-trackpoint-flat.c
--- 1.28.1-1/src/filter-trackpoint-flat.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/filter-trackpoint-flat.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,13 +26,13 @@
 #include "config.h"
 
 #include <assert.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdint.h>
 
+#include "filter-private.h"
 #include "filter.h"
 #include "libinput-util.h"
-#include "filter-private.h"
 
 struct trackpoint_flat_accelerator {
 	struct motion_filter base;
@@ -44,10 +44,11 @@ struct trackpoint_flat_accelerator {
 static struct normalized_coords
 trackpoint_flat_filter(struct motion_filter *filter,
 		       const struct device_float_coords *unaccelerated,
-		       void *data, uint64_t time)
+		       void *data,
+		       uint64_t time)
 {
 	struct trackpoint_flat_accelerator *accel_filter =
-		(struct trackpoint_flat_accelerator *) filter;
+		(struct trackpoint_flat_accelerator *)filter;
 	struct normalized_coords accelerated;
 
 	double factor = accel_filter->speed_factor;
@@ -59,9 +60,10 @@ trackpoint_flat_filter(struct motion_fil
 }
 
 static struct normalized_coords
-trackpoint_flat_filter_noop(struct motion_filter *filter,
-			    const struct device_float_coords *unaccelerated,
-			    void *data, uint64_t time)
+trackpoint_flat_filter_constant(struct motion_filter *filter,
+				const struct device_float_coords *unaccelerated,
+				void *data,
+				uint64_t time)
 {
 	/* We map the unaccelerated flat filter to have the same behavior as
 	 * the "accelerated" flat filter.
@@ -77,6 +79,27 @@ trackpoint_flat_filter_noop(struct motio
 	return trackpoint_flat_filter(filter, unaccelerated, data, time);
 }
 
+static struct normalized_coords
+trackpoint_flat_filter_scroll(struct motion_filter *filter,
+			      const struct device_float_coords *unaccelerated,
+			      void *data,
+			      uint64_t time,
+			      enum filter_scroll_type type)
+{
+	/* Scroll wheels were not historically accelerated and have different
+	 * units than button scrolling. Maintain the status quo and do not
+	 * accelerate wheel events.
+	 */
+	if (type == FILTER_SCROLL_TYPE_WHEEL) {
+		return (struct normalized_coords){
+			.x = unaccelerated->x,
+			.y = unaccelerated->y,
+		};
+	}
+
+	return trackpoint_flat_filter_constant(filter, unaccelerated, data, time);
+}
+
 /* Maps the [-1, 1] speed setting into a constant acceleration
  * range. This isn't a linear scale, we keep 0 as the 'optimized'
  * mid-point and scale down to 0 for setting -1 and up to 5 for
@@ -97,16 +120,14 @@ static inline double
 speed_factor(double s)
 {
 	s += 1; /* map to [0, 2] */
-	return 435837.2 + (0.04762636 - 435837.2)/(1 + pow(s/240.4549,
-							   2.377168));
+	return 435837.2 + (0.04762636 - 435837.2) / (1 + pow(s / 240.4549, 2.377168));
 }
 
 static bool
-trackpoint_flat_set_speed(struct motion_filter *filter,
-			  double speed_adjustment)
+trackpoint_flat_set_speed(struct motion_filter *filter, double speed_adjustment)
 {
 	struct trackpoint_flat_accelerator *accel_filter =
-		(struct trackpoint_flat_accelerator *) filter;
+		(struct trackpoint_flat_accelerator *)filter;
 
 	assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0);
 
@@ -120,7 +141,7 @@ static void
 trackpoint_flat_destroy(struct motion_filter *filter)
 {
 	struct trackpoint_flat_accelerator *accel_filter =
-		(struct trackpoint_flat_accelerator *) filter;
+		(struct trackpoint_flat_accelerator *)filter;
 
 	free(accel_filter);
 }
@@ -128,8 +149,8 @@ trackpoint_flat_destroy(struct motion_fi
 static struct motion_filter_interface accelerator_interface_flat = {
 	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT,
 	.filter = trackpoint_flat_filter,
-	.filter_constant = trackpoint_flat_filter_noop,
-	.filter_scroll = trackpoint_flat_filter_noop,
+	.filter_constant = trackpoint_flat_filter_constant,
+	.filter_scroll = trackpoint_flat_filter_scroll,
 	.restart = NULL,
 	.destroy = trackpoint_flat_destroy,
 	.set_speed = trackpoint_flat_set_speed,
diff -pruN 1.28.1-1/src/filter-trackpoint.c 1.30.0-1/src/filter-trackpoint.c
--- 1.28.1-1/src/filter-trackpoint.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/filter-trackpoint.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,14 +26,14 @@
 #include "config.h"
 
 #include <assert.h>
+#include <math.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdint.h>
-#include <math.h>
 
+#include "filter-private.h"
 #include "filter.h"
 #include "libinput-util.h"
-#include "filter-private.h"
 
 struct trackpoint_accelerator {
 	struct motion_filter base;
@@ -64,7 +64,7 @@ trackpoint_accel_profile(struct motion_f
 	 * 0.4  3
 	 * 0.6  4
 	 */
-	factor = 10.06254 + (0.3 - 10.06254)/(1 + pow(velocity/0.9205459, 1.15363));
+	factor = 10.06254 + (0.3 - 10.06254) / (1 + pow(velocity / 0.9205459, 1.15363));
 
 	factor *= accel_filter->speed_factor;
 	return factor;
@@ -73,7 +73,8 @@ trackpoint_accel_profile(struct motion_f
 static struct normalized_coords
 trackpoint_accelerator_filter(struct motion_filter *filter,
 			      const struct device_float_coords *unaccelerated,
-			      void *data, uint64_t time)
+			      void *data,
+			      uint64_t time)
 {
 	struct trackpoint_accelerator *accel_filter =
 		(struct trackpoint_accelerator *)filter;
@@ -96,9 +97,10 @@ trackpoint_accelerator_filter(struct mot
 }
 
 static struct normalized_coords
-trackpoint_accelerator_filter_noop(struct motion_filter *filter,
-				   const struct device_float_coords *unaccelerated,
-				   void *data, uint64_t time)
+trackpoint_accelerator_filter_constant(struct motion_filter *filter,
+				       const struct device_float_coords *unaccelerated,
+				       void *data,
+				       uint64_t time)
 {
 	struct trackpoint_accelerator *accel_filter =
 		(struct trackpoint_accelerator *)filter;
@@ -110,6 +112,30 @@ trackpoint_accelerator_filter_noop(struc
 	return coords;
 }
 
+static struct normalized_coords
+trackpoint_accelerator_filter_scroll(struct motion_filter *filter,
+				     const struct device_float_coords *unaccelerated,
+				     void *data,
+				     uint64_t time,
+				     enum filter_scroll_type type)
+{
+	/* Scroll wheels were not historically accelerated and have different
+	 * units than button scrolling. Maintain the status quo and do not
+	 * accelerate wheel events.
+	 */
+	if (type == FILTER_SCROLL_TYPE_WHEEL) {
+		return (struct normalized_coords){
+			.x = unaccelerated->x,
+			.y = unaccelerated->y,
+		};
+	}
+
+	return trackpoint_accelerator_filter_constant(filter,
+						      unaccelerated,
+						      data,
+						      time);
+}
+
 /* Maps the [-1, 1] speed setting into a constant acceleration
  * range. This isn't a linear scale, we keep 0 as the 'optimized'
  * mid-point and scale down to 0 for setting -1 and up to 5 for
@@ -130,16 +156,14 @@ static inline double
 speed_factor(double s)
 {
 	s += 1; /* map to [0, 2] */
-	return 435837.2 + (0.04762636 - 435837.2)/(1 + pow(s/240.4549,
-							   2.377168));
+	return 435837.2 + (0.04762636 - 435837.2) / (1 + pow(s / 240.4549, 2.377168));
 }
 
 static bool
-trackpoint_accelerator_set_speed(struct motion_filter *filter,
-				 double speed_adjustment)
+trackpoint_accelerator_set_speed(struct motion_filter *filter, double speed_adjustment)
 {
 	struct trackpoint_accelerator *accel_filter =
-		(struct trackpoint_accelerator*)filter;
+		(struct trackpoint_accelerator *)filter;
 
 	assert(speed_adjustment >= -1.0 && speed_adjustment <= 1.0);
 
@@ -150,12 +174,9 @@ trackpoint_accelerator_set_speed(struct
 }
 
 static void
-trackpoint_accelerator_restart(struct motion_filter *filter,
-			       void *data,
-			       uint64_t time)
+trackpoint_accelerator_restart(struct motion_filter *filter, void *data, uint64_t time)
 {
-	struct trackpoint_accelerator *accel =
-		(struct trackpoint_accelerator *) filter;
+	struct trackpoint_accelerator *accel = (struct trackpoint_accelerator *)filter;
 
 	trackers_reset(&accel->trackers, time);
 }
@@ -173,15 +194,16 @@ trackpoint_accelerator_destroy(struct mo
 static const struct motion_filter_interface accelerator_interface_trackpoint = {
 	.type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
 	.filter = trackpoint_accelerator_filter,
-	.filter_constant = trackpoint_accelerator_filter_noop,
-	.filter_scroll = trackpoint_accelerator_filter_noop,
+	.filter_constant = trackpoint_accelerator_filter_constant,
+	.filter_scroll = trackpoint_accelerator_filter_scroll,
 	.restart = trackpoint_accelerator_restart,
 	.destroy = trackpoint_accelerator_destroy,
 	.set_speed = trackpoint_accelerator_set_speed,
 };
 
 struct motion_filter *
-create_pointer_accelerator_filter_trackpoint(double multiplier, bool use_velocity_averaging)
+create_pointer_accelerator_filter_trackpoint(double multiplier,
+					     bool use_velocity_averaging)
 {
 	struct trackpoint_accelerator *filter;
 
@@ -208,7 +230,8 @@ create_pointer_accelerator_filter_trackp
 	trackers_init(&filter->trackers, use_velocity_averaging ? 16 : 2);
 
 	filter->base.interface = &accelerator_interface_trackpoint;
-	filter->trackers.smoothener = pointer_delta_smoothener_create(ms2us(10), ms2us(10));
+	filter->trackers.smoothener =
+		pointer_delta_smoothener_create(ms2us(10), ms2us(10));
 
 	return &filter->base;
 }
diff -pruN 1.28.1-1/src/filter.c 1.30.0-1/src/filter.c
--- 1.28.1-1/src/filter.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/filter.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,21 +26,22 @@
 #include "config.h"
 
 #include <assert.h>
+#include <math.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdint.h>
-#include <math.h>
 
+#include "filter-private.h"
 #include "filter.h"
 #include "libinput-util.h"
-#include "filter-private.h"
 
 #define MOTION_TIMEOUT		ms2us(1000)
 
 struct normalized_coords
 filter_dispatch(struct motion_filter *filter,
 		const struct device_float_coords *unaccelerated,
-		void *data, uint64_t time)
+		void *data,
+		uint64_t time)
 {
 	return filter->interface->filter(filter, unaccelerated, data, time);
 }
@@ -48,7 +49,8 @@ filter_dispatch(struct motion_filter *fi
 struct normalized_coords
 filter_dispatch_constant(struct motion_filter *filter,
 			 const struct device_float_coords *unaccelerated,
-			 void *data, uint64_t time)
+			 void *data,
+			 uint64_t time)
 {
 	return filter->interface->filter_constant(filter, unaccelerated, data, time);
 }
@@ -56,14 +58,19 @@ filter_dispatch_constant(struct motion_f
 struct normalized_coords
 filter_dispatch_scroll(struct motion_filter *filter,
 		       const struct device_float_coords *unaccelerated,
-		       void *data, uint64_t time)
-{
-	return filter->interface->filter_scroll(filter, unaccelerated, data, time);
+		       void *data,
+		       uint64_t time,
+		       enum filter_scroll_type type)
+{
+	return filter->interface->filter_scroll(filter,
+						unaccelerated,
+						data,
+						time,
+						type);
 }
 
 void
-filter_restart(struct motion_filter *filter,
-	       void *data, uint64_t time)
+filter_restart(struct motion_filter *filter, void *data, uint64_t time)
 {
 	if (filter->interface->restart)
 		filter->interface->restart(filter, data, time);
@@ -79,8 +86,7 @@ filter_destroy(struct motion_filter *fil
 }
 
 bool
-filter_set_speed(struct motion_filter *filter,
-		 double speed_adjustment)
+filter_set_speed(struct motion_filter *filter, double speed_adjustment)
 {
 	return filter->interface->set_speed(filter, speed_adjustment);
 }
@@ -112,8 +118,7 @@ filter_set_accel_config(struct motion_fi
 void
 trackers_init(struct pointer_trackers *trackers, int ntrackers)
 {
-	trackers->trackers = zalloc(ntrackers *
-				    sizeof(*trackers->trackers));
+	trackers->trackers = zalloc(ntrackers * sizeof(*trackers->trackers));
 	trackers->ntrackers = ntrackers;
 	trackers->cur_tracker = 0;
 	trackers->smoothener = NULL;
@@ -127,8 +132,7 @@ trackers_free(struct pointer_trackers *t
 }
 
 void
-trackers_reset(struct pointer_trackers *trackers,
-	       uint64_t time)
+trackers_reset(struct pointer_trackers *trackers, uint64_t time)
 {
 	unsigned int offset;
 	struct pointer_tracker *tracker;
@@ -173,9 +177,8 @@ trackers_feed(struct pointer_trackers *t
 struct pointer_tracker *
 trackers_by_offset(struct pointer_trackers *trackers, unsigned int offset)
 {
-	unsigned int index =
-		(trackers->cur_tracker + trackers->ntrackers - offset)
-		% trackers->ntrackers;
+	unsigned int index = (trackers->cur_tracker + trackers->ntrackers - offset) %
+			     trackers->ntrackers;
 	return &trackers->trackers[index];
 }
 
@@ -232,7 +235,8 @@ trackers_velocity(struct pointer_tracker
 	/* Find least recent vector within a timelimit, maximum velocity diff
 	 * and direction threshold. */
 	for (unsigned int offset = 1; offset < trackers->ntrackers; offset++) {
-		const struct pointer_tracker *tracker = trackers_by_offset(trackers, offset);
+		const struct pointer_tracker *tracker =
+			trackers_by_offset(trackers, offset);
 
 		/* Bug: time running backwards */
 		if (tracker->time > time)
@@ -242,8 +246,8 @@ trackers_velocity(struct pointer_tracker
 		if (time - tracker->time > MOTION_TIMEOUT) {
 			if (offset == 1)
 				result = trackers_velocity_after_timeout(
-							  tracker,
-							  trackers->smoothener);
+					tracker,
+					trackers->smoothener);
 			break;
 		}
 
@@ -306,9 +310,7 @@ calculate_acceleration_simpsons(struct m
 	 * the previous motion and the most recent. */
 	factor = profile(filter, data, velocity, time);
 	factor += profile(filter, data, last_velocity, time);
-	factor += 4.0 * profile(filter, data,
-				(last_velocity + velocity) / 2,
-				time);
+	factor += 4.0 * profile(filter, data, (last_velocity + velocity) / 2, time);
 
 	factor = factor / 6.0;
 
diff -pruN 1.28.1-1/src/filter.h 1.30.0-1/src/filter.h
--- 1.28.1-1/src/filter.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/filter.h	2025-11-25 03:40:43.000000000 +0000
@@ -34,6 +34,12 @@
 
 struct motion_filter;
 
+enum filter_scroll_type {
+	FILTER_SCROLL_TYPE_CONTINUOUS,
+	FILTER_SCROLL_TYPE_WHEEL,
+	FILTER_SCROLL_TYPE_FINGER,
+};
+
 /**
  * Accelerate the given coordinates.
  * Takes a set of unaccelerated deltas and accelerates them based on the
@@ -59,7 +65,8 @@ struct motion_filter;
 struct normalized_coords
 filter_dispatch(struct motion_filter *filter,
 		const struct device_float_coords *unaccelerated,
-		void *data, uint64_t time);
+		void *data,
+		uint64_t time);
 
 /**
  * Apply constant motion filters, but no acceleration.
@@ -80,7 +87,8 @@ filter_dispatch(struct motion_filter *fi
 struct normalized_coords
 filter_dispatch_constant(struct motion_filter *filter,
 			 const struct device_float_coords *unaccelerated,
-			 void *data, uint64_t time);
+			 void *data,
+			 uint64_t time);
 
 /**
  * Apply a scroll filter.
@@ -96,24 +104,25 @@ filter_dispatch_constant(struct motion_f
  * originally provided resolution.
  * @param data Custom data
  * @param time The time of the delta
+ * @param type The type of scroll event
  *
  * @see filter_dispatch
  */
 struct normalized_coords
 filter_dispatch_scroll(struct motion_filter *filter,
 		       const struct device_float_coords *unaccelerated,
-		       void *data, uint64_t time);
+		       void *data,
+		       uint64_t time,
+		       enum filter_scroll_type type);
 
 void
-filter_restart(struct motion_filter *filter,
-	       void *data, uint64_t time);
+filter_restart(struct motion_filter *filter, void *data, uint64_t time);
 
 void
 filter_destroy(struct motion_filter *filter);
 
 bool
-filter_set_speed(struct motion_filter *filter,
-		 double speed);
+filter_set_speed(struct motion_filter *filter, double speed);
 double
 filter_get_speed(struct motion_filter *filter);
 
@@ -127,7 +136,7 @@ typedef double (*accel_profile_func_t)(s
 
 bool
 filter_set_accel_config(struct motion_filter *filter,
-		        struct libinput_config_accel *accel_config);
+			struct libinput_config_accel *accel_config);
 
 /* Pointer acceleration types */
 struct motion_filter *
@@ -141,9 +150,9 @@ create_pointer_accelerator_filter_linear
 
 struct motion_filter *
 create_pointer_accelerator_filter_touchpad(int dpi,
-	uint64_t event_delta_smooth_threshold,
-	uint64_t event_delta_smooth_value,
-	bool use_velocity_averaging);
+					   uint64_t event_delta_smooth_threshold,
+					   uint64_t event_delta_smooth_value,
+					   bool use_velocity_averaging);
 
 struct motion_filter *
 create_pointer_accelerator_filter_touchpad_flat(int dpi);
@@ -152,7 +161,8 @@ struct motion_filter *
 create_pointer_accelerator_filter_lenovo_x230(int dpi, bool use_velocity_averaging);
 
 struct motion_filter *
-create_pointer_accelerator_filter_trackpoint(double multiplier, bool use_velocity_averaging);
+create_pointer_accelerator_filter_trackpoint(double multiplier,
+					     bool use_velocity_averaging);
 
 struct motion_filter *
 create_pointer_accelerator_filter_trackpoint_flat(double multiplier);
@@ -184,9 +194,9 @@ touchpad_accel_profile_linear(struct mot
 			      uint64_t time);
 double
 touchpad_lenovo_x230_accel_profile(struct motion_filter *filter,
-				      void *data,
-				      double speed_in,
-				      uint64_t time);
+				   void *data,
+				   double speed_in,
+				   uint64_t time);
 double
 trackpoint_accel_profile(struct motion_filter *filter,
 			 void *data,
diff -pruN 1.28.1-1/src/libinput-feature.h 1.30.0-1/src/libinput-feature.h
--- 1.28.1-1/src/libinput-feature.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-feature.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * 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.
+ */
+
+#pragma once
+
+enum libinput_feature {
+	LIBINPUT_FEATURE_BUTTON_DEBOUNCING = 1,
+	LIBINPUT_FEATURE_WHEEL_DEBOUNCING,
+	LIBINPUT_FEATURE_TOUCHPAD_JUMP_DETECTION,
+	LIBINPUT_FEATURE_TOUCHPAD_HYSTERESIS,
+	LIBINPUT_FEATURE_TOUCHPAD_PALM_DETECTION,
+
+	_LIBINPUT_N_FEATURES
+};
diff -pruN 1.28.1-1/src/libinput-log.h 1.30.0-1/src/libinput-log.h
--- 1.28.1-1/src/libinput-log.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-log.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2013 Jonas Ådahl
+ * Copyright © 2013-2015 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:
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include <stdbool.h>
+
+#include "util-ratelimit.h"
+
+#include "libinput.h"
+
+#define log_debug(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_DEBUG, __VA_ARGS__)
+#define log_info(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_INFO, __VA_ARGS__)
+#define log_error(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, __VA_ARGS__)
+#define log_bug_kernel(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, "kernel bug: " __VA_ARGS__)
+#define log_bug_libinput(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, "libinput bug: " __VA_ARGS__)
+#define log_bug_client(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, "client bug: " __VA_ARGS__)
+
+#define log_debug_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_DEBUG, __VA_ARGS__)
+#define log_info_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_INFO, __VA_ARGS__)
+#define log_error_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_ERROR, __VA_ARGS__)
+#define log_bug_kernel_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_ERROR, "kernel bug: " __VA_ARGS__)
+#define log_bug_libinput_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_ERROR, "libinput bug: " __VA_ARGS__)
+#define log_bug_client_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_ERROR, "client bug: " __VA_ARGS__)
+
+bool
+log_is_logged(const struct libinput *libinput, enum libinput_log_priority priority);
+
+void
+log_msg_ratelimit(struct libinput *libinput,
+		  struct ratelimit *ratelimit,
+		  enum libinput_log_priority priority,
+		  const char *format,
+		  ...) LIBINPUT_ATTRIBUTE_PRINTF(4, 5);
+
+void
+log_msg(struct libinput *libinput,
+	enum libinput_log_priority priority,
+	const char *format,
+	...) LIBINPUT_ATTRIBUTE_PRINTF(3, 4);
+
+void
+log_msg_va(struct libinput *libinput,
+	   enum libinput_log_priority priority,
+	   const char *format,
+	   va_list args) LIBINPUT_ATTRIBUTE_PRINTF(3, 0);
diff -pruN 1.28.1-1/src/libinput-plugin-button-debounce.c 1.30.0-1/src/libinput-plugin-button-debounce.c
--- 1.28.1-1/src/libinput-plugin-button-debounce.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-button-debounce.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,863 @@
+/*
+ * Copyright © 2017-2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <libevdev/libevdev.h>
+
+#include "util-mem.h"
+#include "util-strings.h"
+
+#include "evdev-frame.h"
+#include "libinput-feature.h"
+#include "libinput-log.h"
+#include "libinput-plugin-button-debounce.h"
+#include "libinput-plugin.h"
+#include "libinput-private.h"
+#include "libinput-util.h"
+#include "quirks.h"
+#include "timer.h"
+
+/* Debounce cases to handle
+     P ... button press
+     R ... button release
+     ---|  timeout duration
+
+     'normal' .... event sent when it happens
+     'filtered' .. event is not sent (but may be sent later)
+     'delayed' ... event is sent with wall-clock delay
+
+   1) P---| R		P normal, R normal
+   2) R---| P		R normal, P normal
+   3) P---R--| P	P normal, R filtered, delayed, P normal
+   4) R---P--| R	R normal, P filtered, delayed, R normal
+   4.1) P---| R--P--|	P normal, R filtered
+   5) P--R-P-| R	P normal, R filtered, P filtered, R normal
+   6) R--P-R-| P	R normal, P filtered, R filtered, P normal
+   7) P--R--|
+	  ---P-|	P normal, R filtered, P filtered
+   8) R--P--|
+	  ---R-|	R normal, P filtered, R filtered
+
+   1, 2 are the normal click cases without debouncing taking effect
+   3, 4 are fast clicks where the second event is delivered with a delay
+   5, 6 are contact bounces, fast
+   7, 8 are contact bounces, slow
+
+   4.1 is a special case with the same event sequence as 4 but we want to
+   filter the *release* event out, it's a button losing contact while being
+   held down.
+
+   7 and 8 are cases where the first event happens within the first timeout
+   but the second event is outside that timeout (but within the timeout of
+   the second event). These cases are handled by restarting the timer on every
+   event that could be part of a bouncing sequence, which makes these cases
+   indistinguishable from 5 and 6.
+*/
+
+enum debounce_event {
+	DEBOUNCE_EVENT_PRESS = 50,
+	DEBOUNCE_EVENT_RELEASE,
+	DEBOUNCE_EVENT_TIMEOUT,
+	DEBOUNCE_EVENT_TIMEOUT_SHORT,
+	DEBOUNCE_EVENT_OTHERBUTTON,
+};
+
+enum debounce_state {
+	DEBOUNCE_STATE_IS_UP = 100,
+	DEBOUNCE_STATE_IS_DOWN,
+	DEBOUNCE_STATE_IS_DOWN_WAITING,
+	DEBOUNCE_STATE_IS_UP_DELAYING,
+	DEBOUNCE_STATE_IS_UP_DELAYING_SPURIOUS,
+	DEBOUNCE_STATE_IS_UP_DETECTING_SPURIOUS,
+	DEBOUNCE_STATE_IS_DOWN_DETECTING_SPURIOUS,
+	DEBOUNCE_STATE_IS_UP_WAITING,
+	DEBOUNCE_STATE_IS_DOWN_DELAYING,
+
+	DEBOUNCE_STATE_DISABLED = 999,
+};
+
+static inline const char *
+debounce_state_to_str(enum debounce_state state)
+{
+	switch (state) {
+	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_UP);
+	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_DOWN);
+	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_DOWN_WAITING);
+	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_UP_DELAYING);
+	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_UP_DELAYING_SPURIOUS);
+	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_UP_DETECTING_SPURIOUS);
+	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_DOWN_DETECTING_SPURIOUS);
+	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_UP_WAITING);
+	CASE_RETURN_STRING(DEBOUNCE_STATE_IS_DOWN_DELAYING);
+	CASE_RETURN_STRING(DEBOUNCE_STATE_DISABLED);
+	}
+
+	return NULL;
+}
+
+static inline const char *
+debounce_event_to_str(enum debounce_event event)
+{
+	switch (event) {
+	CASE_RETURN_STRING(DEBOUNCE_EVENT_PRESS);
+	CASE_RETURN_STRING(DEBOUNCE_EVENT_RELEASE);
+	CASE_RETURN_STRING(DEBOUNCE_EVENT_TIMEOUT);
+	CASE_RETURN_STRING(DEBOUNCE_EVENT_TIMEOUT_SHORT);
+	CASE_RETURN_STRING(DEBOUNCE_EVENT_OTHERBUTTON);
+	}
+	return NULL;
+}
+
+struct plugin_device {
+	struct list link;
+	struct libinput_device *device;
+	struct plugin_data *parent;
+
+	evdev_usage_t button_usage;
+	uint64_t button_time;
+	enum debounce_state state;
+	bool spurious_enabled;
+
+	bool want_feature_disabled;
+
+	struct libinput_plugin_timer *timer;
+	struct libinput_plugin_timer *timer_short;
+};
+
+static void
+plugin_device_destroy(void *d)
+{
+	struct plugin_device *device = d;
+
+	list_remove(&device->link);
+	libinput_plugin_timer_cancel(device->timer);
+	libinput_plugin_timer_unref(device->timer);
+	libinput_plugin_timer_cancel(device->timer_short);
+	libinput_plugin_timer_unref(device->timer_short);
+	libinput_device_unref(device->device);
+
+	free(device);
+}
+
+struct plugin_data {
+	struct list devices;
+	struct libinput_plugin *plugin;
+};
+
+static void
+plugin_data_destroy(void *d)
+{
+	struct plugin_data *data = d;
+
+	struct plugin_device *device;
+	list_for_each_safe(device, &data->devices, link) {
+		plugin_device_destroy(device);
+	}
+
+	free(data);
+}
+
+static void
+plugin_destroy(struct libinput_plugin *libinput_plugin)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	plugin_data_destroy(plugin);
+}
+
+static inline void
+log_debounce_bug(struct plugin_device *device, enum debounce_event event)
+{
+	plugin_log_bug_libinput(device->parent->plugin,
+				"invalid debounce event %s in state %s\n",
+				debounce_event_to_str(event),
+				debounce_state_to_str(device->state));
+}
+
+static inline void
+debounce_maybe_disable(struct plugin_device *device)
+{
+	if (device->state != DEBOUNCE_STATE_IS_UP)
+		return;
+
+	if (device->want_feature_disabled) {
+		plugin_log_debug(device->parent->plugin,
+				 "%s: disabled button debouncing on request\n",
+				 libinput_device_get_name(device->device));
+		device->state = DEBOUNCE_STATE_DISABLED;
+		libinput_plugin_enable_device_event_frame(device->parent->plugin,
+							  device->device,
+							  false);
+	}
+}
+
+static inline void
+debounce_set_state(struct plugin_device *device, enum debounce_state new_state)
+{
+	assert(new_state >= DEBOUNCE_STATE_IS_UP &&
+	       new_state <= DEBOUNCE_STATE_IS_DOWN_DELAYING);
+
+	device->state = new_state;
+}
+
+static inline void
+debounce_set_timer(struct plugin_device *device, uint64_t time)
+{
+	const int DEBOUNCE_TIMEOUT_BOUNCE = ms2us(25);
+
+	libinput_plugin_timer_set(device->timer, time + DEBOUNCE_TIMEOUT_BOUNCE);
+}
+
+static inline void
+debounce_set_timer_short(struct plugin_device *device, uint64_t time)
+{
+	const int DEBOUNCE_TIMEOUT_SPURIOUS = ms2us(12);
+
+	libinput_plugin_timer_set(device->timer_short,
+				  time + DEBOUNCE_TIMEOUT_SPURIOUS);
+}
+
+static inline void
+debounce_cancel_timer(struct plugin_device *device)
+{
+	libinput_plugin_timer_cancel(device->timer);
+}
+
+static inline void
+debounce_cancel_timer_short(struct plugin_device *device)
+{
+	libinput_plugin_timer_cancel(device->timer_short);
+}
+
+static inline void
+debounce_enable_spurious(struct plugin_device *device)
+{
+	if (device->spurious_enabled)
+		plugin_log_bug(device->parent->plugin,
+			       "tried to enable spurious debouncing twice\n");
+
+	device->spurious_enabled = true;
+	plugin_log_info(device->parent->plugin,
+			"%s: enabling spurious button debouncing, "
+			"see %s/button-debouncing.html for details\n",
+			libinput_device_get_name(device->device),
+			HTTP_DOC_LINK);
+}
+
+static void
+debounce_notify_button(struct plugin_device *device,
+		       struct evdev_frame *frame,
+		       enum libinput_button_state state)
+{
+	_unref_(evdev_frame) *button_frame = NULL;
+	if (frame == NULL) {
+		button_frame = evdev_frame_new(2);
+		frame = button_frame;
+	}
+
+	evdev_frame_append_one(frame,
+			       device->button_usage,
+			       state == LIBINPUT_BUTTON_STATE_PRESSED ? 1 : 0);
+	evdev_frame_set_time(frame, device->button_time);
+
+	libinput_plugin_prepend_evdev_frame(device->parent->plugin,
+					    device->device,
+					    frame);
+
+	/* If we used the original frame, reset it to avoid re-sending any
+	 * non-button events that may be present in this frame */
+	if (button_frame == NULL)
+		evdev_frame_reset(frame);
+}
+
+static void
+debounce_is_up_handle_event(struct plugin_device *device,
+			    enum debounce_event event,
+			    struct evdev_frame *frame,
+			    uint64_t time)
+{
+	switch (event) {
+	case DEBOUNCE_EVENT_PRESS:
+		device->button_time = time;
+		debounce_set_timer(device, time);
+		debounce_set_state(device, DEBOUNCE_STATE_IS_DOWN_WAITING);
+		debounce_notify_button(device, frame, LIBINPUT_BUTTON_STATE_PRESSED);
+		break;
+	case DEBOUNCE_EVENT_RELEASE:
+	case DEBOUNCE_EVENT_TIMEOUT:
+	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
+		log_debounce_bug(device, event);
+		break;
+	case DEBOUNCE_EVENT_OTHERBUTTON:
+		break;
+	}
+}
+
+static void
+debounce_is_down_handle_event(struct plugin_device *device,
+			      enum debounce_event event,
+			      struct evdev_frame *frame,
+			      uint64_t time)
+{
+	switch (event) {
+	case DEBOUNCE_EVENT_PRESS:
+		/* If we lost the kernel button release event (e.g. something
+		 * grabbed the device for a short while) we quietly ignore
+		 * the next down event */
+		break;
+	case DEBOUNCE_EVENT_RELEASE:
+		device->button_time = time;
+		debounce_set_timer(device, time);
+		debounce_set_timer_short(device, time);
+		if (device->spurious_enabled) {
+			debounce_set_state(device,
+					   DEBOUNCE_STATE_IS_UP_DELAYING_SPURIOUS);
+		} else {
+			debounce_set_state(device,
+					   DEBOUNCE_STATE_IS_UP_DETECTING_SPURIOUS);
+			debounce_notify_button(device,
+					       frame,
+					       LIBINPUT_BUTTON_STATE_RELEASED);
+		}
+		break;
+	case DEBOUNCE_EVENT_TIMEOUT:
+	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
+		log_debounce_bug(device, event);
+		break;
+	case DEBOUNCE_EVENT_OTHERBUTTON:
+		break;
+	}
+}
+
+static void
+debounce_is_down_waiting_handle_event(struct plugin_device *device,
+				      enum debounce_event event,
+				      struct evdev_frame *frame,
+				      uint64_t time)
+{
+	switch (event) {
+	case DEBOUNCE_EVENT_PRESS:
+		log_debounce_bug(device, event);
+		break;
+	case DEBOUNCE_EVENT_RELEASE:
+		debounce_set_timer(device, time);
+		debounce_set_state(device, DEBOUNCE_STATE_IS_UP_DELAYING);
+		/* Note: In the debouncing RPR case, we use the last
+		 * release's time stamp */
+		device->button_time = time;
+		break;
+	case DEBOUNCE_EVENT_TIMEOUT:
+		debounce_set_state(device, DEBOUNCE_STATE_IS_DOWN);
+		break;
+	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
+		log_debounce_bug(device, event);
+		break;
+	case DEBOUNCE_EVENT_OTHERBUTTON:
+		debounce_set_state(device, DEBOUNCE_STATE_IS_DOWN);
+		break;
+	}
+}
+
+static void
+debounce_is_up_delaying_handle_event(struct plugin_device *device,
+				     enum debounce_event event,
+				     struct evdev_frame *frame,
+				     uint64_t time)
+{
+	switch (event) {
+	case DEBOUNCE_EVENT_PRESS:
+		debounce_set_timer(device, time);
+		debounce_set_state(device, DEBOUNCE_STATE_IS_DOWN_WAITING);
+		break;
+	case DEBOUNCE_EVENT_RELEASE:
+	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
+		log_debounce_bug(device, event);
+		break;
+	case DEBOUNCE_EVENT_TIMEOUT:
+	case DEBOUNCE_EVENT_OTHERBUTTON:
+		debounce_set_state(device, DEBOUNCE_STATE_IS_UP);
+		debounce_maybe_disable(device);
+		debounce_notify_button(device, frame, LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	}
+}
+
+static void
+debounce_is_up_delaying_spurious_handle_event(struct plugin_device *device,
+					      enum debounce_event event,
+					      struct evdev_frame *frame,
+					      uint64_t time)
+{
+	switch (event) {
+	case DEBOUNCE_EVENT_PRESS:
+		debounce_set_state(device, DEBOUNCE_STATE_IS_DOWN);
+		debounce_cancel_timer(device);
+		debounce_cancel_timer_short(device);
+		break;
+	case DEBOUNCE_EVENT_RELEASE:
+	case DEBOUNCE_EVENT_TIMEOUT:
+		log_debounce_bug(device, event);
+		break;
+	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
+		debounce_set_state(device, DEBOUNCE_STATE_IS_UP_WAITING);
+		debounce_notify_button(device, frame, LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	case DEBOUNCE_EVENT_OTHERBUTTON:
+		debounce_set_state(device, DEBOUNCE_STATE_IS_UP);
+		debounce_maybe_disable(device);
+		debounce_notify_button(device, frame, LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	}
+}
+
+static void
+debounce_is_up_detecting_spurious_handle_event(struct plugin_device *device,
+					       enum debounce_event event,
+					       struct evdev_frame *frame,
+					       uint64_t time)
+{
+	switch (event) {
+	case DEBOUNCE_EVENT_PRESS:
+		debounce_set_timer(device, time);
+		debounce_set_timer_short(device, time);
+		/* Note: in a bouncing PRP case, we use the last press
+		 * event time */
+		device->button_time = time;
+		debounce_set_state(device, DEBOUNCE_STATE_IS_DOWN_DETECTING_SPURIOUS);
+		break;
+	case DEBOUNCE_EVENT_RELEASE:
+		log_debounce_bug(device, event);
+		break;
+	case DEBOUNCE_EVENT_TIMEOUT:
+		debounce_set_state(device, DEBOUNCE_STATE_IS_UP);
+		debounce_maybe_disable(device);
+		break;
+	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
+		debounce_set_state(device, DEBOUNCE_STATE_IS_UP_WAITING);
+		break;
+	case DEBOUNCE_EVENT_OTHERBUTTON:
+		debounce_set_state(device, DEBOUNCE_STATE_IS_UP);
+		debounce_maybe_disable(device);
+		break;
+	}
+}
+
+static void
+debounce_is_down_detecting_spurious_handle_event(struct plugin_device *device,
+						 enum debounce_event event,
+						 struct evdev_frame *frame,
+						 uint64_t time)
+{
+	switch (event) {
+	case DEBOUNCE_EVENT_PRESS:
+		log_debounce_bug(device, event);
+		break;
+	case DEBOUNCE_EVENT_RELEASE:
+		debounce_set_timer(device, time);
+		debounce_set_timer_short(device, time);
+		debounce_set_state(device, DEBOUNCE_STATE_IS_UP_DETECTING_SPURIOUS);
+		break;
+	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
+		debounce_cancel_timer(device);
+		debounce_set_state(device, DEBOUNCE_STATE_IS_DOWN);
+		debounce_enable_spurious(device);
+		debounce_notify_button(device, frame, LIBINPUT_BUTTON_STATE_PRESSED);
+		break;
+	case DEBOUNCE_EVENT_TIMEOUT:
+	case DEBOUNCE_EVENT_OTHERBUTTON:
+		debounce_set_state(device, DEBOUNCE_STATE_IS_DOWN);
+		debounce_notify_button(device, frame, LIBINPUT_BUTTON_STATE_PRESSED);
+		break;
+	}
+}
+
+static void
+debounce_is_up_waiting_handle_event(struct plugin_device *device,
+				    enum debounce_event event,
+				    struct evdev_frame *frame,
+				    uint64_t time)
+{
+	switch (event) {
+	case DEBOUNCE_EVENT_PRESS:
+		debounce_set_timer(device, time);
+		/* Note: in a debouncing PRP case, we use the last press'
+		 * time */
+		device->button_time = time;
+		debounce_set_state(device, DEBOUNCE_STATE_IS_DOWN_DELAYING);
+		break;
+	case DEBOUNCE_EVENT_RELEASE:
+	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
+		log_debounce_bug(device, event);
+		break;
+	case DEBOUNCE_EVENT_TIMEOUT:
+	case DEBOUNCE_EVENT_OTHERBUTTON:
+		debounce_set_state(device, DEBOUNCE_STATE_IS_UP);
+		debounce_maybe_disable(device);
+		break;
+	}
+}
+
+static void
+debounce_is_down_delaying_handle_event(struct plugin_device *device,
+				       enum debounce_event event,
+				       struct evdev_frame *frame,
+				       uint64_t time)
+{
+	switch (event) {
+	case DEBOUNCE_EVENT_PRESS:
+		log_debounce_bug(device, event);
+		break;
+	case DEBOUNCE_EVENT_RELEASE:
+		debounce_set_timer(device, time);
+		debounce_set_state(device, DEBOUNCE_STATE_IS_UP_WAITING);
+		break;
+	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
+		log_debounce_bug(device, event);
+		break;
+	case DEBOUNCE_EVENT_TIMEOUT:
+	case DEBOUNCE_EVENT_OTHERBUTTON:
+		debounce_set_state(device, DEBOUNCE_STATE_IS_DOWN);
+		debounce_notify_button(device, frame, LIBINPUT_BUTTON_STATE_PRESSED);
+		break;
+	}
+}
+
+static void
+debounce_disabled_handle_event(struct plugin_device *device,
+			       enum debounce_event event,
+			       struct evdev_frame *frame,
+			       uint64_t time)
+{
+	switch (event) {
+	case DEBOUNCE_EVENT_PRESS:
+		device->button_time = time;
+		debounce_notify_button(device, frame, LIBINPUT_BUTTON_STATE_PRESSED);
+		break;
+	case DEBOUNCE_EVENT_RELEASE:
+		device->button_time = time;
+		debounce_notify_button(device, frame, LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	case DEBOUNCE_EVENT_TIMEOUT_SHORT:
+	case DEBOUNCE_EVENT_TIMEOUT:
+		log_debounce_bug(device, event);
+		break;
+	case DEBOUNCE_EVENT_OTHERBUTTON:
+		break;
+	}
+}
+
+static void
+debounce_handle_event(struct plugin_device *device,
+		      enum debounce_event event,
+		      struct evdev_frame *frame,
+		      uint64_t time)
+{
+	enum debounce_state current = device->state;
+
+	if (event == DEBOUNCE_EVENT_OTHERBUTTON) {
+		debounce_cancel_timer(device);
+		debounce_cancel_timer_short(device);
+	}
+
+	switch (current) {
+	case DEBOUNCE_STATE_IS_UP:
+		debounce_is_up_handle_event(device, event, frame, time);
+		break;
+	case DEBOUNCE_STATE_IS_DOWN:
+		debounce_is_down_handle_event(device, event, frame, time);
+		break;
+	case DEBOUNCE_STATE_IS_DOWN_WAITING:
+		debounce_is_down_waiting_handle_event(device, event, frame, time);
+		break;
+	case DEBOUNCE_STATE_IS_UP_DELAYING:
+		debounce_is_up_delaying_handle_event(device, event, frame, time);
+		break;
+	case DEBOUNCE_STATE_IS_UP_DELAYING_SPURIOUS:
+		debounce_is_up_delaying_spurious_handle_event(device,
+							      event,
+							      frame,
+							      time);
+		break;
+	case DEBOUNCE_STATE_IS_UP_DETECTING_SPURIOUS:
+		debounce_is_up_detecting_spurious_handle_event(device,
+							       event,
+							       frame,
+							       time);
+		break;
+	case DEBOUNCE_STATE_IS_DOWN_DETECTING_SPURIOUS:
+		debounce_is_down_detecting_spurious_handle_event(device,
+								 event,
+								 frame,
+								 time);
+		break;
+	case DEBOUNCE_STATE_IS_UP_WAITING:
+		debounce_is_up_waiting_handle_event(device, event, frame, time);
+		break;
+	case DEBOUNCE_STATE_IS_DOWN_DELAYING:
+		debounce_is_down_delaying_handle_event(device, event, frame, time);
+		break;
+	case DEBOUNCE_STATE_DISABLED:
+		debounce_disabled_handle_event(device, event, frame, time);
+		break;
+	}
+
+	plugin_log_debug(device->parent->plugin,
+			 "debounce state: %s → %s → %s\n",
+			 debounce_state_to_str(current),
+			 debounce_event_to_str(event),
+			 debounce_state_to_str(device->state));
+}
+
+static void
+debounce_plugin_handle_frame(struct plugin_device *device,
+			     struct evdev_frame *frame,
+			     uint64_t time)
+{
+	size_t nchanged = 0;
+	bool flushed = false;
+
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+
+	/* Strip out all button events from this frame (if any). Then
+	 * append the button events to that stripped frame according
+	 * to our state machine.
+	 *
+	 * We allow for a max of 16 buttons to be appended, if you press more
+	 * than 16 buttons within the same frame good luck to you.
+	 */
+	_unref_(evdev_frame) *filtered_frame = evdev_frame_new(nevents + 16);
+	for (size_t i = 0; i < nevents; i++) {
+		struct evdev_event *e = &events[i];
+		if (!evdev_usage_is_button(e->usage)) {
+			evdev_frame_append(filtered_frame, e, 1);
+			continue;
+		}
+
+		nchanged++;
+
+		/* If we have more than one button this frame or a different button,
+		 * flush the state machine with otherbutton */
+		if (!flushed &&
+		    (nchanged > 1 ||
+		     evdev_usage_cmp(e->usage, device->button_usage) != 0)) {
+			debounce_handle_event(device,
+					      DEBOUNCE_EVENT_OTHERBUTTON,
+					      NULL,
+					      time);
+			flushed = true;
+		}
+	}
+
+	if (nchanged == 0)
+		return;
+
+	/* The state machine has some pre-conditions:
+	 * - the IS_DOWN and IS_UP states are neutral entry states without
+	 *   any timeouts
+	 * - a OTHERBUTTON event always flushes the state to IS_DOWN or
+	 *   IS_UP
+	 */
+	for (size_t i = 0; i < nevents; i++) {
+		struct evdev_event *e = &events[i];
+		bool is_down = !!e->value;
+
+		if (!evdev_usage_is_button(e->usage))
+			continue;
+
+		if (flushed && device->state != DEBOUNCE_STATE_DISABLED) {
+			debounce_set_state(device,
+					   !is_down ? DEBOUNCE_STATE_IS_DOWN
+						    : DEBOUNCE_STATE_IS_UP);
+			flushed = false;
+			debounce_maybe_disable(device);
+		}
+
+		device->button_usage = e->usage;
+		debounce_handle_event(device,
+				      is_down ? DEBOUNCE_EVENT_PRESS
+					      : DEBOUNCE_EVENT_RELEASE,
+				      filtered_frame,
+				      time);
+
+		/* if we have more than one event, we flush the state
+		 * machine immediately after the event itself */
+		if (nchanged > 1) {
+			debounce_handle_event(device,
+					      DEBOUNCE_EVENT_OTHERBUTTON,
+					      filtered_frame,
+					      time);
+			flushed = true;
+		}
+	}
+
+	evdev_frame_set(frame,
+			evdev_frame_get_events(filtered_frame, NULL),
+			evdev_frame_get_count(filtered_frame));
+}
+
+static void
+debounce_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
+			    struct libinput_device *device,
+			    struct evdev_frame *frame)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd;
+
+	list_for_each(pd, &plugin->devices, link) {
+		if (pd->device == device) {
+			debounce_plugin_handle_frame(pd, frame, frame->time);
+			break;
+		}
+	}
+}
+
+static void
+debounce_timeout(struct libinput_plugin *plugin, uint64_t now, void *data)
+{
+	struct plugin_device *device = data;
+
+	debounce_handle_event(device, DEBOUNCE_EVENT_TIMEOUT, NULL, now);
+}
+
+static void
+debounce_timeout_short(struct libinput_plugin *plugin, uint64_t now, void *data)
+{
+	struct plugin_device *device = data;
+
+	debounce_handle_event(device, DEBOUNCE_EVENT_TIMEOUT_SHORT, NULL, now);
+}
+
+static void
+debounce_plugin_device_added(struct libinput_plugin *libinput_plugin,
+			     struct libinput_device *device)
+{
+	if (!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER))
+		return;
+
+	if (libinput_device_is_virtual(device))
+		return;
+
+	_unref_(udev_device) *udev_device = libinput_device_get_udev_device(device);
+	if (udev_device) {
+		const char *prop = udev_device_get_property_value(udev_device,
+								  "ID_INPUT_TOUCHPAD");
+		bool val;
+		if (parse_boolean_property(prop, &val) && val) {
+			return;
+		}
+	}
+
+	_unref_(quirks) *q = libinput_device_get_quirks(device);
+	bool result = false;
+	if (q && quirks_get_bool(q, QUIRK_MODEL_BOUNCING_KEYS, &result) && result) {
+		return;
+	}
+
+	libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
+
+	/* We don't care about BTN_TRIGGER_HAPPY_* */
+	for (unsigned int code = BTN_0; code <= KEY_OK; code++) {
+		evdev_usage_t usage = evdev_usage_from_code(EV_KEY, code);
+		if (evdev_usage_is_button(usage)) {
+			libinput_plugin_enable_evdev_usage(libinput_plugin,
+							   evdev_usage_enum(usage));
+		}
+	}
+
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd = zalloc(sizeof(*pd));
+	pd->device = libinput_device_ref(device);
+	pd->parent = plugin;
+	pd->state = DEBOUNCE_STATE_IS_UP;
+
+	_autofree_ char *timer1_name =
+		strdup_printf("debounce-%s", libinput_device_get_sysname(device));
+	_autofree_ char *timer2_name =
+		strdup_printf("debounce-short-%s", libinput_device_get_sysname(device));
+	pd->timer = libinput_plugin_timer_new(libinput_plugin,
+					      timer1_name,
+					      debounce_timeout,
+					      pd);
+	pd->timer_short = libinput_plugin_timer_new(libinput_plugin,
+						    timer2_name,
+						    debounce_timeout_short,
+						    pd);
+
+	list_take_append(&plugin->devices, pd, link);
+}
+
+static void
+debounce_plugin_device_removed(struct libinput_plugin *libinput_plugin,
+			       struct libinput_device *device)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *dev;
+	list_for_each_safe(dev, &plugin->devices, link) {
+		if (dev->device == device) {
+			plugin_device_destroy(dev);
+			return;
+		}
+	}
+}
+
+static void
+debounce_plugin_feature_disabled(struct libinput_plugin *libinput_plugin,
+				 struct libinput_device *device,
+				 enum libinput_feature feature)
+{
+	if (feature != LIBINPUT_FEATURE_BUTTON_DEBOUNCING)
+		return;
+
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd;
+	list_for_each(pd, &plugin->devices, link) {
+		if (pd->device == device) {
+			pd->want_feature_disabled = true;
+			return;
+		}
+	}
+}
+
+static const struct libinput_plugin_interface interface = {
+	.run = NULL,
+	.destroy = plugin_destroy,
+	.device_new = NULL,
+	.device_ignored = NULL,
+	.device_added = debounce_plugin_device_added,
+	.device_removed = debounce_plugin_device_removed,
+	.evdev_frame = debounce_plugin_evdev_frame,
+	.feature_disabled = debounce_plugin_feature_disabled,
+};
+
+void
+libinput_debounce_plugin(struct libinput *libinput)
+{
+	struct plugin_data *plugin = zalloc(sizeof(*plugin));
+	list_init(&plugin->devices);
+
+	_unref_(libinput_plugin) *p =
+		libinput_plugin_new(libinput, "button-debounce", &interface, plugin);
+	plugin->plugin = p;
+}
diff -pruN 1.28.1-1/src/libinput-plugin-button-debounce.h 1.30.0-1/src/libinput-plugin-button-debounce.h
--- 1.28.1-1/src/libinput-plugin-button-debounce.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-button-debounce.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "libinput-plugin.h"
+#include "libinput.h"
+
+void
+libinput_debounce_plugin(struct libinput *libinput);
diff -pruN 1.28.1-1/src/libinput-plugin-lua.c 1.30.0-1/src/libinput-plugin-lua.c
--- 1.28.1-1/src/libinput-plugin-lua.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-lua.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,1358 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <lauxlib.h>
+#include <libevdev/libevdev.h>
+#include <lua.h>
+#include <lualib.h>
+
+#include "util-mem.h"
+#include "util-strings.h"
+
+#include "evdev-frame.h"
+#include "libinput-feature.h"
+#include "libinput-log.h"
+#include "libinput-plugin-lua.h"
+#include "libinput-plugin.h"
+#include "libinput-util.h"
+#include "timer.h"
+
+const uint32_t LIBINPUT_PLUGIN_VERSION = 1U;
+
+#define PLUGIN_METATABLE "LibinputPlugin"
+#define EVDEV_DEVICE_METATABLE "EvdevDevice"
+
+static const char libinput_lua_plugin_key = 'p'; /* key to lua registry */
+static const char libinput_key = 'l';            /* key to lua registry */
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(lua_State *, lua_close);
+
+struct udev_property {
+	struct list link;
+	char *key;
+	char *value;
+};
+
+static inline struct udev_property *
+udev_property_new(const char *key, const char *value)
+{
+	struct udev_property *prop = zalloc(sizeof(*prop));
+	prop->key = safe_strdup(key);
+	prop->value = safe_strdup(value);
+	return prop;
+}
+
+static inline void
+udev_property_destroy(struct udev_property *prop)
+{
+	list_remove(&prop->link);
+	free(prop->key);
+	free(prop->value);
+	free(prop);
+}
+
+/* A thin wrapper struct that just needs to exist, all
+ * the actual logic is struct libinput_lua_plugin */
+typedef struct {
+} LibinputPlugin;
+
+typedef struct {
+	struct list link;
+	int refid;
+
+	struct libinput_device *device;
+
+	unsigned int id;
+	unsigned int bustype;
+	unsigned int vid;
+	unsigned int pid;
+	char *name;
+	struct list udev_properties_list;
+
+	struct libevdev *evdev;
+
+	int device_removed_refid;
+	int frame_refid;
+
+	/* Caches any disable_feature calls during device_new */
+	bool was_added;
+	bitmask_t disabled_features;
+} EvdevDevice;
+
+struct libinput_lua_plugin {
+	struct libinput_plugin *parent;
+	lua_State *L;
+	int sandbox_table_idx;
+	bool register_called;
+
+	struct list evdev_devices; /* EvdevDevice */
+
+	size_t version;
+	int device_new_refid;
+	int timer_expired_refid;
+
+	struct libinput_plugin_timer *timer;
+	bool in_timer_func;
+};
+
+static struct libinput_lua_plugin *
+lua_get_libinput_lua_plugin(lua_State *L)
+{
+	struct libinput_lua_plugin *plugin = NULL;
+
+	lua_pushlightuserdata(L, (void *)&libinput_lua_plugin_key);
+	lua_gettable(L, LUA_REGISTRYINDEX);
+	plugin = lua_touserdata(L, -1);
+	lua_pop(L, 1);
+
+	return plugin;
+}
+
+/* Prints the current stack "layout" with a message */
+#define lua_show_stack(L, ...) { \
+	etrace(__VA_ARGS__); \
+	etrace("pcall: stack has: %d", lua_gettop(L)); \
+	for (int i = -1; i >= -lua_gettop(L); i--) \
+		etrace("   stack %d: %s", i, lua_typename(L, lua_type(L, i))); \
+}
+
+static struct libinput *
+lua_get_libinput(lua_State *L)
+{
+	struct libinput *libinput = NULL;
+
+	lua_pushlightuserdata(L, (void *)&libinput_key);
+	lua_gettable(L, LUA_REGISTRYINDEX);
+	libinput = lua_touserdata(L, -1);
+	lua_pop(L, 1);
+
+	return libinput;
+}
+
+static void
+lua_push_evdev_device(lua_State *L,
+		      struct libinput_lua_plugin *plugin,
+		      struct libinput_device *device,
+		      struct libevdev *evdev,
+		      struct udev_device *udev_device)
+{
+	EvdevDevice *lua_device = lua_newuserdata(L, sizeof(*lua_device));
+	memset(lua_device, 0, sizeof(*lua_device));
+	lua_device->device = libinput_device_ref(device);
+	lua_device->evdev = evdev;
+	lua_device->bustype = libinput_device_get_id_bustype(device);
+	lua_device->vid = libinput_device_get_id_vendor(device);
+	lua_device->pid = libinput_device_get_id_product(device);
+	lua_device->name = strdup(libinput_device_get_name(device));
+	lua_device->device_removed_refid = LUA_NOREF;
+	lua_device->frame_refid = LUA_NOREF;
+	list_init(&lua_device->udev_properties_list);
+
+	struct udev_list_entry *e = udev_device_get_properties_list_entry(udev_device);
+	while (e) {
+		const char *key = udev_list_entry_get_name(e);
+		if (strstartswith(key, "ID_INPUT_") &&
+		    !streq(key, "ID_INPUT_WIDTH_MM") &&
+		    !streq(key, "ID_INPUT_HEIGHT_MM")) {
+			const char *value = udev_list_entry_get_value(e);
+			if (!streq(value, "0")) {
+				struct udev_property *prop =
+					udev_property_new(key, value);
+				list_insert(&lua_device->udev_properties_list,
+					    &prop->link);
+			}
+		}
+		e = udev_list_entry_get_next(e);
+	}
+
+	list_insert(&plugin->evdev_devices, &lua_device->link);
+
+	lua_pushvalue(L, -1);                               /* Copy to top */
+	lua_device->refid = luaL_ref(L, LUA_REGISTRYINDEX); /* ref to device */
+
+	luaL_getmetatable(L, EVDEV_DEVICE_METATABLE);
+	lua_setmetatable(L, -2);
+}
+
+static void
+lua_push_evdev_frame(lua_State *L, struct evdev_frame *frame)
+{
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+
+	lua_newtable(L);
+	for (size_t i = 0; i < nevents; i++) {
+		struct evdev_event *e = &events[i];
+
+		if (evdev_usage_eq(e->usage, EVDEV_SYN_REPORT))
+			break;
+
+		lua_newtable(L);
+		lua_pushinteger(L, evdev_usage_as_uint32_t(e->usage));
+		lua_setfield(L, -2, "usage");
+		lua_pushinteger(L, e->value);
+		lua_setfield(L, -2, "value");
+		lua_rawseti(L, -2, i + 1);
+	}
+}
+
+static void
+lua_pop_evdev_frame(struct libinput_lua_plugin *plugin,
+		    struct libevdev *libevdev,
+		    struct evdev_frame *frame_out)
+{
+	lua_State *L = plugin->L;
+
+	if (lua_isnil(L, -1)) {
+		goto out;
+	}
+
+	if (!lua_istable(L, -1)) {
+		plugin_log_bug(plugin->parent,
+			       "expected table like `{ events = { ... } }`, got %s",
+			       lua_typename(L, lua_type(L, -1)));
+		goto out;
+	}
+
+	struct evdev_event events[64] = { 0 };
+	size_t nevents = 0;
+
+	lua_pushnil(L);
+	while (lua_next(L, -2) != 0 && nevents < ARRAY_LENGTH(events)) {
+
+		/* -2 is the index, -1 our { usage = ... } table */
+		if (!lua_istable(L, -1)) {
+			plugin_log_bug(
+				plugin->parent,
+				"expected table like `{ type = ..., code = ...}`, got %s",
+				lua_typename(L, lua_type(L, -1)));
+			goto out;
+		}
+
+		lua_getfield(L, -1, "usage");
+		uint32_t usage_value = luaL_checkinteger(L, -1);
+		lua_pop(L, 1);
+
+		lua_getfield(L, -1, "value");
+		int32_t value = luaL_checkinteger(L, -1);
+		lua_pop(L, 1);
+
+		lua_pop(L, 1); /* pop { usage = ..., value = ...} */
+
+		evdev_usage_t usage = evdev_usage_from_uint32_t(usage_value);
+		unsigned int type = evdev_usage_type(usage);
+		unsigned int code = evdev_usage_code(usage);
+		if (libevdev_has_event_code(libevdev, type, code)) {
+			struct evdev_event *e = &events[nevents++];
+			e->usage = usage;
+			e->value = value;
+
+			if (evdev_usage_eq(e->usage, EVDEV_SYN_REPORT)) {
+				lua_pop(L, 1); /* force-pop the nil */
+				break;
+			}
+		}
+	}
+
+	if (nevents == 0) {
+		events[0].usage = evdev_usage_from_uint32_t(EVDEV_SYN_REPORT);
+		events[0].value = 0;
+		nevents++;
+	}
+
+	if (evdev_frame_set(frame_out, events, nevents) == -ENOMEM) {
+		plugin_log_bug(plugin->parent, "too many events in frame");
+	}
+
+out:
+	lua_pop(L, 1);
+}
+
+static bool
+libinput_lua_pcall(struct libinput_lua_plugin *plugin, int narg, int nres)
+{
+	lua_State *L = plugin->L;
+
+	int rc = lua_pcall(L, narg, nres, 0);
+	if (rc != LUA_OK) {
+		auto libinput_plugin = plugin->parent;
+		const char *errormsg = lua_tostring(L, -1);
+		if (strstr(errormsg, "@@unregistering@@") == NULL) {
+			plugin_log_bug(libinput_plugin,
+				       "unloading after error: %s\n",
+				       errormsg);
+		}
+		lua_pop(L, 1); /* pop error message */
+
+		if (plugin->timer)
+			libinput_plugin_timer_cancel(plugin->timer);
+		libinput_plugin_unregister(libinput_plugin);
+		/* plugin system will destroy the plugin later */
+	}
+	return rc == LUA_OK;
+}
+
+static void
+libinput_lua_plugin_device_new(struct libinput_plugin *libinput_plugin,
+			       struct libinput_device *device,
+			       struct libevdev *evdev,
+			       struct udev_device *udev_device)
+{
+	struct libinput_lua_plugin *plugin =
+		libinput_plugin_get_user_data(libinput_plugin);
+
+	lua_rawgeti(plugin->L, LUA_REGISTRYINDEX, plugin->device_new_refid);
+	lua_push_evdev_device(plugin->L, plugin, device, evdev, udev_device);
+
+	libinput_lua_pcall(plugin, 1, 0);
+}
+
+static void
+remove_device(struct libinput_lua_plugin *plugin, EvdevDevice *evdev)
+{
+	/* Don't allow access to the libevdev context during remove */
+	evdev->evdev = NULL;
+	if (evdev->device_removed_refid != LUA_NOREF) {
+		lua_rawgeti(plugin->L, LUA_REGISTRYINDEX, evdev->device_removed_refid);
+		lua_rawgeti(plugin->L, LUA_REGISTRYINDEX, evdev->refid);
+
+		if (!libinput_lua_pcall(plugin, 1, 0))
+			return;
+	}
+	luaL_unref(plugin->L, evdev->refid, LUA_REGISTRYINDEX);
+	evdev->refid = LUA_NOREF;
+	list_remove(&evdev->link);
+	list_init(&evdev->link); /* so we can list_remove in _gc */
+
+	struct udev_property *prop;
+	list_for_each_safe(prop, &evdev->udev_properties_list, link) {
+		udev_property_destroy(prop);
+	}
+	free(evdev->name);
+	evdev->name = NULL;
+	evdev->device = libinput_device_unref(evdev->device);
+
+	/* This device no longer exists but our lua code may have a
+	 * reference to it */
+}
+
+static void
+libinput_lua_plugin_device_ignored(struct libinput_plugin *libinput_plugin,
+				   struct libinput_device *device)
+{
+	struct libinput_lua_plugin *plugin =
+		libinput_plugin_get_user_data(libinput_plugin);
+
+	EvdevDevice *evdev;
+	list_for_each_safe(evdev, &plugin->evdev_devices, link) {
+		if (evdev->device != device)
+			continue;
+		remove_device(plugin, evdev);
+	}
+}
+
+static void
+libinput_lua_plugin_device_removed(struct libinput_plugin *libinput_plugin,
+				   struct libinput_device *device)
+{
+	struct libinput_lua_plugin *plugin =
+		libinput_plugin_get_user_data(libinput_plugin);
+
+	EvdevDevice *evdev;
+	list_for_each_safe(evdev, &plugin->evdev_devices, link) {
+		if (evdev->device != device)
+			continue;
+		remove_device(plugin, evdev);
+	}
+}
+
+static void
+libinput_lua_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
+				struct libinput_device *device,
+				struct evdev_frame *frame)
+{
+	struct libinput_lua_plugin *plugin =
+		libinput_plugin_get_user_data(libinput_plugin);
+
+	EvdevDevice *evdev;
+	list_for_each_safe(evdev, &plugin->evdev_devices, link) {
+		if (evdev->device != device)
+			continue;
+
+		if (evdev->frame_refid == LUA_NOREF)
+			continue;
+
+		lua_rawgeti(plugin->L, LUA_REGISTRYINDEX, evdev->frame_refid);
+		lua_rawgeti(plugin->L, LUA_REGISTRYINDEX, evdev->refid);
+		lua_push_evdev_frame(plugin->L, frame);
+		lua_pushinteger(plugin->L, evdev_frame_get_time(frame));
+
+		if (!libinput_lua_pcall(plugin, 3, 1))
+			return;
+		lua_pop_evdev_frame(plugin, evdev->evdev, frame);
+	}
+}
+
+static void
+register_func(struct lua_State *L, int stack_index, int *refid)
+{
+	if (*refid != LUA_NOREF)
+		luaL_unref(L, LUA_REGISTRYINDEX, *refid);
+	lua_pushvalue(L, stack_index);           /* Copy function to top */
+	*refid = luaL_ref(L, LUA_REGISTRYINDEX); /* ref to function */
+}
+
+static void
+unregister_func(struct lua_State *L, int *refid)
+{
+	if (*refid != LUA_NOREF) {
+		luaL_unref(L, LUA_REGISTRYINDEX, *refid);
+		*refid = LUA_NOREF;
+	}
+}
+
+static int
+libinputplugin_connect(lua_State *L)
+{
+	LibinputPlugin *p = luaL_checkudata(L, 1, PLUGIN_METATABLE);
+	luaL_argcheck(L, p != NULL, 1, PLUGIN_METATABLE " expected");
+
+	const char *name = luaL_checkstring(L, 2);
+	luaL_checktype(L, 3, LUA_TFUNCTION);
+
+	struct libinput_lua_plugin *plugin = lua_get_libinput_lua_plugin(L);
+
+	/* Version 1 signals */
+	if (streq(name, "new-evdev-device")) {
+		register_func(L, 3, &plugin->device_new_refid);
+	} else if (streq(name, "timer-expired")) {
+		register_func(L, 3, &plugin->timer_expired_refid);
+	} else {
+		return luaL_error(L, "Unknown name: %s", name);
+	}
+
+	return 0;
+}
+
+static int
+libinputplugin_now(lua_State *L)
+{
+	LibinputPlugin *p = luaL_checkudata(L, 1, PLUGIN_METATABLE);
+	luaL_argcheck(L, p != NULL, 1, PLUGIN_METATABLE " expected");
+
+	struct libinput *libinput = lua_get_libinput(L);
+	uint64_t now = libinput_now(libinput);
+
+	lua_pushinteger(L, now);
+
+	return 1;
+}
+
+static int
+libinputplugin_version(lua_State *L)
+{
+	LibinputPlugin *p = luaL_checkudata(L, 1, PLUGIN_METATABLE);
+	luaL_argcheck(L, p != NULL, 1, PLUGIN_METATABLE " expected");
+
+	struct libinput_lua_plugin *plugin = lua_get_libinput_lua_plugin(L);
+	lua_pushinteger(L, plugin->version);
+	return 1;
+}
+
+static int
+libinputplugin_register(lua_State *L)
+{
+	LibinputPlugin *p = luaL_checkudata(L, 1, PLUGIN_METATABLE);
+	luaL_argcheck(L, p != NULL, 1, PLUGIN_METATABLE " expected");
+
+	struct libinput_lua_plugin *plugin = lua_get_libinput_lua_plugin(L);
+	if (plugin->register_called) {
+		return luaL_error(L, "plugin already registered");
+	}
+
+	uint32_t versions[16] = { 0 };
+	size_t idx = 0;
+
+	luaL_checktype(L, 2, LUA_TTABLE);
+	lua_pushnil(L);
+	while (idx < ARRAY_LENGTH(versions) && lua_next(L, -2) != 0) {
+		int version = luaL_checkinteger(L, -1);
+		lua_pop(L, 1);
+		if (version <= 0) {
+			return luaL_error(L, "Invalid version number");
+		}
+		versions[idx++] = version;
+	}
+
+	ARRAY_FOR_EACH(versions, v) {
+		if (*v == 0)
+			break;
+		if (*v == LIBINPUT_PLUGIN_VERSION) {
+			plugin->version = *v;
+			plugin->register_called = true;
+
+			lua_pushinteger(L, plugin->version);
+
+			return 1;
+		}
+	}
+
+	return luaL_error(L, "None of this plugin's versions are supported");
+}
+
+static int
+libinputplugin_unregister(lua_State *L)
+{
+	LibinputPlugin *p = luaL_checkudata(L, 1, PLUGIN_METATABLE);
+	luaL_argcheck(L, p != NULL, 1, PLUGIN_METATABLE " expected");
+
+	/* Bit of a hack: unregister should work like os.exit(1)
+	 * but we're in a lua context here so the easiest way
+	 * to handle this is pretend we have an error, let
+	 * our error handler unwind and just search for this
+	 * magic string to *not* print log message */
+	return luaL_error(L, "@@unregistering@@");
+}
+
+static int
+libinputplugin_gc(lua_State *L)
+{
+	LibinputPlugin *p = luaL_checkudata(L, 1, PLUGIN_METATABLE);
+	luaL_argcheck(L, p != NULL, 1, PLUGIN_METATABLE " expected");
+
+	struct libinput_lua_plugin *plugin = lua_get_libinput_lua_plugin(L);
+	if (plugin->timer)
+		libinput_plugin_timer_cancel(plugin->timer);
+
+	/* We're about to destroy the plugin so the timer is the only
+	 * thing we need to stop, the rest will be cleaned up
+	 * when we destroy the plugin */
+
+	return 0;
+}
+
+static void
+plugin_timer_func(struct libinput_plugin *libinput_plugin, uint64_t now, void *data)
+{
+	struct libinput_lua_plugin *plugin = data;
+	struct lua_State *L = plugin->L;
+
+	lua_rawgeti(L, LUA_REGISTRYINDEX, plugin->timer_expired_refid);
+	lua_pushinteger(L, now);
+
+	libinput_lua_pcall(plugin, 1, 0);
+}
+
+static int
+libinputplugin_timer_set(lua_State *L, uint64_t offset)
+{
+	LibinputPlugin *p = luaL_checkudata(L, 1, PLUGIN_METATABLE);
+	luaL_argcheck(L, p != NULL, 1, PLUGIN_METATABLE " expected");
+
+	struct libinput_lua_plugin *plugin = lua_get_libinput_lua_plugin(L);
+	uint64_t timeout = luaL_checkinteger(L, 2);
+
+	if (!plugin->timer) {
+		plugin->timer = libinput_plugin_timer_new(
+			plugin->parent,
+			libinput_plugin_get_name(plugin->parent),
+			plugin_timer_func,
+			plugin);
+	}
+
+	libinput_plugin_timer_set(plugin->timer, offset + timeout);
+
+	return 0;
+}
+
+static int
+libinputplugin_timer_set_absolute(lua_State *L)
+{
+	return libinputplugin_timer_set(L, 0);
+}
+
+static int
+libinputplugin_timer_set_relative(lua_State *L)
+{
+	auto libinput = lua_get_libinput(L);
+	return libinputplugin_timer_set(L, libinput_now(libinput));
+}
+
+static int
+libinputplugin_timer_cancel(lua_State *L)
+{
+	LibinputPlugin *p = luaL_checkudata(L, 1, PLUGIN_METATABLE);
+	luaL_argcheck(L, p != NULL, 1, PLUGIN_METATABLE " expected");
+
+	struct libinput_lua_plugin *plugin = lua_get_libinput_lua_plugin(L);
+	if (plugin->timer)
+		libinput_plugin_timer_cancel(plugin->timer);
+
+	return 0;
+}
+
+static int
+libinputplugin_log(lua_State *L, enum libinput_log_priority pri)
+{
+	LibinputPlugin *p = luaL_checkudata(L, 1, PLUGIN_METATABLE);
+	luaL_argcheck(L, p != NULL, 1, PLUGIN_METATABLE " expected");
+
+	const char *message = luaL_checkstring(L, 2);
+
+	auto plugin = lua_get_libinput_lua_plugin(L);
+
+	plugin_log_msg(plugin->parent, pri, "%s\n", message);
+
+	return 0;
+}
+
+static int
+libinputplugin_log_debug(lua_State *L)
+{
+	return libinputplugin_log(L, LIBINPUT_LOG_PRIORITY_DEBUG);
+}
+
+static int
+libinputplugin_log_info(lua_State *L)
+{
+	return libinputplugin_log(L, LIBINPUT_LOG_PRIORITY_INFO);
+}
+
+static int
+libinputplugin_log_error(lua_State *L)
+{
+	return libinputplugin_log(L, LIBINPUT_LOG_PRIORITY_ERROR);
+}
+
+static const struct luaL_Reg libinputplugin_vtable[] = {
+	{ "now", libinputplugin_now },
+	{ "version", libinputplugin_version },
+	{ "connect", libinputplugin_connect },
+	{ "register", libinputplugin_register },
+	{ "unregister", libinputplugin_unregister },
+	{ "timer_cancel", libinputplugin_timer_cancel },
+	{ "timer_set_absolute", libinputplugin_timer_set_absolute },
+	{ "timer_set_relative", libinputplugin_timer_set_relative },
+	{ "log_debug", libinputplugin_log_debug },
+	{ "log_info", libinputplugin_log_info },
+	{ "log_error", libinputplugin_log_error },
+	{ "__gc", libinputplugin_gc },
+	{ NULL, NULL }
+};
+
+static void
+libinputplugin_init(lua_State *L)
+{
+	luaL_newmetatable(L, PLUGIN_METATABLE);
+	lua_pushstring(L, "__index");
+	lua_pushvalue(L, -2); /* push metatable */
+	lua_settable(L, -3);  /* metatable.__index = metatable */
+	luaL_setfuncs(L, libinputplugin_vtable, 0);
+}
+
+static int
+evdevdevice_info(lua_State *L)
+{
+	EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE);
+	luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE " expected");
+
+	lua_newtable(L); /* { bustype: ..., vid: ..., pid: ..., name: ... } */
+
+	if (device->evdev == NULL)
+		return 1;
+
+	lua_pushinteger(L, device->bustype);
+	lua_setfield(L, -2, "bustype");
+	lua_pushinteger(L, device->vid);
+	lua_setfield(L, -2, "vid");
+	lua_pushinteger(L, device->pid);
+	lua_setfield(L, -2, "pid");
+
+	return 1;
+}
+
+static int
+evdevdevice_name(lua_State *L)
+{
+	EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE);
+	luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE "expected");
+
+	lua_pushstring(L, device->name);
+
+	return 1;
+}
+
+static int
+evdevdevice_usages(lua_State *L)
+{
+	EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE);
+	luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE "expected");
+
+	lua_newtable(L); /* { evdev.REL_X: ... } */
+
+	if (device->evdev == NULL)
+		return 1;
+
+	for (unsigned int t = 0; t <= EV_MAX; t++) {
+		if (!libevdev_has_event_type(device->evdev, t))
+			continue;
+
+		int max = libevdev_event_type_get_max(t);
+		for (unsigned int code = 0; (int)code < max; code++) {
+			if (!libevdev_has_event_code(device->evdev, t, code))
+				continue;
+
+			evdev_usage_t usage = evdev_usage_from_code(t, code);
+			lua_pushboolean(L, true);
+			lua_rawseti(L, -2, evdev_usage_as_uint32_t(usage));
+		}
+	}
+
+	return 1;
+}
+
+static int
+evdevdevice_absinfos(lua_State *L)
+{
+	EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE);
+	luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE "expected");
+
+	lua_newtable(L); /* { ABS_X: { min: 1, max: 2, ... }, ... } */
+
+	if (device->evdev == NULL)
+		return 1;
+
+	for (unsigned int code = 0; code <= ABS_MAX; code++) {
+		const struct input_absinfo *abs =
+			libevdev_get_abs_info(device->evdev, code);
+		if (!abs)
+			continue;
+
+		lua_newtable(L);
+		lua_pushinteger(L, abs->minimum);
+		lua_setfield(L, -2, "minimum");
+		lua_pushinteger(L, abs->maximum);
+		lua_setfield(L, -2, "maximum");
+		lua_pushinteger(L, abs->fuzz);
+		lua_setfield(L, -2, "fuzz");
+		lua_pushinteger(L, abs->flat);
+		lua_setfield(L, -2, "flat");
+		lua_pushinteger(L, abs->resolution);
+		lua_setfield(L, -2, "resolution");
+
+		evdev_usage_t usage = evdev_usage_from_code(EV_ABS, code);
+		lua_rawseti(
+			L,
+			-2,
+			evdev_usage_as_uint32_t(usage)); /* Assign to top-level table */
+	}
+
+	return 1;
+}
+
+static int
+evdevdevice_udev_properties(lua_State *L)
+{
+	EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE);
+	luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE "expected");
+
+	lua_newtable(L); /* { ID_INPUT: { ... } , ... } */
+
+	if (device->evdev == NULL)
+		return 1;
+
+	struct udev_property *prop;
+	list_for_each(prop, &device->udev_properties_list, link) {
+		lua_pushstring(L, prop->value);
+		lua_setfield(L, -2, prop->key); /* Assign to top-level table */
+	}
+
+	return 1;
+}
+
+static int
+evdevdevice_enable_evdev_usage(lua_State *L)
+{
+	EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE);
+	luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE "expected");
+
+	struct libinput_lua_plugin *plugin = lua_get_libinput_lua_plugin(L);
+
+	evdev_usage_t usage = evdev_usage_from_uint32_t(luaL_checkinteger(L, 2));
+	uint16_t type = evdev_usage_type(usage);
+	uint16_t code = evdev_usage_code(usage);
+	if (type > EV_MAX) {
+		plugin_log_bug(plugin->parent,
+			       "Ignoring invalid evdev usage %#x\n",
+			       evdev_usage_as_uint32_t(usage));
+		return 0;
+	}
+
+	if (device->evdev == NULL || type == EV_ABS)
+		return 0;
+
+	libevdev_enable_event_code(device->evdev, type, code, NULL);
+
+	return 0;
+}
+
+static int
+evdevdevice_disable_evdev_usage(lua_State *L)
+{
+	EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE);
+	luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE "expected");
+
+	evdev_usage_t usage = evdev_usage_from_uint32_t(luaL_checkinteger(L, 2));
+	uint16_t type = evdev_usage_type(usage);
+	uint16_t code = evdev_usage_code(usage);
+
+	if (device->evdev == NULL || type > EV_MAX)
+		return 0;
+
+	libevdev_disable_event_code(device->evdev, type, code);
+
+	return 0;
+}
+
+static int
+evdevdevice_set_absinfo(lua_State *L)
+{
+	EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE);
+	luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE "expected");
+
+	evdev_usage_t usage = evdev_usage_from_uint32_t(luaL_checkinteger(L, 2));
+	luaL_checktype(L, 3, LUA_TTABLE);
+
+	if (evdev_usage_type(usage) != EV_ABS)
+		return 0;
+
+	if (!device->evdev)
+		return 0;
+
+	uint16_t code = evdev_usage_code(usage);
+	const struct input_absinfo *absinfo =
+		libevdev_get_abs_info(device->evdev, code);
+	struct input_absinfo abs = {};
+	if (absinfo)
+		abs = *absinfo;
+
+	lua_getfield(L, 3, "minimum");
+	if (lua_isnumber(L, -1))
+		abs.minimum = luaL_checkinteger(L, -1);
+	lua_getfield(L, 3, "maximum");
+	if (lua_isnumber(L, -1))
+		abs.maximum = luaL_checkinteger(L, -1);
+	lua_getfield(L, 3, "resolution");
+	if (lua_isnumber(L, -1))
+		abs.resolution = luaL_checkinteger(L, -1);
+	lua_getfield(L, 3, "fuzz");
+	if (lua_isnumber(L, -1))
+		abs.fuzz = luaL_checkinteger(L, -1);
+	lua_getfield(L, 3, "flat");
+	if (lua_isnumber(L, -1))
+		abs.flat = luaL_checkinteger(L, -1);
+
+	libevdev_enable_event_code(device->evdev, EV_ABS, code, &abs);
+
+	return 0;
+}
+
+static int
+evdevdevice_connect(lua_State *L)
+{
+	EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE);
+	luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE " expected");
+
+	const char *name = luaL_checkstring(L, 2);
+	luaL_checktype(L, 3, LUA_TFUNCTION);
+
+	/* No refid means we got removed, so quietly
+	 * drop any connect call */
+	if (device->refid == LUA_NOREF)
+		return 0;
+
+	if (streq(name, "device-removed")) {
+		register_func(L, 3, &device->device_removed_refid);
+	} else if (streq(name, "evdev-frame")) {
+		struct libinput_lua_plugin *plugin = lua_get_libinput_lua_plugin(L);
+		libinput_plugin_enable_device_event_frame(plugin->parent,
+							  device->device,
+							  true);
+		register_func(L, 3, &device->frame_refid);
+	} else {
+		return luaL_error(L, "Unknown name: %s", name);
+	}
+
+	return 0;
+}
+
+static int
+evdevdevice_disconnect(lua_State *L)
+{
+	EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE);
+	luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE " expected");
+
+	const char *name = luaL_checkstring(L, 2);
+
+	/* No refid means we got removed, so quietly
+	 * drop any disconnect call */
+	if (device->refid == LUA_NOREF)
+		return 0;
+
+	if (streq(name, "device-removed")) {
+		unregister_func(L, &device->device_removed_refid);
+	} else if (streq(name, "evdev-frame")) {
+		struct libinput_lua_plugin *plugin = lua_get_libinput_lua_plugin(L);
+		libinput_plugin_enable_device_event_frame(plugin->parent,
+							  device->device,
+							  false);
+		unregister_func(L, &device->frame_refid);
+	} else {
+		return luaL_error(L, "Unknown name: %s", name);
+	}
+
+	return 0;
+}
+
+static struct evdev_frame *
+evdevdevice_frame(lua_State *L, struct libinput_lua_plugin *plugin, EvdevDevice *device)
+{
+	auto frame = evdev_frame_new(64);
+	lua_pop_evdev_frame(plugin, device->evdev, frame);
+
+	struct libinput *libinput = lua_get_libinput(L);
+	uint64_t now = libinput_now(libinput);
+	evdev_frame_set_time(frame, now);
+
+	return frame;
+}
+
+static int
+evdevdevice_prepend_frame(lua_State *L)
+{
+	EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE);
+	luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE " expected");
+
+	luaL_checktype(L, 2, LUA_TTABLE);
+
+	/* No refid means we got removed, so quietly
+	 * drop any disconnect call */
+	if (device->refid == LUA_NOREF)
+		return 0;
+
+	struct libinput_lua_plugin *plugin = lua_get_libinput_lua_plugin(L);
+	_unref_(evdev_frame) *frame = evdevdevice_frame(L, plugin, device);
+	/* FIXME: need to really ensure that the device can never be dangling */
+	libinput_plugin_prepend_evdev_frame(plugin->parent, device->device, frame);
+
+	return 0;
+}
+
+static int
+evdevdevice_append_frame(lua_State *L)
+{
+	EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE);
+	luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE " expected");
+
+	luaL_checktype(L, 2, LUA_TTABLE);
+
+	/* No refid means we got removed, so quietly
+	 * drop any disconnect call */
+	if (device->refid == LUA_NOREF)
+		return 0;
+
+	struct libinput_lua_plugin *plugin = lua_get_libinput_lua_plugin(L);
+	_unref_(evdev_frame) *frame = evdevdevice_frame(L, plugin, device);
+
+	/* FIXME: need to really ensure that the device can never be dangling */
+	libinput_plugin_append_evdev_frame(plugin->parent, device->device, frame);
+
+	return 0;
+}
+
+static int
+evdevdevice_disable_feature(lua_State *L)
+{
+	EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE);
+	luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE " expected");
+
+	const char *feature = luaL_checkstring(L, 2);
+
+	/* No refid means we got removed, so quietly
+	 * drop any call */
+	if (device->refid == LUA_NOREF)
+		return 0;
+
+	const struct {
+		const char *name;
+		enum libinput_feature feature;
+	} map[] = {
+		{ "button-debouncing", LIBINPUT_FEATURE_BUTTON_DEBOUNCING },
+		{ "wheel-debouncing", LIBINPUT_FEATURE_WHEEL_DEBOUNCING },
+		{ "touchpad-jump-detection", LIBINPUT_FEATURE_TOUCHPAD_JUMP_DETECTION },
+		{ "touchpad-palm-detection", LIBINPUT_FEATURE_TOUCHPAD_PALM_DETECTION },
+		{ "touchpad-hysteresis", LIBINPUT_FEATURE_TOUCHPAD_HYSTERESIS },
+	};
+
+	ARRAY_FOR_EACH(map, m) {
+		if (streq(feature, m->name)) {
+			struct libinput_lua_plugin *plugin =
+				lua_get_libinput_lua_plugin(L);
+			libinput_plugin_disable_device_feature(plugin->parent,
+							       device->device,
+							       m->feature);
+			return 0;
+		}
+	}
+
+	return luaL_error(L, "Unknown feature: %s", feature);
+}
+
+static int
+evdevdevice_gc(lua_State *L)
+{
+	EvdevDevice *device = luaL_checkudata(L, 1, EVDEV_DEVICE_METATABLE);
+	luaL_argcheck(L, device != NULL, 1, EVDEV_DEVICE_METATABLE "expected");
+
+	list_remove(&device->link);
+	struct udev_property *prop;
+	list_for_each_safe(prop, &device->udev_properties_list, link) {
+		udev_property_destroy(prop);
+	}
+	free(device->name);
+
+	return 0;
+}
+
+static const struct luaL_Reg evdevdevice_vtable[] = {
+	{ "info", evdevdevice_info },
+	{ "name", evdevdevice_name },
+	{ "usages", evdevdevice_usages },
+	{ "absinfos", evdevdevice_absinfos },
+	{ "udev_properties", evdevdevice_udev_properties },
+	{ "enable_evdev_usage", evdevdevice_enable_evdev_usage },
+	{ "disable_evdev_usage", evdevdevice_disable_evdev_usage },
+	{ "set_absinfo", evdevdevice_set_absinfo },
+	{ "connect", evdevdevice_connect },
+	{ "disconnect", evdevdevice_disconnect },
+	{ "prepend_frame", evdevdevice_prepend_frame },
+	{ "append_frame", evdevdevice_append_frame },
+	{ "disable_feature", evdevdevice_disable_feature },
+	{ "__gc", evdevdevice_gc },
+	{ NULL, NULL }
+};
+
+static void
+evdevdevice_init(lua_State *L)
+{
+	luaL_newmetatable(L, EVDEV_DEVICE_METATABLE);
+	lua_pushstring(L, "__index");
+	lua_pushvalue(L, -2); /* push metatable */
+	lua_settable(L, -3);  /* metatable.__index = metatable */
+	luaL_setfuncs(L, evdevdevice_vtable, 0);
+}
+
+static void
+libinput_lua_plugin_destroy(struct libinput_lua_plugin *plugin)
+{
+	if (plugin->timer)
+		libinput_plugin_timer_cancel(plugin->timer);
+
+	EvdevDevice *evdev;
+	list_for_each_safe(evdev, &plugin->evdev_devices, link) {
+		remove_device(plugin, evdev);
+	}
+
+	if (plugin->timer)
+		plugin->timer = libinput_plugin_timer_unref(plugin->timer);
+	if (plugin->L)
+		lua_close(plugin->L);
+	free(plugin);
+}
+
+DEFINE_DESTROY_CLEANUP_FUNC(libinput_lua_plugin);
+
+static void
+libinput_plugin_destroy(struct libinput_plugin *libinput_plugin)
+{
+	struct libinput_lua_plugin *plugin =
+		libinput_plugin_get_user_data(libinput_plugin);
+	if (plugin)
+		libinput_lua_plugin_destroy(plugin);
+}
+
+static void
+libinput_lua_plugin_run(struct libinput_plugin *libinput_plugin)
+{
+	struct libinput_lua_plugin *plugin =
+		libinput_plugin_get_user_data(libinput_plugin);
+	struct lua_State *L = plugin->L;
+
+	assert(lua_isfunction(L, -1));
+
+	/* Main entry point into the plugin, so we need to push our sandbox
+	 * as _ENV. This only needs to be done here because all other entry
+	 * points into the Lua script are callbacks that are set up during
+	 * this run (and thus share the _ENV)
+	 */
+	lua_pushvalue(L, plugin->sandbox_table_idx);
+	const char *upval = lua_setupvalue(L, -2, 1);
+	if (!upval || !streq(upval, "_ENV")) {
+		plugin_log_bug_libinput(libinput_plugin, "Failed to set up sandbox\n");
+		libinput_plugin_unregister(libinput_plugin);
+		return;
+	}
+
+	if (libinput_lua_pcall(plugin, 0, 0) && !plugin->register_called) {
+		plugin_log_bug(libinput_plugin,
+			       "plugin never registered, unloading plugin\n");
+		libinput_plugin_unregister(libinput_plugin);
+		/* plugin system will destroy the plugin later */
+	}
+}
+
+static void
+libinput_lua_init_evdev_global(lua_State *L, int sandbox_table_idx)
+{
+	lua_newtable(L);
+	for (unsigned int t = 0; t <= EV_MAX; t++) {
+		const char *typename = libevdev_event_type_get_name(t);
+		if (!typename)
+			continue;
+
+		int max = libevdev_event_type_get_max(t);
+		if (max < 0)
+			continue;
+
+		for (int i = 0; i < max; i++) {
+			const char *name = libevdev_event_code_get_name(t, i);
+			if (!name)
+				continue;
+
+			evdev_usage_t usage = evdev_usage_from_code(t, i);
+			lua_pushinteger(L, evdev_usage_as_uint32_t(usage));
+			lua_setfield(L, -2, name);
+		}
+	}
+
+#define pushbus(name, value) do { \
+		lua_pushinteger(L, value); \
+		lua_setfield(L, -2, #name); \
+	} while (0)
+
+	pushbus(BUS_PCI, 0x01);
+	pushbus(BUS_ISAPNP, 0x02);
+	pushbus(BUS_USB, 0x03);
+	pushbus(BUS_HIL, 0x04);
+	pushbus(BUS_BLUETOOTH, 0x05);
+	pushbus(BUS_VIRTUAL, 0x06);
+
+	pushbus(BUS_ISA, 0x10);
+	pushbus(BUS_I8042, 0x11);
+	pushbus(BUS_XTKBD, 0x12);
+	pushbus(BUS_RS232, 0x13);
+	pushbus(BUS_GAMEPORT, 0x14);
+	pushbus(BUS_PARPORT, 0x15);
+	pushbus(BUS_AMIGA, 0x16);
+	pushbus(BUS_ADB, 0x17);
+	pushbus(BUS_I2C, 0x18);
+	pushbus(BUS_HOST, 0x19);
+	pushbus(BUS_GSC, 0x1A);
+	pushbus(BUS_ATARI, 0x1B);
+	pushbus(BUS_SPI, 0x1C);
+	pushbus(BUS_RMI, 0x1D);
+	pushbus(BUS_CEC, 0x1E);
+	pushbus(BUS_INTEL_ISHTP, 0x1F);
+	pushbus(BUS_AMD_SFH, 0x20);
+
+#undef pushbus
+
+	lua_setfield(L, sandbox_table_idx, "evdev");
+}
+
+static const struct libinput_plugin_interface interface = {
+	.run = libinput_lua_plugin_run,
+	.destroy = libinput_plugin_destroy,
+	.device_new = libinput_lua_plugin_device_new,
+	.device_ignored = libinput_lua_plugin_device_ignored,
+	.device_added = NULL,
+	.device_removed = libinput_lua_plugin_device_removed,
+	.evdev_frame = libinput_lua_plugin_evdev_frame,
+};
+
+static lua_State *
+libinput_lua_plugin_init_lua(struct libinput *libinput,
+			     struct libinput_lua_plugin *plugin)
+{
+	lua_State *L = luaL_newstate();
+	if (!L)
+		return NULL;
+
+	/* This will be our _ENV later, see libinput_lua_pcall */
+	lua_newtable(L);
+	int sandbox_table_idx = lua_gettop(L);
+	plugin->sandbox_table_idx = sandbox_table_idx;
+
+	/* Load the modules we want to (partially) expose.
+	 * An (outdated?) list of safe function is here:
+	 * http://lua-users.org/wiki/SandBoxes
+	 *
+	 * Math, String and Table seem to be safe given that our plugins
+	 * all have their own individual sandbox.
+	 */
+
+	luaL_requiref(L, LUA_GNAME, luaopen_base, 0);
+	static const char *allowed_funcs[] = {
+		"assert", "error",    "ipairs",   "next", "pcall",  "pairs",    "print",
+		"select", "tonumber", "tostring", "type", "xpcall", "_VERSION",
+	};
+	ARRAY_FOR_EACH(allowed_funcs, func) {
+		lua_getfield(L, -1, *func);
+		lua_setfield(L, sandbox_table_idx, *func);
+	}
+	lua_pop(L, 1);
+
+	/* Math is fine as a whole */
+	luaL_requiref(L, LUA_MATHLIBNAME, luaopen_math, 0);
+	lua_setfield(L, sandbox_table_idx, "math");
+
+	/* Table is fine as a whole */
+	luaL_requiref(L, LUA_TABLIBNAME, luaopen_table, 0);
+	lua_setfield(L, sandbox_table_idx, "table");
+
+	/* String is fine as a whole */
+	luaL_requiref(L, LUA_STRLIBNAME, luaopen_string, 0);
+	lua_setfield(L, sandbox_table_idx, "string");
+
+	/* Override metatable to prevent access to unregistered globals */
+	lua_pushvalue(L, sandbox_table_idx);
+	lua_newtable(L);
+	lua_pushnil(L);
+	lua_setfield(L, -2, "__index");
+	lua_setmetatable(L, sandbox_table_idx);
+
+	/* Our objects */
+	libinputplugin_init(L);
+	evdevdevice_init(L);
+
+	/* Our globals */
+	lua_newtable(L);
+	libinput_lua_init_evdev_global(L, sandbox_table_idx);
+
+	/* The libinput global object */
+	lua_newuserdata(L, sizeof(LibinputPlugin));
+	luaL_getmetatable(L, PLUGIN_METATABLE);
+	lua_setmetatable(L, -2);
+	lua_setfield(L, sandbox_table_idx, "libinput");
+
+	/* Make struct libinput available in our callbacks */
+	lua_pushlightuserdata(L, (void *)&libinput_key);
+	lua_pushlightuserdata(L, libinput);
+	lua_settable(L, LUA_REGISTRYINDEX);
+
+	/* Make struct libinput_lua_plugin available in our callbacks */
+	lua_pushlightuserdata(L, (void *)&libinput_lua_plugin_key);
+	lua_pushlightuserdata(L, plugin);
+	lua_settable(L, LUA_REGISTRYINDEX);
+
+	return L;
+}
+
+struct libinput_plugin *
+libinput_lua_plugin_new_from_path(struct libinput *libinput, const char *path)
+{
+	_destroy_(libinput_lua_plugin) *plugin = zalloc(sizeof(*plugin));
+	_autofree_ char *name = safe_strdup(safe_basename(path));
+
+	/* libinput's plugin system keeps a ref, we don't need
+	 * a separate ref here, the plugin system will outlast us.
+	 */
+	_unref_(libinput_plugin) *p =
+		libinput_plugin_new(libinput, name, &interface, NULL);
+
+	plugin->parent = p;
+	plugin->register_called = false;
+	plugin->version = LIBINPUT_PLUGIN_VERSION;
+	plugin->device_new_refid = LUA_NOREF;
+	plugin->timer_expired_refid = LUA_NOREF;
+	list_init(&plugin->evdev_devices);
+
+	_cleanup_(lua_closep) lua_State *L =
+		libinput_lua_plugin_init_lua(libinput, plugin);
+	if (!L) {
+		plugin_log_bug(plugin->parent,
+			       "Failed to create lua state for %s\n",
+			       name);
+		libinput_plugin_unregister(p);
+		return NULL;
+	}
+
+	int ret = luaL_loadfile(L, path);
+	if (ret == LUA_OK) {
+		plugin->L = steal(&L);
+
+		libinput_plugin_set_user_data(p, steal(&plugin));
+		return p;
+	} else {
+		const char *lua_error = lua_tostring(L, -1);
+		const char *error = lua_error;
+		if (!error) {
+			switch (ret) {
+			case LUA_ERRMEM:
+				error = "out of memory";
+				break;
+			case LUA_ERRFILE:
+				error = "file not found or not readable";
+				break;
+			case LUA_ERRSYNTAX:
+				error = "syntax error";
+				break;
+			default:
+				break;
+			}
+		}
+
+		if (ret == LUA_ERRSYNTAX &&
+		    log_is_logged(libinput, LIBINPUT_LOG_PRIORITY_DEBUG)) {
+			luaL_traceback(L, L, NULL, 1);
+			for (int i = -1; i > -4; i--) {
+				const char *msg = lua_tostring(L, i);
+				if (!msg)
+					break;
+				log_debug(libinput, "%s %s\n", name, msg);
+			}
+			lua_pop(L, 1); /* traceback */
+		}
+
+		plugin_log_bug(plugin->parent, "Failed to load %s: %s\n", path, error);
+
+		lua_pop(L, 1); /* the lua_error message */
+
+		libinput_plugin_unregister(p);
+
+		return NULL;
+	}
+}
diff -pruN 1.28.1-1/src/libinput-plugin-lua.h 1.30.0-1/src/libinput-plugin-lua.h
--- 1.28.1-1/src/libinput-plugin-lua.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-lua.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,32 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include "libinput-plugin.h"
+#include "libinput.h"
+
+struct libinput_plugin *
+libinput_lua_plugin_new_from_path(struct libinput *libinput, const char *path);
diff -pruN 1.28.1-1/src/libinput-plugin-mouse-wheel-lowres.c 1.30.0-1/src/libinput-plugin-mouse-wheel-lowres.c
--- 1.28.1-1/src/libinput-plugin-mouse-wheel-lowres.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-mouse-wheel-lowres.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,123 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <libevdev/libevdev.h>
+
+#include "evdev.h"
+#include "libinput-plugin-mouse-wheel-lowres.h"
+#include "libinput-plugin.h"
+#include "src/evdev-frame.h"
+
+static void
+wheel_plugin_device_new(struct libinput_plugin *libinput_plugin,
+			struct libinput_device *device,
+			struct libevdev *libevdev,
+			struct udev_device *udev_device)
+{
+	struct evdev_device *evdev = evdev_device(device);
+
+	if (libevdev_has_event_code(libevdev, EV_REL, REL_WHEEL_HI_RES) ||
+	    libevdev_has_event_code(libevdev, EV_REL, REL_HWHEEL_HI_RES))
+		return;
+
+	if (libevdev_has_event_code(libevdev, EV_REL, REL_WHEEL) ||
+	    libevdev_has_event_code(libevdev, EV_REL, REL_HWHEEL))
+		evdev_log_info(evdev,
+			       "emulating high-resolution scroll wheel events.\n");
+
+	if (libevdev_has_event_code(libevdev, EV_REL, REL_WHEEL))
+		libevdev_enable_event_code(libevdev, EV_REL, REL_WHEEL_HI_RES, NULL);
+
+	if (libevdev_has_event_code(libevdev, EV_REL, REL_HWHEEL))
+		libevdev_enable_event_code(libevdev, EV_REL, REL_HWHEEL_HI_RES, NULL);
+
+	libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_WHEEL);
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_HWHEEL);
+
+	/* A device may have those disabled via a quirk but we just re-enabled it
+	 * above. Make sure we get those events too to filter them out */
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_WHEEL_HI_RES);
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_HWHEEL_HI_RES);
+}
+
+static void
+wheel_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
+			 struct libinput_device *device,
+			 struct evdev_frame *frame)
+{
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+
+	_unref_(evdev_frame) *filtered_frame = evdev_frame_new(nevents + 2);
+	for (size_t i = 0; i < nevents; i++) {
+		struct evdev_event *e = &events[i];
+
+		switch (evdev_usage_enum(e->usage)) {
+		case EVDEV_REL_WHEEL_HI_RES:
+		case EVDEV_REL_HWHEEL_HI_RES:
+			/* In the uncommon case that our device sends high-res events
+			 * filter those out. This can happen on devices that have the
+			 * highres scroll axes disabled via quirks. The device still
+			 * sends events so when we re-enable the axis in
+			 * wheel_plugin_device_new we get the device events again,
+			 * effectively duplicating the high resolution scroll events.
+			 */
+			break;
+		case EVDEV_REL_WHEEL:
+			evdev_frame_append(filtered_frame, e, 1);
+			evdev_frame_append_one(filtered_frame,
+					       evdev_usage_from(EVDEV_REL_WHEEL_HI_RES),
+					       e->value * 120);
+			break;
+		case EVDEV_REL_HWHEEL:
+			evdev_frame_append(filtered_frame, e, 1);
+			evdev_frame_append_one(
+				filtered_frame,
+				evdev_usage_from(EVDEV_REL_HWHEEL_HI_RES),
+				e->value * 120);
+			break;
+		default:
+			evdev_frame_append(filtered_frame, e, 1);
+			break;
+		}
+	}
+
+	evdev_frame_set(frame,
+			evdev_frame_get_events(filtered_frame, NULL),
+			evdev_frame_get_count(filtered_frame));
+}
+
+static const struct libinput_plugin_interface interface = {
+	.device_new = wheel_plugin_device_new,
+	.evdev_frame = wheel_plugin_evdev_frame,
+};
+
+void
+libinput_mouse_plugin_wheel_lowres(struct libinput *libinput)
+{
+	_unref_(libinput_plugin) *p =
+		libinput_plugin_new(libinput, "mouse-wheel-lowres", &interface, NULL);
+}
diff -pruN 1.28.1-1/src/libinput-plugin-mouse-wheel-lowres.h 1.30.0-1/src/libinput-plugin-mouse-wheel-lowres.h
--- 1.28.1-1/src/libinput-plugin-mouse-wheel-lowres.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-mouse-wheel-lowres.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "libinput-plugin.h"
+#include "libinput.h"
+
+void
+libinput_mouse_plugin_wheel_lowres(struct libinput *libinput);
diff -pruN 1.28.1-1/src/libinput-plugin-mouse-wheel.c 1.30.0-1/src/libinput-plugin-mouse-wheel.c
--- 1.28.1-1/src/libinput-plugin-mouse-wheel.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-mouse-wheel.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,653 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2013 Jonas Ådahl
+ * Copyright © 2013-2017 Red Hat, Inc.
+ * Copyright © 2017 James Ye <jye836@gmail.com>
+ * Copyright © 2021-2025 José Expósito
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <libevdev/libevdev.h>
+
+#include "evdev-fallback.h"
+#include "evdev.h"
+#include "libinput-feature.h"
+#include "libinput-log.h"
+#include "libinput-plugin-mouse-wheel.h"
+#include "libinput-plugin.h"
+#include "libinput-private.h"
+#include "libinput-util.h"
+
+#define ACC_V120_TRIGGER_THRESHOLD 30  /* 1/4 of a wheel detent */
+#define ACC_V120_THRESHOLD 47 /* Good for both high-ish multipliers (8/120) and the rest of the mice (30/120, 40/120, etc) */
+#define WHEEL_SCROLL_TIMEOUT ms2us(500)
+
+enum wheel_state {
+	WHEEL_STATE_NONE,
+	WHEEL_STATE_ACCUMULATING_SCROLL,
+	WHEEL_STATE_SCROLLING,
+};
+
+enum wheel_direction {
+	WHEEL_DIR_UNKNOW,
+	WHEEL_DIR_VPOS,
+	WHEEL_DIR_VNEG,
+	WHEEL_DIR_HPOS,
+	WHEEL_DIR_HNEG,
+};
+
+enum wheel_event {
+	WHEEL_EVENT_SCROLL_ACCUMULATED,
+	WHEEL_EVENT_SCROLL,
+	WHEEL_EVENT_SCROLL_TIMEOUT,
+	WHEEL_EVENT_SCROLL_DIR_CHANGED,
+};
+
+enum ignore_strategy {
+	MAYBE = 1,         /* use heuristics but don't yet accumulate */
+	ACCUMULATE,        /* accumulate scroll wheel events */
+	ALWAYS_ACCUMULATE, /* always accumulate wheel events */
+};
+
+struct plugin_device {
+	struct list link;
+	struct plugin_data *parent;
+	struct libinput_device *device;
+
+	enum wheel_state state;
+	struct device_coords lo_res;
+	struct device_coords hi_res;
+	bool hi_res_event_received;
+	struct libinput_plugin_timer *scroll_timer;
+	enum wheel_direction dir;
+	enum ignore_strategy ignore_small_hi_res_movements;
+	int min_movement;
+
+	struct ratelimit hires_warning_limit;
+
+	bool want_feature_disabled;
+};
+
+struct plugin_data {
+	struct libinput_plugin *plugin;
+	struct list devices;
+};
+
+static inline const char *
+wheel_state_to_str(enum wheel_state state)
+{
+	switch (state) {
+	CASE_RETURN_STRING(WHEEL_STATE_NONE);
+	CASE_RETURN_STRING(WHEEL_STATE_ACCUMULATING_SCROLL);
+	CASE_RETURN_STRING(WHEEL_STATE_SCROLLING);
+	}
+	return NULL;
+}
+
+static inline const char *
+wheel_event_to_str(enum wheel_event event)
+{
+	switch (event) {
+	CASE_RETURN_STRING(WHEEL_EVENT_SCROLL_ACCUMULATED);
+	CASE_RETURN_STRING(WHEEL_EVENT_SCROLL);
+	CASE_RETURN_STRING(WHEEL_EVENT_SCROLL_TIMEOUT);
+	CASE_RETURN_STRING(WHEEL_EVENT_SCROLL_DIR_CHANGED);
+	}
+	return NULL;
+}
+
+static inline void
+log_wheel_bug(struct plugin_device *pd, enum wheel_event event)
+{
+	plugin_log_bug_libinput(pd->parent->plugin,
+				"invalid wheel event %s in state %s\n",
+				wheel_event_to_str(event),
+				wheel_state_to_str(pd->state));
+}
+
+static inline void
+wheel_set_scroll_timer(struct plugin_device *pd, uint64_t time)
+{
+	if (!pd->scroll_timer)
+		return;
+
+	libinput_plugin_timer_set(pd->scroll_timer, time + WHEEL_SCROLL_TIMEOUT);
+}
+
+static inline void
+wheel_cancel_scroll_timer(struct plugin_device *pd)
+{
+	if (!pd->scroll_timer)
+		return;
+
+	libinput_plugin_timer_cancel(pd->scroll_timer);
+}
+
+static inline void
+wheel_maybe_disable(struct plugin_device *device)
+{
+	if (device->state != WHEEL_STATE_NONE)
+		return;
+
+	if (device->want_feature_disabled) {
+		plugin_log_debug(device->parent->plugin,
+				 "%s: disabled wheel debouncing on request\n",
+				 libinput_device_get_name(device->device));
+		libinput_plugin_enable_device_event_frame(device->parent->plugin,
+							  device->device,
+							  false);
+		libinput_plugin_timer_cancel(device->scroll_timer);
+		device->scroll_timer =
+			libinput_plugin_timer_unref(device->scroll_timer);
+	}
+}
+
+static void
+wheel_handle_event_on_state_none(struct plugin_device *pd,
+				 enum wheel_event event,
+				 uint64_t time)
+{
+	switch (event) {
+	case WHEEL_EVENT_SCROLL:
+		switch (pd->ignore_small_hi_res_movements) {
+		case ACCUMULATE:
+		case ALWAYS_ACCUMULATE:
+			pd->state = WHEEL_STATE_ACCUMULATING_SCROLL;
+			break;
+		case MAYBE:
+			pd->state = WHEEL_STATE_SCROLLING;
+			break;
+		}
+		break;
+	case WHEEL_EVENT_SCROLL_DIR_CHANGED:
+		break;
+	case WHEEL_EVENT_SCROLL_ACCUMULATED:
+	case WHEEL_EVENT_SCROLL_TIMEOUT:
+		log_wheel_bug(pd, event);
+		break;
+	}
+}
+
+static void
+wheel_handle_event_on_state_accumulating_scroll(struct plugin_device *pd,
+						enum wheel_event event,
+						uint64_t time)
+{
+	switch (event) {
+	case WHEEL_EVENT_SCROLL_ACCUMULATED:
+		pd->state = WHEEL_STATE_SCROLLING;
+		wheel_set_scroll_timer(pd, time);
+		break;
+	case WHEEL_EVENT_SCROLL:
+		/* Ignore scroll while accumulating deltas */
+		break;
+	case WHEEL_EVENT_SCROLL_DIR_CHANGED:
+		pd->state = WHEEL_STATE_NONE;
+		break;
+	case WHEEL_EVENT_SCROLL_TIMEOUT:
+		log_wheel_bug(pd, event);
+		break;
+	}
+}
+
+static void
+wheel_handle_event_on_state_scrolling(struct plugin_device *pd,
+				      enum wheel_event event,
+				      uint64_t time)
+{
+	switch (event) {
+	case WHEEL_EVENT_SCROLL:
+		wheel_set_scroll_timer(pd, time);
+		break;
+	case WHEEL_EVENT_SCROLL_TIMEOUT:
+		pd->state = WHEEL_STATE_NONE;
+		break;
+	case WHEEL_EVENT_SCROLL_DIR_CHANGED:
+		wheel_cancel_scroll_timer(pd);
+		pd->state = WHEEL_STATE_NONE;
+		break;
+	case WHEEL_EVENT_SCROLL_ACCUMULATED:
+		log_wheel_bug(pd, event);
+		break;
+	}
+}
+
+static void
+wheel_handle_event(struct plugin_device *pd, enum wheel_event event, uint64_t time)
+{
+	enum wheel_state oldstate = pd->state;
+
+	switch (oldstate) {
+	case WHEEL_STATE_NONE:
+		wheel_handle_event_on_state_none(pd, event, time);
+		break;
+	case WHEEL_STATE_ACCUMULATING_SCROLL:
+		wheel_handle_event_on_state_accumulating_scroll(pd, event, time);
+		break;
+	case WHEEL_STATE_SCROLLING:
+		wheel_handle_event_on_state_scrolling(pd, event, time);
+		break;
+	}
+
+	if (oldstate != pd->state) {
+		plugin_log_debug(pd->parent->plugin,
+				 "wheel: %s → %s → %s\n",
+				 wheel_state_to_str(oldstate),
+				 wheel_event_to_str(event),
+				 wheel_state_to_str(pd->state));
+	}
+}
+
+static void
+wheel_remove_scroll_events(struct evdev_frame *frame)
+{
+	size_t nevents;
+	_unref_(evdev_frame) *copy = evdev_frame_clone(frame);
+	struct evdev_event *events = evdev_frame_get_events(copy, &nevents);
+
+	evdev_frame_reset(frame);
+
+	for (size_t i = 0; i < nevents; i++) {
+		struct evdev_event *e = &events[i];
+
+		switch (evdev_usage_enum(e->usage)) {
+		case EVDEV_REL_WHEEL:
+		case EVDEV_REL_WHEEL_HI_RES:
+		case EVDEV_REL_HWHEEL:
+		case EVDEV_REL_HWHEEL_HI_RES:
+			/* Do not append scroll events */
+			break;
+		default:
+			evdev_frame_append(frame, e, 1);
+			break;
+		}
+	}
+}
+
+static void
+wheel_queue_scroll_events(struct plugin_device *pd, struct evdev_frame *frame)
+{
+	if (pd->hi_res.y != 0) {
+		evdev_frame_append_one(frame,
+				       evdev_usage_from(EVDEV_REL_WHEEL_HI_RES),
+				       pd->hi_res.y);
+		pd->hi_res.y = 0;
+	}
+
+	if (pd->lo_res.y != 0) {
+		evdev_frame_append_one(frame,
+				       evdev_usage_from(EVDEV_REL_WHEEL),
+				       pd->lo_res.y);
+		pd->lo_res.y = 0;
+	}
+
+	if (pd->hi_res.x != 0) {
+		evdev_frame_append_one(frame,
+				       evdev_usage_from(EVDEV_REL_HWHEEL_HI_RES),
+				       pd->hi_res.x);
+		pd->hi_res.x = 0;
+	}
+
+	if (pd->lo_res.x != 0) {
+		evdev_frame_append_one(frame,
+				       evdev_usage_from(EVDEV_REL_HWHEEL),
+				       pd->lo_res.x);
+		pd->lo_res.x = 0;
+	}
+}
+
+static void
+wheel_handle_state_none(struct plugin_device *pd,
+			struct evdev_frame *frame,
+			uint64_t time)
+{
+}
+
+static void
+wheel_handle_state_accumulating_scroll(struct plugin_device *pd,
+				       struct evdev_frame *frame,
+				       uint64_t time)
+{
+	wheel_remove_scroll_events(frame);
+
+	if (abs(pd->hi_res.x) > pd->min_movement ||
+	    abs(pd->hi_res.y) > pd->min_movement) {
+		wheel_handle_event(pd, WHEEL_EVENT_SCROLL_ACCUMULATED, time);
+		wheel_queue_scroll_events(pd, frame);
+	}
+}
+
+static void
+wheel_handle_state_scrolling(struct plugin_device *pd,
+			     struct evdev_frame *frame,
+			     uint64_t time)
+{
+	wheel_remove_scroll_events(frame);
+	wheel_queue_scroll_events(pd, frame);
+}
+
+static void
+wheel_handle_direction_change(struct plugin_device *pd,
+			      struct evdev_event *e,
+			      uint64_t time)
+{
+	enum wheel_direction new_dir = WHEEL_DIR_UNKNOW;
+
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_REL_WHEEL_HI_RES:
+		new_dir = (e->value > 0) ? WHEEL_DIR_VPOS : WHEEL_DIR_VNEG;
+		break;
+	case EVDEV_REL_HWHEEL_HI_RES:
+		new_dir = (e->value > 0) ? WHEEL_DIR_HPOS : WHEEL_DIR_HNEG;
+		break;
+	default:
+		return;
+	}
+
+	if (new_dir != WHEEL_DIR_UNKNOW && new_dir != pd->dir) {
+		pd->dir = new_dir;
+		wheel_handle_event(pd, WHEEL_EVENT_SCROLL_DIR_CHANGED, time);
+	}
+}
+
+static inline void
+wheel_update_strategy(struct plugin_device *pd, int32_t value)
+{
+	if (pd->ignore_small_hi_res_movements != ALWAYS_ACCUMULATE) {
+		pd->min_movement = min(pd->min_movement, abs(value));
+
+		/* Only if a wheel sends movements less than the trigger threshold
+		 * activate the accumulation and debouncing of scroll directions, etc.
+		 */
+		if (pd->ignore_small_hi_res_movements == MAYBE &&
+		    pd->min_movement < ACC_V120_TRIGGER_THRESHOLD)
+			pd->ignore_small_hi_res_movements = ACCUMULATE;
+	}
+}
+
+static void
+wheel_process_relative(struct plugin_device *pd, struct evdev_event *e, uint64_t time)
+{
+	switch (evdev_usage_enum(e->usage)) {
+	case EVDEV_REL_WHEEL:
+		pd->lo_res.y += e->value;
+		wheel_handle_event(pd, WHEEL_EVENT_SCROLL, time);
+		break;
+	case EVDEV_REL_HWHEEL:
+		pd->lo_res.x += e->value;
+		wheel_handle_event(pd, WHEEL_EVENT_SCROLL, time);
+		break;
+	case EVDEV_REL_WHEEL_HI_RES:
+		pd->hi_res.y += e->value;
+		pd->hi_res_event_received = true;
+		wheel_update_strategy(pd, e->value);
+		wheel_handle_direction_change(pd, e, time);
+		wheel_handle_event(pd, WHEEL_EVENT_SCROLL, time);
+		break;
+	case EVDEV_REL_HWHEEL_HI_RES:
+		pd->hi_res.x += e->value;
+		pd->hi_res_event_received = true;
+		wheel_update_strategy(pd, e->value);
+		wheel_handle_direction_change(pd, e, time);
+		wheel_handle_event(pd, WHEEL_EVENT_SCROLL, time);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+wheel_handle_state(struct plugin_device *pd, struct evdev_frame *frame, uint64_t time)
+{
+	struct evdev_device *evdev = evdev_device(pd->device);
+
+	if (!pd->hi_res_event_received && (pd->lo_res.x != 0 || pd->lo_res.y != 0)) {
+		evdev_log_bug_kernel_ratelimit(
+			evdev,
+			&pd->hires_warning_limit,
+			"device supports high-resolution scroll but only low-resolution events have been received.\n"
+			"See %s/incorrectly-enabled-hires.html for details\n",
+			HTTP_DOC_LINK);
+		pd->hi_res.x = pd->lo_res.x * 120;
+		pd->hi_res.y = pd->lo_res.y * 120;
+	}
+
+	switch (pd->state) {
+	case WHEEL_STATE_NONE:
+		wheel_handle_state_none(pd, frame, time);
+		break;
+	case WHEEL_STATE_ACCUMULATING_SCROLL:
+		wheel_handle_state_accumulating_scroll(pd, frame, time);
+		break;
+	case WHEEL_STATE_SCROLLING:
+		wheel_handle_state_scrolling(pd, frame, time);
+		break;
+	}
+
+	wheel_maybe_disable(pd);
+}
+
+static void
+wheel_on_scroll_timer_timeout(struct libinput_plugin *plugin, uint64_t now, void *data)
+{
+	struct plugin_device *pd = data;
+
+	wheel_handle_event(pd, WHEEL_EVENT_SCROLL_TIMEOUT, now);
+}
+
+static struct plugin_device *
+wheel_plugin_device_create(struct libinput_plugin *libinput_plugin,
+			   struct plugin_data *plugin,
+			   struct libinput_device *device)
+{
+	if (libinput_device_is_virtual(device))
+		return NULL;
+
+	struct plugin_device *pd = zalloc(sizeof(*pd));
+	pd->parent = plugin;
+	pd->device = libinput_device_ref(device);
+	pd->state = WHEEL_STATE_NONE;
+	pd->dir = WHEEL_DIR_UNKNOW;
+	pd->min_movement = ACC_V120_THRESHOLD;
+	ratelimit_init(&pd->hires_warning_limit, s2us(24 * 60 * 60), 1);
+
+	if (libinput_device_has_model_quirk(device, QUIRK_MODEL_SCROLL_ON_MIDDLE_CLICK))
+		pd->ignore_small_hi_res_movements = ALWAYS_ACCUMULATE;
+	else
+		pd->ignore_small_hi_res_movements = MAYBE;
+
+	pd->scroll_timer =
+		libinput_plugin_timer_new(libinput_plugin,
+					  libinput_device_get_sysname(device),
+					  wheel_on_scroll_timer_timeout,
+					  pd);
+	return pd;
+}
+
+static void
+wheel_plugin_device_destroy(struct plugin_device *pd)
+{
+	list_remove(&pd->link);
+
+	if (pd->scroll_timer) {
+		wheel_cancel_scroll_timer(pd);
+		libinput_plugin_timer_unref(pd->scroll_timer);
+	}
+
+	libinput_device_unref(pd->device);
+
+	free(pd);
+}
+
+static void
+wheel_plugin_destroy(struct libinput_plugin *libinput_plugin)
+{
+	struct plugin_data *data = libinput_plugin_get_user_data(libinput_plugin);
+
+	struct plugin_device *pd;
+	list_for_each_safe(pd, &data->devices, link) {
+		wheel_plugin_device_destroy(pd);
+	}
+
+	free(data);
+}
+
+static void
+wheel_plugin_device_new(struct libinput_plugin *libinput_plugin,
+			struct libinput_device *device,
+			struct libevdev *libevdev,
+			struct udev_device *udev_device)
+{
+	if (!libevdev_has_event_code(libevdev, EV_REL, REL_WHEEL_HI_RES) &&
+	    !libevdev_has_event_code(libevdev, EV_REL, REL_HWHEEL_HI_RES))
+		return;
+
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd =
+		wheel_plugin_device_create(libinput_plugin, plugin, device);
+	if (!pd)
+		return;
+
+	list_take_append(&plugin->devices, pd, link);
+
+	libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
+
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_WHEEL);
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_WHEEL_HI_RES);
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_HWHEEL);
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_REL_HWHEEL_HI_RES);
+}
+
+static void
+wheel_plugin_device_added(struct libinput_plugin *libinput_plugin,
+			  struct libinput_device *device)
+{
+	if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER))
+		return;
+
+	/* For any non-pointer device: check if we happened to have added
+	 * it during device_new and if so, remove it. We only want to enable
+	 * this on devices that have a wheel *and* are a pointer device */
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd;
+
+	list_for_each_safe(pd, &plugin->devices, link) {
+		if (pd->device == device) {
+			wheel_plugin_device_destroy(pd);
+			return;
+		}
+	}
+}
+
+static void
+wheel_plugin_device_removed(struct libinput_plugin *libinput_plugin,
+			    struct libinput_device *device)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd;
+
+	list_for_each_safe(pd, &plugin->devices, link) {
+		if (pd->device == device) {
+			wheel_plugin_device_destroy(pd);
+			return;
+		}
+	}
+}
+
+static void
+wheel_handle_frame(struct plugin_device *pd, struct evdev_frame *frame, uint64_t time)
+{
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+
+	for (size_t i = 0; i < nevents; i++) {
+		struct evdev_event *e = &events[i];
+		uint16_t type = evdev_event_type(e);
+
+		switch (type) {
+		case EV_REL:
+			wheel_process_relative(pd, e, time);
+			break;
+		case EV_SYN:
+			wheel_handle_state(pd, frame, time);
+			break;
+		}
+	}
+}
+
+static void
+wheel_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
+			 struct libinput_device *device,
+			 struct evdev_frame *frame)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd;
+	uint64_t time = evdev_frame_get_time(frame);
+
+	list_for_each(pd, &plugin->devices, link) {
+		if (pd->device == device) {
+			wheel_handle_frame(pd, frame, time);
+			break;
+		}
+	}
+}
+
+static void
+wheel_plugin_feature_disabled(struct libinput_plugin *libinput_plugin,
+			      struct libinput_device *device,
+			      enum libinput_feature feature)
+{
+	if (feature != LIBINPUT_FEATURE_WHEEL_DEBOUNCING)
+		return;
+
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd;
+	list_for_each(pd, &plugin->devices, link) {
+		if (pd->device == device) {
+			pd->want_feature_disabled = true;
+			return;
+		}
+	}
+}
+
+static const struct libinput_plugin_interface interface = {
+	.run = NULL,
+	.destroy = wheel_plugin_destroy,
+	.device_new = wheel_plugin_device_new,
+	.device_ignored = wheel_plugin_device_removed,
+	.device_added = wheel_plugin_device_added,
+	.device_removed = wheel_plugin_device_removed,
+	.evdev_frame = wheel_plugin_evdev_frame,
+	.feature_disabled = wheel_plugin_feature_disabled,
+};
+
+void
+libinput_mouse_plugin_wheel(struct libinput *libinput)
+{
+	struct plugin_data *plugin = zalloc(sizeof(*plugin));
+	list_init(&plugin->devices);
+
+	_unref_(libinput_plugin) *p =
+		libinput_plugin_new(libinput, "mouse-wheel", &interface, plugin);
+	plugin->plugin = p;
+}
diff -pruN 1.28.1-1/src/libinput-plugin-mouse-wheel.h 1.30.0-1/src/libinput-plugin-mouse-wheel.h
--- 1.28.1-1/src/libinput-plugin-mouse-wheel.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-mouse-wheel.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "libinput-plugin.h"
+#include "libinput.h"
+
+void
+libinput_mouse_plugin_wheel(struct libinput *libinput);
diff -pruN 1.28.1-1/src/libinput-plugin-mtdev.c 1.30.0-1/src/libinput-plugin-mtdev.c
--- 1.28.1-1/src/libinput-plugin-mtdev.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-mtdev.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,234 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2013 Jonas Ådahl
+ * Copyright © 2013-2017 Red Hat, Inc.
+ * Copyright © 2017 James Ye <jye836@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <libevdev/libevdev.h>
+#include <mtdev-plumbing.h>
+#include <mtdev.h>
+
+#include "util-list.h"
+#include "util-mem.h"
+#include "util-strings.h"
+
+#include "evdev-frame.h"
+#include "libinput-log.h"
+#include "libinput-plugin-mtdev.h"
+#include "libinput-plugin.h"
+#include "libinput-util.h"
+
+struct plugin_device {
+	struct list link;
+	struct libinput_device *device;
+
+	struct mtdev *mtdev;
+};
+
+struct plugin_data {
+	struct list devices;
+};
+
+static void
+plugin_device_destroy(struct plugin_device *device)
+{
+	libinput_device_unref(device->device);
+	list_remove(&device->link);
+	mtdev_close_delete(device->mtdev);
+	free(device);
+}
+
+DEFINE_DESTROY_CLEANUP_FUNC(plugin_device);
+
+static void
+plugin_data_destroy(void *d)
+{
+	struct plugin_data *data = d;
+
+	struct plugin_device *device;
+	list_for_each_safe(device, &data->devices, link) {
+		plugin_device_destroy(device);
+	}
+
+	free(data);
+}
+
+DEFINE_DESTROY_CLEANUP_FUNC(plugin_data);
+
+static void
+plugin_destroy(struct libinput_plugin *libinput_plugin)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	plugin_data_destroy(plugin);
+}
+
+static void
+mtdev_plugin_device_handle_frame(struct libinput_plugin *libinput_plugin,
+				 struct plugin_device *device,
+				 struct evdev_frame *frame)
+{
+	uint64_t time = evdev_frame_get_time(frame);
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+	for (size_t i = 0; i < nevents; i++) {
+		struct evdev_event *ev = &events[i];
+		struct input_event e = evdev_event_to_input_event(ev, time);
+		mtdev_put_event(device->mtdev, &e);
+	}
+	evdev_frame_reset(frame);
+
+	while (!mtdev_empty(device->mtdev)) {
+		struct input_event e;
+
+		mtdev_get_event(device->mtdev, &e);
+		evdev_frame_append_input_event(frame, &e);
+		if (e.type == EV_SYN && e.code == SYN_REPORT) {
+			evdev_frame_set_time(frame, input_event_time(&e));
+			/* mtdev can theoretically produce multiple frames but I
+			 * dont think it ever does */
+			if (!mtdev_empty(device->mtdev)) {
+				plugin_log_bug(libinput_plugin,
+					       "Didn't expect multiple frames");
+				break;
+			}
+		}
+	}
+}
+
+static void
+mtdev_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
+			 struct libinput_device *device,
+			 struct evdev_frame *frame)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd;
+
+	list_for_each(pd, &plugin->devices, link) {
+		if (pd->device == device) {
+			mtdev_plugin_device_handle_frame(libinput_plugin, pd, frame);
+			break;
+		}
+	}
+}
+
+static int
+mtdev_needed(struct libevdev *evdev)
+{
+	return (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) &&
+		libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y) &&
+		!libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT));
+}
+
+static void
+mtdev_plugin_device_new(struct libinput_plugin *libinput_plugin,
+			struct libinput_device *device,
+			struct libevdev *evdev,
+			struct udev_device *udev)
+{
+	if (!mtdev_needed(evdev))
+		return;
+
+	libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
+
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	_destroy_(plugin_device) *pd = zalloc(sizeof(*pd));
+	pd->device = libinput_device_ref(device);
+	pd->mtdev = mtdev_new();
+	/* Shouldn't ever happen so no need to warn */
+	if (!pd->mtdev) {
+		libevdev_disable_event_code(evdev, EV_ABS, ABS_MT_POSITION_X);
+		libevdev_disable_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y);
+		return;
+	}
+	mtdev_init(pd->mtdev);
+
+	unsigned int codes[] = {
+		ABS_MT_POSITION_X,  ABS_MT_POSITION_Y,  ABS_MT_TOUCH_MAJOR,
+		ABS_MT_TOUCH_MINOR, ABS_MT_WIDTH_MAJOR, ABS_MT_WIDTH_MINOR,
+		ABS_MT_ORIENTATION,
+	};
+
+	ARRAY_FOR_EACH(codes, code) {
+		const struct input_absinfo *abs = libevdev_get_abs_info(evdev, *code);
+		if (!abs)
+			continue;
+
+		mtdev_set_mt_event(pd->mtdev, *code, abs->value);
+		mtdev_set_abs_minimum(pd->mtdev, *code, abs->minimum);
+		mtdev_set_abs_maximum(pd->mtdev, *code, abs->maximum);
+		mtdev_set_abs_fuzz(pd->mtdev, *code, abs->fuzz);
+		mtdev_set_abs_resolution(pd->mtdev, *code, abs->resolution);
+	}
+
+	/* Let's pretend we have slots */
+	const struct input_absinfo slot = {
+		.minimum = 0,
+		.maximum = 9,
+		.value = 0,
+	};
+	const struct input_absinfo tid = {
+		.minimum = 0,
+		.maximum = 65535,
+		.value = 0,
+	};
+	libevdev_enable_event_code(evdev, EV_ABS, ABS_MT_SLOT, &slot);
+	libevdev_enable_event_code(evdev, EV_ABS, ABS_MT_TRACKING_ID, &tid);
+
+	list_take_append(&plugin->devices, pd, link);
+}
+
+static void
+mtdev_plugin_device_removed(struct libinput_plugin *libinput_plugin,
+			    struct libinput_device *device)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *dev;
+	list_for_each_safe(dev, &plugin->devices, link) {
+		if (dev->device == device) {
+			plugin_device_destroy(dev);
+			return;
+		}
+	}
+}
+
+static const struct libinput_plugin_interface interface = {
+	.run = NULL,
+	.destroy = plugin_destroy,
+	.device_new = mtdev_plugin_device_new,
+	.device_ignored = mtdev_plugin_device_removed,
+	.device_added = NULL,
+	.device_removed = mtdev_plugin_device_removed,
+	.evdev_frame = mtdev_plugin_evdev_frame,
+};
+
+void
+libinput_mtdev_plugin(struct libinput *libinput)
+{
+	_destroy_(plugin_data) *plugin = zalloc(sizeof(*plugin));
+	list_init(&plugin->devices);
+
+	_unref_(libinput_plugin) *p =
+		libinput_plugin_new(libinput, "mtdev", &interface, steal(&plugin));
+}
diff -pruN 1.28.1-1/src/libinput-plugin-mtdev.h 1.30.0-1/src/libinput-plugin-mtdev.h
--- 1.28.1-1/src/libinput-plugin-mtdev.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-mtdev.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "libinput-plugin.h"
+#include "libinput.h"
+
+void
+libinput_mtdev_plugin(struct libinput *libinput);
diff -pruN 1.28.1-1/src/libinput-plugin-private.h 1.30.0-1/src/libinput-plugin-private.h
--- 1.28.1-1/src/libinput-plugin-private.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-private.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include "libinput-plugin.h"
+
+void
+libinput_plugin_run(struct libinput_plugin *plugin);
+
+void
+libinput_plugin_notify_device_new(struct libinput_plugin *plugin,
+				  struct libinput_device *device,
+				  struct libevdev *evdev,
+				  struct udev_device *udev_device);
+
+void
+libinput_plugin_notify_device_added(struct libinput_plugin *plugin,
+				    struct libinput_device *device);
+
+void
+libinput_plugin_notify_device_ignored(struct libinput_plugin *plugin,
+				      struct libinput_device *device);
+
+void
+libinput_plugin_notify_device_removed(struct libinput_plugin *plugin,
+				      struct libinput_device *device);
diff -pruN 1.28.1-1/src/libinput-plugin-system.h 1.30.0-1/src/libinput-plugin-system.h
--- 1.28.1-1/src/libinput-plugin-system.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-system.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,104 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include <libevdev/libevdev.h>
+#include <libudev.h>
+#include <stdbool.h>
+
+#include "util-list.h"
+
+#include "evdev-frame.h"
+#include "libinput.h"
+
+enum libinput_feature;
+struct libinput;
+struct libinput_plugin;
+
+struct libinput_plugin_system {
+	char **directories; /* NULL once loaded == true */
+
+	bool loaded;
+	bool autoload;
+
+	struct list plugins;
+	struct list removed_plugins;
+
+	size_t next_plugin_index; /* sequential index of all plugins */
+};
+
+void
+libinput_plugin_system_init(struct libinput_plugin_system *system);
+
+void
+libinput_plugin_system_autoload(struct libinput *libinput);
+
+void
+libinput_plugin_system_destroy(struct libinput_plugin_system *system);
+
+void
+libinput_plugin_system_run(struct libinput_plugin_system *system);
+
+void
+libinput_plugin_system_register_plugin(struct libinput_plugin_system *system,
+				       struct libinput_plugin *plugin);
+void
+libinput_plugin_system_unregister_plugin(struct libinput_plugin_system *system,
+					 struct libinput_plugin *plugin);
+
+void
+libinput_plugin_system_notify_device_new(struct libinput_plugin_system *system,
+					 struct libinput_device *device,
+					 struct libevdev *evdev,
+					 struct udev_device *udev);
+
+void
+libinput_plugin_system_notify_device_added(struct libinput_plugin_system *system,
+					   struct libinput_device *device);
+
+void
+libinput_plugin_system_notify_device_removed(struct libinput_plugin_system *system,
+					     struct libinput_device *device);
+
+void
+libinput_plugin_system_notify_device_ignored(struct libinput_plugin_system *system,
+					     struct libinput_device *device);
+
+void
+libinput_plugin_system_notify_tablet_tool_configured(
+	struct libinput_plugin_system *system,
+	struct libinput_tablet_tool *tool);
+
+void
+libinput_plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
+					  struct libinput_device *device,
+					  struct evdev_frame *frame);
+
+void
+libinput_plugin_system_notify_device_feature_disabled(
+	struct libinput_plugin_system *system,
+	struct libinput_device *device,
+	enum libinput_feature feature);
diff -pruN 1.28.1-1/src/libinput-plugin-tablet-double-tool.c 1.30.0-1/src/libinput-plugin-tablet-double-tool.c
--- 1.28.1-1/src/libinput-plugin-tablet-double-tool.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-tablet-double-tool.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,388 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <libevdev/libevdev.h>
+
+#include "util-mem.h"
+#include "util-strings.h"
+
+#include "evdev-frame.h"
+#include "libinput-log.h"
+#include "libinput-plugin-tablet-double-tool.h"
+#include "libinput-plugin.h"
+#include "libinput-util.h"
+
+enum {
+	TOOL_PEN_DOWN,
+	TOOL_PEN_UP,
+	TOOL_ERASER_DOWN,
+	TOOL_ERASER_UP,
+	TOOL_DOUBLE_TOOL,
+};
+
+enum tool_filter {
+	SKIP_PEN = bit(1),
+	SKIP_ERASER = bit(2),
+	PEN_IN_PROX = bit(3),
+	PEN_OUT_OF_PROX = bit(4),
+	ERASER_IN_PROX = bit(5),
+	ERASER_OUT_OF_PROX = bit(6),
+};
+
+struct plugin_device {
+	struct list link;
+	struct libinput_device *device;
+	bool ignore_pen;
+	bitmask_t tools_seen;
+
+	int pen_value;
+	int eraser_value;
+};
+
+struct plugin_data {
+	struct list devices;
+};
+
+static void
+plugin_device_destroy(struct plugin_device *device)
+{
+	libinput_device_unref(device->device);
+	list_remove(&device->link);
+	free(device);
+}
+
+static void
+plugin_data_destroy(void *d)
+{
+	struct plugin_data *data = d;
+
+	struct plugin_device *device;
+	list_for_each_safe(device, &data->devices, link) {
+		plugin_device_destroy(device);
+	}
+
+	free(data);
+}
+
+DEFINE_DESTROY_CLEANUP_FUNC(plugin_data);
+
+static void
+plugin_destroy(struct libinput_plugin *libinput_plugin)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	plugin_data_destroy(plugin);
+}
+
+static struct evdev_frame *
+double_tool_plugin_filter_frame(struct libinput_plugin *plugin,
+				struct evdev_frame *frame_in,
+				enum tool_filter filter)
+{
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame_in, &nevents);
+
+	/* +2 because we may add BTN_TOOL_PEN and BTN_TOOL_RUBBER */
+	struct evdev_frame *frame_out = evdev_frame_new(nevents + 2);
+	evdev_frame_set_time(frame_out, evdev_frame_get_time(frame_in));
+
+	for (size_t i = 0; i < nevents; i++) {
+		struct evdev_event *event = &events[i];
+
+		switch (evdev_usage_enum(event->usage)) {
+		case EVDEV_BTN_TOOL_PEN:
+		case EVDEV_BTN_TOOL_RUBBER:
+			/* skip */
+			break;
+		default:
+			evdev_frame_append(frame_out, event, 1);
+		}
+	}
+
+	if (filter & (PEN_IN_PROX | PEN_OUT_OF_PROX)) {
+		evdev_frame_append_one(frame_out,
+				       evdev_usage_from(EVDEV_BTN_TOOL_PEN),
+				       (filter & PEN_IN_PROX) ? 1 : 0);
+	}
+	if (filter & (ERASER_IN_PROX | ERASER_OUT_OF_PROX)) {
+		evdev_frame_append_one(frame_out,
+				       evdev_usage_from(EVDEV_BTN_TOOL_RUBBER),
+				       (filter & ERASER_IN_PROX) ? 1 : 0);
+	}
+
+	return frame_out;
+}
+
+/* Kernel tools are supposed to be mutually exclusive, but we may have
+ * two bits set due to firmware/kernel bugs.
+ * Two cases that have been seen in the wild:
+ * - BTN_TOOL_PEN on proximity in, followed by
+ *   BTN_TOOL_RUBBER later, see #259
+ *   -> We force a prox-out of the pen, trigger prox-in for eraser
+ * - BTN_TOOL_RUBBER on proximity in, but BTN_TOOL_PEN when
+ *   the tip is down, see #702.
+ *   -> We ignore BTN_TOOL_PEN
+ * In both cases the eraser is what we want, so we bias
+ * towards that.
+ */
+static void
+double_tool_plugin_device_handle_frame(struct libinput_plugin *libinput_plugin,
+				       struct plugin_device *device,
+				       struct evdev_frame *frame)
+{
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+
+	const struct evdev_event *eraser_toggle = NULL;
+	const struct evdev_event *pen_toggle = NULL;
+
+	for (size_t i = 0; i < nevents; i++) {
+		struct evdev_event *event = &events[i];
+
+		switch (evdev_usage_enum(event->usage)) {
+		case EVDEV_BTN_TOOL_RUBBER:
+			eraser_toggle = event;
+			device->eraser_value = event->value;
+			break;
+		case EVDEV_BTN_TOOL_PEN:
+			pen_toggle = event;
+			device->pen_value = event->value;
+			break;
+		default:
+			break;
+		}
+	}
+
+	bool eraser_is_down = !!device->eraser_value;
+	bool pen_is_down = !!device->pen_value;
+	bool eraser_toggled = eraser_toggle != NULL;
+	bool pen_toggled = pen_toggle != NULL;
+
+#ifdef EVENT_DEBUGGING
+	plugin_log_debug(libinput_plugin,
+			 "device %s: tool state: pen:%s eraser:%s\n",
+			 libinput_device_get_name(device->device),
+			 pen_toggled   ? (pen_is_down ? "↓" : "↑")
+			 : pen_is_down ? "|"
+				       : ".",
+			 eraser_toggled   ? (eraser_is_down ? "↓" : "↑")
+			 : eraser_is_down ? "|"
+					  : ".");
+#endif
+
+	if (!bitmask_bit_is_set(device->tools_seen, TOOL_DOUBLE_TOOL)) {
+		bitmask_t tool_mask = bitmask_from_bits(TOOL_PEN_DOWN,
+							TOOL_PEN_UP,
+							TOOL_ERASER_DOWN,
+							TOOL_ERASER_UP);
+		if (eraser_toggled) {
+			if (eraser_is_down)
+				bitmask_set_bit(&device->tools_seen, TOOL_ERASER_DOWN);
+			else
+				bitmask_set_bit(&device->tools_seen, TOOL_ERASER_UP);
+		}
+		if (pen_toggled) {
+			if (pen_is_down)
+				bitmask_set_bit(&device->tools_seen, TOOL_PEN_DOWN);
+			else
+				bitmask_set_bit(&device->tools_seen, TOOL_PEN_UP);
+		}
+		/* If we successfully get all four tool events without
+		 * a doubled-up tool, assume the device is sane and
+		 * unregister this device */
+		if (bitmask_all(device->tools_seen, tool_mask)) {
+			plugin_log_debug(
+				libinput_plugin,
+				"device %s: device is fine, unregistering device\n",
+				libinput_device_get_name(device->device));
+			libinput_plugin_enable_device_event_frame(libinput_plugin,
+								  device->device,
+								  false);
+			plugin_device_destroy(device);
+			return;
+		}
+	}
+
+	/* rubber after pen */
+	if (eraser_toggled) {
+		if (eraser_is_down && pen_is_down) {
+			if (!pen_toggled) {
+				_unref_(evdev_frame) *pen_out_of_prox =
+					double_tool_plugin_filter_frame(
+						libinput_plugin,
+						frame,
+						SKIP_ERASER | PEN_OUT_OF_PROX);
+				libinput_plugin_prepend_evdev_frame(libinput_plugin,
+								    device->device,
+								    pen_out_of_prox);
+			}
+
+			_unref_(evdev_frame) *eraser_in_prox =
+				double_tool_plugin_filter_frame(libinput_plugin,
+								frame,
+								SKIP_PEN |
+									ERASER_IN_PROX);
+
+			libinput_plugin_prepend_evdev_frame(libinput_plugin,
+							    device->device,
+							    eraser_in_prox);
+			device->ignore_pen = true;
+
+			bitmask_set_bit(&device->tools_seen, TOOL_DOUBLE_TOOL);
+
+			/* discard the original frame */
+			evdev_frame_reset(frame);
+
+			return;
+		} else if (!eraser_is_down) {
+			_unref_(evdev_frame) *eraser_out_of_prox =
+				double_tool_plugin_filter_frame(
+					libinput_plugin,
+					frame,
+					SKIP_PEN | ERASER_OUT_OF_PROX);
+
+			libinput_plugin_prepend_evdev_frame(libinput_plugin,
+							    device->device,
+							    eraser_out_of_prox);
+
+			/* Only revert back to the pen if the pen was actually toggled
+			 * in this frame, otherwise it's just still set from before */
+			if (pen_toggled && pen_is_down) {
+				_unref_(evdev_frame) *pen_in_prox =
+					double_tool_plugin_filter_frame(
+						libinput_plugin,
+						frame,
+						SKIP_ERASER | PEN_IN_PROX);
+				libinput_plugin_prepend_evdev_frame(libinput_plugin,
+								    device->device,
+								    pen_in_prox);
+			}
+
+			device->ignore_pen = false;
+
+			/* discard the original frame */
+			evdev_frame_reset(frame);
+
+			return;
+		}
+	}
+
+	/* pen after rubber */
+	if (pen_toggled && eraser_is_down) {
+		device->ignore_pen = true;
+	}
+
+	if (device->ignore_pen) {
+		_unref_(evdev_frame) *frame_out =
+			double_tool_plugin_filter_frame(libinput_plugin,
+							frame,
+							SKIP_PEN);
+		size_t out_nevents;
+		evdev_frame_set(frame,
+				evdev_frame_get_events(frame_out, &out_nevents),
+				nevents);
+		bitmask_set_bit(&device->tools_seen, TOOL_DOUBLE_TOOL);
+	} else if (pen_is_down) {
+		_unref_(evdev_frame) *frame_out =
+			double_tool_plugin_filter_frame(libinput_plugin,
+							frame,
+							PEN_IN_PROX);
+		size_t out_nevents;
+		evdev_frame_set(frame,
+				evdev_frame_get_events(frame_out, &out_nevents),
+				nevents);
+	}
+}
+
+static void
+double_tool_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
+			       struct libinput_device *device,
+			       struct evdev_frame *frame)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd;
+
+	list_for_each(pd, &plugin->devices, link) {
+		if (pd->device == device) {
+			double_tool_plugin_device_handle_frame(libinput_plugin,
+							       pd,
+							       frame);
+			break;
+		}
+	}
+}
+
+static void
+double_tool_plugin_device_added(struct libinput_plugin *libinput_plugin,
+				struct libinput_device *device)
+{
+	if (!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_TOOL))
+		return;
+
+	libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_TOOL_PEN);
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_TOOL_RUBBER);
+
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd = zalloc(sizeof(*pd));
+	pd->device = libinput_device_ref(device);
+	list_take_append(&plugin->devices, pd, link);
+}
+
+static void
+double_tool_plugin_device_removed(struct libinput_plugin *libinput_plugin,
+				  struct libinput_device *device)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *dev;
+	list_for_each_safe(dev, &plugin->devices, link) {
+		if (dev->device == device) {
+			plugin_device_destroy(dev);
+			return;
+		}
+	}
+}
+
+static const struct libinput_plugin_interface interface = {
+	.run = NULL,
+	.destroy = plugin_destroy,
+	.device_new = NULL,
+	.device_ignored = NULL,
+	.device_added = double_tool_plugin_device_added,
+	.device_removed = double_tool_plugin_device_removed,
+	.evdev_frame = double_tool_plugin_evdev_frame,
+};
+
+void
+libinput_tablet_plugin_double_tool(struct libinput *libinput)
+{
+	_destroy_(plugin_data) *plugin = zalloc(sizeof(*plugin));
+	list_init(&plugin->devices);
+
+	_unref_(libinput_plugin) *p = libinput_plugin_new(libinput,
+							  "tablet-double-tool",
+							  &interface,
+							  steal(&plugin));
+}
diff -pruN 1.28.1-1/src/libinput-plugin-tablet-double-tool.h 1.30.0-1/src/libinput-plugin-tablet-double-tool.h
--- 1.28.1-1/src/libinput-plugin-tablet-double-tool.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-tablet-double-tool.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "libinput-plugin.h"
+#include "libinput.h"
+
+void
+libinput_tablet_plugin_double_tool(struct libinput *libinput);
diff -pruN 1.28.1-1/src/libinput-plugin-tablet-eraser-button.c 1.30.0-1/src/libinput-plugin-tablet-eraser-button.c
--- 1.28.1-1/src/libinput-plugin-tablet-eraser-button.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-tablet-eraser-button.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,619 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <libevdev/libevdev.h>
+
+#include "util-mem.h"
+#include "util-strings.h"
+
+#include "evdev-frame.h"
+#include "libinput-log.h"
+#include "libinput-plugin-tablet-eraser-button.h"
+#include "libinput-plugin.h"
+#include "libinput-util.h"
+
+static int ERASER_BUTTON_DELAY = 30 * 1000; /* µs */
+
+enum frame_filter_state {
+	DISCARD,
+	PROCESS,
+};
+
+enum eraser_button_state {
+	ERASER_BUTTON_NEUTRAL,
+	ERASER_BUTTON_PEN_PENDING_ERASER,
+	ERASER_BUTTON_BUTTON_HELD_DOWN,
+	ERASER_BUTTON_BUTTON_RELEASED,
+};
+
+enum eraser_button_event {
+	ERASER_EVENT_PEN_ENTERING_PROX,
+	ERASER_EVENT_PEN_LEAVING_PROX,
+	ERASER_EVENT_ERASER_ENTERING_PROX,
+	ERASER_EVENT_ERASER_LEAVING_PROX,
+	ERASER_EVENT_TIMEOUT,
+};
+
+static const char *
+eraser_button_state_str(enum eraser_button_state state)
+{
+	switch (state) {
+	CASE_RETURN_STRING(ERASER_BUTTON_NEUTRAL);
+	CASE_RETURN_STRING(ERASER_BUTTON_PEN_PENDING_ERASER);
+	CASE_RETURN_STRING(ERASER_BUTTON_BUTTON_HELD_DOWN);
+	CASE_RETURN_STRING(ERASER_BUTTON_BUTTON_RELEASED);
+	}
+	abort();
+}
+
+static const char *
+eraser_button_event_str(enum eraser_button_event event)
+{
+	switch (event) {
+	CASE_RETURN_STRING(ERASER_EVENT_PEN_ENTERING_PROX);
+	CASE_RETURN_STRING(ERASER_EVENT_PEN_LEAVING_PROX);
+	CASE_RETURN_STRING(ERASER_EVENT_ERASER_ENTERING_PROX);
+	CASE_RETURN_STRING(ERASER_EVENT_ERASER_LEAVING_PROX);
+	CASE_RETURN_STRING(ERASER_EVENT_TIMEOUT);
+	}
+	abort();
+}
+
+struct plugin_device {
+	struct list link;
+	struct plugin_data *parent;
+	struct libinput_device *device;
+
+	bool pen_in_prox;
+	bool eraser_in_prox;
+
+	struct evdev_frame *last_frame;
+
+	enum libinput_config_eraser_button_mode mode;
+	/* The evdev code of the button to send */
+	evdev_usage_t button;
+	struct libinput_plugin_timer *timer;
+	enum eraser_button_state state;
+};
+
+struct plugin_data {
+	struct libinput_plugin *plugin;
+	struct list devices;
+};
+
+static void
+plugin_device_destroy(struct plugin_device *device)
+{
+	libinput_plugin_timer_cancel(device->timer);
+	libinput_plugin_timer_unref(device->timer);
+	libinput_device_unref(device->device);
+	evdev_frame_unref(device->last_frame);
+	list_remove(&device->link);
+	free(device);
+}
+
+static void
+plugin_data_destroy(void *d)
+{
+	struct plugin_data *data = d;
+
+	struct plugin_device *device;
+	list_for_each_safe(device, &data->devices, link) {
+		plugin_device_destroy(device);
+	}
+
+	free(data);
+}
+
+DEFINE_DESTROY_CLEANUP_FUNC(plugin_data);
+
+static void
+plugin_destroy(struct libinput_plugin *libinput_plugin)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	plugin_data_destroy(plugin);
+}
+
+static void
+eraser_button_set_state(struct plugin_device *device, enum eraser_button_state to)
+{
+	enum eraser_button_state *state = &device->state;
+
+	*state = to;
+}
+
+static void
+eraser_button_set_timer(struct plugin_device *device, uint64_t time)
+{
+	libinput_plugin_timer_set(device->timer, time + ERASER_BUTTON_DELAY);
+}
+
+static void
+eraser_button_cancel_timer(struct plugin_device *device)
+{
+	libinput_plugin_timer_cancel(device->timer);
+}
+
+static void
+eraser_button_state_bug(struct plugin_device *device, enum eraser_button_event event)
+{
+	plugin_log_bug(device->parent->plugin,
+		       "Invalid eraser button event %s in state %s\n",
+		       eraser_button_event_str(event),
+		       eraser_button_state_str(device->state));
+}
+
+enum tool_filter {
+	SKIP_PEN = bit(1),
+	SKIP_ERASER = bit(2),
+	PEN_IN_PROX = bit(3),
+	PEN_OUT_OF_PROX = bit(4),
+	ERASER_IN_PROX = bit(5),
+	ERASER_OUT_OF_PROX = bit(6),
+	BUTTON_DOWN = bit(7),
+	BUTTON_UP = bit(8),
+	SKIP_BTN_TOUCH = bit(9),
+};
+
+static void
+eraser_button_insert_frame(struct plugin_device *device,
+			   struct evdev_frame *frame_in,
+			   enum tool_filter filter,
+			   evdev_usage_t *button)
+{
+	size_t nevents;
+	const struct evdev_event *events = evdev_frame_get_events(frame_in, &nevents);
+
+	/* +2 because we may add BTN_TOOL_PEN and BTN_TOOL_RUBBER */
+	_unref_(evdev_frame) *frame_out = evdev_frame_new(nevents + 2);
+
+	for (size_t i = 0; i < nevents; i++) {
+		struct evdev_event event = events[i];
+		switch (evdev_usage_enum(event.usage)) {
+		case EVDEV_BTN_TOOL_PEN:
+		case EVDEV_BTN_TOOL_RUBBER:
+			/* filter */
+			break;
+		case EVDEV_BTN_TOUCH:
+			if (!(filter & SKIP_BTN_TOUCH))
+				evdev_frame_append(frame_out, &event, 1);
+			break;
+		default:
+			if (button == NULL || evdev_usage_cmp(event.usage, *button))
+				evdev_frame_append(frame_out, &event, 1);
+			break;
+		}
+	}
+
+	if (filter & (PEN_IN_PROX | PEN_OUT_OF_PROX)) {
+		evdev_frame_append_one(frame_out,
+				       evdev_usage_from(EVDEV_BTN_TOOL_PEN),
+				       (filter & PEN_IN_PROX) ? 1 : 0);
+	}
+	if (filter & (ERASER_IN_PROX | ERASER_OUT_OF_PROX)) {
+		evdev_frame_append_one(frame_out,
+				       evdev_usage_from(EVDEV_BTN_TOOL_RUBBER),
+				       (filter & ERASER_IN_PROX) ? 1 : 0);
+	}
+	if (filter & (BUTTON_UP | BUTTON_DOWN)) {
+		assert(button != NULL);
+		evdev_frame_append_one(frame_out,
+				       *button,
+				       (filter & BUTTON_DOWN) ? 1 : 0);
+	}
+
+	evdev_frame_set_time(frame_out, evdev_frame_get_time(frame_in));
+	libinput_plugin_prepend_evdev_frame(device->parent->plugin,
+					    device->device,
+					    frame_out);
+}
+
+static enum frame_filter_state
+eraser_button_neutral_handle_event(struct plugin_device *device,
+				   struct evdev_frame *frame,
+				   enum eraser_button_event event,
+				   uint64_t time)
+{
+	switch (event) {
+	case ERASER_EVENT_PEN_ENTERING_PROX:
+		break;
+	case ERASER_EVENT_PEN_LEAVING_PROX:
+		eraser_button_set_timer(device, time);
+		eraser_button_set_state(device, ERASER_BUTTON_PEN_PENDING_ERASER);
+		return DISCARD; /* Discard this event, it has garbage data anyway */
+	case ERASER_EVENT_ERASER_ENTERING_PROX:
+		/* Change eraser prox in into pen prox in + button down */
+		eraser_button_insert_frame(device,
+					   frame,
+					   PEN_IN_PROX | SKIP_ERASER | BUTTON_DOWN,
+					   &device->button);
+		eraser_button_set_state(device, ERASER_BUTTON_BUTTON_HELD_DOWN);
+		return DISCARD;
+	case ERASER_EVENT_ERASER_LEAVING_PROX:
+		eraser_button_state_bug(device, event);
+		break;
+	case ERASER_EVENT_TIMEOUT:
+		break;
+	}
+
+	return PROCESS;
+}
+
+static enum frame_filter_state
+eraser_button_pending_eraser_handle_event(struct plugin_device *device,
+					  struct evdev_frame *frame,
+					  enum eraser_button_event event,
+					  uint64_t time)
+{
+	switch (event) {
+	case ERASER_EVENT_PEN_ENTERING_PROX:
+		eraser_button_cancel_timer(device);
+		eraser_button_set_state(device, ERASER_BUTTON_NEUTRAL);
+		/* We just papered over a quick prox out/in here */
+		break;
+	case ERASER_EVENT_PEN_LEAVING_PROX:
+		eraser_button_state_bug(device, event);
+		break;
+	case ERASER_EVENT_ERASER_ENTERING_PROX:
+		eraser_button_cancel_timer(device);
+		eraser_button_insert_frame(device,
+					   frame,
+					   SKIP_ERASER | SKIP_PEN | BUTTON_DOWN,
+					   &device->button);
+		eraser_button_set_state(device, ERASER_BUTTON_BUTTON_HELD_DOWN);
+		return DISCARD;
+	case ERASER_EVENT_ERASER_LEAVING_PROX:
+		eraser_button_state_bug(device, event);
+		break;
+	case ERASER_EVENT_TIMEOUT:
+		/* Pen went out of prox and we delayed expecting an eraser to
+		 * come in prox. That didn't happen -> pen prox out */
+		eraser_button_set_state(device, ERASER_BUTTON_NEUTRAL);
+		eraser_button_insert_frame(device,
+					   frame,
+					   SKIP_ERASER | PEN_OUT_OF_PROX,
+					   NULL);
+		break;
+	}
+
+	return PROCESS;
+}
+
+static enum frame_filter_state
+eraser_button_button_held_handle_event(struct plugin_device *device,
+				       struct evdev_frame *frame,
+				       enum eraser_button_event event,
+				       uint64_t time)
+{
+	switch (event) {
+	case ERASER_EVENT_PEN_ENTERING_PROX:
+	case ERASER_EVENT_PEN_LEAVING_PROX:
+		/* We should've seen an eraser out-of-prox out here */
+		eraser_button_state_bug(device, event);
+		break;
+	case ERASER_EVENT_ERASER_ENTERING_PROX:
+		eraser_button_state_bug(device, event);
+		break;
+	case ERASER_EVENT_ERASER_LEAVING_PROX:
+		eraser_button_insert_frame(device,
+					   device->last_frame,
+					   SKIP_ERASER | SKIP_PEN | BUTTON_UP,
+					   &device->button);
+		eraser_button_set_state(device, ERASER_BUTTON_BUTTON_RELEASED);
+		eraser_button_set_timer(device, time);
+		return DISCARD; /* Discard the actual frame, it has garbage data anyway
+				 */
+	case ERASER_EVENT_TIMEOUT:
+		/* Expected to be cancelled in previous state */
+		eraser_button_state_bug(device, event);
+		break;
+	}
+
+	return PROCESS;
+}
+
+static enum frame_filter_state
+eraser_button_button_released_handle_event(struct plugin_device *device,
+					   struct evdev_frame *frame,
+					   enum eraser_button_event event,
+					   uint64_t time)
+{
+	switch (event) {
+	case ERASER_EVENT_PEN_ENTERING_PROX:
+		eraser_button_cancel_timer(device);
+		eraser_button_insert_frame(device, frame, SKIP_PEN | SKIP_ERASER, NULL);
+		eraser_button_set_state(device, ERASER_BUTTON_NEUTRAL);
+		return DISCARD;
+	case ERASER_EVENT_PEN_LEAVING_PROX:
+		eraser_button_state_bug(device, event);
+		break;
+	case ERASER_EVENT_ERASER_ENTERING_PROX:
+		break;
+	case ERASER_EVENT_ERASER_LEAVING_PROX:
+		eraser_button_state_bug(device, event);
+		break;
+	case ERASER_EVENT_TIMEOUT:
+		/* Eraser went out of prox, we expected the pen to come back in prox but
+		 * that didn't happen. We still have the pen simulated in-prox  -> pen
+		 * prox out.
+		 * We release the button first, then send the pen out-of-prox
+		 * event sequence. This way the sequence of tip first/button first is
+		 * predictable.
+		 */
+		eraser_button_insert_frame(device,
+					   frame,
+					   SKIP_PEN | SKIP_ERASER | BUTTON_UP,
+					   &device->button);
+		eraser_button_insert_frame(device, frame, PEN_OUT_OF_PROX, NULL);
+		eraser_button_set_state(device, ERASER_BUTTON_NEUTRAL);
+		break;
+	}
+
+	return PROCESS;
+}
+
+static enum frame_filter_state
+eraser_button_handle_state(struct plugin_device *device,
+			   struct evdev_frame *frame,
+			   enum eraser_button_event event,
+			   uint64_t time)
+{
+	enum eraser_button_state state = device->state;
+	enum frame_filter_state ret = PROCESS;
+
+	switch (state) {
+	case ERASER_BUTTON_NEUTRAL:
+		ret = eraser_button_neutral_handle_event(device, frame, event, time);
+		break;
+	case ERASER_BUTTON_PEN_PENDING_ERASER:
+		ret = eraser_button_pending_eraser_handle_event(device,
+								frame,
+								event,
+								time);
+		break;
+	case ERASER_BUTTON_BUTTON_HELD_DOWN:
+		ret = eraser_button_button_held_handle_event(device,
+							     frame,
+							     event,
+							     time);
+		break;
+	case ERASER_BUTTON_BUTTON_RELEASED:
+		ret = eraser_button_button_released_handle_event(device,
+								 frame,
+								 event,
+								 time);
+		break;
+	}
+
+	if (state != device->state) {
+		plugin_log_debug(device->parent->plugin,
+				 "eraser button: state %s -> %s -> %s\n",
+				 eraser_button_state_str(state),
+				 eraser_button_event_str(event),
+				 eraser_button_state_str(device->state));
+	}
+	return ret;
+}
+
+/**
+ * Physical eraser button handling: if the physical eraser button is
+ * disabled paper over any pen prox out/eraser prox in events and send a button event
+ * instead.
+ */
+static void
+eraser_button_handle_frame(struct plugin_device *device,
+			   struct evdev_frame *frame,
+			   uint64_t time)
+{
+	if (device->mode == LIBINPUT_CONFIG_ERASER_BUTTON_DEFAULT)
+		return;
+
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+
+	bool pen_toggled = false;
+	bool eraser_toggled = false;
+
+	for (size_t i = 0; i < nevents; i++) {
+		struct evdev_event *event = &events[i];
+
+		switch (evdev_usage_enum(event->usage)) {
+		case EVDEV_BTN_TOOL_PEN:
+			pen_toggled = true;
+			device->pen_in_prox = !!event->value;
+			break;
+		case EVDEV_BTN_TOOL_RUBBER:
+			eraser_toggled = true;
+			device->eraser_in_prox = !!event->value;
+			break;
+		default:
+			break;
+		}
+	}
+
+	bool eraser_in_prox = device->eraser_in_prox;
+	bool pen_in_prox = device->pen_in_prox;
+
+	enum eraser_button_event eraser_event =
+		eraser_in_prox ? ERASER_EVENT_ERASER_ENTERING_PROX
+			       : ERASER_EVENT_ERASER_LEAVING_PROX;
+	enum eraser_button_event pen_event = pen_in_prox
+						     ? ERASER_EVENT_PEN_ENTERING_PROX
+						     : ERASER_EVENT_PEN_LEAVING_PROX;
+
+	enum frame_filter_state ret = PROCESS;
+
+	/* bit awkward because we definitely want whatever goes out of prox to
+	 * be handled first but if one sends discard and the other one process?
+	 * Unclear...
+	 */
+	if (eraser_toggled && pen_toggled) {
+		if (pen_in_prox) {
+			eraser_button_handle_state(device, frame, eraser_event, time);
+			ret = eraser_button_handle_state(device,
+							 frame,
+							 pen_event,
+							 time);
+		} else {
+			eraser_button_handle_state(device, frame, pen_event, time);
+			ret = eraser_button_handle_state(device,
+							 frame,
+							 eraser_event,
+							 time);
+		}
+	} else if (eraser_toggled) {
+		ret = eraser_button_handle_state(device, frame, eraser_event, time);
+	} else if (pen_toggled) {
+		ret = eraser_button_handle_state(device, frame, pen_event, time);
+	}
+
+	if (ret == PROCESS) {
+		evdev_frame_reset(device->last_frame);
+		evdev_frame_append(device->last_frame, events, nevents);
+	} else if (ret == DISCARD) {
+		evdev_frame_reset(frame);
+	}
+}
+
+static void
+eraser_button_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
+				 struct libinput_device *device,
+				 struct evdev_frame *frame)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd;
+	uint64_t time = evdev_frame_get_time(frame);
+
+	list_for_each(pd, &plugin->devices, link) {
+		if (pd->device == device) {
+			eraser_button_handle_frame(pd, frame, time);
+			break;
+		}
+	}
+}
+
+static void
+eraser_button_timer_func(struct libinput_plugin *plugin, uint64_t now, void *d)
+{
+	struct plugin_device *device = d;
+
+	if (!device->last_frame) {
+		plugin_log_bug(
+			device->parent->plugin,
+			"Eraser button timer fired without a frame in state %s\n",
+			eraser_button_state_str(device->state));
+		return;
+	}
+	eraser_button_handle_state(device,
+				   device->last_frame,
+				   ERASER_EVENT_TIMEOUT,
+				   now);
+}
+
+static void
+eraser_button_plugin_device_added(struct libinput_plugin *libinput_plugin,
+				  struct libinput_device *device)
+{
+	if (!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_TOOL))
+		return;
+
+	libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_TOOL_PEN);
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_TOOL_RUBBER);
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_TOUCH);
+	/* These are the only ones we allow setting the eraser button to */
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_STYLUS);
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_STYLUS2);
+	libinput_plugin_enable_evdev_usage(libinput_plugin, EVDEV_BTN_STYLUS3);
+
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd = zalloc(sizeof(*pd));
+	pd->device = libinput_device_ref(device);
+	pd->parent = plugin;
+	pd->last_frame = evdev_frame_new(64);
+	pd->timer = libinput_plugin_timer_new(libinput_plugin,
+					      libinput_device_get_sysname(device),
+					      eraser_button_timer_func,
+					      pd);
+
+	list_take_append(&plugin->devices, pd, link);
+}
+
+static void
+eraser_button_plugin_device_removed(struct libinput_plugin *libinput_plugin,
+				    struct libinput_device *device)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *dev;
+	list_for_each_safe(dev, &plugin->devices, link) {
+		if (dev->device == device) {
+			plugin_device_destroy(dev);
+			return;
+		}
+	}
+}
+
+static void
+eraser_button_plugin_tool_configured(struct libinput_plugin *libinput_plugin,
+				     struct libinput_tablet_tool *tool)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd;
+	list_for_each(pd, &plugin->devices, link) {
+		/* FIXME: sigh, we need a separate list of tools? */
+		pd->mode = libinput_tablet_tool_config_eraser_button_get_mode(tool);
+		uint32_t button =
+			libinput_tablet_tool_config_eraser_button_get_button(tool);
+
+		pd->button = evdev_usage_from_code(EV_KEY, button);
+	}
+}
+
+static const struct libinput_plugin_interface interface = {
+	.run = NULL,
+	.destroy = plugin_destroy,
+	.device_new = NULL,
+	.device_ignored = NULL,
+	.device_added = eraser_button_plugin_device_added,
+	.device_removed = eraser_button_plugin_device_removed,
+	.evdev_frame = eraser_button_plugin_evdev_frame,
+	.tool_configured = eraser_button_plugin_tool_configured,
+};
+
+void
+libinput_tablet_plugin_eraser_button(struct libinput *libinput)
+{
+	if (getenv("LIBINPUT_RUNNING_TEST_SUITE"))
+		ERASER_BUTTON_DELAY = ms2us(150);
+
+	_destroy_(plugin_data) *plugin = zalloc(sizeof(*plugin));
+	list_init(&plugin->devices);
+
+	_unref_(libinput_plugin) *p =
+		libinput_plugin_new(libinput, "tablet-eraser-button", &interface, NULL);
+	plugin->plugin = p;
+	libinput_plugin_set_user_data(p, steal(&plugin));
+}
diff -pruN 1.28.1-1/src/libinput-plugin-tablet-eraser-button.h 1.30.0-1/src/libinput-plugin-tablet-eraser-button.h
--- 1.28.1-1/src/libinput-plugin-tablet-eraser-button.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-tablet-eraser-button.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "libinput-plugin.h"
+#include "libinput.h"
+
+void
+libinput_tablet_plugin_eraser_button(struct libinput *libinput);
diff -pruN 1.28.1-1/src/libinput-plugin-tablet-forced-tool.c 1.30.0-1/src/libinput-plugin-tablet-forced-tool.c
--- 1.28.1-1/src/libinput-plugin-tablet-forced-tool.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-tablet-forced-tool.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,254 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <libevdev/libevdev.h>
+
+#include "util-mem.h"
+#include "util-strings.h"
+
+#include "evdev-frame.h"
+#include "libinput-log.h"
+#include "libinput-plugin-tablet-forced-tool.h"
+#include "libinput-plugin.h"
+#include "libinput-util.h"
+
+/*
+ * Handling for tools that never set BTN_TOOL_PEN.
+ */
+
+enum tools {
+	PEN = 0,
+	RUBBER,
+	BRUSH,
+	PENCIL,
+	AIRBRUSH,
+	MOUSE,
+	LENS,
+};
+
+struct plugin_device {
+	struct list link;
+	struct libinput_device *device;
+	bitmask_t tool_state;
+	bool pen_forced_into_proximity;
+};
+
+struct plugin_data {
+	struct list devices;
+};
+
+static void
+plugin_device_destroy(struct plugin_device *device)
+{
+	libinput_device_unref(device->device);
+	list_remove(&device->link);
+	free(device);
+}
+
+static void
+plugin_data_destroy(void *d)
+{
+	struct plugin_data *data = d;
+
+	struct plugin_device *device;
+	list_for_each_safe(device, &data->devices, link) {
+		plugin_device_destroy(device);
+	}
+
+	free(data);
+}
+
+DEFINE_DESTROY_CLEANUP_FUNC(plugin_data);
+
+static void
+plugin_destroy(struct libinput_plugin *libinput_plugin)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	plugin_data_destroy(plugin);
+}
+
+static void
+forced_tool_plugin_force_pen_out(struct libinput_plugin *libinput_plugin,
+				 struct libinput_device *device,
+				 struct evdev_frame *frame)
+{
+	_unref_(evdev_frame) *prox_out_frame = evdev_frame_new(2);
+	evdev_frame_append_one(prox_out_frame, evdev_usage_from(EVDEV_BTN_TOOL_PEN), 0);
+	evdev_frame_set_time(prox_out_frame, evdev_frame_get_time(frame));
+
+	libinput_plugin_prepend_evdev_frame(libinput_plugin, device, prox_out_frame);
+}
+
+static void
+forced_tool_plugin_device_handle_frame(struct libinput_plugin *libinput_plugin,
+				       struct plugin_device *device,
+				       struct evdev_frame *frame)
+{
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+
+	bool axis_change = false;
+
+	for (size_t i = 0; i < nevents; i++) {
+		struct evdev_event *event = &events[i];
+		switch (evdev_usage_enum(event->usage)) {
+		case EVDEV_BTN_TOOL_PEN:
+			if (event->value == 1) {
+				bitmask_set_bit(&device->tool_state, BTN_TOOL_PEN);
+			} else {
+				bitmask_clear_bit(&device->tool_state, BTN_TOOL_PEN);
+				device->pen_forced_into_proximity = false;
+			}
+			return; /* Nothing to do */
+		case EVDEV_BTN_TOOL_RUBBER:
+		case EVDEV_BTN_TOOL_BRUSH:
+		case EVDEV_BTN_TOOL_PENCIL:
+		case EVDEV_BTN_TOOL_AIRBRUSH:
+		case EVDEV_BTN_TOOL_MOUSE:
+		case EVDEV_BTN_TOOL_LENS: {
+			int code = evdev_event_code(event) - BTN_TOOL_PEN;
+			if (event->value == 1) {
+				bitmask_set_bit(&device->tool_state, code);
+				if (device->pen_forced_into_proximity) {
+					forced_tool_plugin_force_pen_out(
+						libinput_plugin,
+						device->device,
+						frame);
+					device->pen_forced_into_proximity = false;
+				}
+			} else {
+				bitmask_clear_bit(&device->tool_state, code);
+			}
+			return; /* Keep the frame as-is */
+		}
+		case EVDEV_ABS_X:
+		case EVDEV_ABS_Y:
+		case EVDEV_ABS_Z: /* rotation */
+		/* not ABS_DISTANCE! */
+		case EVDEV_ABS_PRESSURE:
+		case EVDEV_ABS_TILT_X:
+		case EVDEV_ABS_TILT_Y:
+		case EVDEV_ABS_WHEEL: /* slider */
+			/* no early return here, the BTN_TOOL updates
+			 * may come after the ABS_ events */
+			axis_change = true;
+			break;
+		case EVDEV_REL_WHEEL:
+			/* no early return here, the BTN_TOOL updates
+			 * may come after the REL_ events */
+			axis_change = true;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!axis_change)
+		return;
+
+	const bitmask_t all_tools =
+		bitmask_from_bits(PEN, RUBBER, BRUSH, PENCIL, AIRBRUSH, MOUSE, LENS);
+	if (bitmask_any(device->tool_state, all_tools))
+		return;
+
+	/* We need to force a BTN_TOOL_PEN if we get an axis event (i.e.
+	 * stylus is def. in proximity). We don't do this for pure
+	 * button events because we discard those.
+	 */
+	evdev_frame_append_one(frame,
+			       evdev_usage_from(EVDEV_BTN_TOOL_PEN),
+			       1); /* libinput's event frame will have space */
+	device->pen_forced_into_proximity = true;
+}
+
+static void
+forced_tool_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
+			       struct libinput_device *device,
+			       struct evdev_frame *frame)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd;
+
+	list_for_each(pd, &plugin->devices, link) {
+		if (pd->device == device) {
+			forced_tool_plugin_device_handle_frame(libinput_plugin,
+							       pd,
+							       frame);
+			break;
+		}
+	}
+}
+
+static void
+forced_tool_plugin_device_added(struct libinput_plugin *libinput_plugin,
+				struct libinput_device *device)
+{
+	if (!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_TOOL))
+		return;
+
+	libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
+
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd = zalloc(sizeof(*pd));
+	pd->device = libinput_device_ref(device);
+	list_take_append(&plugin->devices, pd, link);
+}
+
+static void
+forced_tool_plugin_device_removed(struct libinput_plugin *libinput_plugin,
+				  struct libinput_device *device)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *dev;
+	list_for_each_safe(dev, &plugin->devices, link) {
+		if (dev->device == device) {
+			plugin_device_destroy(dev);
+			return;
+		}
+	}
+}
+
+static const struct libinput_plugin_interface interface = {
+	.run = NULL,
+	.destroy = plugin_destroy,
+	.device_new = NULL,
+	.device_ignored = NULL,
+	.device_added = forced_tool_plugin_device_added,
+	.device_removed = forced_tool_plugin_device_removed,
+	.evdev_frame = forced_tool_plugin_evdev_frame,
+};
+
+void
+libinput_tablet_plugin_forced_tool(struct libinput *libinput)
+{
+	_destroy_(plugin_data) *plugin = zalloc(sizeof(*plugin));
+	list_init(&plugin->devices);
+
+	_unref_(libinput_plugin) *p = libinput_plugin_new(libinput,
+							  "tablet-forced-tool",
+							  &interface,
+							  steal(&plugin));
+}
diff -pruN 1.28.1-1/src/libinput-plugin-tablet-forced-tool.h 1.30.0-1/src/libinput-plugin-tablet-forced-tool.h
--- 1.28.1-1/src/libinput-plugin-tablet-forced-tool.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-tablet-forced-tool.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "libinput-plugin.h"
+#include "libinput.h"
+
+void
+libinput_tablet_plugin_forced_tool(struct libinput *libinput);
diff -pruN 1.28.1-1/src/libinput-plugin-tablet-proximity-timer.c 1.30.0-1/src/libinput-plugin-tablet-proximity-timer.c
--- 1.28.1-1/src/libinput-plugin-tablet-proximity-timer.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-tablet-proximity-timer.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,313 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <libevdev/libevdev.h>
+
+#include "util-mem.h"
+#include "util-strings.h"
+
+#include "evdev-frame.h"
+#include "libinput-log.h"
+#include "libinput-plugin-tablet-proximity-timer.h"
+#include "libinput-plugin.h"
+#include "libinput-util.h"
+#include "timer.h"
+
+/* The tablet sends events every ~2ms , 50ms should be plenty enough to
+   detect out-of-range.
+   This value is higher during test suite runs */
+static int FORCED_PROXOUT_TIMEOUT = 50 * 1000; /* µs */
+
+struct plugin_device {
+	struct list link;
+
+	struct libinput_plugin_timer *prox_out_timer;
+	bool proximity_out_forced;
+	uint64_t last_event_time;
+
+	bool pen_state;
+	bitmask_t button_state;
+
+	struct libinput_device *device;
+	struct plugin_data *parent;
+};
+
+static void
+plugin_device_destroy(void *d)
+{
+	struct plugin_device *device = d;
+
+	list_remove(&device->link);
+	libinput_plugin_timer_cancel(device->prox_out_timer);
+	libinput_plugin_timer_unref(device->prox_out_timer);
+	libinput_device_unref(device->device);
+
+	free(device);
+}
+
+struct plugin_data {
+	struct list devices;
+	struct libinput_plugin *plugin;
+};
+
+static void
+plugin_data_destroy(void *d)
+{
+	struct plugin_data *data = d;
+
+	struct plugin_device *device;
+	list_for_each_safe(device, &data->devices, link) {
+		plugin_device_destroy(device);
+	}
+
+	free(data);
+}
+
+static inline void
+proximity_timer_plugin_set_timer(struct plugin_device *device, uint64_t time)
+{
+	libinput_plugin_timer_set(device->prox_out_timer,
+				  time + FORCED_PROXOUT_TIMEOUT);
+}
+
+static void
+tablet_proximity_out_quirk_timer_func(struct libinput_plugin *plugin,
+				      uint64_t now,
+				      void *data)
+{
+	struct plugin_device *device = data;
+
+	if (!bitmask_is_empty(device->button_state)) {
+		proximity_timer_plugin_set_timer(device, now);
+		return;
+	}
+
+	if (device->last_event_time > now - FORCED_PROXOUT_TIMEOUT) {
+		proximity_timer_plugin_set_timer(device, device->last_event_time);
+		return;
+	}
+
+	plugin_log_debug(device->parent->plugin,
+			 "%s: forcing proximity out after timeout\n",
+			 libinput_device_get_name(device->device));
+
+	_unref_(evdev_frame) *prox_out_frame = evdev_frame_new(2);
+	evdev_frame_append_one(prox_out_frame, evdev_usage_from(EVDEV_BTN_TOOL_PEN), 0);
+	evdev_frame_set_time(prox_out_frame, now);
+
+	libinput_plugin_prepend_evdev_frame(device->parent->plugin,
+					    device->device,
+					    prox_out_frame);
+
+	device->proximity_out_forced = true;
+}
+
+/*
+ * Handling for the proximity out workaround. Some tablets only send
+ * BTN_TOOL_PEN on the very first event, then leave it set even when the pen
+ * leaves the detectable range. To libinput this looks like we always have
+ * the pen in proximity.
+ *
+ * To avoid this, we set a timer on BTN_TOOL_PEN in. We expect the tablet to
+ * continuously send events, and while it's doing so we keep updating the
+ * timer. Once we go Xms without an event we assume proximity out and inject
+ * a BTN_TOOL_PEN 0 event into the sequence through the timer func.
+ *
+ * On the next axis event after a prox out we enforce
+ * BTN_TOOL_PEN 1 to force proximity in.
+ */
+static void
+proximity_timer_plugin_device_handle_frame(struct libinput_plugin *libinput_plugin,
+					   struct plugin_device *device,
+					   struct evdev_frame *frame)
+{
+	uint64_t time = evdev_frame_get_time(frame);
+	/* First event after adding a device - by definition the pen
+	 *
+	 * is in proximity if we get this one */
+	if (device->last_event_time == 0)
+		proximity_timer_plugin_set_timer(device, time);
+
+	device->last_event_time = time;
+
+	bool pen_toggled = false;
+
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+	for (size_t i = 0; i < nevents; i++) {
+		struct evdev_event *event = &events[i];
+
+		/* The proximity timeout is only needed for BTN_TOOL_PEN, devices
+		 * that require it don't do erasers */
+		switch (evdev_usage_enum(event->usage)) {
+		case EVDEV_BTN_STYLUS:
+		case EVDEV_BTN_STYLUS2:
+		case EVDEV_BTN_STYLUS3:
+		case EVDEV_BTN_TOUCH:
+			if (event->value)
+				bitmask_set_bit(&device->button_state,
+						evdev_event_code(event) - BTN_STYLUS3);
+			else
+				bitmask_clear_bit(&device->button_state,
+						  evdev_event_code(event) -
+							  BTN_STYLUS3);
+			break;
+		case EVDEV_BTN_TOOL_PEN:
+			pen_toggled = true;
+			device->pen_state = event->value == 1;
+			break;
+		case EVDEV_BTN_TOOL_RUBBER:
+		case EVDEV_BTN_TOOL_BRUSH:
+		case EVDEV_BTN_TOOL_PENCIL:
+		case EVDEV_BTN_TOOL_AIRBRUSH:
+		case EVDEV_BTN_TOOL_FINGER:
+		case EVDEV_BTN_TOOL_MOUSE:
+		case EVDEV_BTN_TOOL_LENS:
+			libinput_plugin_enable_device_event_frame(libinput_plugin,
+								  device->device,
+								  false);
+			plugin_device_destroy(device);
+			return;
+		default:
+			break;
+		}
+	}
+
+	if (pen_toggled) {
+		if (device->pen_state) {
+			proximity_timer_plugin_set_timer(device, time);
+		} else {
+			/* If we get a BTN_TOOL_PEN 0 it means the tablet will
+			 * give us the right events after all and we can disable
+			 * our timer-based proximity out.
+			 */
+			libinput_plugin_timer_cancel(device->prox_out_timer);
+			plugin_log_debug(libinput_plugin,
+					 "%s: proximity out timer unloaded\n",
+					 libinput_device_get_name(device->device));
+			libinput_plugin_enable_device_event_frame(libinput_plugin,
+								  device->device,
+								  false);
+			plugin_device_destroy(device);
+			return;
+		}
+	} else if (device->proximity_out_forced) {
+		plugin_log_debug(libinput_plugin,
+				 "%s: forcing proximity in\n",
+				 libinput_device_get_name(device->device));
+		evdev_frame_append_one(frame, evdev_usage_from(EVDEV_BTN_TOOL_PEN), 1);
+		device->proximity_out_forced = false;
+		proximity_timer_plugin_set_timer(device, time);
+	}
+}
+
+static void
+proximity_timer_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
+				   struct libinput_device *device,
+				   struct evdev_frame *frame)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd;
+
+	list_for_each(pd, &plugin->devices, link) {
+		if (pd->device == device) {
+			proximity_timer_plugin_device_handle_frame(libinput_plugin,
+								   pd,
+								   frame);
+			break;
+		}
+	}
+}
+
+static void
+proximity_timer_plugin_device_added(struct libinput_plugin *libinput_plugin,
+				    struct libinput_device *device)
+{
+	if (!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_TOOL))
+		return;
+
+	libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
+
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *pd = zalloc(sizeof(*pd));
+	pd->device = libinput_device_ref(device);
+	pd->parent = plugin;
+	pd->prox_out_timer =
+		libinput_plugin_timer_new(libinput_plugin,
+					  libinput_device_get_sysname(device),
+					  tablet_proximity_out_quirk_timer_func,
+					  pd);
+
+	list_take_append(&plugin->devices, pd, link);
+}
+
+static void
+proximity_timer_plugin_device_removed(struct libinput_plugin *libinput_plugin,
+				      struct libinput_device *device)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	struct plugin_device *dev;
+	list_for_each_safe(dev, &plugin->devices, link) {
+		if (dev->device == device) {
+			plugin_device_destroy(dev);
+			return;
+		}
+	}
+}
+
+static void
+plugin_destroy(struct libinput_plugin *libinput_plugin)
+{
+	struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
+	plugin_data_destroy(plugin);
+}
+
+static const struct libinput_plugin_interface interface = {
+	.run = NULL,
+	.destroy = plugin_destroy,
+	.device_new = NULL,
+	.device_ignored = NULL,
+	.device_added = proximity_timer_plugin_device_added,
+	.device_removed = proximity_timer_plugin_device_removed,
+	.evdev_frame = proximity_timer_plugin_evdev_frame,
+};
+
+void
+libinput_tablet_plugin_proximity_timer(struct libinput *libinput)
+{
+	struct plugin_data *plugin = zalloc(sizeof(*plugin));
+	list_init(&plugin->devices);
+
+	/* Stop false positives caused by the forced proximity code */
+	if (getenv("LIBINPUT_RUNNING_TEST_SUITE"))
+		FORCED_PROXOUT_TIMEOUT = 150 * 1000; /* µs */
+
+	_unref_(libinput_plugin) *p = libinput_plugin_new(libinput,
+							  "tablet-proximity-timer",
+							  &interface,
+							  plugin);
+	plugin->plugin = p;
+}
diff -pruN 1.28.1-1/src/libinput-plugin-tablet-proximity-timer.h 1.30.0-1/src/libinput-plugin-tablet-proximity-timer.h
--- 1.28.1-1/src/libinput-plugin-tablet-proximity-timer.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin-tablet-proximity-timer.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "libinput-plugin.h"
+#include "libinput.h"
+
+void
+libinput_tablet_plugin_proximity_timer(struct libinput *libinput);
diff -pruN 1.28.1-1/src/libinput-plugin.c 1.30.0-1/src/libinput-plugin.c
--- 1.28.1-1/src/libinput-plugin.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,954 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdbool.h>
+
+#include "util-files.h"
+#include "util-list.h"
+
+#include "evdev-frame.h"
+#include "evdev-plugin.h"
+#include "libinput-feature.h"
+#include "libinput-plugin-button-debounce.h"
+#include "libinput-plugin-lua.h"
+#include "libinput-plugin-mouse-wheel-lowres.h"
+#include "libinput-plugin-mouse-wheel.h"
+#include "libinput-plugin-mtdev.h"
+#include "libinput-plugin-private.h"
+#include "libinput-plugin-system.h"
+#include "libinput-plugin-tablet-double-tool.h"
+#include "libinput-plugin-tablet-eraser-button.h"
+#include "libinput-plugin-tablet-forced-tool.h"
+#include "libinput-plugin-tablet-proximity-timer.h"
+#include "libinput-plugin.h"
+#include "libinput-private.h"
+#include "libinput-util.h"
+#include "timer.h"
+
+struct libinput_plugin {
+	struct libinput *libinput;
+	size_t index; /* sequential index of all plugins */
+	char *name;
+	int refcount;
+	struct list link;
+	void *user_data;
+
+	bool registered;
+
+	const struct libinput_plugin_interface *interface;
+
+	struct list timers;
+
+	struct {
+		struct list *after;
+		struct list *before;
+	} event_queue;
+
+	struct evdev_mask *mask;
+};
+
+struct libinput_plugin_timer {
+	int refcount;
+	struct list link;
+	struct libinput_plugin *plugin;
+	struct libinput_timer timer;
+	void (*func)(struct libinput_plugin *plugin, uint64_t now, void *user_data);
+	void *user_data;
+};
+
+LIBINPUT_ATTRIBUTE_PRINTF(3, 4)
+void
+plugin_log_msg(struct libinput_plugin *plugin,
+	       enum libinput_log_priority priority,
+	       const char *format,
+	       ...)
+{
+
+	if (!log_is_logged(plugin->libinput, priority))
+		return;
+
+	_autofree_ char *prefix = strdup_printf("Plugin:%-15s - ", plugin->name);
+	va_list args;
+	va_start(args, format);
+	_autofree_ char *message = strdup_vprintf(format, args);
+	va_end(args);
+
+	log_msg(plugin->libinput, priority, "%s%s", prefix, message);
+}
+
+static void
+libinput_plugin_system_load_internal_plugins(struct libinput *libinput,
+					     struct libinput_plugin_system *system);
+
+struct libinput_plugin *
+libinput_plugin_new(struct libinput *libinput,
+		    const char *name,
+		    const struct libinput_plugin_interface *interface,
+		    void *user_data)
+{
+	struct libinput_plugin *plugin = zalloc(sizeof(*plugin));
+	plugin->index = libinput->plugin_system.next_plugin_index++;
+	plugin->registered = true;
+	plugin->libinput = libinput;
+	plugin->refcount = 1;
+	plugin->interface = interface;
+	plugin->user_data = user_data;
+	plugin->name = strdup(name);
+	list_init(&plugin->timers);
+
+	if (plugin->index >= 32) {
+		log_bug_libinput(libinput, "Too many plugins, maximum is 32\n");
+	}
+
+	libinput_plugin_system_register_plugin(&libinput->plugin_system, plugin);
+
+	return plugin;
+}
+
+void
+libinput_plugin_unregister(struct libinput_plugin *plugin)
+{
+	struct libinput *libinput = plugin->libinput;
+	if (!plugin->registered)
+		return;
+
+	plugin->registered = false;
+
+	libinput_plugin_system_unregister_plugin(&libinput->plugin_system, plugin);
+}
+
+struct libinput_plugin *
+libinput_plugin_ref(struct libinput_plugin *plugin)
+{
+	assert(plugin->refcount > 0);
+	++plugin->refcount;
+	return plugin;
+}
+
+struct libinput_plugin *
+libinput_plugin_unref(struct libinput_plugin *plugin)
+{
+	assert(plugin->refcount > 0);
+	if (--plugin->refcount == 0) {
+		struct libinput_plugin_timer *timer;
+		list_for_each_safe(timer, &plugin->timers, link) {
+			libinput_plugin_timer_cancel(timer);
+			libinput_plugin_timer_unref(timer);
+		}
+
+		list_remove(&plugin->link);
+		if (plugin->interface->destroy)
+			plugin->interface->destroy(plugin);
+		free(plugin->name);
+		evdev_mask_destroy(plugin->mask);
+		free(plugin);
+	}
+	return NULL;
+}
+
+void
+libinput_plugin_set_user_data(struct libinput_plugin *plugin, void *user_data)
+{
+	plugin->user_data = user_data;
+}
+
+void *
+libinput_plugin_get_user_data(struct libinput_plugin *plugin)
+{
+	return plugin->user_data;
+}
+
+const char *
+libinput_plugin_get_name(struct libinput_plugin *plugin)
+{
+	return plugin->name;
+}
+
+struct libinput *
+libinput_plugin_get_context(struct libinput_plugin *plugin)
+{
+	return plugin->libinput;
+}
+
+void
+libinput_plugin_enable_device_event_frame(struct libinput_plugin *plugin,
+					  struct libinput_device *device,
+					  bool enable)
+{
+	if (enable) {
+		bitmask_set_bit(&device->plugin_frame_callbacks, plugin->index);
+	} else {
+		bitmask_clear_bit(&device->plugin_frame_callbacks, plugin->index);
+	}
+}
+
+void
+libinput_plugin_enable_evdev_usage(struct libinput_plugin *plugin,
+				   enum evdev_usage usage)
+{
+	if (!plugin->mask)
+		plugin->mask = evdev_mask_new();
+
+	evdev_mask_set_usage(plugin->mask, evdev_usage_from(usage));
+}
+
+void
+libinput_plugin_disable_device_feature(struct libinput_plugin *plugin,
+				       struct libinput_device *device,
+				       enum libinput_feature feature)
+{
+	struct libinput *libinput = plugin->libinput;
+
+	/* During device-added, only some plugins are loaded so this notifies
+	 * some of the plugins. All plugins are notified once device-added is
+	 * complete.  */
+	libinput_plugin_system_notify_device_feature_disabled(&libinput->plugin_system,
+							      device,
+							      feature);
+	bitmask_set_bit(&device->disabled_features, feature);
+}
+
+struct plugin_queued_event {
+	struct list link;
+	struct evdev_frame *frame;      /* owns a ref */
+	struct libinput_device *device; /* owns a ref */
+};
+
+static void
+plugin_queued_event_destroy(struct plugin_queued_event *event)
+{
+	evdev_frame_unref(event->frame);
+	libinput_device_unref(event->device);
+	list_remove(&event->link);
+	free(event);
+}
+
+static inline struct plugin_queued_event *
+plugin_queued_event_new(struct evdev_frame *frame, struct libinput_device *device)
+{
+	struct plugin_queued_event *event = zalloc(sizeof(*event));
+
+	event->frame = evdev_frame_ref(frame);
+	event->device = libinput_device_ref(device);
+
+	return event;
+}
+
+static void
+libinput_plugin_queue_evdev_frame(struct list *queue,
+				  const char *func,
+				  struct libinput_plugin *plugin,
+				  struct libinput_device *device,
+				  struct evdev_frame *frame)
+{
+	if (queue == NULL) {
+		plugin_log_bug(plugin,
+			       "%s() called outside evdev_frame processing\n",
+			       func);
+		libinput_plugin_unregister(plugin);
+		return;
+	}
+
+	_unref_(evdev_frame) *clone = evdev_frame_clone(frame);
+	struct plugin_queued_event *event = plugin_queued_event_new(clone, device);
+	list_take_append(queue, event, link);
+}
+
+void
+libinput_plugin_append_evdev_frame(struct libinput_plugin *plugin,
+				   struct libinput_device *device,
+				   struct evdev_frame *frame)
+{
+	libinput_plugin_queue_evdev_frame(plugin->event_queue.after,
+					  __func__,
+					  plugin,
+					  device,
+					  frame);
+}
+
+void
+libinput_plugin_prepend_evdev_frame(struct libinput_plugin *plugin,
+				    struct libinput_device *device,
+				    struct evdev_frame *frame)
+{
+	libinput_plugin_queue_evdev_frame(plugin->event_queue.before,
+					  __func__,
+					  plugin,
+					  device,
+					  frame);
+}
+
+void
+libinput_plugin_inject_evdev_frame(struct libinput_plugin *plugin,
+				   struct libinput_device *device,
+				   struct evdev_frame *frame)
+{
+	if (device->inject_evdev_frame)
+		device->inject_evdev_frame(device, frame);
+}
+
+void
+libinput_plugin_run(struct libinput_plugin *plugin)
+{
+	if (plugin->interface->run)
+		plugin->interface->run(plugin);
+}
+
+void
+libinput_plugin_notify_device_new(struct libinput_plugin *plugin,
+				  struct libinput_device *device,
+				  struct libevdev *evdev,
+				  struct udev_device *udev_device)
+{
+	if (plugin->interface->device_new)
+		plugin->interface->device_new(plugin, device, evdev, udev_device);
+}
+
+void
+libinput_plugin_notify_device_added(struct libinput_plugin *plugin,
+				    struct libinput_device *device)
+{
+	if (plugin->interface->device_added)
+		plugin->interface->device_added(plugin, device);
+}
+
+void
+libinput_plugin_notify_device_ignored(struct libinput_plugin *plugin,
+				      struct libinput_device *device)
+{
+	if (plugin->interface->device_ignored)
+		plugin->interface->device_ignored(plugin, device);
+}
+
+void
+libinput_plugin_notify_device_removed(struct libinput_plugin *plugin,
+				      struct libinput_device *device)
+{
+	if (plugin->interface->device_removed)
+		plugin->interface->device_removed(plugin, device);
+}
+
+static void
+plugin_system_append_path(struct libinput_plugin_system *plugin_system,
+			  const char *path)
+{
+	if (strv_find(plugin_system->directories, path, NULL))
+		return;
+
+	plugin_system->directories =
+		strv_append_strdup(plugin_system->directories, path);
+}
+
+LIBINPUT_EXPORT void
+libinput_plugin_system_append_path(struct libinput *libinput, const char *path)
+{
+	if (libinput->plugin_system.loaded) {
+		log_bug_client(libinput, "plugin system already initialized\n");
+		return;
+	}
+
+	libinput->plugin_system.autoload = false;
+
+	plugin_system_append_path(&libinput->plugin_system, path);
+}
+
+LIBINPUT_EXPORT void
+libinput_plugin_system_append_default_paths(struct libinput *libinput)
+{
+	if (libinput->plugin_system.loaded) {
+		log_bug_client(libinput, "plugin system already initialized\n");
+		return;
+	}
+
+	libinput->plugin_system.autoload = false;
+
+	plugin_system_append_path(&libinput->plugin_system, LIBINPUT_PLUGIN_ETCDIR);
+	plugin_system_append_path(&libinput->plugin_system, LIBINPUT_PLUGIN_LIBDIR);
+}
+
+void
+libinput_plugin_system_autoload(struct libinput *libinput)
+{
+	if (libinput->plugin_system.loaded)
+		return;
+
+	if (libinput->plugin_system.autoload) {
+		libinput_plugin_system_append_default_paths(libinput);
+		libinput_plugin_system_load_plugins(libinput,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+	} else {
+		libinput_plugin_system_load_internal_plugins(libinput,
+							     &libinput->plugin_system);
+	}
+}
+
+LIBINPUT_EXPORT int
+libinput_plugin_system_load_plugins(struct libinput *libinput,
+				    enum libinput_plugin_system_flags flags)
+{
+	if (libinput->plugin_system.loaded) {
+		log_bug_client(libinput, "%s() called twice\n", __func__);
+		return 0;
+	}
+
+#ifdef HAVE_LUA
+	_autostrvfree_ char **directories = steal(&libinput->plugin_system.directories);
+	size_t nfiles = 0;
+	_autostrvfree_ char **plugin_files =
+		list_files((const char **)directories, ".lua", &nfiles);
+	for (size_t i = 0; i < nfiles; i++) {
+		char *path = plugin_files[i];
+		log_debug(libinput, "Loading plugin from %s\n", path);
+		libinput_lua_plugin_new_from_path(libinput, path);
+	}
+#endif
+
+	libinput_plugin_system_load_internal_plugins(libinput,
+						     &libinput->plugin_system);
+	libinput->plugin_system.loaded = true;
+
+	libinput_plugin_system_run(&libinput->plugin_system);
+
+#ifdef HAVE_PLUGINS
+	return 0;
+#else
+	return -ENOSYS;
+#endif
+}
+
+void
+libinput_plugin_system_run(struct libinput_plugin_system *system)
+{
+	struct libinput_plugin *plugin;
+	list_for_each_safe(plugin, &system->plugins, link) {
+		libinput_plugin_run(plugin);
+	}
+}
+
+void
+libinput_plugin_system_register_plugin(struct libinput_plugin_system *system,
+				       struct libinput_plugin *plugin)
+{
+	libinput_plugin_ref(plugin);
+	list_append(&system->plugins, &plugin->link);
+}
+
+void
+libinput_plugin_system_unregister_plugin(struct libinput_plugin_system *system,
+					 struct libinput_plugin *plugin)
+{
+	struct libinput_plugin *p;
+	list_for_each(p, &system->plugins, link) {
+		if (p == plugin) {
+			list_remove(&plugin->link);
+			list_append(&system->removed_plugins, &plugin->link);
+			return;
+		}
+	}
+}
+
+static void
+libinput_plugin_system_drop_unregistered_plugins(struct libinput_plugin_system *system)
+{
+	struct libinput_plugin *plugin;
+	list_for_each_safe(plugin, &system->removed_plugins, link) {
+		list_remove(&plugin->link);
+		list_init(&plugin->link); /* allow list_remove in unref */
+		libinput_plugin_unref(plugin);
+	}
+}
+
+void
+libinput_plugin_system_init(struct libinput_plugin_system *system)
+{
+	system->loaded = false;
+#ifdef AUTOLOAD_PLUGINS
+	system->autoload = true;
+#else
+	system->autoload = false;
+#endif
+	list_init(&system->plugins);
+	list_init(&system->removed_plugins);
+}
+
+static void
+libinput_plugin_system_load_internal_plugins(struct libinput *libinput,
+					     struct libinput_plugin_system *system)
+{
+	if (system->loaded)
+		return;
+
+	system->loaded = true;
+
+#ifdef HAVE_MTDEV
+	libinput_mtdev_plugin(libinput);
+#endif
+	libinput_tablet_plugin_forced_tool(libinput);
+	libinput_tablet_plugin_double_tool(libinput);
+	libinput_tablet_plugin_proximity_timer(libinput);
+	libinput_tablet_plugin_eraser_button(libinput);
+	libinput_debounce_plugin(libinput);
+	libinput_mouse_plugin_wheel_lowres(libinput);
+	libinput_mouse_plugin_wheel(libinput);
+
+	/* Our own event dispatch is implemented as mini-plugin,
+	 * guarantee this one to always be last (and after any
+	 * other plugins have run so none of the devices are
+	 * actually connected to anything yet */
+	libinput_evdev_dispatch_plugin(libinput);
+}
+
+void
+libinput_plugin_system_destroy(struct libinput_plugin_system *system)
+{
+	struct libinput_plugin *plugin;
+	list_for_each_safe(plugin, &system->plugins, link) {
+		libinput_plugin_unregister(plugin);
+	}
+
+	libinput_plugin_system_drop_unregistered_plugins(system);
+
+	strv_free(system->directories);
+}
+
+void
+libinput_plugin_system_notify_device_new(struct libinput_plugin_system *system,
+					 struct libinput_device *device,
+					 struct libevdev *evdev,
+					 struct udev_device *udev_device)
+{
+	struct libinput_plugin *plugin;
+	list_for_each_safe(plugin, &system->plugins, link) {
+		libinput_plugin_notify_device_new(plugin, device, evdev, udev_device);
+	}
+	libinput_plugin_system_drop_unregistered_plugins(system);
+}
+
+void
+libinput_plugin_system_notify_device_added(struct libinput_plugin_system *system,
+					   struct libinput_device *device)
+{
+	struct libinput_plugin *plugin;
+	list_for_each_safe(plugin, &system->plugins, link) {
+		libinput_plugin_notify_device_added(plugin, device);
+	}
+	libinput_plugin_system_drop_unregistered_plugins(system);
+
+	/* Now that we added all our devices in all our plugins, notify
+	 * all plugins about disabled features. Some plugins may get
+	 * this notification twice but they should be able to handle that
+	 * case.
+	 */
+	enum libinput_feature feature = _LIBINPUT_N_FEATURES;
+
+	while (--feature > 0) {
+		if (bitmask_bit_is_set(device->disabled_features, feature)) {
+			libinput_plugin_system_notify_device_feature_disabled(system,
+									      device,
+									      feature);
+		}
+	}
+}
+
+void
+libinput_plugin_system_notify_device_removed(struct libinput_plugin_system *system,
+					     struct libinput_device *device)
+{
+	struct libinput_plugin *plugin;
+	list_for_each_safe(plugin, &system->plugins, link) {
+		libinput_plugin_notify_device_removed(plugin, device);
+	}
+	libinput_plugin_system_drop_unregistered_plugins(system);
+}
+
+void
+libinput_plugin_system_notify_device_ignored(struct libinput_plugin_system *system,
+					     struct libinput_device *device)
+{
+	struct libinput_plugin *plugin;
+	list_for_each_safe(plugin, &system->plugins, link) {
+		libinput_plugin_notify_device_ignored(plugin, device);
+	}
+	libinput_plugin_system_drop_unregistered_plugins(system);
+}
+
+void
+libinput_plugin_system_notify_tablet_tool_configured(
+	struct libinput_plugin_system *system,
+	struct libinput_tablet_tool *tool)
+{
+	struct libinput_plugin *plugin;
+	list_for_each_safe(plugin, &system->plugins, link) {
+		if (plugin->interface->tool_configured)
+			plugin->interface->tool_configured(plugin, tool);
+	}
+	libinput_plugin_system_drop_unregistered_plugins(system);
+}
+
+void
+libinput_plugin_system_notify_device_feature_disabled(
+	struct libinput_plugin_system *system,
+	struct libinput_device *device,
+	enum libinput_feature feature)
+{
+	libinput_device_disable_feature(device, feature);
+
+	struct libinput_plugin *plugin;
+	list_for_each_safe(plugin, &system->plugins, link) {
+		if (plugin->interface->feature_disabled)
+			plugin->interface->feature_disabled(plugin, device, feature);
+	}
+	libinput_plugin_system_drop_unregistered_plugins(system);
+}
+
+static void
+libinput_plugin_process_frame(struct libinput_plugin *plugin,
+			      struct libinput_device *device,
+			      struct evdev_frame *frame,
+			      struct list *queued_events)
+{
+	struct list before_events = LIST_INIT(before_events);
+	struct list after_events = LIST_INIT(after_events);
+
+	plugin->event_queue.before = &before_events;
+	plugin->event_queue.after = &after_events;
+
+	if (plugin->interface->evdev_frame)
+		plugin->interface->evdev_frame(plugin, device, frame);
+
+	plugin->event_queue.before = NULL;
+	plugin->event_queue.after = NULL;
+
+	list_chain(queued_events, &before_events);
+
+	if (!evdev_frame_is_empty(frame)) {
+		struct plugin_queued_event *event =
+			plugin_queued_event_new(frame, device);
+		list_take_append(queued_events, event, link);
+	}
+
+	list_chain(queued_events, &after_events);
+}
+
+_unused_ static inline void
+print_frame(struct libinput *libinput, struct evdev_frame *frame, const char *prefix)
+{
+	static uint32_t offset = 0;
+	static uint32_t last_time = 0;
+	uint32_t time = evdev_frame_get_time(frame) / 1000;
+
+	if (offset == 0) {
+		offset = time;
+		last_time = time - offset;
+	}
+
+	time -= offset;
+
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+
+	for (size_t i = 0; i < nevents; i++) {
+		struct evdev_event e = events[i];
+		enum evdev_usage usage = evdev_usage_enum(e.usage);
+
+		if ((usage > EVDEV_KEY_ESC && usage < EVDEV_KEY_CAPSLOCK) ||
+		    (usage >= EVDEV_KEY_KP7 && usage <= EVDEV_KEY_KPDOT)) {
+			e.usage = evdev_usage_from(EVDEV_KEY_A);
+		} else if (usage == EVDEV_MSC_SCAN) {
+			e.value = 30; /* KEY_A scancode */
+		}
+
+		switch (evdev_usage_enum(e.usage)) {
+		case EVDEV_SYN_REPORT:
+			log_debug(
+				libinput,
+				"%s%u.%03u ----------------- EV_SYN ----------------- +%ums\n",
+				prefix,
+				time / 1000,
+				time % 1000,
+				time - last_time);
+
+			last_time = time;
+			break;
+		case EVDEV_MSC_SERIAL:
+			log_debug(libinput,
+				  "%s%u.%03u %-16s %-16s %#010x\n",
+				  prefix,
+				  time / 1000,
+				  time % 1000,
+				  evdev_event_get_type_name(&e),
+				  evdev_event_get_code_name(&e),
+				  e.value);
+			break;
+		default:
+			log_debug(libinput,
+				  "%s%u.%03u %-16s %-20s %4d\n",
+				  prefix,
+				  time / 1000,
+				  time % 1000,
+				  evdev_event_get_type_name(&e),
+				  evdev_event_get_code_name(&e),
+				  e.value);
+			break;
+		}
+	}
+}
+
+static bool
+plugin_has_mask(struct libinput_plugin *plugin, struct evdev_frame *frame)
+{
+	/* A plugin without a mask wants all events */
+	if (plugin->mask == NULL)
+		return true;
+
+	size_t nevents;
+	struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
+
+	/* nevents - 1 because we don't check the SYN_REPORT */
+	for (size_t i = 0; i < nevents - 1; i++) {
+		struct evdev_event *e = &events[i];
+
+		if (evdev_mask_is_set(plugin->mask, e->usage))
+			return true;
+	}
+
+	return false;
+}
+
+static void
+plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
+				 struct libinput_device *device,
+				 struct evdev_frame *frame,
+				 struct libinput_plugin *sender_plugin)
+{
+	/* This is messy because a single event frame may cause
+	 * *each* plugin to generate multiple event frames for potentially
+	 * different devices and replaying is basically breadth-first traversal.
+	 *
+	 * So we have our event (passed in as 'frame') and we create a queue.
+	 * Each plugin then creates a new event list from each frame in the
+	 * queue.
+	 */
+	struct plugin_queued_event *our_event = plugin_queued_event_new(frame, device);
+
+	struct list queued_events = LIST_INIT(queued_events);
+	list_take_insert(&queued_events, our_event, link);
+
+	uint64_t frame_time = evdev_frame_get_time(frame);
+
+	bool delay = !!sender_plugin;
+
+	struct libinput_plugin *plugin;
+	list_for_each_safe(plugin, &system->plugins, link) {
+		/* We start processing *after* the sender plugin. sender_plugin
+		 * is only set if we're queuing (not injecting) events from
+		 * a plugin timer func
+		 */
+		if (delay) {
+			delay = plugin != sender_plugin;
+			continue;
+		}
+
+		/* The list of queued events for the *next* plugin */
+		struct list next_events = LIST_INIT(next_events);
+
+		/* Iterate through the current list of queued events, pass
+		 * each through to the plugin and remove it from the current
+		 * list. The plugin may generate a new event list (possibly
+		 * containing our frame but not our queued_event directly)
+		 * and that list becomes the event list for the next plugin.
+		 */
+		struct plugin_queued_event *event;
+		list_for_each_safe(event, &queued_events, link) {
+			struct list next = LIST_INIT(next);
+
+			if (evdev_frame_get_time(event->frame) == 0)
+				evdev_frame_set_time(event->frame, frame_time);
+
+			if (!bitmask_bit_is_set(device->plugin_frame_callbacks,
+						plugin->index) ||
+			    !plugin_has_mask(plugin, event->frame)) {
+				list_remove(&event->link);
+				list_append(&next_events, &event->link);
+				continue;
+			}
+
+#ifdef EVENT_DEBUGGING
+			_autofree_ char *prefix = strdup_printf(
+				"%7s: plugin %-22s - ",
+				libinput_device_get_sysname(event->device),
+				plugin->name);
+			print_frame(libinput_device_get_context(device),
+				    event->frame,
+				    prefix);
+#endif
+
+			libinput_plugin_process_frame(plugin,
+						      event->device,
+						      event->frame,
+						      &next);
+
+			list_chain(&next_events, &next);
+			plugin_queued_event_destroy(event);
+		}
+		assert(list_empty(&queued_events));
+		list_chain(&queued_events, &next_events);
+		if (list_empty(&queued_events)) {
+#ifdef EVENT_DEBUGGING
+			if (list_last_entry_by_type(&system->plugins,
+						    struct libinput_plugin,
+						    link) != plugin) {
+				log_debug(
+					libinput_device_get_context(device),
+					"%s: --- empty frame queue - end of events ---\n",
+					plugin->name);
+			}
+#endif
+			/* No more events to process, stop here */
+			break;
+		}
+	}
+
+	/* Our own evdev plugin is last and discards the event for us */
+	if (!list_empty(&queued_events)) {
+		log_bug_libinput(libinput_device_get_context(device),
+				 "Events left over to replay after last plugin\n");
+	}
+	libinput_plugin_system_drop_unregistered_plugins(system);
+}
+
+void
+libinput_plugin_system_notify_evdev_frame(struct libinput_plugin_system *system,
+					  struct libinput_device *device,
+					  struct evdev_frame *frame)
+{
+	plugin_system_notify_evdev_frame(system, device, frame, NULL);
+}
+
+static void
+plugin_timer_func(uint64_t now, void *data)
+{
+	struct libinput_plugin_timer *timer = data;
+	struct libinput_plugin *plugin = timer->plugin;
+	struct libinput *libinput = plugin->libinput;
+
+	if (!timer->func)
+		return;
+
+	struct list before_events = LIST_INIT(before_events);
+	struct list after_events = LIST_INIT(after_events);
+
+	plugin->event_queue.before = &before_events;
+	plugin->event_queue.after = &after_events;
+	timer->func(plugin, now, timer->user_data);
+	plugin->event_queue.before = NULL;
+	plugin->event_queue.after = NULL;
+
+	list_chain(&before_events, &after_events);
+
+	struct plugin_queued_event *event;
+	list_for_each_safe(event, &before_events, link) {
+		plugin_system_notify_evdev_frame(&libinput->plugin_system,
+						 event->device,
+						 event->frame,
+						 plugin);
+		plugin_queued_event_destroy(event);
+	}
+}
+
+struct libinput_plugin_timer *
+libinput_plugin_timer_new(struct libinput_plugin *plugin,
+			  const char *name,
+			  void (*func)(struct libinput_plugin *plugin,
+				       uint64_t now,
+				       void *data),
+			  void *data)
+{
+	struct libinput_plugin_timer *timer = zalloc(sizeof(*timer));
+
+	_autofree_ char *timer_name = strdup_printf("%s-%s", plugin->name, name);
+
+	timer->plugin = plugin;
+	timer->refcount = 2; /* one for the caller, one for our list */
+	timer->func = func;
+	timer->user_data = data;
+
+	libinput_timer_init(&timer->timer,
+			    plugin->libinput,
+			    timer_name,
+			    plugin_timer_func,
+			    timer);
+
+	list_append(&plugin->timers, &timer->link);
+
+	return timer;
+}
+
+void
+libinput_plugin_timer_set_user_data(struct libinput_plugin_timer *timer,
+				    void *user_data)
+{
+	timer->user_data = user_data;
+}
+
+void *
+libinput_plugin_timer_get_user_data(struct libinput_plugin_timer *timer)
+{
+	return timer->user_data;
+}
+
+struct libinput_plugin_timer *
+libinput_plugin_timer_ref(struct libinput_plugin_timer *timer)
+{
+	assert(timer->refcount > 0);
+	++timer->refcount;
+	return timer;
+}
+
+struct libinput_plugin_timer *
+libinput_plugin_timer_unref(struct libinput_plugin_timer *timer)
+{
+	assert(timer->refcount > 0);
+	if (--timer->refcount == 0) {
+		libinput_timer_cancel(&timer->timer);
+		libinput_timer_destroy(&timer->timer);
+		list_remove(&timer->link);
+		free(timer);
+	}
+	return NULL;
+}
+
+/* Set timer expire time, in absolute us CLOCK_MONOTONIC */
+void
+libinput_plugin_timer_set(struct libinput_plugin_timer *timer, uint64_t expire)
+{
+	libinput_timer_set(&timer->timer, expire);
+}
+
+void
+libinput_plugin_timer_cancel(struct libinput_plugin_timer *timer)
+{
+	libinput_timer_cancel(&timer->timer);
+}
diff -pruN 1.28.1-1/src/libinput-plugin.h 1.30.0-1/src/libinput-plugin.h
--- 1.28.1-1/src/libinput-plugin.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/libinput-plugin.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,361 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include <libevdev/libevdev.h>
+#include <libudev.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+/* Forward declarations instead of #includes to make
+ * this header self-contained (bindgen, etc.) */
+struct evdev_frame;
+enum evdev_usage;
+struct libinput;
+struct libinput_device;
+struct libinput_tablet_tool;
+struct libinput_plugin;
+enum libinput_log_priority;
+enum libinput_feature;
+
+#define plugin_log_debug(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_DEBUG, __VA_ARGS__)
+#define plugin_log_info(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_INFO, __VA_ARGS__)
+#define plugin_log_error(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, __VA_ARGS__)
+#define plugin_log_bug_kernel(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, "kernel bug: " __VA_ARGS__)
+#define plugin_log_bug_libinput(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, "libinput bug: " __VA_ARGS__)
+#define plugin_log_bug_client(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, "client bug: " __VA_ARGS__)
+#define plugin_log_bug(p_, ...) plugin_log_msg((p_), LIBINPUT_LOG_PRIORITY_ERROR, "plugin bug: " __VA_ARGS__)
+
+void
+plugin_log_msg(struct libinput_plugin *plugin,
+	       enum libinput_log_priority priority,
+	       const char *format,
+	       ...);
+
+struct libinput_plugin_interface {
+	void (*run)(struct libinput_plugin *plugin);
+	/**
+	 * Notification that the plugin is about to be destroyed.
+	 * When this function is called, the plugin has already
+	 * been unregistered. The plugin should free any
+	 * resources allocated but not the struct libinput_plugin
+	 * itself.
+	 */
+	void (*destroy)(struct libinput_plugin *plugin);
+	/**
+	 * Notification about a newly added device that has **not** yet
+	 * been added by libinput as struct libinput_device.
+	 */
+	void (*device_new)(struct libinput_plugin *plugin,
+			   struct libinput_device *device,
+			   struct libevdev *evdev,
+			   struct udev_device *udev_device);
+	/**
+	 * Notification that a device (previously announced with device_new)
+	 * was ignored by libinput and was **never** added as struct
+	 * libinput_device.
+	 *
+	 * If a device was added (device_added) then this callback will
+	 * not be called for that device.
+	 */
+	void (*device_ignored)(struct libinput_plugin *plugin,
+			       struct libinput_device *device);
+	/**
+	 * Notification that a device was added to libinput. Called
+	 * after the device_new callback if the device matches libinput's
+	 * expectations.
+	 */
+	void (*device_added)(struct libinput_plugin *plugin,
+			     struct libinput_device *device);
+	/**
+	 * Notification that a previously added device was removed.
+	 */
+	void (*device_removed)(struct libinput_plugin *plugin,
+			       struct libinput_device *device);
+	/**
+	 * Notification that a device submitted a frame event.
+	 */
+	void (*evdev_frame)(struct libinput_plugin *plugin,
+			    struct libinput_device *device,
+			    struct evdev_frame *frame);
+
+	/**
+	 * Notification that a configuration option on a tool
+	 * was modified.
+	 */
+	void (*tool_configured)(struct libinput_plugin *plugin,
+				struct libinput_tablet_tool *tool);
+
+	/**
+	 * Notification that the given feature was disabled on
+	 * the given device.
+	 */
+	void (*feature_disabled)(struct libinput_plugin *plugin,
+				 struct libinput_device *device,
+				 enum libinput_feature feature);
+};
+
+/**
+ * Returns a new plugin with the given interface and, optionally,
+ * the user data. The returned plugin has a refcount of at least 1
+ * and must be unref'd by the caller.
+ * Should an error occur, the plugin must be unregistered by
+ * the caller:
+ *
+ * ```
+ * struct libinput_plugin *plugin = libinput_plugin_new(libinput, ...);
+ * if (some_error_condition) {
+ *     libinput_plugin_unregister(plugin);
+ * }
+ * libinput_plugin_unref(plugin);
+ * ```
+ */
+struct libinput_plugin *
+libinput_plugin_new(struct libinput *libinput,
+		    const char *name,
+		    const struct libinput_plugin_interface *interface,
+		    void *user_data);
+
+const char *
+libinput_plugin_get_name(struct libinput_plugin *plugin);
+
+struct libinput *
+libinput_plugin_get_context(struct libinput_plugin *plugin);
+
+void
+libinput_plugin_unregister(struct libinput_plugin *plugin);
+
+void
+libinput_plugin_set_user_data(struct libinput_plugin *plugin, void *user_data);
+void *
+libinput_plugin_get_user_data(struct libinput_plugin *plugin);
+
+struct libinput_plugin *
+libinput_plugin_ref(struct libinput_plugin *plugin);
+
+struct libinput_plugin *
+libinput_plugin_unref(struct libinput_plugin *plugin);
+
+#ifdef DEFINE_UNREF_CLEANUP_FUNC
+DEFINE_UNREF_CLEANUP_FUNC(libinput_plugin);
+#endif
+
+void
+libinput_plugin_enable_device_event_frame(struct libinput_plugin *plugin,
+					  struct libinput_device *device,
+					  bool enable);
+
+void
+libinput_plugin_disable_device_feature(struct libinput_plugin *plugin,
+				       struct libinput_device *device,
+				       enum libinput_feature feature);
+
+/**
+ * Mask this plugin's evdev_frame function to be called only
+ * if the frame **contains** the given evdev usage. Plugins
+ * that e.g. only care about button events should use this function
+ * to avoid being called for every motion event.
+ *
+ * By default the mask includes all events. Calling this function
+ * changes the behavior to *only* include frames with the usages.
+ *
+ * Note that the frame passed to evdev_frame function contains all
+ * events of that frame (i.e. including events that are not specified
+ * by this mask).
+ */
+void
+libinput_plugin_enable_evdev_usage(struct libinput_plugin *plugin,
+				   enum evdev_usage usage);
+
+/**
+ * Inject a new event frame from the given plugin. This
+ * frame is treated as if it was just sent by the kernel's
+ * event node and is processed immediately, interrupting
+ * any other processing from this device.
+ *
+ * This function can be called any time but unlike
+ * libinput_plugin_append_evdev_frame() and
+ * libinput_plugin_prepend_evdev_frame() it starts
+ * processing the frame at the bottom of the plugin
+ * stack.
+ *
+ * The injected event will also be sent to
+ * the current plugin, a guard needs to be in
+ * place to prevent recursion.
+ *
+ * Injecting events may cause other plugins to
+ * behave unexpectedly (see below). In almost all cases
+ * it is better to use libinput_plugin_append_evdev_frame()
+ * or libinput_plugin_prepend_evdev_frame() to only
+ * affect the plugins logically *after* this
+ * plugin.
+ *
+ * Assuming several plugins P1, P2, and P3 and
+ * an event frame E, and P2 calling this function
+ * with an injected event I:
+ * - P1: receives E, optionally modifies E
+ * - P2: receives E, injects I
+ *   - P1: receives I, optionally modifies I
+ *   - P2: receives I, optionally modifies I
+ *   - P3: receives I, optionally modifies I
+ * - P2: continues processing E, optionally modifies E
+ * - P3: receives E, optionally modifies E
+ *
+ * For P1 the injected event I is received after E,
+ * for P3 the injected event I is received before E.
+ *
+ * An example for event injection being harmful:
+ * A plugin may monitor tablet proximity state and prepent
+ * proximity-in events if the tablet does not send proximity-in
+ * events. This plugin stops monitoring events once it sees correct
+ * proximity-in events.
+ * If another plugin were to inject a proximity event (e.g. to fake a
+ * different tool coming into proximity), the plugin would stop
+ * monitoring. Future proximity events from the tablet will then
+ * have the wrong proximity.
+ * This can be avoided by appending or prepending the events instead
+ * of injecting them.
+ */
+void
+libinput_plugin_inject_evdev_frame(struct libinput_plugin *libinput,
+				   struct libinput_device *device,
+				   struct evdev_frame *frame);
+
+/**
+ * Queue an event frame for the next plugin in sequence, after
+ * the current event frame being processed.
+ *
+ * This function can only be called from within the evdev_frame()
+ * callback or within a plugin's timer func and may be used to
+ * queue new frames. If called multiple times with frames F1, F2, ...
+ * while processing the current frame C, frame sequence to be passed
+ * to the next plugin is C, F1, F2, ...
+ *
+ * Assuming several plugins P1, P2, P3, and P4 and an event frame E,
+ * and P2 and P3 calling this function with an queued event frame Q1 and Q2,
+ * respectively.
+ *
+ * - P1: receives E, optionally modifies E
+ * - P2: receives E, appends Q1, optionally modifies E
+ * - P3: receives E, appends Q2, optionally modifies E
+ * - P3: receives Q1, optionally modifies Q1
+ * - P4: receives E, optionally modifies E
+ * - P4: receives Q2, optionally modifies Q2
+ * - P4: receives Q1, optionally modifies Q1
+ *
+ * Once plugin processing is complete, the event sequence passed
+ * back to libinput is thus [E, Q2, Q1].
+ *
+ * To discard the original event frame, the plugin needs to
+ * call evdev_frame_reset() on the frame passed to it.
+ *
+ * It is a plugin bug to call this function from outside the
+ * evdev_frame() callback or a timer callback.
+ *
+ * If called within a plugin's timer callback, any frames generated by
+ * the plugin will only be seen by plugins after this plugin. These
+ * frames will be processed in the usual evdev_fame() callback and there
+ * is no indication that the events were queued from within a timer
+ * callback. Using the above example:
+ *
+ * - P1: <idle>
+ * - P2: timer callback invoked, appends Q1
+ * - P3: receives Q1, appends Q2, optionally modifies Q1
+ * - P4: receives Q2, optionally modifies Q2
+ * - P4: receives Q1, optionally modifies Q1
+ *
+ * Because there is no current frame during a timer callback
+ * libinput_plugin_append_evdev_frame() and
+ * libinput_plugin_prepend_evdev_frame() are functionally equivalent.
+ * If both functions are used, all events from
+ * libinput_plugin_prepend_evdev_frame() will be queued before
+ * events from libinput_plugin_append_evdev_frame().
+ */
+void
+libinput_plugin_append_evdev_frame(struct libinput_plugin *libinput,
+				   struct libinput_device *device,
+				   struct evdev_frame *frame);
+
+/**
+ * Identical to libinput_plugin_append_evdev_frame(), but prepends
+ * the event frame to the current event frame being processed.
+ * If called multiple times with frames F1, F2, ... while processing
+ * the current frame C, frame sequence to be passed to the next
+ * plugin is F1, F2, ..., C
+ */
+void
+libinput_plugin_prepend_evdev_frame(struct libinput_plugin *libinput,
+				    struct libinput_device *device,
+				    struct evdev_frame *frame);
+
+/**
+ * Create a new timer for the given plugin.
+ *
+ * The timer needs to be set with libinput_plugin_timer_set()
+ * before it can be used.
+ *
+ * The refcount of the returned timer is at least 1, and the caller
+ * must call libinput_plugin_timer_unref() to release it.
+ *
+ * The func given is the callback invoked when the timer expires.
+ * It is passed the user_data given here or in a subsequent
+ * call to libinput_plugin_timer_set_user_data().
+ *
+ * Note that event generating inside a timer is subject to
+ * specific behaviors, see the documentation
+ * to libinput_plugin_append_evdev_frame(), libinput_plugin_prepend_evdev_frame()
+ * and libinput_plugin_inject_evdev_frame() for details.
+ */
+struct libinput_plugin_timer *
+libinput_plugin_timer_new(struct libinput_plugin *plugin,
+			  const char *name,
+			  void (*func)(struct libinput_plugin *plugin,
+				       uint64_t now,
+				       void *user_data),
+			  void *user_data);
+
+struct libinput_plugin_timer *
+libinput_plugin_timer_ref(struct libinput_plugin_timer *timer);
+
+struct libinput_plugin_timer *
+libinput_plugin_timer_unref(struct libinput_plugin_timer *timer);
+
+#ifdef DEFINE_UNREF_CLEANUP_FUNC
+DEFINE_UNREF_CLEANUP_FUNC(libinput_plugin_timer);
+#endif
+
+/* Set timer expire time, in absolute us CLOCK_MONOTONIC */
+void
+libinput_plugin_timer_set(struct libinput_plugin_timer *timer, uint64_t expire);
+
+void
+libinput_plugin_timer_set_user_data(struct libinput_plugin_timer *timer,
+				    void *user_data);
+void *
+libinput_plugin_timer_get_user_data(struct libinput_plugin_timer *timer);
+
+void
+libinput_plugin_timer_cancel(struct libinput_plugin_timer *timer);
diff -pruN 1.28.1-1/src/libinput-private-config.c 1.30.0-1/src/libinput-private-config.c
--- 1.28.1-1/src/libinput-private-config.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/libinput-private-config.c	2025-11-25 03:40:43.000000000 +0000
@@ -30,8 +30,7 @@
 int
 libinput_device_config_gesture_hold_is_available(struct libinput_device *device)
 {
-	if (!libinput_device_has_capability(device,
-					    LIBINPUT_DEVICE_CAP_GESTURE))
+	if (!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_GESTURE))
 		return 0;
 
 	if (!device->config.gesture->get_hold_default(device))
@@ -49,8 +48,8 @@ libinput_device_config_gesture_set_hold_
 		return LIBINPUT_CONFIG_STATUS_INVALID;
 
 	if (!libinput_device_config_gesture_hold_is_available(device)) {
-		return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED :
-				LIBINPUT_CONFIG_STATUS_SUCCESS;
+		return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED
+			      : LIBINPUT_CONFIG_STATUS_SUCCESS;
 	}
 
 	return device->config.gesture->set_hold_enabled(device, enable);
diff -pruN 1.28.1-1/src/libinput-private.h 1.30.0-1/src/libinput-private.h
--- 1.28.1-1/src/libinput-private.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/libinput-private.h	2025-11-25 03:40:43.000000000 +0000
@@ -31,19 +31,77 @@
 #include <math.h>
 #include <stdarg.h>
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 #include <libwacom/libwacom.h>
 #endif
 
-#include "linux/input.h"
+#include "util-bits.h"
+#include "util-newtype.h"
 
-#include "libinput.h"
+#include "libinput-log.h"
+#include "libinput-plugin-system.h"
 #include "libinput-private-config.h"
 #include "libinput-util.h"
 #include "libinput-version.h"
+#include "libinput.h"
+#include "linux/input.h"
+#include "quirks.h"
 
 struct libinput_source;
 
+/* The tablet tool pressure offset */
+DECLARE_NEWTYPE(pressure_offset, double);
+
+/**
+ * A numeric button such as used by the tablet pad
+ */
+DECLARE_NEWTYPE(pad_button, uint32_t);
+
+/**
+ * An evdev button code
+ */
+DECLARE_NEWTYPE(button_code, uint32_t);
+
+/**
+ * An evdev key code
+ */
+DECLARE_NEWTYPE(keycode, uint32_t);
+
+static inline pressure_offset_t
+pressure_offset_from_range(double min, double max, double value)
+{
+	return pressure_offset_from_double((value - min) / (max - min));
+}
+
+static inline pressure_offset_t
+pressure_offset_from_hundred(double hundred)
+{
+	assert(hundred >= 0);
+	assert(hundred <= 100);
+	return pressure_offset_from_double(hundred / 100);
+}
+
+static inline double
+pressure_offset_to_hundred(pressure_offset_t pressure_offset)
+{
+	return pressure_offset_as_double(pressure_offset) * 100;
+}
+
+static inline pressure_offset_t
+pressure_offset_from_absinfo(const struct input_absinfo *abs, int value)
+{
+	return pressure_offset_from_range(abs->minimum, abs->maximum, value);
+}
+
+static inline int
+pressure_offset_to_absinfo(pressure_offset_t pressure_offset,
+			   const struct input_absinfo *abs)
+{
+	return (abs->maximum - abs->minimum) *
+		       pressure_offset_as_double(pressure_offset) +
+	       abs->minimum;
+}
+
 /* A coordinate pair in device coordinates */
 struct device_coords {
 	int x, y;
@@ -169,7 +227,9 @@ struct libinput {
 	bool quirks_initialized;
 	struct quirks_context *quirks;
 
-#if HAVE_LIBWACOM
+	struct libinput_plugin_system plugin_system;
+
+#ifdef HAVE_LIBWACOM
 	struct {
 		WacomDeviceDatabase *db;
 		size_t refcount;
@@ -177,7 +237,7 @@ struct libinput {
 #endif
 };
 
-typedef void (*libinput_seat_destroy_func) (struct libinput_seat *seat);
+typedef void (*libinput_seat_destroy_func)(struct libinput_seat *seat);
 
 struct libinput_seat {
 	struct libinput *libinput;
@@ -197,60 +257,74 @@ struct libinput_seat {
 
 struct libinput_device_config_tap {
 	int (*count)(struct libinput_device *device);
-	enum libinput_config_status (*set_enabled)(struct libinput_device *device,
-						   enum libinput_config_tap_state enable);
+	enum libinput_config_status (*set_enabled)(
+		struct libinput_device *device,
+		enum libinput_config_tap_state enable);
 	enum libinput_config_tap_state (*get_enabled)(struct libinput_device *device);
 	enum libinput_config_tap_state (*get_default)(struct libinput_device *device);
 
 	enum libinput_config_status (*set_map)(struct libinput_device *device,
-						   enum libinput_config_tap_button_map map);
+					       enum libinput_config_tap_button_map map);
 	enum libinput_config_tap_button_map (*get_map)(struct libinput_device *device);
-	enum libinput_config_tap_button_map (*get_default_map)(struct libinput_device *device);
+	enum libinput_config_tap_button_map (*get_default_map)(
+		struct libinput_device *device);
 
-	enum libinput_config_status (*set_drag_enabled)(struct libinput_device *device,
-							enum libinput_config_drag_state);
-	enum libinput_config_drag_state (*get_drag_enabled)(struct libinput_device *device);
-	enum libinput_config_drag_state (*get_default_drag_enabled)(struct libinput_device *device);
-
-	enum libinput_config_status (*set_draglock_enabled)(struct libinput_device *device,
-							    enum libinput_config_drag_lock_state);
-	enum libinput_config_drag_lock_state (*get_draglock_enabled)(struct libinput_device *device);
-	enum libinput_config_drag_lock_state (*get_default_draglock_enabled)(struct libinput_device *device);
+	enum libinput_config_status (*set_drag_enabled)(
+		struct libinput_device *device,
+		enum libinput_config_drag_state);
+	enum libinput_config_drag_state (*get_drag_enabled)(
+		struct libinput_device *device);
+	enum libinput_config_drag_state (*get_default_drag_enabled)(
+		struct libinput_device *device);
+
+	enum libinput_config_status (*set_draglock_enabled)(
+		struct libinput_device *device,
+		enum libinput_config_drag_lock_state);
+	enum libinput_config_drag_lock_state (*get_draglock_enabled)(
+		struct libinput_device *device);
+	enum libinput_config_drag_lock_state (*get_default_draglock_enabled)(
+		struct libinput_device *device);
 };
 
 struct libinput_device_config_3fg_drag {
 	int (*count)(struct libinput_device *device);
-	enum libinput_config_status (*set_enabled)(struct libinput_device *device,
-						   enum libinput_config_3fg_drag_state enable);
-	enum libinput_config_3fg_drag_state (*get_enabled)(struct libinput_device *device);
-	enum libinput_config_3fg_drag_state (*get_default)(struct libinput_device *device);
-
+	enum libinput_config_status (*set_enabled)(
+		struct libinput_device *device,
+		enum libinput_config_3fg_drag_state enable);
+	enum libinput_config_3fg_drag_state (*get_enabled)(
+		struct libinput_device *device);
+	enum libinput_config_3fg_drag_state (*get_default)(
+		struct libinput_device *device);
 };
 
 struct libinput_device_config_calibration {
 	int (*has_matrix)(struct libinput_device *device);
 	enum libinput_config_status (*set_matrix)(struct libinput_device *device,
 						  const float matrix[6]);
-	int (*get_matrix)(struct libinput_device *device,
-			  float matrix[6]);
-	int (*get_default_matrix)(struct libinput_device *device,
-							  float matrix[6]);
+	int (*get_matrix)(struct libinput_device *device, float matrix[6]);
+	int (*get_default_matrix)(struct libinput_device *device, float matrix[6]);
 };
 
 struct libinput_device_config_area {
 	int (*has_rectangle)(struct libinput_device *device);
-	enum libinput_config_status (*set_rectangle)(struct libinput_device *device,
-						     const struct libinput_config_area_rectangle *rectangle);
-	struct libinput_config_area_rectangle (*get_rectangle)(struct libinput_device *device);
-	struct libinput_config_area_rectangle (*get_default_rectangle)(struct libinput_device *device);
+	enum libinput_config_status (*set_rectangle)(
+		struct libinput_device *device,
+		const struct libinput_config_area_rectangle *rectangle);
+	struct libinput_config_area_rectangle (*get_rectangle)(
+		struct libinput_device *device);
+	struct libinput_config_area_rectangle (*get_default_rectangle)(
+		struct libinput_device *device);
 };
 
 struct libinput_device_config_send_events {
 	uint32_t (*get_modes)(struct libinput_device *device);
-	enum libinput_config_status (*set_mode)(struct libinput_device *device,
-						   enum libinput_config_send_events_mode mode);
-	enum libinput_config_send_events_mode (*get_mode)(struct libinput_device *device);
-	enum libinput_config_send_events_mode (*get_default_mode)(struct libinput_device *device);
+	enum libinput_config_status (*set_mode)(
+		struct libinput_device *device,
+		enum libinput_config_send_events_mode mode);
+	enum libinput_config_send_events_mode (*get_mode)(
+		struct libinput_device *device);
+	enum libinput_config_send_events_mode (*get_default_mode)(
+		struct libinput_device *device);
 };
 
 /**
@@ -290,7 +364,7 @@ struct libinput_config_accel_custom_func
 struct libinput_config_accel {
 	enum libinput_config_accel_profile profile;
 
-	struct  {
+	struct {
 		struct libinput_config_accel_custom_func *fallback;
 		struct libinput_config_accel_custom_func *motion;
 		struct libinput_config_accel_custom_func *scroll;
@@ -307,10 +381,13 @@ struct libinput_device_config_accel {
 	uint32_t (*get_profiles)(struct libinput_device *device);
 	enum libinput_config_status (*set_profile)(struct libinput_device *device,
 						   enum libinput_config_accel_profile);
-	enum libinput_config_accel_profile (*get_profile)(struct libinput_device *device);
-	enum libinput_config_accel_profile (*get_default_profile)(struct libinput_device *device);
-	enum libinput_config_status (*set_accel_config)(struct libinput_device *device,
-						        struct libinput_config_accel *accel_config);
+	enum libinput_config_accel_profile (*get_profile)(
+		struct libinput_device *device);
+	enum libinput_config_accel_profile (*get_default_profile)(
+		struct libinput_device *device);
+	enum libinput_config_status (*set_accel_config)(
+		struct libinput_device *device,
+		struct libinput_config_accel *accel_config);
 };
 
 struct libinput_device_config_natural_scroll {
@@ -323,86 +400,97 @@ struct libinput_device_config_natural_sc
 
 struct libinput_device_config_left_handed {
 	int (*has)(struct libinput_device *device);
-	enum libinput_config_status (*set)(struct libinput_device *device, int left_handed);
+	enum libinput_config_status (*set)(struct libinput_device *device,
+					   int left_handed);
 	int (*get)(struct libinput_device *device);
 	int (*get_default)(struct libinput_device *device);
 };
 
 struct libinput_device_config_scroll_method {
 	uint32_t (*get_methods)(struct libinput_device *device);
-	enum libinput_config_status (*set_method)(struct libinput_device *device,
-						  enum libinput_config_scroll_method method);
-	enum libinput_config_scroll_method (*get_method)(struct libinput_device *device);
-	enum libinput_config_scroll_method (*get_default_method)(struct libinput_device *device);
+	enum libinput_config_status (*set_method)(
+		struct libinput_device *device,
+		enum libinput_config_scroll_method method);
+	enum libinput_config_scroll_method (*get_method)(
+		struct libinput_device *device);
+	enum libinput_config_scroll_method (*get_default_method)(
+		struct libinput_device *device);
 	enum libinput_config_status (*set_button)(struct libinput_device *device,
 						  uint32_t button);
 	uint32_t (*get_button)(struct libinput_device *device);
 	uint32_t (*get_default_button)(struct libinput_device *device);
-	enum libinput_config_status (*set_button_lock)(struct libinput_device *device,
-						       enum libinput_config_scroll_button_lock_state);
-	enum libinput_config_scroll_button_lock_state (*get_button_lock)(struct libinput_device *device);
-	enum libinput_config_scroll_button_lock_state (*get_default_button_lock)(struct libinput_device *device);
+	enum libinput_config_status (*set_button_lock)(
+		struct libinput_device *device,
+		enum libinput_config_scroll_button_lock_state);
+	enum libinput_config_scroll_button_lock_state (*get_button_lock)(
+		struct libinput_device *device);
+	enum libinput_config_scroll_button_lock_state (*get_default_button_lock)(
+		struct libinput_device *device);
 };
 
 struct libinput_device_config_click_method {
 	uint32_t (*get_methods)(struct libinput_device *device);
-	enum libinput_config_status (*set_method)(struct libinput_device *device,
-						  enum libinput_config_click_method method);
+	enum libinput_config_status (*set_method)(
+		struct libinput_device *device,
+		enum libinput_config_click_method method);
 	enum libinput_config_click_method (*get_method)(struct libinput_device *device);
-	enum libinput_config_click_method (*get_default_method)(struct libinput_device *device);
-	enum libinput_config_status (*set_clickfinger_map)(struct libinput_device *device,
-							   enum libinput_config_clickfinger_button_map map);
-	enum libinput_config_clickfinger_button_map (*get_clickfinger_map)(struct libinput_device *device);
-	enum libinput_config_clickfinger_button_map (*get_default_clickfinger_map)(struct libinput_device *device);
+	enum libinput_config_click_method (*get_default_method)(
+		struct libinput_device *device);
+	enum libinput_config_status (*set_clickfinger_map)(
+		struct libinput_device *device,
+		enum libinput_config_clickfinger_button_map map);
+	enum libinput_config_clickfinger_button_map (*get_clickfinger_map)(
+		struct libinput_device *device);
+	enum libinput_config_clickfinger_button_map (*get_default_clickfinger_map)(
+		struct libinput_device *device);
 };
 
 struct libinput_device_config_middle_emulation {
 	int (*available)(struct libinput_device *device);
-	enum libinput_config_status (*set)(
-			 struct libinput_device *device,
-			 enum libinput_config_middle_emulation_state);
+	enum libinput_config_status (*set)(struct libinput_device *device,
+					   enum libinput_config_middle_emulation_state);
 	enum libinput_config_middle_emulation_state (*get)(
-			 struct libinput_device *device);
+		struct libinput_device *device);
 	enum libinput_config_middle_emulation_state (*get_default)(
-			 struct libinput_device *device);
+		struct libinput_device *device);
 };
 
 struct libinput_device_config_dwt {
 	int (*is_available)(struct libinput_device *device);
 	enum libinput_config_status (*set_enabled)(
-			 struct libinput_device *device,
-			 enum libinput_config_dwt_state enable);
-	enum libinput_config_dwt_state (*get_enabled)(
-			 struct libinput_device *device);
+		struct libinput_device *device,
+		enum libinput_config_dwt_state enable);
+	enum libinput_config_dwt_state (*get_enabled)(struct libinput_device *device);
 	enum libinput_config_dwt_state (*get_default_enabled)(
-			 struct libinput_device *device);
+		struct libinput_device *device);
 };
 
 struct libinput_device_config_dwtp {
 	int (*is_available)(struct libinput_device *device);
 	enum libinput_config_status (*set_enabled)(
-			 struct libinput_device *device,
-			 enum libinput_config_dwtp_state enable);
-	enum libinput_config_dwtp_state (*get_enabled)(
-			 struct libinput_device *device);
+		struct libinput_device *device,
+		enum libinput_config_dwtp_state enable);
+	enum libinput_config_dwtp_state (*get_enabled)(struct libinput_device *device);
 	enum libinput_config_dwtp_state (*get_default_enabled)(
-			 struct libinput_device *device);
+		struct libinput_device *device);
 };
 
 struct libinput_device_config_rotation {
 	int (*is_available)(struct libinput_device *device);
-	enum libinput_config_status (*set_angle)(
-			 struct libinput_device *device,
-			 unsigned int degrees_cw);
+	enum libinput_config_status (*set_angle)(struct libinput_device *device,
+						 unsigned int degrees_cw);
 	unsigned int (*get_angle)(struct libinput_device *device);
 	unsigned int (*get_default_angle)(struct libinput_device *device);
 };
 
 struct libinput_device_config_gesture {
-	enum libinput_config_status (*set_hold_enabled)(struct libinput_device *device,
-			 enum libinput_config_hold_state enabled);
-	enum libinput_config_hold_state (*get_hold_enabled)(struct libinput_device *device);
-	enum libinput_config_hold_state (*get_hold_default)(struct libinput_device *device);
+	enum libinput_config_status (*set_hold_enabled)(
+		struct libinput_device *device,
+		enum libinput_config_hold_state enabled);
+	enum libinput_config_hold_state (*get_hold_enabled)(
+		struct libinput_device *device);
+	enum libinput_config_hold_state (*get_hold_default)(
+		struct libinput_device *device);
 };
 
 struct libinput_device_config {
@@ -439,6 +527,16 @@ struct libinput_device {
 	void *user_data;
 	int refcount;
 	struct libinput_device_config config;
+
+	bitmask_t plugin_frame_callbacks;
+	/**
+	 * Lua plugins see the device before our internal
+	 * plugins do any calls need to be cached.
+	 */
+	bitmask_t disabled_features;
+
+	void (*inject_evdev_frame)(struct libinput_device *device,
+				   struct evdev_frame *frame);
 };
 
 enum libinput_tablet_tool_axis {
@@ -479,9 +577,29 @@ enum pressure_heuristic_state {
 
 struct libinput_tablet_tool_config_pressure_range {
 	int (*is_available)(struct libinput_tablet_tool *tool);
-	enum libinput_config_status (*set)(struct libinput_tablet_tool *tool, double min, double max);
+	enum libinput_config_status (*set)(struct libinput_tablet_tool *tool,
+					   double min,
+					   double max);
 	void (*get)(struct libinput_tablet_tool *tool, double *min, double *max);
-	void (*get_default)(struct libinput_tablet_tool *tool, double *min, double *max);
+	void (*get_default)(struct libinput_tablet_tool *tool,
+			    double *min,
+			    double *max);
+};
+
+struct libinput_tablet_tool_config_eraser_button {
+	bitmask_t (*get_modes)(struct libinput_tablet_tool *tool);
+	enum libinput_config_status (*set_mode)(
+		struct libinput_tablet_tool *tool,
+		enum libinput_config_eraser_button_mode mode);
+	enum libinput_config_eraser_button_mode (*get_mode)(
+		struct libinput_tablet_tool *tool);
+	enum libinput_config_eraser_button_mode (*get_default_mode)(
+		struct libinput_tablet_tool *tool);
+
+	enum libinput_config_status (*set_button)(struct libinput_tablet_tool *tool,
+						  uint32_t button);
+	unsigned int (*get_button)(struct libinput_tablet_tool *tool);
+	unsigned int (*get_default_button)(struct libinput_tablet_tool *tool);
 };
 
 struct libinput_tablet_tool_pressure_threshold {
@@ -490,7 +608,7 @@ struct libinput_tablet_tool_pressure_thr
 	/* The configured axis we actually work with */
 	struct input_absinfo abs_pressure;
 	struct threshold threshold; /* in device coordinates */
-	int offset; /* in device coordinates */
+	pressure_offset_t offset;
 	bool has_offset;
 
 	/* This gives us per-tablet heuristic state which is arguably
@@ -508,23 +626,33 @@ struct libinput_tablet_tool {
 	unsigned char buttons[NCHARS(KEY_MAX) + 1];
 	int refcount;
 	void *user_data;
+	struct libinput_device *last_device;
 
 	struct {
-                /* We're assuming that the *configured* pressure range is per
-                 * tool, not per tablet. The *adjusted* thresholds are then
-                 * per-tablet. */
-                struct normalized_range range;
+		/* We're assuming that the *configured* pressure range is per
+		 * tool, not per tablet. The *adjusted* thresholds are then
+		 * per-tablet. */
+		struct normalized_range range;
 		struct normalized_range wanted_range;
 		bool has_configured_range;
 
-		/* Hard-coded because I doubt we have users with more
-		 * than 4 tablets at the same time */
-		struct libinput_tablet_tool_pressure_threshold thresholds[4];
+		struct libinput_tablet_tool_pressure_threshold threshold;
 	} pressure;
 
 	struct {
+		bitmask_t available_modes;
+		enum libinput_config_eraser_button_mode mode;
+		enum libinput_config_eraser_button_mode want_mode;
+		unsigned int button;
+		unsigned int want_button;
+	} eraser_button;
+
+	struct {
 		struct libinput_tablet_tool_config_pressure_range pressure_range;
+		struct libinput_tablet_tool_config_eraser_button eraser_button;
 	} config;
+
+	unsigned int last_tablet_id; /* tablet_dispatch->tablet_id */
 };
 
 struct libinput_tablet_pad_mode_group {
@@ -554,54 +682,14 @@ struct libinput_event {
 
 struct libinput_event_listener {
 	struct list link;
-	void (*notify_func)(uint64_t time, struct libinput_event *ev, void *notify_func_data);
+	void (*notify_func)(uint64_t time,
+			    struct libinput_event *ev,
+			    void *notify_func_data);
 	void *notify_func_data;
 };
 
 typedef void (*libinput_source_dispatch_t)(void *data);
 
-#define log_debug(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_DEBUG, __VA_ARGS__)
-#define log_info(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_INFO, __VA_ARGS__)
-#define log_error(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, __VA_ARGS__)
-#define log_bug_kernel(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, "kernel bug: " __VA_ARGS__)
-#define log_bug_libinput(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, "libinput bug: " __VA_ARGS__)
-#define log_bug_client(li_, ...) log_msg((li_), LIBINPUT_LOG_PRIORITY_ERROR, "client bug: " __VA_ARGS__)
-
-#define log_debug_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_DEBUG, __VA_ARGS__)
-#define log_info_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_INFO, __VA_ARGS__)
-#define log_error_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_ERROR, __VA_ARGS__)
-#define log_bug_kernel_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_ERROR, "kernel bug: " __VA_ARGS__)
-#define log_bug_libinput_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_ERROR, "libinput bug: " __VA_ARGS__)
-#define log_bug_client_ratelimit(li_, r_, ...) log_msg_ratelimit((li_), (r_), LIBINPUT_LOG_PRIORITY_ERROR, "client bug: " __VA_ARGS__)
-
-static inline bool
-is_logged(const struct libinput *libinput,
-	  enum libinput_log_priority priority)
-{
-       return libinput->log_handler &&
-               libinput->log_priority <= priority;
-}
-
-void
-log_msg_ratelimit(struct libinput *libinput,
-		  struct ratelimit *ratelimit,
-		  enum libinput_log_priority priority,
-		  const char *format, ...)
-	LIBINPUT_ATTRIBUTE_PRINTF(4, 5);
-
-void
-log_msg(struct libinput *libinput,
-	enum libinput_log_priority priority,
-	const char *format, ...)
-	LIBINPUT_ATTRIBUTE_PRINTF(3, 4);
-
-void
-log_msg_va(struct libinput *libinput,
-	   enum libinput_log_priority priority,
-	   const char *format,
-	   va_list args)
-	LIBINPUT_ATTRIBUTE_PRINTF(3, 0);
-
 int
 libinput_init(struct libinput *libinput,
 	      const struct libinput_interface *interface,
@@ -618,12 +706,10 @@ libinput_add_fd(struct libinput *libinpu
 		void *data);
 
 void
-libinput_remove_source(struct libinput *libinput,
-		       struct libinput_source *source);
+libinput_remove_source(struct libinput *libinput, struct libinput_source *source);
 
 int
-open_restricted(struct libinput *libinput,
-		const char *path, int flags);
+open_restricted(struct libinput *libinput, const char *path, int flags);
 
 void
 close_restricted(struct libinput *libinput, int fd);
@@ -639,16 +725,23 @@ libinput_seat_init(struct libinput_seat
 		   libinput_seat_destroy_func destroy);
 
 void
-libinput_device_init(struct libinput_device *device,
-		     struct libinput_seat *seat);
+libinput_device_init(struct libinput_device *device, struct libinput_seat *seat);
+
+bool
+libinput_device_has_model_quirk(struct libinput_device *device, enum quirk model_quirk);
+
+bool
+libinput_device_is_virtual(struct libinput_device *device);
+
+void
+libinput_device_disable_feature(struct libinput_device *device,
+				enum libinput_feature feature);
 
 struct libinput_device_group *
-libinput_device_group_create(struct libinput *libinput,
-			     const char *identifier);
+libinput_device_group_create(struct libinput *libinput, const char *identifier);
 
 struct libinput_device_group *
-libinput_device_group_find_group(struct libinput *libinput,
-				 const char *identifier);
+libinput_device_group_find_group(struct libinput *libinput, const char *identifier);
 
 void
 libinput_device_set_device_group(struct libinput_device *device,
@@ -660,10 +753,9 @@ libinput_device_init_event_listener(stru
 void
 libinput_device_add_event_listener(struct libinput_device *device,
 				   struct libinput_event_listener *listener,
-				   void (*notify_func)(
-						uint64_t time,
-						struct libinput_event *event,
-						void *notify_func_data),
+				   void (*notify_func)(uint64_t time,
+						       struct libinput_event *event,
+						       void *notify_func_data),
 				   void *notify_func_data);
 
 void
@@ -678,7 +770,7 @@ notify_removed_device(struct libinput_de
 void
 keyboard_notify_key(struct libinput_device *device,
 		    uint64_t time,
-		    uint32_t key,
+		    keycode_t key,
 		    enum libinput_key_state state);
 
 void
@@ -695,7 +787,7 @@ pointer_notify_motion_absolute(struct li
 void
 pointer_notify_button(struct libinput_device *device,
 		      uint64_t time,
-		      int32_t button,
+		      button_code_t button,
 		      enum libinput_button_state state);
 
 void
@@ -750,8 +842,7 @@ touch_notify_touch_cancel(struct libinpu
 			  int32_t seat_slot);
 
 void
-touch_notify_frame(struct libinput_device *device,
-		   uint64_t time);
+touch_notify_frame(struct libinput_device *device, uint64_t time);
 
 void
 gesture_notify_swipe(struct libinput_device *device,
@@ -831,7 +922,7 @@ tablet_notify_button(struct libinput_dev
 		     struct libinput_tablet_tool *tool,
 		     enum libinput_tablet_tool_tip_state tip_state,
 		     const struct tablet_axes *axes,
-		     int32_t button,
+		     button_code_t button,
 		     enum libinput_button_state state,
 		     const struct input_absinfo *x,
 		     const struct input_absinfo *y);
@@ -839,9 +930,10 @@ tablet_notify_button(struct libinput_dev
 void
 tablet_pad_notify_button(struct libinput_device *device,
 			 uint64_t time,
-			 int32_t button,
+			 pad_button_t button,
 			 enum libinput_button_state state,
 			 struct libinput_tablet_pad_mode_group *group);
+
 void
 tablet_pad_notify_dial(struct libinput_device *device,
 		       uint64_t time,
@@ -874,20 +966,6 @@ switch_notify_toggle(struct libinput_dev
 		     enum libinput_switch sw,
 		     enum libinput_switch_state state);
 
-static inline uint64_t
-libinput_now(struct libinput *libinput)
-{
-	uint64_t now;
-	int rc = now_in_us(&now);
-
-	if (rc < 0) {
-		log_error(libinput, "clock_gettime failed: %s\n", strerror(-rc));
-		return 0;
-	}
-
-	return now;
-}
-
 static inline struct device_float_coords
 device_delta(const struct device_coords a, const struct device_coords b)
 {
@@ -911,7 +989,8 @@ device_average(const struct device_coord
 }
 
 static inline struct device_float_coords
-device_float_delta(const struct device_float_coords a, const struct device_float_coords b)
+device_float_delta(const struct device_float_coords a,
+		   const struct device_float_coords b)
 {
 	struct device_float_coords delta;
 
@@ -922,7 +1001,8 @@ device_float_delta(const struct device_f
 }
 
 static inline struct device_float_coords
-device_float_average(const struct device_float_coords a, const struct device_float_coords b)
+device_float_average(const struct device_float_coords a,
+		     const struct device_float_coords b)
 {
 	struct device_float_coords average;
 
@@ -957,13 +1037,13 @@ length_in_mm(const struct phys_coords mm
 }
 
 enum directions {
-	N  = bit(0),
+	N = bit(0),
 	NE = bit(1),
-	E  = bit(2),
+	E = bit(2),
 	SE = bit(3),
-	S  = bit(4),
+	S = bit(4),
 	SW = bit(5),
-	W  = bit(6),
+	W = bit(6),
 	NW = bit(7),
 	UNDEFINED_DIRECTION = 0xff
 };
@@ -1000,8 +1080,8 @@ xy_get_direction(double x, double y)
 		 * d_8 = 8 * d_f
 		 */
 		r = atan2(y, x);
-		r = fmod(r + 2.5*M_PI, 2*M_PI);
-		r *= 4*M_1_PI;
+		r = fmod(r + 2.5 * M_PI, 2 * M_PI);
+		r *= 4 * M_1_PI;
 
 		/* Mark one or two close enough octants */
 		d1 = (int)(r + 0.9) % 8;
@@ -1034,23 +1114,27 @@ device_float_get_direction(const struct
  * left edge but excluding the right edge.
  */
 static inline bool
-point_in_rect(const struct device_coords *point,
-	      const struct device_coord_rect *rect)
+point_in_rect(const struct device_coords *point, const struct device_coord_rect *rect)
 {
-	return (point->x >= rect->x &&
-		point->x < rect->x + rect->w &&
-		point->y >= rect->y &&
-		point->y < rect->y + rect->h);
+	return (point->x >= rect->x && point->x < rect->x + rect->w &&
+		point->y >= rect->y && point->y < rect->y + rect->h);
 }
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 WacomDeviceDatabase *
 libinput_libwacom_ref(struct libinput *li);
 void
 libinput_libwacom_unref(struct libinput *li);
 #else
-static inline void *libinput_libwacom_ref(struct libinput *li) { return NULL; }
-static inline void libinput_libwacom_unref(struct libinput *li) {}
+static inline void *
+libinput_libwacom_ref(struct libinput *li)
+{
+	return NULL;
+}
+static inline void
+libinput_libwacom_unref(struct libinput *li)
+{
+}
 #endif
 
 #endif /* LIBINPUT_PRIVATE_H */
diff -pruN 1.28.1-1/src/libinput-uninstalled.pc.in 1.30.0-1/src/libinput-uninstalled.pc.in
--- 1.28.1-1/src/libinput-uninstalled.pc.in	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/libinput-uninstalled.pc.in	1970-01-01 00:00:00.000000000 +0000
@@ -1,10 +0,0 @@
-libdir=@abs_builddir@/.libs
-includedir=@abs_srcdir@
-
-Name: Libinput
-Description: Input device library (not installed)
-Version: @LIBINPUT_VERSION@
-Cflags: -I${includedir}
-Libs: -L${libdir} -linput
-Libs.private: -lm -lrt
-Requires.private: libudev
diff -pruN 1.28.1-1/src/libinput-util.h 1.30.0-1/src/libinput-util.h
--- 1.28.1-1/src/libinput-util.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/libinput-util.h	2025-11-25 03:40:43.000000000 +0000
@@ -31,19 +31,20 @@
 #warning "libinput relies on assert(). #defining NDEBUG is not recommended"
 #endif
 
-#include "libinput.h"
-
 #include "util-bits.h"
-#include "util-macros.h"
 #include "util-list.h"
+#include "util-macros.h"
 #include "util-matrix.h"
+#include "util-mem.h"
 #include "util-multivalue.h"
-#include "util-strings.h"
-#include "util-ratelimit.h"
-#include "util-range.h"
 #include "util-prop-parsers.h"
+#include "util-range.h"
+#include "util-ratelimit.h"
+#include "util-strings.h"
 #include "util-time.h"
 
+#include "libinput.h"
+
 #define VENDOR_ID_APPLE 0x5ac
 #define VENDOR_ID_CHICONY 0x4f2
 #define VENDOR_ID_LOGITECH 0x46d
@@ -69,6 +70,22 @@
 #define etrace(...) _trace(stderr, __VA_ARGS__)
 
 #define LIBINPUT_EXPORT __attribute__ ((visibility("default")))
-#define LIBINPUT_UNUSED __attribute__ ((unused))
+
+/* Commonly-used cleanup  */
+#ifdef udev_list_entry_foreach
+DEFINE_UNREF_CLEANUP_FUNC(udev);
+DEFINE_UNREF_CLEANUP_FUNC(udev_device);
+DEFINE_UNREF_CLEANUP_FUNC(udev_enumerate);
+DEFINE_UNREF_CLEANUP_FUNC(udev_monitor);
+#endif
+#ifdef LIBEVDEV_ATTRIBUTE_PRINTF
+DEFINE_FREE_CLEANUP_FUNC(libevdev);
+#endif
+DEFINE_UNREF_CLEANUP_FUNC(libinput);
+DEFINE_UNREF_CLEANUP_FUNC(libinput_device);
+DEFINE_UNREF_CLEANUP_FUNC(libinput_tablet_tool);
+DEFINE_UNREF_CLEANUP_FUNC(libinput_seat);
+DEFINE_DESTROY_CLEANUP_FUNC(libinput_event);
+DEFINE_DESTROY_CLEANUP_FUNC(libinput_config_accel);
 
 #endif /* LIBINPUT_UTIL_H */
diff -pruN 1.28.1-1/src/libinput-versionsort.h 1.30.0-1/src/libinput-versionsort.h
--- 1.28.1-1/src/libinput-versionsort.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/libinput-versionsort.h	2025-11-25 03:40:43.000000000 +0000
@@ -25,8 +25,8 @@
 #include "config.h"
 
 #include <ctype.h>
-#include <string.h>
 #include <dirent.h>
+#include <string.h>
 
 #if !defined(HAVE_VERSIONSORT) || defined(TEST_VERSIONSORT)
 static inline int
@@ -39,23 +39,29 @@ libinput_strverscmp(const char *l0, cons
 
 	/* Find maximal matching prefix and track its maximal digit
 	 * suffix and whether those digits are all zeros. */
-	for (dp=i=0; l[i]==r[i]; i++) {
+	for (dp = i = 0; l[i] == r[i]; /* NOLINT(clang-analyzer-security.ArrayBound) */
+	     i++) {
 		int c = l[i];
-		if (!c) return 0;
-		if (!isdigit(c)) dp=i+1, z=1;
-		else if (c!='0') z=0;
+		if (!c)
+			return 0;
+		if (!isdigit(c))
+			dp = i + 1, z = 1;
+		else if (c != '0')
+			z = 0;
 	}
 
-	if (l[dp]!='0' && r[dp]!='0') {
+	if (l[dp] != '0' && r[dp] != '0') {
 		/* If we're not looking at a digit sequence that began
 		 * with a zero, longest digit string is greater. */
-		for (j=i; isdigit(l[j]); j++)
-			if (!isdigit(r[j])) return 1;
-		if (isdigit(r[j])) return -1;
-	} else if (z && dp<i && (isdigit(l[i]) || isdigit(r[i]))) {
+		for (j = i; isdigit(l[j]); j++)
+			if (!isdigit(r[j]))
+				return 1;
+		if (isdigit(r[j]))
+			return -1;
+	} else if (z && dp < i && (isdigit(l[i]) || isdigit(r[i]))) {
 		/* Otherwise, if common prefix of digit sequence is
 		 * all zeros, digits order less than non-digits. */
-		return (unsigned char)(l[i]-'0') - (unsigned char)(r[i]-'0');
+		return (unsigned char)(l[i] - '0') - (unsigned char)(r[i] - '0');
 	}
 
 	return l[i] - r[i];
diff -pruN 1.28.1-1/src/libinput.c 1.30.0-1/src/libinput.c
--- 1.28.1-1/src/libinput.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/libinput.c	2025-11-25 03:40:43.000000000 +0000
@@ -24,28 +24,30 @@
 
 #include "config.h"
 
+#include <assert.h>
 #include <errno.h>
 #include <inttypes.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdarg.h>
 #include <string.h>
 #include <sys/epoll.h>
 #include <unistd.h>
-#include <assert.h>
 
-#include "libinput.h"
-#include "libinput-private.h"
 #include "util-input-event.h"
 #include "util-libinput.h"
+
 #include "evdev.h"
-#include "timer.h"
+#include "libinput-feature.h"
+#include "libinput-private.h"
+#include "libinput.h"
 #include "quirks.h"
+#include "timer.h"
 
 #define require_event_type(li_, type_, retval_, ...)	\
 	if (type_ == LIBINPUT_EVENT_NONE) abort(); \
 	if (!check_event_type(li_, __func__, type_, __VA_ARGS__, -1)) \
-		return retval_; \
+		return retval_;
 
 #define ASSERT_INT_SIZE(type_) \
 	static_assert(sizeof(type_) == sizeof(unsigned int), \
@@ -83,7 +85,7 @@ ASSERT_INT_SIZE(enum libinput_config_dwt
 static inline const char *
 event_type_to_str(enum libinput_event_type type)
 {
-	switch(type) {
+	switch (type) {
 	CASE_RETURN_STRING(LIBINPUT_EVENT_DEVICE_ADDED);
 	CASE_RETURN_STRING(LIBINPUT_EVENT_DEVICE_REMOVED);
 	CASE_RETURN_STRING(LIBINPUT_EVENT_KEYBOARD_KEY);
@@ -151,7 +153,9 @@ check_event_type(struct libinput *libinp
 		const char *name = event_type_to_str(type_in);
 		log_bug_client(libinput,
 			       "Invalid event type %s (%d) passed to %s()\n",
-			       name, type_in, function_name);
+			       name,
+			       type_in,
+			       function_name);
 	}
 
 	return rc;
@@ -267,35 +271,51 @@ LIBINPUT_ATTRIBUTE_PRINTF(3, 0)
 static void
 libinput_default_log_func(struct libinput *libinput,
 			  enum libinput_log_priority priority,
-			  const char *format, va_list args)
+			  const char *format,
+			  va_list args)
 {
 	const char *prefix;
 
-	switch(priority) {
-	case LIBINPUT_LOG_PRIORITY_DEBUG: prefix = "debug"; break;
-	case LIBINPUT_LOG_PRIORITY_INFO: prefix = "info"; break;
-	case LIBINPUT_LOG_PRIORITY_ERROR: prefix = "error"; break;
-	default: prefix="<invalid priority>"; break;
+	switch (priority) {
+	case LIBINPUT_LOG_PRIORITY_DEBUG:
+		prefix = "debug";
+		break;
+	case LIBINPUT_LOG_PRIORITY_INFO:
+		prefix = "info";
+		break;
+	case LIBINPUT_LOG_PRIORITY_ERROR:
+		prefix = "error";
+		break;
+	default:
+		prefix = "<invalid priority>";
+		break;
 	}
 
 	fprintf(stderr, "libinput %s: ", prefix);
 	vfprintf(stderr, format, args);
 }
 
+bool
+log_is_logged(const struct libinput *libinput, enum libinput_log_priority priority)
+{
+	return libinput->log_handler && libinput->log_priority <= priority;
+}
+
 void
 log_msg_va(struct libinput *libinput,
 	   enum libinput_log_priority priority,
 	   const char *format,
 	   va_list args)
 {
-	if (is_logged(libinput, priority))
+	if (log_is_logged(libinput, priority))
 		libinput->log_handler(libinput, priority, format, args);
 }
 
 void
 log_msg(struct libinput *libinput,
 	enum libinput_log_priority priority,
-	const char *format, ...)
+	const char *format,
+	...)
 {
 	va_list args;
 
@@ -308,7 +328,8 @@ void
 log_msg_ratelimit(struct libinput *libinput,
 		  struct ratelimit *ratelimit,
 		  enum libinput_log_priority priority,
-		  const char *format, ...)
+		  const char *format,
+		  ...)
 {
 	va_list args;
 	enum ratelimit_state state;
@@ -343,8 +364,7 @@ libinput_log_get_priority(const struct l
 }
 
 LIBINPUT_EXPORT void
-libinput_log_set_handler(struct libinput *libinput,
-			 libinput_log_handler log_handler)
+libinput_log_set_handler(struct libinput *libinput, libinput_log_handler log_handler)
 {
 	libinput->log_handler = log_handler;
 }
@@ -353,8 +373,7 @@ static void
 libinput_device_group_destroy(struct libinput_device_group *group);
 
 static void
-libinput_post_event(struct libinput *libinput,
-		    struct libinput_event *event);
+libinput_post_event(struct libinput *libinput, struct libinput_event *event);
 
 LIBINPUT_EXPORT enum libinput_event_type
 libinput_event_get_type(struct libinput_event *event)
@@ -388,7 +407,7 @@ libinput_event_get_pointer_event(struct
 			   LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS,
 			   LIBINPUT_EVENT_POINTER_AXIS);
 
-	return (struct libinput_event_pointer *) event;
+	return (struct libinput_event_pointer *)event;
 }
 
 LIBINPUT_EXPORT struct libinput_event_keyboard *
@@ -399,7 +418,7 @@ libinput_event_get_keyboard_event(struct
 			   NULL,
 			   LIBINPUT_EVENT_KEYBOARD_KEY);
 
-	return (struct libinput_event_keyboard *) event;
+	return (struct libinput_event_keyboard *)event;
 }
 
 LIBINPUT_EXPORT struct libinput_event_touch *
@@ -413,7 +432,7 @@ libinput_event_get_touch_event(struct li
 			   LIBINPUT_EVENT_TOUCH_MOTION,
 			   LIBINPUT_EVENT_TOUCH_CANCEL,
 			   LIBINPUT_EVENT_TOUCH_FRAME);
-	return (struct libinput_event_touch *) event;
+	return (struct libinput_event_touch *)event;
 }
 
 LIBINPUT_EXPORT struct libinput_event_gesture *
@@ -431,7 +450,7 @@ libinput_event_get_gesture_event(struct
 			   LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
 			   LIBINPUT_EVENT_GESTURE_HOLD_END);
 
-	return (struct libinput_event_gesture *) event;
+	return (struct libinput_event_gesture *)event;
 }
 
 LIBINPUT_EXPORT struct libinput_event_tablet_tool *
@@ -445,7 +464,7 @@ libinput_event_get_tablet_tool_event(str
 			   LIBINPUT_EVENT_TABLET_TOOL_TIP,
 			   LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 
-	return (struct libinput_event_tablet_tool *) event;
+	return (struct libinput_event_tablet_tool *)event;
 }
 
 LIBINPUT_EXPORT struct libinput_event_tablet_pad *
@@ -460,7 +479,7 @@ libinput_event_get_tablet_pad_event(stru
 			   LIBINPUT_EVENT_TABLET_PAD_BUTTON,
 			   LIBINPUT_EVENT_TABLET_PAD_KEY);
 
-	return (struct libinput_event_tablet_pad *) event;
+	return (struct libinput_event_tablet_pad *)event;
 }
 
 LIBINPUT_EXPORT struct libinput_event_device_notify *
@@ -472,7 +491,7 @@ libinput_event_get_device_notify_event(s
 			   LIBINPUT_EVENT_DEVICE_ADDED,
 			   LIBINPUT_EVENT_DEVICE_REMOVED);
 
-	return (struct libinput_event_device_notify *) event;
+	return (struct libinput_event_device_notify *)event;
 }
 
 LIBINPUT_EXPORT struct libinput_event_switch *
@@ -483,7 +502,7 @@ libinput_event_get_switch_event(struct l
 			   NULL,
 			   LIBINPUT_EVENT_SWITCH_TOGGLE);
 
-	return (struct libinput_event_switch *) event;
+	return (struct libinput_event_switch *)event;
 }
 
 LIBINPUT_EXPORT uint32_t
@@ -531,8 +550,7 @@ libinput_event_keyboard_get_key_state(st
 }
 
 LIBINPUT_EXPORT uint32_t
-libinput_event_keyboard_get_seat_key_count(
-	struct libinput_event_keyboard *event)
+libinput_event_keyboard_get_seat_key_count(struct libinput_event_keyboard *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -599,8 +617,7 @@ libinput_event_pointer_get_dy(struct lib
 }
 
 LIBINPUT_EXPORT double
-libinput_event_pointer_get_dx_unaccelerated(
-	struct libinput_event_pointer *event)
+libinput_event_pointer_get_dx_unaccelerated(struct libinput_event_pointer *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -611,8 +628,7 @@ libinput_event_pointer_get_dx_unaccelera
 }
 
 LIBINPUT_EXPORT double
-libinput_event_pointer_get_dy_unaccelerated(
-	struct libinput_event_pointer *event)
+libinput_event_pointer_get_dy_unaccelerated(struct libinput_event_pointer *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -649,9 +665,8 @@ libinput_event_pointer_get_absolute_y(st
 }
 
 LIBINPUT_EXPORT double
-libinput_event_pointer_get_absolute_x_transformed(
-	struct libinput_event_pointer *event,
-	uint32_t width)
+libinput_event_pointer_get_absolute_x_transformed(struct libinput_event_pointer *event,
+						  uint32_t width)
 {
 	struct evdev_device *device = evdev_device(event->base.device);
 
@@ -664,9 +679,8 @@ libinput_event_pointer_get_absolute_x_tr
 }
 
 LIBINPUT_EXPORT double
-libinput_event_pointer_get_absolute_y_transformed(
-	struct libinput_event_pointer *event,
-	uint32_t height)
+libinput_event_pointer_get_absolute_y_transformed(struct libinput_event_pointer *event,
+						  uint32_t height)
 {
 	struct evdev_device *device = evdev_device(event->base.device);
 
@@ -701,8 +715,7 @@ libinput_event_pointer_get_button_state(
 }
 
 LIBINPUT_EXPORT uint32_t
-libinput_event_pointer_get_seat_button_count(
-	struct libinput_event_pointer *event)
+libinput_event_pointer_get_seat_button_count(struct libinput_event_pointer *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1071,8 +1084,7 @@ libinput_event_gesture_get_dy(struct lib
 }
 
 LIBINPUT_EXPORT double
-libinput_event_gesture_get_dx_unaccelerated(
-	struct libinput_event_gesture *event)
+libinput_event_gesture_get_dx_unaccelerated(struct libinput_event_gesture *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1088,8 +1100,7 @@ libinput_event_gesture_get_dx_unaccelera
 }
 
 LIBINPUT_EXPORT double
-libinput_event_gesture_get_dy_unaccelerated(
-	struct libinput_event_gesture *event)
+libinput_event_gesture_get_dy_unaccelerated(struct libinput_event_gesture *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1131,8 +1142,7 @@ libinput_event_gesture_get_angle_delta(s
 }
 
 LIBINPUT_EXPORT int
-libinput_event_tablet_tool_x_has_changed(
-				struct libinput_event_tablet_tool *event)
+libinput_event_tablet_tool_x_has_changed(struct libinput_event_tablet_tool *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1142,13 +1152,11 @@ libinput_event_tablet_tool_x_has_changed
 			   LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
 			   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
-	return bit_is_set(event->changed_axes,
-			  LIBINPUT_TABLET_TOOL_AXIS_X);
+	return bit_is_set(event->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_X);
 }
 
 LIBINPUT_EXPORT int
-libinput_event_tablet_tool_y_has_changed(
-				struct libinput_event_tablet_tool *event)
+libinput_event_tablet_tool_y_has_changed(struct libinput_event_tablet_tool *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1158,13 +1166,12 @@ libinput_event_tablet_tool_y_has_changed
 			   LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
 			   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
-	return bit_is_set(event->changed_axes,
-			  LIBINPUT_TABLET_TOOL_AXIS_Y);
+	return bit_is_set(event->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_Y);
 }
 
 LIBINPUT_EXPORT int
 libinput_event_tablet_tool_pressure_has_changed(
-				struct libinput_event_tablet_tool *event)
+	struct libinput_event_tablet_tool *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1174,13 +1181,12 @@ libinput_event_tablet_tool_pressure_has_
 			   LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
 			   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
-	return bit_is_set(event->changed_axes,
-			  LIBINPUT_TABLET_TOOL_AXIS_PRESSURE);
+	return bit_is_set(event->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE);
 }
 
 LIBINPUT_EXPORT int
 libinput_event_tablet_tool_distance_has_changed(
-				struct libinput_event_tablet_tool *event)
+	struct libinput_event_tablet_tool *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1190,13 +1196,11 @@ libinput_event_tablet_tool_distance_has_
 			   LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
 			   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
-	return bit_is_set(event->changed_axes,
-			  LIBINPUT_TABLET_TOOL_AXIS_DISTANCE);
+	return bit_is_set(event->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_DISTANCE);
 }
 
 LIBINPUT_EXPORT int
-libinput_event_tablet_tool_tilt_x_has_changed(
-				struct libinput_event_tablet_tool *event)
+libinput_event_tablet_tool_tilt_x_has_changed(struct libinput_event_tablet_tool *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1206,13 +1210,11 @@ libinput_event_tablet_tool_tilt_x_has_ch
 			   LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
 			   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
-	return bit_is_set(event->changed_axes,
-			  LIBINPUT_TABLET_TOOL_AXIS_TILT_X);
+	return bit_is_set(event->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_X);
 }
 
 LIBINPUT_EXPORT int
-libinput_event_tablet_tool_tilt_y_has_changed(
-				struct libinput_event_tablet_tool *event)
+libinput_event_tablet_tool_tilt_y_has_changed(struct libinput_event_tablet_tool *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1222,13 +1224,12 @@ libinput_event_tablet_tool_tilt_y_has_ch
 			   LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
 			   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
-	return bit_is_set(event->changed_axes,
-			  LIBINPUT_TABLET_TOOL_AXIS_TILT_Y);
+	return bit_is_set(event->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_TILT_Y);
 }
 
 LIBINPUT_EXPORT int
 libinput_event_tablet_tool_rotation_has_changed(
-				struct libinput_event_tablet_tool *event)
+	struct libinput_event_tablet_tool *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1238,13 +1239,11 @@ libinput_event_tablet_tool_rotation_has_
 			   LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
 			   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
-	return bit_is_set(event->changed_axes,
-			  LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
+	return bit_is_set(event->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
 }
 
 LIBINPUT_EXPORT int
-libinput_event_tablet_tool_slider_has_changed(
-				struct libinput_event_tablet_tool *event)
+libinput_event_tablet_tool_slider_has_changed(struct libinput_event_tablet_tool *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1254,13 +1253,12 @@ libinput_event_tablet_tool_slider_has_ch
 			   LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
 			   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
-	return bit_is_set(event->changed_axes,
-			  LIBINPUT_TABLET_TOOL_AXIS_SLIDER);
+	return bit_is_set(event->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_SLIDER);
 }
 
 LIBINPUT_EXPORT int
 libinput_event_tablet_tool_size_major_has_changed(
-				struct libinput_event_tablet_tool *event)
+	struct libinput_event_tablet_tool *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1270,13 +1268,12 @@ libinput_event_tablet_tool_size_major_ha
 			   LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
 			   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
-	return bit_is_set(event->changed_axes,
-			  LIBINPUT_TABLET_TOOL_AXIS_SIZE_MAJOR);
+	return bit_is_set(event->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_SIZE_MAJOR);
 }
 
 LIBINPUT_EXPORT int
 libinput_event_tablet_tool_size_minor_has_changed(
-				struct libinput_event_tablet_tool *event)
+	struct libinput_event_tablet_tool *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1286,13 +1283,11 @@ libinput_event_tablet_tool_size_minor_ha
 			   LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
 			   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
-	return bit_is_set(event->changed_axes,
-			  LIBINPUT_TABLET_TOOL_AXIS_SIZE_MINOR);
+	return bit_is_set(event->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_SIZE_MINOR);
 }
 
 LIBINPUT_EXPORT int
-libinput_event_tablet_tool_wheel_has_changed(
-				struct libinput_event_tablet_tool *event)
+libinput_event_tablet_tool_wheel_has_changed(struct libinput_event_tablet_tool *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1302,8 +1297,7 @@ libinput_event_tablet_tool_wheel_has_cha
 			   LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
 			   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
-	return bit_is_set(event->changed_axes,
-			  LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL);
+	return bit_is_set(event->changed_axes, LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL);
 }
 
 LIBINPUT_EXPORT double
@@ -1317,8 +1311,7 @@ libinput_event_tablet_tool_get_x(struct
 			   LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
 			   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
-	return absinfo_convert_to_mm(&event->abs.x,
-				     event->axes.point.x);
+	return absinfo_convert_to_mm(&event->abs.x, event->axes.point.x);
 }
 
 LIBINPUT_EXPORT double
@@ -1332,8 +1325,7 @@ libinput_event_tablet_tool_get_y(struct
 			   LIBINPUT_EVENT_TABLET_TOOL_BUTTON,
 			   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
-	return absinfo_convert_to_mm(&event->abs.y,
-				     event->axes.point.y);
+	return absinfo_convert_to_mm(&event->abs.y, event->axes.point.y);
 }
 
 LIBINPUT_EXPORT double
@@ -1492,7 +1484,7 @@ libinput_event_tablet_tool_get_wheel_del
 
 LIBINPUT_EXPORT int
 libinput_event_tablet_tool_get_wheel_delta_discrete(
-				      struct libinput_event_tablet_tool *event)
+	struct libinput_event_tablet_tool *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1628,7 +1620,8 @@ libinput_event_tablet_tool_get_button_st
 }
 
 LIBINPUT_EXPORT uint32_t
-libinput_event_tablet_tool_get_seat_button_count(struct libinput_event_tablet_tool *event)
+libinput_event_tablet_tool_get_seat_button_count(
+	struct libinput_event_tablet_tool *event)
 {
 	require_event_type(libinput_event_get_context(&event->base),
 			   event->base.type,
@@ -1665,55 +1658,47 @@ libinput_tablet_tool_get_serial(struct l
 LIBINPUT_EXPORT int
 libinput_tablet_tool_has_pressure(struct libinput_tablet_tool *tool)
 {
-	return bit_is_set(tool->axis_caps,
-			  LIBINPUT_TABLET_TOOL_AXIS_PRESSURE);
+	return bit_is_set(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_PRESSURE);
 }
 
 LIBINPUT_EXPORT int
 libinput_tablet_tool_has_distance(struct libinput_tablet_tool *tool)
 {
-	return bit_is_set(tool->axis_caps,
-			  LIBINPUT_TABLET_TOOL_AXIS_DISTANCE);
+	return bit_is_set(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_DISTANCE);
 }
 
 LIBINPUT_EXPORT int
 libinput_tablet_tool_has_tilt(struct libinput_tablet_tool *tool)
 {
-	return bit_is_set(tool->axis_caps,
-			  LIBINPUT_TABLET_TOOL_AXIS_TILT_X);
+	return bit_is_set(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_TILT_X);
 }
 
 LIBINPUT_EXPORT int
 libinput_tablet_tool_has_rotation(struct libinput_tablet_tool *tool)
 {
-	return bit_is_set(tool->axis_caps,
-			  LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
+	return bit_is_set(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_ROTATION_Z);
 }
 
 LIBINPUT_EXPORT int
 libinput_tablet_tool_has_slider(struct libinput_tablet_tool *tool)
 {
-	return bit_is_set(tool->axis_caps,
-			  LIBINPUT_TABLET_TOOL_AXIS_SLIDER);
+	return bit_is_set(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_SLIDER);
 }
 
 LIBINPUT_EXPORT int
 libinput_tablet_tool_has_wheel(struct libinput_tablet_tool *tool)
 {
-	return bit_is_set(tool->axis_caps,
-			  LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL);
+	return bit_is_set(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_REL_WHEEL);
 }
 
 LIBINPUT_EXPORT int
 libinput_tablet_tool_has_size(struct libinput_tablet_tool *tool)
 {
-	return bit_is_set(tool->axis_caps,
-			  LIBINPUT_TABLET_TOOL_AXIS_SIZE_MAJOR);
+	return bit_is_set(tool->axis_caps, LIBINPUT_TABLET_TOOL_AXIS_SIZE_MAJOR);
 }
 
 LIBINPUT_EXPORT int
-libinput_tablet_tool_has_button(struct libinput_tablet_tool *tool,
-				uint32_t code)
+libinput_tablet_tool_has_button(struct libinput_tablet_tool *tool, uint32_t code)
 {
 	if (NCHARS(code) > sizeof(tool->buttons))
 		return 0;
@@ -1722,8 +1707,7 @@ libinput_tablet_tool_has_button(struct l
 }
 
 LIBINPUT_EXPORT void
-libinput_tablet_tool_set_user_data(struct libinput_tablet_tool *tool,
-				   void *user_data)
+libinput_tablet_tool_set_user_data(struct libinput_tablet_tool *tool, void *user_data)
 {
 	tool->user_data = user_data;
 }
@@ -1751,6 +1735,8 @@ libinput_tablet_tool_unref(struct libinp
 		return tool;
 
 	list_remove(&tool->link);
+	if (tool->last_device)
+		tool->last_device = libinput_device_unref(tool->last_device);
 	free(tool);
 	return NULL;
 }
@@ -1837,8 +1823,7 @@ libinput_add_fd(struct libinput *libinpu
 }
 
 void
-libinput_remove_source(struct libinput *libinput,
-		       struct libinput_source *source)
+libinput_remove_source(struct libinput *libinput, struct libinput_source *source)
 {
 	epoll_ctl(libinput->epoll_fd, EPOLL_CTL_DEL, source->fd, NULL);
 	source->fd = -1;
@@ -1871,6 +1856,8 @@ libinput_init(struct libinput *libinput,
 	list_init(&libinput->device_group_list);
 	list_init(&libinput->tool_list);
 
+	libinput_plugin_system_init(&libinput->plugin_system);
+
 	if (libinput_timer_subsys_init(libinput) != 0) {
 		free(libinput->events);
 		close(libinput->epoll_fd);
@@ -1883,8 +1870,7 @@ libinput_init(struct libinput *libinput,
 void
 libinput_init_quirks(struct libinput *libinput)
 {
-	const char *data_path,
-	           *override_file = NULL;
+	const char *data_path, *override_file = NULL;
 	struct quirks_context *quirks;
 
 	if (libinput->quirks_initialized)
@@ -1912,8 +1898,7 @@ libinput_init_quirks(struct libinput *li
 			  data_path,
 			  override_file ? " and " : "",
 			  override_file ? override_file : "",
-			  HTTP_DOC_LINK
-			  );
+			  HTTP_DOC_LINK);
 		return;
 	}
 
@@ -1965,29 +1950,27 @@ libinput_unref(struct libinput *libinput
 	libinput->interface_backend->destroy(libinput);
 
 	while ((event = libinput_get_event(libinput)))
-	       libinput_event_destroy(event);
+		libinput_event_destroy(event);
 
 	free(libinput->events);
 
+	list_for_each_safe(tool, &libinput->tool_list, link) {
+		libinput_tablet_tool_unref(tool);
+	}
+
+	libinput_plugin_system_destroy(&libinput->plugin_system);
+
 	list_for_each_safe(seat, &libinput->seat_list, link) {
-		list_for_each_safe(device,
-				   &seat->devices_list,
-				   link)
+		list_for_each_safe(device, &seat->devices_list, link)
 			libinput_device_destroy(device);
 
 		libinput_seat_destroy(seat);
 	}
 
-	list_for_each_safe(group,
-			   &libinput->device_group_list,
-			   link) {
+	list_for_each_safe(group, &libinput->device_group_list, link) {
 		libinput_device_group_destroy(group);
 	}
 
-	list_for_each_safe(tool, &libinput->tool_list, link) {
-		libinput_tablet_tool_unref(tool);
-	}
-
 	libinput_timer_subsys_destroy(libinput);
 	libinput_drop_destroyed_sources(libinput);
 	quirks_context_unref(libinput->quirks);
@@ -1997,6 +1980,16 @@ libinput_unref(struct libinput *libinput
 	return NULL;
 }
 
+struct quirks *
+libinput_device_get_quirks(struct libinput_device *device)
+{
+	struct libinput *libinput = libinput_device_get_context(device);
+	_unref_(udev_device) *udev_device = libinput_device_get_udev_device(device);
+	if (udev_device)
+		return quirks_fetch_for_device(libinput->quirks, udev_device);
+	return NULL;
+}
+
 static void
 libinput_event_tablet_tool_destroy(struct libinput_event_tablet_tool *event)
 {
@@ -2016,13 +2009,13 @@ libinput_event_destroy(struct libinput_e
 	if (event == NULL)
 		return;
 
-	switch(event->type) {
+	switch (event->type) {
 	case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
 	case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
 	case LIBINPUT_EVENT_TABLET_TOOL_TIP:
 	case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
 		libinput_event_tablet_tool_destroy(
-		   libinput_event_get_tablet_tool_event(event));
+			libinput_event_get_tablet_tool_event(event));
 		break;
 	case LIBINPUT_EVENT_TABLET_PAD_RING:
 	case LIBINPUT_EVENT_TABLET_PAD_DIAL:
@@ -2030,7 +2023,7 @@ libinput_event_destroy(struct libinput_e
 	case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
 	case LIBINPUT_EVENT_TABLET_PAD_KEY:
 		libinput_event_tablet_pad_destroy(
-		   libinput_event_get_tablet_pad_event(event));
+			libinput_event_get_tablet_pad_event(event));
 		break;
 	default:
 		break;
@@ -2043,12 +2036,9 @@ libinput_event_destroy(struct libinput_e
 }
 
 int
-open_restricted(struct libinput *libinput,
-		const char *path, int flags)
+open_restricted(struct libinput *libinput, const char *path, int flags)
 {
-	return libinput->interface->open_restricted(path,
-						    flags,
-						    libinput->user_data);
+	return libinput->interface->open_restricted(path, flags, libinput->user_data);
 }
 
 void
@@ -2143,8 +2133,7 @@ libinput_seat_get_logical_name(struct li
 }
 
 void
-libinput_device_init(struct libinput_device *device,
-		     struct libinput_seat *seat)
+libinput_device_init(struct libinput_device *device, struct libinput_seat *seat)
 {
 	device->seat = seat;
 	device->refcount = 1;
@@ -2178,6 +2167,13 @@ libinput_device_unref(struct libinput_de
 	return device;
 }
 
+void
+libinput_device_disable_feature(struct libinput_device *device,
+				enum libinput_feature feature)
+{
+	evdev_device_disable_feature((struct evdev_device *)device, feature);
+}
+
 LIBINPUT_EXPORT int
 libinput_get_fd(struct libinput *libinput)
 {
@@ -2226,10 +2222,9 @@ libinput_device_init_event_listener(stru
 void
 libinput_device_add_event_listener(struct libinput_device *device,
 				   struct libinput_event_listener *listener,
-				   void (*notify_func)(
-						uint64_t time,
-						struct libinput_event *event,
-						void *notify_func_data),
+				   void (*notify_func)(uint64_t time,
+						       struct libinput_event *event,
+						       void *notify_func_data),
 				   void *notify_func_data)
 {
 	listener->notify_func = notify_func;
@@ -2243,12 +2238,34 @@ libinput_device_remove_event_listener(st
 	list_remove(&listener->link);
 }
 
+bool
+libinput_device_has_model_quirk(struct libinput_device *device, enum quirk model_quirk)
+{
+	assert(quirk_get_name(model_quirk) != NULL);
+
+	_unref_(quirks) *q = libinput_device_get_quirks(device);
+	bool result = false;
+
+	if (q)
+		quirks_get_bool(q, model_quirk, &result);
+
+	return result;
+}
+
+bool
+libinput_device_is_virtual(struct libinput_device *device)
+{
+	return evdev_device_is_virtual((struct evdev_device *)device);
+}
+
 static uint32_t
 update_seat_key_count(struct libinput_seat *seat,
-		      int32_t key,
+		      keycode_t keycode,
 		      enum libinput_key_state state)
 {
-	assert(key >= 0 && key <= KEY_MAX);
+	uint32_t key = keycode_as_uint32_t(keycode);
+
+	assert(key <= KEY_MAX);
 
 	switch (state) {
 	case LIBINPUT_KEY_STATE_PRESSED:
@@ -2266,10 +2283,11 @@ update_seat_key_count(struct libinput_se
 
 static uint32_t
 update_seat_button_count(struct libinput_seat *seat,
-			 int32_t button,
+			 button_code_t button_code,
 			 enum libinput_button_state state)
 {
-	assert(button >= 0 && button <= KEY_MAX);
+	uint32_t button = button_code_as_uint32_t(button_code);
+	assert(button <= KEY_MAX);
 
 	switch (state) {
 	case LIBINPUT_BUTTON_STATE_PRESSED:
@@ -2334,13 +2352,14 @@ post_device_event(struct libinput_device
 void
 notify_added_device(struct libinput_device *device)
 {
+	struct libinput *libinput = device->seat->libinput;
+	libinput_plugin_system_notify_device_added(&libinput->plugin_system, device);
+
 	struct libinput_event_device_notify *added_device_event;
 
 	added_device_event = zalloc(sizeof *added_device_event);
 
-	post_base_event(device,
-			LIBINPUT_EVENT_DEVICE_ADDED,
-			&added_device_event->base);
+	post_base_event(device, LIBINPUT_EVENT_DEVICE_ADDED, &added_device_event->base);
 
 #ifdef __clang_analyzer__
 	/* clang doesn't realize we're not leaking the event here, so
@@ -2352,6 +2371,9 @@ notify_added_device(struct libinput_devi
 void
 notify_removed_device(struct libinput_device *device)
 {
+	struct libinput *libinput = device->seat->libinput;
+	libinput_plugin_system_notify_device_removed(&libinput->plugin_system, device);
+
 	struct libinput_event_device_notify *removed_device_event;
 
 	removed_device_event = zalloc(sizeof *removed_device_event);
@@ -2368,8 +2390,7 @@ notify_removed_device(struct libinput_de
 }
 
 static inline bool
-device_has_cap(struct libinput_device *device,
-	       enum libinput_device_capability cap)
+device_has_cap(struct libinput_device *device, enum libinput_device_capability cap)
 {
 	const char *capability;
 
@@ -2411,7 +2432,7 @@ device_has_cap(struct libinput_device *d
 void
 keyboard_notify_key(struct libinput_device *device,
 		    uint64_t time,
-		    uint32_t key,
+		    keycode_t keycode,
 		    enum libinput_key_state state)
 {
 	struct libinput_event_keyboard *key_event;
@@ -2422,18 +2443,16 @@ keyboard_notify_key(struct libinput_devi
 
 	key_event = zalloc(sizeof *key_event);
 
-	seat_key_count = update_seat_key_count(device->seat, key, state);
+	seat_key_count = update_seat_key_count(device->seat, keycode, state);
 
-	*key_event = (struct libinput_event_keyboard) {
+	*key_event = (struct libinput_event_keyboard){
 		.time = time,
-		.key = key,
+		.key = keycode_as_uint32_t(keycode),
 		.state = state,
 		.seat_key_count = seat_key_count,
 	};
 
-	post_device_event(device, time,
-			  LIBINPUT_EVENT_KEYBOARD_KEY,
-			  &key_event->base);
+	post_device_event(device, time, LIBINPUT_EVENT_KEYBOARD_KEY, &key_event->base);
 }
 
 void
@@ -2449,13 +2468,14 @@ pointer_notify_motion(struct libinput_de
 
 	motion_event = zalloc(sizeof *motion_event);
 
-	*motion_event = (struct libinput_event_pointer) {
+	*motion_event = (struct libinput_event_pointer){
 		.time = time,
 		.delta = *delta,
 		.delta_raw = *raw,
 	};
 
-	post_device_event(device, time,
+	post_device_event(device,
+			  time,
 			  LIBINPUT_EVENT_POINTER_MOTION,
 			  &motion_event->base);
 }
@@ -2472,12 +2492,13 @@ pointer_notify_motion_absolute(struct li
 
 	motion_absolute_event = zalloc(sizeof *motion_absolute_event);
 
-	*motion_absolute_event = (struct libinput_event_pointer) {
+	*motion_absolute_event = (struct libinput_event_pointer){
 		.time = time,
 		.absolute = *point,
 	};
 
-	post_device_event(device, time,
+	post_device_event(device,
+			  time,
 			  LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE,
 			  &motion_absolute_event->base);
 }
@@ -2485,7 +2506,7 @@ pointer_notify_motion_absolute(struct li
 void
 pointer_notify_button(struct libinput_device *device,
 		      uint64_t time,
-		      int32_t button,
+		      button_code_t button,
 		      enum libinput_button_state state)
 {
 	struct libinput_event_pointer *button_event;
@@ -2496,31 +2517,30 @@ pointer_notify_button(struct libinput_de
 
 	button_event = zalloc(sizeof *button_event);
 
-	seat_button_count = update_seat_button_count(device->seat,
-						     button,
-						     state);
+	seat_button_count = update_seat_button_count(device->seat, button, state);
 
-	*button_event = (struct libinput_event_pointer) {
+	*button_event = (struct libinput_event_pointer){
 		.time = time,
-		.button = button,
+		.button = button_code_as_uint32_t(button),
 		.state = state,
 		.seat_button_count = seat_button_count,
 	};
 
-	post_device_event(device, time,
+	post_device_event(device,
+			  time,
 			  LIBINPUT_EVENT_POINTER_BUTTON,
 			  &button_event->base);
 }
 
 void
 pointer_notify_axis_finger(struct libinput_device *device,
-			  uint64_t time,
-			  uint32_t axes,
-			  const struct normalized_coords *delta)
+			   uint64_t time,
+			   uint32_t axes,
+			   const struct normalized_coords *delta)
 {
 	struct libinput_event_pointer *axis_event, *axis_event_legacy;
-	const struct discrete_coords zero_discrete = {0};
-	const struct wheel_v120 zero_v120 = {0};
+	const struct discrete_coords zero_discrete = { 0 };
+	const struct wheel_v120 zero_v120 = { 0 };
 
 	if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER))
 		return;
@@ -2528,7 +2548,7 @@ pointer_notify_axis_finger(struct libinp
 	axis_event = zalloc(sizeof *axis_event);
 	axis_event_legacy = zalloc(sizeof *axis_event_legacy);
 
-	*axis_event = (struct libinput_event_pointer) {
+	*axis_event = (struct libinput_event_pointer){
 		.time = time,
 		.delta = *delta,
 		.source = LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
@@ -2538,10 +2558,12 @@ pointer_notify_axis_finger(struct libinp
 	};
 	*axis_event_legacy = *axis_event;
 
-	post_device_event(device, time,
+	post_device_event(device,
+			  time,
 			  LIBINPUT_EVENT_POINTER_SCROLL_FINGER,
 			  &axis_event->base);
-	post_device_event(device, time,
+	post_device_event(device,
+			  time,
 			  LIBINPUT_EVENT_POINTER_AXIS,
 			  &axis_event_legacy->base);
 }
@@ -2553,8 +2575,8 @@ pointer_notify_axis_continuous(struct li
 			       const struct normalized_coords *delta)
 {
 	struct libinput_event_pointer *axis_event, *axis_event_legacy;
-	const struct discrete_coords zero_discrete = {0};
-	const struct wheel_v120 zero_v120 = {0};
+	const struct discrete_coords zero_discrete = { 0 };
+	const struct wheel_v120 zero_v120 = { 0 };
 
 	if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER))
 		return;
@@ -2562,7 +2584,7 @@ pointer_notify_axis_continuous(struct li
 	axis_event = zalloc(sizeof *axis_event);
 	axis_event_legacy = zalloc(sizeof *axis_event_legacy);
 
-	*axis_event = (struct libinput_event_pointer) {
+	*axis_event = (struct libinput_event_pointer){
 		.time = time,
 		.delta = *delta,
 		.source = LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
@@ -2572,10 +2594,12 @@ pointer_notify_axis_continuous(struct li
 	};
 	*axis_event_legacy = *axis_event;
 
-	post_device_event(device, time,
+	post_device_event(device,
+			  time,
 			  LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS,
 			  &axis_event->base);
-	post_device_event(device, time,
+	post_device_event(device,
+			  time,
 			  LIBINPUT_EVENT_POINTER_AXIS,
 			  &axis_event_legacy->base);
 }
@@ -2588,14 +2612,14 @@ pointer_notify_axis_legacy_wheel(struct
 				 const struct discrete_coords *discrete)
 {
 	struct libinput_event_pointer *axis_event;
-	const struct wheel_v120 zero_v120 = {0};
+	const struct wheel_v120 zero_v120 = { 0 };
 
 	if (!device_has_cap(device, LIBINPUT_DEVICE_CAP_POINTER))
 		return;
 
 	axis_event = zalloc(sizeof *axis_event);
 
-	*axis_event = (struct libinput_event_pointer) {
+	*axis_event = (struct libinput_event_pointer){
 		.time = time,
 		.delta = *delta,
 		.source = LIBINPUT_POINTER_AXIS_SOURCE_WHEEL,
@@ -2604,9 +2628,7 @@ pointer_notify_axis_legacy_wheel(struct
 		.v120 = zero_v120,
 	};
 
-	post_device_event(device, time,
-			  LIBINPUT_EVENT_POINTER_AXIS,
-			  &axis_event->base);
+	post_device_event(device, time, LIBINPUT_EVENT_POINTER_AXIS, &axis_event->base);
 }
 
 void
@@ -2623,7 +2645,7 @@ pointer_notify_axis_wheel(struct libinpu
 
 	axis_event = zalloc(sizeof *axis_event);
 
-	*axis_event = (struct libinput_event_pointer) {
+	*axis_event = (struct libinput_event_pointer){
 		.time = time,
 		.delta = *delta,
 		.source = LIBINPUT_POINTER_AXIS_SOURCE_WHEEL,
@@ -2633,7 +2655,8 @@ pointer_notify_axis_wheel(struct libinpu
 		.v120 = *v120,
 	};
 
-	post_device_event(device, time,
+	post_device_event(device,
+			  time,
 			  LIBINPUT_EVENT_POINTER_SCROLL_WHEEL,
 			  &axis_event->base);
 
@@ -2654,16 +2677,14 @@ touch_notify_touch_down(struct libinput_
 
 	touch_event = zalloc(sizeof *touch_event);
 
-	*touch_event = (struct libinput_event_touch) {
+	*touch_event = (struct libinput_event_touch){
 		.time = time,
 		.slot = slot,
 		.seat_slot = seat_slot,
 		.point = *point,
 	};
 
-	post_device_event(device, time,
-			  LIBINPUT_EVENT_TOUCH_DOWN,
-			  &touch_event->base);
+	post_device_event(device, time, LIBINPUT_EVENT_TOUCH_DOWN, &touch_event->base);
 }
 
 void
@@ -2680,14 +2701,15 @@ touch_notify_touch_motion(struct libinpu
 
 	touch_event = zalloc(sizeof *touch_event);
 
-	*touch_event = (struct libinput_event_touch) {
+	*touch_event = (struct libinput_event_touch){
 		.time = time,
 		.slot = slot,
 		.seat_slot = seat_slot,
 		.point = *point,
 	};
 
-	post_device_event(device, time,
+	post_device_event(device,
+			  time,
 			  LIBINPUT_EVENT_TOUCH_MOTION,
 			  &touch_event->base);
 }
@@ -2705,15 +2727,13 @@ touch_notify_touch_up(struct libinput_de
 
 	touch_event = zalloc(sizeof *touch_event);
 
-	*touch_event = (struct libinput_event_touch) {
+	*touch_event = (struct libinput_event_touch){
 		.time = time,
 		.slot = slot,
 		.seat_slot = seat_slot,
 	};
 
-	post_device_event(device, time,
-			  LIBINPUT_EVENT_TOUCH_UP,
-			  &touch_event->base);
+	post_device_event(device, time, LIBINPUT_EVENT_TOUCH_UP, &touch_event->base);
 }
 
 void
@@ -2729,20 +2749,20 @@ touch_notify_touch_cancel(struct libinpu
 
 	touch_event = zalloc(sizeof *touch_event);
 
-	*touch_event = (struct libinput_event_touch) {
+	*touch_event = (struct libinput_event_touch){
 		.time = time,
 		.slot = slot,
 		.seat_slot = seat_slot,
 	};
 
-	post_device_event(device, time,
+	post_device_event(device,
+			  time,
 			  LIBINPUT_EVENT_TOUCH_CANCEL,
 			  &touch_event->base);
 }
 
 void
-touch_notify_frame(struct libinput_device *device,
-		   uint64_t time)
+touch_notify_frame(struct libinput_device *device, uint64_t time)
 {
 	struct libinput_event_touch *touch_event;
 
@@ -2751,13 +2771,11 @@ touch_notify_frame(struct libinput_devic
 
 	touch_event = zalloc(sizeof *touch_event);
 
-	*touch_event = (struct libinput_event_touch) {
+	*touch_event = (struct libinput_event_touch){
 		.time = time,
 	};
 
-	post_device_event(device, time,
-			  LIBINPUT_EVENT_TOUCH_FRAME,
-			  &touch_event->base);
+	post_device_event(device, time, LIBINPUT_EVENT_TOUCH_FRAME, &touch_event->base);
 }
 
 void
@@ -2774,7 +2792,7 @@ tablet_notify_axis(struct libinput_devic
 
 	axis_event = zalloc(sizeof *axis_event);
 
-	*axis_event = (struct libinput_event_tablet_tool) {
+	*axis_event = (struct libinput_event_tablet_tool){
 		.time = time,
 		.tool = libinput_tablet_tool_ref(tool),
 		.proximity_state = LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN,
@@ -2808,7 +2826,7 @@ tablet_notify_proximity(struct libinput_
 
 	proximity_event = zalloc(sizeof *proximity_event);
 
-	*proximity_event = (struct libinput_event_tablet_tool) {
+	*proximity_event = (struct libinput_event_tablet_tool){
 		.time = time,
 		.tool = libinput_tablet_tool_ref(tool),
 		.tip_state = LIBINPUT_TABLET_TOOL_TIP_UP,
@@ -2841,7 +2859,7 @@ tablet_notify_tip(struct libinput_device
 
 	tip_event = zalloc(sizeof *tip_event);
 
-	*tip_event = (struct libinput_event_tablet_tool) {
+	*tip_event = (struct libinput_event_tablet_tool){
 		.time = time,
 		.tool = libinput_tablet_tool_ref(tool),
 		.tip_state = tip_state,
@@ -2850,9 +2868,7 @@ tablet_notify_tip(struct libinput_device
 		.abs.x = *x,
 		.abs.y = *y,
 	};
-	memcpy(tip_event->changed_axes,
-	       changed_axes,
-	       sizeof(tip_event->changed_axes));
+	memcpy(tip_event->changed_axes, changed_axes, sizeof(tip_event->changed_axes));
 
 	post_device_event(device,
 			  time,
@@ -2866,7 +2882,7 @@ tablet_notify_button(struct libinput_dev
 		     struct libinput_tablet_tool *tool,
 		     enum libinput_tablet_tool_tip_state tip_state,
 		     const struct tablet_axes *axes,
-		     int32_t button,
+		     button_code_t button,
 		     enum libinput_button_state state,
 		     const struct input_absinfo *x,
 		     const struct input_absinfo *y)
@@ -2876,14 +2892,12 @@ tablet_notify_button(struct libinput_dev
 
 	button_event = zalloc(sizeof *button_event);
 
-	seat_button_count = update_seat_button_count(device->seat,
-						     button,
-						     state);
+	seat_button_count = update_seat_button_count(device->seat, button, state);
 
-	*button_event = (struct libinput_event_tablet_tool) {
+	*button_event = (struct libinput_event_tablet_tool){
 		.time = time,
 		.tool = libinput_tablet_tool_ref(tool),
-		.button = button,
+		.button = button_code_as_uint32_t(button),
 		.state = state,
 		.seat_button_count = seat_button_count,
 		.proximity_state = LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN,
@@ -2902,7 +2916,7 @@ tablet_notify_button(struct libinput_dev
 void
 tablet_pad_notify_button(struct libinput_device *device,
 			 uint64_t time,
-			 int32_t button,
+			 pad_button_t button,
 			 enum libinput_button_state state,
 			 struct libinput_tablet_pad_mode_group *group)
 {
@@ -2913,9 +2927,9 @@ tablet_pad_notify_button(struct libinput
 
 	mode = libinput_tablet_pad_mode_group_get_mode(group);
 
-	*button_event = (struct libinput_event_tablet_pad) {
+	*button_event = (struct libinput_event_tablet_pad){
 		.time = time,
-		.button.number = button,
+		.button.number = pad_button_as_uint32_t(button),
 		.button.state = state,
 		.mode_group = libinput_tablet_pad_mode_group_ref(group),
 		.mode = mode,
@@ -2941,7 +2955,7 @@ tablet_pad_notify_dial(struct libinput_d
 
 	mode = libinput_tablet_pad_mode_group_get_mode(group);
 
-	*dial_event = (struct libinput_event_tablet_pad) {
+	*dial_event = (struct libinput_event_tablet_pad){
 		.time = time,
 		.dial.number = number,
 		.dial.v120 = value,
@@ -2970,7 +2984,7 @@ tablet_pad_notify_ring(struct libinput_d
 
 	mode = libinput_tablet_pad_mode_group_get_mode(group);
 
-	*ring_event = (struct libinput_event_tablet_pad) {
+	*ring_event = (struct libinput_event_tablet_pad){
 		.time = time,
 		.ring.number = number,
 		.ring.position = value,
@@ -3000,7 +3014,7 @@ tablet_pad_notify_strip(struct libinput_
 
 	mode = libinput_tablet_pad_mode_group_get_mode(group);
 
-	*strip_event = (struct libinput_event_tablet_pad) {
+	*strip_event = (struct libinput_event_tablet_pad){
 		.time = time,
 		.strip.number = number,
 		.strip.position = value,
@@ -3025,7 +3039,7 @@ tablet_pad_notify_key(struct libinput_de
 
 	key_event = zalloc(sizeof *key_event);
 
-	*key_event = (struct libinput_event_tablet_pad) {
+	*key_event = (struct libinput_event_tablet_pad){
 		.time = time,
 		.key.code = key,
 		.key.state = state,
@@ -3055,7 +3069,7 @@ gesture_notify(struct libinput_device *d
 
 	gesture_event = zalloc(sizeof *gesture_event);
 
-	*gesture_event = (struct libinput_event_gesture) {
+	*gesture_event = (struct libinput_event_gesture){
 		.time = time,
 		.finger_count = finger_count,
 		.cancelled = cancelled,
@@ -3065,8 +3079,7 @@ gesture_notify(struct libinput_device *d
 		.angle = angle,
 	};
 
-	post_device_event(device, time, type,
-			  &gesture_event->base);
+	post_device_event(device, time, type, &gesture_event->base);
 }
 
 void
@@ -3077,8 +3090,7 @@ gesture_notify_swipe(struct libinput_dev
 		     const struct normalized_coords *delta,
 		     const struct normalized_coords *unaccel)
 {
-	gesture_notify(device, time, type, finger_count, 0, delta, unaccel,
-		       0.0, 0.0);
+	gesture_notify(device, time, type, finger_count, 0, delta, unaccel, 0.0, 0.0);
 }
 
 void
@@ -3089,8 +3101,15 @@ gesture_notify_swipe_end(struct libinput
 {
 	const struct normalized_coords zero = { 0.0, 0.0 };
 
-	gesture_notify(device, time, LIBINPUT_EVENT_GESTURE_SWIPE_END,
-		       finger_count, cancelled, &zero, &zero, 0.0, 0.0);
+	gesture_notify(device,
+		       time,
+		       LIBINPUT_EVENT_GESTURE_SWIPE_END,
+		       finger_count,
+		       cancelled,
+		       &zero,
+		       &zero,
+		       0.0,
+		       0.0);
 }
 
 void
@@ -3103,8 +3122,15 @@ gesture_notify_pinch(struct libinput_dev
 		     double scale,
 		     double angle)
 {
-	gesture_notify(device, time, type, finger_count, 0,
-		       delta, unaccel, scale, angle);
+	gesture_notify(device,
+		       time,
+		       type,
+		       finger_count,
+		       0,
+		       delta,
+		       unaccel,
+		       scale,
+		       angle);
 }
 
 void
@@ -3116,8 +3142,15 @@ gesture_notify_pinch_end(struct libinput
 {
 	const struct normalized_coords zero = { 0.0, 0.0 };
 
-	gesture_notify(device, time, LIBINPUT_EVENT_GESTURE_PINCH_END,
-		       finger_count, cancelled, &zero, &zero, scale, 0.0);
+	gesture_notify(device,
+		       time,
+		       LIBINPUT_EVENT_GESTURE_PINCH_END,
+		       finger_count,
+		       cancelled,
+		       &zero,
+		       &zero,
+		       scale,
+		       0.0);
 }
 
 void
@@ -3127,8 +3160,15 @@ gesture_notify_hold_begin(struct libinpu
 {
 	const struct normalized_coords zero = { 0.0, 0.0 };
 
-	gesture_notify(device, time, LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
-		       finger_count, 0, &zero, &zero, 0.0, 0.0);
+	gesture_notify(device,
+		       time,
+		       LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
+		       finger_count,
+		       0,
+		       &zero,
+		       &zero,
+		       0.0,
+		       0.0);
 }
 
 void
@@ -3139,8 +3179,15 @@ gesture_notify_hold_end(struct libinput_
 {
 	const struct normalized_coords zero = { 0.0, 0.0 };
 
-	gesture_notify(device, time, LIBINPUT_EVENT_GESTURE_HOLD_END,
-		       finger_count, cancelled, &zero, &zero, 0, 0.0);
+	gesture_notify(device,
+		       time,
+		       LIBINPUT_EVENT_GESTURE_HOLD_END,
+		       finger_count,
+		       cancelled,
+		       &zero,
+		       &zero,
+		       0,
+		       0.0);
 }
 
 void
@@ -3156,13 +3203,14 @@ switch_notify_toggle(struct libinput_dev
 
 	switch_event = zalloc(sizeof *switch_event);
 
-	*switch_event = (struct libinput_event_switch) {
+	*switch_event = (struct libinput_event_switch){
 		.time = time,
 		.sw = sw,
 		.state = state,
 	};
 
-	post_device_event(device, time,
+	post_device_event(device,
+			  time,
 			  LIBINPUT_EVENT_SWITCH_TOGGLE,
 			  &switch_event->base);
 
@@ -3173,19 +3221,20 @@ switch_notify_toggle(struct libinput_dev
 #endif
 }
 
-LIBINPUT_UNUSED
-static inline void
+_unused_ static inline void
 libinput_print_queued_event(struct libinput_event *event)
 {
+	struct libinput_print_options opts = {
+		.show_keycodes = false,
+	};
 	struct libinput *libinput = libinput_event_get_context(event);
-	char *event_str = libinput_event_to_str(event, 0, NULL);
+	char *event_str = libinput_event_to_str(event, 0, &opts);
 	log_debug(libinput, "Queuing %s\n", event_str);
 	free(event_str);
 }
 
 static void
-libinput_post_event(struct libinput *libinput,
-		    struct libinput_event *event)
+libinput_post_event(struct libinput *libinput, struct libinput_event *event)
 {
 	struct libinput_event **events = libinput->events;
 	size_t events_len = libinput->events_len;
@@ -3193,7 +3242,7 @@ libinput_post_event(struct libinput *lib
 	size_t move_len;
 	size_t new_out;
 
-#if EVENT_DEBUGGING
+#ifdef EVENT_DEBUGGING
 	libinput_print_queued_event(event);
 #endif
 
@@ -3245,8 +3294,7 @@ libinput_get_event(struct libinput *libi
 		return NULL;
 
 	event = libinput->events[libinput->events_out];
-	libinput->events_out =
-		(libinput->events_out + 1) % libinput->events_len;
+	libinput->events_out = (libinput->events_out + 1) % libinput->events_len;
 	libinput->events_count--;
 
 	return event;
@@ -3265,8 +3313,7 @@ libinput_next_event_type(struct libinput
 }
 
 LIBINPUT_EXPORT void
-libinput_set_user_data(struct libinput *libinput,
-		       void *user_data)
+libinput_set_user_data(struct libinput *libinput, void *user_data)
 {
 	libinput->user_data = user_data;
 }
@@ -3316,37 +3363,37 @@ libinput_device_get_device_group(struct
 LIBINPUT_EXPORT const char *
 libinput_device_get_sysname(struct libinput_device *device)
 {
-	return evdev_device_get_sysname((struct evdev_device *) device);
+	return evdev_device_get_sysname((struct evdev_device *)device);
 }
 
 LIBINPUT_EXPORT const char *
 libinput_device_get_name(struct libinput_device *device)
 {
-	return evdev_device_get_name((struct evdev_device *) device);
+	return evdev_device_get_name((struct evdev_device *)device);
 }
 
 LIBINPUT_EXPORT unsigned int
 libinput_device_get_id_bustype(struct libinput_device *device)
 {
-	return evdev_device_get_id_bustype((struct evdev_device *) device);
+	return evdev_device_get_id_bustype((struct evdev_device *)device);
 }
 
 LIBINPUT_EXPORT unsigned int
 libinput_device_get_id_product(struct libinput_device *device)
 {
-	return evdev_device_get_id_product((struct evdev_device *) device);
+	return evdev_device_get_id_product((struct evdev_device *)device);
 }
 
 LIBINPUT_EXPORT unsigned int
 libinput_device_get_id_vendor(struct libinput_device *device)
 {
-	return evdev_device_get_id_vendor((struct evdev_device *) device);
+	return evdev_device_get_id_vendor((struct evdev_device *)device);
 }
 
 LIBINPUT_EXPORT const char *
 libinput_device_get_output_name(struct libinput_device *device)
 {
-	return evdev_device_get_output((struct evdev_device *) device);
+	return evdev_device_get_output((struct evdev_device *)device);
 }
 
 LIBINPUT_EXPORT struct libinput_seat *
@@ -3356,16 +3403,14 @@ libinput_device_get_seat(struct libinput
 }
 
 LIBINPUT_EXPORT int
-libinput_device_set_seat_logical_name(struct libinput_device *device,
-				      const char *name)
+libinput_device_set_seat_logical_name(struct libinput_device *device, const char *name)
 {
 	struct libinput *libinput = device->seat->libinput;
 
 	if (name == NULL)
 		return -1;
 
-	return libinput->interface_backend->device_change_seat(device,
-							       name);
+	return libinput->interface_backend->device_change_seat(device, name);
 }
 
 LIBINPUT_EXPORT struct udev_device *
@@ -3375,28 +3420,22 @@ libinput_device_get_udev_device(struct l
 }
 
 LIBINPUT_EXPORT void
-libinput_device_led_update(struct libinput_device *device,
-			   enum libinput_led leds)
+libinput_device_led_update(struct libinput_device *device, enum libinput_led leds)
 {
-	evdev_device_led_update((struct evdev_device *) device, leds);
+	evdev_device_led_update((struct evdev_device *)device, leds);
 }
 
 LIBINPUT_EXPORT int
 libinput_device_has_capability(struct libinput_device *device,
 			       enum libinput_device_capability capability)
 {
-	return evdev_device_has_capability((struct evdev_device *) device,
-					   capability);
+	return evdev_device_has_capability((struct evdev_device *)device, capability);
 }
 
 LIBINPUT_EXPORT int
-libinput_device_get_size(struct libinput_device *device,
-			 double *width,
-			 double *height)
+libinput_device_get_size(struct libinput_device *device, double *width, double *height)
 {
-	return evdev_device_get_size((struct evdev_device *)device,
-				     width,
-				     height);
+	return evdev_device_get_size((struct evdev_device *)device, width, height);
 }
 
 LIBINPUT_EXPORT int
@@ -3427,8 +3466,7 @@ libinput_device_switch_has_switch(struct
 LIBINPUT_EXPORT int
 libinput_device_tablet_pad_has_key(struct libinput_device *device, uint32_t code)
 {
-	return evdev_device_tablet_pad_has_key((struct evdev_device *)device,
-					       code);
+	return evdev_device_tablet_pad_has_key((struct evdev_device *)device, code);
 }
 
 LIBINPUT_EXPORT int
@@ -3458,10 +3496,11 @@ libinput_device_tablet_pad_get_num_strip
 LIBINPUT_EXPORT int
 libinput_device_tablet_pad_get_num_mode_groups(struct libinput_device *device)
 {
-	return evdev_device_tablet_pad_get_num_mode_groups((struct evdev_device *)device);
+	return evdev_device_tablet_pad_get_num_mode_groups(
+		(struct evdev_device *)device);
 }
 
-LIBINPUT_EXPORT struct libinput_tablet_pad_mode_group*
+LIBINPUT_EXPORT struct libinput_tablet_pad_mode_group *
 libinput_device_tablet_pad_get_mode_group(struct libinput_device *device,
 					  unsigned int index)
 {
@@ -3471,7 +3510,7 @@ libinput_device_tablet_pad_get_mode_grou
 
 LIBINPUT_EXPORT unsigned int
 libinput_tablet_pad_mode_group_get_num_modes(
-				     struct libinput_tablet_pad_mode_group *group)
+	struct libinput_tablet_pad_mode_group *group)
 {
 	return group->num_modes;
 }
@@ -3492,8 +3531,7 @@ LIBINPUT_EXPORT int
 libinput_tablet_pad_mode_group_has_button(struct libinput_tablet_pad_mode_group *group,
 					  unsigned int button)
 {
-	if ((int)button >=
-	    libinput_device_tablet_pad_get_num_buttons(group->device))
+	if ((int)button >= libinput_device_tablet_pad_get_num_buttons(group->device))
 		return 0;
 
 	return !!(group->button_mask & bit(button));
@@ -3503,8 +3541,7 @@ LIBINPUT_EXPORT int
 libinput_tablet_pad_mode_group_has_dial(struct libinput_tablet_pad_mode_group *group,
 					unsigned int dial)
 {
-	if ((int)dial >=
-	    libinput_device_tablet_pad_get_num_dials(group->device))
+	if ((int)dial >= libinput_device_tablet_pad_get_num_dials(group->device))
 		return 0;
 
 	return !!(group->dial_mask & bit(dial));
@@ -3514,8 +3551,7 @@ LIBINPUT_EXPORT int
 libinput_tablet_pad_mode_group_has_ring(struct libinput_tablet_pad_mode_group *group,
 					unsigned int ring)
 {
-	if ((int)ring >=
-	    libinput_device_tablet_pad_get_num_rings(group->device))
+	if ((int)ring >= libinput_device_tablet_pad_get_num_rings(group->device))
 		return 0;
 
 	return !!(group->ring_mask & bit(ring));
@@ -3525,35 +3561,32 @@ LIBINPUT_EXPORT int
 libinput_tablet_pad_mode_group_has_strip(struct libinput_tablet_pad_mode_group *group,
 					 unsigned int strip)
 {
-	if ((int)strip >=
-	    libinput_device_tablet_pad_get_num_strips(group->device))
+	if ((int)strip >= libinput_device_tablet_pad_get_num_strips(group->device))
 		return 0;
 
 	return !!(group->strip_mask & bit(strip));
 }
 
 LIBINPUT_EXPORT int
-libinput_tablet_pad_mode_group_button_is_toggle(struct libinput_tablet_pad_mode_group *group,
-						unsigned int button)
+libinput_tablet_pad_mode_group_button_is_toggle(
+	struct libinput_tablet_pad_mode_group *group,
+	unsigned int button)
 {
-	if ((int)button >=
-	    libinput_device_tablet_pad_get_num_buttons(group->device))
+	if ((int)button >= libinput_device_tablet_pad_get_num_buttons(group->device))
 		return 0;
 
 	return !!(group->toggle_button_mask & bit(button));
 }
 
 LIBINPUT_EXPORT struct libinput_tablet_pad_mode_group *
-libinput_tablet_pad_mode_group_ref(
-			struct libinput_tablet_pad_mode_group *group)
+libinput_tablet_pad_mode_group_ref(struct libinput_tablet_pad_mode_group *group)
 {
 	group->refcount++;
 	return group;
 }
 
 LIBINPUT_EXPORT struct libinput_tablet_pad_mode_group *
-libinput_tablet_pad_mode_group_unref(
-			struct libinput_tablet_pad_mode_group *group)
+libinput_tablet_pad_mode_group_unref(struct libinput_tablet_pad_mode_group *group)
 {
 	assert(group->refcount > 0);
 
@@ -3568,15 +3601,15 @@ libinput_tablet_pad_mode_group_unref(
 
 LIBINPUT_EXPORT void
 libinput_tablet_pad_mode_group_set_user_data(
-			struct libinput_tablet_pad_mode_group *group,
-			void *user_data)
+	struct libinput_tablet_pad_mode_group *group,
+	void *user_data)
 {
 	group->user_data = user_data;
 }
 
 LIBINPUT_EXPORT void *
 libinput_tablet_pad_mode_group_get_user_data(
-			struct libinput_tablet_pad_mode_group *group)
+	struct libinput_tablet_pad_mode_group *group)
 {
 	return group->user_data;
 }
@@ -3881,8 +3914,7 @@ libinput_device_group_ref(struct libinpu
 }
 
 struct libinput_device_group *
-libinput_device_group_create(struct libinput *libinput,
-			     const char *identifier)
+libinput_device_group_create(struct libinput *libinput, const char *identifier)
 {
 	struct libinput_device_group *group;
 
@@ -3897,14 +3929,12 @@ libinput_device_group_create(struct libi
 }
 
 struct libinput_device_group *
-libinput_device_group_find_group(struct libinput *libinput,
-				 const char *identifier)
+libinput_device_group_find_group(struct libinput *libinput, const char *identifier)
 {
 	struct libinput_device_group *g = NULL;
 
 	list_for_each(g, &libinput->device_group_list, link) {
-		if (identifier && g->identifier &&
-		    streq(g->identifier, identifier)) {
+		if (identifier && g->identifier && streq(g->identifier, identifier)) {
 			return g;
 		}
 	}
@@ -3959,7 +3989,7 @@ libinput_config_status_to_str(enum libin
 {
 	const char *str = NULL;
 
-	switch(status) {
+	switch (status) {
 	case LIBINPUT_CONFIG_STATUS_SUCCESS:
 		str = "Success";
 		break;
@@ -3989,11 +4019,10 @@ libinput_device_config_tap_set_enabled(s
 		return LIBINPUT_CONFIG_STATUS_INVALID;
 
 	if (libinput_device_config_tap_get_finger_count(device) == 0)
-		return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED :
-				LIBINPUT_CONFIG_STATUS_SUCCESS;
+		return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED
+			      : LIBINPUT_CONFIG_STATUS_SUCCESS;
 
 	return device->config.tap->set_enabled(device, enable);
-
 }
 
 LIBINPUT_EXPORT enum libinput_config_tap_state
@@ -4016,7 +4045,7 @@ libinput_device_config_tap_get_default_e
 
 LIBINPUT_EXPORT enum libinput_config_status
 libinput_device_config_tap_set_button_map(struct libinput_device *device,
-					    enum libinput_config_tap_button_map map)
+					  enum libinput_config_tap_button_map map)
 {
 	switch (map) {
 	case LIBINPUT_CONFIG_TAP_MAP_LRM:
@@ -4059,8 +4088,8 @@ libinput_device_config_tap_set_drag_enab
 		return LIBINPUT_CONFIG_STATUS_INVALID;
 
 	if (libinput_device_config_tap_get_finger_count(device) == 0)
-		return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED :
-				LIBINPUT_CONFIG_STATUS_SUCCESS;
+		return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED
+			      : LIBINPUT_CONFIG_STATUS_SUCCESS;
 
 	return device->config.tap->set_drag_enabled(device, enable);
 }
@@ -4084,8 +4113,9 @@ libinput_device_config_tap_get_default_d
 }
 
 LIBINPUT_EXPORT enum libinput_config_status
-libinput_device_config_tap_set_drag_lock_enabled(struct libinput_device *device,
-						 enum libinput_config_drag_lock_state enable)
+libinput_device_config_tap_set_drag_lock_enabled(
+	struct libinput_device *device,
+	enum libinput_config_drag_lock_state enable)
 {
 	if (enable != LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY &&
 	    enable != LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT &&
@@ -4093,8 +4123,8 @@ libinput_device_config_tap_set_drag_lock
 		return LIBINPUT_CONFIG_STATUS_INVALID;
 
 	if (libinput_device_config_tap_get_finger_count(device) == 0)
-		return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED :
-				LIBINPUT_CONFIG_STATUS_SUCCESS;
+		return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED
+			      : LIBINPUT_CONFIG_STATUS_SUCCESS;
 
 	return device->config.tap->set_draglock_enabled(device, enable);
 }
@@ -4162,8 +4192,9 @@ libinput_device_config_3fg_drag_get_defa
 LIBINPUT_EXPORT int
 libinput_device_config_calibration_has_matrix(struct libinput_device *device)
 {
-	return device->config.calibration ?
-		device->config.calibration->has_matrix(device) : 0;
+	return device->config.calibration
+		       ? device->config.calibration->has_matrix(device)
+		       : 0;
 }
 
 LIBINPUT_EXPORT enum libinput_config_status
@@ -4199,13 +4230,13 @@ libinput_device_config_calibration_get_d
 LIBINPUT_EXPORT int
 libinput_device_config_area_has_rectangle(struct libinput_device *device)
 {
-	return device->config.area ?
-		device->config.area->has_rectangle(device) : 0;
+	return device->config.area ? device->config.area->has_rectangle(device) : 0;
 }
 
 LIBINPUT_EXPORT enum libinput_config_status
-libinput_device_config_area_set_rectangle(struct libinput_device *device,
-					  const struct libinput_config_area_rectangle *rectangle)
+libinput_device_config_area_set_rectangle(
+	struct libinput_device *device,
+	const struct libinput_config_area_rectangle *rectangle)
 {
 	if (!libinput_device_config_area_has_rectangle(device))
 		return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
@@ -4213,8 +4244,8 @@ libinput_device_config_area_set_rectangl
 	if (rectangle->x1 >= rectangle->x2 || rectangle->y1 >= rectangle->y2)
 		return LIBINPUT_CONFIG_STATUS_INVALID;
 
-	if (rectangle->x1 < 0.0 || rectangle->x2 > 1.0 ||
-	    rectangle->y1 < 0.0 || rectangle->y2 > 1.0)
+	if (rectangle->x1 < 0.0 || rectangle->x2 > 1.0 || rectangle->y1 < 0.0 ||
+	    rectangle->y2 > 1.0)
 		return LIBINPUT_CONFIG_STATUS_INVALID;
 
 	return device->config.area->set_rectangle(device, rectangle);
@@ -4285,13 +4316,11 @@ libinput_device_config_send_events_get_d
 LIBINPUT_EXPORT int
 libinput_device_config_accel_is_available(struct libinput_device *device)
 {
-	return device->config.accel ?
-		device->config.accel->available(device) : 0;
+	return device->config.accel ? device->config.accel->available(device) : 0;
 }
 
 LIBINPUT_EXPORT enum libinput_config_status
-libinput_device_config_accel_set_speed(struct libinput_device *device,
-				       double speed)
+libinput_device_config_accel_set_speed(struct libinput_device *device, double speed)
 {
 	/* Need the negation in case speed is NaN */
 	if (!(speed >= -1.0 && speed <= 1.0))
@@ -4381,7 +4410,8 @@ libinput_config_accel_custom_func_create
 }
 
 static inline void
-libinput_config_accel_custom_func_destroy(struct libinput_config_accel_custom_func * func)
+libinput_config_accel_custom_func_destroy(
+	struct libinput_config_accel_custom_func *func)
 {
 	free(func);
 }
@@ -4422,14 +4452,14 @@ libinput_device_config_accel_apply(struc
 				   struct libinput_config_accel *accel_config)
 {
 	enum libinput_config_status status;
-	status = libinput_device_config_accel_set_profile(device, accel_config->profile);
+	status =
+		libinput_device_config_accel_set_profile(device, accel_config->profile);
 	if (status != LIBINPUT_CONFIG_STATUS_SUCCESS)
 		return status;
 
 	switch (accel_config->profile) {
 	case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT:
-	case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE:
-	{
+	case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: {
 		double speed = libinput_device_config_accel_get_default_speed(device);
 		return libinput_device_config_accel_set_speed(device, speed);
 	}
@@ -4444,7 +4474,9 @@ libinput_device_config_accel_apply(struc
 LIBINPUT_EXPORT enum libinput_config_status
 libinput_config_accel_set_points(struct libinput_config_accel *config,
 				 enum libinput_config_accel_type accel_type,
-				 double step, size_t npoints, double *points)
+				 double step,
+				 size_t npoints,
+				 const double *points)
 {
 	if (config->profile != LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM)
 		return LIBINPUT_CONFIG_STATUS_INVALID;
@@ -4461,7 +4493,8 @@ libinput_config_accel_set_points(struct
 	if (step <= 0 || step > LIBINPUT_ACCEL_STEP_MAX)
 		return LIBINPUT_CONFIG_STATUS_INVALID;
 
-	if (npoints < LIBINPUT_ACCEL_NPOINTS_MIN || npoints > LIBINPUT_ACCEL_NPOINTS_MAX)
+	if (npoints < LIBINPUT_ACCEL_NPOINTS_MIN ||
+	    npoints > LIBINPUT_ACCEL_NPOINTS_MAX)
 		return LIBINPUT_CONFIG_STATUS_INVALID;
 
 	for (size_t idx = 0; idx < npoints; idx++) {
@@ -4470,7 +4503,8 @@ libinput_config_accel_set_points(struct
 			return LIBINPUT_CONFIG_STATUS_INVALID;
 	}
 
-	struct libinput_config_accel_custom_func *func = libinput_config_accel_custom_func_create();
+	struct libinput_config_accel_custom_func *func =
+		libinput_config_accel_custom_func_create();
 
 	func->step = step;
 	func->npoints = npoints;
@@ -4523,7 +4557,8 @@ libinput_device_config_scroll_get_natura
 }
 
 LIBINPUT_EXPORT int
-libinput_device_config_scroll_get_default_natural_scroll_enabled(struct libinput_device *device)
+libinput_device_config_scroll_get_default_natural_scroll_enabled(
+	struct libinput_device *device)
 {
 	if (!device->config.natural_scroll)
 		return 0;
@@ -4541,8 +4576,7 @@ libinput_device_config_left_handed_is_av
 }
 
 LIBINPUT_EXPORT enum libinput_config_status
-libinput_device_config_left_handed_set(struct libinput_device *device,
-				       int left_handed)
+libinput_device_config_left_handed_set(struct libinput_device *device, int left_handed)
 {
 	if (!libinput_device_config_left_handed_is_available(device))
 		return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
@@ -4620,8 +4654,9 @@ libinput_device_config_click_get_default
 }
 
 LIBINPUT_EXPORT enum libinput_config_status
-libinput_device_config_click_set_clickfinger_button_map(struct libinput_device *device,
-							enum libinput_config_clickfinger_button_map map)
+libinput_device_config_click_set_clickfinger_button_map(
+	struct libinput_device *device,
+	enum libinput_config_clickfinger_button_map map)
 {
 	switch (map) {
 	case LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM:
@@ -4632,7 +4667,8 @@ libinput_device_config_click_set_clickfi
 	}
 
 	if ((libinput_device_config_click_get_methods(device) &
-	     LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) != LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER)
+	     LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) !=
+	    LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER)
 		return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
 
 	return device->config.click_method->set_clickfinger_map(device, map);
@@ -4642,25 +4678,27 @@ LIBINPUT_EXPORT enum libinput_config_cli
 libinput_device_config_click_get_clickfinger_button_map(struct libinput_device *device)
 {
 	if ((libinput_device_config_click_get_methods(device) &
-	     LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) != LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER)
+	     LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) !=
+	    LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER)
 		return LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM;
 
 	return device->config.click_method->get_clickfinger_map(device);
 }
 
 LIBINPUT_EXPORT enum libinput_config_clickfinger_button_map
-libinput_device_config_click_get_default_clickfinger_button_map(struct libinput_device *device)
+libinput_device_config_click_get_default_clickfinger_button_map(
+	struct libinput_device *device)
 {
 	if ((libinput_device_config_click_get_methods(device) &
-	     LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) != LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER)
+	     LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) !=
+	    LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER)
 		return LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM;
 
 	return device->config.click_method->get_default_clickfinger_map(device);
 }
 
 LIBINPUT_EXPORT int
-libinput_device_config_middle_emulation_is_available(
-		struct libinput_device *device)
+libinput_device_config_middle_emulation_is_available(struct libinput_device *device)
 {
 	if (device->config.middle_emulation)
 		return device->config.middle_emulation->available(device);
@@ -4670,11 +4708,10 @@ libinput_device_config_middle_emulation_
 
 LIBINPUT_EXPORT enum libinput_config_status
 libinput_device_config_middle_emulation_set_enabled(
-		struct libinput_device *device,
-		enum libinput_config_middle_emulation_state enable)
+	struct libinput_device *device,
+	enum libinput_config_middle_emulation_state enable)
 {
-	int available =
-		libinput_device_config_middle_emulation_is_available(device);
+	int available = libinput_device_config_middle_emulation_is_available(device);
 
 	switch (enable) {
 	case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED:
@@ -4693,8 +4730,7 @@ libinput_device_config_middle_emulation_
 }
 
 LIBINPUT_EXPORT enum libinput_config_middle_emulation_state
-libinput_device_config_middle_emulation_get_enabled(
-		struct libinput_device *device)
+libinput_device_config_middle_emulation_get_enabled(struct libinput_device *device)
 {
 	if (!libinput_device_config_middle_emulation_is_available(device))
 		return LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
@@ -4704,7 +4740,7 @@ libinput_device_config_middle_emulation_
 
 LIBINPUT_EXPORT enum libinput_config_middle_emulation_state
 libinput_device_config_middle_emulation_get_default_enabled(
-		struct libinput_device *device)
+	struct libinput_device *device)
 {
 	if (!libinput_device_config_middle_emulation_is_available(device))
 		return LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
@@ -4799,8 +4835,9 @@ libinput_device_config_scroll_get_defaul
 }
 
 LIBINPUT_EXPORT enum libinput_config_status
-libinput_device_config_scroll_set_button_lock(struct libinput_device *device,
-					      enum libinput_config_scroll_button_lock_state state)
+libinput_device_config_scroll_set_button_lock(
+	struct libinput_device *device,
+	enum libinput_config_scroll_button_lock_state state)
 {
 	if ((libinput_device_config_scroll_get_methods(device) &
 	     LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) == 0)
@@ -4855,8 +4892,8 @@ libinput_device_config_dwt_set_enabled(s
 		return LIBINPUT_CONFIG_STATUS_INVALID;
 
 	if (!libinput_device_config_dwt_is_available(device))
-		return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED :
-				LIBINPUT_CONFIG_STATUS_SUCCESS;
+		return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED
+			      : LIBINPUT_CONFIG_STATUS_SUCCESS;
 
 	return device->config.dwt->set_enabled(device, enable);
 }
@@ -4890,15 +4927,15 @@ libinput_device_config_dwtp_is_available
 
 LIBINPUT_EXPORT enum libinput_config_status
 libinput_device_config_dwtp_set_enabled(struct libinput_device *device,
-				       enum libinput_config_dwtp_state enable)
+					enum libinput_config_dwtp_state enable)
 {
 	if (enable != LIBINPUT_CONFIG_DWTP_ENABLED &&
 	    enable != LIBINPUT_CONFIG_DWTP_DISABLED)
 		return LIBINPUT_CONFIG_STATUS_INVALID;
 
 	if (!libinput_device_config_dwtp_is_available(device))
-		return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED :
-				LIBINPUT_CONFIG_STATUS_SUCCESS;
+		return enable ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED
+			      : LIBINPUT_CONFIG_STATUS_SUCCESS;
 
 	return device->config.dwtp->set_enabled(device, enable);
 }
@@ -4935,8 +4972,8 @@ libinput_device_config_rotation_set_angl
 					  unsigned int degrees_cw)
 {
 	if (!libinput_device_config_rotation_is_available(device))
-		return degrees_cw ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED :
-				    LIBINPUT_CONFIG_STATUS_SUCCESS;
+		return degrees_cw ? LIBINPUT_CONFIG_STATUS_UNSUPPORTED
+				  : LIBINPUT_CONFIG_STATUS_SUCCESS;
 
 	if (degrees_cw >= 360)
 		return LIBINPUT_CONFIG_STATUS_INVALID;
@@ -4963,7 +5000,8 @@ libinput_device_config_rotation_get_defa
 }
 
 LIBINPUT_EXPORT int
-libinput_tablet_tool_config_pressure_range_is_available(struct libinput_tablet_tool *tool)
+libinput_tablet_tool_config_pressure_range_is_available(
+	struct libinput_tablet_tool *tool)
 {
 	if (!tool->config.pressure_range.is_available)
 		return 0;
@@ -4979,8 +5017,7 @@ libinput_tablet_tool_config_pressure_ran
 	if (!libinput_tablet_tool_config_pressure_range_is_available(tool))
 		return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
 
-	if (minimum < 0.0 || minimum >= 1.0 ||
-	    maximum <= 0.0 || maximum > 1.0 ||
+	if (minimum < 0.0 || minimum >= 1.0 || maximum <= 0.0 || maximum > 1.0 ||
 	    maximum <= minimum)
 		return LIBINPUT_CONFIG_STATUS_INVALID;
 
@@ -4988,7 +5025,8 @@ libinput_tablet_tool_config_pressure_ran
 }
 
 LIBINPUT_EXPORT double
-libinput_tablet_tool_config_pressure_range_get_minimum(struct libinput_tablet_tool *tool)
+libinput_tablet_tool_config_pressure_range_get_minimum(
+	struct libinput_tablet_tool *tool)
 {
 	double min = 0.0, max = 1.0;
 
@@ -4999,7 +5037,8 @@ libinput_tablet_tool_config_pressure_ran
 }
 
 LIBINPUT_EXPORT double
-libinput_tablet_tool_config_pressure_range_get_maximum(struct libinput_tablet_tool *tool)
+libinput_tablet_tool_config_pressure_range_get_maximum(
+	struct libinput_tablet_tool *tool)
 {
 	double min = 0.0, max = 1.0;
 
@@ -5010,7 +5049,8 @@ libinput_tablet_tool_config_pressure_ran
 }
 
 LIBINPUT_EXPORT double
-libinput_tablet_tool_config_pressure_range_get_default_minimum(struct libinput_tablet_tool *tool)
+libinput_tablet_tool_config_pressure_range_get_default_minimum(
+	struct libinput_tablet_tool *tool)
 {
 	double min = 0.0, max = 1.0;
 
@@ -5021,7 +5061,8 @@ libinput_tablet_tool_config_pressure_ran
 }
 
 LIBINPUT_EXPORT double
-libinput_tablet_tool_config_pressure_range_get_default_maximum(struct libinput_tablet_tool *tool)
+libinput_tablet_tool_config_pressure_range_get_default_maximum(
+	struct libinput_tablet_tool *tool)
 {
 	double min = 0.0, max = 1.0;
 
@@ -5031,7 +5072,95 @@ libinput_tablet_tool_config_pressure_ran
 	return max;
 }
 
-#if HAVE_LIBWACOM
+LIBINPUT_EXPORT uint32_t
+libinput_tablet_tool_config_eraser_button_get_modes(struct libinput_tablet_tool *tool)
+{
+	if (!tool->config.eraser_button.get_modes)
+		return 0;
+
+	return bitmask_as_u32(tool->config.eraser_button.get_modes(tool));
+}
+
+LIBINPUT_EXPORT enum libinput_config_status
+libinput_tablet_tool_config_eraser_button_set_mode(
+	struct libinput_tablet_tool *tool,
+	enum libinput_config_eraser_button_mode mode)
+{
+	uint32_t modes = libinput_tablet_tool_config_eraser_button_get_modes(tool);
+	if (mode && (modes & mode) == 0)
+		return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
+
+	switch (mode) {
+	case LIBINPUT_CONFIG_ERASER_BUTTON_DEFAULT:
+	case LIBINPUT_CONFIG_ERASER_BUTTON_BUTTON:
+		break;
+	default:
+		return LIBINPUT_CONFIG_STATUS_INVALID;
+	}
+
+	return tool->config.eraser_button.set_mode(tool, mode);
+}
+
+LIBINPUT_EXPORT enum libinput_config_eraser_button_mode
+libinput_tablet_tool_config_eraser_button_get_mode(struct libinput_tablet_tool *tool)
+{
+	if (!libinput_tablet_tool_config_eraser_button_get_modes(tool))
+		return LIBINPUT_CONFIG_ERASER_BUTTON_DEFAULT;
+
+	return tool->config.eraser_button.get_mode(tool);
+}
+
+LIBINPUT_EXPORT enum libinput_config_eraser_button_mode
+libinput_tablet_tool_config_eraser_button_get_default_mode(
+	struct libinput_tablet_tool *tool)
+{
+	if (!libinput_tablet_tool_config_eraser_button_get_modes(tool))
+		return LIBINPUT_CONFIG_ERASER_BUTTON_DEFAULT;
+
+	return tool->config.eraser_button.get_mode(tool);
+}
+
+LIBINPUT_EXPORT enum libinput_config_status
+libinput_tablet_tool_config_eraser_button_set_button(struct libinput_tablet_tool *tool,
+						     uint32_t button)
+{
+	if (!libinput_tablet_tool_config_eraser_button_get_modes(tool))
+		return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
+
+	switch (button) {
+	case BTN_STYLUS:
+	case BTN_STYLUS2:
+	case BTN_STYLUS3:
+		break;
+	default:
+		if (!libinput_tablet_tool_has_button(tool, button))
+			return LIBINPUT_CONFIG_STATUS_INVALID;
+		break;
+	}
+
+	return tool->config.eraser_button.set_button(tool, button);
+}
+
+LIBINPUT_EXPORT unsigned int
+libinput_tablet_tool_config_eraser_button_get_button(struct libinput_tablet_tool *tool)
+{
+	if (!libinput_tablet_tool_config_eraser_button_get_modes(tool))
+		return 0;
+
+	return tool->config.eraser_button.get_button(tool);
+}
+
+LIBINPUT_EXPORT unsigned int
+libinput_tablet_tool_config_eraser_button_get_default_button(
+	struct libinput_tablet_tool *tool)
+{
+	if (!libinput_tablet_tool_config_eraser_button_get_modes(tool))
+		return 0;
+
+	return tool->config.eraser_button.get_button(tool);
+}
+
+#ifdef HAVE_LIBWACOM
 WacomDeviceDatabase *
 libinput_libwacom_ref(struct libinput *li)
 {
@@ -5039,8 +5168,7 @@ libinput_libwacom_ref(struct libinput *l
 	if (!li->libwacom.db) {
 		db = libwacom_database_new();
 		if (!db) {
-			log_error(li,
-				  "Failed to initialize libwacom context\n");
+			log_error(li, "Failed to initialize libwacom context\n");
 			return NULL;
 		}
 
diff -pruN 1.28.1-1/src/libinput.h 1.30.0-1/src/libinput.h
--- 1.28.1-1/src/libinput.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/libinput.h	2025-11-25 03:40:43.000000000 +0000
@@ -29,10 +29,10 @@
 extern "C" {
 #endif
 
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdarg.h>
 #include <libudev.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
 
 #define LIBINPUT_ATTRIBUTE_PRINTF(_format, _args) \
 	__attribute__ ((format (printf, _format, _args)))
@@ -145,6 +145,20 @@ struct libinput_event_pointer;
 struct libinput_event_touch;
 
 /**
+ * @ingroup event_gesture
+ * @struct libinput_event_gesture
+ *
+ * A gesture event representing a swipe, pinch or hold gesture. Valid event
+ * types for this event are @ref LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, @ref
+ * LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, @ref LIBINPUT_EVENT_GESTURE_SWIPE_END,
+ * @ref LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, @ref
+ * LIBINPUT_EVENT_GESTURE_PINCH_UPDATE, @ref LIBINPUT_EVENT_GESTURE_PINCH_END,
+ * @ref LIBINPUT_EVENT_GESTURE_HOLD_BEGIN and @ref
+ * LIBINPUT_EVENT_GESTURE_HOLD_END.
+ */
+struct libinput_event_gesture;
+
+/**
  * @ingroup event_tablet
  * @struct libinput_event_tablet_tool
  *
@@ -346,17 +360,17 @@ enum libinput_tablet_pad_strip_axis_sour
  * @since 1.2
  */
 enum libinput_tablet_tool_type {
-	LIBINPUT_TABLET_TOOL_TYPE_PEN = 1,	/**< A generic pen */
-	LIBINPUT_TABLET_TOOL_TYPE_ERASER,	/**< Eraser */
-	LIBINPUT_TABLET_TOOL_TYPE_BRUSH,	/**< A paintbrush-like tool */
-	LIBINPUT_TABLET_TOOL_TYPE_PENCIL,	/**< Physical drawing tool, e.g.
-					             Wacom Inking Pen */
-	LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH,	/**< An airbrush-like tool */
-	LIBINPUT_TABLET_TOOL_TYPE_MOUSE,	/**< A mouse bound to the tablet */
-	LIBINPUT_TABLET_TOOL_TYPE_LENS,		/**< A mouse tool with a lens */
-	LIBINPUT_TABLET_TOOL_TYPE_TOTEM,	/**< A rotary device with
-						     positional and rotation
-						     data */
+	LIBINPUT_TABLET_TOOL_TYPE_PEN = 1,  /**< A generic pen */
+	LIBINPUT_TABLET_TOOL_TYPE_ERASER,   /**< Eraser */
+	LIBINPUT_TABLET_TOOL_TYPE_BRUSH,    /**< A paintbrush-like tool */
+	LIBINPUT_TABLET_TOOL_TYPE_PENCIL,   /**< Physical drawing tool, e.g.
+						 Wacom Inking Pen */
+	LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH, /**< An airbrush-like tool */
+	LIBINPUT_TABLET_TOOL_TYPE_MOUSE,    /**< A mouse bound to the tablet */
+	LIBINPUT_TABLET_TOOL_TYPE_LENS,     /**< A mouse tool with a lens */
+	LIBINPUT_TABLET_TOOL_TYPE_TOTEM,    /**< A rotary device with
+						 positional and rotation
+						 data */
 };
 
 /**
@@ -466,7 +480,7 @@ libinput_device_tablet_pad_get_num_mode_
  *
  * @since 1.4
  */
-struct libinput_tablet_pad_mode_group*
+struct libinput_tablet_pad_mode_group *
 libinput_device_tablet_pad_get_mode_group(struct libinput_device *device,
 					  unsigned int index);
 
@@ -503,7 +517,8 @@ libinput_tablet_pad_mode_group_get_index
  * @since 1.4
  */
 unsigned int
-libinput_tablet_pad_mode_group_get_num_modes(struct libinput_tablet_pad_mode_group *group);
+libinput_tablet_pad_mode_group_get_num_modes(
+	struct libinput_tablet_pad_mode_group *group);
 
 /**
  * @ingroup tablet_pad_modes
@@ -573,7 +588,7 @@ libinput_tablet_pad_mode_group_has_dial(
  */
 int
 libinput_tablet_pad_mode_group_has_ring(struct libinput_tablet_pad_mode_group *group,
-					  unsigned int ring);
+					unsigned int ring);
 
 /**
  * @ingroup tablet_pad_modes
@@ -589,7 +604,7 @@ libinput_tablet_pad_mode_group_has_ring(
  */
 int
 libinput_tablet_pad_mode_group_has_strip(struct libinput_tablet_pad_mode_group *group,
-					  unsigned int strip);
+					 unsigned int strip);
 
 /**
  * @ingroup tablet_pad_modes
@@ -610,8 +625,9 @@ libinput_tablet_pad_mode_group_has_strip
  * @since 1.4
  */
 int
-libinput_tablet_pad_mode_group_button_is_toggle(struct libinput_tablet_pad_mode_group *group,
-						unsigned int button);
+libinput_tablet_pad_mode_group_button_is_toggle(
+	struct libinput_tablet_pad_mode_group *group,
+	unsigned int button);
 
 /**
  * @ingroup tablet_pad_modes
@@ -625,8 +641,7 @@ libinput_tablet_pad_mode_group_button_is
  * @since 1.4
  */
 struct libinput_tablet_pad_mode_group *
-libinput_tablet_pad_mode_group_ref(
-			struct libinput_tablet_pad_mode_group *group);
+libinput_tablet_pad_mode_group_ref(struct libinput_tablet_pad_mode_group *group);
 
 /**
  * @ingroup tablet_pad_modes
@@ -640,8 +655,7 @@ libinput_tablet_pad_mode_group_ref(
  * @since 1.4
  */
 struct libinput_tablet_pad_mode_group *
-libinput_tablet_pad_mode_group_unref(
-			struct libinput_tablet_pad_mode_group *group);
+libinput_tablet_pad_mode_group_unref(struct libinput_tablet_pad_mode_group *group);
 
 /**
  * @ingroup tablet_pad_modes
@@ -658,8 +672,8 @@ libinput_tablet_pad_mode_group_unref(
  */
 void
 libinput_tablet_pad_mode_group_set_user_data(
-			struct libinput_tablet_pad_mode_group *group,
-			void *user_data);
+	struct libinput_tablet_pad_mode_group *group,
+	void *user_data);
 
 /**
  * @ingroup tablet_pad_modes
@@ -674,7 +688,7 @@ libinput_tablet_pad_mode_group_set_user_
  */
 void *
 libinput_tablet_pad_mode_group_get_user_data(
-			struct libinput_tablet_pad_mode_group *group);
+	struct libinput_tablet_pad_mode_group *group);
 
 /**
  * @ingroup device
@@ -881,8 +895,8 @@ enum libinput_event_type {
 	 * with @ref LIBINPUT_EVENT_TABLET_TOOL_AXIS events.
 	 *
 	 * Some tools may always be in proximity. For these tools, events of
-	 * type @ref LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN are sent only once after @ref
-	 * LIBINPUT_EVENT_DEVICE_ADDED, and events of type @ref
+	 * type @ref LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN are sent only once after
+	 * @ref LIBINPUT_EVENT_DEVICE_ADDED, and events of type @ref
 	 * LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT are sent only once before @ref
 	 * LIBINPUT_EVENT_DEVICE_REMOVED.
 	 *
@@ -1260,8 +1274,7 @@ libinput_event_keyboard_get_base_event(s
  * @return The seat wide pressed key count for the key of this event
  */
 uint32_t
-libinput_event_keyboard_get_seat_key_count(
-	struct libinput_event_keyboard *event);
+libinput_event_keyboard_get_seat_key_count(struct libinput_event_keyboard *event);
 
 /**
  * @defgroup event_pointer Pointer events
@@ -1356,8 +1369,7 @@ libinput_event_pointer_get_dy(struct lib
  * @return The unaccelerated relative x movement since the last event
  */
 double
-libinput_event_pointer_get_dx_unaccelerated(
-	struct libinput_event_pointer *event);
+libinput_event_pointer_get_dx_unaccelerated(struct libinput_event_pointer *event);
 
 /**
  * @ingroup event_pointer
@@ -1381,8 +1393,7 @@ libinput_event_pointer_get_dx_unaccelera
  * @return The unaccelerated relative y movement since the last event
  */
 double
-libinput_event_pointer_get_dy_unaccelerated(
-	struct libinput_event_pointer *event);
+libinput_event_pointer_get_dy_unaccelerated(struct libinput_event_pointer *event);
 
 /**
  * @ingroup event_pointer
@@ -1438,9 +1449,8 @@ libinput_event_pointer_get_absolute_y(st
  * @return The current absolute x coordinate transformed to a screen coordinate
  */
 double
-libinput_event_pointer_get_absolute_x_transformed(
-	struct libinput_event_pointer *event,
-	uint32_t width);
+libinput_event_pointer_get_absolute_x_transformed(struct libinput_event_pointer *event,
+						  uint32_t width);
 
 /**
  * @ingroup event_pointer
@@ -1460,9 +1470,8 @@ libinput_event_pointer_get_absolute_x_tr
  * @return The current absolute y coordinate transformed to a screen coordinate
  */
 double
-libinput_event_pointer_get_absolute_y_transformed(
-	struct libinput_event_pointer *event,
-	uint32_t height);
+libinput_event_pointer_get_absolute_y_transformed(struct libinput_event_pointer *event,
+						  uint32_t height);
 
 /**
  * @ingroup event_pointer
@@ -1508,8 +1517,7 @@ libinput_event_pointer_get_button_state(
  * @return The seat wide pressed button count for the key of this event
  */
 uint32_t
-libinput_event_pointer_get_seat_button_count(
-	struct libinput_event_pointer *event);
+libinput_event_pointer_get_seat_button_count(struct libinput_event_pointer *event);
 
 /**
  * @ingroup event_pointer
@@ -2039,8 +2047,7 @@ libinput_event_gesture_get_dy(struct lib
  * @return the unaccelerated relative x movement since the last event
  */
 double
-libinput_event_gesture_get_dx_unaccelerated(
-	struct libinput_event_gesture *event);
+libinput_event_gesture_get_dx_unaccelerated(struct libinput_event_gesture *event);
 
 /**
  * @ingroup event_gesture
@@ -2061,8 +2068,7 @@ libinput_event_gesture_get_dx_unaccelera
  * @return the unaccelerated relative y movement since the last event
  */
 double
-libinput_event_gesture_get_dy_unaccelerated(
-	struct libinput_event_gesture *event);
+libinput_event_gesture_get_dy_unaccelerated(struct libinput_event_gesture *event);
 
 /**
  * @ingroup event_gesture
@@ -2164,8 +2170,7 @@ libinput_event_tablet_tool_get_base_even
  * @since 1.2
  */
 int
-libinput_event_tablet_tool_x_has_changed(
-				struct libinput_event_tablet_tool *event);
+libinput_event_tablet_tool_x_has_changed(struct libinput_event_tablet_tool *event);
 
 /**
  * @ingroup event_tablet
@@ -2187,8 +2192,7 @@ libinput_event_tablet_tool_x_has_changed
  * @since 1.2
  */
 int
-libinput_event_tablet_tool_y_has_changed(
-				struct libinput_event_tablet_tool *event);
+libinput_event_tablet_tool_y_has_changed(struct libinput_event_tablet_tool *event);
 
 /**
  * @ingroup event_tablet
@@ -2211,7 +2215,7 @@ libinput_event_tablet_tool_y_has_changed
  */
 int
 libinput_event_tablet_tool_pressure_has_changed(
-				struct libinput_event_tablet_tool *event);
+	struct libinput_event_tablet_tool *event);
 
 /**
  * @ingroup event_tablet
@@ -2236,7 +2240,7 @@ libinput_event_tablet_tool_pressure_has_
  */
 int
 libinput_event_tablet_tool_distance_has_changed(
-				struct libinput_event_tablet_tool *event);
+	struct libinput_event_tablet_tool *event);
 
 /**
  * @ingroup event_tablet
@@ -2258,8 +2262,7 @@ libinput_event_tablet_tool_distance_has_
  * @since 1.2
  */
 int
-libinput_event_tablet_tool_tilt_x_has_changed(
-				struct libinput_event_tablet_tool *event);
+libinput_event_tablet_tool_tilt_x_has_changed(struct libinput_event_tablet_tool *event);
 
 /**
  * @ingroup event_tablet
@@ -2281,8 +2284,7 @@ libinput_event_tablet_tool_tilt_x_has_ch
  * @since 1.2
  */
 int
-libinput_event_tablet_tool_tilt_y_has_changed(
-				struct libinput_event_tablet_tool *event);
+libinput_event_tablet_tool_tilt_y_has_changed(struct libinput_event_tablet_tool *event);
 /**
  * @ingroup event_tablet
  *
@@ -2304,7 +2306,7 @@ libinput_event_tablet_tool_tilt_y_has_ch
  */
 int
 libinput_event_tablet_tool_rotation_has_changed(
-				struct libinput_event_tablet_tool *event);
+	struct libinput_event_tablet_tool *event);
 /**
  * @ingroup event_tablet
  *
@@ -2325,8 +2327,7 @@ libinput_event_tablet_tool_rotation_has_
  * @since 1.2
  */
 int
-libinput_event_tablet_tool_slider_has_changed(
-				struct libinput_event_tablet_tool *event);
+libinput_event_tablet_tool_slider_has_changed(struct libinput_event_tablet_tool *event);
 
 /**
  * @ingroup event_tablet
@@ -2347,7 +2348,7 @@ libinput_event_tablet_tool_slider_has_ch
  */
 int
 libinput_event_tablet_tool_size_major_has_changed(
-				struct libinput_event_tablet_tool *event);
+	struct libinput_event_tablet_tool *event);
 
 /**
  * @ingroup event_tablet
@@ -2368,7 +2369,7 @@ libinput_event_tablet_tool_size_major_ha
  */
 int
 libinput_event_tablet_tool_size_minor_has_changed(
-				struct libinput_event_tablet_tool *event);
+	struct libinput_event_tablet_tool *event);
 
 /**
  * @ingroup event_tablet
@@ -2390,8 +2391,7 @@ libinput_event_tablet_tool_size_minor_ha
  * @since 1.2
  */
 int
-libinput_event_tablet_tool_wheel_has_changed(
-				struct libinput_event_tablet_tool *event);
+libinput_event_tablet_tool_wheel_has_changed(struct libinput_event_tablet_tool *event);
 
 /**
  * @ingroup event_tablet
@@ -2587,7 +2587,8 @@ libinput_event_tablet_tool_get_rotation(
  * @since 1.2
  */
 double
-libinput_event_tablet_tool_get_slider_position(struct libinput_event_tablet_tool *event);
+libinput_event_tablet_tool_get_slider_position(
+	struct libinput_event_tablet_tool *event);
 
 /**
  * @ingroup event_tablet
@@ -2636,8 +2637,7 @@ libinput_event_tablet_tool_get_size_mino
  * @see libinput_event_tablet_tool_get_wheel_delta_discrete
  */
 double
-libinput_event_tablet_tool_get_wheel_delta(
-				   struct libinput_event_tablet_tool *event);
+libinput_event_tablet_tool_get_wheel_delta(struct libinput_event_tablet_tool *event);
 
 /**
  * @ingroup event_tablet
@@ -2653,7 +2653,7 @@ libinput_event_tablet_tool_get_wheel_del
  */
 int
 libinput_event_tablet_tool_get_wheel_delta_discrete(
-				    struct libinput_event_tablet_tool *event);
+	struct libinput_event_tablet_tool *event);
 
 /**
  * @ingroup event_tablet
@@ -2713,7 +2713,7 @@ libinput_event_tablet_tool_get_y_transfo
  *
  * If the caller holds at least one reference, this struct is used
  * whenever the tools enters proximity again.
-  *
+ *
  * @note Physical tool tracking requires hardware support. If unavailable,
  * libinput creates one tool per type per tablet. See
  * libinput_tablet_tool_get_serial() for more details.
@@ -2753,7 +2753,8 @@ libinput_event_tablet_tool_get_tool(stru
  * @since 1.2
  */
 enum libinput_tablet_tool_proximity_state
-libinput_event_tablet_tool_get_proximity_state(struct libinput_event_tablet_tool *event);
+libinput_event_tablet_tool_get_proximity_state(
+	struct libinput_event_tablet_tool *event);
 
 /**
  * @ingroup event_tablet
@@ -2820,7 +2821,8 @@ libinput_event_tablet_tool_get_button_st
  * @since 1.2
  */
 uint32_t
-libinput_event_tablet_tool_get_seat_button_count(struct libinput_event_tablet_tool *event);
+libinput_event_tablet_tool_get_seat_button_count(
+	struct libinput_event_tablet_tool *event);
 
 /**
  * @ingroup event_tablet
@@ -3037,8 +3039,7 @@ libinput_tablet_tool_has_wheel(struct li
  * @since 1.2
  */
 int
-libinput_tablet_tool_has_button(struct libinput_tablet_tool *tool,
-				uint32_t code);
+libinput_tablet_tool_has_button(struct libinput_tablet_tool *tool, uint32_t code);
 
 /**
  * @ingroup event_tablet
@@ -3124,8 +3125,7 @@ libinput_tablet_tool_get_user_data(struc
  * @since 1.2
  */
 void
-libinput_tablet_tool_set_user_data(struct libinput_tablet_tool *tool,
-				   void *user_data);
+libinput_tablet_tool_set_user_data(struct libinput_tablet_tool *tool, void *user_data);
 
 /**
  * @defgroup event_tablet_pad Tablet pad events
@@ -3635,8 +3635,7 @@ libinput_udev_create_context(const struc
  * @return 0 on success or -1 on failure.
  */
 int
-libinput_udev_assign_seat(struct libinput *libinput,
-			  const char *seat_id);
+libinput_udev_assign_seat(struct libinput *libinput, const char *seat_id);
 
 /**
  * @ingroup base
@@ -3682,8 +3681,7 @@ libinput_path_create_context(const struc
  * context initialized with libinput_udev_create_context().
  */
 struct libinput_device *
-libinput_path_add_device(struct libinput *libinput,
-			 const char *path);
+libinput_path_add_device(struct libinput *libinput, const char *path);
 
 /**
  * @ingroup base
@@ -3709,6 +3707,98 @@ libinput_path_remove_device(struct libin
 /**
  * @ingroup base
  *
+ * Appends the given directory path to the libinput plugin lookup path.
+ * If the path is already in the lookup paths, this function does nothing.
+ *
+ * A path's priority is determined by its position in the list; the first
+ * path in the list has the highest priority.
+ *
+ * Plugin lookup is performed across all paths in lexical order. If
+ * a plugin exists in multiple paths, the one in the highest priority
+ * path (i.e. front of the list) is used.
+ *
+ * Paths are not traversed recursively.
+ *
+ * Plugins that have a 0 byte size shadow any plugins with the same name
+ * but do not provide any fuctionality. This allows disabling a plugin
+ * by simply dropping an empty file in a higher-priority directory.
+ *
+ * This function must be called before libinput_plugin_system_load_plugins().
+ *
+ * @see libinput_plugin_system_append_default_paths
+ *
+ * @since 1.30
+ */
+void
+libinput_plugin_system_append_path(struct libinput *libinput, const char *path);
+
+/**
+ * @ingroup base
+ *
+ * Add the default plugin lookup paths, typically:
+ * - /etc/libinput/plugins/
+ * - /usr/lib{64}/libinput/plugins/
+ *
+ * @warning More paths may be added to the default lookup paths in future releases.
+ * A caller relying on a specific set of default paths should add those individually
+ * using libinput_plugin_system_append_path().
+ *
+ * These paths are inserted at the current priority - to add
+ * paths with a higher priority than these, call
+ * libinput_plugin_system_append_path() prior to this function.
+ *
+ * See libinput_plugin_system_append_path() for more details.
+ *
+ * The exact value of these paths depend on the libdir and sysconfigdir
+ * variables, defined at compile time.
+ *
+ * This function must be called before
+ * libinput_plugin_system_load_plugins().
+ *
+ * @see libinput_plugin_system_append_path
+ *
+ * @since 1.30
+ */
+void
+libinput_plugin_system_append_default_paths(struct libinput *libinput);
+
+enum libinput_plugin_system_flags {
+	LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE = 0,
+};
+
+/**
+ * @ingroup base
+ *
+ * Load the plugins from the set of lookup paths. This function does nothing
+ * if no plugin paths have been configured, see
+ * libinput_plugin_system_append_default_paths() and
+ * libinput_plugin_system_append_path().
+ *
+ * The typical use of this function is:
+ * ```
+ * struct libinput *li = libinput_udev_create_context(...);
+ * libinput_plugin_system_append_default_paths(li);
+ * libinput_plugin_system_load_plugins(li, flags);
+ * ```
+ * @warning The default lookup paths may change over time. See
+ * libinput_plugin_system_append_default_paths().
+ *
+ * This function must be called before libinput iterates through the
+ * devices, i.e. before libinput_udev_assign_seat() or the first
+ * call to libinput_path_add_device().
+ *
+ * @return 0 or a negative errno on failure
+ * @retval -ENOSYS libinput was compiled without plugin support
+ *
+ * @since 1.30
+ */
+int
+libinput_plugin_system_load_plugins(struct libinput *libinput,
+				    enum libinput_plugin_system_flags flags);
+
+/**
+ * @ingroup base
+ *
  * libinput keeps a single file descriptor for all events. Call into
  * libinput_dispatch() if any events become available on this fd.
  *
@@ -3777,8 +3867,7 @@ libinput_next_event_type(struct libinput
  * interfaces.
  */
 void
-libinput_set_user_data(struct libinput *libinput,
-		       void *user_data);
+libinput_set_user_data(struct libinput *libinput, void *user_data);
 
 /**
  * @ingroup base
@@ -3917,8 +4006,8 @@ libinput_log_get_priority(const struct l
  */
 typedef void (*libinput_log_handler)(struct libinput *libinput,
 				     enum libinput_log_priority priority,
-				     const char *format, va_list args)
-	   LIBINPUT_ATTRIBUTE_PRINTF(3, 0);
+				     const char *format,
+				     va_list args) LIBINPUT_ATTRIBUTE_PRINTF(3, 0);
 
 /**
  * @ingroup base
@@ -3936,8 +4025,7 @@ typedef void (*libinput_log_handler)(str
  * @see libinput_log_get_priority
  */
 void
-libinput_log_set_handler(struct libinput *libinput,
-			 libinput_log_handler log_handler);
+libinput_log_set_handler(struct libinput *libinput, libinput_log_handler log_handler);
 
 /**
  * @defgroup seat Initialization and manipulation of seats
@@ -4313,8 +4401,7 @@ libinput_device_get_seat(struct libinput
  * @return 0 on success, non-zero on error
  */
 int
-libinput_device_set_seat_logical_name(struct libinput_device *device,
-				      const char *name);
+libinput_device_set_seat_logical_name(struct libinput_device *device, const char *name);
 
 /**
  * @ingroup device
@@ -4349,8 +4436,7 @@ libinput_device_get_udev_device(struct l
  * @param leds A mask of the LEDs to set, or unset.
  */
 void
-libinput_device_led_update(struct libinput_device *device,
-			   enum libinput_led leds);
+libinput_device_led_update(struct libinput_device *device, enum libinput_led leds);
 
 /**
  * @ingroup device
@@ -4378,9 +4464,7 @@ libinput_device_has_capability(struct li
  * @return 0 on success, or nonzero otherwise
  */
 int
-libinput_device_get_size(struct libinput_device *device,
-			 double *width,
-			 double *height);
+libinput_device_get_size(struct libinput_device *device, double *width, double *height);
 
 /**
  * @ingroup device
@@ -4410,8 +4494,7 @@ libinput_device_pointer_has_button(struc
  * on error.
  */
 int
-libinput_device_keyboard_has_key(struct libinput_device *device,
-				 uint32_t code);
+libinput_device_keyboard_has_key(struct libinput_device *device, uint32_t code);
 
 /**
  * @ingroup device
@@ -4530,8 +4613,7 @@ libinput_device_tablet_pad_get_num_strip
  * @since 1.15
  */
 int
-libinput_device_tablet_pad_has_key(struct libinput_device *device,
-				   uint32_t code);
+libinput_device_tablet_pad_has_key(struct libinput_device *device, uint32_t code);
 
 /**
  * @ingroup device
@@ -4644,10 +4726,10 @@ libinput_device_group_get_user_data(stru
  * Status codes returned when applying configuration settings.
  */
 enum libinput_config_status {
-	LIBINPUT_CONFIG_STATUS_SUCCESS = 0,	/**< Config applied successfully */
-	LIBINPUT_CONFIG_STATUS_UNSUPPORTED,	/**< Configuration not available on
-						     this device */
-	LIBINPUT_CONFIG_STATUS_INVALID,		/**< Invalid parameter range */
+	LIBINPUT_CONFIG_STATUS_SUCCESS = 0, /**< Config applied successfully */
+	LIBINPUT_CONFIG_STATUS_UNSUPPORTED, /**< Configuration not available on
+						 this device */
+	LIBINPUT_CONFIG_STATUS_INVALID,     /**< Invalid parameter range */
 };
 
 /**
@@ -4668,8 +4750,8 @@ libinput_config_status_to_str(enum libin
 enum libinput_config_tap_state {
 	LIBINPUT_CONFIG_TAP_DISABLED, /**< Tapping is to be disabled, or is
 					currently disabled */
-	LIBINPUT_CONFIG_TAP_ENABLED, /**< Tapping is to be enabled, or is
-				       currently enabled */
+	LIBINPUT_CONFIG_TAP_ENABLED,  /**< Tapping is to be enabled, or is
+					currently enabled */
 };
 
 /**
@@ -4804,7 +4886,7 @@ enum libinput_config_clickfinger_button_
  */
 enum libinput_config_status
 libinput_device_config_tap_set_button_map(struct libinput_device *device,
-					    enum libinput_config_tap_button_map map);
+					  enum libinput_config_tap_button_map map);
 
 /**
  * @ingroup config
@@ -4973,8 +5055,9 @@ enum libinput_config_drag_lock_state {
  * @see libinput_device_config_tap_get_default_drag_lock_enabled
  */
 enum libinput_config_status
-libinput_device_config_tap_set_drag_lock_enabled(struct libinput_device *device,
-						 enum libinput_config_drag_lock_state enable);
+libinput_device_config_tap_set_drag_lock_enabled(
+	struct libinput_device *device,
+	enum libinput_config_drag_lock_state enable);
 
 /**
  * @ingroup config
@@ -5022,7 +5105,8 @@ libinput_device_config_tap_get_drag_lock
  * @see libinput_device_config_tap_get_drag_lock_enabled
  */
 enum libinput_config_drag_lock_state
-libinput_device_config_tap_get_default_drag_lock_enabled(struct libinput_device *device);
+libinput_device_config_tap_get_default_drag_lock_enabled(
+	struct libinput_device *device);
 
 /**
  * @ingroup config
@@ -5329,8 +5413,9 @@ libinput_device_config_area_has_rectangl
  * @see libinput_device_config_area_get_default_rectangle
  */
 enum libinput_config_status
-libinput_device_config_area_set_rectangle(struct libinput_device *device,
-					  const struct libinput_config_area_rectangle *rect);
+libinput_device_config_area_set_rectangle(
+	struct libinput_device *device,
+	const struct libinput_config_area_rectangle *rect);
 
 /**
  * @ingroup config
@@ -5533,8 +5618,7 @@ libinput_device_config_accel_is_availabl
  * @see libinput_device_config_accel_get_default_speed
  */
 enum libinput_config_status
-libinput_device_config_accel_set_speed(struct libinput_device *device,
-				       double speed);
+libinput_device_config_accel_set_speed(struct libinput_device *device, double speed);
 
 /**
  * @ingroup config
@@ -5775,7 +5859,9 @@ enum libinput_config_accel_type {
 enum libinput_config_status
 libinput_config_accel_set_points(struct libinput_config_accel *accel_config,
 				 enum libinput_config_accel_type accel_type,
-				 double step, size_t npoints, double *points);
+				 double step,
+				 size_t npoints,
+				 const double *points);
 
 /**
  * @ingroup config
@@ -5900,7 +5986,8 @@ libinput_device_config_scroll_set_natura
  * @see libinput_device_config_scroll_get_default_natural_scroll_enabled
  */
 int
-libinput_device_config_scroll_get_natural_scroll_enabled(struct libinput_device *device);
+libinput_device_config_scroll_get_natural_scroll_enabled(
+	struct libinput_device *device);
 
 /**
  * @ingroup config
@@ -5916,7 +6003,8 @@ libinput_device_config_scroll_get_natura
  * @see libinput_device_config_scroll_get_natural_scroll_enabled
  */
 int
-libinput_device_config_scroll_get_default_natural_scroll_enabled(struct libinput_device *device);
+libinput_device_config_scroll_get_default_natural_scroll_enabled(
+	struct libinput_device *device);
 
 /**
  * @ingroup config
@@ -5958,8 +6046,7 @@ libinput_device_config_left_handed_is_av
  * @see libinput_device_config_left_handed_get_default
  */
 enum libinput_config_status
-libinput_device_config_left_handed_set(struct libinput_device *device,
-				       int left_handed);
+libinput_device_config_left_handed_set(struct libinput_device *device, int left_handed);
 
 /**
  * @ingroup config
@@ -6118,8 +6205,9 @@ libinput_device_config_click_get_default
  * @see libinput_device_config_click_get_default_clickfinger_button_map
  */
 enum libinput_config_status
-libinput_device_config_click_set_clickfinger_button_map(struct libinput_device *device,
-							enum libinput_config_clickfinger_button_map map);
+libinput_device_config_click_set_clickfinger_button_map(
+	struct libinput_device *device,
+	enum libinput_config_clickfinger_button_map map);
 
 /**
  * @ingroup config
@@ -6153,7 +6241,8 @@ libinput_device_config_click_get_clickfi
  * @see libinput_device_config_click_get_clickfinger_button_map
  */
 enum libinput_config_clickfinger_button_map
-libinput_device_config_click_get_default_clickfinger_button_map(struct libinput_device *device);
+libinput_device_config_click_get_default_clickfinger_button_map(
+	struct libinput_device *device);
 
 /**
  * @ingroup config
@@ -6192,8 +6281,7 @@ enum libinput_config_middle_emulation_st
  * @see libinput_device_config_middle_emulation_get_default_enabled
  */
 int
-libinput_device_config_middle_emulation_is_available(
-		struct libinput_device *device);
+libinput_device_config_middle_emulation_is_available(struct libinput_device *device);
 
 /**
  * @ingroup config
@@ -6219,8 +6307,8 @@ libinput_device_config_middle_emulation_
  */
 enum libinput_config_status
 libinput_device_config_middle_emulation_set_enabled(
-		struct libinput_device *device,
-		enum libinput_config_middle_emulation_state enable);
+	struct libinput_device *device,
+	enum libinput_config_middle_emulation_state enable);
 
 /**
  * @ingroup config
@@ -6246,8 +6334,7 @@ libinput_device_config_middle_emulation_
  * @see libinput_device_config_middle_emulation_get_default_enabled
  */
 enum libinput_config_middle_emulation_state
-libinput_device_config_middle_emulation_get_enabled(
-		struct libinput_device *device);
+libinput_device_config_middle_emulation_get_enabled(struct libinput_device *device);
 
 /**
  * @ingroup config
@@ -6275,7 +6362,7 @@ libinput_device_config_middle_emulation_
  */
 enum libinput_config_middle_emulation_state
 libinput_device_config_middle_emulation_get_default_enabled(
-		struct libinput_device *device);
+	struct libinput_device *device);
 
 /**
  * @ingroup config
@@ -6508,8 +6595,9 @@ enum libinput_config_scroll_button_lock_
  * @see libinput_device_config_scroll_get_default_button
  */
 enum libinput_config_status
-libinput_device_config_scroll_set_button_lock(struct libinput_device *device,
-					      enum libinput_config_scroll_button_lock_state state);
+libinput_device_config_scroll_set_button_lock(
+	struct libinput_device *device,
+	enum libinput_config_scroll_button_lock_state state);
 
 /**
  * @ingroup config
@@ -6703,7 +6791,7 @@ libinput_device_config_dwtp_is_available
  */
 enum libinput_config_status
 libinput_device_config_dwtp_set_enabled(struct libinput_device *device,
-				       enum libinput_config_dwtp_state enable);
+					enum libinput_config_dwtp_state enable);
 
 /**
  * @ingroup config
@@ -6848,7 +6936,8 @@ libinput_device_config_rotation_get_defa
  * @since 1.26
  */
 int
-libinput_tablet_tool_config_pressure_range_is_available(struct libinput_tablet_tool *tool);
+libinput_tablet_tool_config_pressure_range_is_available(
+	struct libinput_tablet_tool *tool);
 
 /**
  * @ingroup config
@@ -6903,7 +6992,8 @@ libinput_tablet_tool_config_pressure_ran
  * @since 1.26
  */
 double
-libinput_tablet_tool_config_pressure_range_get_minimum(struct libinput_tablet_tool *tool);
+libinput_tablet_tool_config_pressure_range_get_minimum(
+	struct libinput_tablet_tool *tool);
 
 /**
  * @ingroup config
@@ -6925,7 +7015,8 @@ libinput_tablet_tool_config_pressure_ran
  * @since 1.26
  */
 double
-libinput_tablet_tool_config_pressure_range_get_maximum(struct libinput_tablet_tool *tool);
+libinput_tablet_tool_config_pressure_range_get_maximum(
+	struct libinput_tablet_tool *tool);
 
 /**
  * @ingroup config
@@ -6947,7 +7038,8 @@ libinput_tablet_tool_config_pressure_ran
  * @since 1.26
  */
 double
-libinput_tablet_tool_config_pressure_range_get_default_minimum(struct libinput_tablet_tool *tool);
+libinput_tablet_tool_config_pressure_range_get_default_minimum(
+	struct libinput_tablet_tool *tool);
 
 /**
  * @ingroup config
@@ -6969,7 +7061,213 @@ libinput_tablet_tool_config_pressure_ran
  * @since 1.26
  */
 double
-libinput_tablet_tool_config_pressure_range_get_default_maximum(struct libinput_tablet_tool *tool);
+libinput_tablet_tool_config_pressure_range_get_default_maximum(
+	struct libinput_tablet_tool *tool);
+
+/**
+ * @ingroup config
+ */
+enum libinput_config_eraser_button_mode {
+	/**
+	 * Use the default hardware behavior of the tool. libinput
+	 * does not modify the behavior of the eraser button (if any).
+	 */
+	LIBINPUT_CONFIG_ERASER_BUTTON_DEFAULT = 0,
+	/**
+	 * The eraser button on the tool sends a button event
+	 * instead. If this tool comes into proximity as an eraser,
+	 * a button event on the pen is emulated instead.
+	 *
+	 * See libinput_tablet_tool_config_eraser_button_set_mode() for details.
+	 */
+	LIBINPUT_CONFIG_ERASER_BUTTON_BUTTON = (1 << 0),
+};
+
+/**
+ * @ingroup config
+ *
+ * Check if a tool can change the behavior of or to a firmware eraser button.
+ *
+ * A firmware eraser button is a button on the tool that, when pressed,
+ * virtually toggles the pen going out of proximity followed by the
+ * eraser tool coming in proximity. When released, the eraser goes
+ * out of proximity followed by the pen coming back into proximity.
+ *
+ * This is the default behavior for many contemporary pens who implement
+ * this in firmware. See also the [Windows Pen
+ * States](https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states).
+ *
+ * See the libinput documentation for more details.
+ *
+ * @param tool The libinput tool
+ * @return Non-zero if the device can be set to change to an eraser on button
+ * press.
+ *
+ * @see libinput_tablet_tool_config_eraser_button_get_modes
+ * @see libinput_tablet_tool_config_eraser_button_set_mode
+ * @see libinput_tablet_tool_config_eraser_button_get_mode
+ * @see libinput_tablet_tool_config_eraser_button_get_default_mode
+ *
+ * @since 1.29
+ */
+uint32_t
+libinput_tablet_tool_config_eraser_button_get_modes(struct libinput_tablet_tool *tool);
+
+/**
+ * @ingroup config
+ *
+ * Change the eraser button behavior on a tool.
+ *
+ * If set to @ref LIBINPUT_CONFIG_ERASER_BUTTON_BUTTON, pressing the
+ * firmware eraser button on the tool instead triggers an event
+ * of type @ref LIBINPUT_EVENT_TABLET_TOOL_BUTTON.
+ * This event's libinput_event_tablet_tool_get_button() returns the
+ * button set with
+ * libinput_tablet_tool_config_eraser_button_set_button()
+ * Releasing the firmware eraser button releases that button again.
+ *
+ * @param tool The libinput tool
+ * @param mode The eraser button mode to switch to
+ *
+ * @return A config status code
+ *
+ * @see libinput_tablet_tool_config_eraser_button_get_modes
+ * @see libinput_tablet_tool_config_eraser_button_set_mode
+ * @see libinput_tablet_tool_config_eraser_button_get_mode
+ * @see libinput_tablet_tool_config_eraser_button_get_default_mode
+ * @see libinput_tablet_tool_config_eraser_button_set_button
+ * @see libinput_tablet_tool_config_eraser_button_get_button
+ * @see libinput_tablet_tool_config_eraser_button_get_default_button
+ *
+ * @since 1.29
+ */
+enum libinput_config_status
+libinput_tablet_tool_config_eraser_button_set_mode(
+	struct libinput_tablet_tool *tool,
+	enum libinput_config_eraser_button_mode mode);
+
+/**
+ * @ingroup config
+ *
+ * Get the mode for the eraser button.
+ *
+ * @param tool The libinput tool
+ *
+ * @return The eraser mode
+ *
+ * @see libinput_tablet_tool_config_eraser_button_get_modes
+ * @see libinput_tablet_tool_config_eraser_button_set_mode
+ * @see libinput_tablet_tool_config_eraser_button_get_mode
+ * @see libinput_tablet_tool_config_eraser_button_get_default_mode
+ * @see libinput_tablet_tool_config_eraser_button_set_button
+ * @see libinput_tablet_tool_config_eraser_button_get_button
+ * @see libinput_tablet_tool_config_eraser_button_get_default_button
+ *
+ * @since 1.29
+ */
+enum libinput_config_eraser_button_mode
+libinput_tablet_tool_config_eraser_button_get_mode(struct libinput_tablet_tool *tool);
+
+/**
+ * @ingroup config
+ *
+ * Get the default mode for the eraser button.
+ *
+ * @param tool The libinput tool
+ *
+ * @return The eraser button, if any, or zero otherwise
+ *
+ * @see libinput_tablet_tool_config_eraser_button_get_modes
+ * @see libinput_tablet_tool_config_eraser_button_set_mode
+ * @see libinput_tablet_tool_config_eraser_button_get_mode
+ * @see libinput_tablet_tool_config_eraser_button_get_default_mode
+ * @see libinput_tablet_tool_config_eraser_button_set_button
+ * @see libinput_tablet_tool_config_eraser_button_get_button
+ * @see libinput_tablet_tool_config_eraser_button_get_default_button
+ *
+ * @since 1.29
+ */
+enum libinput_config_eraser_button_mode
+libinput_tablet_tool_config_eraser_button_get_default_mode(
+	struct libinput_tablet_tool *tool);
+
+/**
+ * @ingroup config
+ *
+ * Set a button to be the eraser button for this tool.
+ * This configuration has no effect unless the caller also sets
+ * the eraser mode to @ref LIBINPUT_CONFIG_ERASER_BUTTON_BUTTON via
+ * libinput_tablet_tool_config_eraser_button_set_mode().
+ *
+ * The buttons BTN_STYLUS, BTN_STYLUS2 and BTN_STYLUS2 are always
+ * allowed, even if libinput_tablet_tool_has_button() returns zero
+ * for the button. Otherwise, the button must be one that
+ * libinput_tablet_tool_has_button() returns a nonzero value for.
+ *
+ * @param tool The libinput tool
+ * @param button The button, usually one of BTN_STYLUS, BTN_STYLUS2 or
+ * BTN_STYLUS3
+ *
+ * @return A config status code
+ *
+ * @see libinput_tablet_tool_config_eraser_button_get_modes
+ * @see libinput_tablet_tool_config_eraser_button_set_mode
+ * @see libinput_tablet_tool_config_eraser_button_get_mode
+ * @see libinput_tablet_tool_config_eraser_button_get_default_mode
+ * @see libinput_tablet_tool_config_eraser_button_set_button
+ * @see libinput_tablet_tool_config_eraser_button_get_button
+ * @see libinput_tablet_tool_config_eraser_button_get_default_button
+ *
+ * @since 1.29
+ */
+enum libinput_config_status
+libinput_tablet_tool_config_eraser_button_set_button(struct libinput_tablet_tool *tool,
+						     uint32_t button);
+
+/**
+ * @ingroup config
+ *
+ * Get the button configured to emulate an eraser for this tool.
+ *
+ * @param tool The libinput tool
+ *
+ * @return The eraser button, if any, or zero otherwise
+ *
+ * @see libinput_tablet_tool_config_eraser_button_get_modes
+ * @see libinput_tablet_tool_config_eraser_button_set_mode
+ * @see libinput_tablet_tool_config_eraser_button_get_mode
+ * @see libinput_tablet_tool_config_eraser_button_get_default_mode
+ * @see libinput_tablet_tool_config_eraser_button_set_button
+ * @see libinput_tablet_tool_config_eraser_button_get_button
+ * @see libinput_tablet_tool_config_eraser_button_get_default_button
+ *
+ * @since 1.29
+ */
+unsigned int
+libinput_tablet_tool_config_eraser_button_get_button(struct libinput_tablet_tool *tool);
+
+/**
+ * @ingroup config
+ *
+ * Get the default button configured to emulate an eraser for this tool.
+ *
+ * @param tool The libinput tool
+ *
+ * @return The eraser button, if any, or zero otherwise
+ *
+ * @see libinput_tablet_tool_config_eraser_button_get_modes
+ * @see libinput_tablet_tool_config_eraser_button_set_mode
+ * @see libinput_tablet_tool_config_eraser_button_get_mode
+ * @see libinput_tablet_tool_config_eraser_button_get_default_mode
+ * @see libinput_tablet_tool_config_eraser_button_set_button
+ * @see libinput_tablet_tool_config_eraser_button_get_button
+ * @see libinput_tablet_tool_config_eraser_button_get_default_button
+ *
+ * @since 1.29
+ */
+unsigned int
+libinput_tablet_tool_config_eraser_button_get_default_button(
+	struct libinput_tablet_tool *tool);
 
 #ifdef __cplusplus
 }
diff -pruN 1.28.1-1/src/libinput.pc.in 1.30.0-1/src/libinput.pc.in
--- 1.28.1-1/src/libinput.pc.in	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/libinput.pc.in	1970-01-01 00:00:00.000000000 +0000
@@ -1,14 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-datarootdir=@datarootdir@
-pkgdatadir=@datadir@/@PACKAGE@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: Libinput
-Description: Input device library
-Version: @LIBINPUT_VERSION@
-Cflags: -I${includedir}
-Libs: -L${libdir} -linput
-Libs.private: -lm -lrt
-Requires.private: libudev
diff -pruN 1.28.1-1/src/libinput.sym 1.30.0-1/src/libinput.sym
--- 1.28.1-1/src/libinput.sym	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/libinput.sym	2025-11-25 03:40:43.000000000 +0000
@@ -364,3 +364,19 @@ LIBINPUT_1.28 {
 	libinput_device_config_3fg_drag_get_enabled;
 	libinput_device_config_3fg_drag_get_default_enabled;
 } LIBINPUT_1.27;
+
+LIBINPUT_1.29 {
+	libinput_tablet_tool_config_eraser_button_get_button;
+	libinput_tablet_tool_config_eraser_button_get_default_button;
+	libinput_tablet_tool_config_eraser_button_get_default_mode;
+	libinput_tablet_tool_config_eraser_button_get_mode;
+	libinput_tablet_tool_config_eraser_button_get_modes;
+	libinput_tablet_tool_config_eraser_button_set_button;
+	libinput_tablet_tool_config_eraser_button_set_mode;
+} LIBINPUT_1.28;
+
+LIBINPUT_1.30 {
+	libinput_plugin_system_append_default_paths;
+	libinput_plugin_system_append_path;
+	libinput_plugin_system_load_plugins;
+} LIBINPUT_1.29;
diff -pruN 1.28.1-1/src/path-seat.c 1.30.0-1/src/path-seat.c
--- 1.28.1-1/src/path-seat.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/path-seat.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,9 +23,9 @@
 
 #include "config.h"
 
+#include <libudev.h>
 #include <string.h>
 #include <sys/stat.h>
-#include <libudev.h>
 
 #include "evdev.h"
 
@@ -53,8 +53,7 @@ path_disable_device(struct evdev_device
 	struct libinput_seat *seat = device->base.seat;
 	struct evdev_device *dev;
 
-	list_for_each_safe(dev,
-			   &seat->devices_list, base.link) {
+	list_for_each_safe(dev, &seat->devices_list, base.link) {
 		if (dev != device)
 			continue;
 
@@ -66,14 +65,13 @@ path_disable_device(struct evdev_device
 static void
 path_input_disable(struct libinput *libinput)
 {
-	struct path_input *input = (struct path_input*)libinput;
+	struct path_input *input = (struct path_input *)libinput;
 	struct path_seat *seat;
 	struct evdev_device *device;
 
 	list_for_each_safe(seat, &input->base.seat_list, base.link) {
 		libinput_seat_ref(&seat->base);
-		list_for_each_safe(device,
-				   &seat->base.devices_list, base.link)
+		list_for_each_safe(device, &seat->base.devices_list, base.link)
 			path_disable_device(device);
 		libinput_seat_unref(&seat->base);
 	}
@@ -82,11 +80,11 @@ path_input_disable(struct libinput *libi
 static void
 path_seat_destroy(struct libinput_seat *seat)
 {
-	struct path_seat *pseat = (struct path_seat*)seat;
+	struct path_seat *pseat = (struct path_seat *)seat;
 	free(pseat);
 }
 
-static struct path_seat*
+static struct path_seat *
 path_seat_create(struct path_input *input,
 		 const char *seat_name,
 		 const char *seat_logical_name)
@@ -95,13 +93,16 @@ path_seat_create(struct path_input *inpu
 
 	seat = zalloc(sizeof(*seat));
 
-	libinput_seat_init(&seat->base, &input->base, seat_name,
-			   seat_logical_name, path_seat_destroy);
+	libinput_seat_init(&seat->base,
+			   &input->base,
+			   seat_name,
+			   seat_logical_name,
+			   path_seat_destroy);
 
 	return seat;
 }
 
-static struct path_seat*
+static struct path_seat *
 path_seat_get_named(struct path_input *input,
 		    const char *seat_name_physical,
 		    const char *seat_name_logical)
@@ -138,7 +139,8 @@ path_seat_get_for_device(struct path_inp
 		seat_logical_name = safe_strdup(seat_logical_name_override);
 	} else {
 		seat_prop = udev_device_get_property_value(udev_device, "WL_SEAT");
-		seat_logical_name = safe_strdup(seat_prop ? seat_prop : default_seat_name);
+		seat_logical_name =
+			safe_strdup(seat_prop ? seat_prop : default_seat_name);
 	}
 
 	if (!seat_logical_name) {
@@ -215,7 +217,7 @@ out:
 static int
 path_input_enable(struct libinput *libinput)
 {
-	struct path_input *input = (struct path_input*)libinput;
+	struct path_input *input = (struct path_input *)libinput;
 	struct path_device *dev;
 
 	list_for_each(dev, &input->path_list, link) {
@@ -239,14 +241,13 @@ path_device_destroy(struct path_device *
 static void
 path_input_destroy(struct libinput *input)
 {
-	struct path_input *path_input = (struct path_input*)input;
+	struct path_input *path_input = (struct path_input *)input;
 	struct path_device *dev;
 
 	udev_unref(path_input->udev);
 
 	list_for_each_safe(dev, &path_input->path_list, link)
 		path_device_destroy(dev);
-
 }
 
 static struct libinput_device *
@@ -254,7 +255,7 @@ path_create_device(struct libinput *libi
 		   struct udev_device *udev_device,
 		   const char *seat_name)
 {
-	struct path_input *input = (struct path_input*)libinput;
+	struct path_input *input = (struct path_input *)libinput;
 	struct path_device *dev;
 	struct libinput_device *device;
 
@@ -272,8 +273,7 @@ path_create_device(struct libinput *libi
 }
 
 static int
-path_device_change_seat(struct libinput_device *device,
-			const char *seat_name)
+path_device_change_seat(struct libinput_device *device, const char *seat_name)
 {
 	struct libinput *libinput = device->seat->libinput;
 	struct evdev_device *evdev = evdev_device(device);
@@ -302,24 +302,22 @@ libinput_path_create_context(const struc
 			     void *user_data)
 {
 	struct path_input *input;
-	struct udev *udev;
 
 	if (!interface)
 		return NULL;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	if (!udev)
 		return NULL;
 
 	input = zalloc(sizeof *input);
-	if (libinput_init(&input->base, interface,
-			  &interface_backend, user_data) != 0) {
-		udev_unref(udev);
+	if (libinput_init(&input->base, interface, &interface_backend, user_data) !=
+	    0) {
 		free(input);
 		return NULL;
 	}
 
-	input->udev = udev;
+	input->udev = udev_ref(udev);
 	list_init(&input->path_list);
 
 	return &input->base;
@@ -344,8 +342,8 @@ udev_device_from_devnode(struct libinput
 		count++;
 		if (count > 200) {
 			log_bug_libinput(libinput,
-					"udev device never initialized (%s)\n",
-					devnode);
+					 "udev device never initialized (%s)\n",
+					 devnode);
 			return NULL;
 		}
 		msleep(10);
@@ -356,12 +354,10 @@ udev_device_from_devnode(struct libinput
 }
 
 LIBINPUT_EXPORT struct libinput_device *
-libinput_path_add_device(struct libinput *libinput,
-			 const char *path)
+libinput_path_add_device(struct libinput *libinput, const char *path)
 {
 	struct path_input *input = (struct path_input *)libinput;
 	struct udev *udev = input->udev;
-	struct udev_device *udev_device;
 	struct libinput_device *device;
 
 	if (strlen(path) > PATH_MAX) {
@@ -376,17 +372,19 @@ libinput_path_add_device(struct libinput
 		return NULL;
 	}
 
-	udev_device = udev_device_from_devnode(libinput, udev, path);
+	_unref_(udev_device) *udev_device =
+		udev_device_from_devnode(libinput, udev, path);
 	if (!udev_device) {
 		log_bug_client(libinput, "Invalid path %s\n", path);
 		return NULL;
 	}
 
 	if (ignore_litest_test_suite_device(udev_device)) {
-		udev_device_unref(udev_device);
 		return NULL;
 	}
 
+	libinput_plugin_system_autoload(libinput);
+
 	/* We cannot do this during path_create_context because the log
 	 * handler isn't set up there but we really want to log to the right
 	 * place if the quirks run into parser errors. So we have to do it
@@ -395,7 +393,6 @@ libinput_path_add_device(struct libinput
 	libinput_init_quirks(libinput);
 
 	device = path_create_device(libinput, udev_device, NULL);
-	udev_device_unref(udev_device);
 	return device;
 }
 
@@ -403,7 +400,7 @@ LIBINPUT_EXPORT void
 libinput_path_remove_device(struct libinput_device *device)
 {
 	struct libinput *libinput = device->seat->libinput;
-	struct path_input *input = (struct path_input*)libinput;
+	struct path_input *input = (struct path_input *)libinput;
 	struct libinput_seat *seat;
 	struct evdev_device *evdev = evdev_device(device);
 	struct path_device *dev;
diff -pruN 1.28.1-1/src/quirks.c 1.30.0-1/src/quirks.c
--- 1.28.1-1/src/quirks.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/quirks.c	2025-11-25 03:40:43.000000000 +0000
@@ -30,18 +30,17 @@
 
 #undef NDEBUG /* You don't get to disable asserts here */
 #include <assert.h>
-#include <stdlib.h>
-#include <libudev.h>
 #include <dirent.h>
 #include <fnmatch.h>
 #include <libgen.h>
+#include <libudev.h>
+#include <stdlib.h>
 #ifdef __FreeBSD__
 #include <kenv.h>
 #endif
 
-#include "libinput-versionsort.h"
 #include "libinput-util.h"
-
+#include "libinput-versionsort.h"
 #include "quirks.h"
 
 /* Custom logging so we can have detailed output for the tool but minimal
@@ -95,17 +94,17 @@ struct property {
 };
 
 enum match_flags {
-	M_NAME		= bit(0),
-	M_BUS		= bit(1),
-	M_VID		= bit(2),
-	M_PID		= bit(3),
-	M_DMI		= bit(4),
-	M_UDEV_TYPE	= bit(5),
-	M_DT		= bit(6),
-	M_VERSION	= bit(7),
-	M_UNIQ          = bit(8),
+	M_NAME = bit(0),
+	M_BUS = bit(1),
+	M_VID = bit(2),
+	M_PID = bit(3),
+	M_DMI = bit(4),
+	M_UDEV_TYPE = bit(5),
+	M_DT = bit(6),
+	M_VERSION = bit(7),
+	M_UNIQ = bit(8),
 
-	M_LAST		= M_UNIQ,
+	M_LAST = M_UNIQ,
 };
 
 enum bustype {
@@ -119,13 +118,13 @@ enum bustype {
 };
 
 enum udev_type {
-	UDEV_MOUSE		= bit(1),
-	UDEV_POINTINGSTICK	= bit(2),
-	UDEV_TOUCHPAD		= bit(3),
-	UDEV_TABLET		= bit(4),
-	UDEV_TABLET_PAD		= bit(5),
-	UDEV_JOYSTICK		= bit(6),
-	UDEV_KEYBOARD		= bit(7),
+	UDEV_MOUSE = bit(1),
+	UDEV_POINTINGSTICK = bit(2),
+	UDEV_TOUCHPAD = bit(3),
+	UDEV_TABLET = bit(4),
+	UDEV_TABLET_PAD = bit(5),
+	UDEV_JOYSTICK = bit(6),
+	UDEV_KEYBOARD = bit(7),
 };
 
 /**
@@ -144,12 +143,12 @@ struct match {
 	uint32_t product[64]; /* zero-terminated */
 	uint32_t version;
 
-	char *dmi;	/* dmi modalias with preceding "dmi:" */
+	char *dmi; /* dmi modalias with preceding "dmi:" */
 
 	/* We can have more than one type set, so this is a bitfield */
 	uint32_t udev_type;
 
-	char *dt;	/* device tree compatible (first) string */
+	char *dt; /* device tree compatible (first) string */
 };
 
 /**
@@ -158,10 +157,10 @@ struct match {
 struct section {
 	struct list link;
 
-	bool has_match;		/* to check for empty sections */
-	bool has_property;	/* to check for empty sections */
+	bool has_match;    /* to check for empty sections */
+	bool has_property; /* to check for empty sections */
 
-	char *name;		/* the [Section Name] */
+	char *name; /* the [Section Name] */
 	struct match match;
 	struct list properties;
 };
@@ -241,57 +240,103 @@ quirk_log_msg(struct quirks_context *ctx
 	va_start(args, format);
 	quirk_log_msg_va(ctx, priority, format, args);
 	va_end(args);
-
 }
 
 const char *
 quirk_get_name(enum quirk q)
 {
-	switch(q) {
-	case QUIRK_MODEL_ALPS_SERIAL_TOUCHPAD:		return "ModelALPSSerialTouchpad";
-	case QUIRK_MODEL_APPLE_TOUCHPAD:		return "ModelAppleTouchpad";
-	case QUIRK_MODEL_APPLE_TOUCHPAD_ONEBUTTON:	return "ModelAppleTouchpadOneButton";
-	case QUIRK_MODEL_BOUNCING_KEYS:			return "ModelBouncingKeys";
-	case QUIRK_MODEL_CHROMEBOOK:			return "ModelChromebook";
-	case QUIRK_MODEL_CLEVO_W740SU:			return "ModelClevoW740SU";
-	case QUIRK_MODEL_DELL_CANVAS_TOTEM:		return "ModelDellCanvasTotem";
-	case QUIRK_MODEL_HP_PAVILION_DM4_TOUCHPAD:	return "ModelHPPavilionDM4Touchpad";
-	case QUIRK_MODEL_HP_ZBOOK_STUDIO_G3:		return "ModelHPZBookStudioG3";
-	case QUIRK_MODEL_INVERT_HORIZONTAL_SCROLLING:	return "ModelInvertHorizontalScrolling";
-	case QUIRK_MODEL_LENOVO_SCROLLPOINT:		return "ModelLenovoScrollPoint";
-	case QUIRK_MODEL_LENOVO_T450_TOUCHPAD:		return "ModelLenovoT450Touchpad";
-	case QUIRK_MODEL_LENOVO_X1GEN6_TOUCHPAD:	return "ModelLenovoX1Gen6Touchpad";
-	case QUIRK_MODEL_LENOVO_X230:			return "ModelLenovoX230";
-	case QUIRK_MODEL_SYNAPTICS_SERIAL_TOUCHPAD:	return "ModelSynapticsSerialTouchpad";
-	case QUIRK_MODEL_SYSTEM76_BONOBO:		return "ModelSystem76Bonobo";
-	case QUIRK_MODEL_SYSTEM76_GALAGO:		return "ModelSystem76Galago";
-	case QUIRK_MODEL_SYSTEM76_KUDU:			return "ModelSystem76Kudu";
-	case QUIRK_MODEL_TABLET_MODE_NO_SUSPEND:	return "ModelTabletModeNoSuspend";
-	case QUIRK_MODEL_TABLET_MODE_SWITCH_UNRELIABLE:	return "ModelTabletModeSwitchUnreliable";
-	case QUIRK_MODEL_TOUCHPAD_VISIBLE_MARKER:	return "ModelTouchpadVisibleMarker";
-	case QUIRK_MODEL_TOUCHPAD_PHANTOM_CLICKS:	return "ModelTouchpadPhantomClicks";
-	case QUIRK_MODEL_TRACKBALL:			return "ModelTrackball";
-	case QUIRK_MODEL_WACOM_TOUCHPAD:		return "ModelWacomTouchpad";
-	case QUIRK_MODEL_PRESSURE_PAD:			return "ModelPressurePad";
-
-	case QUIRK_ATTR_SIZE_HINT:			return "AttrSizeHint";
-	case QUIRK_ATTR_TOUCH_SIZE_RANGE:		return "AttrTouchSizeRange";
-	case QUIRK_ATTR_PALM_SIZE_THRESHOLD:		return "AttrPalmSizeThreshold";
-	case QUIRK_ATTR_LID_SWITCH_RELIABILITY:		return "AttrLidSwitchReliability";
-	case QUIRK_ATTR_KEYBOARD_INTEGRATION:		return "AttrKeyboardIntegration";
-	case QUIRK_ATTR_TRACKPOINT_INTEGRATION:		return "AttrPointingStickIntegration";
-	case QUIRK_ATTR_TPKBCOMBO_LAYOUT:		return "AttrTPKComboLayout";
-	case QUIRK_ATTR_PRESSURE_RANGE:			return "AttrPressureRange";
-	case QUIRK_ATTR_PALM_PRESSURE_THRESHOLD:	return "AttrPalmPressureThreshold";
-	case QUIRK_ATTR_RESOLUTION_HINT:		return "AttrResolutionHint";
-	case QUIRK_ATTR_TRACKPOINT_MULTIPLIER:		return "AttrTrackpointMultiplier";
-	case QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD:	return "AttrThumbPressureThreshold";
-	case QUIRK_ATTR_USE_VELOCITY_AVERAGING:		return "AttrUseVelocityAveraging";
-	case QUIRK_ATTR_TABLET_SMOOTHING:               return "AttrTabletSmoothing";
-	case QUIRK_ATTR_THUMB_SIZE_THRESHOLD:		return "AttrThumbSizeThreshold";
-	case QUIRK_ATTR_MSC_TIMESTAMP:			return "AttrMscTimestamp";
-	case QUIRK_ATTR_EVENT_CODE:			return "AttrEventCode";
-	case QUIRK_ATTR_INPUT_PROP:			return "AttrInputProp";
+	switch (q) {
+	case QUIRK_MODEL_ALPS_SERIAL_TOUCHPAD:
+		return "ModelALPSSerialTouchpad";
+	case QUIRK_MODEL_APPLE_TOUCHPAD:
+		return "ModelAppleTouchpad";
+	case QUIRK_MODEL_APPLE_TOUCHPAD_ONEBUTTON:
+		return "ModelAppleTouchpadOneButton";
+	case QUIRK_MODEL_BOUNCING_KEYS:
+		return "ModelBouncingKeys";
+	case QUIRK_MODEL_CHROMEBOOK:
+		return "ModelChromebook";
+	case QUIRK_MODEL_CLEVO_W740SU:
+		return "ModelClevoW740SU";
+	case QUIRK_MODEL_DELL_CANVAS_TOTEM:
+		return "ModelDellCanvasTotem";
+	case QUIRK_MODEL_HP_PAVILION_DM4_TOUCHPAD:
+		return "ModelHPPavilionDM4Touchpad";
+	case QUIRK_MODEL_HP_ZBOOK_STUDIO_G3:
+		return "ModelHPZBookStudioG3";
+	case QUIRK_MODEL_INVERT_HORIZONTAL_SCROLLING:
+		return "ModelInvertHorizontalScrolling";
+	case QUIRK_MODEL_LENOVO_SCROLLPOINT:
+		return "ModelLenovoScrollPoint";
+	case QUIRK_MODEL_LENOVO_T450_TOUCHPAD:
+		return "ModelLenovoT450Touchpad";
+	case QUIRK_MODEL_LENOVO_X1GEN6_TOUCHPAD:
+		return "ModelLenovoX1Gen6Touchpad";
+	case QUIRK_MODEL_LENOVO_X230:
+		return "ModelLenovoX230";
+	case QUIRK_MODEL_SCROLL_ON_MIDDLE_CLICK:
+		return "ModelScrollOnMiddleClick";
+	case QUIRK_MODEL_SYNAPTICS_SERIAL_TOUCHPAD:
+		return "ModelSynapticsSerialTouchpad";
+	case QUIRK_MODEL_SYSTEM76_BONOBO:
+		return "ModelSystem76Bonobo";
+	case QUIRK_MODEL_SYSTEM76_GALAGO:
+		return "ModelSystem76Galago";
+	case QUIRK_MODEL_SYSTEM76_KUDU:
+		return "ModelSystem76Kudu";
+	case QUIRK_MODEL_TABLET_MODE_NO_SUSPEND:
+		return "ModelTabletModeNoSuspend";
+	case QUIRK_MODEL_TABLET_MODE_SWITCH_UNRELIABLE:
+		return "ModelTabletModeSwitchUnreliable";
+	case QUIRK_MODEL_TOUCHPAD_VISIBLE_MARKER:
+		return "ModelTouchpadVisibleMarker";
+	case QUIRK_MODEL_TOUCHPAD_PHANTOM_CLICKS:
+		return "ModelTouchpadPhantomClicks";
+	case QUIRK_MODEL_TRACKBALL:
+		return "ModelTrackball";
+	case QUIRK_MODEL_WACOM_TOUCHPAD:
+		return "ModelWacomTouchpad";
+	case QUIRK_MODEL_PRESSURE_PAD:
+		return "ModelPressurePad";
+
+	case QUIRK_ATTR_SIZE_HINT:
+		return "AttrSizeHint";
+	case QUIRK_ATTR_TOUCH_SIZE_RANGE:
+		return "AttrTouchSizeRange";
+	case QUIRK_ATTR_PALM_SIZE_THRESHOLD:
+		return "AttrPalmSizeThreshold";
+	case QUIRK_ATTR_LID_SWITCH_RELIABILITY:
+		return "AttrLidSwitchReliability";
+	case QUIRK_ATTR_KEYBOARD_INTEGRATION:
+		return "AttrKeyboardIntegration";
+	case QUIRK_ATTR_TRACKPOINT_INTEGRATION:
+		return "AttrPointingStickIntegration";
+	case QUIRK_ATTR_TPKBCOMBO_LAYOUT:
+		return "AttrTPKComboLayout";
+	case QUIRK_ATTR_PRESSURE_RANGE:
+		return "AttrPressureRange";
+	case QUIRK_ATTR_PALM_PRESSURE_THRESHOLD:
+		return "AttrPalmPressureThreshold";
+	case QUIRK_ATTR_RESOLUTION_HINT:
+		return "AttrResolutionHint";
+	case QUIRK_ATTR_TRACKPOINT_MULTIPLIER:
+		return "AttrTrackpointMultiplier";
+	case QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD:
+		return "AttrThumbPressureThreshold";
+	case QUIRK_ATTR_USE_VELOCITY_AVERAGING:
+		return "AttrUseVelocityAveraging";
+	case QUIRK_ATTR_TABLET_SMOOTHING:
+		return "AttrTabletSmoothing";
+	case QUIRK_ATTR_THUMB_SIZE_THRESHOLD:
+		return "AttrThumbSizeThreshold";
+	case QUIRK_ATTR_MSC_TIMESTAMP:
+		return "AttrMscTimestamp";
+	case QUIRK_ATTR_EVENT_CODE:
+		return "AttrEventCode";
+	case QUIRK_ATTR_INPUT_PROP:
+		return "AttrInputProp";
+	case QUIRK_ATTR_IS_VIRTUAL:
+		return "AttrIsVirtual";
 	default:
 		abort();
 	}
@@ -300,16 +345,34 @@ quirk_get_name(enum quirk q)
 static inline const char *
 matchflagname(enum match_flags f)
 {
-	switch(f) {
-	case M_NAME:		return "MatchName";		break;
-	case M_BUS:		return "MatchBus";		break;
-	case M_VID:		return "MatchVendor";		break;
-	case M_PID:		return "MatchProduct";		break;
-	case M_VERSION:		return "MatchVersion";		break;
-	case M_DMI:		return "MatchDMIModalias";	break;
-	case M_UDEV_TYPE:	return "MatchUdevType";		break;
-	case M_DT:		return "MatchDeviceTree";	break;
-	case M_UNIQ:		return "MatchUniq";		break;
+	switch (f) {
+	case M_NAME:
+		return "MatchName";
+		break;
+	case M_BUS:
+		return "MatchBus";
+		break;
+	case M_VID:
+		return "MatchVendor";
+		break;
+	case M_PID:
+		return "MatchProduct";
+		break;
+	case M_VERSION:
+		return "MatchVersion";
+		break;
+	case M_DMI:
+		return "MatchDMIModalias";
+		break;
+	case M_UDEV_TYPE:
+		return "MatchUdevType";
+		break;
+	case M_DT:
+		return "MatchDeviceTree";
+		break;
+	case M_UNIQ:
+		return "MatchUniq";
+		break;
 	default:
 		abort();
 	}
@@ -369,20 +432,16 @@ property_cleanup(struct property *p)
 static inline char *
 init_dmi_linux(void)
 {
-	struct udev *udev;
-	struct udev_device *udev_device;
 	const char *modalias = NULL;
-	char *copy = NULL;
 	const char *syspath = "/sys/devices/virtual/dmi/id";
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	if (!udev)
 		return NULL;
 
-	udev_device = udev_device_new_from_syspath(udev, syspath);
+	_unref_(udev_device) *udev_device = udev_device_new_from_syspath(udev, syspath);
 	if (udev_device)
-		modalias = udev_device_get_property_value(udev_device,
-							  "MODALIAS");
+		modalias = udev_device_get_property_value(udev_device, "MODALIAS");
 
 	/* Not sure whether this could ever really fail, if so we should
 	 * open the sysfs file directly. But then udev wouldn't have failed,
@@ -390,12 +449,7 @@ init_dmi_linux(void)
 	if (!modalias)
 		modalias = "dmi:*";
 
-	copy = safe_strdup(modalias);
-
-	udev_device_unref(udev_device);
-	udev_unref(udev);
-
-	return copy;
+	return safe_strdup(modalias);
 }
 #endif
 
@@ -440,11 +494,21 @@ init_dmi_freebsd(void)
 	else if (strcmp(chassis_type, "Detachable") == 0)
 		chassis_type_num = 0x20;
 
-	xasprintf(&modalias,
+	xasprintf(
+		&modalias,
 		"dmi:bvn%s:bvr%s:bd%s:svn%s:pn%s:pvr%s:rvn%s:rn%s:rvr%s:cvn%s:ct%d:cvr%s:",
-		bios_vendor, bios_version, bios_date, sys_vendor, product_name,
-		product_version, board_vendor, board_name, board_version, chassis_vendor,
-		chassis_type_num, chassis_version);
+		bios_vendor,
+		bios_version,
+		bios_date,
+		sys_vendor,
+		product_name,
+		product_version,
+		board_vendor,
+		board_name,
+		board_version,
+		chassis_vendor,
+		chassis_type_num,
+		chassis_version);
 
 	return modalias;
 }
@@ -531,10 +595,8 @@ section_destroy(struct section *s)
 static inline bool
 parse_hex(const char *value, unsigned int *parsed)
 {
-	return strstartswith(value, "0x") &&
-	       safe_atou_base(value, parsed, 16) &&
-	       strspn(value, "0123456789xABCDEF") == strlen(value) &&
-	       *parsed <= 0xFFFF;
+	return strstartswith(value, "0x") && safe_atou_base(value, parsed, 16) &&
+	       strspn(value, "0123456789xABCDEF") == strlen(value) && *parsed <= 0xFFFF;
 }
 
 static int
@@ -600,12 +662,15 @@ parse_match(struct quirks_context *ctx,
 
 		s->match.vendor = vendor;
 	} else if (streq(key, "MatchProduct")) {
-		unsigned int product[ARRAY_LENGTH(s->match.product)] = {0};
+		unsigned int product[ARRAY_LENGTH(s->match.product)] = { 0 };
 		const size_t max = ARRAY_LENGTH(s->match.product) - 1;
 
 		size_t nelems = 0;
 		char **strs = strv_from_string(value, ";", &nelems);
-		int rc = strv_for_each_n((const char**)strs, max, strv_parse_hex, product);
+		int rc = strv_for_each_n((const char **)strs,
+					 max,
+					 strv_parse_hex,
+					 product);
 		strv_free(strs);
 		if (rc != 0)
 			goto out;
@@ -688,8 +753,7 @@ parse_model(struct quirks_context *ctx,
 	do {
 		if (streq(key, quirk_get_name(q))) {
 			struct property *p = property_new();
-			p->id = q,
-			p->type = PT_BOOL;
+			p->id = q, p->type = PT_BOOL;
 			p->value.b = b;
 			list_append(&s->properties, &p->link);
 			s->has_property = true;
@@ -750,8 +814,7 @@ parse_attr(struct quirks_context *ctx,
 		rc = true;
 	} else if (streq(key, quirk_get_name(QUIRK_ATTR_LID_SWITCH_RELIABILITY))) {
 		p->id = QUIRK_ATTR_LID_SWITCH_RELIABILITY;
-		if (!streq(value, "reliable") &&
-		    !streq(value, "write_open") &&
+		if (!streq(value, "reliable") && !streq(value, "write_open") &&
 		    !streq(value, "unreliable"))
 			goto out;
 		p->type = PT_STRING;
@@ -847,8 +910,7 @@ parse_attr(struct quirks_context *ctx,
 
 		p->id = QUIRK_ATTR_EVENT_CODE;
 
-		if (!parse_evcode_property(value, events, &nevents) ||
-		    nevents == 0)
+		if (!parse_evcode_property(value, events, &nevents) || nevents == 0)
 			goto out;
 
 		for (size_t i = 0; i < nevents; i++) {
@@ -866,8 +928,7 @@ parse_attr(struct quirks_context *ctx,
 
 		p->id = QUIRK_ATTR_INPUT_PROP;
 
-		if (!parse_input_prop_property(value, props, &nprops) ||
-		    nprops == 0)
+		if (!parse_input_prop_property(value, props, &nprops) || nprops == 0)
 			goto out;
 
 		for (size_t i = 0; i < nprops; i++) {
@@ -879,6 +940,13 @@ parse_attr(struct quirks_context *ctx,
 		p->type = PT_TUPLES;
 
 		rc = true;
+	} else if (streq(key, quirk_get_name(QUIRK_ATTR_IS_VIRTUAL))) {
+		p->id = QUIRK_ATTR_IS_VIRTUAL;
+		if (!parse_boolean_property(value, &b))
+			goto out;
+		p->type = PT_BOOL;
+		p->value.b = b;
+		rc = true;
 	} else {
 		qlog_error(ctx, "Unknown key %s in %s\n", key, s->name);
 	}
@@ -997,7 +1065,9 @@ parse_file(struct quirks_context *ctx, c
 		case '\t':
 			qlog_parser(ctx,
 				    "%s:%d: Trailing whitespace '%s'\n",
-				    path, lineno, line);
+				    path,
+				    lineno,
+				    line);
 			goto out;
 		}
 
@@ -1009,27 +1079,38 @@ parse_file(struct quirks_context *ctx, c
 		/* white space not allowed */
 		case ' ':
 		case '\t':
-			qlog_parser(ctx, "%s:%d: Preceding whitespace '%s'\n",
-					 path, lineno, line);
+			qlog_parser(ctx,
+				    "%s:%d: Preceding whitespace '%s'\n",
+				    path,
+				    lineno,
+				    line);
 			goto out;
 		/* section title */
 		case '[':
 			if (line[strlen(line) - 1] != ']') {
-				qlog_parser(ctx, "%s:%d: Closing ] missing '%s'\n",
-					    path, lineno, line);
+				qlog_parser(ctx,
+					    "%s:%d: Closing ] missing '%s'\n",
+					    path,
+					    lineno,
+					    line);
 				goto out;
 			}
 
-			if (state != STATE_SECTION &&
-			    state != STATE_VALUE_OR_SECTION) {
-				qlog_parser(ctx, "%s:%d: expected section before %s\n",
-					  path, lineno, line);
+			if (state != STATE_SECTION && state != STATE_VALUE_OR_SECTION) {
+				qlog_parser(ctx,
+					    "%s:%d: expected section before %s\n",
+					    path,
+					    lineno,
+					    line);
 				goto out;
 			}
 			if (section &&
 			    (!section->has_match || !section->has_property)) {
-				qlog_parser(ctx, "%s:%d: previous section %s was empty\n",
-					  path, lineno, section->name);
+				qlog_parser(ctx,
+					    "%s:%d: previous section %s was empty\n",
+					    path,
+					    lineno,
+					    section->name);
 				goto out; /* Previous section was empty */
 			}
 
@@ -1040,19 +1121,29 @@ parse_file(struct quirks_context *ctx, c
 		default:
 			/* entries must start with A-Z */
 			if (line[0] < 'A' || line[0] > 'Z') {
-				qlog_parser(ctx, "%s:%d: Unexpected line %s\n",
-						 path, lineno, line);
+				qlog_parser(ctx,
+					    "%s:%d: Unexpected line %s\n",
+					    path,
+					    lineno,
+					    line);
 				goto out;
 			}
 			switch (state) {
 			case STATE_SECTION:
-				qlog_parser(ctx, "%s:%d: expected [Section], got %s\n",
-					  path, lineno, line);
+				qlog_parser(ctx,
+					    "%s:%d: expected [Section], got %s\n",
+					    path,
+					    lineno,
+					    line);
 				goto out;
 			case STATE_MATCH:
 				if (!strstartswith(line, "Match")) {
-					qlog_parser(ctx, "%s:%d: expected MatchFoo=bar, have %s\n",
-							 path, lineno, line);
+					qlog_parser(
+						ctx,
+						"%s:%d: expected MatchFoo=bar, have %s\n",
+						path,
+						lineno,
+						line);
 					goto out;
 				}
 				state = STATE_MATCH_OR_VALUE;
@@ -1063,8 +1154,12 @@ parse_file(struct quirks_context *ctx, c
 				break;
 			case STATE_VALUE_OR_SECTION:
 				if (strstartswith(line, "Match")) {
-					qlog_parser(ctx, "%s:%d: expected value or [Section], have %s\n",
-							 path, lineno, line);
+					qlog_parser(
+						ctx,
+						"%s:%d: expected value or [Section], have %s\n",
+						path,
+						lineno,
+						line);
 					goto out;
 				}
 				break;
@@ -1073,8 +1168,11 @@ parse_file(struct quirks_context *ctx, c
 			}
 
 			if (!parse_value_line(ctx, section, line)) {
-				qlog_parser(ctx, "%s:%d: failed to parse %s\n",
-						 path, lineno, line);
+				qlog_parser(ctx,
+					    "%s:%d: failed to parse %s\n",
+					    path,
+					    lineno,
+					    line);
 				goto out;
 			}
 			break;
@@ -1086,9 +1184,12 @@ parse_file(struct quirks_context *ctx, c
 		goto out;
 	}
 
-	if ((!section->has_match || !section->has_property)) {
-		qlog_parser(ctx, "%s:%d: previous section %s was empty\n",
-				 path, lineno, section->name);
+	if (!section->has_match || !section->has_property) {
+		qlog_parser(ctx,
+			    "%s:%d: previous section %s was empty\n",
+			    path,
+			    lineno,
+			    section->name);
 		goto out; /* Previous section was empty */
 	}
 
@@ -1101,12 +1202,15 @@ out:
 }
 
 static int
-is_data_file(const struct dirent *dir) {
+is_data_file(const struct dirent *dir)
+{
 	return strendswith(dir->d_name, ".quirks");
 }
 
 static inline bool
-parse_files(struct quirks_context *ctx, const char *data_path)
+parse_files(struct quirks_context *ctx,
+	    const char *data_path,
+	    bool allow_empty_directory)
 {
 	struct dirent **namelist;
 	int ndev = -1;
@@ -1114,20 +1218,17 @@ parse_files(struct quirks_context *ctx,
 
 	ndev = scandir(data_path, &namelist, is_data_file, versionsort);
 	if (ndev <= 0) {
-		qlog_error(ctx,
-			   "%s: failed to find data files\n",
-			   data_path);
+		if (allow_empty_directory)
+			return true;
+
+		qlog_error(ctx, "%s: failed to find data files\n", data_path);
 		return false;
 	}
 
 	for (idx = 0; idx < ndev; idx++) {
 		char path[PATH_MAX];
 
-		snprintf(path,
-			 sizeof(path),
-			 "%s/%s",
-			 data_path,
-			 namelist[idx]->d_name);
+		snprintf(path, sizeof(path), "%s/%s", data_path, namelist[idx]->d_name);
 
 		if (!parse_file(ctx, path))
 			break;
@@ -1147,7 +1248,7 @@ quirks_init_subsystem(const char *data_p
 		      struct libinput *libinput,
 		      enum quirks_log_type log_type)
 {
-	struct quirks_context *ctx = zalloc(sizeof *ctx);
+	_unref_(quirks_context) *ctx = zalloc(sizeof *ctx);
 
 	assert(data_path);
 
@@ -1163,19 +1264,24 @@ quirks_init_subsystem(const char *data_p
 	ctx->dmi = init_dmi();
 	ctx->dt = init_dt();
 	if (!ctx->dmi && !ctx->dt)
-		goto error;
+		return NULL;
 
-	if (!parse_files(ctx, data_path))
-		goto error;
+	if (!parse_files(ctx, data_path, false))
+		return NULL;
 
 	if (override_file && !parse_file(ctx, override_file))
-		goto error;
+		return NULL;
 
-	return ctx;
+	_autofree_ char *xdg_runtime_dir = safe_strdup(getenv("XDG_RUNTIME_DIR"));
+	if (!xdg_runtime_dir)
+		xdg_runtime_dir = strdup_printf("/run/user/%d", geteuid());
+
+	_autofree_ char *xdg_runtime_quirks_dir =
+		strdup_printf("%s/libinput/", xdg_runtime_dir);
+	if (!parse_files(ctx, xdg_runtime_quirks_dir, true))
+		return NULL;
 
-error:
-	quirks_context_unref(ctx);
-	return NULL;
+	return steal(&ctx);
 }
 
 struct quirks_context *
@@ -1277,8 +1383,7 @@ udev_prop(struct udev_device *device, co
 }
 
 static inline void
-match_fill_name(struct match *m,
-		struct udev_device *device)
+match_fill_name(struct match *m, struct udev_device *device)
 {
 	const char *str = udev_prop(device, "NAME");
 	size_t slen;
@@ -1292,16 +1397,14 @@ match_fill_name(struct match *m,
 
 	m->name = safe_strdup(str);
 	slen = strlen(m->name);
-	if (slen > 1 &&
-	    m->name[slen - 1] == '"')
+	if (slen > 1 && m->name[slen - 1] == '"')
 		m->name[slen - 1] = '\0';
 
 	m->bits |= M_NAME;
 }
 
 static inline void
-match_fill_uniq(struct match *m,
-		struct udev_device *device)
+match_fill_uniq(struct match *m, struct udev_device *device)
 {
 	const char *str = udev_prop(device, "UNIQ");
 	size_t slen;
@@ -1315,16 +1418,14 @@ match_fill_uniq(struct match *m,
 
 	m->uniq = safe_strdup(str);
 	slen = safe_strlen(m->uniq);
-	if (slen > 1 &&
-	    m->uniq[slen - 1] == '"')
+	if (slen > 1 && m->uniq[slen - 1] == '"')
 		m->uniq[slen - 1] = '\0';
 
 	m->bits |= M_UNIQ;
 }
 
 static inline void
-match_fill_bus_vid_pid(struct match *m,
-		       struct udev_device *device)
+match_fill_bus_vid_pid(struct match *m, struct udev_device *device)
 {
 	const char *str;
 	unsigned int product, vendor, bus, version;
@@ -1342,7 +1443,7 @@ match_fill_bus_vid_pid(struct match *m,
 	m->product[1] = 0;
 	m->vendor = vendor;
 	m->version = version;
-	m->bits |= M_PID|M_VID|M_VERSION;
+	m->bits |= M_PID | M_VID | M_VERSION;
 	switch (bus) {
 	case BUS_USB:
 		m->bus = BT_USB;
@@ -1374,8 +1475,7 @@ match_fill_bus_vid_pid(struct match *m,
 }
 
 static inline void
-match_fill_udev_type(struct match *m,
-		     struct udev_device *device)
+match_fill_udev_type(struct match *m, struct udev_device *device)
 {
 	struct ut_map {
 		const char *prop;
@@ -1413,8 +1513,7 @@ match_fill_dmi_dt(struct match *m, char
 }
 
 static struct match *
-match_new(struct udev_device *device,
-	  char *dmi, char *dt)
+match_new(struct udev_device *device, char *dmi, char *dt)
 {
 	struct match *m = zalloc(sizeof *m);
 
@@ -1435,6 +1534,8 @@ match_free(struct match *m)
 	free(m);
 }
 
+DEFINE_FREE_CLEANUP_FUNC(match);
+
 static void
 quirk_merge_event_codes(struct quirks_context *ctx,
 			struct quirks *q,
@@ -1452,7 +1553,8 @@ quirk_merge_event_codes(struct quirks_co
 		for (size_t j = 0; j < property->value.tuples.ntuples; j++) {
 			if (offset + j >= max)
 				break;
-			p->value.tuples.tuples[offset + j] = property->value.tuples.tuples[j];
+			p->value.tuples.tuples[offset + j] =
+				property->value.tuples.tuples[j];
 			p->value.tuples.ntuples++;
 		}
 		return;
@@ -1466,7 +1568,9 @@ quirk_merge_event_codes(struct quirks_co
 	newprop->type = property->type;
 	newprop->value.tuples = property->value.tuples;
 	/* Caller responsible for pre-allocating space */
-	q->properties[q->nproperties++] = property_ref(newprop);
+	q->properties[q->nproperties++] = /* NOLINT(clang-analyzer-security.ArrayBound)
+					   */
+		property_ref(newprop);
 	list_append(&q->floating_properties, &newprop->link);
 }
 
@@ -1490,8 +1594,10 @@ quirk_apply_section(struct quirks_contex
 
 	q->properties = tmp;
 	list_for_each(p, &s->properties, link) {
-		qlog_debug(ctx, "property added: %s from %s\n",
-			   quirk_get_name(p->id), s->name);
+		qlog_debug(ctx,
+			   "property added: %s from %s\n",
+			   quirk_get_name(p->id),
+			   s->name);
 
 		/* All quirks but AttrEventCode and AttrInputProp
 		 * simply overwrite each other, so we can just append the
@@ -1508,8 +1614,7 @@ quirk_apply_section(struct quirks_contex
 		 * have one struct property in the list (not owned by a section)
 		 * and we simply merge any extra sections onto that.
 		 */
-		if (p->id == QUIRK_ATTR_EVENT_CODE ||
-		    p->id == QUIRK_ATTR_INPUT_PROP)
+		if (p->id == QUIRK_ATTR_EVENT_CODE || p->id == QUIRK_ATTR_INPUT_PROP)
 			quirk_merge_event_codes(ctx, q, p);
 		else
 			q->properties[q->nproperties++] = property_ref(p);
@@ -1536,7 +1641,8 @@ quirk_match_section(struct quirks_contex
 		if ((m->bits & flag) == 0) {
 			qlog_debug(ctx,
 				   "%s wants %s but we don't have that\n",
-				   s->name, matchflagname(flag));
+				   s->name,
+				   matchflagname(flag));
 			continue;
 		}
 
@@ -1610,37 +1716,28 @@ quirk_match_section(struct quirks_contex
 }
 
 struct quirks *
-quirks_fetch_for_device(struct quirks_context *ctx,
-			struct udev_device *udev_device)
+quirks_fetch_for_device(struct quirks_context *ctx, struct udev_device *udev_device)
 {
-	struct quirks *q = NULL;
-	struct section *s;
-	struct match *m;
-
 	if (!ctx)
 		return NULL;
 
-	qlog_debug(ctx, "%s: fetching quirks\n",
-		   udev_device_get_devnode(udev_device));
-
-	q = quirks_new();
+	qlog_debug(ctx, "%s: fetching quirks\n", udev_device_get_devnode(udev_device));
 
-	m = match_new(udev_device, ctx->dmi, ctx->dt);
+	_unref_(quirks) *q = quirks_new();
+	_free_(match) *m = match_new(udev_device, ctx->dmi, ctx->dt);
 
+	struct section *s;
 	list_for_each(s, &ctx->sections, link) {
 		quirk_match_section(ctx, q, s, m, udev_device);
 	}
 
-	match_free(m);
-
 	if (q->nproperties == 0) {
-		quirks_unref(q);
 		return NULL;
 	}
 
 	list_insert(&ctx->quirks, &q->link);
 
-	return q;
+	return steal(&q);
 }
 
 static inline struct property *
@@ -1753,9 +1850,7 @@ quirks_get_bool(struct quirks *q, enum q
 }
 
 bool
-quirks_get_dimensions(struct quirks *q,
-		      enum quirk which,
-		      struct quirk_dimensions *val)
+quirks_get_dimensions(struct quirks *q, enum quirk which, struct quirk_dimensions *val)
 {
 	struct property *p;
 
@@ -1773,9 +1868,7 @@ quirks_get_dimensions(struct quirks *q,
 }
 
 bool
-quirks_get_range(struct quirks *q,
-		 enum quirk which,
-		 struct quirk_range *val)
+quirks_get_range(struct quirks *q, enum quirk which, struct quirk_range *val)
 {
 	struct property *p;
 
diff -pruN 1.28.1-1/src/quirks.h 1.30.0-1/src/quirks.h
--- 1.28.1-1/src/quirks.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/quirks.h	2025-11-25 03:40:43.000000000 +0000
@@ -25,10 +25,11 @@
 
 #include "config.h"
 
+#include <libudev.h>
 #include <stdbool.h>
 #include <stdint.h>
 
-#include <libudev.h>
+#include "util-mem.h"
 
 #include "libinput.h"
 
@@ -42,6 +43,9 @@ struct quirks_context;
  */
 struct quirks;
 
+struct quirks *
+libinput_device_get_quirks(struct libinput_device *device);
+
 struct quirk_dimensions {
 	size_t x, y;
 };
@@ -63,6 +67,8 @@ struct quirk_tuples {
  * Quirks known to libinput
  */
 enum quirk {
+	QUIRK_NONE = 0,
+
 	QUIRK_MODEL_ALPS_SERIAL_TOUCHPAD = 100,
 	QUIRK_MODEL_APPLE_TOUCHPAD,
 	QUIRK_MODEL_APPLE_TOUCHPAD_ONEBUTTON,
@@ -77,38 +83,40 @@ enum quirk {
 	QUIRK_MODEL_LENOVO_T450_TOUCHPAD,
 	QUIRK_MODEL_LENOVO_X1GEN6_TOUCHPAD,
 	QUIRK_MODEL_LENOVO_X230,
+	QUIRK_MODEL_PRESSURE_PAD,
+	QUIRK_MODEL_SCROLL_ON_MIDDLE_CLICK,
 	QUIRK_MODEL_SYNAPTICS_SERIAL_TOUCHPAD,
 	QUIRK_MODEL_SYSTEM76_BONOBO,
 	QUIRK_MODEL_SYSTEM76_GALAGO,
 	QUIRK_MODEL_SYSTEM76_KUDU,
 	QUIRK_MODEL_TABLET_MODE_NO_SUSPEND,
 	QUIRK_MODEL_TABLET_MODE_SWITCH_UNRELIABLE,
+	QUIRK_MODEL_TOUCHPAD_PHANTOM_CLICKS,
 	QUIRK_MODEL_TOUCHPAD_VISIBLE_MARKER,
 	QUIRK_MODEL_TRACKBALL,
 	QUIRK_MODEL_WACOM_TOUCHPAD,
-	QUIRK_MODEL_PRESSURE_PAD,
-	QUIRK_MODEL_TOUCHPAD_PHANTOM_CLICKS,
 
 	_QUIRK_LAST_MODEL_QUIRK_, /* Guard: do not modify */
 
 	QUIRK_ATTR_SIZE_HINT = 300,
-	QUIRK_ATTR_TOUCH_SIZE_RANGE,
-	QUIRK_ATTR_PALM_SIZE_THRESHOLD,
-	QUIRK_ATTR_LID_SWITCH_RELIABILITY,
+	QUIRK_ATTR_EVENT_CODE,
+	QUIRK_ATTR_INPUT_PROP,
+	QUIRK_ATTR_IS_VIRTUAL,
 	QUIRK_ATTR_KEYBOARD_INTEGRATION,
-	QUIRK_ATTR_TRACKPOINT_INTEGRATION,
-	QUIRK_ATTR_TPKBCOMBO_LAYOUT,
-	QUIRK_ATTR_PRESSURE_RANGE,
+	QUIRK_ATTR_LID_SWITCH_RELIABILITY,
+	QUIRK_ATTR_MSC_TIMESTAMP,
 	QUIRK_ATTR_PALM_PRESSURE_THRESHOLD,
+	QUIRK_ATTR_PALM_SIZE_THRESHOLD,
+	QUIRK_ATTR_PRESSURE_RANGE,
 	QUIRK_ATTR_RESOLUTION_HINT,
-	QUIRK_ATTR_TRACKPOINT_MULTIPLIER,
-	QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD,
-	QUIRK_ATTR_USE_VELOCITY_AVERAGING,
 	QUIRK_ATTR_TABLET_SMOOTHING,
+	QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD,
 	QUIRK_ATTR_THUMB_SIZE_THRESHOLD,
-	QUIRK_ATTR_MSC_TIMESTAMP,
-	QUIRK_ATTR_EVENT_CODE,
-	QUIRK_ATTR_INPUT_PROP,
+	QUIRK_ATTR_TOUCH_SIZE_RANGE,
+	QUIRK_ATTR_TPKBCOMBO_LAYOUT,
+	QUIRK_ATTR_TRACKPOINT_INTEGRATION,
+	QUIRK_ATTR_TRACKPOINT_MULTIPLIER,
+	QUIRK_ATTR_USE_VELOCITY_AVERAGING,
 
 	_QUIRK_LAST_ATTR_QUIRK_, /* Guard: do not modify */
 };
@@ -117,7 +125,7 @@ enum quirk {
  * Returns a printable name for the quirk. This name is for developer
  * tools, not user consumption. Do not display this in a GUI.
  */
-const char*
+const char *
 quirk_get_name(enum quirk q);
 
 /**
@@ -178,6 +186,8 @@ quirks_init_subsystem(const char *data_p
 struct quirks_context *
 quirks_context_unref(struct quirks_context *ctx);
 
+DEFINE_UNREF_CLEANUP_FUNC(quirks_context);
+
 struct quirks_context *
 quirks_context_ref(struct quirks_context *ctx);
 
@@ -188,8 +198,7 @@ quirks_context_ref(struct quirks_context
  * @return A new quirks struct, use quirks_unref() to release
  */
 struct quirks *
-quirks_fetch_for_device(struct quirks_context *ctx,
-			struct udev_device *device);
+quirks_fetch_for_device(struct quirks_context *ctx, struct udev_device *device);
 
 /**
  * Reduce the refcount by one. When the refcount reaches zero, the
@@ -200,6 +209,8 @@ quirks_fetch_for_device(struct quirks_co
 struct quirks *
 quirks_unref(struct quirks *q);
 
+DEFINE_UNREF_CLEANUP_FUNC(quirks);
+
 /**
  * Returns true if the given quirk applies is in this quirk list.
  */
@@ -215,9 +226,7 @@ quirks_has_quirk(struct quirks *q, enum
  * @return true if the quirk value is valid, false otherwise.
  */
 bool
-quirks_get_uint32(struct quirks *q,
-		  enum quirk which,
-		  uint32_t *val);
+quirks_get_uint32(struct quirks *q, enum quirk which, uint32_t *val);
 
 /**
  * Get the value of the given quirk, as signed integer.
@@ -228,9 +237,7 @@ quirks_get_uint32(struct quirks *q,
  * @return true if the quirk value is valid, false otherwise.
  */
 bool
-quirks_get_int32(struct quirks *q,
-		 enum quirk which,
-		 int32_t *val);
+quirks_get_int32(struct quirks *q, enum quirk which, int32_t *val);
 
 /**
  * Get the value of the given quirk, as double.
@@ -241,9 +248,7 @@ quirks_get_int32(struct quirks *q,
  * @return true if the quirk value is valid, false otherwise.
  */
 bool
-quirks_get_double(struct quirks *q,
-		  enum quirk which,
-		  double *val);
+quirks_get_double(struct quirks *q, enum quirk which, double *val);
 
 /**
  * Get the value of the given quirk, as string.
@@ -257,9 +262,7 @@ quirks_get_double(struct quirks *q,
  * @return true if the quirk value is valid, false otherwise.
  */
 bool
-quirks_get_string(struct quirks *q,
-		  enum quirk which,
-		  char **val);
+quirks_get_string(struct quirks *q, enum quirk which, char **val);
 
 /**
  * Get the value of the given quirk, as bool.
@@ -270,9 +273,7 @@ quirks_get_string(struct quirks *q,
  * @return true if the quirk value is valid, false otherwise.
  */
 bool
-quirks_get_bool(struct quirks *q,
-		enum quirk which,
-		bool *val);
+quirks_get_bool(struct quirks *q, enum quirk which, bool *val);
 
 /**
  * Get the value of the given quirk, as dimension.
@@ -283,9 +284,7 @@ quirks_get_bool(struct quirks *q,
  * @return true if the quirk value is valid, false otherwise.
  */
 bool
-quirks_get_dimensions(struct quirks *q,
-		      enum quirk which,
-		      struct quirk_dimensions *val);
+quirks_get_dimensions(struct quirks *q, enum quirk which, struct quirk_dimensions *val);
 
 /**
  * Get the value of the given quirk, as range.
@@ -296,9 +295,7 @@ quirks_get_dimensions(struct quirks *q,
  * @return true if the quirk value is valid, false otherwise.
  */
 bool
-quirks_get_range(struct quirks *q,
-		 enum quirk which,
-		 struct quirk_range *val);
+quirks_get_range(struct quirks *q, enum quirk which, struct quirk_range *val);
 
 /**
  * Get the tuples of the given quirk.
diff -pruN 1.28.1-1/src/timer.c 1.30.0-1/src/timer.c
--- 1.28.1-1/src/timer.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/timer.c	2025-11-25 03:40:43.000000000 +0000
@@ -44,8 +44,7 @@ libinput_timer_init(struct libinput_time
 	timer->timer_func = timer_func;
 	timer->timer_func_data = timer_func_data;
 	/* at most 5 "expiry in the past" log messages per hour */
-	ratelimit_init(&libinput->timer.expiry_in_past_limit,
-		       s2us(60 * 60), 5);
+	ratelimit_init(&libinput->timer.expiry_in_past_limit, s2us(60 * 60), 5);
 }
 
 void
@@ -81,15 +80,15 @@ libinput_timer_arm_timer_fd(struct libin
 
 	r = timerfd_settime(libinput->timer.fd, TFD_TIMER_ABSTIME, &its, NULL);
 	if (r)
-		log_error(libinput, "timer: timerfd_settime error: %s\n", strerror(errno));
+		log_error(libinput,
+			  "timer: timerfd_settime error: %s\n",
+			  strerror(errno));
 
 	libinput->timer.next_expiry = earliest_expire;
 }
 
 void
-libinput_timer_set_flags(struct libinput_timer *timer,
-			 uint64_t expire,
-			 uint32_t flags)
+libinput_timer_set_flags(struct libinput_timer *timer, uint64_t expire, uint32_t flags)
 {
 #ifndef NDEBUG
 	/* We only warn if we're more than 20ms behind */
@@ -98,16 +97,18 @@ libinput_timer_set_flags(struct libinput
 	if (expire < now) {
 		if ((flags & TIMER_FLAG_ALLOW_NEGATIVE) == 0 &&
 		    now - expire > timer_warning_limit)
-			log_bug_client_ratelimit(timer->libinput,
-						 &timer->libinput->timer.expiry_in_past_limit,
-						 "timer %s: scheduled expiry is in the past (-%dms), your system is too slow\n",
-						 timer->timer_name,
-						 us2ms(now - expire));
+			log_bug_client_ratelimit(
+				timer->libinput,
+				&timer->libinput->timer.expiry_in_past_limit,
+				"timer %s: scheduled expiry is in the past (-%dms), your system is too slow\n",
+				timer->timer_name,
+				us2ms(now - expire));
 	} else if ((expire - now) > ms2us(5000)) {
 		log_bug_libinput(timer->libinput,
-			 "timer %s: offset more than 5s, now %d expire %d\n",
-			 timer->timer_name,
-			 us2ms(now), us2ms(expire));
+				 "timer %s: offset more than 5s, now %d expire %d\n",
+				 timer->timer_name,
+				 us2ms(now),
+				 us2ms(expire));
 	}
 #endif
 
@@ -138,7 +139,7 @@ libinput_timer_cancel(struct libinput_ti
 }
 
 static void
-libinput_timer_handler(struct libinput *libinput , uint64_t now)
+libinput_timer_handler(struct libinput *libinput, uint64_t now)
 {
 	struct libinput_timer *timer;
 
@@ -191,8 +192,8 @@ libinput_timer_dispatch(void *data)
 int
 libinput_timer_subsys_init(struct libinput *libinput)
 {
-	libinput->timer.fd = timerfd_create(CLOCK_MONOTONIC,
-					    TFD_CLOEXEC | TFD_NONBLOCK);
+	libinput->timer.fd =
+		timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
 	if (libinput->timer.fd < 0)
 		return -1;
 
@@ -244,9 +245,22 @@ libinput_timer_subsys_destroy(struct lib
 void
 libinput_timer_flush(struct libinput *libinput, uint64_t now)
 {
-	if (libinput->timer.next_expiry == 0 ||
-	    libinput->timer.next_expiry > now)
+	if (libinput->timer.next_expiry == 0 || libinput->timer.next_expiry > now)
 		return;
 
 	libinput_timer_handler(libinput, now);
 }
+
+uint64_t
+libinput_now(struct libinput *libinput)
+{
+	uint64_t now;
+	int rc = now_in_us(&now);
+
+	if (rc < 0) {
+		log_error(libinput, "clock_gettime failed: %s\n", strerror(-rc));
+		return 0;
+	}
+
+	return now;
+}
diff -pruN 1.28.1-1/src/timer.h 1.30.0-1/src/timer.h
--- 1.28.1-1/src/timer.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/timer.h	2025-11-25 03:40:43.000000000 +0000
@@ -24,6 +24,8 @@
 #ifndef TIMER_H
 #define TIMER_H
 
+#include "config.h"
+
 #include <stdint.h>
 
 #include "libinput-util.h"
@@ -40,7 +42,8 @@ struct libinput_timer {
 };
 
 void
-libinput_timer_init(struct libinput_timer *timer, struct libinput *libinput,
+libinput_timer_init(struct libinput_timer *timer,
+		    struct libinput *libinput,
 		    const char *timer_name,
 		    void (*timer_func)(uint64_t now, void *timer_func_data),
 		    void *timer_func_data);
@@ -58,9 +61,7 @@ enum timer_flags {
 };
 
 void
-libinput_timer_set_flags(struct libinput_timer *timer,
-			 uint64_t expire,
-			 uint32_t flags);
+libinput_timer_set_flags(struct libinput_timer *timer, uint64_t expire, uint32_t flags);
 
 void
 libinput_timer_cancel(struct libinput_timer *timer);
@@ -74,4 +75,7 @@ libinput_timer_subsys_destroy(struct lib
 void
 libinput_timer_flush(struct libinput *libinput, uint64_t now);
 
+uint64_t
+libinput_now(struct libinput *libinput);
+
 #endif
diff -pruN 1.28.1-1/src/udev-seat.c 1.30.0-1/src/udev-seat.c
--- 1.28.1-1/src/udev-seat.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/udev-seat.c	2025-11-25 03:40:43.000000000 +0000
@@ -24,8 +24,8 @@
 
 #include "config.h"
 
-#include <stdlib.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include "evdev.h"
@@ -42,8 +42,7 @@ static struct udev_seat *
 udev_seat_get_named(struct udev_input *input, const char *seat_name);
 
 static inline bool
-filter_duplicates(struct udev_seat *udev_seat,
-		  struct udev_device *udev_device)
+filter_duplicates(struct udev_seat *udev_seat, struct udev_device *udev_device)
 {
 	struct libinput_device *device;
 	const char *new_syspath = udev_device_get_syspath(udev_device);
@@ -54,16 +53,14 @@ filter_duplicates(struct udev_seat *udev
 
 	list_for_each(device, &udev_seat->base.devices_list, link) {
 		const char *syspath;
-		struct udev_device *ud;
 
-		ud = libinput_device_get_udev_device(device);
+		_unref_(udev_device) *ud = libinput_device_get_udev_device(device);
 		if (!ud)
 			continue;
 
 		syspath = udev_device_get_syspath(ud);
 		if (syspath && new_syspath && streq(syspath, new_syspath))
 			ignore_device = true;
-		udev_device_unref(ud);
 
 		if (ignore_device)
 			break;
@@ -154,8 +151,7 @@ device_removed(struct udev_device *udev_
 
 	syspath = udev_device_get_syspath(udev_device);
 	list_for_each(seat, &input->base.seat_list, base.link) {
-		list_for_each_safe(device,
-				   &seat->base.devices_list, base.link) {
+		list_for_each_safe(device, &seat->base.devices_list, base.link) {
 			if (streq(syspath,
 				  udev_device_get_syspath(device->udev_device))) {
 				evdev_device_remove(device);
@@ -168,23 +164,19 @@ device_removed(struct udev_device *udev_
 static int
 udev_input_add_devices(struct udev_input *input, struct udev *udev)
 {
-	struct udev_enumerate *e;
 	struct udev_list_entry *entry;
-	struct udev_device *device;
-	const char *path, *sysname;
 
-	e = udev_enumerate_new(udev);
+	_unref_(udev_enumerate) *e = udev_enumerate_new(udev);
 	udev_enumerate_add_match_subsystem(e, "input");
 	udev_enumerate_scan_devices(e);
 	udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
-		path = udev_list_entry_get_name(entry);
-		device = udev_device_new_from_syspath(udev, path);
+		const char *path = udev_list_entry_get_name(entry);
+		_unref_(udev_device) *device = udev_device_new_from_syspath(udev, path);
 		if (!device)
 			continue;
 
-		sysname = udev_device_get_sysname(device);
+		const char *sysname = udev_device_get_sysname(device);
 		if (!strstartswith(sysname, "event")) {
-			udev_device_unref(device);
 			continue;
 		}
 
@@ -195,20 +187,13 @@ udev_input_add_devices(struct udev_input
 				  "%-7s - skip unconfigured input device '%s'\n",
 				  sysname,
 				  udev_device_get_devnode(device));
-			udev_device_unref(device);
 			continue;
 		}
 
 		if (device_added(device, input, NULL) < 0) {
-			udev_device_unref(device);
-			udev_enumerate_unref(e);
 			return -1;
 		}
-
-		udev_device_unref(device);
 	}
-	udev_enumerate_unref(e);
-
 	return 0;
 }
 
@@ -216,27 +201,21 @@ static void
 evdev_udev_handler(void *data)
 {
 	struct udev_input *input = data;
-	struct udev_device *udev_device;
 	const char *action;
 
-	udev_device = udev_monitor_receive_device(input->udev_monitor);
+	_unref_(udev_device) *udev_device =
+		udev_monitor_receive_device(input->udev_monitor);
 	if (!udev_device)
 		return;
 
 	action = udev_device_get_action(udev_device);
-	if (!action)
-		goto out;
-
-	if (!strstartswith(udev_device_get_sysname(udev_device), "event"))
-		goto out;
+	if (!action || !strstartswith(udev_device_get_sysname(udev_device), "event"))
+		return;
 
 	if (streq(action, "add"))
 		device_added(udev_device, input, NULL);
 	else if (streq(action, "remove"))
 		device_removed(udev_device, input);
-
-out:
-	udev_device_unref(udev_device);
 }
 
 static void
@@ -247,8 +226,7 @@ udev_input_remove_devices(struct udev_in
 
 	list_for_each_safe(seat, &input->base.seat_list, base.link) {
 		libinput_seat_ref(&seat->base);
-		list_for_each_safe(device,
-				   &seat->base.devices_list, base.link) {
+		list_for_each_safe(device, &seat->base.devices_list, base.link) {
 			evdev_device_remove(device);
 		}
 		libinput_seat_unref(&seat->base);
@@ -258,7 +236,7 @@ udev_input_remove_devices(struct udev_in
 static void
 udev_input_disable(struct libinput *libinput)
 {
-	struct udev_input *input = (struct udev_input*)libinput;
+	struct udev_input *input = (struct udev_input *)libinput;
 
 	if (!input->udev_monitor)
 		return;
@@ -274,7 +252,7 @@ udev_input_disable(struct libinput *libi
 static int
 udev_input_enable(struct libinput *libinput)
 {
-	struct udev_input *input = (struct udev_input*)libinput;
+	struct udev_input *input = (struct udev_input *)libinput;
 	struct udev *udev = input->udev;
 	int fd;
 
@@ -283,13 +261,13 @@ udev_input_enable(struct libinput *libin
 
 	input->udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
 	if (!input->udev_monitor) {
-		log_info(libinput,
-			 "udev: failed to create the udev monitor\n");
+		log_info(libinput, "udev: failed to create the udev monitor\n");
 		return -1;
 	}
 
-	if (udev_monitor_filter_add_match_subsystem_devtype(
-				input->udev_monitor, "input", NULL)) {
+	if (udev_monitor_filter_add_match_subsystem_devtype(input->udev_monitor,
+							    "input",
+							    NULL)) {
 		log_info(libinput, "udev: failed to set up filter\n");
 		return -1;
 	}
@@ -302,10 +280,8 @@ udev_input_enable(struct libinput *libin
 	}
 
 	fd = udev_monitor_get_fd(input->udev_monitor);
-	input->udev_monitor_source = libinput_add_fd(&input->base,
-						     fd,
-						     evdev_udev_handler,
-						     input);
+	input->udev_monitor_source =
+		libinput_add_fd(&input->base, fd, evdev_udev_handler, input);
 	if (!input->udev_monitor_source) {
 		udev_monitor_unref(input->udev_monitor);
 		input->udev_monitor = NULL;
@@ -323,7 +299,7 @@ udev_input_enable(struct libinput *libin
 static void
 udev_input_destroy(struct libinput *input)
 {
-	struct udev_input *udev_input = (struct udev_input*)input;
+	struct udev_input *udev_input = (struct udev_input *)input;
 
 	if (input == NULL)
 		return;
@@ -335,7 +311,7 @@ udev_input_destroy(struct libinput *inpu
 static void
 udev_seat_destroy(struct libinput_seat *seat)
 {
-	struct udev_seat *useat = (struct udev_seat*)seat;
+	struct udev_seat *useat = (struct udev_seat *)seat;
 	free(useat);
 }
 
@@ -348,8 +324,10 @@ udev_seat_create(struct udev_input *inpu
 
 	seat = zalloc(sizeof *seat);
 
-	libinput_seat_init(&seat->base, &input->base,
-			   device_seat, seat_name,
+	libinput_seat_init(&seat->base,
+			   &input->base,
+			   device_seat,
+			   seat_name,
 			   udev_seat_destroy);
 
 	return seat;
@@ -369,8 +347,7 @@ udev_seat_get_named(struct udev_input *i
 }
 
 static int
-udev_device_change_seat(struct libinput_device *device,
-			const char *seat_name)
+udev_device_change_seat(struct libinput_device *device, const char *seat_name)
 {
 	struct libinput *libinput = device->seat->libinput;
 	struct udev_input *input = (struct udev_input *)libinput;
@@ -405,8 +382,8 @@ libinput_udev_create_context(const struc
 
 	input = zalloc(sizeof *input);
 
-	if (libinput_init(&input->base, interface,
-			  &interface_backend, user_data) != 0) {
+	if (libinput_init(&input->base, interface, &interface_backend, user_data) !=
+	    0) {
 		libinput_unref(&input->base);
 		free(input);
 		return NULL;
@@ -418,10 +395,9 @@ libinput_udev_create_context(const struc
 }
 
 LIBINPUT_EXPORT int
-libinput_udev_assign_seat(struct libinput *libinput,
-			  const char *seat_id)
+libinput_udev_assign_seat(struct libinput *libinput, const char *seat_id)
 {
-	struct udev_input *input = (struct udev_input*)libinput;
+	struct udev_input *input = (struct udev_input *)libinput;
 
 	if (!seat_id)
 		return -1;
@@ -440,6 +416,8 @@ libinput_udev_assign_seat(struct libinpu
 	if (input->seat_id != NULL)
 		return -1;
 
+	libinput_plugin_system_autoload(libinput);
+
 	/* We cannot do this during udev_create_context because the log
 	 * handler isn't set up there but we really want to log to the right
 	 * place if the quirks run into parser errors. So we have to do it
diff -pruN 1.28.1-1/src/udev-seat.h 1.30.0-1/src/udev-seat.h
--- 1.28.1-1/src/udev-seat.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/udev-seat.h	2025-11-25 03:40:43.000000000 +0000
@@ -27,6 +27,7 @@
 #include "config.h"
 
 #include <libudev.h>
+
 #include "libinput-private.h"
 
 struct udev_seat {
diff -pruN 1.28.1-1/src/util-backtrace.h 1.30.0-1/src/util-backtrace.h
--- 1.28.1-1/src/util-backtrace.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-backtrace.h	2025-11-25 03:40:43.000000000 +0000
@@ -27,8 +27,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 #include "util-macros.h"
 #include "util-strings.h"
@@ -54,7 +54,7 @@ backtrace_print(FILE *fp,
 		const char *highlight_before,
 		const char *highlight_extra)
 {
-#if HAVE_GSTACK
+#ifdef HAVE_GSTACK
 	pid_t parent, child;
 	int pipefd[2];
 
@@ -84,12 +84,13 @@ backtrace_print(FILE *fp,
 
 	status = WEXITSTATUS(status);
 	if (status != 0) {
-		fprintf(fp, "ERROR: gstack failed, no backtrace available: %s\n",
-			   strerror(status));
+		fprintf(fp,
+			"ERROR: gstack failed, no backtrace available: %s\n",
+			strerror(status));
 		goto out;
 	}
 
-	char buf[2048] = {0};
+	char buf[2048] = { 0 };
 	fprintf(fp, "\nBacktrace:\n");
 	read(pipefd[0], buf, sizeof(buf) - 1);
 	if (!use_colors || (!highlight_after && !highlight_before)) {
@@ -100,7 +101,8 @@ backtrace_print(FILE *fp,
 		char **line = lines;
 		bool highlight = highlight_after == NULL;
 		while (line && *line) {
-			if (highlight && highlight_before && strstr(*line, highlight_before))
+			if (highlight && highlight_before &&
+			    strstr(*line, highlight_before))
 				highlight = false;
 
 			const char *hlcolor = highlight ? ANSI_BRIGHT_CYAN : "";
@@ -109,11 +111,13 @@ backtrace_print(FILE *fp,
 			    strstr(*line, highlight_extra))
 				hlcolor = ANSI_BRIGHT_MAGENTA;
 
-			fprintf(fp, "%s%s%s\n",
+			fprintf(fp,
+				"%s%s%s\n",
 				hlcolor,
 				*line,
 				highlight ? ANSI_NORMAL : "");
-			if (!highlight && highlight_after && strstr(*line, highlight_after))
+			if (!highlight && highlight_after &&
+			    strstr(*line, highlight_after))
 				highlight = true;
 			line++;
 		}
diff -pruN 1.28.1-1/src/util-bits.h 1.30.0-1/src/util-bits.h
--- 1.28.1-1/src/util-bits.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-bits.h	2025-11-25 03:40:43.000000000 +0000
@@ -28,8 +28,14 @@
 #include "config.h"
 
 #include <assert.h>
+#include <limits.h>
+#include <stdarg.h>
 #include <stdbool.h>
 #include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "util-macros.h"
 
 #define bit(x_) (1UL << (x_))
 #define NBITS(b) (b * 8)
@@ -53,7 +59,7 @@ set_bit(unsigned char *array, int bit)
 	array[bit / 8] |= (1 << (bit % 8));
 }
 
-	static inline void
+static inline void
 clear_bit(unsigned char *array, int bit)
 {
 	array[bit / 8] &= ~(1 << (bit % 8));
@@ -98,3 +104,332 @@ long_any_bit_set(unsigned long *array, s
 			return true;
 	return false;
 }
+
+/* A wrapper around a bit mask to avoid type confusion */
+typedef struct {
+	uint32_t mask;
+} bitmask_t;
+
+static inline size_t
+bitmask_size(void)
+{
+	return 32;
+}
+
+static inline uint32_t
+bitmask_as_u32(bitmask_t mask)
+{
+	return mask.mask;
+}
+
+static inline bool
+bitmask_is_empty(bitmask_t mask)
+{
+	return mask.mask == 0;
+}
+
+static inline bool
+bitmask_any(bitmask_t mask, bitmask_t bits)
+{
+	return !!(mask.mask & bits.mask);
+}
+
+static inline bool
+bitmask_all(bitmask_t mask, bitmask_t bits)
+{
+	return bits.mask != 0 && (mask.mask & bits.mask) == bits.mask;
+}
+
+_nonnull_(1) static inline bool bitmask_merge(bitmask_t *mask, bitmask_t bits)
+{
+	bool all = bitmask_all(*mask, bits);
+
+	mask->mask |= bits.mask;
+
+	return all;
+}
+
+_nonnull_(1) static inline bool bitmask_clear(bitmask_t *mask, bitmask_t bits)
+{
+	bool all = bitmask_all(*mask, bits);
+
+	mask->mask &= ~bits.mask;
+
+	return all;
+}
+
+static inline bool
+bitmask_bit_is_set(bitmask_t mask, unsigned int bit)
+{
+	return !!(mask.mask & bit(bit)); // NOLINT: core.UndefinedBinaryOperatorResult
+}
+
+_nonnull_(1) static inline bool bitmask_set_bit(bitmask_t *mask, unsigned int bit)
+{
+	bool isset = bitmask_bit_is_set(*mask, bit);
+	mask->mask |= bit(bit);
+	return isset;
+}
+
+_nonnull_(1) static inline bool bitmask_clear_bit(bitmask_t *mask, unsigned int bit)
+{
+	bool isset = bitmask_bit_is_set(*mask, bit);
+	mask->mask &= ~bit(bit);
+	return isset;
+}
+
+static inline bitmask_t
+bitmask_new(void)
+{
+	bitmask_t m = { 0 };
+	return m;
+}
+
+static inline bitmask_t
+bitmask_from_bit(unsigned int bit)
+{
+	bitmask_t m = { .mask = bit(bit) };
+	return m;
+}
+
+static inline bitmask_t
+bitmask_from_u32(uint32_t mask)
+{
+	bitmask_t m = { .mask = mask };
+	return m;
+}
+
+static inline bitmask_t
+_bitmask_from_masks(uint32_t mask1, ...)
+{
+	uint32_t mask = mask1;
+	va_list args;
+	va_start(args, mask1);
+
+	uint32_t v = va_arg(args, unsigned int);
+	while (v != 0) {
+		mask |= v;
+		v = va_arg(args, unsigned int);
+	}
+	va_end(args);
+
+	return bitmask_from_u32(mask);
+}
+
+#define bitmask_from_masks(...) \
+	_bitmask_from_masks(__VA_ARGS__, 0)
+
+static inline bitmask_t
+_bitmask_from_bits(unsigned int bit1, ...)
+{
+	uint32_t mask = bit(bit1);
+	va_list args;
+	va_start(args, bit1);
+
+	uint32_t v = va_arg(args, unsigned int);
+	while (v < 32) {
+		mask |= bit(v);
+		v = va_arg(args, unsigned int);
+	}
+	va_end(args);
+
+	return bitmask_from_u32(mask);
+}
+
+#define bitmask_from_bits(...) \
+	_bitmask_from_bits(__VA_ARGS__, 32)
+
+/* An infinite bitmask that grows as needed.
+ *
+ * Note that unlike the bitmask_t this struct contains
+ * pointers and some care must be taken when using assign-by-value.
+ */
+typedef struct {
+	bitmask_t *mask;
+	size_t nmasks;
+} infmask_t;
+
+static inline size_t
+_infmask_size_for_bit(unsigned int bit)
+{
+	return (bit / bitmask_size()) + 1;
+}
+
+_nonnull_(1) static inline void _infmask_ensure_size(infmask_t *mask, unsigned int bit)
+{
+	size_t required = _infmask_size_for_bit(bit);
+	if (required > mask->nmasks) {
+		mask->mask = realloc(mask->mask, required * sizeof(bitmask_t));
+		/* Zero out the new memory */
+		for (size_t i = mask->nmasks; i < required; i++)
+			mask->mask[i] = bitmask_new();
+		mask->nmasks = required;
+	}
+	/* for clang-tidy, poor thing gets confused otherwise */
+	assert(mask->mask);
+	assert(mask->nmasks >= 1);
+}
+
+static inline infmask_t
+infmask_new(void)
+{
+	infmask_t m = { .mask = NULL, .nmasks = 0 };
+	return m;
+}
+
+_nonnull_(1) static inline void infmask_reset(infmask_t *mask)
+{
+	free(mask->mask);
+	mask->mask = NULL;
+	mask->nmasks = 0;
+}
+
+_nonnull_(1) static inline void infmask_destroy(infmask_t *mask)
+{
+	infmask_reset(mask);
+	free(mask);
+}
+
+_nonnull_(1) static inline bool infmask_is_empty(const infmask_t *mask)
+{
+	if (!mask->mask)
+		return true;
+
+	for (size_t i = 0; i < mask->nmasks; i++)
+		if (!bitmask_is_empty(mask->mask[i]))
+			return false;
+	return true;
+}
+
+_nonnull_(1) static inline bool infmask_any(const infmask_t *mask,
+					    const infmask_t *bits)
+{
+	if (!mask->mask || !bits->mask)
+		return false;
+
+	size_t min_size = min(mask->nmasks, bits->nmasks);
+	for (size_t i = 0; i < min_size; i++)
+		if (bitmask_any(mask->mask[i], bits->mask[i]))
+			return true;
+	return false;
+}
+
+_nonnull_(1) static inline bool infmask_all(const infmask_t *mask,
+					    const infmask_t *bits)
+{
+	if (!bits->mask)
+		return true;
+	if (!mask->mask)
+		return false;
+
+	size_t min_size = min(mask->nmasks, bits->nmasks);
+	for (size_t i = 0; i < min_size; i++)
+		if (!bitmask_all(mask->mask[i], bits->mask[i]))
+			return false;
+
+	/* Check if bits has any bits set beyond min_size */
+	for (size_t i = min_size; i < bits->nmasks; i++)
+		if (!bitmask_is_empty(bits->mask[i]))
+			return false;
+
+	return true;
+}
+
+_nonnull_(1) static inline bool infmask_merge(infmask_t *mask, const infmask_t *bits)
+{
+	if (!bits->mask)
+		return true;
+
+	_infmask_ensure_size(mask, bits->nmasks * bitmask_size() - 1);
+
+	bool all = true;
+
+	for (size_t i = 0; i < bits->nmasks; i++) {
+		all = all && bitmask_all(mask->mask[i], bits->mask[i]);
+		bitmask_merge(&mask->mask[i], bits->mask[i]);
+	}
+	return all;
+}
+
+_nonnull_(1) static inline bool infmask_clear(infmask_t *mask, const infmask_t *bits)
+{
+	if (!mask->mask || !bits->mask)
+		return false;
+
+	bool all = infmask_all(mask, bits);
+
+	size_t min_size = min(mask->nmasks, bits->nmasks);
+	for (size_t i = 0; i < min_size; i++)
+		bitmask_clear(&mask->mask[i], bits->mask[i]);
+
+	return all;
+}
+
+_nonnull_(1) static inline bool infmask_bit_is_set(const infmask_t *mask,
+						   unsigned int bit)
+{
+	if (!mask->mask || bit / bitmask_size() >= mask->nmasks)
+		return false;
+
+	return bitmask_bit_is_set(
+		mask->mask[bit / bitmask_size()], // NOLINT:
+						  // core.UndefinedBinaryOperatorResult
+		bit % bitmask_size());
+}
+
+_nonnull_(1) static inline bool infmask_set_bit(infmask_t *mask, unsigned int bit)
+{
+	_infmask_ensure_size(mask, bit);
+
+	bool isset = infmask_bit_is_set(mask, bit);
+	bitmask_set_bit(&mask->mask[bit / bitmask_size()], bit % bitmask_size());
+	return isset;
+}
+
+_nonnull_(1) static inline bool infmask_clear_bit(infmask_t *mask, unsigned int bit)
+{
+	if (!mask->mask || bit / bitmask_size() >= mask->nmasks)
+		return false;
+
+	bool isset = infmask_bit_is_set(mask, bit);
+	bitmask_clear_bit(&mask->mask[bit / bitmask_size()], bit % bitmask_size());
+	return isset;
+}
+
+static inline infmask_t
+infmask_from_bit(unsigned int bit)
+{
+	infmask_t m = infmask_new();
+	infmask_set_bit(&m, bit);
+	return m;
+}
+
+static inline infmask_t
+_infmask_from_bits(unsigned int bit1, ...)
+{
+	infmask_t m = infmask_new();
+	infmask_set_bit(&m, bit1);
+
+	va_list args;
+	va_start(args, bit1);
+
+	unsigned int v = va_arg(args, unsigned int);
+	while (v < UINT_MAX) {
+		infmask_set_bit(&m, v);
+		v = va_arg(args, unsigned int);
+	}
+	va_end(args);
+
+	return m;
+}
+
+#define infmask_from_bits(...) \
+	_infmask_from_bits(__VA_ARGS__, UINT_MAX)
+
+static inline infmask_t
+infmask_from_u32(uint32_t mask)
+{
+	infmask_t m = infmask_new();
+	infmask_set_bit(&m, mask);
+	return m;
+}
diff -pruN 1.28.1-1/src/util-files.c 1.30.0-1/src/util-files.c
--- 1.28.1-1/src/util-files.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/util-files.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,144 @@
+/*
+ * Copyright © 2024 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "util-files.h"
+#include "util-list.h"
+#include "util-macros.h"
+#include "util-strings.h"
+
+#include "libinput-versionsort.h"
+
+struct file {
+	struct list link;
+	char *name;
+	char *directory;
+};
+
+static void
+file_destroy(struct file *f)
+{
+	list_remove(&f->link);
+	free(f->name);
+	free(f->directory);
+	free(f);
+}
+
+DEFINE_DESTROY_CLEANUP_FUNC(file);
+
+/**
+ * Appends to the given list all files files in the given directory that end
+ * with the given with the given suffix.
+ */
+static void
+filenames(const char *directory, const char *suffix, struct list *list)
+{
+	_autofree_ struct dirent **namelist = NULL;
+
+	int ndev = scandir(directory, &namelist, NULL, versionsort);
+	if (ndev <= 0)
+		return;
+
+	for (int i = 0; i < ndev; i++) {
+		_autofree_ struct dirent *entry = namelist[i];
+		if (!strendswith(entry->d_name, suffix))
+			continue;
+
+		struct file *f = zalloc(sizeof(*f));
+		f->name = safe_strdup(entry->d_name);
+		f->directory = safe_strdup(directory);
+		list_append(list, &f->link);
+	}
+}
+
+static int
+filenamesort(const void *a, const void *b)
+{
+	const struct file *f1 = *(const struct file **)a;
+	const struct file *f2 = *(const struct file **)b;
+
+	return strverscmp(f1->name, f2->name);
+}
+
+char **
+list_files(const char **directories, const char *suffix, size_t *nfiles_out)
+{
+	struct list files = LIST_INIT(files);
+
+	if (!directories) {
+		if (nfiles_out)
+			*nfiles_out = 0;
+		return zalloc(1 * sizeof(char *));
+	}
+
+	const char **d = directories;
+	while (*d) {
+		struct list new_files = LIST_INIT(new_files);
+		filenames(*d, suffix, &new_files);
+
+		struct file *old_file;
+		list_for_each_safe(old_file, &files, link) {
+			struct file *new_file;
+			list_for_each_safe(new_file, &new_files, link) {
+				if (streq(old_file->name, new_file->name)) {
+					file_destroy(new_file);
+					break;
+				}
+			}
+		}
+		struct file *new_file;
+		list_for_each_safe(new_file, &new_files, link) {
+			list_remove(&new_file->link);
+			list_append(&files, &new_file->link);
+		}
+		d++;
+	}
+
+	size_t nfiles = 0;
+	struct file *f;
+	list_for_each(f, &files, link) {
+		nfiles++;
+	}
+	/* Allocating +1 conveniently handles the directories[0] = NULL case */
+	_autofree_ struct file **fs = zalloc((nfiles + 1) * sizeof(*fs));
+	size_t idx = 0;
+	list_for_each_safe(f, &files, link) {
+		fs[idx++] = f;
+		list_remove(&f->link);
+		list_init(&f->link); // So we can file_destroy it later
+	}
+
+	qsort(fs, nfiles, sizeof(*fs), filenamesort);
+
+	char **paths = zalloc((nfiles + 1) * sizeof(*paths));
+	for (size_t i = 0; i < nfiles; i++) {
+		_destroy_(file) *f = fs[i];
+		paths[i] = strdup_printf("%s/%s", f->directory, f->name);
+	}
+
+	if (nfiles_out)
+		*nfiles_out = nfiles;
+
+	return steal(&paths);
+}
diff -pruN 1.28.1-1/src/util-files.h 1.30.0-1/src/util-files.h
--- 1.28.1-1/src/util-files.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-files.h	2025-11-25 03:40:43.000000000 +0000
@@ -25,10 +25,11 @@
 
 #include "config.h"
 
+#include <dirent.h>
 #include <errno.h>
 #include <libgen.h>
-#include <unistd.h>
 #include <sys/stat.h>
+#include <unistd.h>
 
 #include "util-strings.h"
 
@@ -43,17 +44,49 @@ mkdir_p(const char *dir)
 
 	path = safe_strdup(dir);
 	parent = dirname(path);
+	rc = mkdir_p(parent);
+	free(path);
 
-	if ((rc = mkdir_p(parent)) < 0)
+	if (rc < 0)
 		return rc;
 
 	rc = mkdir(dir, 0755);
 
-	free(path);
-
 	return (rc == -1 && errno != EEXIST) ? -errno : 0;
 }
 
+DEFINE_TRIVIAL_CLEANUP_FUNC(DIR *, closedir);
+
+static inline int
+rmdir_r(const char *dir)
+{
+	_cleanup_(closedirp) DIR *d = opendir(dir);
+	if (!d)
+		return -errno;
+
+	struct dirent *entry;
+	int rc = 0;
+
+	while (rc >= 0 && (entry = readdir(d))) {
+		if (streq(entry->d_name, ".") || streq(entry->d_name, ".."))
+			continue;
+
+		_autofree_ char *path = strdup_printf("%s/%s", dir, entry->d_name);
+
+		struct stat st;
+		if (stat(path, &st) < 0)
+			return -errno;
+
+		if (S_ISDIR(st.st_mode))
+			rc = rmdir_r(path);
+		else
+			rc = unlink(path) < 0 ? -errno : 0;
+	}
+	rc = rmdir(dir) < 0 ? -errno : rc;
+
+	return rc;
+}
+
 static inline void
 xclose(int *fd)
 {
@@ -62,3 +95,47 @@ xclose(int *fd)
 		*fd = -1;
 	}
 }
+
+/**
+ * In the NULL-terminated list of directories
+ * search for files with the given suffix and return
+ * a filename-ordered NULL-terminated list of those
+ * full paths.
+ *
+ * The directories are given in descending priority order.
+ * Any file with a given filename shadows the same file
+ * in another directory of lower sorting order.
+ *
+ * If nfiles is not NULL, it is set to the number of
+ * files returned (not including the NULL terminator).
+ */
+char **
+list_files(const char **directories, const char *suffix, size_t *nfiles);
+
+struct tmpdir {
+	char *path;
+};
+
+static inline void
+tmpdir_destroy(struct tmpdir *tmpdir)
+{
+	/* String check so we can't accidentally rm -rf */
+	if (tmpdir->path && strstr(tmpdir->path, "tmpdir-")) {
+		rmdir_r(tmpdir->path);
+		free(tmpdir->path);
+	}
+	free(tmpdir);
+}
+
+DEFINE_DESTROY_CLEANUP_FUNC(tmpdir);
+
+static inline struct tmpdir *
+tmpdir_create(const char *basedir)
+{
+	_destroy_(tmpdir) *tmpdir = zalloc(sizeof(*tmpdir));
+	tmpdir->path = strdup_printf("%s/tmpdir-XXXXXX", basedir ? basedir : "/tmp");
+	if (!mkdtemp(tmpdir->path))
+		return NULL;
+
+	return steal(&tmpdir);
+}
diff -pruN 1.28.1-1/src/util-input-event.h 1.30.0-1/src/util-input-event.h
--- 1.28.1-1/src/util-input-event.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-input-event.h	2025-11-25 03:40:43.000000000 +0000
@@ -25,14 +25,17 @@
 
 #include "config.h"
 
-#include "util-time.h"
+#include <libevdev/libevdev.h>
 #include <linux/input.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "util-mem.h"
+#include "util-newtype.h"
+#include "util-time.h"
 
 static inline struct input_event
-input_event_init(uint64_t time,
-		 unsigned int type,
-		 unsigned int code,
-		 int value)
+input_event_init(uint64_t time, unsigned int type, unsigned int code, int value)
 {
 	struct input_event ev;
 	struct timeval tval = us2tv(time);
@@ -58,8 +61,7 @@ input_event_time(const struct input_even
 }
 
 static inline void
-input_event_set_time(struct input_event *e,
-		     uint64_t time)
+input_event_set_time(struct input_event *e, uint64_t time)
 {
 	struct timeval tval = us2tv(time);
 
@@ -76,7 +78,9 @@ absinfo_range(const struct input_absinfo
 static inline double
 absinfo_normalize_value(const struct input_absinfo *abs, int value)
 {
-	return min(1.0, max(0.0, (double)(value - abs->minimum)/(abs->maximum - abs->minimum)));
+	return min(1.0,
+		   max(0.0,
+		       (double)(value - abs->minimum) / (abs->maximum - abs->minimum)));
 }
 
 static inline double
@@ -95,5 +99,5 @@ static inline double
 absinfo_convert_to_mm(const struct input_absinfo *absinfo, double v)
 {
 	double value = v - absinfo->minimum;
-	return value/absinfo->resolution;
+	return value / absinfo->resolution;
 }
diff -pruN 1.28.1-1/src/util-libinput.c 1.30.0-1/src/util-libinput.c
--- 1.28.1-1/src/util-libinput.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-libinput.c	2025-11-25 03:40:43.000000000 +0000
@@ -25,17 +25,18 @@
 
 #include <inttypes.h>
 
-#include "libevdev/libevdev.h"
-
-#include "util-strings.h"
 #include "util-libinput.h"
+#include "util-mem.h"
+#include "util-strings.h"
+
+#include "libevdev/libevdev.h"
 
 static const char *
 event_type_to_str(enum libinput_event_type evtype)
 {
 	const char *type;
 
-	switch(evtype) {
+	switch (evtype) {
 	case LIBINPUT_EVENT_NONE:
 		abort();
 	case LIBINPUT_EVENT_DEVICE_ADDED:
@@ -176,70 +177,76 @@ static inline char *
 print_device_options(struct libinput_device *dev)
 {
 	uint32_t scroll_methods, click_methods;
-	char *tap = NULL,
-	     *scroll = NULL,
-	     *clickm = NULL,
-	     *dwt = NULL,
-	     *dwtp = NULL,
-	     *pad = NULL;
+	_autofree_ char *tap = NULL;
+	_autofree_ char *scroll = NULL;
+	_autofree_ char *clickm = NULL;
+	_autofree_ char *dwt = NULL;
+	_autofree_ char *dwtp = NULL;
+	_autofree_ char *pad = NULL;
 
 	if (libinput_device_config_tap_get_finger_count(dev)) {
-		tap = strdup_printf(" tap (dl %s)",
-				    onoff(libinput_device_config_tap_get_drag_lock_enabled(dev)));
+		tap = strdup_printf(
+			" tap (dl %s)",
+			onoff(libinput_device_config_tap_get_drag_lock_enabled(dev)));
 	}
 
 	scroll_methods = libinput_device_config_scroll_get_methods(dev);
 	if (scroll_methods != LIBINPUT_CONFIG_SCROLL_NO_SCROLL) {
-		scroll = strdup_printf(" scroll%s%s%s",
-			(scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG) ?  "-2fg" : "",
+		scroll = strdup_printf(
+			" scroll%s%s%s",
+			(scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG) ? "-2fg" : "",
 			(scroll_methods & LIBINPUT_CONFIG_SCROLL_EDGE) ? "-edge" : "",
-			(scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) ? "-button" : "");
+			(scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN)
+				? "-button"
+				: "");
 	}
 
 	click_methods = libinput_device_config_click_get_methods(dev);
 	if (click_methods != LIBINPUT_CONFIG_CLICK_METHOD_NONE) {
-		clickm = strdup_printf(" click%s%s",
-				       (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "-buttonareas" : "",
-				       (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) ? "-clickfinger" : "");
+		clickm = strdup_printf(
+			" click%s%s",
+			(click_methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS)
+				? "-buttonareas"
+				: "",
+			(click_methods & LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER)
+				? "-clickfinger"
+				: "");
 	}
 
 	if (libinput_device_config_dwt_is_available(dev)) {
 		dwt = strdup_printf(" dwt-%s",
-				    onoff(libinput_device_config_dwt_get_enabled(dev) == LIBINPUT_CONFIG_DWT_ENABLED));
+				    onoff(libinput_device_config_dwt_get_enabled(dev) ==
+					  LIBINPUT_CONFIG_DWT_ENABLED));
 	}
 
 	if (libinput_device_config_dwtp_is_available(dev)) {
-		dwt = strdup_printf(" dwtp-%s",
-				    onoff(libinput_device_config_dwtp_get_enabled(dev) == LIBINPUT_CONFIG_DWTP_ENABLED));
-	}
-
-	if (libinput_device_has_capability(dev,
-					   LIBINPUT_DEVICE_CAP_TABLET_PAD)) {
-		pad = strdup_printf(" buttons:%d strips:%d rings:%d mode groups:%d",
-				    libinput_device_tablet_pad_get_num_buttons(dev),
-				    libinput_device_tablet_pad_get_num_strips(dev),
-				    libinput_device_tablet_pad_get_num_rings(dev),
-				    libinput_device_tablet_pad_get_num_mode_groups(dev));
-	}
-
-	char *str = strdup_printf("%s%s%s%s%s%s%s%s%s",
-				  tap ? tap : "",
-				  libinput_device_config_left_handed_is_available(dev) ? " left" : "",
-				  libinput_device_config_scroll_has_natural_scroll(dev) ? " scroll-nat" : "",
-				  libinput_device_config_calibration_has_matrix(dev) ? " calib" : "",
-				  scroll ? scroll : "",
-				  clickm ? clickm : "",
-				  dwt ? dwt : "",
-				  dwtp ? dwtp : "",
-				  pad ? pad : "");
-	free(tap);
-	free(scroll);
-	free(clickm);
-	free(dwt);
-	free(dwtp);
-	free(pad);
-
-	return str;
+		dwtp = strdup_printf(
+			" dwtp-%s",
+			onoff(libinput_device_config_dwtp_get_enabled(dev) ==
+			      LIBINPUT_CONFIG_DWTP_ENABLED));
+	}
+
+	if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TABLET_PAD)) {
+		pad = strdup_printf(
+			" buttons:%d strips:%d rings:%d mode groups:%d",
+			libinput_device_tablet_pad_get_num_buttons(dev),
+			libinput_device_tablet_pad_get_num_strips(dev),
+			libinput_device_tablet_pad_get_num_rings(dev),
+			libinput_device_tablet_pad_get_num_mode_groups(dev));
+	}
+
+	return strdup_printf(
+		"%s%s%s%s%s%s%s%s%s",
+		tap ? tap : "",
+		libinput_device_config_left_handed_is_available(dev) ? " left" : "",
+		libinput_device_config_scroll_has_natural_scroll(dev) ? " scroll-nat"
+								      : "",
+		libinput_device_config_calibration_has_matrix(dev) ? " calib" : "",
+		scroll ? scroll : "",
+		clickm ? clickm : "",
+		dwt ? dwt : "",
+		dwtp ? dwtp : "",
+		pad ? pad : "");
 }
 
 static char *
@@ -251,47 +258,52 @@ print_device_notify(struct libinput_even
 	double w, h;
 	static int next_group_id = 0;
 	intptr_t group_id;
-	char *size = NULL,
-	     *ntouches = NULL,
-	     *options = NULL;
+	_autofree_ char *size = NULL;
+	_autofree_ char *ntouches = NULL;
+	_autofree_ char *options = NULL;
 
 	group = libinput_device_get_device_group(dev);
 	group_id = (intptr_t)libinput_device_group_get_user_data(group);
 	if (!group_id) {
 		group_id = ++next_group_id;
-		libinput_device_group_set_user_data(group, (void*)group_id);
+		libinput_device_group_set_user_data(group, (void *)group_id);
 	}
 
 	if (libinput_device_get_size(dev, &w, &h) == 0)
 		size = strdup_printf("  size %.0fx%.0fmm", w, h);
 
-	if (libinput_device_has_capability(dev,
-					   LIBINPUT_DEVICE_CAP_TOUCH))
-		ntouches = strdup_printf(" ntouches %d", libinput_device_touch_get_touch_count(dev));
+	if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TOUCH))
+		ntouches = strdup_printf(" ntouches %d",
+					 libinput_device_touch_get_touch_count(dev));
 
 	if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED)
 		options = print_device_options(dev);
 
-	char *str = strdup_printf("%-33s %5s %7s group%-2d cap:%s%s%s%s%s%s%s%s%s%s",
-				  libinput_device_get_name(dev),
-				  libinput_seat_get_physical_name(seat),
-				  libinput_seat_get_logical_name(seat),
-				  (int)group_id,
-				  libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_KEYBOARD) ? "k" : "",
-				  libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_POINTER) ? "p" : "",
-				  libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TOUCH) ? "t" : "",
-				  libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_GESTURE) ? "g" : "",
-				  libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TABLET_TOOL) ? "T" : "",
-				  libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TABLET_PAD) ? "P" : "",
-				  libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_SWITCH) ? "S"  : "",
-				  size ? size : "",
-				  ntouches ? ntouches : "",
-				  options ? options : "");
-	free(size);
-	free(ntouches);
-	free(options);
-
-	return str;
+	return strdup_printf(
+		"%-33s %5s %7s group%-2d cap:%s%s%s%s%s%s%s%s%s%s",
+		libinput_device_get_name(dev),
+		libinput_seat_get_physical_name(seat),
+		libinput_seat_get_logical_name(seat),
+		(int)group_id,
+		libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_KEYBOARD) ? "k"
+										  : "",
+		libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_POINTER) ? "p"
+										 : "",
+		libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TOUCH) ? "t"
+									       : "",
+		libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_GESTURE) ? "g"
+										 : "",
+		libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TABLET_TOOL)
+			? "T"
+			: "",
+		libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TABLET_PAD)
+			? "P"
+			: "",
+		libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_SWITCH) ? "S"
+										: "",
+		size ? size : "",
+		ntouches ? ntouches : "",
+		options ? options : "");
 }
 
 static char *
@@ -318,7 +330,8 @@ print_key_event(struct libinput_event *e
 			     time,
 			     keyname,
 			     key,
-			     state == LIBINPUT_KEY_STATE_PRESSED ? "pressed" : "released");
+			     state == LIBINPUT_KEY_STATE_PRESSED ? "pressed"
+								 : "released");
 }
 
 static char *
@@ -337,13 +350,16 @@ print_motion_event(struct libinput_event
 }
 
 static char *
-print_absmotion_event(struct libinput_event *ev, const struct libinput_print_options *opts)
+print_absmotion_event(struct libinput_event *ev,
+		      const struct libinput_print_options *opts)
 {
 	struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
-	double x = libinput_event_pointer_get_absolute_x_transformed(
-		p, opts->screen_width);
-	double y = libinput_event_pointer_get_absolute_y_transformed(
-		p, opts->screen_height);
+	double x =
+		libinput_event_pointer_get_absolute_x_transformed(p,
+								  opts->screen_width);
+	double y =
+		libinput_event_pointer_get_absolute_y_transformed(p,
+								  opts->screen_height);
 	char time[16];
 
 	print_event_time(time, opts->start_time, libinput_event_pointer_get_time(p));
@@ -351,7 +367,8 @@ print_absmotion_event(struct libinput_ev
 }
 
 static char *
-print_pointer_button_event(struct libinput_event *ev, const struct libinput_print_options *opts)
+print_pointer_button_event(struct libinput_event *ev,
+			   const struct libinput_print_options *opts)
 {
 	struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
 	enum libinput_button_state state;
@@ -369,7 +386,8 @@ print_pointer_button_event(struct libinp
 			     time,
 			     buttonname ? buttonname : "???",
 			     button,
-			     state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released",
+			     state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed"
+								    : "released",
 			     libinput_event_pointer_get_seat_button_count(p));
 }
 
@@ -378,22 +396,24 @@ print_tablet_axes(struct libinput_event_
 {
 	struct libinput_tablet_tool *tool = libinput_event_tablet_tool_get_tool(t);
 	double x, y;
-	char *tilt = NULL,
-	     *distance = NULL,
-	     *rot = NULL,
-	     *whl = NULL,
-	     *sld = NULL,
-	     *size = NULL;
+	_autofree_ char *tilt = NULL;
+	_autofree_ char *distance = NULL;
+	_autofree_ char *rot = NULL;
+	_autofree_ char *whl = NULL;
+	_autofree_ char *sld = NULL;
+	_autofree_ char *size = NULL;
 
 #define changed_sym(ev, ax) \
-	(libinput_event_tablet_tool_##ax##_has_changed(ev) ? "*" : "")
+	(libinput_event_tablet_tool_##ax##_has_changed(ev) ? "*" : " ")
 
 	if (libinput_tablet_tool_has_tilt(tool)) {
 		x = libinput_event_tablet_tool_get_tilt_x(t);
 		y = libinput_event_tablet_tool_get_tilt_y(t);
 		tilt = strdup_printf("\ttilt: %.2f%s/%.2f%s",
-				     x, changed_sym(t, tilt_x),
-				     y, changed_sym(t, tilt_y));
+				     x,
+				     changed_sym(t, tilt_x),
+				     y,
+				     changed_sym(t, tilt_y));
 	}
 
 	if (libinput_tablet_tool_has_distance(tool) ||
@@ -402,29 +422,32 @@ print_tablet_axes(struct libinput_event_
 		double pressure = libinput_event_tablet_tool_get_pressure(t);
 		if (dist)
 			distance = strdup_printf("\tdistance: %.2f%s",
-						 dist, changed_sym(t, distance));
+						 dist,
+						 changed_sym(t, distance));
 		else
 			distance = strdup_printf("\tpressure: %.2f%s",
-						 pressure, changed_sym(t, pressure));
+						 pressure,
+						 changed_sym(t, pressure));
 	}
 
 	if (libinput_tablet_tool_has_rotation(tool)) {
 		double rotation = libinput_event_tablet_tool_get_rotation(t);
 		rot = strdup_printf("\trotation: %6.2f%s",
-				    rotation, changed_sym(t, rotation));
+				    rotation,
+				    changed_sym(t, rotation));
 	}
 
 	if (libinput_tablet_tool_has_slider(tool)) {
 		double slider = libinput_event_tablet_tool_get_slider_position(t);
-		sld = strdup_printf("\tslider: %.2f%s",
-				    slider, changed_sym(t, slider));
+		sld = strdup_printf("\tslider: %.2f%s", slider, changed_sym(t, slider));
 	}
 
 	if (libinput_tablet_tool_has_wheel(tool)) {
 		double wheel = libinput_event_tablet_tool_get_wheel_delta(t);
 		double delta = libinput_event_tablet_tool_get_wheel_delta_discrete(t);
 		whl = strdup_printf("\twheel: %.2f%s (%d)",
-				    wheel, changed_sym(t, wheel),
+				    wheel,
+				    changed_sym(t, wheel),
 				    (int)delta);
 	}
 
@@ -432,54 +455,51 @@ print_tablet_axes(struct libinput_event_
 		double major = libinput_event_tablet_tool_get_size_major(t);
 		double minor = libinput_event_tablet_tool_get_size_minor(t);
 		size = strdup_printf("\tsize: %.2f%s/%.2f%s",
-				     major, changed_sym(t, size_major),
-				     minor, changed_sym(t, size_minor));
+				     major,
+				     changed_sym(t, size_major),
+				     minor,
+				     changed_sym(t, size_minor));
 	}
 
 	x = libinput_event_tablet_tool_get_x(t);
 	y = libinput_event_tablet_tool_get_y(t);
-	char *str = strdup_printf("\t%.2f%s/%.2f%s%s%s%s%s%s%s",
-				  x, changed_sym(t, x),
-				  y, changed_sym(t, y),
-				  tilt ? tilt : "",
-				  distance ? distance : "",
-				  rot ? rot : "",
-				  whl ? whl : "",
-				  sld ? sld : "",
-				  size ? size : "");
-	free(tilt);
-	free(distance);
-	free(rot);
-	free(whl);
-	free(sld);
-	free(size);
-
-	return str;
+	return strdup_printf("\t%.2f%s/%.2f%s%s%s%s%s%s%s",
+			     x,
+			     changed_sym(t, x),
+			     y,
+			     changed_sym(t, y),
+			     tilt ? tilt : "",
+			     distance ? distance : "",
+			     rot ? rot : "",
+			     whl ? whl : "",
+			     sld ? sld : "",
+			     size ? size : "");
 }
 
 static char *
-print_tablet_tip_event(struct libinput_event *ev, const struct libinput_print_options *opts)
+print_tablet_tip_event(struct libinput_event *ev,
+		       const struct libinput_print_options *opts)
 {
 	struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(ev);
 	enum libinput_tablet_tool_tip_state state;
 	char time[16];
 
-	print_event_time(time, opts->start_time, libinput_event_tablet_tool_get_time(t));
+	print_event_time(time,
+			 opts->start_time,
+			 libinput_event_tablet_tool_get_time(t));
 
-	char *axes = print_tablet_axes(t);
+	_autofree_ char *axes = print_tablet_axes(t);
 
 	state = libinput_event_tablet_tool_get_tip_state(t);
-	char *str = strdup_printf("%s\t%s %s",
-				  time,
-				  axes,
-				  state == LIBINPUT_TABLET_TOOL_TIP_DOWN ? "down" : "up");
-	free(axes);
-
-	return str;
+	return strdup_printf("%s\t%s %s",
+			     time,
+			     axes,
+			     state == LIBINPUT_TABLET_TOOL_TIP_DOWN ? "down" : "up");
 }
 
 static char *
-print_tablet_button_event(struct libinput_event *ev, const struct libinput_print_options *opts)
+print_tablet_button_event(struct libinput_event *ev,
+			  const struct libinput_print_options *opts)
 {
 	struct libinput_event_tablet_tool *p = libinput_event_get_tablet_tool_event(ev);
 	enum libinput_button_state state;
@@ -487,7 +507,9 @@ print_tablet_button_event(struct libinpu
 	int button;
 	char time[16];
 
-	print_event_time(time, opts->start_time, libinput_event_tablet_tool_get_time(p));
+	print_event_time(time,
+			 opts->start_time,
+			 libinput_event_tablet_tool_get_time(p));
 
 	button = libinput_event_tablet_tool_get_button(p);
 	buttonname = libevdev_event_code_get_name(EV_KEY, button);
@@ -497,17 +519,74 @@ print_tablet_button_event(struct libinpu
 			     time,
 			     button,
 			     buttonname ? buttonname : "???",
-			     state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released",
+			     state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed"
+								    : "released",
 			     libinput_event_tablet_tool_get_seat_button_count(p));
 }
 
 static char *
-print_pointer_axis_event(struct libinput_event *ev, const struct libinput_print_options *opts)
+print_legacy_pointer_axis_event(struct libinput_event *ev,
+				const struct libinput_print_options *opts)
+{
+	struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
+	double v = 0, h = 0;
+	int v_discrete = 0, h_discrete = 0;
+	const char *have_vert = "", *have_horiz = "";
+	const char *source = NULL;
+	enum libinput_pointer_axis axis;
+	char time[16];
+
+	switch (libinput_event_pointer_get_axis_source(p)) {
+	case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
+		source = "wheel";
+		break;
+	case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
+		source = "finger";
+		break;
+	case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
+		source = "continuous";
+		break;
+	case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT:
+		/* No device uses wheel tilt */
+		abort();
+		break;
+	default:
+		abort();
+		break;
+	}
+
+	axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
+	if (libinput_event_pointer_has_axis(p, axis)) {
+		v = libinput_event_pointer_get_axis_value(p, axis);
+		v_discrete = libinput_event_pointer_get_axis_value_discrete(p, axis);
+		have_vert = "*";
+	}
+	axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
+	if (libinput_event_pointer_has_axis(p, axis)) {
+		h = libinput_event_pointer_get_axis_value(p, axis);
+		h_discrete = libinput_event_pointer_get_axis_value_discrete(p, axis);
+		have_horiz = "*";
+	}
+
+	print_event_time(time, opts->start_time, libinput_event_pointer_get_time(p));
+	return strdup_printf("%s\tvert %.2f/%d%s horiz %.2f/%d%s (%s)",
+			     time,
+			     v,
+			     v_discrete,
+			     have_vert,
+			     h,
+			     h_discrete,
+			     have_horiz,
+			     source);
+}
+
+static char *
+print_pointer_axis_event(struct libinput_event *ev,
+			 const struct libinput_print_options *opts)
 {
 	struct libinput_event_pointer *p = libinput_event_get_pointer_event(ev);
 	double v = 0, h = 0, v120 = 0, h120 = 0;
-	const char *have_vert = "",
-		   *have_horiz = "";
+	const char *have_vert = "", *have_horiz = "";
 	const char *source = NULL;
 	enum libinput_pointer_axis axis;
 	enum libinput_event_type type;
@@ -548,35 +627,41 @@ print_pointer_axis_event(struct libinput
 	print_event_time(time, opts->start_time, libinput_event_pointer_get_time(p));
 	return strdup_printf("%s\tvert %.2f/%.1f%s horiz %.2f/%.1f%s (%s)",
 			     time,
-			     v, v120, have_vert,
-			     h, h120, have_horiz, source);
+			     v,
+			     v120,
+			     have_vert,
+			     h,
+			     h120,
+			     have_horiz,
+			     source);
 }
 
 static char *
-print_tablet_axis_event(struct libinput_event *ev, const struct libinput_print_options *opts)
+print_tablet_axis_event(struct libinput_event *ev,
+			const struct libinput_print_options *opts)
 {
 	struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(ev);
 	char time[16];
 
-	print_event_time(time, opts->start_time, libinput_event_tablet_tool_get_time(t));
-	char *axes = print_tablet_axes(t);
+	print_event_time(time,
+			 opts->start_time,
+			 libinput_event_tablet_tool_get_time(t));
+	_autofree_ char *axes = print_tablet_axes(t);
 
-	char *str = strdup_printf("%s\t%s", time, axes);
-	free(axes);
-	return str;
+	return strdup_printf("%s\t%s", time, axes);
 }
 
 static char *
-print_proximity_event(struct libinput_event *ev, const struct libinput_print_options *opts)
+print_proximity_event(struct libinput_event *ev,
+		      const struct libinput_print_options *opts)
 {
 	struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(ev);
 	struct libinput_tablet_tool *tool = libinput_event_tablet_tool_get_tool(t);
 	enum libinput_tablet_tool_proximity_state state;
-	const char *tool_str,
-	           *state_str;
+	const char *tool_str, *state_str;
 	char time[16];
-	char *axes = NULL,
-	     *proxin = NULL;
+	_autofree_ char *axes = NULL;
+	_autofree_ char *proxin = NULL;
 
 	switch (libinput_tablet_tool_get_type(tool)) {
 	case LIBINPUT_TABLET_TOOL_TYPE_PEN:
@@ -609,7 +694,9 @@ print_proximity_event(struct libinput_ev
 
 	state = libinput_event_tablet_tool_get_proximity_state(t);
 
-	print_event_time(time, opts->start_time, libinput_event_tablet_tool_get_time(t));
+	print_event_time(time,
+			 opts->start_time,
+			 libinput_event_tablet_tool_get_time(t));
 
 	if (state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN) {
 		axes = print_tablet_axes(t);
@@ -622,37 +709,34 @@ print_proximity_event(struct libinput_ev
 	}
 
 	if (state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN) {
-		proxin = strdup_printf("\taxes:%s%s%s%s%s%s\tbtn:%s%s%s%s%s%s%s%s%s%s",
-		libinput_tablet_tool_has_distance(tool) ? "d" : "",
-		libinput_tablet_tool_has_pressure(tool) ? "p" : "",
-		libinput_tablet_tool_has_tilt(tool) ? "t" : "",
-		libinput_tablet_tool_has_rotation(tool) ? "r" : "",
-		libinput_tablet_tool_has_slider(tool) ? "s" : "",
-		libinput_tablet_tool_has_wheel(tool) ? "w" : "",
-		libinput_tablet_tool_has_size(tool) ? "S" : "",
-		libinput_tablet_tool_has_button(tool, BTN_TOUCH) ? "T" : "",
-		libinput_tablet_tool_has_button(tool, BTN_STYLUS) ? "S" : "",
-		libinput_tablet_tool_has_button(tool, BTN_STYLUS2) ? "S2" : "",
-		libinput_tablet_tool_has_button(tool, BTN_LEFT) ? "L" : "",
-		libinput_tablet_tool_has_button(tool, BTN_MIDDLE) ? "M" : "",
-		libinput_tablet_tool_has_button(tool, BTN_RIGHT) ? "R" : "",
-		libinput_tablet_tool_has_button(tool, BTN_SIDE) ? "Sd" : "",
-		libinput_tablet_tool_has_button(tool, BTN_EXTRA) ? "Ex" : "",
-		libinput_tablet_tool_has_button(tool, BTN_0) ? "0" : "");
-	}
-
-	char *str = strdup_printf("%s\t%s\t%-8s (%#" PRIx64 ", id %#" PRIx64 ") %s%s",
-				  time,
-				  axes ? axes : "",
-				  tool_str,
-				  libinput_tablet_tool_get_serial(tool),
-				  libinput_tablet_tool_get_tool_id(tool),
-				  state_str,
-				  proxin ? proxin : "");
-	free(axes);
-	free(proxin);
+		proxin = strdup_printf(
+			"\taxes:%s%s%s%s%s%s\tbtn:%s%s%s%s%s%s%s%s%s%s",
+			libinput_tablet_tool_has_distance(tool) ? "d" : "",
+			libinput_tablet_tool_has_pressure(tool) ? "p" : "",
+			libinput_tablet_tool_has_tilt(tool) ? "t" : "",
+			libinput_tablet_tool_has_rotation(tool) ? "r" : "",
+			libinput_tablet_tool_has_slider(tool) ? "s" : "",
+			libinput_tablet_tool_has_wheel(tool) ? "w" : "",
+			libinput_tablet_tool_has_size(tool) ? "S" : "",
+			libinput_tablet_tool_has_button(tool, BTN_TOUCH) ? "T" : "",
+			libinput_tablet_tool_has_button(tool, BTN_STYLUS) ? "S" : "",
+			libinput_tablet_tool_has_button(tool, BTN_STYLUS2) ? "S2" : "",
+			libinput_tablet_tool_has_button(tool, BTN_LEFT) ? "L" : "",
+			libinput_tablet_tool_has_button(tool, BTN_MIDDLE) ? "M" : "",
+			libinput_tablet_tool_has_button(tool, BTN_RIGHT) ? "R" : "",
+			libinput_tablet_tool_has_button(tool, BTN_SIDE) ? "Sd" : "",
+			libinput_tablet_tool_has_button(tool, BTN_EXTRA) ? "Ex" : "",
+			libinput_tablet_tool_has_button(tool, BTN_0) ? "0" : "");
+	}
 
-	return str;
+	return strdup_printf("%s\t%s\t%-8s (%#" PRIx64 ", id %#" PRIx64 ") %s%s",
+			     time,
+			     axes ? axes : "",
+			     tool_str,
+			     libinput_tablet_tool_get_serial(tool),
+			     libinput_tablet_tool_get_tool_id(tool),
+			     state_str,
+			     proxin ? proxin : "");
 }
 
 static char *
@@ -661,8 +745,8 @@ print_touch_event(struct libinput_event
 	struct libinput_event_touch *t = libinput_event_get_touch_event(ev);
 	enum libinput_event_type type = libinput_event_get_type(ev);
 	char time[16];
-	char *slot = NULL,
-	     *pos = NULL;
+	_autofree_ char *slot = NULL;
+	_autofree_ char *pos = NULL;
 
 	print_event_time(time, opts->start_time, libinput_event_touch_get_time(t));
 
@@ -672,27 +756,23 @@ print_touch_event(struct libinput_event
 				     libinput_event_touch_get_seat_slot(t));
 	}
 
-	if (type == LIBINPUT_EVENT_TOUCH_DOWN ||
-	    type == LIBINPUT_EVENT_TOUCH_MOTION) {
-		double x = libinput_event_touch_get_x_transformed(t, opts->screen_width);
-		double y = libinput_event_touch_get_y_transformed(t, opts->screen_height);
+	if (type == LIBINPUT_EVENT_TOUCH_DOWN || type == LIBINPUT_EVENT_TOUCH_MOTION) {
+		double x =
+			libinput_event_touch_get_x_transformed(t, opts->screen_width);
+		double y =
+			libinput_event_touch_get_y_transformed(t, opts->screen_height);
 		double xmm = libinput_event_touch_get_x(t);
 		double ymm = libinput_event_touch_get_y(t);
 
 		pos = strdup_printf(" %5.2f/%5.2f (%5.2f/%5.2fmm)", x, y, xmm, ymm);
 	}
 
-	char *str = strdup_printf("%s\t%s%s",
-				  time,
-				  slot ? slot : "",
-				  pos ? pos : "");
-	free(slot);
-	free(pos);
-	return str;
+	return strdup_printf("%s\t%s%s", time, slot ? slot : "", pos ? pos : "");
 }
 
 static char *
-print_gesture_event_without_coords(struct libinput_event *ev, const struct libinput_print_options *opts)
+print_gesture_event_without_coords(struct libinput_event *ev,
+				   const struct libinput_print_options *opts)
 {
 	struct libinput_event_gesture *t = libinput_event_get_gesture_event(ev);
 	int finger_count = libinput_event_gesture_get_finger_count(t);
@@ -705,14 +785,18 @@ print_gesture_event_without_coords(struc
 	if (type == LIBINPUT_EVENT_GESTURE_SWIPE_END ||
 	    type == LIBINPUT_EVENT_GESTURE_PINCH_END ||
 	    type == LIBINPUT_EVENT_GESTURE_HOLD_END)
-	    cancelled = libinput_event_gesture_get_cancelled(t);
+		cancelled = libinput_event_gesture_get_cancelled(t);
 
 	print_event_time(time, opts->start_time, libinput_event_gesture_get_time(t));
-	return strdup_printf("%s\t%d%s", time, finger_count, cancelled ? " cancelled" : "");
+	return strdup_printf("%s\t%d%s",
+			     time,
+			     finger_count,
+			     cancelled ? " cancelled" : "");
 }
 
 static char *
-print_gesture_event_with_coords(struct libinput_event *ev, const struct libinput_print_options *opts)
+print_gesture_event_with_coords(struct libinput_event *ev,
+				const struct libinput_print_options *opts)
 {
 	struct libinput_event_gesture *t = libinput_event_get_gesture_event(ev);
 	double dx = libinput_event_gesture_get_dx(t);
@@ -720,36 +804,37 @@ print_gesture_event_with_coords(struct l
 	double dx_unaccel = libinput_event_gesture_get_dx_unaccelerated(t);
 	double dy_unaccel = libinput_event_gesture_get_dy_unaccelerated(t);
 	char time[16];
-	char *pinch = NULL;
+	_autofree_ char *pinch = NULL;
 
 	print_event_time(time, opts->start_time, libinput_event_gesture_get_time(t));
 
-	if (libinput_event_get_type(ev) ==
-	    LIBINPUT_EVENT_GESTURE_PINCH_UPDATE) {
+	if (libinput_event_get_type(ev) == LIBINPUT_EVENT_GESTURE_PINCH_UPDATE) {
 		double scale = libinput_event_gesture_get_scale(t);
 		double angle = libinput_event_gesture_get_angle_delta(t);
 
 		pinch = strdup_printf(" %5.2f @ %5.2f", scale, angle);
 	}
 
-	char *str = strdup_printf("%s\t%d %5.2f/%5.2f (%5.2f/%5.2f unaccelerated)%s",
-				  time,
-				  libinput_event_gesture_get_finger_count(t),
-				  dx, dy, dx_unaccel, dy_unaccel,
-				  pinch ? pinch : "");
-	free(pinch);
-	return str;
+	return strdup_printf("%s\t%d %5.2f/%5.2f (%5.2f/%5.2f unaccelerated)%s",
+			     time,
+			     libinput_event_gesture_get_finger_count(t),
+			     dx,
+			     dy,
+			     dx_unaccel,
+			     dy_unaccel,
+			     pinch ? pinch : "");
 }
 
 static char *
-print_tablet_pad_button_event(struct libinput_event *ev, const struct libinput_print_options *opts)
+print_tablet_pad_button_event(struct libinput_event *ev,
+			      const struct libinput_print_options *opts)
 {
 	struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
 	struct libinput_tablet_pad_mode_group *group;
 	enum libinput_button_state state;
 	unsigned int button, mode;
 	char time[16];
-	char *toggle = NULL;
+	const char *toggle = NULL;
 
 	print_event_time(time, opts->start_time, libinput_event_tablet_pad_get_time(p));
 
@@ -763,13 +848,15 @@ print_tablet_pad_button_event(struct lib
 
 	return strdup_printf("%3d %s (mode %d)%s",
 			     button,
-			     state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released",
+			     state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed"
+								    : "released",
 			     mode,
 			     toggle ? toggle : "");
 }
 
 static char *
-print_tablet_pad_ring_event(struct libinput_event *ev, const struct libinput_print_options *opts)
+print_tablet_pad_ring_event(struct libinput_event *ev,
+			    const struct libinput_print_options *opts)
 {
 	struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
 	const char *source = NULL;
@@ -797,7 +884,8 @@ print_tablet_pad_ring_event(struct libin
 }
 
 static char *
-print_tablet_pad_strip_event(struct libinput_event *ev, const struct libinput_print_options *opts)
+print_tablet_pad_strip_event(struct libinput_event *ev,
+			     const struct libinput_print_options *opts)
 {
 	struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
 	const char *source = NULL;
@@ -825,7 +913,8 @@ print_tablet_pad_strip_event(struct libi
 }
 
 static char *
-print_tablet_pad_key_event(struct libinput_event *ev, const struct libinput_print_options *opts)
+print_tablet_pad_key_event(struct libinput_event *ev,
+			   const struct libinput_print_options *opts)
 {
 	struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
 	enum libinput_key_state state;
@@ -848,11 +937,13 @@ print_tablet_pad_key_event(struct libinp
 			     time,
 			     keyname,
 			     key,
-			     state == LIBINPUT_KEY_STATE_PRESSED ? "pressed" : "released");
+			     state == LIBINPUT_KEY_STATE_PRESSED ? "pressed"
+								 : "released");
 }
 
 static char *
-print_tablet_pad_dial_event(struct libinput_event *ev, const struct libinput_print_options *opts)
+print_tablet_pad_dial_event(struct libinput_event *ev,
+			    const struct libinput_print_options *opts)
 {
 	struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
 	unsigned int mode;
@@ -900,14 +991,18 @@ libinput_event_to_str(struct libinput_ev
 		      const struct libinput_print_options *options)
 {
 	enum libinput_event_type type = libinput_event_get_type(ev);
-	char *event_header = print_event_header(ev, event_repeat_count);
-	char *event_str = NULL;
+	_autofree_ char *event_header = print_event_header(ev, event_repeat_count);
+	_autofree_ char *event_str = NULL;
 
 	struct libinput_print_options opts = {
 		.start_time = options ? options->start_time : 0,
 		.show_keycodes = options ? options->show_keycodes : true,
-		.screen_width = (options && options->screen_width > 0) ? options->screen_width : 100,
-		.screen_height = (options && options->screen_height > 0) ? options->screen_height : 100,
+		.screen_width = (options && options->screen_width > 0)
+					? options->screen_width
+					: 100,
+		.screen_height = (options && options->screen_height > 0)
+					 ? options->screen_height
+					 : 100,
 
 	};
 
@@ -933,7 +1028,7 @@ libinput_event_to_str(struct libinput_ev
 		event_str = print_pointer_button_event(ev, &opts);
 		break;
 	case LIBINPUT_EVENT_POINTER_AXIS:
-		/* ignore */
+		event_str = print_legacy_pointer_axis_event(ev, &opts);
 		break;
 	case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
 	case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
@@ -1005,8 +1100,5 @@ libinput_event_to_str(struct libinput_ev
 		break;
 	}
 
-	char *str = strdup_printf("%s %s", event_header, event_str);
-	free(event_header);
-	free(event_str);
-	return str;
+	return strdup_printf("%s %s", event_header, event_str);
 }
diff -pruN 1.28.1-1/src/util-list.c 1.30.0-1/src/util-list.c
--- 1.28.1-1/src/util-list.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-list.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,11 +26,40 @@
 #include "config.h"
 
 #include <assert.h>
-#include <stddef.h>
 #include <stdbool.h>
+#include <stddef.h>
 
 #include "util-list.h"
 
+#include "libinput-util.h"
+
+#if 0
+static void
+list_debug(struct list *list)
+{
+	struct list *head = list;
+
+	assert((head->next != NULL && head->prev != NULL) ||
+	       !"list->next|prev is NULL, possibly missing list_init()");
+
+	trace("list %p:", head);
+	trace("  head->next %p", head->next);
+	trace("  head->prev %p", head->prev);
+	struct list *elm = head->next;
+	int index = 0;
+	while (elm != head) {
+		trace("..%-3d: %p->next %p",
+		      index, elm, elm->next);
+		trace("..%-3d: %p->prev %p",
+		      index, elm, elm->prev);
+		elm = elm->next;
+		index++;
+		if (index > 20)
+			break;
+	}
+}
+#endif
+
 void
 list_init(struct list *list)
 {
@@ -66,6 +95,45 @@ list_append(struct list *list, struct li
 }
 
 void
+list_chain(struct list *list, struct list *other)
+{
+	assert((list->next != NULL && list->prev != NULL) ||
+	       !"list->next|prev is NULL, possibly missing list_init()");
+	assert((other->next != NULL && other->prev != NULL) ||
+	       !"other->next|prev is NULL, possibly missing list_init()");
+
+	if (list_empty(other))
+		return;
+
+	struct list *last = list->prev;
+	struct list *first = other->next;
+
+	first->prev = last;
+	last->next = first;
+
+	list->prev->next = other->next;
+	other->next->prev = list->prev;
+	other->prev->next = list;
+	list->prev = other->prev;
+	list_init(other);
+}
+
+size_t
+list_length(const struct list *list)
+{
+	assert((list->next != NULL && list->prev != NULL) ||
+	       !"list->next|prev is NULL, possibly missing list_init()");
+
+	size_t count = 0;
+	const struct list *elm;
+
+	for (elm = list->next; elm != list; elm = elm->next)
+		count++;
+
+	return count;
+}
+
+void
 list_remove(struct list *elm)
 {
 	assert((elm->next != NULL && elm->prev != NULL) ||
diff -pruN 1.28.1-1/src/util-list.h 1.30.0-1/src/util-list.h
--- 1.28.1-1/src/util-list.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-list.h	2025-11-25 03:40:43.000000000 +0000
@@ -69,16 +69,56 @@ struct list {
  * head. This function *must not* be called for a node to be added to a
  * list.
  */
-void list_init(struct list *list);
+void
+list_init(struct list *list);
 
 /**
  * Insert an element at the front of the list
  */
-void list_insert(struct list *list, struct list *elm);
+void
+list_insert(struct list *list, struct list *elm);
 /**
  * Append an element to the  back of the list
  */
-void list_append(struct list *list, struct list *elm);
+void
+list_append(struct list *list, struct list *elm);
+
+/**
+ * Chain other onto list, resetting other to be the empty list.
+ */
+void
+list_chain(struct list *list, struct list *other);
+
+size_t
+list_length(const struct list *list);
+
+/**
+ * Takes the given pointer ands inserts it to the list with the pointer's field.
+ * The pointer is reset to NULL. Use this to prevent automatic cleanup
+ * of the pointer type.
+ *
+ * @code
+ *	list_take_insert(&f->list_of_bars, b, link);
+ * @endcode
+ */
+#define list_take_insert(list_, ptr_, field_) do {\
+	list_insert(list_, &(ptr_)->field_); \
+	ptr_ = NULL; \
+} while(0)
+
+/**
+ * Takes the given pointer ands adds it to the list with the pointer's field.
+ * The pointer is reset to NULL. Use this to prevent automatic cleanup
+ * of the pointer type.
+ *
+ * @code
+ *	list_take_append(&f->list_of_bars, b, link);
+ * @endcode
+ */
+#define list_take_append(list_, ptr_, field_) do {\
+	list_append(list_, &(ptr_)->field_); \
+	ptr_ = NULL; \
+} while(0)
 
 /**
  * Remove an element from list.
@@ -87,11 +127,13 @@ void list_append(struct list *list, stru
  * whether the list node has already been removed.
  *
  */
-void list_remove(struct list *elm);
+void
+list_remove(struct list *elm);
 /**
  * Returns true if the given list head is an empty list.
  */
-bool list_empty(const struct list *list);
+bool
+list_empty(const struct list *list);
 
 /**
  * Return the 'type' parent container struct of 'ptr' of which
@@ -160,6 +202,51 @@ bool list_empty(const struct list *list)
 	container_of((head)->next, container_type, member)
 
 /**
+ * Given a list 'head', return the last entry of type 'pos' that has a
+ * member 'link'.
+ *
+ * The 'pos' argument is solely used to determine the type be returned and
+ * not modified otherwise. It is common to use the same pointer that the
+ * return value of list_last_entry() is assigned to, for example:
+ *
+ * @code
+ *     struct foo {
+ *        struct list list_of_bars;
+ *     };
+ *
+ *     struct bar {
+ *         struct list link;
+ *     }
+ *
+ *     struct foo *f = get_a_foo();
+ *     struct bar *b = 0;  // initialize to avoid static analysis errors
+ *     b = list_last_entry(&f->list_of_bars, b, link);
+ * @endcode
+ */
+#define list_last_entry(head, pointer_of_type, member)				\
+	container_of((head)->prev, __typeof__(*pointer_of_type), member)
+
+/**
+ * Given a list 'head', return the last entry of type 'container_type' that
+ * has a member 'link'.
+ *
+ * @code
+ *     struct foo {
+ *        struct list list_of_bars;
+ *     };
+ *
+ *     struct bar {
+ *         struct list link;
+ *     }
+ *
+ *     struct foo *f = get_a_foo();
+ *     struct bar *b = list_last_entry(&f->list_of_bars, struct bar, link);
+ * @endcode
+ */
+#define list_last_entry_by_type(head, container_type, member)		\
+	container_of((head)->prev, container_type, member)
+
+/**
  * Iterate through the list.
  *
  * @code
diff -pruN 1.28.1-1/src/util-macros.h 1.30.0-1/src/util-macros.h
--- 1.28.1-1/src/util-macros.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-macros.h	2025-11-25 03:40:43.000000000 +0000
@@ -27,6 +27,10 @@
 
 #include "config.h"
 
+#ifndef HAVE_C23_AUTO
+#define auto __auto_type
+#endif
+
 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
 /**
  * Iterate through the array _arr, assigning the variable elem to each
@@ -39,27 +43,65 @@
 
 #define min(a, b) (((a) < (b)) ? (a) : (b))
 #define max(a, b) (((a) > (b)) ? (a) : (b))
+#define clamp(v, lo, hi) min((hi), max((lo), (v)))
 
-#define ANSI_HIGHLIGHT		"\x1B[0;1;39m"
+#define ANSI_BOLD		"\x1B[0;1m"
 #define ANSI_RED		"\x1B[0;31m"
 #define ANSI_GREEN		"\x1B[0;32m"
 #define ANSI_YELLOW		"\x1B[0;33m"
 #define ANSI_BLUE		"\x1B[0;34m"
 #define ANSI_MAGENTA		"\x1B[0;35m"
 #define ANSI_CYAN		"\x1B[0;36m"
-#define ANSI_BRIGHT_RED		"\x1B[0;31;1m"
-#define ANSI_BRIGHT_GREEN	"\x1B[0;32;1m"
-#define ANSI_BRIGHT_YELLOW	"\x1B[0;33;1m"
-#define ANSI_BRIGHT_BLUE	"\x1B[0;34;1m"
-#define ANSI_BRIGHT_MAGENTA	"\x1B[0;35;1m"
-#define ANSI_BRIGHT_CYAN	"\x1B[0;36;1m"
+#define ANSI_WHITE		"\x1B[0;37m"
+#define ANSI_BRIGHT_RED		"\x1B[0;91m"
+#define ANSI_BRIGHT_GREEN	"\x1B[0;92m"
+#define ANSI_BRIGHT_YELLOW	"\x1B[0;93m"
+#define ANSI_BRIGHT_BLUE	"\x1B[0;94m"
+#define ANSI_BRIGHT_MAGENTA	"\x1B[0;95m"
+#define ANSI_BRIGHT_CYAN	"\x1B[0;96m"
+#define ANSI_BRIGHT_WHITE	"\x1B[0;97m"
+#define ANSI_BOLD_RED		"\x1B[0;1;31m"
+#define ANSI_BOLD_GREEN		"\x1B[0;1;32m"
+#define ANSI_BOLD_YELLOW	"\x1B[0;1;33m"
+#define ANSI_BOLD_BLUE		"\x1B[0;1;34m"
+#define ANSI_BOLD_MAGENTA	"\x1B[0;1;35m"
+#define ANSI_BOLD_CYAN		"\x1B[0;1;36m"
+#define ANSI_BOLD_WHITE		"\x1B[0;1;37m"
+#define ANSI_BOLD_BRIGHT_RED	"\x1B[0;1;91m"
+#define ANSI_BOLD_BRIGHT_GREEN	"\x1B[0;1;92m"
+#define ANSI_BOLD_BRIGHT_YELLOW	"\x1B[0;1;93m"
+#define ANSI_BOLD_BRIGHT_BLUE	"\x1B[0;1;94m"
+#define ANSI_BOLD_BRIGHT_MAGENTA "\x1B[0;1;95m"
+#define ANSI_BOLD_BRIGHT_CYAN	"\x1B[0;1;96m"
+#define ANSI_BOLD_BRIGHT_WHITE	"\x1B[0;1;97m"
 #define ANSI_NORMAL		"\x1B[0m"
 
+#define ANSI_BG_RED		"\x1B[0;41m"
+#define ANSI_BG_GREEN		"\x1B[0;42m"
+#define ANSI_BG_YELLOW		"\x1B[0;43m"
+#define ANSI_BG_BLUE		"\x1B[0;44m"
+#define ANSI_BG_MAGENTA		"\x1B[0;45m"
+#define ANSI_BG_CYAN		"\x1B[0;46m"
+#define ANSI_BG_WHITE		"\x1B[0;47m"
+#define ANSI_BG_BRIGHT_RED	"\x1B[0;101m"
+#define ANSI_BG_BRIGHT_GREEN	"\x1B[0;102m"
+#define ANSI_BG_BRIGHT_YELLOW	"\x1B[0;103m"
+#define ANSI_BG_BRIGHT_BLUE	"\x1B[0;104m"
+#define ANSI_BG_BRIGHT_MAGENTA	"\x1B[0;105m"
+#define ANSI_BG_BRIGHT_CYAN	"\x1B[0;106m"
+#define ANSI_BG_BRIGHT_WHITE    "\x1B[0;107m"
+
 #define ANSI_UP			"\x1B[%dA"
 #define ANSI_DOWN		"\x1B[%dB"
 #define ANSI_RIGHT		"\x1B[%dC"
 #define ANSI_LEFT		"\x1B[%dD"
 
+#define ANSI_RGB(r, g, b)       "\x1B[38;2;" #r  ";" #g ";" #b "m"
+#define ANSI_RGB_BG(r, g, b)    "\x1B[48;2;" #r  ";" #g ";" #b "m"
+
+#define ANSI_CLEAR_LINE         "\x1B[2K"
+#define ANSI_CLEAR_EOL          "\x1B[0K"
+
 #define CASE_RETURN_STRING(a) case a: return #a
 
 /**
@@ -68,7 +110,73 @@
  * will produce:
  *	int foo_123;
  */
-#define CONCAT2(X,Y) X##Y
-#define CONCAT(X,Y) CONCAT2(X,Y)
+#define CONCAT2(X, Y) X##Y
+#define CONCAT(X, Y) CONCAT2(X,Y)
 
+#define _unused_ __attribute__((unused))
 #define _fallthrough_ __attribute__((fallthrough))
+#define _nonnull_(...) __attribute__((nonnull(__VA_ARGS__)))
+
+/* Returns the number of macro arguments, this expands
+ * _VARIABLE_MACRO_NARGS(a, b, c) to NTH_ARG(a, b, c, 15, 14, 13, .... 4, 3, 2, 1).
+ * _VARIABLE_MACRO_NTH_ARG always returns the 16th argument which in our case is 3.
+ *
+ * If we want more than 16 values _VARIABLE_MACRO_COUNTDOWN and
+ * _VARIABLE_MACRO_NTH_ARG both need to be updated.
+ */
+#define _VARIABLE_MACRO_NARGS(...)  _VARIABLE_MACRO_NARGS1(__VA_ARGS__, _VARIABLE_MACRO_COUNTDOWN)
+#define _VARIABLE_MACRO_NARGS1(...) _VARIABLE_MACRO_NTH_ARG(__VA_ARGS__)
+
+/* Add to this if we need more than 16 args */
+#define _VARIABLE_MACRO_COUNTDOWN \
+	15, 14, 13, 12, 11, 10, 9, 8, 7,  6,  5,  4,  3,  2, 1, 0
+
+/* Return the 16th argument passed in. See _VARIABLE_MACRO_NARGS above for usage.
+ * Note this is 1-indexed.
+ */
+#define _VARIABLE_MACRO_NTH_ARG(_1,                                                    \
+				_2,                                                    \
+				_3,                                                    \
+				_4,                                                    \
+				_5,                                                    \
+				_6,                                                    \
+				_7,                                                    \
+				_8,                                                    \
+				_9,                                                    \
+				_10,                                                   \
+				_11,                                                   \
+				_12,                                                   \
+				_13,                                                   \
+				_14,                                                   \
+				_15,                                                   \
+				N,                                                     \
+				...) N
+
+/* Defines a different expansion of macros depending on the
+ * number of arguments, e.g. it turns
+ * VARIABLE_MACRO(_ARG, a, b, c) into _ARG3(a, b, c)
+ *
+ * This can be used to have custom macros that expand to different things
+ * depending on the number of arguments. This example converts a
+ * single macro argument into value + stringify, two arguments into
+ * first and second argument.
+ *
+ *     #define _ARG1(_1)     _1, #_1,
+ *     #define _ARG2(_1, _2) _1, _2,
+ *
+ *     #define MYMACRO(...) _VARIABLE_MACRO(_ARG, __VA_ARGS__)
+ *
+ *     static void foo(int value, char *name) { printf("%d: %s\n", value, name); }
+ *
+ *     int main(void) {
+ *         foo(MYMACRO(0));           // prints "0: 0"
+ *         foo(MYMACRO(0, "zero"));   // prints "0: zero"
+ *         return 0;
+ *     }
+ *
+ * The first argument to VARIABLE_MACRO defines the prefix of the
+ * expander macros (here _ARG -> _ARG0, _ARG1, ...). These need to be defined
+ * in the caller for the number of arguments accepted
+ * (up to _VARIABLE_MACRO_COUNTDOWN args).
+ */
+#define _VARIABLE_MACRO(func, ...) CONCAT(func, _VARIABLE_MACRO_NARGS(__VA_ARGS__)) (__VA_ARGS__)
diff -pruN 1.28.1-1/src/util-matrix.h 1.30.0-1/src/util-matrix.h
--- 1.28.1-1/src/util-matrix.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-matrix.h	2025-11-25 03:40:43.000000000 +0000
@@ -26,9 +26,9 @@
 
 #include "config.h"
 
-#include <string.h>
-#include <stdbool.h>
 #include <math.h>
+#include <stdbool.h>
+#include <string.h>
 
 struct matrix {
 	float val[3][3]; /* [row][col] */
@@ -101,21 +101,13 @@ matrix_init_rotate(struct matrix *m, int
 static inline bool
 matrix_is_identity(const struct matrix *m)
 {
-	return (m->val[0][0] == 1 &&
-		m->val[0][1] == 0 &&
-		m->val[0][2] == 0 &&
-		m->val[1][0] == 0 &&
-		m->val[1][1] == 1 &&
-		m->val[1][2] == 0 &&
-		m->val[2][0] == 0 &&
-		m->val[2][1] == 0 &&
-		m->val[2][2] == 1);
+	return (m->val[0][0] == 1 && m->val[0][1] == 0 && m->val[0][2] == 0 &&
+		m->val[1][0] == 0 && m->val[1][1] == 1 && m->val[1][2] == 0 &&
+		m->val[2][0] == 0 && m->val[2][1] == 0 && m->val[2][2] == 1);
 }
 
 static inline void
-matrix_mult(struct matrix *dest,
-	    const struct matrix *m1,
-	    const struct matrix *m2)
+matrix_mult(struct matrix *dest, const struct matrix *m1, const struct matrix *m2)
 {
 	struct matrix m; /* allow for dest == m1 or dest == m2 */
 	int row, col, i;
diff -pruN 1.28.1-1/src/util-mem.h 1.30.0-1/src/util-mem.h
--- 1.28.1-1/src/util-mem.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/util-mem.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,188 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2020 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:
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static inline void *
+zalloc(size_t size)
+{
+	void *p;
+
+	/* We never need to alloc anything more than 1,5 MB so we can assume
+	 * if we ever get above that something's going wrong */
+	if (size > 1536 * 1024)
+		assert(!"bug: internal malloc size limit exceeded");
+
+	p = calloc(1, size);
+	if (!p)
+		abort();
+
+	return p;
+}
+
+/**
+ * Use: _cleanup_(somefunction) struct foo *bar;
+ */
+#define _cleanup_(_x) __attribute__((cleanup(_x)))
+
+/**
+ * Use: _unref_(foo) struct foo *bar;
+ *
+ * This requires foo_unrefp() to be present, use DEFINE_UNREF_CLEANUP_FUNC.
+ */
+#define _unref_(_type) __attribute__((cleanup(_type##_unrefp))) struct _type
+
+/**
+ * Use: _destroy_(foo) struct foo *bar;
+ *
+ * This requires foo_destroyp() to be present, use DEFINE_UNREF_CLEANUP_FUNC.
+ */
+#define _destroy_(_type) __attribute__((cleanup(_type##_destroyp))) struct _type
+
+/**
+ * Use: _free_(foo) struct foo *bar;
+ *
+ * This requires foo_freep() to be present, use DEFINE_FREE_CLEANUP_FUNC.
+ */
+#define _free_(_type) __attribute__((cleanup(_type##_freep))) struct _type
+
+static inline void
+_free_ptr_(void *p)
+{
+	free(*(void **)p);
+}
+/**
+ * Use: _autofree_ char *data;
+ */
+#define _autofree_ _cleanup_(_free_ptr_)
+
+static inline void
+_close_fd_(int *fd)
+{
+	if (*fd != -1)
+		close(*fd);
+}
+
+/**
+ * Use: _autoclose_ int fd = open(...);
+ */
+#define _autoclose_ _cleanup_(_close_fd_)
+
+static inline void
+_close_file_(FILE **fp)
+{
+	if (*fp)
+		fclose(*fp);
+}
+
+/**
+ * Use: _autofclose_ FILE *fp = fopen(...);
+ */
+#define _autofclose_ _cleanup_(_close_file_)
+
+/**
+ * Use:
+ * DEFINE_TRIVIAL_CLEANUP_FUNC(struct foo *, foo_unref)
+ * _cleanup_(foo_unrefp) struct foo *bar;
+ */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC(_type, _func)		\
+	static inline void _func##p(_type *_p) {		\
+		if (*_p)					\
+			_func(*_p);				\
+	}							\
+	struct __useless_struct_to_allow_trailing_semicolon__
+
+/**
+ * Define a cleanup function for the struct type foo with a matching
+ * foo_unref(). Use:
+ * DEFINE_UNREF_CLEANUP_FUNC(foo)
+ * _unref_(foo) struct foo *bar;
+ */
+#define DEFINE_UNREF_CLEANUP_FUNC(_type)		\
+	static inline void _type##_unrefp(struct _type **_p) {	\
+		if (*_p)					\
+			_type##_unref(*_p);			\
+	}							\
+	struct __useless_struct_to_allow_trailing_semicolon__
+
+/**
+ * Define a cleanup function for the struct type foo with a matching
+ * foo_destroy(). Use:
+ * DEFINE_DESTROY_CLEANUP_FUNC(foo)
+ * _destroy_(foo) struct foo *bar;
+ */
+#define DEFINE_DESTROY_CLEANUP_FUNC(_type)		\
+	static inline void _type##_destroyp(struct _type **_p) {\
+		if (*_p)					\
+			_type##_destroy(*_p);			\
+	}							\
+	struct __useless_struct_to_allow_trailing_semicolon__
+
+/**
+ * Define a cleanup function for the struct type foo with a matching
+ * foo_free(). Use:
+ * DEFINE_FREE_CLEANUP_FUNC(foo)
+ * _free_(foo) struct foo *bar;
+ */
+#define DEFINE_FREE_CLEANUP_FUNC(_type)		\
+	static inline void _type##_freep(struct _type **_p) {\
+		if (*_p)					\
+			_type##_free(*_p);			\
+	}							\
+	struct __useless_struct_to_allow_trailing_semicolon__
+
+static inline void *
+_steal(void *ptr)
+{
+	void **original = (void **)ptr;
+	void *swapped = *original;
+	*original = NULL;
+	return swapped;
+}
+
+/**
+ * Resets the pointer content and resets the data to NULL.
+ * This circumvents _cleanup_ handling for that pointer.
+ * Use:
+ *   _cleanup_free_ char *data = malloc();
+ *   return steal(&data);
+ *
+ */
+#define steal(ptr_) \
+  (typeof(*ptr_))_steal(ptr_)
+
+static inline int
+steal_fd(int *fd)
+{
+	int copy = *fd;
+	*fd = -1;
+	return copy;
+}
diff -pruN 1.28.1-1/src/util-multivalue.h 1.30.0-1/src/util-multivalue.h
--- 1.28.1-1/src/util-multivalue.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-multivalue.h	2025-11-25 03:40:43.000000000 +0000
@@ -24,8 +24,10 @@
 
 #pragma once
 
-#include <stdint.h>
+#include "config.h"
+
 #include <stdbool.h>
+#include <stdint.h>
 
 #include "util-strings.h"
 
@@ -46,20 +48,33 @@ static inline void
 multivalue_extract(const struct multivalue *v, void *ptr)
 {
 	/* ignore false positives from gcc:
-	 * ../src/util-multivalue.h:52:33: warning: array subscript ‘double[0]’ is partly outside array bounds of ‘int32_t[1]’ {aka ‘int[1]’} [-Warray-bounds=]
+	 * ../src/util-multivalue.h:52:33: warning: array subscript ‘double[0]’ is
+	 * partly outside array bounds of ‘int32_t[1]’ {aka ‘int[1]’} [-Warray-bounds=]
 	 *  52 |         case 'd': *(double*)ptr = v->value.d; break;
 	 */
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Warray-bounds"
 	switch (v->type) {
-	case 'b': *(bool *)ptr = v->value.b; break;
-	case 'c': *(char *)ptr = v->value.c; break;
-	case 'u': *(uint32_t *)ptr = v->value.u; break;
-	case 'i': *(int32_t *)ptr = v->value.i; break;
-	case 'd': *(double *)ptr = v->value.d; break;
-	case 's': *(const char **)ptr = v->value.s; break;
+	case 'b':
+		*(bool *)ptr = v->value.b;
+		break;
+	case 'c':
+		*(char *)ptr = v->value.c;
+		break;
+	case 'u':
+		*(uint32_t *)ptr = v->value.u;
+		break;
+	case 'i':
+		*(int32_t *)ptr = v->value.i;
+		break;
+	case 'd':
+		*(double *)ptr = v->value.d;
+		break;
+	case 's':
+		*(const char **)ptr = v->value.s;
+		break;
 	default:
-		  abort();
+		abort();
 	}
 #pragma GCC diagnostic pop
 }
@@ -81,9 +96,7 @@ multivalue_copy(const struct multivalue
 static inline struct multivalue
 multivalue_new_string(const char *str)
 {
-	struct multivalue v = {
-		.type = 's'
-	};
+	struct multivalue v = { .type = 's' };
 
 	assert(strlen(str) < sizeof(v.value.s));
 
diff -pruN 1.28.1-1/src/util-newtype.h 1.30.0-1/src/util-newtype.h
--- 1.28.1-1/src/util-newtype.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/util-newtype.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,111 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include "util-macros.h"
+
+/**
+ * This is a C version of the Rust newtypes in the
+ * form struct Foo(u32);
+ *
+ * Use: DECLARE_NEWTYPE(foo, int)
+ *
+ * Defines a single-value struct called foo_t
+ * with the following helper functions:
+ *
+ * - int foo_as_int(foo_t f);
+ * - int foo(foo f);
+ * - foo_t foo_from_int(int);
+ * - foo_t foo_copy(foo_t f);
+ * - foo_t foo_min(foo_t a, foo b);
+ * - foo_t foo_max(foo_t a, foo b);
+ * - foo_t foo_cmp(foo_t a, foo b);
+ * - bool foo_eq(foo_t a, int b);
+ * - bool foo_ne(foo_t a, int b);
+ * - bool foo_le(foo_t a, int b);
+ * - bool foo_lt(foo_t a, int b);
+ * - bool foo_ge(foo_t a, int b);
+ * - bool foo_gt(foo_t a, int b);
+ *
+ * Since all the structs are single-value the provided functions don't
+ * use pointers but pass the struct around as value. The compiler
+ * hopefully optimizes things so this is the same as passing ints around
+ * but having some better type-safety.
+ *
+ * For example, this logic error is no longer possible using newtypes:
+ * ```
+ * double cm_to_inches(int cm) { return cm / 2.54; }
+ *
+ * struct person {
+ *      int age;
+ *      int height;
+ * };
+ * struct person p = { .age = 20, .height = 180 };
+ * double inches = cm_to_inches(p.age);
+ * ```
+ * With newtypes this would be:
+ * ```
+ * DECLARE_NEWTYPE(year, int);
+ * DECLARE_NEWTYPE(cm, int);
+ * DECLARE_NEWTYPE(in, double);
+ *
+ * in_t cm_to_inches(cm_t cm) { return in_from_double(cm_as_int(cm) / 2.54); }
+ *
+ * struct person {
+ *      year_t age;
+ *      cm_t height;
+ * };
+ * struct person p = { .age = year_from_int(20), .height = cm_from_int(180) };
+ * in_t inches = cm_to_inches(p.age); // Compiler error!
+ * in_t inches = cm_to_inches(p.height); // Yay
+ * ```
+ * And a side-effect is that the units are documented as part of the type.
+ */
+#define DECLARE_NEWTYPE(name_, type_) \
+	typedef struct { \
+		type_ v; \
+	} name_##_t; \
+	\
+	static inline name_##_t name_##_from_##type_(type_ v) { return (name_##_t) { .v = v }; }; \
+	static inline type_ name_##_as_##type_(name_##_t name_) { return name_.v; }; \
+	static inline type_ name_(name_##_t name_) { return name_##_as_##type_(name_); } \
+	static inline name_##_t name_##_copy(name_##_t name_) { return name_##_from_##type_(name_.v); }; \
+	static inline name_##_t name_##_min(name_##_t a, name_##_t b) { \
+		return name_##_from_##type_(min(a.v, b.v)); \
+	}; \
+	static inline name_##_t name_##_max(name_##_t a, name_##_t b) { \
+		return name_##_from_##type_(max(a.v, b.v)); \
+	}; \
+	static inline int name_##_cmp(name_##_t a, name_##_t b) { \
+		return a.v < b.v ? -1 : (a.v > b.v ? 1 : 0); \
+	}; \
+	static inline int name_##_eq(name_##_t a, type_ b) { return a.v == b; }\
+	static inline int name_##_ne(name_##_t a, type_ b) { return a.v != b; }\
+	static inline int name_##_le(name_##_t a, type_ b) { return a.v <= b; }\
+	static inline int name_##_lt(name_##_t a, type_ b) { return a.v < b; }\
+	static inline int name_##_ge(name_##_t a, type_ b) { return a.v >= b; }\
+	static inline int name_##_gt(name_##_t a, type_ b) { return a.v > b; }\
+	struct __useless_struct_to_allow_trailing_semicolon__
diff -pruN 1.28.1-1/src/util-prop-parsers.c 1.30.0-1/src/util-prop-parsers.c
--- 1.28.1-1/src/util-prop-parsers.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-prop-parsers.c	2025-11-25 03:40:43.000000000 +0000
@@ -109,7 +109,7 @@ parse_mouse_wheel_click_count_property(c
 	if (!safe_atoi(prop, &count) || abs(count) > 360)
 		return 0;
 
-        return count;
+	return count;
 }
 
 /**
@@ -133,7 +133,7 @@ parse_mouse_wheel_click_angle_property(c
 	if (!safe_atoi(prop, &angle) || abs(angle) > 360)
 		return 0;
 
-        return angle;
+	return angle;
 }
 
 /**
@@ -234,8 +234,7 @@ parse_switch_reliability_property(const
  * @return true on success, false otherwise
  */
 bool
-parse_tpkbcombo_layout_poperty(const char *prop,
-			       enum tpkbcombo_layout *layout)
+parse_tpkbcombo_layout_poperty(const char *prop, enum tpkbcombo_layout *layout)
 {
 	if (!prop)
 		return false;
@@ -316,11 +315,8 @@ parse_evcode_string(const char *s, int *
 			const char *str;
 			int type;
 		} map[] = {
-			{ "KEY_", EV_KEY },
-			{ "BTN_", EV_KEY },
-			{ "ABS_", EV_ABS },
-			{ "REL_", EV_REL },
-			{ "SW_", EV_SW },
+			{ "KEY_", EV_KEY }, { "BTN_", EV_KEY }, { "ABS_", EV_ABS },
+			{ "REL_", EV_REL }, { "SW_", EV_SW },
 		};
 		bool found = false;
 
@@ -384,8 +380,12 @@ parse_evcode_property(const char *prop,
 		bool enable;
 
 		switch (*s) {
-		case '+': enable = true; break;
-		case '-': enable = false; break;
+		case '+':
+			enable = true;
+			break;
+		case '-':
+			enable = false;
+			break;
 		default:
 			goto out;
 		}
@@ -399,13 +399,13 @@ parse_evcode_property(const char *prop,
 				goto out;
 		} else {
 			int consumed;
-			char stype[13] = {0}; /* EV_FF_STATUS + '\0' */
+			char stype[13] = { 0 }; /* EV_FF_STATUS + '\0' */
 
 			if (sscanf(s, "%12[A-Z_]:%x%n", stype, &code, &consumed) != 2 ||
 			    strlen(s) != (size_t)consumed ||
 			    (type = libevdev_event_type_from_name(stype)) == -1 ||
 			    code < 0 || code > libevdev_event_type_get_max(type))
-			    goto out;
+				goto out;
 		}
 
 		evs[idx].type = type;
@@ -435,7 +435,9 @@ out:
  * On success, props contains nprops elements.
  */
 bool
-parse_input_prop_property(const char *prop, struct input_prop *props_out, size_t *nprops)
+parse_input_prop_property(const char *prop,
+			  struct input_prop *props_out,
+			  size_t *nprops)
 {
 	bool rc = false;
 	struct input_prop props[INPUT_PROP_CNT]; /* doubling up on quirks is a bug */
@@ -452,8 +454,12 @@ parse_input_prop_property(const char *pr
 		bool enable;
 
 		switch (*s) {
-		case '+': enable = true; break;
-		case '-': enable = false; break;
+		case '+':
+			enable = true;
+			break;
+		case '-':
+			enable = false;
+			break;
 		default:
 			goto out;
 		}
diff -pruN 1.28.1-1/src/util-prop-parsers.h 1.30.0-1/src/util-prop-parsers.h
--- 1.28.1-1/src/util-prop-parsers.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-prop-parsers.h	2025-11-25 03:40:43.000000000 +0000
@@ -35,23 +35,34 @@ struct input_prop {
 	bool enabled;
 };
 
-int parse_mouse_dpi_property(const char *prop);
-int parse_mouse_wheel_click_angle_property(const char *prop);
-int parse_mouse_wheel_click_count_property(const char *prop);
-bool parse_dimension_property(const char *prop, size_t *width, size_t *height);
-bool parse_calibration_property(const char *prop, float calibration[6]);
-bool parse_range_property(const char *prop, int *hi, int *lo);
-bool parse_boolean_property(const char *prop, bool *b);
+int
+parse_mouse_dpi_property(const char *prop);
+int
+parse_mouse_wheel_click_angle_property(const char *prop);
+int
+parse_mouse_wheel_click_count_property(const char *prop);
+bool
+parse_dimension_property(const char *prop, size_t *width, size_t *height);
+bool
+parse_calibration_property(const char *prop, float calibration[6]);
+bool
+parse_range_property(const char *prop, int *hi, int *lo);
+bool
+parse_boolean_property(const char *prop, bool *b);
 #define EVENT_CODE_UNDEFINED 0xffff
-bool parse_evcode_property(const char *prop, struct input_event *events, size_t *nevents);
-bool parse_input_prop_property(const char *prop, struct input_prop *props_out, size_t *nprops);
+bool
+parse_evcode_property(const char *prop, struct input_event *events, size_t *nevents);
+bool
+parse_input_prop_property(const char *prop,
+			  struct input_prop *props_out,
+			  size_t *nprops);
 
 enum tpkbcombo_layout {
 	TPKBCOMBO_LAYOUT_UNKNOWN,
 	TPKBCOMBO_LAYOUT_BELOW,
 };
-bool parse_tpkbcombo_layout_poperty(const char *prop,
-				    enum tpkbcombo_layout *layout);
+bool
+parse_tpkbcombo_layout_poperty(const char *prop, enum tpkbcombo_layout *layout);
 
 enum switch_reliability {
 	RELIABILITY_RELIABLE,
@@ -71,4 +82,5 @@ enum {
 	ABS_MASK_FLAT = 0x10,
 };
 
-uint32_t parse_evdev_abs_prop(const char *prop, struct input_absinfo *abs);
+uint32_t
+parse_evdev_abs_prop(const char *prop, struct input_absinfo *abs);
diff -pruN 1.28.1-1/src/util-range.h 1.30.0-1/src/util-range.h
--- 1.28.1-1/src/util-range.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-range.h	2025-11-25 03:40:43.000000000 +0000
@@ -32,17 +32,20 @@ struct range {
 };
 
 static inline struct range
-range_init_empty(void) {
+range_init_empty(void)
+{
 	return (struct range){ .lower = 0, .upper = -1 };
 }
 
 static inline struct range
-range_init_inclusive(int lower, int upper) {
-	return (struct range) { .lower = lower, .upper = upper + 1};
+range_init_inclusive(int lower, int upper)
+{
+	return (struct range){ .lower = lower, .upper = upper + 1 };
 }
 
 static inline struct range
-range_init_exclusive(int lower, int upper) {
+range_init_exclusive(int lower, int upper)
+{
 	return (struct range){ .lower = lower, .upper = upper };
 }
 
diff -pruN 1.28.1-1/src/util-ratelimit.c 1.30.0-1/src/util-ratelimit.c
--- 1.28.1-1/src/util-ratelimit.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-ratelimit.c	2025-11-25 03:40:43.000000000 +0000
@@ -73,8 +73,7 @@ ratelimit_test(struct ratelimit *r)
 
 	if (r->num < r->burst) {
 		/* continue burst */
-		return (++r->num == r->burst) ? RATELIMIT_THRESHOLD
-					      : RATELIMIT_PASS;
+		return (++r->num == r->burst) ? RATELIMIT_THRESHOLD : RATELIMIT_PASS;
 	}
 
 	return RATELIMIT_EXCEEDED;
diff -pruN 1.28.1-1/src/util-ratelimit.h 1.30.0-1/src/util-ratelimit.h
--- 1.28.1-1/src/util-ratelimit.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-ratelimit.h	2025-11-25 03:40:43.000000000 +0000
@@ -41,5 +41,7 @@ struct ratelimit {
 	unsigned int num;
 };
 
-void ratelimit_init(struct ratelimit *r, uint64_t ival_us, unsigned int burst);
-enum ratelimit_state ratelimit_test(struct ratelimit *r);
+void
+ratelimit_init(struct ratelimit *r, uint64_t ival_us, unsigned int burst);
+enum ratelimit_state
+ratelimit_test(struct ratelimit *r);
diff -pruN 1.28.1-1/src/util-stringbuf.h 1.30.0-1/src/util-stringbuf.h
--- 1.28.1-1/src/util-stringbuf.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-stringbuf.h	2025-11-25 03:40:43.000000000 +0000
@@ -28,6 +28,7 @@
 #include <unistd.h>
 
 #include "util-macros.h"
+#include "util-mem.h"
 
 struct stringbuf {
 	char *data;
@@ -56,7 +57,6 @@ stringbuf_reset(struct stringbuf *b)
 	b->data = NULL;
 	b->sz = 0;
 	b->len = 0;
-
 }
 
 static inline struct stringbuf *
@@ -74,6 +74,8 @@ stringbuf_destroy(struct stringbuf *b)
 	free(b);
 }
 
+DEFINE_DESTROY_CLEANUP_FUNC(stringbuf);
+
 static inline char *
 stringbuf_steal_destroy(struct stringbuf *b)
 {
@@ -147,12 +149,12 @@ stringbuf_append_from_fd(struct stringbu
 static inline int
 stringbuf_append_string(struct stringbuf *b, const char *msg)
 {
-	int r = stringbuf_ensure_space(b, strlen(msg) + 1);
+	size_t slen = strlen(msg);
+
+	int r = stringbuf_ensure_space(b, slen + 1);
 	if (r < 0)
 		return r;
 
-	size_t slen = strlen(msg);
-
 	if (!b->data) /* cannot happen, but let's make scan-build happy */
 		abort();
 
diff -pruN 1.28.1-1/src/util-strings.c 1.30.0-1/src/util-strings.c
--- 1.28.1-1/src/util-strings.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-strings.c	2025-11-25 03:40:43.000000000 +0000
@@ -62,6 +62,64 @@ next_word(const char **state, size_t *le
 	return next;
 }
 
+size_t
+strv_len(char **strv)
+{
+	if (!strv)
+		return 0;
+
+	size_t size = 1;
+	while (*strv) {
+		size++;
+		strv++;
+	}
+	return size;
+}
+
+char **
+strv_append_vprintf(char **strv, const char *fmt, va_list args)
+{
+	char *dup = strdup_vprintf(fmt, args);
+	char **s = strv_append_take(strv, &dup);
+	return s;
+}
+
+char **
+strv_append_printf(char **strv, const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	char **s = strv_append_vprintf(strv, fmt, args);
+	va_end(args);
+	return s;
+}
+
+char **
+strv_append_strdup(char **strv, const char *str)
+{
+	char *dup = safe_strdup(str);
+	return strv_append_take(strv, &dup);
+}
+
+char **
+strv_append_take(char **strv, char **str)
+{
+	if (str && *str) {
+		size_t len = strv_len(strv) + 1;
+		len = max(len, 2);
+
+		char **s = realloc(strv, len * sizeof(*strv));
+		if (!s)
+			abort();
+		s[len - 1] = NULL;
+		s[len - 2] = *str;
+		*str = NULL;
+		return s;
+	} else {
+		return strv;
+	}
+}
+
 /**
  * Return a null-terminated string array with the contents of argv
  * duplicated.
@@ -70,7 +128,7 @@ next_word(const char **state, size_t *le
  *
  * @return A null-terminated string array or NULL on errors
  */
-char**
+char **
 strv_from_argv(int argc, char **argv)
 {
 	char **strv = NULL;
@@ -115,7 +173,6 @@ strv_from_string(const char *in, const c
 {
 	assert(in != NULL);
 	assert(separators != NULL);
-	assert(num_elements != NULL);
 
 	const char *s = in;
 	size_t l, nelems = 0;
@@ -123,7 +180,8 @@ strv_from_string(const char *in, const c
 		nelems++;
 
 	if (nelems == 0) {
-		*num_elements = 0;
+		if (num_elements)
+			*num_elements = 0;
 		return NULL;
 	}
 
@@ -137,14 +195,17 @@ strv_from_string(const char *in, const c
 		char *copy = strndup(word, l);
 		if (!copy) {
 			strv_free(strv);
-			*num_elements = 0;
+			if (num_elements)
+				*num_elements = 0;
 			return NULL;
 		}
 
+		assert(idx < strv_len);
 		strv[idx++] = copy;
 	}
 
-	*num_elements = nelems;
+	if (num_elements)
+		*num_elements = nelems;
 
 	return strv;
 }
@@ -166,8 +227,6 @@ strv_from_string(const char *in, const c
 char *
 strv_join(char **strv, const char *joiner)
 {
-	assert(strv != NULL);
-
 	char **s;
 	char *str;
 	size_t slen = 0;
@@ -192,10 +251,10 @@ strv_join(char **strv, const char *joine
 
 	str = zalloc(slen + 1); /* trailing \0 */
 	for (s = strv; *s; s++) {
-		strcat(str, *s);
+		strcat(str, *s); // NOLINT: security.insecureAPI.strcpy
 		--count;
 		if (count > 0)
-			strcat(str, joiner);
+			strcat(str, joiner); // NOLINT: security.insecureAPI.strcpy
 	}
 
 	return str;
@@ -211,7 +270,8 @@ strv_join(char **strv, const char *joine
  *
  * @return zero on success, otherwise the error returned by the callback
  */
-int strv_for_each_n(const char **strv, size_t max, strv_foreach_callback_t func, void *data)
+int
+strv_for_each_n(const char **strv, size_t max, strv_foreach_callback_t func, void *data)
 {
 	for (size_t i = 0; i < max && strv && strv[i]; i++) {
 		int ret = func(strv[i], i, data);
@@ -228,11 +288,54 @@ int strv_for_each_n(const char **strv, s
  *
  * @return zero on success, otherwise the error returned by the callback
  */
-int strv_for_each(const char **strv, strv_foreach_callback_t func, void *data)
+int
+strv_for_each(const char **strv, strv_foreach_callback_t func, void *data)
 {
 	return strv_for_each_n(strv, SIZE_MAX, func, data);
 }
 
+bool
+strv_find(char **strv, const char *needle, size_t *index_out)
+{
+	if (!strv)
+		return false;
+
+	size_t index = 0;
+	char **s = strv;
+	while (*s != NULL) {
+		if (streq(*s, needle)) {
+			if (index_out)
+				*index_out = index;
+			return true;
+		}
+		s++;
+		index++;
+	}
+
+	return false;
+}
+
+bool
+strv_find_substring(char **strv, const char *needle, size_t *index_out)
+{
+	if (!strv || !needle)
+		return false;
+
+	size_t index = 0;
+	char **s = strv;
+	while (*s != NULL) {
+		if (strstr(*s, needle)) {
+			if (index_out)
+				*index_out = index;
+			return true;
+		}
+		s++;
+		index++;
+	}
+
+	return false;
+}
+
 /**
  * Return a pointer to the basename within filename.
  * If the filename the empty string or a directory (i.e. the last char of
@@ -284,5 +387,5 @@ trunkname(const char *filename)
 	if (suffix == NULL)
 		return safe_strdup(base);
 	else
-		return strndup(base, suffix-base);
+		return strndup(base, suffix - base);
 }
diff -pruN 1.28.1-1/src/util-strings.h 1.30.0-1/src/util-strings.h
--- 1.28.1-1/src/util-strings.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-strings.h	2025-11-25 03:40:43.000000000 +0000
@@ -31,11 +31,12 @@
 #include <errno.h>
 #include <limits.h>
 #include <math.h>
-#include <string.h>
-#include <stdio.h>
+#include <stdarg.h>
 #include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
 #include <stdlib.h>
-#include <stdarg.h>
+#include <string.h>
 #ifdef HAVE_LOCALE_H
 #include <locale.h>
 #endif
@@ -44,6 +45,7 @@
 #endif
 
 #include "util-macros.h"
+#include "util-mem.h"
 
 #define yesno(b) ((b) ? "yes" : "no")
 #define truefalse(b) ((b) ? "true" : "false")
@@ -61,7 +63,7 @@ streq(const char *str1, const char *str2
 }
 
 static inline bool
-strneq(const char *str1, const char *str2, int n)
+strneq(const char *str1, const char *str2, size_t n)
 {
 	/* one NULL, one not NULL is always false */
 	if (str1 && str2)
@@ -69,29 +71,12 @@ strneq(const char *str1, const char *str
 	return str1 == str2;
 }
 
-static inline void *
-zalloc(size_t size)
-{
-	void *p;
-
-	/* We never need to alloc anything more than 1,5 MB so we can assume
-	 * if we ever get above that something's going wrong */
-	if (size > 1536 * 1024)
-		assert(!"bug: internal malloc size limit exceeded");
-
-	p = calloc(1, size);
-	if (!p)
-		abort();
-
-	return p;
-}
-
 /**
  * strdup guaranteed to succeed. If the input string is NULL, the output
  * string is NULL. If the input string is a string pointer, we strdup or
  * abort on failure.
  */
-static inline char*
+static inline char *
 safe_strdup(const char *str)
 {
 	char *s;
@@ -127,8 +112,7 @@ safe_strlen(const char *str)
  * upon success or -1 upon failure. In the case of failure the pointer is set
  * to NULL.
  */
-__attribute__ ((format (printf, 2, 3)))
-static inline int
+__attribute__((format(printf, 2, 3))) static inline int
 xasprintf(char **strp, const char *fmt, ...)
 {
 	int rc = 0;
@@ -143,8 +127,7 @@ xasprintf(char **strp, const char *fmt,
 	return rc;
 }
 
-__attribute__ ((format (printf, 2, 0)))
-static inline int
+__attribute__((format(printf, 2, 0))) static inline int
 xvasprintf(char **strp, const char *fmt, va_list args)
 {
 	int rc = 0;
@@ -155,8 +138,7 @@ xvasprintf(char **strp, const char *fmt,
 	return rc;
 }
 
-__attribute__ ((format (printf, 1, 2)))
-static inline char *
+__attribute__((format(printf, 1, 2))) static inline char *
 strdup_printf(const char *fmt, ...)
 {
 	int rc = 0;
@@ -171,8 +153,7 @@ strdup_printf(const char *fmt, ...)
 	return strp;
 }
 
-__attribute__ ((format (printf, 1, 0)))
-static inline char *
+__attribute__((format(printf, 1, 0))) static inline char *
 strdup_vprintf(const char *fmt, va_list args)
 {
 	char *strp;
@@ -249,15 +230,45 @@ safe_atou(const char *str, unsigned int
 }
 
 static inline bool
+safe_atou64_base(const char *str, uint64_t *val, int base)
+{
+	assert(str != NULL);
+
+	char *endptr;
+	unsigned long long v;
+
+	assert(base == 10 || base == 16 || base == 8);
+
+	errno = 0;
+	v = strtoull(str, &endptr, base);
+	if (errno > 0)
+		return false;
+	if (str == endptr)
+		return false;
+	if (*str != '\0' && *endptr != '\0')
+		return false;
+
+	if ((long long)v < 0)
+		return false;
+
+	*val = v;
+	return true;
+}
+
+static inline bool
+safe_atou64(const char *str, uint64_t *val)
+{
+	assert(str != NULL);
+	return safe_atou64_base(str, val, 10);
+}
+
+static inline bool
 safe_atod(const char *str, double *val)
 {
 	assert(str != NULL);
 
 	char *endptr;
 	double v;
-#ifdef HAVE_LOCALE_H
-	locale_t c_locale;
-#endif
 	size_t slen = strlen(str);
 
 	/* We don't have a use-case where we want to accept hex for a double
@@ -266,8 +277,8 @@ safe_atod(const char *str, double *val)
 		char c = str[i];
 
 		if (isdigit(c))
-		       continue;
-		switch(c) {
+			continue;
+		switch (c) {
 		case '+':
 		case '-':
 		case '.':
@@ -279,7 +290,7 @@ safe_atod(const char *str, double *val)
 
 #ifdef HAVE_LOCALE_H
 	/* Create a "C" locale to force strtod to use '.' as separator */
-	c_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
+	locale_t c_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
 	if (c_locale == (locale_t)0)
 		return false;
 
@@ -304,30 +315,63 @@ safe_atod(const char *str, double *val)
 	return true;
 }
 
-char **strv_from_argv(int argc, char **argv);
-char **strv_from_string(const char *in, const char *separator, size_t *num_elements);
-char *strv_join(char **strv, const char *joiner);
+/* Returns the length of the strv, including the terminating NULL */
+size_t
+strv_len(char **strv);
+char **
+strv_from_argv(int argc, char **argv);
+char **
+strv_from_string(const char *in, const char *separator, size_t *num_elements);
+char *
+strv_join(char **strv, const char *joiner);
+char **
+strv_append_strdup(char **strv, const char *s);
+/* Takes ownership of the string and appends it to strv, s is set to NULL */
+char **
+strv_append_take(char **strv, char **s);
+__attribute__((format(printf, 2, 3))) char **
+strv_append_printf(char **strv, const char *fmt, ...);
+__attribute__((format(printf, 2, 0))) char **
+strv_append_vprintf(char **strv, const char *fmt, va_list args);
+
+bool
+strv_find(char **strv, const char *needle, size_t *index_out);
+bool
+strv_find_substring(char **strv, const char *needle, size_t *index_out);
 
 typedef int (*strv_foreach_callback_t)(const char *str, size_t index, void *data);
-int strv_for_each(const char **strv, strv_foreach_callback_t func, void *data);
-int strv_for_each_n(const char **strv, size_t max, strv_foreach_callback_t func, void *data);
+int
+strv_for_each(const char **strv, strv_foreach_callback_t func, void *data);
+int
+strv_for_each_n(const char **strv,
+		size_t max,
+		strv_foreach_callback_t func,
+		void *data);
 
 static inline void
-strv_free(char **strv) {
+strv_free(char **strv)
+{
 	char **s = strv;
 
 	if (!strv)
 		return;
 
-	while (*s != NULL) {
+	while (*s != NULL) { /* NOLINT(clang-analyzer-security.ArrayBound) */
 		free(*s);
-		*s = (char*)0x1; /* detect use-after-free */
+		*s = (char *)0x1; /* detect use-after-free */
 		s++;
 	}
 
-	free (strv);
+	free(strv);
 }
 
+DEFINE_TRIVIAL_CLEANUP_FUNC(char **, strv_free);
+
+/**
+ * Use: _autostrvfree_ char **strv = ...;
+ */
+#define _autostrvfree_ _cleanup_(strv_freep)
+
 /**
  * parse a string containing a list of doubles into a double array.
  *
@@ -338,9 +382,7 @@ strv_free(char **strv) {
  * @return true when parsed successfully otherwise false
  */
 static inline double *
-double_array_from_string(const char *in,
-			 const char *separator,
-			 size_t *length)
+double_array_from_string(const char *in, const char *separator, size_t *length)
 {
 	assert(in != NULL);
 	assert(separator != NULL);
@@ -373,7 +415,7 @@ out:
 	return result;
 }
 
-struct key_value_str{
+struct key_value_str {
 	char *key;
 	char *value;
 };
@@ -392,8 +434,8 @@ kv_double_from_string(const char *string
 {
 	struct key_value_double *result = NULL;
 
-	if (!pair_separator || pair_separator[0] == '\0' ||
-	    !kv_separator || kv_separator[0] == '\0')
+	if (!pair_separator || pair_separator[0] == '\0' || !kv_separator ||
+	    kv_separator[0] == '\0')
 		return -1;
 
 	size_t npairs;
@@ -409,8 +451,7 @@ kv_double_from_string(const char *string
 		char **kv = strv_from_string(pair, kv_separator, &nelem);
 		double k, v;
 
-		if (!kv || nelem != 2 ||
-		    !safe_atod(kv[0], &k) ||
+		if (!kv || nelem != 2 || !safe_atod(kv[0], &k) ||
 		    !safe_atod(kv[1], &v)) {
 			strv_free(kv);
 			goto error;
@@ -491,7 +532,7 @@ strstartswith(const char *str, const cha
 
 	size_t prefixlen = strlen(prefix);
 
-	return prefixlen > 0 ? strneq(str, prefix, strlen(prefix)) : false;
+	return prefixlen > 0 ? strneq(str, prefix, prefixlen) : false;
 }
 
 const char *
@@ -513,7 +554,8 @@ str_sanitize(const char *str)
 	if (!strchr(str, '%'))
 		return strdup(str);
 
-	size_t slen = min(strlen(str), 512);
+	size_t slen = strlen(str);
+	slen = min(slen, 512);
 	char *sanitized = zalloc(2 * slen + 1);
 	const char *src = str;
 	char *dst = sanitized;
diff -pruN 1.28.1-1/src/util-time.h 1.30.0-1/src/util-time.h
--- 1.28.1-1/src/util-time.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/src/util-time.h	2025-11-25 03:40:43.000000000 +0000
@@ -27,10 +27,10 @@
 
 #include <assert.h>
 #include <errno.h>
-#include <time.h>
+#include <linux/input.h>
 #include <stdint.h>
+#include <time.h>
 #include <unistd.h>
-#include <linux/input.h>
 
 #include "util-macros.h"
 
@@ -148,17 +148,13 @@ to_human_time(uint64_t us)
 		unsigned int change_from_previous;
 		uint64_t limit;
 	} conversion[] = {
-		{"us", 1, 5000},
-		{"ms", 1000, 5000},
-		{"s", 1000, 120},
-		{"min", 60, 120},
-		{"h", 60, 48},
-		{"d", 24, ~0},
+		{ "us", 1, 5000 },  { "ms", 1000, 5000 }, { "s", 1000, 120 },
+		{ "min", 60, 120 }, { "h", 60, 48 },      { "d", 24, ~0 },
 	};
 	uint64_t value = us;
 
 	ARRAY_FOR_EACH(conversion, c) {
-		value = value/c->change_from_previous;
+		value = value / c->change_from_previous;
 		if (value < c->limit) {
 			t.unit = c->unit;
 			t.value = value;
diff -pruN 1.28.1-1/src/util-udev.h 1.30.0-1/src/util-udev.h
--- 1.28.1-1/src/util-udev.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/src/util-udev.h	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2025 Ryan Hendrickson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef UTIL_UDEV_H
+#define UTIL_UDEV_H
+
+#include "config.h"
+
+#include <libudev.h>
+
+#include "util-strings.h"
+
+static inline bool
+udev_device_is_virtual(struct udev_device *udev_device)
+{
+	const char *path = udev_device_get_syspath(udev_device);
+	if (!path)
+		return false;
+	return strstartswith(path, "/sys/devices/virtual/input/");
+}
+
+#endif /* UTIL_UDEV_H */
diff -pruN 1.28.1-1/test/.clang-tidy 1.30.0-1/test/.clang-tidy
--- 1.28.1-1/test/.clang-tidy	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/test/.clang-tidy	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,6 @@
+# optin.core.FloatLoopCounter: doubles in for loops - it's fine in the tests
+# optin.performance.Padding: not a performance concern in the tests
+InheritParentConfig: true
+Checks: >
+  -clang-analyzer-security.FloatLoopCounter,
+  -clang-analyzer-optin.performance.Padding,
diff -pruN 1.28.1-1/test/litest-device-absinfo-override.c 1.30.0-1/test/litest-device-absinfo-override.c
--- 1.28.1-1/test/litest-device-absinfo-override.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-absinfo-override.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x11,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x4567,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -44,7 +45,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_POINTER,
 	-1 , -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 2000, 0, 0, 0 },
 	{ ABS_Y, 0, 1400, 0, 0, 0 },
@@ -53,24 +56,24 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_X, 0, 2000, 0, 0, 0 },
 	{ ABS_MT_POSITION_Y, 0, 1400, 0, 0, 0 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 /* This device only exists to verify that the EVDEV_ABS override bits work
  * correctly */
 TEST_DEVICE(LITEST_ABSINFO_OVERRIDE,
-	.features = LITEST_IGNORED,
-	.interface = NULL,
+	    .features = LITEST_IGNORED,
+	    .interface = NULL,
 
-	.name = "absinfo override",
-	.id = &input_id,
-	.absinfo = absinfo,
-	.events = events,
-	.udev_properties = {
-	  { "EVDEV_ABS_00", "1:1000:100:10" },
-	  { "EVDEV_ABS_01", "2:2000:200:20" },
-	  { "EVDEV_ABS_35", "3:3000:300:30" },
-	  { "EVDEV_ABS_36", "4:4000:400:40" },
-	  { NULL },
-	},
-)
+	    .name = "absinfo override",
+	    .id = &input_id,
+	    .absinfo = absinfo,
+	    .events = events,
+	    .udev_properties = {
+		    { "EVDEV_ABS_00", "1:1000:100:10" },
+		    { "EVDEV_ABS_01", "2:2000:200:20" },
+		    { "EVDEV_ABS_35", "3:3000:300:30" },
+		    { "EVDEV_ABS_36", "4:4000:400:40" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-acer-hawaii-keyboard.c 1.30.0-1/test/litest-device-acer-hawaii-keyboard.c
--- 1.28.1-1/test/litest-device-acer-hawaii-keyboard.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-acer-hawaii-keyboard.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x1558,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, KEY_ESC,
 	EV_KEY, KEY_1,
@@ -187,13 +188,13 @@ static int events[] = {
 	EV_KEY, KEY_FN,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_ACER_HAWAII_KEYBOARD,
-	.features = LITEST_KEYS,
-	.interface = NULL,
+	    .features = LITEST_KEYS,
+	    .interface = NULL,
 
-	.name = "Chicony ACER Hawaii Keyboard",
-	.id = &input_id,
-	.events = events,
-	.absinfo = NULL,
-)
+	    .name = "Chicony ACER Hawaii Keyboard",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = NULL, )
diff -pruN 1.28.1-1/test/litest-device-acer-hawaii-touchpad.c 1.30.0-1/test/litest-device-acer-hawaii-touchpad.c
--- 1.28.1-1/test/litest-device-acer-hawaii-touchpad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-acer-hawaii-touchpad.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,11 +23,11 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
@@ -39,7 +39,7 @@ static struct input_event down[] = {
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
@@ -58,6 +58,7 @@ static struct input_id input_id = {
 	.product = 0x1558,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_TOOL_FINGER,
@@ -70,7 +71,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1151, 0, 0, 12 },
 	{ ABS_Y, 0, 738, 0, 0, 14 },
@@ -78,19 +81,19 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_X, 0, 1151, 0, 0, 12 },
 	{ ABS_MT_POSITION_Y, 0, 738, 0, 0, 14 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_ACER_HAWAII_TOUCHPAD,
-	.features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
+	    .interface = &interface,
 
-	.name = "Chicony ACER Hawaii Keyboard Touchpad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "ID_INPUT_TOUCHPAD_INTEGRATION", "external" },
-		{ NULL },
-	}
-)
+	    .name = "Chicony ACER Hawaii Keyboard Touchpad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "ID_INPUT_TOUCHPAD_INTEGRATION", "external" },
+		    { NULL },
+	    })
diff -pruN 1.28.1-1/test/litest-device-aiptek-tablet.c 1.30.0-1/test/litest-device-aiptek-tablet.c
--- 1.28.1-1/test/litest-device-aiptek-tablet.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-aiptek-tablet.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in[] = {
 	/* Note: this device does not send BTN_TOOL_PEN */
@@ -76,6 +76,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 5999, 0, 0, 26 },
 	{ ABS_Y, 0, 4499, 0, 0, 15 },
@@ -85,6 +86,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_TILT_Y, -128, 127, 0, 0, 0 }, /* mute axis */
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -92,6 +94,7 @@ static struct input_id input_id = {
 	.product = 0x10,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, KEY_ESC,
 	EV_KEY, KEY_F1,
@@ -145,13 +148,13 @@ static int events[] = {
 	EV_MSC, MSC_SERIAL,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_AIPTEK,
-	.features = LITEST_TABLET | LITEST_HOVER | LITEST_FORCED_PROXOUT,
-	.interface = &interface,
+	    .features = LITEST_TABLET | LITEST_HOVER | LITEST_FORCED_PROXOUT,
+	    .interface = &interface,
 
-	.name = "Aiptek",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "Aiptek",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-alps-3fg.c 1.30.0-1/test/litest-device-alps-3fg.c
--- 1.28.1-1/test/litest-device-alps-3fg.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-alps-3fg.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,9 +26,8 @@
 #include <assert.h>
 
 #include "libinput-util.h"
-
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 struct alps {
 	unsigned int first, second;
@@ -59,7 +58,7 @@ touch_down(struct litest_device *d, unsi
 	 * litest takes care of BTN_TOOL_* for us. */
 	if (alps->active_touches > 2) {
 		/* Need to send SYN_REPORT to flush litest's BTN_TOOL_* updates */
-		litest_event(d, EV_SYN, SYN_REPORT,  0);
+		litest_event(d, EV_SYN, SYN_REPORT, 0);
 		return true;
 	}
 
@@ -71,9 +70,7 @@ touch_move(struct litest_device *d, unsi
 {
 	struct alps *alps = d->private;
 
-	if (alps->active_touches > 2 &&
-	    slot != alps->first &&
-	    slot != alps->second)
+	if (alps->active_touches > 2 && slot != alps->first && slot != alps->second)
 		return true;
 
 	return false;
@@ -88,10 +85,8 @@ touch_up(struct litest_device *d, unsign
 	alps->active_touches--;
 
 	/* Need to send SYN_REPORT to flush litest's BTN_TOOL_* updates */
-	if (alps->active_touches > 2 &&
-	    slot != alps->first &&
-	    slot != alps->second) {
-		litest_event(d, EV_SYN, SYN_REPORT,  0);
+	if (alps->active_touches > 2 && slot != alps->first && slot != alps->second) {
+		litest_event(d, EV_SYN, SYN_REPORT, 0);
 		return true;
 	}
 
@@ -104,7 +99,7 @@ touch_up(struct litest_device *d, unsign
 }
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
@@ -116,7 +111,7 @@ static struct input_event down[] = {
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
@@ -140,6 +135,7 @@ static struct input_id input_id = {
 	.version = 0x700,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -153,8 +149,10 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_POINTER,
 	-1, -1,
 };
+/* clang-format on */
 
 /* Note: we use the user-supplied resolution here, see #408 */
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 4095, 0, 0, 37 },
 	{ ABS_Y, 0, 2047, 0, 0, 26 },
@@ -163,16 +161,16 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_X, 0, 4095, 0, 0, 37 },
 	{ ABS_MT_POSITION_Y, 0, 2047, 0, 0, 26 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_ALPS_3FG,
-	.features = LITEST_TOUCHPAD | LITEST_BUTTON,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD | LITEST_BUTTON,
+	    .interface = &interface,
 
-	.name = "AlpsPS/2 ALPS GlidePoint",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.create = alps_create,
-)
+	    .name = "AlpsPS/2 ALPS GlidePoint",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .create = alps_create, )
diff -pruN 1.28.1-1/test/litest-device-alps-dualpoint.c 1.30.0-1/test/litest-device-alps-dualpoint.c
--- 1.28.1-1/test/litest-device-alps-dualpoint.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-alps-dualpoint.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,12 +26,11 @@
 #include <assert.h>
 
 #include "libinput-util.h"
-
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
@@ -44,7 +43,7 @@ static struct input_event down[] = {
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
@@ -79,6 +78,7 @@ static struct input_id input_id = {
 	.version = 0x310,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -92,7 +92,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_SEMI_MT,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 2000, 0, 0, 25 },
 	{ ABS_Y, 0, 1400, 0, 0, 32 },
@@ -101,21 +103,21 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_X, 0, 2000, 0, 0, 25 },
 	{ ABS_MT_POSITION_Y, 0, 1400, 0, 0, 32 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 static const char quirk_file[] =
-"[litest ALPS Touchpad]\n"
-"MatchName=litest AlpsPS/2 ALPS DualPoint TouchPad\n"
-"ModelTouchpadVisibleMarker=1\n";
+	"[litest ALPS Touchpad]\n"
+	"MatchName=litest AlpsPS/2 ALPS DualPoint TouchPad\n"
+	"ModelTouchpadVisibleMarker=1\n";
 
 TEST_DEVICE(LITEST_ALPS_DUALPOINT,
-	.features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_SEMI_MT,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_SEMI_MT,
+	    .interface = &interface,
 
-	.name = "AlpsPS/2 ALPS DualPoint TouchPad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.quirk_file = quirk_file,
-)
+	    .name = "AlpsPS/2 ALPS DualPoint TouchPad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .quirk_file = quirk_file, )
diff -pruN 1.28.1-1/test/litest-device-alps-semi-mt.c 1.30.0-1/test/litest-device-alps-semi-mt.c
--- 1.28.1-1/test/litest-device-alps-semi-mt.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-alps-semi-mt.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,12 +26,11 @@
 #include <assert.h>
 
 #include "libinput-util.h"
-
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
@@ -44,7 +43,7 @@ static struct input_event down[] = {
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
@@ -78,6 +77,7 @@ static struct input_id input_id = {
 	.product = 0x8,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -91,7 +91,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_SEMI_MT,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 2000, 0, 0, 0 },
 	{ ABS_Y, 0, 1400, 0, 0, 0 },
@@ -100,15 +102,15 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_X, 0, 2000, 0, 0, 0 },
 	{ ABS_MT_POSITION_Y, 0, 1400, 0, 0, 0 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_ALPS_SEMI_MT,
-	.features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_SEMI_MT,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_SEMI_MT,
+	    .interface = &interface,
 
-	.name = "AlpsPS/2 ALPS GlidePoint",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "AlpsPS/2 ALPS GlidePoint",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-anker-mouse-kbd.c 1.30.0-1/test/litest-device-anker-mouse-kbd.c
--- 1.28.1-1/test/litest-device-anker-mouse-kbd.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-anker-mouse-kbd.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 /* Recording from https://bugs.freedesktop.org/show_bug.cgi?id=93474
  * This is the keyboard device for this mouse.
@@ -36,8 +36,10 @@ static struct input_id input_id = {
 	.product = 0xfa50,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_REL, REL_HWHEEL,
+	EV_REL, REL_HWHEEL_HI_RES,
 	EV_KEY, KEY_ESC,
 	EV_KEY, KEY_KPMINUS,
 	EV_KEY, KEY_KPPLUS,
@@ -183,7 +185,9 @@ static int events[] = {
 	EV_MSC, MSC_SCAN,
 	-1 , -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_VOLUME, 0, 4096, 0, 0, 0 },
 	{ ABS_MISC, 0, 255, 0, 0, 0 },
@@ -205,13 +209,13 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_X, 0, 255, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_ANKER_MOUSE_KBD,
-	.features = LITEST_KEYS | LITEST_WHEEL,
-	.interface = NULL,
+	    .features = LITEST_KEYS | LITEST_WHEEL,
+	    .interface = NULL,
 
-	.name = "USB Laser Game Mouse",
-	.id = &input_id,
-	.absinfo = absinfo,
-	.events = events,
-)
+	    .name = "USB Laser Game Mouse",
+	    .id = &input_id,
+	    .absinfo = absinfo,
+	    .events = events, )
diff -pruN 1.28.1-1/test/litest-device-apple-appletouch.c 1.30.0-1/test/litest-device-apple-appletouch.c
--- 1.28.1-1/test/litest-device-apple-appletouch.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-apple-appletouch.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,11 +23,11 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
@@ -35,7 +35,7 @@ static struct input_event down[] = {
 };
 
 static struct input_event move[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
@@ -67,6 +67,7 @@ static struct input_id input_id = {
 	.product = 0x21a,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_TOOL_FINGER,
@@ -75,26 +76,28 @@ static int events[] = {
 	EV_KEY, BTN_TOOL_TRIPLETAP,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1215, 0, 0, 0 },
 	{ ABS_Y, 0, 588, 0, 0, 0 },
 	{ ABS_PRESSURE, 0, 300, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 static const char quirk_file[] =
-"[litest Apple Touchpad]\n"
-"MatchName=litest appletouch\n"
-"ModelAppleTouchpadOneButton=1\n";
+	"[litest Apple Touchpad]\n"
+	"MatchName=litest appletouch\n"
+	"ModelAppleTouchpadOneButton=1\n";
 
 TEST_DEVICE(LITEST_APPLETOUCH,
-	.features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_SINGLE_TOUCH,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_SINGLE_TOUCH,
+	    .interface = &interface,
 
-	.name = "appletouch",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.quirk_file = quirk_file,
-)
+	    .name = "appletouch",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .quirk_file = quirk_file, )
diff -pruN 1.28.1-1/test/litest-device-apple-internal-keyboard.c 1.30.0-1/test/litest-device-apple-internal-keyboard.c
--- 1.28.1-1/test/litest-device-apple-internal-keyboard.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-apple-internal-keyboard.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x273,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, KEY_ESC,
 	EV_KEY, KEY_1,
@@ -216,13 +217,13 @@ static int events[] = {
 	EV_LED, LED_KANA,
 	-1, -1
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_APPLE_KEYBOARD,
-	.features = LITEST_KEYS,
-	.interface = NULL,
+	    .features = LITEST_KEYS,
+	    .interface = NULL,
 
-	.name = "Apple Inc. Apple Internal Keyboard / Trackpad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = NULL,
-)
+	    .name = "Apple Inc. Apple Internal Keyboard / Trackpad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = NULL, )
diff -pruN 1.28.1-1/test/litest-device-apple-magicmouse.c 1.30.0-1/test/litest-device-apple-magicmouse.c
--- 1.28.1-1/test/litest-device-apple-magicmouse.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-apple-magicmouse.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
@@ -60,6 +60,7 @@ static struct input_id input_id = {
 	.product = 0x30d,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -70,7 +71,9 @@ static int events[] = {
 	EV_REL, REL_HWHEEL,
 	-1 , -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_MT_SLOT, 0, 15, 0, 0, 0 },
 	{ ABS_MT_TOUCH_MAJOR, 0, 1020, 0, 0, 0 },
@@ -79,23 +82,23 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_X, -1100, 1258, 4, 0, 26 },
 	{ ABS_MT_POSITION_Y, -1589, 2047, 4, 0, 26 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_MAGICMOUSE,
-	.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
-	.interface = &interface,
+	    .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
+	    .interface = &interface,
 
-	.name = "Apple Magic Mouse",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-
-	/* Force MOUSE_DPI to 1000. systemd commit f013e99e160f says it's 1300
-	 * but our test were written with different assumptions and it's not worth
-	 * re-writing those, so let's assume the default */
-	.udev_properties = {
-		{ "MOUSE_DPI", "1000" },
-		{ NULL },
-	},
-)
+	    .name = "Apple Magic Mouse",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+
+	    /* Force MOUSE_DPI to 1000. systemd commit f013e99e160f says it's 1300
+	     * but our test were written with different assumptions and it's not worth
+	     * re-writing those, so let's assume the default */
+	    .udev_properties = {
+		    { "MOUSE_DPI", "1000" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-asus-rog-gladius.c 1.30.0-1/test/litest-device-asus-rog-gladius.c
--- 1.28.1-1/test/litest-device-asus-rog-gladius.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-asus-rog-gladius.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 /* Note: this is the second event node of this mouse only, the first event
  * node is just a normal mouse */
@@ -35,6 +35,7 @@ static struct input_id input_id = {
 	.product = 0x181a,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_REL, REL_X,
 	EV_REL, REL_Y,
@@ -306,18 +307,20 @@ static int events[] = {
 	EV_LED, LED_KANA,
 	-1 , -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_VOLUME, 0, 668, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_MOUSE_GLADIUS,
-	.features = LITEST_RELATIVE | LITEST_WHEEL | LITEST_KEYS,
-	.interface = NULL,
+	    .features = LITEST_RELATIVE | LITEST_WHEEL | LITEST_KEYS,
+	    .interface = NULL,
 
-	.name = "ASUS ROG GLADIUS",
-	.id = &input_id,
-	.absinfo = absinfo,
-	.events = events,
-)
+	    .name = "ASUS ROG GLADIUS",
+	    .id = &input_id,
+	    .absinfo = absinfo,
+	    .events = events, )
diff -pruN 1.28.1-1/test/litest-device-atmel-hover.c 1.30.0-1/test/litest-device-atmel-hover.c
--- 1.28.1-1/test/litest-device-atmel-hover.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-atmel-hover.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,20 +26,19 @@
 #include <assert.h>
 
 #include "libinput-util.h"
-
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_DISTANCE, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
 };
@@ -48,11 +47,11 @@ static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_DISTANCE, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
 };
@@ -61,7 +60,7 @@ static struct input_event up[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = -1 },
 	{ .type = EV_ABS, .code = ABS_MT_DISTANCE, .value = 1 },
-	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = 0  },
+	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = 0 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
 };
@@ -92,6 +91,7 @@ static struct input_id input_id = {
 	.product = 0x0,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_TOOL_FINGER,
@@ -104,7 +104,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 960, 0, 0, 10 },
 	{ ABS_Y, 0, 540, 0, 0, 10 },
@@ -118,15 +120,16 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ ABS_MT_PRESSURE, 0, 255, 0, 0, 0 },
 	{ ABS_MT_DISTANCE, 0, 1, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_ATMEL_HOVER,
-	.features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_CLICKPAD | LITEST_HOVER,
-	.interface = &interface,
-
-	.name = "Atmel maXTouch Touchpad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_CLICKPAD |
+			LITEST_HOVER,
+	    .interface = &interface,
+
+	    .name = "Atmel maXTouch Touchpad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-bcm5974.c 1.30.0-1/test/litest-device-bcm5974.c
--- 1.28.1-1/test/litest-device-bcm5974.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-bcm5974.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,13 +23,13 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
@@ -45,7 +45,7 @@ static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_ORIENTATION, .value = LITEST_AUTO_ASSIGN },
@@ -80,6 +80,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, -4824, 4824, 0, 0, 0 },
 	{ ABS_Y, -172, 4290, 0, 0, 0 },
@@ -96,6 +97,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -103,6 +105,7 @@ static struct input_id input_id = {
 	.product = 0x249,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_TOOL_FINGER,
@@ -114,14 +117,14 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
 	-1, -1
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_BCM5974,
-	.features = LITEST_TOUCHPAD | LITEST_CLICKPAD |
-		    LITEST_BUTTON | LITEST_APPLE_CLICKPAD,
-	.interface = &interface,
-
-	.name = "bcm5974",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON |
+			LITEST_APPLE_CLICKPAD,
+	    .interface = &interface,
+
+	    .name = "bcm5974",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-calibrated-touchscreen.c 1.30.0-1/test/litest-device-calibrated-touchscreen.c
--- 1.28.1-1/test/litest-device-calibrated-touchscreen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-calibrated-touchscreen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
@@ -48,6 +48,7 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1500, 0, 0, 0 },
 	{ ABS_Y, 0, 2500, 0, 0, 0 },
@@ -57,6 +58,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x11,
@@ -64,23 +66,24 @@ static struct input_id input_id = {
 	.product = 0x33,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOUCH,
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_CALIBRATED_TOUCHSCREEN,
-	.features = LITEST_TOUCH|LITEST_PRECALIBRATED,
-	.interface = &interface,
+	    .features = LITEST_TOUCH | LITEST_PRECALIBRATED,
+	    .interface = &interface,
 
-	.name = "Calibrated Touchscreen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-	{ "LIBINPUT_CALIBRATION_MATRIX", "1.2 3.4 5.6 7.8 9.10 11.12" },
-	{ "WL_OUTPUT", "myOutput" },
-	{ NULL }
-	},
-)
+	    .name = "Calibrated Touchscreen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "LIBINPUT_CALIBRATION_MATRIX", "1.2 3.4 5.6 7.8 9.10 11.12" },
+		    { "WL_OUTPUT", "myOutput" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-cyborg-rat-5.c 1.30.0-1/test/litest-device-cyborg-rat-5.c
--- 1.28.1-1/test/litest-device-cyborg-rat-5.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-cyborg-rat-5.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0xcd5,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -48,13 +49,13 @@ static int events[] = {
 	EV_REL, REL_WHEEL,
 	-1 , -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_CYBORG_RAT,
-	.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
-	.interface = NULL,
+	    .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
+	    .interface = NULL,
 
-	.name = "Saitek Cyborg R.A.T.5 Mouse",
-	.id = &input_id,
-	.absinfo = NULL,
-	.events = events,
-)
+	    .name = "Saitek Cyborg R.A.T.5 Mouse",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events, )
diff -pruN 1.28.1-1/test/litest-device-dell-canvas-totem-touch.c 1.30.0-1/test/litest-device-dell-canvas-totem-touch.c
--- 1.28.1-1/test/litest-device-dell-canvas-totem-touch.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-dell-canvas-totem-touch.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
@@ -52,6 +52,7 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_MT_SLOT, 0, 4, 0, 0, 0 },
 	{ ABS_X, 0, 32767, 0, 0, 55 },
@@ -61,6 +62,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -69,23 +71,24 @@ static struct input_id input_id = {
 	.version = 0x111,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOUCH,
 	EV_MSC, MSC_TIMESTAMP,
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_DELL_CANVAS_TOTEM_TOUCH,
-	.features = LITEST_TOUCH,
-	.interface = &interface,
+	    .features = LITEST_TOUCH,
+	    .interface = &interface,
 
-	.name = "Advanced Silicon S.A. CoolTouch® System",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-	 { "LIBINPUT_DEVICE_GROUP", "dell-canvas-totem-group" },
-	 { NULL },
-	},
-)
+	    .name = "Advanced Silicon S.A. CoolTouch® System",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "LIBINPUT_DEVICE_GROUP", "dell-canvas-totem-group" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-dell-canvas-totem.c 1.30.0-1/test/litest-device-dell-canvas-totem.c
--- 1.28.1-1/test/litest-device-dell-canvas-totem.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-dell-canvas-totem.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,20 +23,24 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 /* We don't expect anything but slot 0 to be used, ever */
 #define TOTEM_SLOT 0
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = TOTEM_SLOT },
-	{ .type = EV_ABS, .code = ABS_MT_TOOL_TYPE, .value = MT_TOOL_DIAL }, /* fixed value in device */
+	{ .type = EV_ABS,
+	  .code = ABS_MT_TOOL_TYPE,
+	  .value = MT_TOOL_DIAL }, /* fixed value in device */
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_ORIENTATION, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = 718 }, /* fixed value in device */
+	{ .type = EV_ABS,
+	  .code = ABS_MT_TOUCH_MAJOR,
+	  .value = 718 }, /* fixed value in device */
 	{ .type = EV_ABS, .code = ABS_MT_TOUCH_MINOR, .value = 718 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
@@ -47,7 +51,9 @@ static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_ORIENTATION, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = 718 }, /* fixed value in device */
+	{ .type = EV_ABS,
+	  .code = ABS_MT_TOUCH_MAJOR,
+	  .value = 718 }, /* fixed value in device */
 	{ .type = EV_ABS, .code = ABS_MT_TOUCH_MINOR, .value = 718 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
@@ -79,6 +85,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_MT_SLOT, 0, 4, 0, 0, 0 },
 	{ ABS_MT_TOUCH_MAJOR, 0, 32767, 0, 0, 10 },
@@ -92,6 +99,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -100,23 +108,24 @@ static struct input_id input_id = {
 	.version = 0x111,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_0,
 	EV_MSC, MSC_TIMESTAMP,
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_DELL_CANVAS_TOTEM,
-	.features = LITEST_TOTEM | LITEST_TABLET,
-	.interface = &interface,
+	    .features = LITEST_TOTEM | LITEST_TABLET,
+	    .interface = &interface,
 
-	.name = "Advanced Silicon S.A. CoolTouch® System System Multi Axis",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-	 { "LIBINPUT_DEVICE_GROUP", "dell-canvas-totem-group" },
-	 { NULL },
-	},
-)
+	    .name = "Advanced Silicon S.A. CoolTouch® System System Multi Axis",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "LIBINPUT_DEVICE_GROUP", "dell-canvas-totem-group" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-elan-tablet.c 1.30.0-1/test/litest-device-elan-tablet.c
--- 1.28.1-1/test/litest-device-elan-tablet.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-elan-tablet.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in_events[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -52,7 +52,8 @@ static struct input_event motion_events[
 static bool
 proximity_in(struct litest_device *d,
 	     unsigned int tool_type,
-	     double *x, double *y,
+	     double *x,
+	     double *y,
 	     struct axis_replacement *axes)
 {
 	/* nothing special needed for the pen tool, so let litest handle
@@ -106,12 +107,14 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 18176, 0, 0, 62 },
 	{ ABS_Y, 0, 10240, 0, 0, 62 },
 	{ ABS_PRESSURE, 0, 4096, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x18,
@@ -125,6 +128,7 @@ static struct input_id input_id = {
  * The one in the issue isn't the exact same model, but only the pid and x/y
  * axis max differs differs.
  */
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOOL_PEN,
 	EV_KEY, BTN_TOOL_RUBBER,
@@ -133,13 +137,13 @@ static int events[] = {
 	EV_MSC, MSC_SCAN,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_ELAN_TABLET,
-	.features = LITEST_TABLET,
-	.interface = &interface,
+	    .features = LITEST_TABLET,
+	    .interface = &interface,
 
-	.name = "ELAN2514:00 04F3:23B9",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "ELAN2514:00 04F3:23B9",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-elantech-touchpad.c 1.30.0-1/test/litest-device-elantech-touchpad.c
--- 1.28.1-1/test/litest-device-elantech-touchpad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-elantech-touchpad.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,13 +23,13 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
@@ -40,9 +40,9 @@ static struct input_event down[] = {
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
@@ -74,6 +74,7 @@ static struct input_id input_id = {
 	.product = 0xe,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -85,7 +86,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_POINTER,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1280, 0, 0, 0 },
 	{ ABS_Y, 0, 704, 0, 0, 0 },
@@ -95,15 +98,15 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_X, 0, 1280, 0, 0, 0 },
 	{ ABS_MT_POSITION_Y, 0, 704, 0, 0, 0 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_ELANTECH_TOUCHPAD,
-	.features = LITEST_TOUCHPAD | LITEST_BUTTON,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD | LITEST_BUTTON,
+	    .interface = &interface,
 
-	.name = "ETPS/2 Elantech Touchpad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "ETPS/2 Elantech Touchpad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-format-string.c 1.30.0-1/test/litest-device-format-string.c
--- 1.28.1-1/test/litest-device-format-string.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-format-string.c	2025-11-25 03:40:43.000000000 +0000
@@ -24,8 +24,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -33,6 +33,7 @@ static struct input_id input_id = {
 	.product = 0x0456,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -43,13 +44,13 @@ static int events[] = {
 	EV_REL, REL_WHEEL_HI_RES,
 	-1 , -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_MOUSE_FORMAT_STRING,
-	.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
-	.interface = NULL,
+	    .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
+	    .interface = NULL,
 
-	.name = "Evil %s %d %x Mouse %p %",
-	.id = &input_id,
-	.absinfo = NULL,
-	.events = events,
-)
+	    .name = "Evil %s %d %x Mouse %p %",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events, )
diff -pruN 1.28.1-1/test/litest-device-generic-pressurepad.c 1.30.0-1/test/litest-device-generic-pressurepad.c
--- 1.28.1-1/test/litest-device-generic-pressurepad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-generic-pressurepad.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 /* This is the same device as the one from
    https://gitlab.freedesktop.org/libinput/libinput/-/issues/562
@@ -34,7 +34,7 @@
  */
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
@@ -47,7 +47,7 @@ static struct input_event down[] = {
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
@@ -86,6 +86,7 @@ static struct input_id input_id = {
 	.product = 0x4567,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_TOOL_FINGER,
@@ -98,7 +99,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1224, 0, 0, 12 },
 	{ ABS_Y, 0, 756, 0, 0, 12 },
@@ -109,15 +112,15 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ ABS_MT_PRESSURE, 0, 255, 0, 0, 40 }, /* some random resolution */
 	{ ABS_MT_TOOL_TYPE, 0, 2, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_GENERIC_PRESSUREPAD,
-	.features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
+	    .interface = &interface,
 
-	.name = "Some Generic Pressurepad Touchpad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "Some Generic Pressurepad Touchpad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-generic-singletouch.c 1.30.0-1/test/litest-device-generic-singletouch.c
--- 1.28.1-1/test/litest-device-generic-singletouch.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-generic-singletouch.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -47,11 +47,13 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 10000, 20000, 0, 0, 10 },
 	{ ABS_Y, -2000, 2000, 0, 0, 9 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x01,
@@ -59,18 +61,19 @@ static struct input_id input_id = {
 	.product = 0x03,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOUCH,
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_GENERIC_SINGLETOUCH,
-	.features = LITEST_SINGLE_TOUCH,
-	.interface = &interface,
+	    .features = LITEST_SINGLE_TOUCH,
+	    .interface = &interface,
 
-	.name = "generic_singletouch",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "generic_singletouch",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-gpio-keys.c 1.30.0-1/test/litest-device-gpio-keys.c
--- 1.28.1-1/test/litest-device-gpio-keys.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-gpio-keys.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x19,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x1,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_SW, SW_LID,
 	EV_SW, SW_TABLET_MODE,
@@ -41,24 +42,24 @@ static int events[] = {
 	EV_KEY, KEY_POWER,
 	-1, -1,
 };
+/* clang-format on */
 
 static const char quirk_file[] =
-"[litest gpio quirk]\n"
-"MatchName=litest gpio-keys\n"
-"AttrLidSwitchReliability=reliable\n";
+	"[litest gpio quirk]\n"
+	"MatchName=litest gpio-keys\n"
+	"AttrLidSwitchReliability=reliable\n";
 
 TEST_DEVICE(LITEST_GPIO_KEYS,
-	.features = LITEST_SWITCH,
-	.interface = NULL,
+	    .features = LITEST_SWITCH,
+	    .interface = NULL,
 
-	.name = "gpio-keys",
-	.id = &input_id,
-	.events = events,
-	.absinfo = NULL,
-
-	.quirk_file = quirk_file,
-	.udev_properties = {
-		{ "ID_INPUT_SWITCH", "1" },
-		{ NULL },
-	}
-)
+	    .name = "gpio-keys",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = NULL,
+
+	    .quirk_file = quirk_file,
+	    .udev_properties = {
+		    { "ID_INPUT_SWITCH", "1" },
+		    { NULL },
+	    })
diff -pruN 1.28.1-1/test/litest-device-hp-wmi-hotkeys.c 1.30.0-1/test/litest-device-hp-wmi-hotkeys.c
--- 1.28.1-1/test/litest-device-hp-wmi-hotkeys.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-hp-wmi-hotkeys.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x19,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x000,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, KEY_HELP,
 	EV_KEY, KEY_SETUP,
@@ -46,18 +47,18 @@ static int events[] = {
 	EV_SW, SW_DOCK,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_HP_WMI_HOTKEYS,
-	.features = LITEST_SWITCH,
-	.interface = NULL,
+	    .features = LITEST_SWITCH,
+	    .interface = NULL,
 
-	.name = "HP WMI hotkeys",
-	.id = &input_id,
-	.events = events,
-	.absinfo = NULL,
-
-	.udev_properties = {
-		{ "ID_INPUT_SWITCH", "1" },
-		{ NULL },
-	}
-)
+	    .name = "HP WMI hotkeys",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = NULL,
+
+	    .udev_properties = {
+		    { "ID_INPUT_SWITCH", "1" },
+		    { NULL },
+	    })
diff -pruN 1.28.1-1/test/litest-device-huion-pentablet.c 1.30.0-1/test/litest-device-huion-pentablet.c
--- 1.28.1-1/test/litest-device-huion-pentablet.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-huion-pentablet.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -73,20 +73,24 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 40000, 0, 0, 157 },
 	{ ABS_Y, 0, 25000, 0, 0, 157 },
 	{ ABS_PRESSURE, 0, 2047, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
-	/* Note: this VID/PID is shared with multiple devices, see the libwacom database for a list */
+	/* Note: this VID/PID is shared with multiple devices, see the libwacom database
+	   for a list */
 	.vendor = 0x256c,
 	.product = 0x6e,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOOL_PEN,
 	EV_KEY, BTN_TOUCH,
@@ -95,13 +99,13 @@ static int events[] = {
 	EV_MSC, MSC_SCAN,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_HUION_TABLET,
-	.features = LITEST_TABLET | LITEST_HOVER | LITEST_FORCED_PROXOUT,
-	.interface = &interface,
+	    .features = LITEST_TABLET | LITEST_HOVER | LITEST_FORCED_PROXOUT,
+	    .interface = &interface,
 
-	.name = "HUION PenTablet Pen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "HUION PenTablet Pen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-huion-q620m-dial.c 1.30.0-1/test/litest-device-huion-q620m-dial.c
--- 1.28.1-1/test/litest-device-huion-q620m-dial.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-huion-q620m-dial.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = -1, .code = -1 },
@@ -39,38 +39,44 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1, 0, 0, 0 },
 	{ ABS_Y, 0, 1, 0, 0, 0 },
 	{ ABS_MISC, 0, 255, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
-	/* Note: this VID/PID is shared with multiple devices, see the libwacom database for a list */
+	/* Note: this VID/PID is shared with multiple devices, see the libwacom database
+	   for a list */
 	.vendor = 0x256c,
 	.product = 0x006d,
 };
 
+/* clang-format off */
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_0,
 	EV_REL, REL_WHEEL,
 	EV_REL, REL_WHEEL_HI_RES,
 	-1, -1,
 };
+/* clang-format on */
+/* clang-format on */
 
 /* Device from https://gitlab.freedesktop.org/libinput/libinput/-/issues/600 */
 TEST_DEVICE(LITEST_HUION_Q620M_DIAL,
-	.features = LITEST_TABLET_PAD | LITEST_DIAL,
-	.interface = &interface,
+	    .features = LITEST_TABLET_PAD | LITEST_DIAL,
+	    .interface = &interface,
 
-	.name = "HUION Huion Tablet_Q620M Dial",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "ID_INPUT_TABLET_PAD", "1" },
-		{ NULL },
-	},
-)
+	    .name = "HUION Huion Tablet_Q620M Dial",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "ID_INPUT_TABLET_PAD", "1" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-ignored-mouse.c 1.30.0-1/test/litest-device-ignored-mouse.c
--- 1.28.1-1/test/litest-device-ignored-mouse.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-ignored-mouse.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x6019,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -41,17 +42,17 @@ static int events[] = {
 	EV_REL, REL_WHEEL,
 	-1 , -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_IGNORED_MOUSE,
-	.features = LITEST_IGNORED | LITEST_BUTTON | LITEST_RELATIVE,
-	.interface = NULL,
+	    .features = LITEST_IGNORED | LITEST_BUTTON | LITEST_RELATIVE,
+	    .interface = NULL,
 
-	.name = "Ignored Mouse",
-	.id = &input_id,
-	.absinfo = NULL,
-	.events = events,
-	.udev_properties = {
-	  { "LIBINPUT_IGNORE_DEVICE", "1" },
-	  { NULL },
-	},
-)
+	    .name = "Ignored Mouse",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events,
+	    .udev_properties = {
+		    { "LIBINPUT_IGNORE_DEVICE", "1" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-keyboard-all-codes.c 1.30.0-1/test/litest-device-keyboard-all-codes.c
--- 1.28.1-1/test/litest-device-keyboard-all-codes.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-keyboard-all-codes.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,12 +23,13 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 #define NAME "All event codes keyboard"
 
-static bool all_codes_create(struct litest_device *d);
+static bool
+all_codes_create(struct litest_device *d);
 
 static struct input_id input_id = {
 	.bustype = 0x11,
@@ -37,15 +38,14 @@ static struct input_id input_id = {
 };
 
 TEST_DEVICE(LITEST_KEYBOARD_ALL_CODES,
-	.features = LITEST_KEYS,
-	.interface = NULL,
-	.create = all_codes_create,
-
-	.name = NAME,
-	.id = &input_id,
-	.events = NULL,
-	.absinfo = NULL,
-)
+	    .features = LITEST_KEYS,
+	    .interface = NULL,
+	    .create = all_codes_create,
+
+	    .name = NAME,
+	    .id = &input_id,
+	    .events = NULL,
+	    .absinfo = NULL, )
 
 static bool
 all_codes_create(struct litest_device *d)
diff -pruN 1.28.1-1/test/litest-device-keyboard-quirked.c 1.30.0-1/test/litest-device-keyboard-quirked.c
--- 1.28.1-1/test/litest-device-keyboard-quirked.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-keyboard-quirked.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x11,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x1,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_REL, REL_X,
 	EV_REL, REL_Y,
@@ -197,58 +198,56 @@ static int events[] = {
 
 	-1, -1,
 };
+/* clang-format on */
 
 static const char quirk_file[] =
-"[litest Quirked Keyboard enable buttons]\n"
-"MatchName=litest Quirked Keyboard\n"
-"AttrEventCode=+BTN_RIGHT;+BTN_MIDDLE;+EV_KEY:0x110\n" /* BTN_LEFT */
-"\n"
-"[litest Quirked Keyboard disable buttons]\n"
-"MatchName=litest Quirked Keyboard\n"
-"AttrEventCode=-BTN_MIDDLE;-BTN_RIGHT\n"
-"\n"
-"[litest Quirked Keyboard re-enable buttons]\n"
-"MatchName=litest Quirked Keyboard\n"
-"AttrEventCode=+BTN_RIGHT\n"
-"\n"
-"[litest Quirked keyboard disable F1-F3]\n"
-"MatchName=litest Quirked Keyboard\n"
-"AttrEventCode=-KEY_F1;-EV_KEY:0x3c;-KEY_F3\n"
-#if HAVE_LIBEVDEV_DISABLE_PROPERTY
-"\n"
-"[litest Quirked keyboard enable buttonpad]\n"
-"MatchName=litest Quirked Keyboard\n"
-"AttrInputProp=+INPUT_PROP_BUTTONPAD\n"
-"\n"
-"[litest Quirked keyboard disable pointingstick]\n"
-"MatchName=litest Quirked Keyboard\n"
-"AttrInputProp=-INPUT_PROP_POINTING_STICK\n"
-"\n"
-"[litest Quirked keyboard enable direct]\n"
-"MatchName=litest Quirked Keyboard\n"
-"AttrInputProp=+INPUT_PROP_DIRECT\n"
-"\n"
-"[litest Quirked keyboard disable direct]\n"
-"MatchName=litest Quirked Keyboard\n"
-"AttrInputProp=-INPUT_PROP_DIRECT\n"
-"\n"
-"[litest Quirked keyboard disable semi-mt]\n"
-"MatchName=litest Quirked Keyboard\n"
-"AttrInputProp=-INPUT_PROP_SEMI_MT\n"
-"\n"
-"[litest Quirked keyboard enable semi-mt]\n"
-"MatchName=litest Quirked Keyboard\n"
-"AttrInputProp=+INPUT_PROP_SEMI_MT\n"
-#endif
-;
+	"[litest Quirked Keyboard enable buttons]\n"
+	"MatchName=litest Quirked Keyboard\n"
+	"AttrEventCode=+BTN_RIGHT;+BTN_MIDDLE;+EV_KEY:0x110\n" /* BTN_LEFT */
+	"\n"
+	"[litest Quirked Keyboard disable buttons]\n"
+	"MatchName=litest Quirked Keyboard\n"
+	"AttrEventCode=-BTN_MIDDLE;-BTN_RIGHT\n"
+	"\n"
+	"[litest Quirked Keyboard re-enable buttons]\n"
+	"MatchName=litest Quirked Keyboard\n"
+	"AttrEventCode=+BTN_RIGHT\n"
+	"\n"
+	"[litest Quirked keyboard disable F1-F3]\n"
+	"MatchName=litest Quirked Keyboard\n"
+	"AttrEventCode=-KEY_F1;-EV_KEY:0x3c;-KEY_F3\n"
+	"\n"
+	"[litest Quirked keyboard enable buttonpad]\n"
+	"MatchName=litest Quirked Keyboard\n"
+	"AttrInputProp=+INPUT_PROP_BUTTONPAD\n"
+	"\n"
+	"[litest Quirked keyboard disable pointingstick]\n"
+	"MatchName=litest Quirked Keyboard\n"
+	"AttrInputProp=-INPUT_PROP_POINTING_STICK\n"
+	"\n"
+	"[litest Quirked keyboard enable direct]\n"
+	"MatchName=litest Quirked Keyboard\n"
+	"AttrInputProp=+INPUT_PROP_DIRECT\n"
+	"\n"
+	"[litest Quirked keyboard disable direct]\n"
+	"MatchName=litest Quirked Keyboard\n"
+	"AttrInputProp=-INPUT_PROP_DIRECT\n"
+	"\n"
+	"[litest Quirked keyboard disable semi-mt]\n"
+	"MatchName=litest Quirked Keyboard\n"
+	"AttrInputProp=-INPUT_PROP_SEMI_MT\n"
+	"\n"
+	"[litest Quirked keyboard enable semi-mt]\n"
+	"MatchName=litest Quirked Keyboard\n"
+	"AttrInputProp=+INPUT_PROP_SEMI_MT\n";
 
 TEST_DEVICE(LITEST_KEYBOARD_QUIRKED,
-	.features = LITEST_KEYS | LITEST_IGNORED, /* Only use this keyboard in specific tests */
-	.interface = NULL,
-
-	.name = "Quirked Keyboard",
-	.id = &input_id,
-	.events = events,
-	.absinfo = NULL,
-	.quirk_file = quirk_file,
-)
+	    .features = LITEST_KEYS |
+			LITEST_IGNORED, /* Only use this keyboard in specific tests */
+	    .interface = NULL,
+
+	    .name = "Quirked Keyboard",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = NULL,
+	    .quirk_file = quirk_file, )
diff -pruN 1.28.1-1/test/litest-device-keyboard-razer-blackwidow.c 1.30.0-1/test/litest-device-keyboard-razer-blackwidow.c
--- 1.28.1-1/test/litest-device-keyboard-razer-blackwidow.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-keyboard-razer-blackwidow.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 /* Recording from https://bugs.freedesktop.org/show_bug.cgi?id=89783
  * This is the second of 4 devices exported by this keyboard, the first is
@@ -38,6 +38,7 @@ static struct input_id input_id = {
 	.product = 0x11b,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_REL, REL_HWHEEL,
 	EV_KEY, KEY_ESC,
@@ -299,7 +300,9 @@ static int events[] = {
 	EV_MSC, MSC_SCAN,
 	-1 , -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_VOLUME, 0, 572, 0, 0, 0 },
 	{ ABS_MISC, 0, 255, 0, 0, 0 },
@@ -331,13 +334,13 @@ static struct input_absinfo absinfo[] =
 	{ 0x3f, 0, 255, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_KEYBOARD_BLACKWIDOW,
-	.features = LITEST_KEYS | LITEST_WHEEL,
-	.interface = NULL,
+	    .features = LITEST_KEYS | LITEST_WHEEL,
+	    .interface = NULL,
 
-	.name = "Razer Razer BlackWidow 2013",
-	.id = &input_id,
-	.absinfo = absinfo,
-	.events = events,
-)
+	    .name = "Razer Razer BlackWidow 2013",
+	    .id = &input_id,
+	    .absinfo = absinfo,
+	    .events = events, )
diff -pruN 1.28.1-1/test/litest-device-keyboard-razer-blade-stealth-videoswitch.c 1.30.0-1/test/litest-device-keyboard-razer-blade-stealth-videoswitch.c
--- 1.28.1-1/test/litest-device-keyboard-razer-blade-stealth-videoswitch.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-keyboard-razer-blade-stealth-videoswitch.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 /* Recording from https://bugs.freedesktop.org/show_bug.cgi?id=102039
  * This is the 'video switch' of 2 devices exported by this keyboard.
@@ -36,6 +36,7 @@ static struct input_id input_id = {
 	.product = 0x220,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, KEY_ESC,
 	EV_KEY, KEY_1,
@@ -206,12 +207,12 @@ static int events[] = {
 	EV_MSC, MSC_SCAN,
 	-1 , -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_KEYBOARD_BLADE_STEALTH_VIDEOSWITCH,
-	.features = LITEST_KEYS,
-	.interface = NULL,
+	    .features = LITEST_KEYS,
+	    .interface = NULL,
 
-	.name = "Razer Razer Blade Stealth",
-	.id = &input_id,
-	.events = events,
-)
+	    .name = "Razer Razer Blade Stealth",
+	    .id = &input_id,
+	    .events = events, )
diff -pruN 1.28.1-1/test/litest-device-keyboard-razer-blade-stealth.c 1.30.0-1/test/litest-device-keyboard-razer-blade-stealth.c
--- 1.28.1-1/test/litest-device-keyboard-razer-blade-stealth.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-keyboard-razer-blade-stealth.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 /* Recording from https://bugs.freedesktop.org/show_bug.cgi?id=102039
  * This is the 'normal keyboard' of 2 devices exported by this keyboard.
@@ -36,6 +36,7 @@ static struct input_id input_id = {
 	.product = 0x220,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_REL, REL_HWHEEL,
 	EV_KEY, BTN_0,
@@ -297,7 +298,9 @@ static int events[] = {
 	EV_MSC, MSC_SCAN,
 	-1 , -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_VOLUME, 0, 572, 0, 0, 0 },
 	{ ABS_MISC, 0, 255, 0, 0, 0 },
@@ -329,13 +332,13 @@ static struct input_absinfo absinfo[] =
 	{ 0x3f, 0, 255, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_KEYBOARD_BLADE_STEALTH,
-	.features = LITEST_KEYS | LITEST_WHEEL,
-	.interface = NULL,
+	    .features = LITEST_KEYS | LITEST_WHEEL,
+	    .interface = NULL,
 
-	.name = "Razer Razer Blade Stealth",
-	.id = &input_id,
-	.absinfo = absinfo,
-	.events = events,
-)
+	    .name = "Razer Razer Blade Stealth",
+	    .id = &input_id,
+	    .absinfo = absinfo,
+	    .events = events, )
diff -pruN 1.28.1-1/test/litest-device-keyboard.c 1.30.0-1/test/litest-device-keyboard.c
--- 1.28.1-1/test/litest-device-keyboard.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-keyboard.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x11,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x1,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, KEY_ESC,
 	EV_KEY, KEY_1,
@@ -188,15 +189,17 @@ static int events[] = {
 	EV_LED, LED_NUML,
 	EV_LED, LED_CAPSL,
 	EV_LED, LED_SCROLLL,
+
+	EV_MSC, MSC_SCAN,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_KEYBOARD,
-	.features = LITEST_KEYS,
-	.interface = NULL,
+	    .features = LITEST_KEYS,
+	    .interface = NULL,
 
-	.name = "AT Translated Set 2 keyboard",
-	.id = &input_id,
-	.events = events,
-	.absinfo = NULL,
-)
+	    .name = "AT Translated Set 2 keyboard",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = NULL, )
diff -pruN 1.28.1-1/test/litest-device-lenovo-scrollpoint.c 1.30.0-1/test/litest-device-lenovo-scrollpoint.c
--- 1.28.1-1/test/litest-device-lenovo-scrollpoint.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-lenovo-scrollpoint.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x3109,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -43,17 +44,17 @@ static int events[] = {
 	EV_REL, REL_HWHEEL,
 	-1 , -1,
 };
+/* clang-format on */
 
 /* Note: device is not tagged with LITEST_WHEEL to avoid running the
  * "standard" wheel tests. Device has a custom wheel
  * behavior that is tested directly.
  */
 TEST_DEVICE(LITEST_LENOVO_SCROLLPOINT,
-	.features = LITEST_RELATIVE | LITEST_BUTTON,
-	.interface = NULL,
+	    .features = LITEST_RELATIVE | LITEST_BUTTON,
+	    .interface = NULL,
 
-	.name = "HID 04b3:3109",
-	.id = &input_id,
-	.absinfo = NULL,
-	.events = events,
-)
+	    .name = "HID 04b3:3109",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events, )
diff -pruN 1.28.1-1/test/litest-device-lid-switch-surface3.c 1.30.0-1/test/litest-device-lid-switch-surface3.c
--- 1.28.1-1/test/litest-device-lid-switch-surface3.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-lid-switch-surface3.c	2025-11-25 03:40:43.000000000 +0000
@@ -24,8 +24,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x19,
@@ -33,28 +33,29 @@ static struct input_id input_id = {
 	.product = 0x5,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_SW, SW_LID,
 	-1, -1,
 };
+/* clang-format on */
 
 static const char quirk_file[] =
-"[litest Surface Lid]\n"
-"MatchName=litest Lid Switch Surface3\n"
-"AttrLidSwitchReliability=write_open\n";
+	"[litest Surface Lid]\n"
+	"MatchName=litest Lid Switch Surface3\n"
+	"AttrLidSwitchReliability=write_open\n";
 
 TEST_DEVICE(LITEST_LID_SWITCH_SURFACE3,
-	.features = LITEST_SWITCH,
-	.interface = NULL,
+	    .features = LITEST_SWITCH,
+	    .interface = NULL,
 
-	.name = "Lid Switch Surface3",
-	.id = &input_id,
-	.events = events,
-	.absinfo = NULL,
-
-	.quirk_file = quirk_file,
-	.udev_properties = {
-		{ "ID_INPUT_SWITCH", "1" },
-		{ NULL },
-	},
-)
+	    .name = "Lid Switch Surface3",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = NULL,
+
+	    .quirk_file = quirk_file,
+	    .udev_properties = {
+		    { "ID_INPUT_SWITCH", "1" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-lid-switch.c 1.30.0-1/test/litest-device-lid-switch.c
--- 1.28.1-1/test/litest-device-lid-switch.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-lid-switch.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x19,
@@ -32,28 +32,29 @@ static struct input_id input_id = {
 	.product = 0x5,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_SW, SW_LID,
 	-1, -1,
 };
+/* clang-format on */
 
 static const char quirk_file[] =
-"[litest Lid Switch]\n"
-"MatchName=litest Lid Switch\n"
-"AttrLidSwitchReliability=reliable\n";
+	"[litest Lid Switch]\n"
+	"MatchName=litest Lid Switch\n"
+	"AttrLidSwitchReliability=reliable\n";
 
 TEST_DEVICE(LITEST_LID_SWITCH,
-	.features = LITEST_SWITCH,
-	.interface = NULL,
+	    .features = LITEST_SWITCH,
+	    .interface = NULL,
 
-	.name = "Lid Switch",
-	.id = &input_id,
-	.events = events,
-	.absinfo = NULL,
-
-	.quirk_file = quirk_file,
-	.udev_properties = {
-		{ "ID_INPUT_SWITCH", "1" },
-		{ NULL },
-	},
-)
+	    .name = "Lid Switch",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = NULL,
+
+	    .quirk_file = quirk_file,
+	    .udev_properties = {
+		    { "ID_INPUT_SWITCH", "1" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-logitech-media-keyboard-elite.c 1.30.0-1/test/litest-device-logitech-media-keyboard-elite.c
--- 1.28.1-1/test/litest-device-logitech-media-keyboard-elite.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-logitech-media-keyboard-elite.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,9 +26,8 @@
 #include <assert.h>
 
 #include "libinput-util.h"
-
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 /* Description taken from
  * https://gitlab.freedesktop.org/libinput/libinput/-/issues/514
@@ -40,6 +39,7 @@ static struct input_id input_id = {
 	.product = 0x30f,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, KEY_MUTE,
 	EV_KEY, KEY_VOLUMEDOWN,
@@ -79,13 +79,13 @@ static int events[] = {
 	EV_MSC, MSC_SCAN,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_KEYBOARD_LOGITECH_MEDIA_KEYBOARD_ELITE,
-	.features = LITEST_KEYS,
-	.interface = NULL,
+	    .features = LITEST_KEYS,
+	    .interface = NULL,
 
-	.name = "Logitech Logitech USB Keyboard Consumer Control",
-	.id = &input_id,
-	.events = events,
-	.absinfo = NULL,
-)
+	    .name = "Logitech Logitech USB Keyboard Consumer Control",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = NULL, )
diff -pruN 1.28.1-1/test/litest-device-logitech-trackball.c 1.30.0-1/test/litest-device-logitech-trackball.c
--- 1.28.1-1/test/litest-device-logitech-trackball.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-logitech-trackball.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0xc408,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -42,13 +43,13 @@ static int events[] = {
 	EV_REL, REL_Y,
 	-1 , -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_LOGITECH_TRACKBALL,
-	.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_TRACKBALL,
-	.interface = NULL,
+	    .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_TRACKBALL,
+	    .interface = NULL,
 
-	.name = "Logitech USB Trackball",
-	.id = &input_id,
-	.absinfo = NULL,
-	.events = events,
-)
+	    .name = "Logitech USB Trackball",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events, )
diff -pruN 1.28.1-1/test/litest-device-magic-trackpad.c 1.30.0-1/test/litest-device-magic-trackpad.c
--- 1.28.1-1/test/litest-device-magic-trackpad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-magic-trackpad.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,11 +23,11 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
@@ -74,6 +74,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, -2909, 3167, 4, 0, 46 },
 	{ ABS_Y, -2456, 2565, 4, 0, 45 },
@@ -86,6 +87,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x5,
@@ -93,6 +95,7 @@ static struct input_id input_id = {
 	.product = 0x30e,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_TOOL_FINGER,
@@ -104,19 +107,19 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
 	-1, -1
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_MAGIC_TRACKPAD,
-	.features = LITEST_TOUCHPAD | LITEST_CLICKPAD |
-		    LITEST_BUTTON | LITEST_APPLE_CLICKPAD,
-	.interface = &interface,
-
-	.name = "Apple Wireless Trackpad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-
-	.udev_properties = {
-		{ "ID_INPUT_TOUCHPAD_INTEGRATION", "external" },
-		{ NULL },
-	},
-)
+	    .features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON |
+			LITEST_APPLE_CLICKPAD,
+	    .interface = &interface,
+
+	    .name = "Apple Wireless Trackpad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+
+	    .udev_properties = {
+		    { "ID_INPUT_TOUCHPAD_INTEGRATION", "external" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-mouse-low-dpi.c 1.30.0-1/test/litest-device-mouse-low-dpi.c
--- 1.28.1-1/test/litest-device-mouse-low-dpi.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-mouse-low-dpi.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x1,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -44,17 +45,17 @@ static int events[] = {
 	EV_REL, REL_HWHEEL_HI_RES,
 	-1 , -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_MOUSE_LOW_DPI,
-	.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
-	.interface = NULL,
+	    .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
+	    .interface = NULL,
 
-	.name = "Low DPI Mouse",
-	.id = &input_id,
-	.absinfo = NULL,
-	.events = events,
-	.udev_properties = {
-		{ "MOUSE_DPI", "400@125" },
-		{ NULL },
-	},
-)
+	    .name = "Low DPI Mouse",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events,
+	    .udev_properties = {
+		    { "MOUSE_DPI", "400@125" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-mouse-ps2.c 1.30.0-1/test/litest-device-mouse-ps2.c
--- 1.28.1-1/test/litest-device-mouse-ps2.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/test/litest-device-mouse-ps2.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,58 @@
+
+/*
+ * Copyright © 2013 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "litest-int.h"
+#include "litest.h"
+
+static struct input_id input_id = {
+	.bustype = 0x11,
+	.vendor = 0x0002,
+	.product = 0x0006,
+};
+
+/* clang-format off */
+static int events[] = {
+	EV_KEY, BTN_LEFT,
+	EV_KEY, BTN_RIGHT,
+	EV_KEY, BTN_MIDDLE,
+	EV_KEY, BTN_SIDE,
+	EV_KEY, BTN_EXTRA,
+	EV_REL, REL_X,
+	EV_REL, REL_Y,
+	EV_REL, REL_WHEEL,
+	EV_REL, REL_HWHEEL,
+	-1 , -1,
+};
+/* clang-format on */
+
+TEST_DEVICE(LITEST_MOUSE_PS2,
+	    .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
+	    .interface = NULL,
+
+	    .name = "ImExPS/2 Generic Explorer Mouse",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events, )
diff -pruN 1.28.1-1/test/litest-device-mouse-roccat.c 1.30.0-1/test/litest-device-mouse-roccat.c
--- 1.28.1-1/test/litest-device-mouse-roccat.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-mouse-roccat.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x2e22,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_REL, REL_X,
 	EV_REL, REL_Y,
@@ -174,22 +175,24 @@ static int events[] = {
 	EV_KEY, KEY_BRIGHTNESS_MAX,
 	-1 , -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_VOLUME, 0, 572, 0, 0, 0 },
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ ABS_MISC + 1, 0, 0, 0, 0, 0 },
 	{ ABS_MISC + 2, 0, 0, 0, 0, 0 },
 	{ ABS_MISC + 3, 0, 0, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_MOUSE_ROCCAT,
-	.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL | LITEST_KEYS,
-	.interface = NULL,
+	    .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL | LITEST_KEYS,
+	    .interface = NULL,
 
-	.name = "ROCCAT ROCCAT Kone XTD",
-	.id = &input_id,
-	.absinfo = absinfo,
-	.events = events,
-)
+	    .name = "ROCCAT ROCCAT Kone XTD",
+	    .id = &input_id,
+	    .absinfo = absinfo,
+	    .events = events, )
diff -pruN 1.28.1-1/test/litest-device-mouse-virtual.c 1.30.0-1/test/litest-device-mouse-virtual.c
--- 1.28.1-1/test/litest-device-mouse-virtual.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/test/litest-device-mouse-virtual.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2025 Ryan Hendrickson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "litest-int.h"
+#include "litest.h"
+
+static struct input_id input_id = {
+	.bustype = 0x11,
+	.vendor = 0x1,
+	.product = 0x2,
+};
+
+/* clang-format off */
+static int events[] = {
+	EV_REL, REL_WHEEL,
+	EV_REL, REL_WHEEL_HI_RES,
+	-1, -1,
+};
+/* clang-format on */
+
+static const char quirk_file[] =
+	"[litest Virtual Mouse is virtual]\n"
+	"MatchName=litest Virtual Mouse\n"
+	"AttrIsVirtual=1\n";
+
+TEST_DEVICE(LITEST_MOUSE_VIRTUAL,
+	    .features = LITEST_WHEEL |
+			LITEST_IGNORED, /* Only needed for mouse wheel tests */
+	    .interface = NULL,
+
+	    .name = "Virtual Mouse",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = NULL,
+	    .quirk_file = quirk_file, )
diff -pruN 1.28.1-1/test/litest-device-mouse-wheel-click-angle.c 1.30.0-1/test/litest-device-mouse-wheel-click-angle.c
--- 1.28.1-1/test/litest-device-mouse-wheel-click-angle.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-mouse-wheel-click-angle.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x5678,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -41,19 +42,19 @@ static int events[] = {
 	EV_REL, REL_WHEEL,
 	-1 , -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_MOUSE_WHEEL_CLICK_ANGLE,
-	.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
-	.interface = NULL,
+	    .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
+	    .interface = NULL,
 
-	.name = "Wheel Click Angle Mouse",
-	.id = &input_id,
-	.absinfo = NULL,
-	.events = events,
-
-	.udev_properties = {
-		{ "MOUSE_WHEEL_CLICK_ANGLE", "-7" },
-		{ "MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL", "13" },
-		{ NULL },
-	}
-)
+	    .name = "Wheel Click Angle Mouse",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events,
+
+	    .udev_properties = {
+		    { "MOUSE_WHEEL_CLICK_ANGLE", "-7" },
+		    { "MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL", "13" },
+		    { NULL },
+	    })
diff -pruN 1.28.1-1/test/litest-device-mouse-wheel-click-count.c 1.30.0-1/test/litest-device-mouse-wheel-click-count.c
--- 1.28.1-1/test/litest-device-mouse-wheel-click-count.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-mouse-wheel-click-count.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x5678,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -41,20 +42,20 @@ static int events[] = {
 	EV_REL, REL_WHEEL,
 	-1 , -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_MOUSE_WHEEL_CLICK_COUNT,
-	.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
-	.interface = NULL,
+	    .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
+	    .interface = NULL,
 
-	.name = "Wheel Click Count Mouse",
-	.id = &input_id,
-	.absinfo = NULL,
-	.events = events,
-	.udev_properties = {
-		{ "MOUSE_WHEEL_CLICK_ANGLE", "-15" },
-		{ "MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL", "13" },
-		{ "MOUSE_WHEEL_CLICK_COUNT", "-14" },
-		{ "MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL", "27" },
-		{ NULL },
-	}
-)
+	    .name = "Wheel Click Count Mouse",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events,
+	    .udev_properties = {
+		    { "MOUSE_WHEEL_CLICK_ANGLE", "-15" },
+		    { "MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL", "13" },
+		    { "MOUSE_WHEEL_CLICK_COUNT", "-14" },
+		    { "MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL", "27" },
+		    { NULL },
+	    })
diff -pruN 1.28.1-1/test/litest-device-mouse-wheel-hires-disabled.c 1.30.0-1/test/litest-device-mouse-wheel-hires-disabled.c
--- 1.28.1-1/test/litest-device-mouse-wheel-hires-disabled.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/test/litest-device-mouse-wheel-hires-disabled.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2013 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "litest-int.h"
+#include "litest.h"
+
+static struct input_id input_id = {
+	.bustype = 0x3,
+	.vendor = 0x1234,
+	.product = 0xabcd,
+};
+
+/* clang-format off */
+static int events[] = {
+	EV_KEY, BTN_LEFT,
+	EV_KEY, BTN_RIGHT,
+	EV_KEY, BTN_MIDDLE,
+	EV_REL, REL_X,
+	EV_REL, REL_Y,
+	EV_REL, REL_WHEEL,
+	EV_REL, REL_WHEEL_HI_RES,
+	EV_REL, REL_HWHEEL,
+	EV_REL, REL_HWHEEL_HI_RES,
+	-1 , -1,
+};
+/* clang-format on */
+
+static const char quirk_file[] =
+	"[litest hires wheel disabled mouse]\n"
+	"MatchName=litest Mouse with disabled high-res wheels\n"
+	"AttrEventCode=-REL_WHEEL_HI_RES;-REL_HWHEEL_HI_RES\n";
+
+TEST_DEVICE(LITEST_MOUSE_WHEEL_HIRES_DISABLED,
+	    .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
+	    .interface = NULL,
+
+	    .name = "Mouse with disabled high-res wheels",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events,
+	    .quirk_file = quirk_file, )
diff -pruN 1.28.1-1/test/litest-device-mouse-wheel-tilt.c 1.30.0-1/test/litest-device-mouse-wheel-tilt.c
--- 1.28.1-1/test/litest-device-mouse-wheel-tilt.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-mouse-wheel-tilt.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x6019,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -42,18 +43,18 @@ static int events[] = {
 	EV_REL, REL_HWHEEL,
 	-1 , -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_MOUSE_WHEEL_TILT,
-	.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
-	.interface = NULL,
+	    .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
+	    .interface = NULL,
 
-	.name = "Wheel Tilt Mouse",
-	.id = &input_id,
-	.absinfo = NULL,
-	.events = events,
-	.udev_properties = {
-		{ "MOUSE_WHEEL_TILT_HORIZONTAL", "1" },
-		{ "MOUSE_WHEEL_TILT_VERTICAL", "1" },
-		{ NULL },
-	}
-)
+	    .name = "Wheel Tilt Mouse",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events,
+	    .udev_properties = {
+		    { "MOUSE_WHEEL_TILT_HORIZONTAL", "1" },
+		    { "MOUSE_WHEEL_TILT_VERTICAL", "1" },
+		    { NULL },
+	    })
diff -pruN 1.28.1-1/test/litest-device-mouse.c 1.30.0-1/test/litest-device-mouse.c
--- 1.28.1-1/test/litest-device-mouse.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-mouse.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x6019,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -42,13 +43,13 @@ static int events[] = {
 	EV_REL, REL_WHEEL_HI_RES,
 	-1 , -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_MOUSE,
-	.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
-	.interface = NULL,
+	    .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL,
+	    .interface = NULL,
 
-	.name = "Lenovo Optical USB Mouse",
-	.id = &input_id,
-	.absinfo = NULL,
-	.events = events,
-)
+	    .name = "Lenovo Optical USB Mouse",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events, )
diff -pruN 1.28.1-1/test/litest-device-ms-nano-transceiver-mouse.c 1.30.0-1/test/litest-device-ms-nano-transceiver-mouse.c
--- 1.28.1-1/test/litest-device-ms-nano-transceiver-mouse.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-ms-nano-transceiver-mouse.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x0800,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -45,13 +46,14 @@ static int events[] = {
 	EV_REL, REL_HWHEEL,
 	-1 , -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_MS_NANO_TRANSCEIVER_MOUSE,
-	.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL | LITEST_NO_DEBOUNCE,
-	.interface = NULL,
-
-	.name = "Microsoft Microsoft® Nano Transceiver v2.0",
-	.id = &input_id,
-	.absinfo = NULL,
-	.events = events,
-)
+	    .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_WHEEL |
+			LITEST_NO_DEBOUNCE,
+	    .interface = NULL,
+
+	    .name = "Microsoft Microsoft® Nano Transceiver v2.0",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events, )
diff -pruN 1.28.1-1/test/litest-device-ms-surface-cover.c 1.30.0-1/test/litest-device-ms-surface-cover.c
--- 1.28.1-1/test/litest-device-ms-surface-cover.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-ms-surface-cover.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -52,6 +52,7 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1022, 0, 0, 12 },
 	{ ABS_Y, 0, 487, 0, 0, 12 },
@@ -85,6 +86,7 @@ static struct input_absinfo absinfo[] =
 	{ 62, -127, 127, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -92,6 +94,7 @@ static struct input_id input_id = {
 	.product = 0x7dc,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_REL, REL_X,
 	EV_REL, REL_Y,
@@ -369,13 +372,14 @@ static int events[] = {
 	EV_LED, LED_SCROLLL,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_MS_SURFACE_COVER,
-	.features = LITEST_KEYS | LITEST_ABSOLUTE | LITEST_RELATIVE | LITEST_FAKE_MT | LITEST_BUTTON | LITEST_WHEEL,
-	.interface = &interface,
+	    .features = LITEST_KEYS | LITEST_ABSOLUTE | LITEST_RELATIVE |
+			LITEST_FAKE_MT | LITEST_BUTTON | LITEST_WHEEL,
+	    .interface = &interface,
 
-	.name = "Microsoft Surface Type Cover",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "Microsoft Surface Type Cover",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-nexus4-touch-screen.c 1.30.0-1/test/litest-device-nexus4-touch-screen.c
--- 1.28.1-1/test/litest-device-nexus4-touch-screen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-nexus4-touch-screen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
@@ -52,6 +52,7 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1500, 0, 0, 0 },
 	{ ABS_Y, 0, 2500, 0, 0, 0 },
@@ -63,6 +64,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x1,
@@ -70,18 +72,19 @@ static struct input_id input_id = {
 	.product = 0x26,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOUCH,
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_NEXUS4_TOUCH_SCREEN,
-	.features = LITEST_TOUCH|LITEST_ELLIPSE,
-	.interface = &interface,
+	    .features = LITEST_TOUCH | LITEST_ELLIPSE,
+	    .interface = &interface,
 
-	.name = "Nexus 4 touch screen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "Nexus 4 touch screen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-ploopy-pavonis-stylus.c 1.30.0-1/test/litest-device-ploopy-pavonis-stylus.c
--- 1.28.1-1/test/litest-device-ploopy-pavonis-stylus.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/test/litest-device-ploopy-pavonis-stylus.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,130 @@
+
+/*
+ * Copyright © 2025 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:
+ *
+ * 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.
+ */
+
+/* Device from https://gitlab.freedesktop.org/libinput/libinput/-/issues/1152 */
+
+#include "config.h"
+
+#include "litest-int.h"
+#include "litest.h"
+
+/*
+ * Copyright © 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "litest-int.h"
+#include "litest.h"
+
+static struct input_event proximity_in[] = {
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_KEY, .code = LITEST_BTN_TOOL_AUTO, .value = 1 },
+	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+	{ .type = -1, .code = -1 },
+};
+
+static struct input_event proximity_out[] = {
+	{ .type = EV_ABS, .code = ABS_X, .value = 0 },
+	{ .type = EV_ABS, .code = ABS_Y, .value = 0 },
+	{ .type = EV_KEY, .code = LITEST_BTN_TOOL_AUTO, .value = 0 },
+	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+	{ .type = -1, .code = -1 },
+};
+
+static struct input_event motion[] = {
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
+	{ .type = -1, .code = -1 },
+};
+
+static int
+get_axis_default(struct litest_device *d, unsigned int evcode, int32_t *value)
+{
+	abort();
+	return 1;
+}
+
+static struct litest_device_interface interface = {
+	.tablet_proximity_in_events = proximity_in,
+	.tablet_proximity_out_events = proximity_out,
+	.tablet_motion_events = motion,
+
+	.get_axis_default = get_axis_default,
+};
+
+/* clang-format off */
+static struct input_absinfo absinfo[] = {
+	{ ABS_X, 0, 15600, 0, 0, 100 },
+	{ ABS_Y, 0, 9900, 0, 0, 100 },
+	{ .value = -1 },
+};
+/* clang-format on */
+
+static struct input_id input_id = {
+	.bustype = 0x3,
+	.vendor = 0xfeed,
+	.product = 0x0000,
+};
+
+/* clang-format off */
+static int events[] = {
+	EV_KEY, BTN_TOOL_PEN,
+	EV_KEY, BTN_TOUCH,
+	EV_KEY, BTN_STYLUS,
+	EV_MSC, MSC_SCAN,
+	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
+	-1, -1,
+};
+/* clang-format on */
+
+TEST_DEVICE(LITEST_PLOOPY_PAVONIS_STYLUS,
+	    .features = LITEST_TABLET,
+	    .interface = &interface,
+
+	    .name = "Ploopy Corporation Ploopy Pavonis Trackpad Stylus",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-protocol-a-touch-screen.c 1.30.0-1/test/litest-device-protocol-a-touch-screen.c
--- 1.28.1-1/test/litest-device-protocol-a-touch-screen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-protocol-a-touch-screen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 #define PROTOCOL_A_MAX_SLOTS 10
 
@@ -164,7 +164,6 @@ protocolA_up(struct litest_device *d, un
 		litest_event(d, EV_ABS, ABS_MT_POSITION_X, s->x);
 		litest_event(d, EV_ABS, ABS_MT_POSITION_Y, s->y);
 		litest_event(d, EV_SYN, SYN_MT_REPORT, 0);
-
 	}
 
 	if (first)
@@ -180,6 +179,7 @@ static struct litest_device_interface in
 	.touch_up = protocolA_up,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 32767, 0, 0, 0 },
 	{ ABS_Y, 0, 32767, 0, 0, 0 },
@@ -188,6 +188,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_PRESSURE, 0, 1, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x18,
@@ -195,20 +196,21 @@ static struct input_id input_id = {
 	.product = 0x20,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOUCH,
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_PROTOCOL_A_SCREEN,
-	.features = LITEST_PROTOCOL_A|LITEST_TOUCH,
-	.create = protocolA_create,
+	    .features = LITEST_PROTOCOL_A | LITEST_TOUCH,
+	    .create = protocolA_create,
 
-	.interface = &interface,
+	    .interface = &interface,
 
-	.name = "Protocol A touch screen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "Protocol A touch screen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-qemu-usb-tablet.c 1.30.0-1/test/litest-device-qemu-usb-tablet.c
--- 1.28.1-1/test/litest-device-qemu-usb-tablet.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-qemu-usb-tablet.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,10 +23,11 @@
 
 #include "config.h"
 
-#include "litest.h"
-#include "litest-int.h"
 #include <assert.h>
 
+#include "litest-int.h"
+#include "litest.h"
+
 static bool
 touch_down(struct litest_device *d, unsigned int slot, double x, double y)
 {
@@ -66,11 +67,13 @@ static struct litest_device_interface in
 	.touch_up = touch_up,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 32767, 0, 0, 0 },
 	{ ABS_Y, 0, 32767, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x03,
@@ -78,6 +81,7 @@ static struct input_id input_id = {
 	.product = 0x01,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -85,13 +89,13 @@ static int events[] = {
 	EV_REL, REL_WHEEL,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_QEMU_TABLET,
-	.features = LITEST_WHEEL | LITEST_BUTTON | LITEST_ABSOLUTE,
-	.interface = &interface,
+	    .features = LITEST_WHEEL | LITEST_BUTTON | LITEST_ABSOLUTE,
+	    .interface = &interface,
 
-	.name = "QEMU 0.12.1 QEMU USB Tablet",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "QEMU 0.12.1 QEMU USB Tablet",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-sony-vaio-keys.c 1.30.0-1/test/litest-device-sony-vaio-keys.c
--- 1.28.1-1/test/litest-device-sony-vaio-keys.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-sony-vaio-keys.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,9 +26,8 @@
 #include <assert.h>
 
 #include "libinput-util.h"
-
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 /* Description taken from
  * https://gitlab.freedesktop.org/libinput/libinput/-/issues/515
@@ -40,6 +39,7 @@ static struct input_id input_id = {
 	.product = 0x00,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, KEY_UP,
 	EV_KEY, KEY_DOWN,
@@ -85,13 +85,13 @@ static int events[] = {
 	EV_MSC, MSC_SCAN,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_SONY_VAIO_KEYS,
-	.features = LITEST_KEYS,
-	.interface = NULL,
+	    .features = LITEST_KEYS,
+	    .interface = NULL,
 
-	.name = "Sony Vaio Keys",
-	.id = &input_id,
-	.events = events,
-	.absinfo = NULL,
-)
+	    .name = "Sony Vaio Keys",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = NULL, )
diff -pruN 1.28.1-1/test/litest-device-synaptics-hover.c 1.30.0-1/test/litest-device-synaptics-hover.c
--- 1.28.1-1/test/litest-device-synaptics-hover.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-synaptics-hover.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,12 +26,11 @@
 #include <assert.h>
 
 #include "libinput-util.h"
-
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
@@ -44,7 +43,7 @@ static struct input_event down[] = {
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
@@ -78,6 +77,7 @@ static struct input_id input_id = {
 	.product = 0x7,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -89,7 +89,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_SEMI_MT,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 1472, 5472, 0, 0, 60 },
 	{ ABS_Y, 1408, 4498, 0, 0, 85 },
@@ -99,15 +101,15 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_X, 1472, 5472, 0, 0, 60 },
 	{ ABS_MT_POSITION_Y, 1408, 4498, 0, 0, 85 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_SYNAPTICS_HOVER_SEMI_MT,
-	.features = LITEST_TOUCHPAD | LITEST_SEMI_MT | LITEST_BUTTON,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD | LITEST_SEMI_MT | LITEST_BUTTON,
+	    .interface = &interface,
 
-	.name = "SynPS/2 Synaptics TouchPad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "SynPS/2 Synaptics TouchPad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-synaptics-i2c.c 1.30.0-1/test/litest-device-synaptics-i2c.c
--- 1.28.1-1/test/litest-device-synaptics-i2c.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-synaptics-i2c.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,11 +23,11 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
@@ -39,7 +39,7 @@ static struct input_event down[] = {
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
@@ -58,6 +58,7 @@ static struct input_id input_id = {
 	.product = 0x76ad,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_TOOL_FINGER,
@@ -68,7 +69,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1216, 0, 0, 12 },
 	{ ABS_Y, 0, 680, 0, 0, 12 },
@@ -76,21 +79,21 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_X, 0, 1216, 0, 0, 12 },
 	{ ABS_MT_POSITION_Y, 0, 680, 0, 0, 12 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 static const char quirk_file[] =
-"[litest Synaptics i2c Touchpad]\n"
-"MatchName=litest DLL0704:01 06CB:76AD Touchpad\n"
-"ModelTouchpadVisibleMarker=1\n";
+	"[litest Synaptics i2c Touchpad]\n"
+	"MatchName=litest DLL0704:01 06CB:76AD Touchpad\n"
+	"ModelTouchpadVisibleMarker=1\n";
 
 TEST_DEVICE(LITEST_SYNAPTICS_I2C,
-	.features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
+	    .interface = &interface,
 
-	.name = "DLL0704:01 06CB:76AD Touchpad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.quirk_file = quirk_file,
-)
+	    .name = "DLL0704:01 06CB:76AD Touchpad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .quirk_file = quirk_file, )
diff -pruN 1.28.1-1/test/litest-device-synaptics-phantomclicks.c 1.30.0-1/test/litest-device-synaptics-phantomclicks.c
--- 1.28.1-1/test/litest-device-synaptics-phantomclicks.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-synaptics-phantomclicks.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,11 +23,11 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
@@ -39,7 +39,7 @@ static struct input_event down[] = {
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
@@ -58,6 +58,7 @@ static struct input_id input_id = {
 	.product = 0x311c,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_TOOL_FINGER,
@@ -70,7 +71,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 4654, 0, 0, 31 },
 	{ ABS_Y, 0, 2730, 0, 0, 31 },
@@ -79,21 +82,21 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_Y, 0, 2730, 0, 0, 31 },
 	{ ABS_MT_TOOL_TYPE, 0, 2, 0, 0, 0 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 static const char quirk_file[] =
-"[litest Dell XPS 15 9500 Touchpad]\n"
-"MatchName=litest DELL097D:00 04F3:311C Touchpad\n"
-"ModelTouchpadVisibleMarker=0\n"
-"ModelTouchpadPhantomClicks=1\n";
+	"[litest Dell XPS 15 9500 Touchpad]\n"
+	"MatchName=litest DELL097D:00 04F3:311C Touchpad\n"
+	"ModelTouchpadVisibleMarker=0\n"
+	"ModelTouchpadPhantomClicks=1\n";
 
 TEST_DEVICE(LITEST_SYNAPTICS_PHANTOMCLICKS,
-	.features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
-	.interface = &interface,
-	.name = "DELL097D:00 04F3:311C Touchpad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.quirk_file = quirk_file,
-)
+	    .features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
+	    .interface = &interface,
+	    .name = "DELL097D:00 04F3:311C Touchpad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .quirk_file = quirk_file, )
diff -pruN 1.28.1-1/test/litest-device-synaptics-pressurepad.c 1.30.0-1/test/litest-device-synaptics-pressurepad.c
--- 1.28.1-1/test/litest-device-synaptics-pressurepad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-synaptics-pressurepad.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 /*
  * Device from https://gitlab.freedesktop.org/libinput/libinput/-/issues/562
@@ -34,7 +34,7 @@
  */
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
@@ -47,7 +47,7 @@ static struct input_event down[] = {
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
@@ -86,6 +86,7 @@ static struct input_id input_id = {
 	.product = 0xce37,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_TOOL_FINGER,
@@ -98,7 +99,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1224, 0, 0, 12 },
 	{ ABS_Y, 0, 756, 0, 0, 12 },
@@ -109,15 +112,15 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ ABS_MT_PRESSURE, 0, 255, 0, 0, 0 }, /* note: resolution zero */
 	{ ABS_MT_TOOL_TYPE, 0, 2, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_SYNAPTICS_PRESSUREPAD,
-	.features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
+	    .interface = &interface,
 
-	.name = "SYNA2B31:00 06CB:CE37 Touchpad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "SYNA2B31:00 06CB:CE37 Touchpad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-synaptics-rmi4.c 1.30.0-1/test/litest-device-synaptics-rmi4.c
--- 1.28.1-1/test/litest-device-synaptics-rmi4.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-synaptics-rmi4.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,13 +23,13 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_ORIENTATION, .value = 0 },
@@ -44,9 +44,9 @@ static struct input_event down[] = {
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_ORIENTATION, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_MT_TOUCH_MAJOR, .value = 2 },
 	{ .type = EV_ABS, .code = ABS_MT_TOUCH_MINOR, .value = 2 },
@@ -82,6 +82,7 @@ static struct input_id input_id = {
 	.product = 0x0,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_TOOL_FINGER,
@@ -94,7 +95,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1940, 0, 0, 20 },
 	{ ABS_Y, 0, 1062, 0, 0, 20 },
@@ -108,15 +111,15 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TOOL_TYPE, 0, 2, 0, 0, 0 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ ABS_MT_PRESSURE, 0, 255, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_SYNAPTICS_RMI4,
-	.features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
+	    .interface = &interface,
 
-	.name = "Synaptics TM3053-004",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "Synaptics TM3053-004",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-synaptics-st.c 1.30.0-1/test/litest-device-synaptics-st.c
--- 1.28.1-1/test/litest-device-synaptics-st.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-synaptics-st.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,14 +23,14 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
-	{ .type = EV_ABS, .code = ABS_TOOL_WIDTH, .value = 7  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
+	{ .type = EV_ABS, .code = ABS_TOOL_WIDTH, .value = 7 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
 };
@@ -66,6 +66,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 1472, 5472, 0, 0, 75 },
 	{ ABS_Y, 1408, 4448, 0, 0, 129 },
@@ -73,6 +74,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_TOOL_WIDTH, 0, 15, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x11,
@@ -80,6 +82,7 @@ static struct input_id input_id = {
 	.product = 0x7,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -87,13 +90,13 @@ static int events[] = {
 	EV_KEY, BTN_TOUCH,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_SYNAPTICS_TOUCHPAD,
-	.features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_SINGLE_TOUCH,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD | LITEST_BUTTON | LITEST_SINGLE_TOUCH,
+	    .interface = &interface,
 
-	.name = "SynPS/2 Synaptics TouchPad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "SynPS/2 Synaptics TouchPad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-synaptics-t440.c 1.30.0-1/test/litest-device-synaptics-t440.c
--- 1.28.1-1/test/litest-device-synaptics-t440.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-synaptics-t440.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,13 +23,13 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
@@ -41,9 +41,9 @@ static struct input_event down[] = {
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
@@ -76,6 +76,7 @@ static struct input_id input_id = {
 	.product = 0x7,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_TOOL_FINGER,
@@ -89,7 +90,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_TOPBUTTONPAD,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 1024, 5112, 0, 0, 42 },
 	{ ABS_Y, 2024, 4832, 0, 0, 42 },
@@ -100,15 +103,16 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_Y, 2024, 4832, 0, 0, 42 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ ABS_MT_PRESSURE, 0, 255, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_SYNAPTICS_TOPBUTTONPAD,
-	.features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON | LITEST_TOPBUTTONPAD,
-	.interface = &interface,
-
-	.name = "SynPS/2 Synaptics TouchPad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON |
+			LITEST_TOPBUTTONPAD,
+	    .interface = &interface,
+
+	    .name = "SynPS/2 Synaptics TouchPad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-synaptics-x1-carbon-3rd.c 1.30.0-1/test/litest-device-synaptics-x1-carbon-3rd.c
--- 1.28.1-1/test/litest-device-synaptics-x1-carbon-3rd.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-synaptics-x1-carbon-3rd.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,30 +23,30 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
 };
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
 };
@@ -76,6 +76,7 @@ static struct input_id input_id = {
 	.product = 0x7,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_TOOL_FINGER,
@@ -91,7 +92,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 1266, 5676, 0, 0, 45 },
 	{ ABS_Y, 1096, 4758, 0, 0, 68 },
@@ -102,21 +105,21 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_Y, 1096, 4758, 0, 0, 68 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ ABS_MT_PRESSURE, 0, 255, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 static const char quirk_file[] =
-"[litest Synaptics X1 Carbon 3rd Touchpad]\n"
-"MatchName=litest SynPS/2 Synaptics TouchPad X1C3rd\n"
-"ModelLenovoT450Touchpad=1\n";
+	"[litest Synaptics X1 Carbon 3rd Touchpad]\n"
+	"MatchName=litest SynPS/2 Synaptics TouchPad X1C3rd\n"
+	"ModelLenovoT450Touchpad=1\n";
 
 TEST_DEVICE(LITEST_SYNAPTICS_TRACKPOINT_BUTTONS,
-	.features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
+	    .interface = &interface,
 
-	.name = "SynPS/2 Synaptics TouchPad X1C3rd",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.quirk_file = quirk_file,
-)
+	    .name = "SynPS/2 Synaptics TouchPad X1C3rd",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .quirk_file = quirk_file, )
diff -pruN 1.28.1-1/test/litest-device-synaptics-x220.c 1.30.0-1/test/litest-device-synaptics-x220.c
--- 1.28.1-1/test/litest-device-synaptics-x220.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-synaptics-x220.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,30 +23,30 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
 };
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
 };
@@ -76,6 +76,7 @@ static struct input_id input_id = {
 	.product = 0x11,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_TOOL_FINGER,
@@ -88,7 +89,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 1472, 5472, 0, 0, 75 },
 	{ ABS_Y, 1408, 4448, 0, 0, 129 },
@@ -99,15 +102,15 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_Y, 1408, 4448, 0, 0, 129 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ ABS_MT_PRESSURE, 0, 255, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_SYNAPTICS_CLICKPAD_X220,
-	.features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON,
+	    .interface = &interface,
 
-	.name = "SynPS/2 Synaptics TouchPad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "SynPS/2 Synaptics TouchPad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-tablet-doubledial.c 1.30.0-1/test/litest-device-tablet-doubledial.c
--- 1.28.1-1/test/litest-device-tablet-doubledial.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-tablet-doubledial.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = -1, .code = -1 },
@@ -39,12 +39,14 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1, 0, 0, 0 },
 	{ ABS_Y, 0, 1, 0, 0, 0 },
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -52,6 +54,7 @@ static struct input_id input_id = {
 	.product = 0x678,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_0,
 	EV_KEY, BTN_1,
@@ -64,17 +67,17 @@ static int events[] = {
 	EV_REL, REL_HWHEEL_HI_RES,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_TABLET_DOUBLEDIAL_PAD,
-	.features = LITEST_TABLET_PAD | LITEST_DIAL,
-	.interface = &interface,
+	    .features = LITEST_TABLET_PAD | LITEST_DIAL,
+	    .interface = &interface,
 
-	.name = "Generic Double Dial Pad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "ID_INPUT_TABLET_PAD", "1" },
-		{ NULL },
-	},
-)
+	    .name = "Generic Double Dial Pad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "ID_INPUT_TABLET_PAD", "1" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-tablet-mode-switch.c 1.30.0-1/test/litest-device-tablet-mode-switch.c
--- 1.28.1-1/test/litest-device-tablet-mode-switch.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-tablet-mode-switch.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x18,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x456,
 };
 
+/* clang-format off */
 static int events[] = {
 	/* buttons are needed - the unreliable quirk removes SW_TABLET_MODE
 	 * so we'd end up with a device with no seat caps and that won't get
@@ -41,24 +42,24 @@ static int events[] = {
 	EV_SW, SW_TABLET_MODE,
 	-1, -1,
 };
+/* clang-format on */
 
 static const char quirk_file[] =
-"[litest unreliable tablet mode switch]\n"
-"MatchName=litest Unreliable Tablet Mode Switch device\n"
-"ModelTabletModeSwitchUnreliable=1\n";
+	"[litest unreliable tablet mode switch]\n"
+	"MatchName=litest Unreliable Tablet Mode Switch device\n"
+	"ModelTabletModeSwitchUnreliable=1\n";
 
 TEST_DEVICE(LITEST_TABLET_MODE_UNRELIABLE,
-	.features = LITEST_SWITCH,
-	.interface = NULL,
+	    .features = LITEST_SWITCH,
+	    .interface = NULL,
 
-	.name = "Unreliable Tablet Mode Switch device",
-	.id = &input_id,
-	.events = events,
-	.absinfo = NULL,
-
-	.quirk_file = quirk_file,
-	.udev_properties = {
-		{ "ID_INPUT_SWITCH", "1" },
-		{ NULL },
-	}
-)
+	    .name = "Unreliable Tablet Mode Switch device",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = NULL,
+
+	    .quirk_file = quirk_file,
+	    .udev_properties = {
+		    { "ID_INPUT_SWITCH", "1" },
+		    { NULL },
+	    })
diff -pruN 1.28.1-1/test/litest-device-tablet-rel-dial.c 1.30.0-1/test/litest-device-tablet-rel-dial.c
--- 1.28.1-1/test/litest-device-tablet-rel-dial.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-tablet-rel-dial.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = -1, .code = -1 },
@@ -39,12 +39,14 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1, 0, 0, 0 },
 	{ ABS_Y, 0, 1, 0, 0, 0 },
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -52,6 +54,7 @@ static struct input_id input_id = {
 	.product = 0x456,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_0,
 	EV_KEY, BTN_1,
@@ -61,17 +64,17 @@ static int events[] = {
 	EV_REL, REL_DIAL,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_TABLET_REL_DIAL_PAD,
-	.features = LITEST_TABLET_PAD | LITEST_DIAL,
-	.interface = &interface,
+	    .features = LITEST_TABLET_PAD | LITEST_DIAL,
+	    .interface = &interface,
 
-	.name = "Generic Rel DialPad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "ID_INPUT_TABLET_PAD", "1" },
-		{ NULL },
-	},
-)
+	    .name = "Generic Rel DialPad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "ID_INPUT_TABLET_PAD", "1" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-thinkpad-extrabuttons.c 1.30.0-1/test/litest-device-thinkpad-extrabuttons.c
--- 1.28.1-1/test/litest-device-thinkpad-extrabuttons.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-thinkpad-extrabuttons.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x19,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x5054,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, KEY_MUTE,
 	EV_KEY, KEY_VOLUMEUP,
@@ -69,17 +70,17 @@ static int events[] = {
 
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_THINKPAD_EXTRABUTTONS,
-	.features = LITEST_KEYS | LITEST_SWITCH,
-	.interface = NULL,
+	    .features = LITEST_KEYS | LITEST_SWITCH,
+	    .interface = NULL,
 
-	.name = "ThinkPad Extra Buttons",
-	.id = &input_id,
-	.events = events,
-	.absinfo = NULL,
-	.udev_properties = {
-		{ "ID_INPUT_SWITCH", "1" },
-		{ NULL },
-	},
-)
+	    .name = "ThinkPad Extra Buttons",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = NULL,
+	    .udev_properties = {
+		    { "ID_INPUT_SWITCH", "1" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-touch-screen.c 1.30.0-1/test/litest-device-touch-screen.c
--- 1.28.1-1/test/litest-device-touch-screen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-touch-screen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
@@ -56,6 +56,7 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 50000, 0, 0, 100 },
 	{ ABS_Y, 0, 33000, 0, 0, 100 },
@@ -69,6 +70,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x1,
@@ -76,18 +78,19 @@ static struct input_id input_id = {
 	.product = 0x25,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOUCH,
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_GENERIC_MULTITOUCH_SCREEN,
-	.features = LITEST_TOUCH|LITEST_ELLIPSE,
-	.interface = &interface,
+	    .features = LITEST_TOUCH | LITEST_ELLIPSE,
+	    .interface = &interface,
 
-	.name = "generic-mt",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "generic-mt",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-touchpad-palm-threshold-zero.c 1.30.0-1/test/litest-device-touchpad-palm-threshold-zero.c
--- 1.28.1-1/test/litest-device-touchpad-palm-threshold-zero.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-touchpad-palm-threshold-zero.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,30 +23,30 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
 };
 
 static struct input_event move[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_X, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = LITEST_AUTO_ASSIGN },
-	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN  },
+	{ .type = EV_ABS, .code = ABS_MT_PRESSURE, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
 };
@@ -76,6 +76,7 @@ static struct input_id input_id = {
 	.product = 0x7,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_TOOL_FINGER,
@@ -91,7 +92,9 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_BUTTONPAD,
 	-1, -1,
 };
+/* clang-format on */
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 1266, 5676, 0, 0, 45 },
 	{ ABS_Y, 1096, 4758, 0, 0, 68 },
@@ -102,21 +105,21 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_POSITION_Y, 1096, 4758, 0, 0, 68 },
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ ABS_MT_PRESSURE, 0, 255, 0, 0, 0 },
-	{ .value = -1 }
+	{ .value = -1 },
 };
+/* clang-format on */
 
 static const char quirk_file[] =
-"[litest TouchpadPalmPressure Zero]\n"
-"MatchName=litest Touchpad PalmPressureThreshold 0\n"
-"AttrPalmPressureThreshold=0\n";
+	"[litest TouchpadPalmPressure Zero]\n"
+	"MatchName=litest Touchpad PalmPressureThreshold 0\n"
+	"AttrPalmPressureThreshold=0\n";
 
 TEST_DEVICE(LITEST_TOUCHPAD_PALMPRESSURE_ZERO,
-	.features = LITEST_IGNORED, /* Only use for specific tests */
-	.interface = &interface,
+	    .features = LITEST_IGNORED, /* Only use for specific tests */
+	    .interface = &interface,
 
-	.name = "Touchpad PalmPressureThreshold 0",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.quirk_file = quirk_file,
-)
+	    .name = "Touchpad PalmPressureThreshold 0",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .quirk_file = quirk_file, )
diff -pruN 1.28.1-1/test/litest-device-touchscreen-fuzz.c 1.30.0-1/test/litest-device-touchscreen-fuzz.c
--- 1.28.1-1/test/litest-device-touchscreen-fuzz.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-touchscreen-fuzz.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
@@ -50,6 +50,7 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1500, 10, 0, 0 },
 	{ ABS_Y, 0, 2500, 12, 0, 0 },
@@ -60,6 +61,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x1,
@@ -67,18 +69,19 @@ static struct input_id input_id = {
 	.product = 0x25,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOUCH,
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_MULTITOUCH_FUZZ_SCREEN,
-	.features = LITEST_TOUCH,
-	.interface = &interface,
+	    .features = LITEST_TOUCH,
+	    .interface = &interface,
 
-	.name = "touchscreen with fuzz",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "touchscreen with fuzz",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-touchscreen-invalid-range.c 1.30.0-1/test/litest-device-touchscreen-invalid-range.c
--- 1.28.1-1/test/litest-device-touchscreen-invalid-range.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-touchscreen-invalid-range.c	2025-11-25 03:40:43.000000000 +0000
@@ -24,8 +24,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
@@ -51,6 +51,7 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 1000, 2500, 0, 0, 10 },
 	{ ABS_Y, 2000, 4500, 0, 0, 10 },
@@ -60,6 +61,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x1,
@@ -67,18 +69,19 @@ static struct input_id input_id = {
 	.product = 0x25,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOUCH,
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_TOUCHSCREEN_INVALID_RANGE,
-	.features = LITEST_TOUCH,
-	.interface = &interface,
+	    .features = LITEST_TOUCH,
+	    .interface = &interface,
 
-	.name = "touchscreen-invalid-range",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "touchscreen-invalid-range",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-touchscreen-mt-tool.c 1.30.0-1/test/litest-device-touchscreen-mt-tool.c
--- 1.28.1-1/test/litest-device-touchscreen-mt-tool.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-touchscreen-mt-tool.c	2025-11-25 03:40:43.000000000 +0000
@@ -24,8 +24,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_MT_SLOT, .value = LITEST_AUTO_ASSIGN },
@@ -53,6 +53,7 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 1000, 2500, 0, 0, 10 },
 	{ ABS_Y, 2000, 4500, 0, 0, 10 },
@@ -63,6 +64,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x1,
@@ -70,18 +72,19 @@ static struct input_id input_id = {
 	.product = 0x25,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOUCH,
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_TOUCHSCREEN_MT_TOOL_TYPE,
-	.features = LITEST_TOUCH,
-	.interface = &interface,
+	    .features = LITEST_TOUCH,
+	    .interface = &interface,
 
-	.name = "touchscreen-mt-tool-type",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "touchscreen-mt-tool-type",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-trackpoint.c 1.30.0-1/test/litest-device-trackpoint.c
--- 1.28.1-1/test/litest-device-trackpoint.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-trackpoint.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x11,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0xa,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -42,14 +43,13 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_POINTING_STICK,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_TRACKPOINT,
-	.features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_POINTINGSTICK,
-	.interface = NULL,
-
-	.name = "TPPS/2 IBM TrackPoint",
-	.id = &input_id,
-	.absinfo = NULL,
-	.events = events,
+	    .features = LITEST_RELATIVE | LITEST_BUTTON | LITEST_POINTINGSTICK,
+	    .interface = NULL,
 
-)
+	    .name = "TPPS/2 IBM TrackPoint",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events, )
diff -pruN 1.28.1-1/test/litest-device-uclogic-tablet.c 1.30.0-1/test/litest-device-uclogic-tablet.c
--- 1.28.1-1/test/litest-device-uclogic-tablet.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-uclogic-tablet.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -72,20 +72,24 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 32767, 0, 0, 235 },
 	{ ABS_Y, 0, 32767, 0, 0, 323 },
 	{ ABS_PRESSURE, 0, 1023, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
-	/* Note: this VID/PID is shared with multiple devices, see the libwacom database for a list */
+	/* Note: this VID/PID is shared with multiple devices, see the libwacom database
+	   for a list */
 	.vendor = 0x256c,
 	.product = 0x6e,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOUCH,
 	EV_KEY, BTN_STYLUS,
@@ -93,13 +97,13 @@ static int events[] = {
 	EV_MSC, MSC_SCAN,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_UCLOGIC_TABLET,
-	.features = LITEST_TABLET | LITEST_HOVER | LITEST_FORCED_PROXOUT,
-	.interface = &interface,
+	    .features = LITEST_TABLET | LITEST_HOVER | LITEST_FORCED_PROXOUT,
+	    .interface = &interface,
 
-	.name = "uclogic PenTablet Pen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "uclogic PenTablet Pen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-vmware-virtual-usb-mouse.c 1.30.0-1/test/litest-device-vmware-virtual-usb-mouse.c
--- 1.28.1-1/test/litest-device-vmware-virtual-usb-mouse.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-vmware-virtual-usb-mouse.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,10 +23,11 @@
 
 #include "config.h"
 
-#include "litest.h"
-#include "litest-int.h"
 #include <assert.h>
 
+#include "litest-int.h"
+#include "litest.h"
+
 static bool
 touch_down(struct litest_device *d, unsigned int slot, double x, double y)
 {
@@ -66,11 +67,13 @@ static struct litest_device_interface in
 	.touch_up = touch_up,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 32767, 0, 0, 0 },
 	{ ABS_Y, 0, 32767, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x03,
@@ -78,6 +81,7 @@ static struct input_id input_id = {
 	.product = 0x03,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -99,13 +103,14 @@ static int events[] = {
 	EV_REL, REL_HWHEEL,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_VMWARE_VIRTMOUSE,
-	.features = LITEST_WHEEL | LITEST_BUTTON | LITEST_ABSOLUTE | LITEST_NO_DEBOUNCE,
-	.interface = &interface,
-
-	.name = "VMware VMware Virtual USB Mouse",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .features = LITEST_WHEEL | LITEST_BUTTON | LITEST_ABSOLUTE |
+			LITEST_NO_DEBOUNCE,
+	    .interface = &interface,
+
+	    .name = "VMware VMware Virtual USB Mouse",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-wacom-bamboo-16fg-pen.c 1.30.0-1/test/litest-device-wacom-bamboo-16fg-pen.c
--- 1.28.1-1/test/litest-device-wacom-bamboo-16fg-pen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-bamboo-16fg-pen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -40,6 +40,7 @@ static struct input_event proximity_out[
 	{ .type = EV_ABS, .code = ABS_X, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_Y, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_DISTANCE, .value = 0 },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 0 },
 	{ .type = EV_KEY, .code = LITEST_BTN_TOOL_AUTO, .value = 0 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
@@ -76,6 +77,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 14720, 4, 0, 100 },
 	{ ABS_Y, 0, 9200, 4, 0, 100 },
@@ -83,6 +85,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_DISTANCE, 0, 31, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -91,6 +94,7 @@ static struct input_id input_id = {
 	.version = 0x100,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOOL_PEN,
 	EV_KEY, BTN_TOOL_RUBBER,
@@ -100,13 +104,13 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_POINTER,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_BAMBOO_16FG_PEN,
-	.features = LITEST_TABLET | LITEST_DISTANCE | LITEST_HOVER,
-	.interface = &interface,
+	    .features = LITEST_TABLET | LITEST_DISTANCE | LITEST_HOVER,
+	    .interface = &interface,
 
-	.name = "Wacom Bamboo 16FG 4x5 Pen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "Wacom Bamboo 16FG 4x5 Pen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-wacom-bamboo-2fg-finger.c 1.30.0-1/test/litest-device-wacom-bamboo-2fg-finger.c
--- 1.28.1-1/test/litest-device-wacom-bamboo-2fg-finger.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-bamboo-2fg-finger.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -52,6 +52,7 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 15360, 0, 0, 128 },
 	{ ABS_Y, 0, 10240, 0, 0, 128 },
@@ -62,6 +63,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -70,6 +72,7 @@ static struct input_id input_id = {
 	.version = 0x110,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOOL_FINGER,
 	EV_KEY, BTN_TOUCH,
@@ -77,18 +80,19 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_POINTER,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_BAMBOO_2FG_FINGER,
-	.features = LITEST_TOUCHPAD,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD,
+	    .interface = &interface,
 
-	.name = "Wacom Bamboo 2FG 4x5 Finger",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "LIBINPUT_DEVICE_GROUP", "wacom-bamboo-2fg-group" },
-		{ "ID_INPUT_TABLET", "1" },
-		{ "ID_INPUT_TOUCHPAD", "1" },
-	}
-)
+	    .name = "Wacom Bamboo 2FG 4x5 Finger",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "LIBINPUT_DEVICE_GROUP", "wacom-bamboo-2fg-group" },
+		    { "ID_INPUT_TABLET", "1" },
+		    { "ID_INPUT_TOUCHPAD", "1" },
+		    { NULL },
+	    })
diff -pruN 1.28.1-1/test/litest-device-wacom-bamboo-2fg-pad.c 1.30.0-1/test/litest-device-wacom-bamboo-2fg-pad.c
--- 1.28.1-1/test/litest-device-wacom-bamboo-2fg-pad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-bamboo-2fg-pad.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = -1, .code = -1 },
@@ -39,11 +39,13 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1, 0, 0, 0 },
 	{ ABS_Y, 0, 1, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -52,6 +54,7 @@ static struct input_id input_id = {
 	.version = 0x100,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -60,18 +63,18 @@ static int events[] = {
 	EV_KEY, BTN_STYLUS,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_BAMBOO_2FG_PAD,
-	.features = LITEST_TABLET_PAD,
-	.interface = &interface,
+	    .features = LITEST_TABLET_PAD,
+	    .interface = &interface,
 
-	.name = "Wacom Bamboo 2FG 4x5 Pad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ .key = "ID_INPUT_TABLET_PAD", .value = "1" },
-		{ .key = "LIBINPUT_DEVICE_GROUP", .value = "1" },
-		{ NULL }
-	}
-)
+	    .name = "Wacom Bamboo 2FG 4x5 Pad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { .key = "ID_INPUT_TABLET_PAD", .value = "1" },
+		    { .key = "LIBINPUT_DEVICE_GROUP", .value = "1" },
+		    { NULL },
+	    })
diff -pruN 1.28.1-1/test/litest-device-wacom-bamboo-2fg-pen.c 1.30.0-1/test/litest-device-wacom-bamboo-2fg-pen.c
--- 1.28.1-1/test/litest-device-wacom-bamboo-2fg-pen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-bamboo-2fg-pen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -40,6 +40,7 @@ static struct input_event proximity_out[
 	{ .type = EV_ABS, .code = ABS_X, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_Y, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_DISTANCE, .value = 0 },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 0 },
 	{ .type = EV_KEY, .code = LITEST_BTN_TOOL_AUTO, .value = 0 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
@@ -76,6 +77,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 14720, 4, 0, 100 },
 	{ ABS_Y, 0, 9200, 4, 0, 100 },
@@ -83,6 +85,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_DISTANCE, 0, 31, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -91,6 +94,7 @@ static struct input_id input_id = {
 	.version = 0x100,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOOL_PEN,
 	EV_KEY, BTN_TOOL_RUBBER,
@@ -100,17 +104,17 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_POINTER,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_BAMBOO_2FG_PEN,
-	.features = LITEST_TABLET | LITEST_DISTANCE | LITEST_HOVER,
-	.interface = &interface,
+	    .features = LITEST_TABLET | LITEST_DISTANCE | LITEST_HOVER,
+	    .interface = &interface,
 
-	.name = "Wacom Bamboo 2FG 4x5 Pen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "LIBINPUT_DEVICE_GROUP", "wacom-bamboo-2fg-group" },
-		{ NULL },
-	},
-)
+	    .name = "Wacom Bamboo 2FG 4x5 Pen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "LIBINPUT_DEVICE_GROUP", "wacom-bamboo-2fg-group" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-wacom-calibrated-tablet.c 1.30.0-1/test/litest-device-wacom-calibrated-tablet.c
--- 1.28.1-1/test/litest-device-wacom-calibrated-tablet.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-calibrated-tablet.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,13 +23,11 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static bool
-inverter(struct litest_device *dev,
-		 double *x, double *y,
-		 struct axis_replacement *axes)
+inverter(struct litest_device *dev, double *x, double *y, struct axis_replacement *axes)
 {
 	/* Input data is in percent (0-100), so let's swap x and y around.
 	 * With our matrix this should be undone by libinput later
@@ -44,7 +42,8 @@ inverter(struct litest_device *dev,
 static bool
 proximity_in_handler(struct litest_device *dev,
 		     unsigned int tool_type,
-		     double *x, double *y,
+		     double *x,
+		     double *y,
 		     struct axis_replacement *axes)
 {
 	/* let the generic code handle the event */
@@ -63,6 +62,7 @@ static struct input_event proximity_in[]
 static struct input_event proximity_out[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_Y, .value = 0 },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 0 },
 	{ .type = EV_KEY, .code = LITEST_BTN_TOOL_AUTO, .value = 0 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
@@ -100,12 +100,14 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 29476, 4, 0, 100 },
 	{ ABS_Y, 0, 16624, 4, 0, 100 },
 	{ ABS_PRESSURE, 0, 1023, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -114,6 +116,7 @@ static struct input_id input_id = {
 	.version = 0x111,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOOL_PEN,
 	EV_KEY, BTN_TOOL_RUBBER,
@@ -123,17 +126,17 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_CALIBRATED_TABLET_PEN,
-	.features = LITEST_TABLET|LITEST_PRECALIBRATED,
-	.interface = &interface,
+	    .features = LITEST_TABLET | LITEST_PRECALIBRATED,
+	    .interface = &interface,
 
-	.name = "Wacom MultiTouch Sensor Pen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-	{ "LIBINPUT_CALIBRATION_MATRIX", "-1 0 1 0 -1 1" },
-	{ NULL }
-	},
-)
+	    .name = "Wacom MultiTouch Sensor Pen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "LIBINPUT_CALIBRATION_MATRIX", "-1 0 1 0 -1 1" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-wacom-cintiq-12wx-pen.c 1.30.0-1/test/litest-device-wacom-cintiq-12wx-pen.c
--- 1.28.1-1/test/litest-device-wacom-cintiq-12wx-pen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-cintiq-12wx-pen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -44,6 +44,7 @@ static struct input_event proximity_out[
 	{ .type = EV_ABS, .code = ABS_X, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_Y, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_DISTANCE, .value = 0 },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_TILT_X, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_TILT_Y, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 0 },
@@ -91,6 +92,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 53020, 4, 0, 200 },
 	{ ABS_Y, 0, 33440, 4, 0, 200 },
@@ -105,6 +107,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -113,6 +116,7 @@ static struct input_id input_id = {
 	.version = 0x113,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_0,
 	EV_KEY, BTN_1,
@@ -136,13 +140,14 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_CINTIQ_12WX_PEN,
-	.features = LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_SERIAL | LITEST_TILT | LITEST_DIRECT | LITEST_HOVER,
-	.interface = &interface,
-
-	.name = "Wacom Cintiq 12WX",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .features = LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_SERIAL |
+			LITEST_TILT | LITEST_DIRECT | LITEST_HOVER,
+	    .interface = &interface,
+
+	    .name = "Wacom Cintiq 12WX",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-wacom-cintiq-13hdt-finger.c 1.30.0-1/test/litest-device-wacom-cintiq-13hdt-finger.c
--- 1.28.1-1/test/litest-device-wacom-cintiq-13hdt-finger.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-cintiq-13hdt-finger.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -53,6 +53,7 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 2937, 0, 0, 10 },
 	{ ABS_Y, 0, 1652, 0, 0, 10 },
@@ -67,6 +68,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -75,22 +77,23 @@ static struct input_id input_id = {
 	.version = 0x110,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOUCH,
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_CINTIQ_13HDT_FINGER,
-	.features = LITEST_TOUCH,
-	.interface = &interface,
+	    .features = LITEST_TOUCH,
+	    .interface = &interface,
 
-	.name = "Wacom Cintiq 13 HD touch Finger",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "LIBINPUT_DEVICE_GROUP", "wacom-13hdt-group" },
-		{ NULL },
-	},
-)
+	    .name = "Wacom Cintiq 13 HD touch Finger",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "LIBINPUT_DEVICE_GROUP", "wacom-13hdt-group" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-wacom-cintiq-13hdt-pad.c 1.30.0-1/test/litest-device-wacom-cintiq-13hdt-pad.c
--- 1.28.1-1/test/litest-device-wacom-cintiq-13hdt-pad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-cintiq-13hdt-pad.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = -1, .code = -1 },
@@ -39,20 +39,20 @@ static struct input_event ring_start[] =
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 15 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct input_event ring_change[] = {
 	{ .type = EV_ABS, .code = ABS_WHEEL, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct input_event ring_end[] = {
 	{ .type = EV_ABS, .code = ABS_WHEEL, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 0 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct litest_device_interface interface = {
 	.touch_down_events = down,
@@ -61,6 +61,7 @@ static struct litest_device_interface in
 	.pad_ring_change_events = ring_change,
 	.pad_ring_end_events = ring_end,
 };
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1, 0, 0, 0 },
 	{ ABS_Y, 0, 1, 0, 0, 0 },
@@ -68,6 +69,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -76,6 +78,7 @@ static struct input_id input_id = {
 	.version = 0x110,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_0,
 	EV_KEY, BTN_1,
@@ -89,18 +92,18 @@ static int events[] = {
 	EV_KEY, BTN_STYLUS,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_CINTIQ_13HDT_PAD,
-	.features = LITEST_TABLET_PAD | LITEST_RING,
-	.interface = &interface,
+	    .features = LITEST_TABLET_PAD | LITEST_RING,
+	    .interface = &interface,
 
-	.name = "Wacom Cintiq 13 HD touch Pad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "ID_INPUT_TABLET_PAD", "1" },
-		{ "LIBINPUT_DEVICE_GROUP", "wacom-13hdt-group" },
-		{ NULL },
-	},
-)
+	    .name = "Wacom Cintiq 13 HD touch Pad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "ID_INPUT_TABLET_PAD", "1" },
+		    { "LIBINPUT_DEVICE_GROUP", "wacom-13hdt-group" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-wacom-cintiq-13hdt-pen.c 1.30.0-1/test/litest-device-wacom-cintiq-13hdt-pen.c
--- 1.28.1-1/test/litest-device-wacom-cintiq-13hdt-pen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-cintiq-13hdt-pen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -44,6 +44,7 @@ static struct input_event proximity_out[
 	{ .type = EV_ABS, .code = ABS_X, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_Y, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_DISTANCE, .value = 0 },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_TILT_X, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_TILT_Y, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 0 },
@@ -91,6 +92,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 400, 59152, 4, 0, 200 },
 	{ ABS_Y, 400, 33448, 4, 0, 200 },
@@ -104,6 +106,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -112,6 +115,7 @@ static struct input_id input_id = {
 	.version = 0x110,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOOL_PEN,
 	EV_KEY, BTN_TOOL_RUBBER,
@@ -125,17 +129,18 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_CINTIQ_13HDT_PEN,
-	.features = LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_SERIAL | LITEST_TILT | LITEST_DIRECT | LITEST_HOVER,
-	.interface = &interface,
-
-	.name = "Wacom Cintiq 13 HD touch Pen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "LIBINPUT_DEVICE_GROUP", "wacom-13hdt-group" },
-		{ NULL },
-	},
-)
+	    .features = LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_SERIAL |
+			LITEST_TILT | LITEST_DIRECT | LITEST_HOVER,
+	    .interface = &interface,
+
+	    .name = "Wacom Cintiq 13 HD touch Pen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "LIBINPUT_DEVICE_GROUP", "wacom-13hdt-group" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-wacom-cintiq-24hd-pen.c 1.30.0-1/test/litest-device-wacom-cintiq-24hd-pen.c
--- 1.28.1-1/test/litest-device-wacom-cintiq-24hd-pen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-cintiq-24hd-pen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -44,6 +44,7 @@ static struct input_event proximity_out[
 	{ .type = EV_ABS, .code = ABS_X, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_Y, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_DISTANCE, .value = 0 },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_TILT_X, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_TILT_Y, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 0 },
@@ -91,6 +92,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 400, 104080, 4, 0, 200 },
 	{ ABS_Y, 400, 65200, 4, 0, 200 },
@@ -104,6 +106,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -112,6 +115,7 @@ static struct input_id input_id = {
 	.version = 0x113,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOOL_PEN,
 	EV_KEY, BTN_TOOL_RUBBER,
@@ -125,13 +129,14 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_CINTIQ_24HD_PEN,
-	.features = LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_SERIAL | LITEST_TILT | LITEST_DIRECT | LITEST_HOVER,
-	.interface = &interface,
-
-	.name = "Wacom Cintiq 24 HD Pen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .features = LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_SERIAL |
+			LITEST_TILT | LITEST_DIRECT | LITEST_HOVER,
+	    .interface = &interface,
+
+	    .name = "Wacom Cintiq 24 HD Pen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-wacom-cintiq-24hdt-pad.c 1.30.0-1/test/litest-device-wacom-cintiq-24hdt-pad.c
--- 1.28.1-1/test/litest-device-wacom-cintiq-24hdt-pad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-cintiq-24hdt-pad.c	2025-11-25 03:40:43.000000000 +0000
@@ -25,11 +25,11 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <unistd.h>
 #include <sys/stat.h>
+#include <unistd.h>
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static void
 litest_wacom_cintiq_pad_teardown(void)
@@ -50,20 +50,20 @@ static struct input_event ring_start[] =
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 15 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct input_event ring_change[] = {
 	{ .type = EV_ABS, .code = ABS_WHEEL, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct input_event ring_end[] = {
 	{ .type = EV_ABS, .code = ABS_WHEEL, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 0 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct litest_device_interface interface = {
 	.touch_down_events = down,
@@ -73,6 +73,8 @@ static struct litest_device_interface in
 	.pad_ring_end_events = ring_end,
 };
 
+/* clang-format off */
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1, 0, 0, 0 },
 	{ ABS_Y, 0, 1, 0, 0, 0 },
@@ -81,6 +83,8 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -89,6 +93,7 @@ static struct input_id input_id = {
 	.version = 0x110,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, KEY_PROG1,
 	EV_KEY, KEY_PROG2,
@@ -112,19 +117,19 @@ static int events[] = {
 	EV_KEY, BTN_STYLUS,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_CINTIQ_24HDT_PAD,
-	.features = LITEST_TABLET_PAD | LITEST_RING,
-	.teardown = litest_wacom_cintiq_pad_teardown,
-	.interface = &interface,
-
-	.name = "Wacom Cintiq 24 HD touch Pad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "ID_INPUT_TABLET_PAD", "1" },
-		{ "LIBINPUT_DEVICE_GROUP", "wacom-24hdt-group" },
-		{ NULL },
-	},
-)
+	    .features = LITEST_TABLET_PAD | LITEST_RING,
+	    .teardown = litest_wacom_cintiq_pad_teardown,
+	    .interface = &interface,
+
+	    .name = "Wacom Cintiq 24 HD touch Pad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "ID_INPUT_TABLET_PAD", "1" },
+		    { "LIBINPUT_DEVICE_GROUP", "wacom-24hdt-group" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-wacom-cintiq-pro-16-finger.c 1.30.0-1/test/litest-device-wacom-cintiq-pro-16-finger.c
--- 1.28.1-1/test/litest-device-wacom-cintiq-pro-16-finger.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-cintiq-pro-16-finger.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -53,6 +53,7 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 13824, 0, 0, 40 },
 	{ ABS_Y, 0, 7776, 0, 0, 40 },
@@ -66,6 +67,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -74,22 +76,23 @@ static struct input_id input_id = {
 	.version = 0xb,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOUCH,
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_CINTIQ_PRO16_FINGER,
-	.features = LITEST_TOUCH,
-	.interface = &interface,
+	    .features = LITEST_TOUCH,
+	    .interface = &interface,
 
-	.name = "Wacom Cintiq Pro 16 Finger",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "LIBINPUT_DEVICE_GROUP", "wacom-pro16-group" },
-		{ NULL },
-	},
-)
+	    .name = "Wacom Cintiq Pro 16 Finger",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "LIBINPUT_DEVICE_GROUP", "wacom-pro16-group" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-wacom-cintiq-pro-16-pad.c 1.30.0-1/test/litest-device-wacom-cintiq-pro-16-pad.c
--- 1.28.1-1/test/litest-device-wacom-cintiq-pro-16-pad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-cintiq-pro-16-pad.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = -1, .code = -1 },
@@ -38,12 +38,14 @@ static struct litest_device_interface in
 	.touch_down_events = down,
 	.touch_move_events = move,
 };
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1, 0, 0, 0 },
 	{ ABS_Y, 0, 1, 0, 0, 0 },
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -52,6 +54,7 @@ static struct input_id input_id = {
 	.version = 0xb,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_STYLUS,
 	EV_KEY, KEY_BUTTONCONFIG,
@@ -59,18 +62,18 @@ static int events[] = {
 	EV_KEY, KEY_ONSCREEN_KEYBOARD,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_CINTIQ_PRO16_PAD,
-	.features = LITEST_TABLET_PAD,
-	.interface = &interface,
+	    .features = LITEST_TABLET_PAD,
+	    .interface = &interface,
 
-	.name = "Wacom Cintiq Pro 16 Pad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "ID_INPUT_TABLET_PAD", "1" },
-		{ "LIBINPUT_DEVICE_GROUP", "wacom-pro16-group" },
-		{ NULL },
-	},
-)
+	    .name = "Wacom Cintiq Pro 16 Pad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "ID_INPUT_TABLET_PAD", "1" },
+		    { "LIBINPUT_DEVICE_GROUP", "wacom-pro16-group" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-wacom-cintiq-pro-16-pen.c 1.30.0-1/test/litest-device-wacom-cintiq-pro-16-pen.c
--- 1.28.1-1/test/litest-device-wacom-cintiq-pro-16-pen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-cintiq-pro-16-pen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -47,6 +47,7 @@ static struct input_event proximity_out[
 	{ .type = EV_ABS, .code = ABS_Y, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_Z, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_WHEEL, .value = 0 },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_DISTANCE, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_TILT_X, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_TILT_Y, .value = 0 },
@@ -98,6 +99,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 69920, 0, 0, 200 },
 	{ ABS_Y, 0, 39980, 0, 0, 200 },
@@ -110,6 +112,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -118,6 +121,7 @@ static struct input_id input_id = {
 	.version = 0xb,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOOL_PEN,
 	EV_KEY, BTN_TOOL_RUBBER,
@@ -132,17 +136,18 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_CINTIQ_PRO16_PEN,
-	.features = LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_SERIAL | LITEST_TILT | LITEST_DIRECT | LITEST_HOVER,
-	.interface = &interface,
-
-	.name = "Wacom Cintiq Pro 16 Pen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "LIBINPUT_DEVICE_GROUP", "wacom-pro16-group" },
-		{ NULL },
-	},
-)
+	    .features = LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_SERIAL |
+			LITEST_TILT | LITEST_DIRECT | LITEST_HOVER,
+	    .interface = &interface,
+
+	    .name = "Wacom Cintiq Pro 16 Pen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "LIBINPUT_DEVICE_GROUP", "wacom-pro16-group" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-wacom-ekr.c 1.30.0-1/test/litest-device-wacom-ekr.c
--- 1.28.1-1/test/litest-device-wacom-ekr.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-ekr.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = -1, .code = -1 },
@@ -39,20 +39,20 @@ static struct input_event ring_start[] =
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 15 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct input_event ring_change[] = {
 	{ .type = EV_ABS, .code = ABS_WHEEL, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct input_event ring_end[] = {
 	{ .type = EV_ABS, .code = ABS_WHEEL, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 0 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct litest_device_interface interface = {
 	.touch_down_events = down,
@@ -62,6 +62,7 @@ static struct litest_device_interface in
 	.pad_ring_end_events = ring_end,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1, 0, 0, 0 },
 	{ ABS_Y, 0, 1, 0, 0, 0 },
@@ -69,6 +70,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -76,6 +78,7 @@ static struct input_id input_id = {
 	.product = 0x331,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_0,
 	EV_KEY, BTN_1,
@@ -99,17 +102,17 @@ static int events[] = {
 	EV_KEY, BTN_STYLUS,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_EKR,
-	.features = LITEST_TABLET_PAD | LITEST_RING,
-	.interface = &interface,
+	    .features = LITEST_TABLET_PAD | LITEST_RING,
+	    .interface = &interface,
 
-	.name = "Wacom Express Key Remote Pad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "ID_INPUT_TABLET_PAD", "1" },
-		{ NULL },
-	},
-)
+	    .name = "Wacom Express Key Remote Pad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "ID_INPUT_TABLET_PAD", "1" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-wacom-hid4800-pen.c 1.30.0-1/test/litest-device-wacom-hid4800-pen.c
--- 1.28.1-1/test/litest-device-wacom-hid4800-pen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-hid4800-pen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -39,6 +39,7 @@ static struct input_event proximity_in[]
 static struct input_event proximity_out[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_Y, .value = 0 },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 0 },
 	{ .type = EV_MSC, .code = MSC_SERIAL, .value = 297797542 },
 	{ .type = EV_KEY, .code = LITEST_BTN_TOOL_AUTO, .value = 0 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
@@ -73,12 +74,14 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 21696, 4, 0, 100 },
 	{ ABS_Y, 0, 13560, 4, 0, 100 },
 	{ ABS_PRESSURE, 0, 2047, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x18,
@@ -87,6 +90,7 @@ static struct input_id input_id = {
 	.version = 0x100,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOOL_PEN,
 	EV_KEY, BTN_TOOL_RUBBER,
@@ -97,13 +101,13 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_HID4800_PEN,
-	.features = LITEST_TABLET | LITEST_HOVER,
-	.interface = &interface,
+	    .features = LITEST_TABLET | LITEST_HOVER,
+	    .interface = &interface,
 
-	.name = "Wacom HID 4800 Pen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "Wacom HID 4800 Pen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-wacom-intuos3-pad.c 1.30.0-1/test/litest-device-wacom-intuos3-pad.c
--- 1.28.1-1/test/litest-device-wacom-intuos3-pad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-intuos3-pad.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = -1, .code = -1 },
@@ -39,20 +39,20 @@ static struct input_event strip_start[]
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 15 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct input_event strip_change[] = {
 	{ .type = EV_ABS, .code = ABS_RX, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct input_event strip_end[] = {
 	{ .type = EV_ABS, .code = ABS_RX, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 0 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct litest_device_interface interface = {
 	.touch_down_events = down,
@@ -62,6 +62,7 @@ static struct litest_device_interface in
 	.pad_strip_end_events = strip_end,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1, 0, 0, 0 },
 	{ ABS_Y, 0, 1, 0, 0, 0 },
@@ -69,6 +70,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -76,6 +78,7 @@ static struct input_id input_id = {
 	.product = 0xb7,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_0,
 	EV_KEY, BTN_1,
@@ -84,17 +87,17 @@ static int events[] = {
 	EV_KEY, BTN_STYLUS,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_INTUOS3_PAD,
-	.features = LITEST_TABLET_PAD | LITEST_STRIP,
-	.interface = &interface,
+	    .features = LITEST_TABLET_PAD | LITEST_STRIP,
+	    .interface = &interface,
 
-	.name = "Wacom Intuos3 4x6 Pad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "ID_INPUT_TABLET_PAD", "1" },
-		{ NULL },
-	},
-)
+	    .name = "Wacom Intuos3 4x6 Pad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "ID_INPUT_TABLET_PAD", "1" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-wacom-intuos5-finger.c 1.30.0-1/test/litest-device-wacom-intuos5-finger.c
--- 1.28.1-1/test/litest-device-wacom-intuos5-finger.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-intuos5-finger.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -74,6 +74,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 4096, 0, 0, 18 },
 	{ ABS_Y, 0, 4096, 0, 0, 29 },
@@ -85,6 +86,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -92,6 +94,7 @@ static struct input_id input_id = {
 	.product = 0x27,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOOL_FINGER,
 	EV_KEY, BTN_TOOL_QUINTTAP,
@@ -102,19 +105,19 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_POINTER,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_INTUOS5_FINGER,
-	.features = LITEST_TOUCHPAD,
-	.interface = &interface,
+	    .features = LITEST_TOUCHPAD,
+	    .interface = &interface,
 
-	.name = "Wacom Intuos5 touch M Finger",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "ID_INPUT_TABLET", "1" },
-		{ "ID_INPUT_TOUCHPAD", "1" },
-		{ "LIBINPUT_DEVICE_GROUP", "wacom-i5-group" },
-		{ NULL },
-	},
-)
+	    .name = "Wacom Intuos5 touch M Finger",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "ID_INPUT_TABLET", "1" },
+		    { "ID_INPUT_TOUCHPAD", "1" },
+		    { "LIBINPUT_DEVICE_GROUP", "wacom-i5-group" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-wacom-intuos5-pad.c 1.30.0-1/test/litest-device-wacom-intuos5-pad.c
--- 1.28.1-1/test/litest-device-wacom-intuos5-pad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-intuos5-pad.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = -1, .code = -1 },
@@ -39,20 +39,20 @@ static struct input_event ring_start[] =
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 15 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct input_event ring_change[] = {
 	{ .type = EV_ABS, .code = ABS_WHEEL, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct input_event ring_end[] = {
 	{ .type = EV_ABS, .code = ABS_WHEEL, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 0 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct litest_device_interface interface = {
 	.touch_down_events = down,
@@ -62,6 +62,7 @@ static struct litest_device_interface in
 	.pad_ring_end_events = ring_end,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 1, 0, 0, 0 },
 	{ ABS_Y, 0, 1, 0, 0, 0 },
@@ -69,6 +70,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MISC, 0, 0, 0, 0, 10 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -76,6 +78,7 @@ static struct input_id input_id = {
 	.product = 0x27,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_0,
 	EV_KEY, BTN_1,
@@ -89,18 +92,18 @@ static int events[] = {
 	EV_KEY, BTN_STYLUS,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_INTUOS5_PAD,
-	.features = LITEST_TABLET_PAD | LITEST_RING,
-	.interface = &interface,
+	    .features = LITEST_TABLET_PAD | LITEST_RING,
+	    .interface = &interface,
 
-	.name = "Wacom Intuos5 touch M Pad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "ID_INPUT_TABLET_PAD", "1" },
-		{ "LIBINPUT_DEVICE_GROUP", "wacom-i5-group" },
-		{ NULL },
-	},
-)
+	    .name = "Wacom Intuos5 touch M Pad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "ID_INPUT_TABLET_PAD", "1" },
+		    { "LIBINPUT_DEVICE_GROUP", "wacom-i5-group" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-wacom-intuos5-pen.c 1.30.0-1/test/litest-device-wacom-intuos5-pen.c
--- 1.28.1-1/test/litest-device-wacom-intuos5-pen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-intuos5-pen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -44,6 +44,7 @@ static struct input_event proximity_out[
 	{ .type = EV_ABS, .code = ABS_X, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_Y, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_DISTANCE, .value = 0 },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_TILT_X, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_TILT_Y, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 0 },
@@ -91,6 +92,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 44704, 4, 0, 200 },
 	{ ABS_Y, 0, 27940, 4, 0, 200 },
@@ -104,6 +106,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -111,6 +114,7 @@ static struct input_id input_id = {
 	.product = 0x27,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_0,
 	EV_KEY, BTN_1,
@@ -141,17 +145,18 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_POINTER,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_INTUOS5_PEN,
-	.features = LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_SERIAL | LITEST_TILT | LITEST_TOOL_MOUSE | LITEST_HOVER,
-	.interface = &interface,
-
-	.name = "Wacom Intuos5 touch M Pen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "LIBINPUT_DEVICE_GROUP", "wacom-i5-group" },
-		{ NULL },
-	},
-)
+	    .features = LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_SERIAL |
+			LITEST_TILT | LITEST_TOOL_MOUSE | LITEST_HOVER,
+	    .interface = &interface,
+
+	    .name = "Wacom Intuos5 touch M Pen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "LIBINPUT_DEVICE_GROUP", "wacom-i5-group" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-wacom-isdv4-4200-pen.c 1.30.0-1/test/litest-device-wacom-isdv4-4200-pen.c
--- 1.28.1-1/test/litest-device-wacom-isdv4-4200-pen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-isdv4-4200-pen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -37,6 +37,7 @@ static struct input_event proximity_in[]
 
 static struct input_event proximity_out[] = {
 	{ .type = EV_KEY, .code = LITEST_BTN_TOOL_AUTO, .value = 0 },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 0 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
 };
@@ -71,6 +72,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 59674, 0, 0, 100 },
 	{ ABS_Y, 0, 33566, 0, 0, 100 },
@@ -80,6 +82,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_PRESSURE, 0, 4096, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -87,6 +90,7 @@ static struct input_id input_id = {
 	.product = 0x4200,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOOL_PEN,
 	EV_KEY, BTN_TOOL_RUBBER,
@@ -97,13 +101,13 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_ISDV4_4200_PEN,
-	.features = LITEST_TABLET|LITEST_HOVER,
-	.interface = &interface,
+	    .features = LITEST_TABLET | LITEST_HOVER,
+	    .interface = &interface,
 
-	.name = "Wacom ISD-V4 Pen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "Wacom ISD-V4 Pen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-wacom-isdv4-524c-pen.c 1.30.0-1/test/litest-device-wacom-isdv4-524c-pen.c
--- 1.28.1-1/test/litest-device-wacom-isdv4-524c-pen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-isdv4-524c-pen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 struct priv {
 	unsigned int tool;
@@ -48,6 +48,7 @@ static struct input_event proximity_in[]
 
 static struct input_event proximity_out[] = {
 	{ .type = EV_KEY, .code = LITEST_BTN_TOOL_AUTO, .value = 0 },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 0 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
 };
@@ -74,10 +75,12 @@ get_axis_default(struct litest_device *d
 	return 1;
 }
 
-static bool prox_in(struct litest_device *d,
-		  unsigned int tool_type,
-		  double *x, double *y,
-		  struct axis_replacement *axes)
+static bool
+prox_in(struct litest_device *d,
+	unsigned int tool_type,
+	double *x,
+	double *y,
+	struct axis_replacement *axes)
 {
 	struct priv *priv = d->private;
 	priv->tool = tool_type;
@@ -85,7 +88,8 @@ static bool prox_in(struct litest_device
 	return false;
 }
 
-static bool prox_out(struct litest_device *d, unsigned int tool_type)
+static bool
+prox_out(struct litest_device *d, unsigned int tool_type)
 {
 	struct priv *priv = d->private;
 	priv->tool = 0;
@@ -94,18 +98,14 @@ static bool prox_out(struct litest_devic
 }
 
 static bool
-tip_down(struct litest_device *d,
-	 double *x, double *y,
-	 struct axis_replacement *axes)
+tip_down(struct litest_device *d, double *x, double *y, struct axis_replacement *axes)
 {
 	litest_event(d, EV_KEY, BTN_TOOL_PEN, 1);
 	return false; /* use the default behavior otherwise */
 }
 
 static bool
-tip_up(struct litest_device *d,
-	 double* x, double *y,
-	 struct axis_replacement *axes)
+tip_up(struct litest_device *d, double *x, double *y, struct axis_replacement *axes)
 {
 	struct priv *priv = d->private;
 	if (priv->tool != BTN_TOOL_PEN)
@@ -126,6 +126,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 30931, 0, 0, 100 },
 	{ ABS_Y, 0, 17399, 0, 0, 100 },
@@ -135,6 +136,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_PRESSURE, 0, 4095, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -142,6 +144,7 @@ static struct input_id input_id = {
 	.product = 0x524c,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOOL_PEN,
 	EV_KEY, BTN_TOOL_RUBBER,
@@ -151,14 +154,14 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_ISDV4_524C_PEN,
-	.features = LITEST_TABLET|LITEST_HOVER,
-	.interface = &interface,
+	    .features = LITEST_TABLET | LITEST_HOVER,
+	    .interface = &interface,
 
-	.name = "Wacom Co.,Ltd. Pen and multitouch sensor Stylus",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.create = create,
-)
+	    .name = "Wacom Co.,Ltd. Pen and multitouch sensor Stylus",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .create = create, )
diff -pruN 1.28.1-1/test/litest-device-wacom-isdv4-e6-finger.c 1.30.0-1/test/litest-device-wacom-isdv4-e6-finger.c
--- 1.28.1-1/test/litest-device-wacom-isdv4-e6-finger.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-isdv4-e6-finger.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -54,6 +54,7 @@ static struct litest_device_interface in
 	.touch_move_events = move,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 2776, 0, 0, 10 },
 	{ ABS_Y, 0, 1569, 0, 0, 9 },
@@ -63,6 +64,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MT_TRACKING_ID, 0, 65535, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -70,18 +72,19 @@ static struct input_id input_id = {
 	.product = 0xe6,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOUCH,
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_ISDV4_E6_FINGER,
-	.features = LITEST_TOUCH,
-	.interface = &interface,
+	    .features = LITEST_TOUCH,
+	    .interface = &interface,
 
-	.name = "Wacom ISDv4 E6 Finger",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "Wacom ISDv4 E6 Finger",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-wacom-isdv4-e6-pen.c 1.30.0-1/test/litest-device-wacom-isdv4-e6-pen.c
--- 1.28.1-1/test/litest-device-wacom-isdv4-e6-pen.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-isdv4-e6-pen.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -37,6 +37,7 @@ static struct input_event proximity_in[]
 
 static struct input_event proximity_out[] = {
 	{ .type = EV_KEY, .code = LITEST_BTN_TOOL_AUTO, .value = 0 },
+	{ .type = EV_ABS, .code = ABS_PRESSURE, .value = 0 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
 };
@@ -68,12 +69,14 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 27760, 4, 0, 100 },
 	{ ABS_Y, 0, 15694, 4, 0, 100 },
 	{ ABS_PRESSURE, 0, 255, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -81,6 +84,7 @@ static struct input_id input_id = {
 	.product = 0xe6,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_TOOL_PEN,
 	EV_KEY, BTN_TOOL_RUBBER,
@@ -90,13 +94,13 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_DIRECT,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_ISDV4_E6_PEN,
-	.features = LITEST_TABLET | LITEST_HOVER,
-	.interface = &interface,
+	    .features = LITEST_TABLET | LITEST_HOVER,
+	    .interface = &interface,
 
-	.name = "Wacom ISDv4 E6 Pen",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "Wacom ISDv4 E6 Pen",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-wacom-mobilestudio-pro-pad.c 1.30.0-1/test/litest-device-wacom-mobilestudio-pro-pad.c
--- 1.28.1-1/test/litest-device-wacom-mobilestudio-pro-pad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wacom-mobilestudio-pro-pad.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event down[] = {
 	{ .type = -1, .code = -1 },
@@ -39,20 +39,20 @@ static struct input_event ring_start[] =
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 15 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct input_event ring_change[] = {
 	{ .type = EV_ABS, .code = ABS_WHEEL, .value = LITEST_AUTO_ASSIGN },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct input_event ring_end[] = {
 	{ .type = EV_ABS, .code = ABS_WHEEL, .value = 0 },
 	{ .type = EV_ABS, .code = ABS_MISC, .value = 0 },
 	{ .type = EV_SYN, .code = SYN_REPORT, .value = 0 },
 	{ .type = -1, .code = -1 },
-} ;
+};
 
 static struct litest_device_interface interface = {
 	.touch_down_events = down,
@@ -61,6 +61,7 @@ static struct litest_device_interface in
 	.pad_ring_change_events = ring_change,
 	.pad_ring_end_events = ring_end,
 };
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, -2048, 2048, 0, 0, 0 },
 	{ ABS_Y, -2048, 2048, 0, 0, 0 },
@@ -69,6 +70,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_MISC, 0, 0, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -77,6 +79,7 @@ static struct input_id input_id = {
 	.version = 0x110,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_0,
 	EV_KEY, BTN_1,
@@ -95,18 +98,18 @@ static int events[] = {
 	INPUT_PROP_MAX, INPUT_PROP_ACCELEROMETER,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WACOM_MOBILESTUDIO_PRO_16_PAD,
-	.features = LITEST_TABLET_PAD | LITEST_RING,
-	.interface = &interface,
+	    .features = LITEST_TABLET_PAD | LITEST_RING,
+	    .interface = &interface,
 
-	.name = "Wacom MobileStudio Pro 16 Pad",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.udev_properties = {
-		{ "ID_INPUT_TABLET", "1" },
-		{ "ID_INPUT_TABLET_PAD", "1" },
-		{ NULL },
-	},
-)
+	    .name = "Wacom MobileStudio Pro 16 Pad",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .udev_properties = {
+		    { "ID_INPUT_TABLET", "1" },
+		    { "ID_INPUT_TABLET_PAD", "1" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-waltop-tablet.c 1.30.0-1/test/litest-device-waltop-tablet.c
--- 1.28.1-1/test/litest-device-waltop-tablet.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-waltop-tablet.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_event proximity_in[] = {
 	{ .type = EV_ABS, .code = ABS_X, .value = LITEST_AUTO_ASSIGN },
@@ -80,6 +80,7 @@ static struct litest_device_interface in
 	.get_axis_default = get_axis_default,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 32000, 0, 0, 0 },
 	{ ABS_Y, 0, 32000, 0, 0, 0 },
@@ -88,6 +89,7 @@ static struct input_absinfo absinfo[] =
 	{ ABS_TILT_Y, -127, 127, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -95,6 +97,7 @@ static struct input_id input_id = {
 	.product = 0x509,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, KEY_ESC,
 	EV_KEY, KEY_1,
@@ -219,19 +222,19 @@ static int events[] = {
 	EV_MSC, MSC_SERIAL,
 	-1, -1,
 };
+/* clang-format on */
 
 static const char quirk_file[] =
-"[litest Waltop Tablet]\n"
-"MatchName=litest          WALTOP     Batteryless Tablet*\n"
-"AttrSizeHint=200x200\n";
+	"[litest Waltop Tablet]\n"
+	"MatchName=litest          WALTOP     Batteryless Tablet*\n"
+	"AttrSizeHint=200x200\n";
 
 TEST_DEVICE(LITEST_WALTOP,
-	.features = LITEST_TABLET | LITEST_WHEEL | LITEST_TILT | LITEST_HOVER,
-	.interface = &interface,
+	    .features = LITEST_TABLET | LITEST_WHEEL | LITEST_TILT | LITEST_HOVER,
+	    .interface = &interface,
 
-	.name = "         WALTOP     Batteryless Tablet ", /* sic */
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-	.quirk_file = quirk_file,
-)
+	    .name = "         WALTOP     Batteryless Tablet ", /* sic */
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo,
+	    .quirk_file = quirk_file, )
diff -pruN 1.28.1-1/test/litest-device-wheel-only.c 1.30.0-1/test/litest-device-wheel-only.c
--- 1.28.1-1/test/litest-device-wheel-only.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-wheel-only.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,21 +32,22 @@ static struct input_id input_id = {
 	.product = 0x2,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_REL, REL_WHEEL,
 	-1 , -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_WHEEL_ONLY,
-	.features = LITEST_WHEEL,
-	.interface = NULL,
+	    .features = LITEST_WHEEL,
+	    .interface = NULL,
 
-	.name = "wheel only device",
-	.id = &input_id,
-	.absinfo = NULL,
-	.events = events,
-	.udev_properties = {
-		{ "ID_INPUT_KEY", "1" },
-		{ NULL },
-	},
-)
+	    .name = "wheel only device",
+	    .id = &input_id,
+	    .absinfo = NULL,
+	    .events = events,
+	    .udev_properties = {
+		    { "ID_INPUT_KEY", "1" },
+		    { NULL },
+	    }, )
diff -pruN 1.28.1-1/test/litest-device-xen-virtual-pointer.c 1.30.0-1/test/litest-device-xen-virtual-pointer.c
--- 1.28.1-1/test/litest-device-xen-virtual-pointer.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-xen-virtual-pointer.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,10 +23,11 @@
 
 #include "config.h"
 
-#include "litest.h"
-#include "litest-int.h"
 #include <assert.h>
 
+#include "litest-int.h"
+#include "litest.h"
+
 static bool
 touch_down(struct litest_device *d, unsigned int slot, double x, double y)
 {
@@ -66,11 +67,13 @@ static struct litest_device_interface in
 	.touch_up = touch_up,
 };
 
+/* clang-format off */
 static struct input_absinfo absinfo[] = {
 	{ ABS_X, 0, 800, 0, 0, 0 },
 	{ ABS_Y, 0, 800, 0, 0, 0 },
 	{ .value = -1 },
 };
+/* clang-format on */
 
 static struct input_id input_id = {
 	.bustype = 0x01,
@@ -78,6 +81,7 @@ static struct input_id input_id = {
 	.product = 0xfffe,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, BTN_LEFT,
 	EV_KEY, BTN_RIGHT,
@@ -90,13 +94,13 @@ static int events[] = {
 	EV_REL, REL_WHEEL,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_XEN_VIRTUAL_POINTER,
-	.features = LITEST_WHEEL | LITEST_BUTTON | LITEST_ABSOLUTE,
-	.interface = &interface,
+	    .features = LITEST_WHEEL | LITEST_BUTTON | LITEST_ABSOLUTE,
+	    .interface = &interface,
 
-	.name = "Xen Virtual Pointer",
-	.id = &input_id,
-	.events = events,
-	.absinfo = absinfo,
-)
+	    .name = "Xen Virtual Pointer",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = absinfo, )
diff -pruN 1.28.1-1/test/litest-device-yubikey.c 1.30.0-1/test/litest-device-yubikey.c
--- 1.28.1-1/test/litest-device-yubikey.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-device-yubikey.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,8 +23,8 @@
 
 #include "config.h"
 
-#include "litest.h"
 #include "litest-int.h"
+#include "litest.h"
 
 static struct input_id input_id = {
 	.bustype = 0x3,
@@ -32,6 +32,7 @@ static struct input_id input_id = {
 	.product = 0x10,
 };
 
+/* clang-format off */
 static int events[] = {
 	EV_KEY, KEY_ESC,
 	EV_KEY, KEY_1,
@@ -146,13 +147,13 @@ static int events[] = {
 	EV_LED, LED_KANA,
 	-1, -1,
 };
+/* clang-format on */
 
 TEST_DEVICE(LITEST_YUBIKEY,
-	.features = LITEST_KEYS,
-	.interface = NULL,
+	    .features = LITEST_KEYS,
+	    .interface = NULL,
 
-	.name = "Yubico Yubico Yubikey II",
-	.id = &input_id,
-	.events = events,
-	.absinfo = NULL,
-)
+	    .name = "Yubico Yubico Yubikey II",
+	    .id = &input_id,
+	    .events = events,
+	    .absinfo = NULL, )
diff -pruN 1.28.1-1/test/litest-int.h 1.30.0-1/test/litest-int.h
--- 1.28.1-1/test/litest-int.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-int.h	2025-11-25 03:40:43.000000000 +0000
@@ -22,6 +22,7 @@
  */
 
 #include "config.h"
+
 #include <limits.h>
 
 #ifndef LITEST_INT_H
@@ -40,17 +41,17 @@ struct litest_test_device {
 	enum litest_device_type type;
 	int64_t features;
 	const char *shortname;
-	void (*setup)(void); /* test fixture, used by check */
+	void (*setup)(void);    /* test fixture, used by check */
 	void (*teardown)(void); /* test fixture, used by check */
 	/**
-	* If create is non-NULL it will be called to initialize the device.
-	* For such devices, no overrides are possible. If create is NULL,
-	* the information in name, id, events, absinfo is used to
-	* create the device instead.
-	*
-	* @return true if the device needs to be created by litest, false if
-	*	the device creates itself
-	*/
+	 * If create is non-NULL it will be called to initialize the device.
+	 * For such devices, no overrides are possible. If create is NULL,
+	 * the information in name, id, events, absinfo is used to
+	 * create the device instead.
+	 *
+	 * @return true if the device needs to be created by litest, false if
+	 *	the device creates itself
+	 */
 	bool (*create)(struct litest_device *d);
 
 	/**
@@ -62,13 +63,13 @@ struct litest_test_device {
 	 */
 	const struct input_id *id;
 	/**
-	* List of event type/code tuples, terminated with -1, e.g.
-	* EV_REL, REL_X, EV_KEY, BTN_LEFT, -1
-	* Special tuple is INPUT_PROP_MAX, <actual property> to set.
-	*
-	* Any EV_ABS codes in this list will be initialized with a default
-	* axis range.
-	*/
+	 * List of event type/code tuples, terminated with -1, e.g.
+	 * EV_REL, REL_X, EV_KEY, BTN_LEFT, -1
+	 * Special tuple is INPUT_PROP_MAX, <actual property> to set.
+	 *
+	 * Any EV_ABS codes in this list will be initialized with a default
+	 * axis range.
+	 */
 	int *events;
 	/**
 	 * List of abs codes to enable, with absinfo.value determining the
@@ -84,15 +85,23 @@ struct litest_test_device {
 };
 
 struct litest_device_interface {
-	bool (*touch_down)(struct litest_device *d, unsigned int slot, double x, double y);
-	bool (*touch_move)(struct litest_device *d, unsigned int slot, double x, double y);
+	bool (*touch_down)(struct litest_device *d,
+			   unsigned int slot,
+			   double x,
+			   double y);
+	bool (*touch_move)(struct litest_device *d,
+			   unsigned int slot,
+			   double x,
+			   double y);
 	bool (*touch_up)(struct litest_device *d, unsigned int slot);
 
 	/**
 	 * Default value for the given EV_ABS axis.
 	 * @return 0 on success, nonzero otherwise
 	 */
-	int (*get_axis_default)(struct litest_device *d, unsigned int code, int32_t *value);
+	int (*get_axis_default)(struct litest_device *d,
+				unsigned int code,
+				int32_t *value);
 
 	/**
 	 * Set of of events to execute on touch down, terminated by a .type
@@ -116,17 +125,21 @@ struct litest_device_interface {
 
 	bool (*tablet_proximity_in)(struct litest_device *d,
 				    unsigned int tool_type,
-				    double *x, double *y,
+				    double *x,
+				    double *y,
 				    struct axis_replacement *axes);
 	bool (*tablet_proximity_out)(struct litest_device *d, unsigned int tool_type);
 	bool (*tablet_tip_down)(struct litest_device *d,
-				double *x, double *y,
+				double *x,
+				double *y,
 				struct axis_replacement *axes);
 	bool (*tablet_tip_up)(struct litest_device *d,
-			      double *x, double *y,
+			      double *x,
+			      double *y,
 			      struct axis_replacement *axes);
 	bool (*tablet_motion)(struct litest_device *d,
-			      double *x, double *y,
+			      double *x,
+			      double *y,
 			      struct axis_replacement *axes);
 
 	/**
@@ -170,8 +183,45 @@ struct litest_context {
 	struct list paths;
 };
 
-void litest_set_current_device(struct litest_device *device);
-int litest_scale(const struct litest_device *d, unsigned int axis, double val);
-void litest_generic_device_teardown(void);
+struct test {
+	struct list node;
+	char *name;
+	char *devname;
+	const void *func;
+	void *setup;
+	void *teardown;
+
+	struct range range;
+	int rangeval;
+	bool deviceless;
+
+	struct litest_test_parameters *params;
+};
+
+struct suite {
+	struct list node;
+	struct list tests;
+	char *name;
+};
+
+enum litest_runner_result
+litest_run(struct list *suites, int jobs);
+
+enum litest_mode {
+	LITEST_MODE_ERROR,
+	LITEST_MODE_TEST,
+	LITEST_MODE_LIST,
+};
+enum litest_mode
+litest_parse_argv(int argc, char **argv, int *njobs_out);
+void
+litest_add_test_device(struct list *device);
+
+void
+litest_set_current_device(struct litest_device *device);
+int
+litest_scale(const struct litest_device *d, unsigned int axis, double val);
+void
+litest_generic_device_teardown(void);
 
 #endif
diff -pruN 1.28.1-1/test/litest-main.c 1.30.0-1/test/litest-main.c
--- 1.28.1-1/test/litest-main.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/test/litest-main.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,278 @@
+/*
+ * Copyright © 2013 Red Hat, Inc.
+ * Copyright © 2013 Marcin Slusarz <marcin.slusarz@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <getopt.h>
+#include <libudev.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/sysinfo.h>
+#include <sys/timerfd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "linux/input.h"
+#ifdef HAVE_LIBSYSTEMD
+#include <systemd/sd-bus.h>
+#endif
+#ifdef __FreeBSD__
+#include <termios.h>
+#endif
+
+#include <valgrind/valgrind.h>
+
+#include "util-backtrace.h"
+#include "util-files.h"
+#include "util-libinput.h"
+
+#include "builddir.h"
+#include "libinput-util.h"
+#include "litest-int.h"
+#include "litest-runner.h"
+#include "litest.h"
+#include "quirks.h"
+
+static struct list all_test_suites = LIST_INIT(all_test_suites); /* struct suite */
+
+static int jobs;
+extern bool use_colors;
+extern bool in_debugger;
+extern bool verbose;
+extern bool run_deviceless;
+extern struct suite *current_suite;
+
+static bool
+is_debugger_attached(void)
+{
+	int status;
+	bool rc;
+	int pid = fork();
+
+	if (pid == -1)
+		return 0;
+
+	if (pid == 0) {
+		int ppid = getppid();
+		if (ptrace(PTRACE_ATTACH, ppid, NULL, 0) == 0) {
+			waitpid(ppid, NULL, 0);
+			ptrace(PTRACE_CONT, ppid, NULL, 0);
+			ptrace(PTRACE_DETACH, ppid, NULL, 0);
+			rc = false;
+		} else {
+			rc = true;
+		}
+		_exit(rc);
+	} else {
+		waitpid(pid, &status, 0);
+		rc = WEXITSTATUS(status);
+	}
+
+	return !!rc;
+}
+
+static void
+litest_list_tests(struct list *tests)
+{
+	struct suite *s;
+	const char *last_test_name = "<invalid>";
+	const char *last_dev_name = "<invalid>";
+
+	printf("groups:\n");
+	list_for_each(s, tests, node) {
+		struct test *t;
+		printf("  - group: \"%s\"\n", s->name);
+		printf("    tests:\n");
+		list_for_each(t, &s->tests, node) {
+			bool same_test = streq(last_test_name, t->name);
+			bool same_dev = streq(last_dev_name, t->devname);
+
+			if (!same_test) {
+				printf("      - name: \"%s\"\n", t->name);
+				printf("        devices:\n");
+			}
+
+			if (!same_test || !same_dev) {
+				last_test_name = t->name;
+				last_dev_name = t->devname;
+				printf("          - name: \"%s\"\n", t->devname);
+			}
+		}
+	}
+}
+
+extern const struct test_device __start_test_device_section, __stop_test_device_section;
+
+static void
+litest_init_test_devices(void)
+{
+	const struct test_device *t;
+	const struct test_device *start = &__start_test_device_section;
+	const struct test_device *stop = &__stop_test_device_section;
+
+	for (t = start; t < stop; t++)
+		litest_add_test_device(
+			&t->device->node); /* NOLINT(clang-analyzer-security.ArrayBound)
+					    */
+}
+
+extern const struct test_collection __start_test_collection_section,
+	__stop_test_collection_section;
+
+static void
+setup_tests(void)
+{
+	/* Iterate through linker-provided section boundaries.
+	 * These symbols mark the start and end of the test_collection_section. */
+	const struct test_collection *start = &__start_test_collection_section;
+	const struct test_collection *stop = &__stop_test_collection_section;
+	const struct test_collection *c;
+
+	for (c = start; c < stop;
+	     c++) { /* NOLINT(clang-analyzer-security.ArrayBound) */
+		struct suite *s;
+		s = zalloc(sizeof(*s));
+		s->name = safe_strdup(
+			c->name); /* NOLINT(clang-analyzer-security.ArrayBound) */
+
+		list_init(&s->tests);
+		list_append(&all_test_suites, &s->node);
+
+		current_suite = s;
+		c->setup();
+		current_suite = NULL;
+	}
+}
+
+static int
+check_device_access(void)
+{
+	if (getuid() != 0) {
+		fprintf(stderr,
+			"%s must be run as root.\n",
+			program_invocation_short_name);
+		return 77;
+	}
+
+	if (access("/dev/uinput", F_OK) == -1 &&
+	    access("/dev/input/uinput", F_OK) == -1) {
+		fprintf(stderr, "uinput device is missing, skipping tests.\n");
+		return 77;
+	}
+
+	return 0;
+}
+
+static void
+litest_free_test_list(struct list *tests)
+{
+	struct suite *s;
+
+	list_for_each_safe(s, tests, node) {
+		struct test *t;
+
+		list_for_each_safe(t, &s->tests, node) {
+			litest_test_parameters_unref(t->params);
+			free(t->name);
+			free(t->devname);
+			list_remove(&t->node);
+			free(t);
+		}
+
+		list_remove(&s->node);
+		free(s->name);
+		free(s);
+	}
+}
+
+int
+main(int argc, char **argv)
+{
+	enum litest_mode mode;
+	int rc;
+	const char *meson_testthreads;
+
+	use_colors = getenv("FORCE_COLOR") || isatty(STDERR_FILENO);
+	if (getenv("NO_COLOR"))
+		use_colors = false;
+
+	in_debugger = is_debugger_attached();
+	if (in_debugger) {
+		jobs = 0;
+	} else if ((meson_testthreads = getenv("MESON_TESTTHREADS")) == NULL ||
+		   !safe_atoi(meson_testthreads, &jobs)) {
+		jobs = get_nprocs();
+		if (!RUNNING_ON_VALGRIND)
+			jobs *= 2;
+	}
+
+	if (getenv("LITEST_VERBOSE"))
+		verbose = true;
+
+	mode = litest_parse_argv(argc, argv, &jobs);
+	if (mode == LITEST_MODE_ERROR)
+		return EXIT_FAILURE;
+
+	litest_init_test_devices();
+
+	setup_tests();
+	if (list_empty(&all_test_suites)) {
+		fprintf(stderr, "Error: filters are too strict, no tests to run.\n");
+		return EXIT_FAILURE;
+	}
+
+	if (mode == LITEST_MODE_LIST) {
+		litest_list_tests(&all_test_suites);
+		return EXIT_SUCCESS;
+	}
+
+	if (!run_deviceless && (rc = check_device_access()) != 0)
+		return rc;
+
+	enum litest_runner_result result = litest_run(&all_test_suites, jobs);
+
+	litest_free_test_list(&all_test_suites);
+
+	switch (result) {
+	case LITEST_PASS:
+		return EXIT_SUCCESS;
+	case LITEST_SKIP:
+		return 77;
+	default:
+		return result;
+	}
+}
diff -pruN 1.28.1-1/test/litest-runner.c 1.30.0-1/test/litest-runner.c
--- 1.28.1-1/test/litest-runner.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-runner.c	2025-11-25 03:40:43.000000000 +0000
@@ -24,26 +24,27 @@
 #include "config.h"
 
 #include <errno.h>
+#include <inttypes.h>
 #include <sys/epoll.h>
-#include <sys/timerfd.h>
 #include <sys/sysinfo.h>
+#include <sys/timerfd.h>
 #include <sys/wait.h>
 #ifdef HAVE_PIDFD_OPEN
 #include <sys/syscall.h>
 #endif
 #include <fcntl.h>
-#include <stdlib.h>
-#include <signal.h>
 #include <setjmp.h>
+#include <signal.h>
+#include <stdlib.h>
 #include <valgrind/valgrind.h>
 
-#include "litest-runner.h"
-
 #include "util-files.h"
 #include "util-list.h"
 #include "util-multivalue.h"
 #include "util-stringbuf.h"
 
+#include "litest-runner.h"
+
 static bool use_jmpbuf; /* only used for max_forks = 0 */
 static jmp_buf jmpbuf;
 
@@ -70,7 +71,7 @@ struct litest_runner_test {
 	int sig_or_errno;
 
 	struct stringbuf logs[_FD_LAST];
-	pid_t pid; /* the test's PID, if any */
+	pid_t pid;              /* the test's PID, if any */
 	int read_fds[_FD_LAST]; /* logging fds while the test is running */
 
 	int epollfd;
@@ -93,8 +94,8 @@ struct litest_runner {
 
 	int terminating;
 
-	struct list tests; /* struct litest_runner_test */
-	struct list tests_running; /* struct litest_runner_test */
+	struct list tests;          /* struct litest_runner_test */
+	struct list tests_running;  /* struct litest_runner_test */
 	struct list tests_complete; /* struct litest_runner_test */
 
 	struct {
@@ -180,12 +181,12 @@ static const char *
 litest_runner_result_as_str(enum litest_runner_result result)
 {
 	switch (result) {
-		CASE_RETURN_STRING(LITEST_PASS);
-		CASE_RETURN_STRING(LITEST_NOT_APPLICABLE);
-		CASE_RETURN_STRING(LITEST_FAIL);
-		CASE_RETURN_STRING(LITEST_SYSTEM_ERROR);
-		CASE_RETURN_STRING(LITEST_TIMEOUT);
-		CASE_RETURN_STRING(LITEST_SKIP);
+	CASE_RETURN_STRING(LITEST_PASS);
+	CASE_RETURN_STRING(LITEST_NOT_APPLICABLE);
+	CASE_RETURN_STRING(LITEST_FAIL);
+	CASE_RETURN_STRING(LITEST_SYSTEM_ERROR);
+	CASE_RETURN_STRING(LITEST_TIMEOUT);
+	CASE_RETURN_STRING(LITEST_SKIP);
 	}
 
 	litest_abort_msg("Unknown result %d", result);
@@ -279,18 +280,17 @@ sighandler_forked_child(int signal)
 	act.sa_handler = SIG_DFL;
 	sigaction(signal, &act, NULL);
 
-        /* abort() was probably called by litest_assert... which inserts
-         * the backtrace anyway -  we only need to backtrace the other signals
-         */
-        if (signal != SIGABRT)
+	/* abort() was probably called by litest_assert... which inserts
+	 * the backtrace anyway -  we only need to backtrace the other signals
+	 */
+	if (signal != SIGABRT)
 		litest_backtrace(NULL);
 
 	raise(signal);
 }
 
 static int
-litest_runner_fork_test(struct litest_runner *runner,
-			struct litest_runner_test *t)
+litest_runner_fork_test(struct litest_runner *runner, struct litest_runner_test *t)
 {
 	pid_t pid;
 	struct sigaction act;
@@ -363,9 +363,8 @@ valgrind_logfile(pid_t pid)
 	if (!prefix)
 		prefix = ".";
 
-	char *filename = NULL;
-	int rc = xasprintf(&filename, "%s/valgrind.%d.log", prefix, pid);
-	litest_assert_neg_errno_success(rc);
+	char *filename = strdup_printf("%s/valgrind.%d.log", prefix, pid);
+	litest_assert_ptr_notnull(filename);
 
 	return filename;
 }
@@ -375,10 +374,9 @@ collect_file(const char *filename, struc
 {
 	int fd = open(filename, O_RDONLY);
 	if (fd == -1) {
-		char *msg;
-		xasprintf(&msg, "Failed to find '%s': %m", filename);
+		_autofree_ char *msg =
+			strdup_printf("Failed to find '%s': %m", filename);
 		stringbuf_append_string(b, msg);
-		free(msg);
 	} else {
 		stringbuf_append_from_fd(b, fd, 0);
 		close(fd);
@@ -399,38 +397,45 @@ litest_runner_test_collect_child(struct
 		t->result = WEXITSTATUS(status);
 		if (RUNNING_ON_VALGRIND && t->result == 3) {
 			char msg[64];
-			snprintf(msg, sizeof(msg), "valgrind exited with an error code, see logs\n");
+			snprintf(msg,
+				 sizeof(msg),
+				 "valgrind exited with an error code, see logs\n");
 			stringbuf_append_string(&t->logs[FD_LOG], msg);
 			t->result = LITEST_SYSTEM_ERROR;
 		}
 		switch (t->result) {
-			case LITEST_PASS:
-			case LITEST_SKIP:
-			case LITEST_NOT_APPLICABLE:
-			case LITEST_FAIL:
-			case LITEST_TIMEOUT:
-			case LITEST_SYSTEM_ERROR:
-				break;
+		case LITEST_PASS:
+		case LITEST_SKIP:
+		case LITEST_NOT_APPLICABLE:
+		case LITEST_FAIL:
+		case LITEST_TIMEOUT:
+		case LITEST_SYSTEM_ERROR:
+			break;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch"
+		case 0:
 			/* if a test execve's itself allow for the normal
 			 * exit codes to map to the results */
-			#pragma GCC diagnostic push
-			#pragma GCC diagnostic ignored "-Wswitch"
-			case 0:
-				t->result = LITEST_PASS;
-				break;
-			#pragma GCC diagnostic pop
-			default: {
-				char msg[64];
-				snprintf(msg, sizeof(msg), "Invalid test exit status %d", t->result);
-				stringbuf_append_string(&t->logs[FD_LOG], msg);
-				t->result = LITEST_FAIL;
-				break;
-			}
+			t->result = LITEST_PASS;
+			break;
+#pragma GCC diagnostic pop
+		default: {
+			char msg[64];
+			snprintf(msg,
+				 sizeof(msg),
+				 "Invalid test exit status %d",
+				 t->result);
+			stringbuf_append_string(&t->logs[FD_LOG], msg);
+			t->result = LITEST_FAIL;
+			break;
+		}
 		}
 	} else {
 		if (WIFSIGNALED(status)) {
 			t->sig_or_errno = WTERMSIG(status);
-			t->result = (t->sig_or_errno == t->desc.args.signal) ? LITEST_PASS : LITEST_FAIL;
+			t->result = (t->sig_or_errno == t->desc.args.signal)
+					    ? LITEST_PASS
+					    : LITEST_FAIL;
 		} else {
 			t->result = LITEST_FAIL;
 		}
@@ -441,9 +446,8 @@ litest_runner_test_collect_child(struct
 	t->times.end_millis = us2ms(now);
 
 	if (RUNNING_ON_VALGRIND) {
-		char *filename = valgrind_logfile(t->pid);
+		_autofree_ char *filename = valgrind_logfile(t->pid);
 		collect_file(filename, &t->logs[FD_VALGRIND]);
-		free(filename);
 	}
 
 	t->pid = 0;
@@ -472,11 +476,13 @@ litest_runner_test_setup_monitoring(stru
 			r = -errno;
 			goto error;
 		}
-		r = timerfd_settime(pidfd, 0,
-				    &((struct itimerspec ){
-				      .it_interval.tv_nsec = 200 * 1000 * 1000,
-				      .it_value.tv_nsec = 200 * 1000 * 1000,
-				      }), NULL);
+		r = timerfd_settime(pidfd,
+				    0,
+				    &((struct itimerspec){
+					    .it_interval.tv_nsec = 200 * 1000 * 1000,
+					    .it_value.tv_nsec = 200 * 1000 * 1000,
+				    }),
+				    NULL);
 		if (r < 0) {
 			r = -errno;
 			goto error;
@@ -495,15 +501,21 @@ litest_runner_test_setup_monitoring(stru
 		r = -errno;
 		goto error;
 	}
-	timerfd_settime(timerfd, 0, &((struct itimerspec ){ .it_value.tv_sec = runner->timeout}), NULL);
+	timerfd_settime(timerfd,
+			0,
+			&((struct itimerspec){ .it_value.tv_sec = runner->timeout }),
+			NULL);
 
 	epollfd = epoll_create(1);
 	if (epollfd < 0)
 		goto error;
 	ev[nevents++] = (struct epoll_event){ .events = EPOLLIN, .data.fd = pidfd };
-	ev[nevents++] = (struct epoll_event){ .events = EPOLLIN, .data.fd = t->read_fds[FD_STDOUT] };
-	ev[nevents++] = (struct epoll_event){ .events = EPOLLIN, .data.fd = t->read_fds[FD_STDERR] };
-	ev[nevents++] = (struct epoll_event){ .events = EPOLLIN, .data.fd = t->read_fds[FD_LOG] };
+	ev[nevents++] = (struct epoll_event){ .events = EPOLLIN,
+					      .data.fd = t->read_fds[FD_STDOUT] };
+	ev[nevents++] = (struct epoll_event){ .events = EPOLLIN,
+					      .data.fd = t->read_fds[FD_STDERR] };
+	ev[nevents++] = (struct epoll_event){ .events = EPOLLIN,
+					      .data.fd = t->read_fds[FD_LOG] };
 	ev[nevents++] = (struct epoll_event){ .events = EPOLLIN, .data.fd = timerfd };
 
 	for (size_t i = 0; i < nevents; i++) {
@@ -550,7 +562,9 @@ litest_runner_test_check_status(struct l
 
 		if (e.data.fd == t->pidfd) {
 			uint64_t buf;
-			int ignore = read(t->pidfd, &buf, sizeof(buf)); /* for timerfd fallback */
+			int ignore = read(t->pidfd,
+					  &buf,
+					  sizeof(buf)); /* for timerfd fallback */
 			(void)ignore;
 			if (litest_runner_test_collect_child(t)) {
 				break;
@@ -565,7 +579,9 @@ litest_runner_test_check_status(struct l
 		} else {
 			for (int i = 0; i < _FD_LAST; i++) {
 				if (e.data.fd == t->read_fds[i]) {
-					stringbuf_append_from_fd(&t->logs[i], e.data.fd, 1024);
+					stringbuf_append_from_fd(&t->logs[i],
+								 e.data.fd,
+								 1024);
 				}
 			}
 		}
@@ -594,9 +610,9 @@ litest_runner_test_update_errno(struct l
 	}
 }
 
-__attribute__((noreturn))
-void
-litest_runner_abort(void)  {
+__attribute__((noreturn)) void
+litest_runner_abort(void)
+{
 	if (use_jmpbuf) {
 		longjmp(jmpbuf, SIGABRT);
 	} else {
@@ -641,12 +657,11 @@ static void
 print_lines(FILE *fp, const char *log, const char *prefix)
 {
 	size_t nlines = 0;
-	char **lines = strv_from_string (log, "\n", &nlines);
+	_autostrvfree_ char **lines = strv_from_string(log, "\n", &nlines);
 
 	for (size_t i = 0; i < nlines; i++) {
 		fprintf(fp, "%s%s\n", prefix, lines[i]);
 	}
-	strv_free(lines);
 }
 
 void
@@ -666,7 +681,10 @@ _litest_test_param_fetch(const struct li
 		list_for_each(p, &params->test_params, link) {
 			if (streq(p->name, name)) {
 				if (tolower(p->value.type) != tolower(type))
-					litest_abort_msg("Paramter type mismatch: parameter '%s' is of type %c", p->name, p->value.type);
+					litest_abort_msg(
+						"Paramter type mismatch: parameter '%s' is of type %c",
+						p->name,
+						p->value.type);
 				found = true;
 				multivalue_extract(&p->value, ptr);
 				break;
@@ -705,7 +723,8 @@ litest_test_parameters_unref(struct lite
 }
 
 static void
-litest_runner_log_test_result(struct litest_runner *runner, struct litest_runner_test *t)
+litest_runner_log_test_result(struct litest_runner *runner,
+			      struct litest_runner_test *t)
 {
 	const char *color = NULL;
 	const char *status = NULL;
@@ -714,61 +733,79 @@ litest_runner_log_test_result(struct lit
 	litest_assert_int_le(t->result, (enum litest_runner_result)LITEST_SYSTEM_ERROR);
 
 	switch (t->result) {
-		case LITEST_PASS: color = ANSI_BRIGHT_GREEN; break;
-		case LITEST_FAIL: color = ANSI_BRIGHT_RED; break;
-		case LITEST_SKIP: color = ANSI_BRIGHT_YELLOW; break;
-		case LITEST_NOT_APPLICABLE: color = ANSI_BLUE; break;
-		case LITEST_TIMEOUT: color = ANSI_BRIGHT_CYAN; break;
-		case LITEST_SYSTEM_ERROR: color = ANSI_BRIGHT_MAGENTA; break;
+	case LITEST_PASS:
+		color = ANSI_BOLD_GREEN;
+		break;
+	case LITEST_FAIL:
+		color = ANSI_BOLD_RED;
+		break;
+	case LITEST_SKIP:
+		color = ANSI_BOLD_YELLOW;
+		break;
+	case LITEST_NOT_APPLICABLE:
+		color = ANSI_BLUE;
+		break;
+	case LITEST_TIMEOUT:
+		color = ANSI_BOLD_CYAN;
+		break;
+	case LITEST_SYSTEM_ERROR:
+		color = ANSI_BOLD_MAGENTA;
+		break;
 	}
 
 	fprintf(runner->fp, "  - name: \"%s\"\n", t->desc.name);
-	int min = t->desc.args.range.lower,
-	    max = t->desc.args.range.upper;
+	int min = t->desc.args.range.lower, max = t->desc.args.range.upper;
 	if (range_is_valid(&t->desc.args.range))
-		fprintf(runner->fp, "    rangeval: %d  # %d..%d\n", t->desc.rangeval, min, max);
+		fprintf(runner->fp,
+			"    rangeval: %d  # %d..%d\n",
+			t->desc.rangeval,
+			min,
+			max);
 
 	if (t->desc.params) {
 		fprintf(runner->fp, "    params:\n");
 		struct litest_test_param *p;
 		list_for_each(p, &t->desc.params->test_params, link) {
-			char *val = multivalue_as_str(&p->value);
+			_autofree_ char *val = multivalue_as_str(&p->value);
 			fprintf(runner->fp, "      %s: %s\n", p->name, val);
-			free(val);
 		}
 	}
 
 	fprintf(runner->fp,
-		"    duration: %ld  # (ms), total test run time: %02d:%02d\n",
+		"    duration: %" PRIu64 "  # (ms), total test run time: %02" PRIu32
+		":%02" PRIu32 "\n",
 		t->times.end_millis - t->times.start_millis,
 		(ms2s(t->times.end_millis - runner->times.start_millis)) / 60,
 		(ms2s(t->times.end_millis - runner->times.start_millis)) % 60);
 
 	status = litest_runner_result_as_str(t->result);
-	fprintf(runner->fp, "    status: %s%s%s\n",
+	fprintf(runner->fp,
+		"    status: %s%s%s\n",
 		runner->use_colors ? color : "",
 		&status[7], /* skip LITEST_ prefix */
 		runner->use_colors ? ANSI_NORMAL : "");
 
 	switch (t->result) {
-		case LITEST_PASS:
-		case LITEST_SKIP:
-		case LITEST_NOT_APPLICABLE:
-			if (!runner->verbose)
-				return;
-			break;
-		default:
-			break;
+	case LITEST_PASS:
+	case LITEST_SKIP:
+	case LITEST_NOT_APPLICABLE:
+		if (!runner->verbose)
+			return;
+		break;
+	default:
+		break;
 	}
 
 	if (t->sig_or_errno > 0)
-		fprintf(runner->fp, "    signal: %d # SIG%s \n",
-		       t->sig_or_errno,
-		       sigabbrev_np(t->sig_or_errno));
+		fprintf(runner->fp,
+			"    signal: %d # SIG%s \n",
+			t->sig_or_errno,
+			sigabbrev_np(t->sig_or_errno));
 	else if (t->sig_or_errno < 0)
-		fprintf(runner->fp, "    errno: %d # %s\n",
-		       -t->sig_or_errno,
-		       strerror(-t->sig_or_errno));
+		fprintf(runner->fp,
+			"    errno: %d # %s\n",
+			-t->sig_or_errno,
+			strerror(-t->sig_or_errno));
 	if (!stringbuf_is_empty(&t->logs[FD_LOG])) {
 		fprintf(runner->fp, "    log: |\n");
 		print_lines(runner->fp, t->logs[FD_LOG].data, "      ");
@@ -803,37 +840,32 @@ litest_runner_new(void)
 }
 
 void
-litest_runner_set_timeout(struct litest_runner *runner,
-			  unsigned int timeout)
+litest_runner_set_timeout(struct litest_runner *runner, unsigned int timeout)
 {
 	runner->timeout = timeout;
 }
 
 void
-litest_runner_set_output_file(struct litest_runner *runner,
-			      FILE *fp)
+litest_runner_set_output_file(struct litest_runner *runner, FILE *fp)
 {
 	setlinebuf(fp);
 	runner->fp = fp;
 }
 
 void
-litest_runner_set_num_parallel(struct litest_runner *runner,
-			       size_t num_jobs)
+litest_runner_set_num_parallel(struct litest_runner *runner, size_t num_jobs)
 {
 	runner->max_forks = num_jobs;
 }
 
 void
-litest_runner_set_verbose(struct litest_runner *runner,
-			  bool verbose)
+litest_runner_set_verbose(struct litest_runner *runner, bool verbose)
 {
 	runner->verbose = verbose;
 }
 
 void
-litest_runner_set_use_colors(struct litest_runner *runner,
-			     bool use_colors)
+litest_runner_set_use_colors(struct litest_runner *runner, bool use_colors)
 {
 	runner->use_colors = use_colors;
 }
@@ -959,7 +991,10 @@ litest_runner_run_tests(struct litest_ru
 	runner->times.start = time(NULL);
 	ltime = localtime(&runner->times.start);
 	strftime(timestamp, sizeof(timestamp), "%FT%H:%M", ltime);
-	fprintf(runner->fp, "start: %ld  # \"%s\"\n", runner->times.start, timestamp);
+	fprintf(runner->fp,
+		"start: %lld  # \"%s\"\n",
+		(long long)runner->times.start,
+		timestamp);
 	fprintf(runner->fp, "jobs: %zd\n", runner->max_forks);
 	fprintf(runner->fp, "tests:\n");
 	list_for_each_safe(t, &runner->tests, node) {
@@ -983,13 +1018,13 @@ litest_runner_run_tests(struct litest_ru
 			struct litest_runner_test *complete;
 			list_for_each(complete, &runner->tests_complete, node) {
 				switch (complete->result) {
-					case LITEST_FAIL:
-					case LITEST_SYSTEM_ERROR:
-					case LITEST_TIMEOUT:
-						do_exit = true;
-						break;
-					default:
-						break;
+				case LITEST_FAIL:
+				case LITEST_SYSTEM_ERROR:
+				case LITEST_TIMEOUT:
+					do_exit = true;
+					break;
+				default:
+					break;
 				}
 				if (do_exit)
 					break;
@@ -1012,32 +1047,35 @@ litest_runner_run_tests(struct litest_ru
 	list_for_each(t, &runner->tests_complete, node) {
 		ncomplete++;
 		switch (t->result) {
-			case LITEST_PASS:
-				npass++;
-				break;
-			case LITEST_NOT_APPLICABLE:
-				nna++;
-				break;
-			case LITEST_FAIL:
-			case LITEST_SYSTEM_ERROR:
-			case LITEST_TIMEOUT:
-				nfail++;
-				break;
-			case LITEST_SKIP:
-				nskip++;
-				break;
+		case LITEST_PASS:
+			npass++;
+			break;
+		case LITEST_NOT_APPLICABLE:
+			nna++;
+			break;
+		case LITEST_FAIL:
+		case LITEST_SYSTEM_ERROR:
+		case LITEST_TIMEOUT:
+			nfail++;
+			break;
+		case LITEST_SKIP:
+			nskip++;
+			break;
 		}
 	}
 
 	runner->times.end = time(NULL);
 	ltime = localtime(&runner->times.end);
 	strftime(timestamp, sizeof(timestamp), "%FT%H:%M", ltime);
-	fprintf(runner->fp, "end: %ld  # \"%s\"\n", runner->times.end, timestamp);
 	fprintf(runner->fp,
-		"duration: %ld  # (s) %02ld:%02ld\n",
-		runner->times.end - runner->times.start,
-		(runner->times.end - runner->times.start) / 60,
-		(runner->times.end - runner->times.start) % 60);
+		"end: %lld  # \"%s\"\n",
+		(long long)runner->times.end,
+		timestamp);
+	fprintf(runner->fp,
+		"duration: %lld  # (s) %02lld:%02lld\n",
+		(long long)(runner->times.end - runner->times.start),
+		(long long)((runner->times.end - runner->times.start) / 60),
+		(long long)((runner->times.end - runner->times.start) % 60));
 	fprintf(runner->fp, "summary:\n");
 	fprintf(runner->fp, "  completed: %zd\n", ncomplete);
 	fprintf(runner->fp, "  pass: %zd\n", npass);
@@ -1048,27 +1086,27 @@ litest_runner_run_tests(struct litest_ru
 		fprintf(runner->fp, "  failed:\n");
 		list_for_each(t, &runner->tests_complete, node) {
 			switch (t->result) {
-				case LITEST_FAIL:
-				case LITEST_SYSTEM_ERROR:
-				case LITEST_TIMEOUT:
-					fprintf(runner->fp, "    - \"%s\"\n",  t->desc.name);
-					break;
-				default:
-					break;
+			case LITEST_FAIL:
+			case LITEST_SYSTEM_ERROR:
+			case LITEST_TIMEOUT:
+				litest_runner_log_test_result(runner, t);
+				break;
+			default:
+				break;
 			}
 		}
 	}
 
 	if (RUNNING_ON_VALGRIND) {
-		char *filename = valgrind_logfile(getpid());
-		struct stringbuf *b = stringbuf_new();
+		_autofree_ char *filename = valgrind_logfile(getpid());
+		_destroy_(stringbuf) *b = stringbuf_new();
 
 		collect_file(filename, b);
 		fprintf(runner->fp, "valgrind:\n");
 		print_lines(runner->fp, b->data, "  ");
-		fprintf(runner->fp, "# Valgrind log is incomplete, see %s for full log\n", filename);
-		free(filename);
-		stringbuf_destroy(b);
+		fprintf(runner->fp,
+			"# Valgrind log is incomplete, see %s for full log\n",
+			filename);
 	}
 
 	enum litest_runner_result result = LITEST_PASS;
@@ -1079,12 +1117,13 @@ litest_runner_run_tests(struct litest_ru
 	} else {
 		list_for_each(t, &runner->tests_complete, node) {
 			switch (t->result) {
-				case LITEST_PASS:
-				case LITEST_NOT_APPLICABLE:
-					break;
-				default:
-					result = LITEST_FAIL;
-					break;
+			case LITEST_PASS:
+			case LITEST_SKIP:
+			case LITEST_NOT_APPLICABLE:
+				break;
+			default:
+				result = LITEST_FAIL;
+				break;
 			}
 		}
 	}
diff -pruN 1.28.1-1/test/litest-runner.h 1.30.0-1/test/litest-runner.h
--- 1.28.1-1/test/litest-runner.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-runner.h	2025-11-25 03:40:43.000000000 +0000
@@ -25,22 +25,24 @@
 
 #include "config.h"
 
-#include "litest.h"
+#include "util-mem.h"
 #include "util-range.h"
 
+#include "litest.h"
+
 #define LITEST_RUNNER_DEFAULT_TIMEOUT 30
 
 /**
  * Result returned from tests or suites.
  */
 enum litest_runner_result {
-	LITEST_PASS = 75,		/**< test successful */
-	LITEST_FAIL = 76,		/**< test failed. Should not be returned directly,
-					     Use the litest_ macros instead */
-	LITEST_SKIP = 77,		/**< test was skipped */
-	LITEST_NOT_APPLICABLE = 78,	/**< test does not apply */
-	LITEST_TIMEOUT = 79,		/**< test aborted after timeout */
-	LITEST_SYSTEM_ERROR = 80,	/**< unrelated error occurred */
+	LITEST_PASS = 75,           /**< test successful */
+	LITEST_FAIL = 76,           /**< test failed. Should not be returned directly,
+					 Use the litest_ macros instead */
+	LITEST_SKIP = 77,           /**< test was skipped */
+	LITEST_NOT_APPLICABLE = 78, /**< test does not apply */
+	LITEST_TIMEOUT = 79,        /**< test aborted after timeout */
+	LITEST_SYSTEM_ERROR = 80,   /**< unrelated error occurred */
 };
 
 /* For parametrized tests (litest_add_parametrized and friends)
@@ -71,7 +73,8 @@ void
 _litest_test_param_fetch(const struct litest_test_parameters *params, ...);
 
 static inline const char *
-litest_test_param_get_string(const struct litest_test_parameters *params, const char *name)
+litest_test_param_get_string(const struct litest_test_parameters *params,
+			     const char *name)
 {
 	const char *p;
 	litest_test_param_fetch(params, name, 's', &p);
@@ -79,7 +82,8 @@ litest_test_param_get_string(const struc
 }
 
 static inline bool
-litest_test_param_get_bool(const struct litest_test_parameters *params, const char *name)
+litest_test_param_get_bool(const struct litest_test_parameters *params,
+			   const char *name)
 {
 	bool p;
 	litest_test_param_fetch(params, name, 'b', &p);
@@ -103,7 +107,8 @@ litest_test_param_get_u32(const struct l
 }
 
 static inline char
-litest_test_param_get_char(const struct litest_test_parameters *params, const char *name)
+litest_test_param_get_char(const struct litest_test_parameters *params,
+			   const char *name)
 {
 	char p;
 	litest_test_param_fetch(params, name, 'c', &p);
@@ -111,7 +116,8 @@ litest_test_param_get_char(const struct
 }
 
 static inline double
-litest_test_param_get_double(const struct litest_test_parameters *params, const char *name)
+litest_test_param_get_double(const struct litest_test_parameters *params,
+			     const char *name)
 {
 	double p;
 	litest_test_param_fetch(params, name, 'd', &p);
@@ -122,13 +128,13 @@ litest_test_param_get_double(const struc
  * This struct is passed into every test.
  */
 struct litest_runner_test_env {
-	int rangeval;			/* The current value within the args.range (or 0) */
+	int rangeval; /* The current value within the args.range (or 0) */
 	const struct litest_test_parameters *params;
 };
 
 struct litest_runner_test_description {
-	char name[512];			/* The name of the test */
-	int rangeval;			/* The current value within the args.range (or 0) */
+	char name[512]; /* The name of the test */
+	int rangeval;   /* The current value within the args.range (or 0) */
 
 	struct litest_test_parameters *params;
 
@@ -138,29 +144,38 @@ struct litest_runner_test_description {
 	void (*teardown)(const struct litest_runner_test_description *);
 
 	struct {
-		struct range range;	/* The range this test applies to */
-		int signal;		/* expected signal for fail tests */
+		struct range range; /* The range this test applies to */
+		int signal;         /* expected signal for fail tests */
 	} args;
 };
 
 struct litest_runner;
 
-struct litest_runner *litest_runner_new(void);
+struct litest_runner *
+litest_runner_new(void);
 
 /**
  * Default is nprocs * 2.
  * Setting this to 0 means *no* forking. Setting this to 1 means only one test
  * is run at a time but in a child process.
  */
-void litest_runner_set_num_parallel(struct litest_runner *runner, size_t num_jobs);
-void litest_runner_set_timeout(struct litest_runner *runner, unsigned int timeout);
-void litest_runner_set_verbose(struct litest_runner *runner, bool verbose);
-void litest_runner_set_use_colors(struct litest_runner *runner, bool use_colors);
-void litest_runner_set_exit_on_fail(struct litest_runner *runner, bool do_exit);
-void litest_runner_set_output_file(struct litest_runner *runner, FILE *fp);
-void litest_runner_add_test(struct litest_runner *runner,
-			    const struct litest_runner_test_description *t);
-enum litest_runner_result litest_runner_run_tests(struct litest_runner *runner);
+void
+litest_runner_set_num_parallel(struct litest_runner *runner, size_t num_jobs);
+void
+litest_runner_set_timeout(struct litest_runner *runner, unsigned int timeout);
+void
+litest_runner_set_verbose(struct litest_runner *runner, bool verbose);
+void
+litest_runner_set_use_colors(struct litest_runner *runner, bool use_colors);
+void
+litest_runner_set_exit_on_fail(struct litest_runner *runner, bool do_exit);
+void
+litest_runner_set_output_file(struct litest_runner *runner, FILE *fp);
+void
+litest_runner_add_test(struct litest_runner *runner,
+		       const struct litest_runner_test_description *t);
+enum litest_runner_result
+litest_runner_run_tests(struct litest_runner *runner);
 
 typedef enum litest_runner_result (*litest_runner_global_setup_func_t)(void *userdata);
 typedef void (*litest_runner_global_teardown_func_t)(void *userdata);
@@ -171,12 +186,15 @@ litest_runner_set_setup_funcs(struct lit
 			      litest_runner_global_teardown_func_t teardown,
 			      void *userdata);
 
-void litest_runner_destroy(struct litest_runner *runner);
+void
+litest_runner_destroy(struct litest_runner *runner);
+
+DEFINE_DESTROY_CLEANUP_FUNC(litest_runner);
 
 /*
  * Function to call abort(). Depending on the number of forks permitted,
  * this function may simply abort() or it may longjmp back out to collect
  * errors from non-forking tests.
  */
-__attribute__((noreturn))
-void litest_runner_abort(void);
+__attribute__((noreturn)) void
+litest_runner_abort(void);
diff -pruN 1.28.1-1/test/litest-selftest.c 1.30.0-1/test/litest-selftest.c
--- 1.28.1-1/test/litest-selftest.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest-selftest.c	2025-11-25 03:40:43.000000000 +0000
@@ -1,9 +1,8 @@
 #include <config.h>
 
+#include <signal.h>
 #include <sys/resource.h>
 #include <sys/time.h>
-#include <signal.h>
-
 #include <valgrind/valgrind.h>
 
 #include "litest.h"
@@ -299,9 +298,9 @@ END_TEST
 
 START_TEST(litest_double_eq_and_ne)
 {
-	litest_assert_double_eq(0.4,0.4);
-	litest_assert_double_eq(0.4,0.4 + 1E-6);
-	litest_assert_double_ne(0.4,0.4 + 1E-3);
+	litest_assert_double_eq(0.4, 0.4);
+	litest_assert_double_eq(0.4, 0.4 + 1E-6);
+	litest_assert_double_ne(0.4, 0.4 + 1E-3);
 
 	litest_assert_double_eq_epsilon(0.4, 0.5, 0.1);
 	litest_assert_double_eq_epsilon(0.4, 0.5, 0.2);
@@ -312,30 +311,30 @@ END_TEST
 
 START_TEST(litest_double_lt_gt)
 {
-	litest_assert_double_lt(12.0,13.0);
-	litest_assert_double_gt(15.4,13.0);
-	litest_assert_double_le(12.0,12.0);
-	litest_assert_double_le(12.0,20.0);
-	litest_assert_double_ge(12.0,12.0);
-	litest_assert_double_ge(20.0,12.0);
+	litest_assert_double_lt(12.0, 13.0);
+	litest_assert_double_gt(15.4, 13.0);
+	litest_assert_double_le(12.0, 12.0);
+	litest_assert_double_le(12.0, 20.0);
+	litest_assert_double_ge(12.0, 12.0);
+	litest_assert_double_ge(20.0, 12.0);
 }
 END_TEST
 
 START_TEST(litest_double_eq_fails)
 {
-	litest_assert_double_eq(0.41,0.4);
+	litest_assert_double_eq(0.41, 0.4);
 }
 END_TEST
 
 START_TEST(litest_double_eq_epsilon_fails)
 {
-	litest_assert_double_eq_epsilon(0.4,0.5,0.05);
+	litest_assert_double_eq_epsilon(0.4, 0.5, 0.05);
 }
 END_TEST
 
 START_TEST(litest_double_ne_fails)
 {
-	litest_assert_double_ne(0.4 + 1E-7,0.4);
+	litest_assert_double_ne(0.4 + 1E-7, 0.4);
 }
 END_TEST
 
@@ -461,30 +460,20 @@ permutation_func(struct litest_parameter
 START_TEST(parameter_permutations)
 {
 	struct permutation permutations[] = {
-		{ 1, "a", true },
-		{ 1, "a", false },
-		{ 1, "ab", true },
-		{ 1, "ab", false },
-		{ 1, "abc", true },
-		{ 1, "abc", false },
-		{ 2, "a", true },
-		{ 2, "a", false },
-		{ 2, "ab", true },
-		{ 2, "ab", false },
-		{ 2, "abc", true },
-		{ 2, "abc", false },
-		{ 3, "a", true },
-		{ 3, "a", false },
-		{ 3, "ab", true },
-		{ 3, "ab", false },
-		{ 3, "abc", true },
-		{ 3, "abc", false },
+		{ 1, "a", true },          { 1, "a", false },  { 1, "ab", true },
+		{ 1, "ab", false },        { 1, "abc", true }, { 1, "abc", false },
+		{ 2, "a", true },          { 2, "a", false },  { 2, "ab", true },
+		{ 2, "ab", false },        { 2, "abc", true }, { 2, "abc", false },
+		{ 3, "a", true },          { 3, "a", false },  { 3, "ab", true },
+		{ 3, "ab", false },        { 3, "abc", true }, { 3, "abc", false },
 		{ 0, NULL, false, false },
 	};
+	/* clang-format off */
 	struct litest_parameters *params = litest_parameters_new("first", 'i', 3, 1, 2, 3,
 								 "second", 's', 3, "a", "ab", "abc",
 								 "third", 'b',
 								 NULL);
+	/* clang-format on */
 
 	litest_parameters_permutations(params, permutation_func, permutations);
 
@@ -503,7 +492,6 @@ litest_assert_macros_suite(void)
 	Suite *s;
 
 	s = suite_create("litest:assert macros");
-#if 0
 	tc = tcase_create("assert");
 	tcase_add_test_raise_signal(tc, litest_assert_trigger, SIGABRT);
 	tcase_add_test(tc, litest_assert_notrigger);
@@ -575,7 +563,6 @@ litest_assert_macros_suite(void)
 	tcase_add_test_raise_signal(tc, zalloc_too_large, SIGABRT);
 	suite_add_tcase(s, tc);
 
-#endif
 	tc = tcase_create("parameters ");
 	tcase_add_test(tc, parameter_permutations);
 	suite_add_tcase(s, tc);
@@ -584,24 +571,24 @@ litest_assert_macros_suite(void)
 }
 
 int
-main (int argc, char **argv)
+main(int argc, char **argv)
 {
 	const struct rlimit corelimit = { 0, 0 };
 	int nfailed;
 	Suite *s;
 	SRunner *sr;
 
-        /* when running under valgrind we're using nofork mode, so a signal
-         * raised by a test will fail in valgrind. There's nothing to
-         * memcheck here anyway, so just skip the valgrind test */
-        if (RUNNING_ON_VALGRIND)
-            return 77;
+	/* when running under valgrind we're using nofork mode, so a signal
+	 * raised by a test will fail in valgrind. There's nothing to
+	 * memcheck here anyway, so just skip the valgrind test */
+	if (RUNNING_ON_VALGRIND)
+		return 77;
 
 	if (setrlimit(RLIMIT_CORE, &corelimit) != 0)
 		perror("WARNING: Core dumps not disabled");
 
 	s = litest_assert_macros_suite();
-        sr = srunner_create(s);
+	sr = srunner_create(s);
 
 	srunner_run_all(sr, CK_ENV);
 	nfailed = srunner_ntests_failed(sr);
diff -pruN 1.28.1-1/test/litest.c 1.30.0-1/test/litest.c
--- 1.28.1-1/test/litest.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest.c	2025-11-25 03:40:43.000000000 +0000
@@ -29,67 +29,71 @@
 #include <fcntl.h>
 #include <fnmatch.h>
 #include <getopt.h>
+#include <libudev.h>
 #include <poll.h>
 #include <signal.h>
+#include <stdarg.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdarg.h>
-#include <time.h>
-#include <unistd.h>
-#include "linux/input.h"
 #include <sys/ptrace.h>
 #include <sys/resource.h>
-#include <sys/timerfd.h>
-#include <sys/wait.h>
 #include <sys/stat.h>
-#include <sys/types.h>
 #include <sys/sysinfo.h>
-#include <libudev.h>
-#if HAVE_LIBSYSTEMD
+#include <sys/timerfd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "linux/input.h"
+#ifdef HAVE_LIBSYSTEMD
 #include <systemd/sd-bus.h>
 #endif
 #ifdef __FreeBSD__
 #include <termios.h>
 #endif
 
+#include <libevdev/libevdev.h>
+#include <linux/kd.h>
 #include <valgrind/valgrind.h>
 
-#include "util-files.h"
-#include "litest.h"
-#include "litest-runner.h"
-#include "litest-int.h"
-#include "libinput-util.h"
-#include "quirks.h"
-#include "builddir.h"
-
 #include "util-backtrace.h"
+#include "util-files.h"
 #include "util-libinput.h"
+#include "util-mem.h"
 
-#include <linux/kd.h>
+#include "builddir.h"
+#include "libinput-util.h"
+#include "litest-int.h"
+#include "litest-runner.h"
+#include "litest.h"
+#include "quirks.h"
 
 #define evbit(t, c) ((t) << 16U | (c & 0xffff))
 
 #define UDEV_RULES_D "/run/udev/rules.d"
-#define UDEV_FUZZ_OVERRIDE_RULE_FILE UDEV_RULES_D \
-	"/91-litest-fuzz-override-REMOVEME-XXXXXX.rules"
-#define UDEV_TEST_DEVICE_RULE_FILE UDEV_RULES_D \
-	"/91-litest-test-device-REMOVEME-XXXXXXX.rules"
-#define UDEV_DEVICE_GROUPS_FILE UDEV_RULES_D \
-	"/80-libinput-device-groups-litest-XXXXXX.rules"
-
-static int jobs;
-static bool in_debugger = false;
-static bool verbose = false;
-static bool run_deviceless = false;
+#define UDEV_FUZZ_OVERRIDE_RULE_FILE                                           \
+  UDEV_RULES_D                                                                 \
+  "/91-litest-fuzz-override-REMOVEME-XXXXXX.rules"
+#define UDEV_TEST_DEVICE_RULE_FILE                                             \
+  UDEV_RULES_D                                                                 \
+  "/91-litest-test-device-REMOVEME-XXXXXXX.rules"
+#define UDEV_DEVICE_GROUPS_FILE                                                \
+  UDEV_RULES_D                                                                 \
+  "/80-libinput-device-groups-litest-XXXXXX.rules"
+
+bool verbose = false;
+bool in_debugger = false;
+bool run_deviceless = false;
 static bool use_system_rules_quirks = false;
 static bool exit_first = false;
-static FILE * outfile = NULL;
+static FILE *outfile = NULL;
 static const char *filter_test = NULL;
 static const char *filter_device = NULL;
 static const char *filter_group = NULL;
 static int filter_rangeval = INT_MIN;
-static bool use_colors = false;
+bool use_colors = false;
 
 struct param_filter {
 	char name[64];
@@ -119,27 +123,34 @@ created_file_unlink(struct created_file
 	rmdir(f->path);
 }
 
-static struct suite *current_suite = NULL;
+struct suite *current_suite = NULL;
 
-static void litest_init_udev_rules(struct list *created_files_list);
-static void litest_remove_udev_rules(struct list *created_files_list);
-static void litest_print_event(struct libinput_event *event, const char *message);
+static void
+litest_init_udev_rules(struct list *created_files_list);
+static void
+litest_remove_udev_rules(struct list *created_files_list);
+static void
+litest_print_event(struct libinput_event *event, const char *message);
 
 enum quirks_setup_mode {
 	QUIRKS_SETUP_USE_SRCDIR,
 	QUIRKS_SETUP_ONLY_DEVICE,
 	QUIRKS_SETUP_FULL,
 };
-static void litest_setup_quirks(struct list *created_files_list,
-				enum quirks_setup_mode mode);
+static void
+litest_setup_quirks(struct list *created_files_list, enum quirks_setup_mode mode);
 
 /* defined for the litest selftest */
 #ifndef LITEST_DISABLE_BACKTRACE_LOGGING
 #define litest_log(...) fprintf(stderr, __VA_ARGS__)
 #define litest_vlog(format_, args_) vfprintf(stderr, format_, args_)
 #else
-#define litest_log(...) { /* __VA_ARGS__ */ }
-#define litest_vlog(...) { /* __VA_ARGS__ */ }
+#define litest_log(...)                                                        \
+  { /* __VA_ARGS__ */                                                          \
+  }
+#define litest_vlog(...)                                                       \
+  { /* __VA_ARGS__ */                                                          \
+  }
 #endif
 
 LIBINPUT_ATTRIBUTE_PRINTF(4, 5)
@@ -159,7 +170,8 @@ _litest_checkpoint(const char *func,
 		fprintf(stderr,
 			"%s%s():%d - %s%s%s\n",
 			use_colors ? ANSI_BRIGHT_BLUE : "",
-			func, line,
+			func,
+			line,
 			use_colors ? color : "",
 			buf,
 			use_colors ? ANSI_NORMAL : "");
@@ -188,8 +200,7 @@ litest_backtrace(const char *func)
 }
 
 LIBINPUT_ATTRIBUTE_PRINTF(5, 6)
-__attribute__((noreturn))
-void
+__attribute__((noreturn)) void
 litest_fail_condition(const char *file,
 		      int line,
 		      const char *func,
@@ -208,13 +219,12 @@ litest_fail_condition(const char *file,
 		litest_log("%s\n", buf);
 	}
 
-	litest_log("in %s() (%s:%d)\n", func, file, line);
+	litest_log("in %s() (%s:%d)\n", func, file ? file : "???", line);
 	litest_backtrace(func);
 	litest_runner_abort();
 }
 
-__attribute__((noreturn))
-void
+__attribute__((noreturn)) void
 litest_fail_comparison_int(const char *file,
 			   int line,
 			   const char *func,
@@ -231,8 +241,7 @@ litest_fail_comparison_int(const char *f
 	litest_runner_abort();
 }
 
-__attribute__((noreturn))
-void
+__attribute__((noreturn)) void
 litest_fail_comparison_double(const char *file,
 			      int line,
 			      const char *func,
@@ -249,8 +258,7 @@ litest_fail_comparison_double(const char
 	litest_runner_abort();
 }
 
-__attribute__((noreturn))
-void
+__attribute__((noreturn)) void
 litest_fail_comparison_ptr(const char *file,
 			   int line,
 			   const char *func,
@@ -262,8 +270,7 @@ litest_fail_comparison_ptr(const char *f
 	litest_runner_abort();
 }
 
-__attribute__((noreturn))
-void
+__attribute__((noreturn)) void
 litest_fail_comparison_str(const char *file,
 			   int line,
 			   const char *func,
@@ -279,27 +286,6 @@ litest_fail_comparison_str(const char *f
 	litest_runner_abort();
 }
 
-struct test {
-	struct list node;
-	char *name;
-	char *devname;
-	const void *func;
-	void *setup;
-	void *teardown;
-
-	struct range range;
-	int rangeval;
-	bool deviceless;
-
-	struct litest_test_parameters *params;
-};
-
-struct suite {
-	struct list node;
-	struct list tests;
-	char *name;
-};
-
 struct litest_parameter_value {
 	size_t refcnt;
 	struct list link; /* litest_parameter->values */
@@ -393,7 +379,8 @@ litest_parameter_add_double(struct lites
 }
 
 static inline void
-litest_parameter_add_named_i32(struct litest_parameter *p, const struct litest_named_i32 i)
+litest_parameter_add_named_i32(struct litest_parameter *p,
+			       const struct litest_named_i32 i)
 {
 	assert(p->type == 'I');
 
@@ -413,7 +400,8 @@ litest_parameter_value_ref(struct litest
 #endif
 
 static struct litest_parameter_value *
-litest_parameter_value_unref(struct litest_parameter_value *pv) {
+litest_parameter_value_unref(struct litest_parameter_value *pv)
+{
 	if (pv) {
 		assert(pv->refcnt > 0);
 		if (--pv->refcnt == 0) {
@@ -424,7 +412,7 @@ litest_parameter_value_unref(struct lite
 	return NULL;
 }
 
-static struct litest_parameter*
+static struct litest_parameter *
 litest_parameter_new(const char *name, char type)
 {
 	struct litest_parameter *p = zalloc(sizeof *p);
@@ -437,9 +425,9 @@ litest_parameter_new(const char *name, c
 	case 'I':
 	case 's':
 	case 'u':
-		  break;
+		break;
 	default:
-		  assert(!"Type not yet implemented");
+		assert(!"Type not yet implemented");
 	}
 
 	list_init(&p->link);
@@ -452,7 +440,8 @@ litest_parameter_new(const char *name, c
 }
 
 static struct litest_parameter *
-litest_parameter_ref(struct litest_parameter *p) {
+litest_parameter_ref(struct litest_parameter *p)
+{
 	assert(p);
 	assert(p->refcnt > 0);
 	p->refcnt++;
@@ -460,7 +449,8 @@ litest_parameter_ref(struct litest_param
 }
 
 static struct litest_parameter *
-litest_parameter_unref(struct litest_parameter *p) {
+litest_parameter_unref(struct litest_parameter *p)
+{
 	if (p) {
 		assert(p->refcnt > 0);
 		if (--p->refcnt == 0) {
@@ -475,6 +465,8 @@ litest_parameter_unref(struct litest_par
 	return NULL;
 }
 
+DEFINE_UNREF_CLEANUP_FUNC(litest_parameter);
+
 static void
 litest_parameters_add(struct litest_parameters *ps, struct litest_parameter *param)
 {
@@ -488,7 +480,8 @@ litest_parameters_add(struct litest_para
 }
 
 struct litest_parameters *
-_litest_parameters_new(const char *name, ...) {
+_litest_parameters_new(const char *name, ...)
+{
 	struct litest_parameters *ps = zalloc(sizeof *ps);
 
 	list_init(&ps->params);
@@ -500,7 +493,7 @@ _litest_parameters_new(const char *name,
 	while (name) {
 		char type = va_arg(args, int);
 
-		struct litest_parameter *param = litest_parameter_new(name, type);
+		_unref_(litest_parameter) *param = litest_parameter_new(name, type);
 		if (type == 'b') {
 			litest_parameter_add_bool(param, true);
 			litest_parameter_add_bool(param, false);
@@ -534,19 +527,21 @@ _litest_parameters_new(const char *name,
 					break;
 				}
 				case 'I': {
-					struct litest_named_i32 p = va_arg(args, struct litest_named_i32);
+					struct litest_named_i32 p =
+						va_arg(args, struct litest_named_i32);
 					litest_parameter_add_named_i32(param, p);
 					break;
 				}
 				default:
-					abort();
+					litest_abort_msg(
+						"Unhandled parameter type '%c'",
+						type);
 					break;
 				}
 			}
 		}
 
 		litest_parameters_add(ps, param);
-		litest_parameter_unref(param);
 		name = va_arg(args, const char *);
 	}
 
@@ -556,7 +551,8 @@ _litest_parameters_new(const char *name,
 }
 
 struct litest_parameters *
-litest_parameters_ref(struct litest_parameters *p) {
+litest_parameters_ref(struct litest_parameters *p)
+{
 	assert(p);
 	assert(p->refcnt > 0);
 	p->refcnt++;
@@ -564,7 +560,8 @@ litest_parameters_ref(struct litest_para
 }
 
 struct litest_parameters *
-litest_parameters_unref(struct litest_parameters *params) {
+litest_parameters_unref(struct litest_parameters *params)
+{
 	if (params) {
 		assert(params->refcnt > 0);
 		if (--params->refcnt == 0) {
@@ -592,14 +589,18 @@ _permutate(struct litest_parameters_perm
 	struct litest_parameter_value *pv;
 	struct litest_parameter *param = list_first_entry(next_param, param, link);
 	list_for_each(pv, &param->values, link) {
-		struct litest_parameters_permutation_value v  = {
+		struct litest_parameters_permutation_value v = {
 			.value = pv->value,
 		};
 
 		memcpy(v.name, param->name, min(sizeof(v.name), sizeof(param->name)));
 
 		list_append(&permutation->values, &v.link);
-		int rc = _permutate(permutation, &param->link, list_head, func, userdata);
+		int rc = _permutate(permutation,
+				    &param->link,
+				    list_head,
+				    func,
+				    userdata);
 		if (rc)
 			return rc;
 		list_remove(&v.link);
@@ -621,36 +622,45 @@ litest_parameters_permutations(struct li
 	struct litest_parameters_permutation permutation;
 	list_init(&permutation.values);
 
-	return _permutate(&permutation, &params->params, &params->params, func, userdata);
+	return _permutate(&permutation,
+			  &params->params,
+			  &params->params,
+			  func,
+			  userdata);
 }
 
 static struct litest_device *current_device;
 
-struct litest_device *litest_current_device(void)
+struct litest_device *
+litest_current_device(void)
 {
 	return current_device;
 }
 
 int
-_litest_dispatch(struct libinput *li,
-		 const char *func,
-		 int line)
+_litest_dispatch(struct libinput *li, const char *func, int line)
 {
 	static int dispatch_counter = 0;
 
 	++dispatch_counter;
 
-	_litest_checkpoint(func, line, ANSI_MAGENTA,
+	_litest_checkpoint(func,
+			   line,
+			   ANSI_MAGENTA,
 			   "┌────────────────────  dispatch %3d ────────────────────┐",
 			   dispatch_counter);
 	int rc = libinput_dispatch(li);
 	enum libinput_event_type type = libinput_next_event_type(li);
 
-	const char *evtype = type == LIBINPUT_EVENT_NONE ? "NONE" : litest_event_type_str(type);
-	_litest_checkpoint(func, line, ANSI_MAGENTA,
-			   "└──────────────────── /dispatch %3d ────────────────────┘ pending %s",
-			   dispatch_counter,
-			   evtype);
+	const char *evtype =
+		type == LIBINPUT_EVENT_NONE ? "NONE" : litest_event_type_str(type);
+	_litest_checkpoint(
+		func,
+		line,
+		ANSI_MAGENTA,
+		"└──────────────────── /dispatch %3d ────────────────────┘ pending %s",
+		dispatch_counter,
+		evtype);
 	return rc;
 }
 
@@ -659,7 +669,7 @@ grab_device(struct litest_device *device
 {
 	struct libinput *li = libinput_device_get_context(device->libinput_device);
 	struct litest_context *ctx = libinput_get_user_data(li);
-	struct udev_device *udev_device;
+	_unref_(udev_device) * udev_device;
 	const char *devnode;
 	struct path *p;
 
@@ -675,14 +685,14 @@ grab_device(struct litest_device *device
 	 */
 	list_for_each(p, &ctx->paths, link) {
 		if (streq(p->path, devnode)) {
-			int rc = ioctl(p->fd, EVIOCGRAB, (void*)mode ? 1 : 0);
+			int rc = ioctl(p->fd, EVIOCGRAB, (void *)mode ? 1 : 0);
 			litest_assert_errno_success(rc);
-			udev_device_unref(udev_device);
 			return;
 		}
 	}
 	litest_abort_msg("Failed to find device %s to %sgrab",
-			 devnode, mode ? "" : "un");
+			 devnode,
+			 mode ? "" : "un");
 }
 
 void
@@ -697,20 +707,26 @@ litest_ungrab_device(struct litest_devic
 	grab_device(device, false);
 }
 
-void litest_set_current_device(struct litest_device *device)
+void
+litest_set_current_device(struct litest_device *device)
 {
 	current_device = device;
 }
 
-void litest_generic_device_teardown(void)
+void
+litest_generic_device_teardown(void)
 {
-	litest_delete_device(current_device);
+	litest_device_destroy(current_device);
 	current_device = NULL;
 }
 
 static struct list devices = LIST_INIT(devices); /* struct litest_test_device */
 
-static struct list all_test_suites = LIST_INIT(all_test_suites); /* struct suite */
+void
+litest_add_test_device(struct list *device)
+{
+	list_append(&devices, device);
+}
 
 static inline void
 litest_system(const char *command)
@@ -770,8 +786,8 @@ litest_add_tcase_for_device(struct suite
 			t->devname = safe_strdup(dev->shortname);
 			t->func = func;
 			t->setup = dev->setup;
-			t->teardown = dev->teardown ?
-					dev->teardown : litest_generic_device_teardown;
+			t->teardown = dev->teardown ? dev->teardown
+						    : litest_generic_device_teardown;
 			if (range)
 				t->range = *range;
 			t->rangeval = rangeval;
@@ -781,8 +797,7 @@ litest_add_tcase_for_device(struct suite
 	} while (++rangeval < range->upper);
 }
 
-struct permutation_userdata
-{
+struct permutation_userdata {
 	struct suite *suite;
 	const char *funcname;
 	const void *func;
@@ -804,10 +819,9 @@ permutation_func(struct litest_parameter
 		const struct param_filter *f = data->param_filters;
 		while (!filtered && strlen(f->name)) {
 			if (streq(pmv->name, f->name)) {
-				char *s = multivalue_as_str(&pmv->value);
+				_autofree_ char *s = multivalue_as_str(&pmv->value);
 				if (fnmatch(f->glob, s, 0) != 0)
 					filtered = true;
-				free(s);
 			}
 			f++;
 		}
@@ -834,8 +848,8 @@ permutation_func(struct litest_parameter
 	if (data->dev) {
 		t->devname = safe_strdup(data->dev->shortname);
 		t->setup = data->dev->setup;
-		t->teardown = data->dev->teardown ?
-				data->dev->teardown : litest_generic_device_teardown;
+		t->teardown = data->dev->teardown ? data->dev->teardown
+						  : litest_generic_device_teardown;
 	} else {
 		t->devname = safe_strdup(data->devname);
 		t->setup = NULL;
@@ -879,8 +893,7 @@ litest_add_tcase_no_device(struct suite
 	const char *test_name = funcname;
 	const struct range no_range = range_init_empty();
 
-	if (filter_device &&
-	    fnmatch(filter_device, test_name, 0) != 0)
+	if (filter_device && fnmatch(filter_device, test_name, 0) != 0)
 		return;
 
 	if (run_deviceless)
@@ -917,8 +930,7 @@ litest_add_tcase_no_device_with_params(s
 {
 	const char *test_name = funcname;
 
-	if (filter_device &&
-	    fnmatch(filter_device, test_name, 0) != 0)
+	if (filter_device && fnmatch(filter_device, test_name, 0) != 0)
 		return;
 
 	if (run_deviceless)
@@ -944,8 +956,7 @@ litest_add_tcase_deviceless(struct suite
 	const char *test_name = funcname;
 	const struct range no_range = range_init_empty();
 
-	if (filter_device &&
-	    fnmatch(filter_device, test_name, 0) != 0)
+	if (filter_device && fnmatch(filter_device, test_name, 0) != 0)
 		return;
 
 	if (!range)
@@ -980,8 +991,7 @@ litest_add_tcase_deviceless_with_params(
 {
 	const char *test_name = funcname;
 
-	if (filter_device &&
-	    fnmatch(filter_device, test_name, 0) != 0)
+	if (filter_device && fnmatch(filter_device, test_name, 0) != 0)
 		return;
 
 	struct permutation_userdata data = {
@@ -1009,8 +1019,7 @@ litest_add_tcase(const char *filename,
 	litest_assert(required >= LITEST_DEVICELESS);
 	litest_assert(excluded >= LITEST_DEVICELESS);
 
-	if (filter_test &&
-	    fnmatch(filter_test, funcname, 0) != 0)
+	if (filter_test && fnmatch(filter_test, funcname, 0) != 0)
 		return;
 
 	struct suite *suite = current_suite;
@@ -1018,17 +1027,22 @@ litest_add_tcase(const char *filename,
 	if (filter_group && fnmatch(filter_group, suite->name, 0) != 0)
 		return;
 
-	if (required == LITEST_DEVICELESS &&
-	    excluded == LITEST_DEVICELESS) {
+	if (required == LITEST_DEVICELESS && excluded == LITEST_DEVICELESS) {
 		if (params)
-			litest_add_tcase_deviceless_with_params(suite, func, funcname, params);
+			litest_add_tcase_deviceless_with_params(suite,
+								func,
+								funcname,
+								params);
 		else
 			litest_add_tcase_deviceless(suite, func, funcname, range);
 		added = true;
 	} else if (required == LITEST_DISABLE_DEVICE &&
-	    excluded == LITEST_DISABLE_DEVICE) {
+		   excluded == LITEST_DISABLE_DEVICE) {
 		if (params)
-			litest_add_tcase_no_device_with_params(suite, func, funcname, params);
+			litest_add_tcase_no_device_with_params(suite,
+							       func,
+							       funcname,
+							       params);
 		else
 			litest_add_tcase_no_device(suite, func, funcname, range);
 		added = true;
@@ -1089,11 +1103,11 @@ litest_add_tcase(const char *filename,
 		}
 	}
 
-	if (!added &&
-	    filter_test == NULL &&
-	    filter_device == NULL &&
+	if (!added && filter_test == NULL && filter_device == NULL &&
 	    filter_group == NULL) {
-		fprintf(stderr, "Test '%s' does not match any devices. Aborting.\n", funcname);
+		fprintf(stderr,
+			"Test '%s' does not match any devices. Aborting.\n",
+			funcname);
 		abort();
 	}
 }
@@ -1110,7 +1124,9 @@ _litest_add_parametrized_no_device(const
 				   const void *func,
 				   struct litest_parameters *params)
 {
-	_litest_add_parametrized(name, funcname, func,
+	_litest_add_parametrized(name,
+				 funcname,
+				 func,
 				 LITEST_DISABLE_DEVICE,
 				 LITEST_DISABLE_DEVICE,
 				 params);
@@ -1131,9 +1147,7 @@ _litest_add_ranged_no_device(const char
 }
 
 void
-_litest_add_deviceless(const char *name,
-		       const char *funcname,
-		       const void *func)
+_litest_add_deviceless(const char *name, const char *funcname, const void *func)
 {
 	_litest_add_ranged(name,
 			   funcname,
@@ -1149,7 +1163,9 @@ _litest_add_parametrized_deviceless(cons
 				    const void *func,
 				    struct litest_parameters *params)
 {
-	_litest_add_parametrized(name, funcname, func,
+	_litest_add_parametrized(name,
+				 funcname,
+				 func,
 				 LITEST_DISABLE_DEVICE,
 				 LITEST_DISABLE_DEVICE,
 				 params);
@@ -1162,12 +1178,7 @@ _litest_add(const char *name,
 	    int64_t required,
 	    int64_t excluded)
 {
-	_litest_add_ranged(name,
-			   funcname,
-			   func,
-			   required,
-			   excluded,
-			   NULL);
+	_litest_add_ranged(name, funcname, func, required, excluded, NULL);
 }
 
 void
@@ -1213,8 +1224,7 @@ _litest_add_ranged_for_device(const char
 
 	litest_assert(type < LITEST_NO_DEVICE);
 
-	if (filter_test &&
-	    fnmatch(filter_test, funcname, 0) != 0)
+	if (filter_test && fnmatch(filter_test, funcname, 0) != 0)
 		return;
 
 	struct suite *s = current_suite;
@@ -1223,18 +1233,13 @@ _litest_add_ranged_for_device(const char
 		return;
 
 	list_for_each(dev, &devices, node) {
-		if (filter_device &&
-		    fnmatch(filter_device, dev->shortname, 0) != 0) {
+		if (filter_device && fnmatch(filter_device, dev->shortname, 0) != 0) {
 			device_filtered = true;
 			continue;
 		}
 
 		if (dev->type == type) {
-			litest_add_tcase_for_device(s,
-						    funcname,
-						    func,
-						    dev,
-						    range);
+			litest_add_tcase_for_device(s, funcname, func, dev, range);
 			return;
 		}
 	}
@@ -1256,8 +1261,7 @@ _litest_add_parametrized_for_device(cons
 
 	litest_assert(type < LITEST_NO_DEVICE);
 
-	if (filter_test &&
-	    fnmatch(filter_test, funcname, 0) != 0)
+	if (filter_test && fnmatch(filter_test, funcname, 0) != 0)
 		return;
 
 	struct suite *s = current_suite;
@@ -1266,8 +1270,7 @@ _litest_add_parametrized_for_device(cons
 		return;
 
 	list_for_each(dev, &devices, node) {
-		if (filter_device &&
-		    fnmatch(filter_device, dev->shortname, 0) != 0) {
+		if (filter_device && fnmatch(filter_device, dev->shortname, 0) != 0) {
 			device_filtered = true;
 			continue;
 		}
@@ -1287,6 +1290,19 @@ _litest_add_parametrized_for_device(cons
 		litest_abort_msg("Invalid test device type");
 }
 
+static int
+is_actual_error(const char *error, size_t index, void *data)
+{
+	/* We don't want to abort on these errors */
+	if (((RUNNING_ON_VALGRIND || in_debugger) &&
+	     strstr(error, "scheduled expiry is in the past")) ||
+	    strstr(error, "event processing lagging behind")) {
+		return 0;
+	}
+
+	return 1;
+}
+
 LIBINPUT_ATTRIBUTE_PRINTF(3, 0)
 static void
 litest_log_handler(struct libinput *libinput,
@@ -1295,12 +1311,13 @@ litest_log_handler(struct libinput *libi
 		   va_list args)
 {
 	const char *priority = NULL;
-	const char *color;
+	const char *color = ""; // NOLINT: deadcode.DeadStores
+	const char *color_reset = ANSI_NORMAL;
 
-	switch(pri) {
+	switch (pri) {
 	case LIBINPUT_LOG_PRIORITY_INFO:
-		priority =  "info ";
-		color = ANSI_HIGHLIGHT;
+		priority = "info ";
+		color = ANSI_BOLD;
 		break;
 	case LIBINPUT_LOG_PRIORITY_ERROR:
 		priority = "error";
@@ -1311,39 +1328,38 @@ litest_log_handler(struct libinput *libi
 		color = ANSI_NORMAL;
 		break;
 	default:
-		  abort();
+		abort();
 	}
 
+	_autofree_ char *msg = strdup_vprintf(format, args);
+
 	if (!use_colors)
-		color = "";
+		color_reset = "";
 	else if (strstr(format, "tap:"))
 		color = ANSI_BLUE;
 	else if (strstr(format, "thumb state:"))
 		color = ANSI_YELLOW;
 	else if (strstr(format, "button state:"))
 		color = ANSI_MAGENTA;
-	else if (strstr(format, "touch-size:") ||
-		 strstr(format, "pressure:"))
+	else if (strstr(format, "touch-size:") || strstr(format, "pressure:"))
 		color = ANSI_GREEN;
-	else if (strstr(format, "palm:") ||
-		 strstr(format, "thumb:"))
+	else if (strstr(format, "palm:") || strstr(format, "thumb:"))
 		color = ANSI_CYAN;
 	else if (strstr(format, "edge-scroll:"))
 		color = ANSI_BRIGHT_GREEN;
 	else if (strstr(format, "gesture:"))
 		color = ANSI_BRIGHT_YELLOW;
+	else if (strstr(msg, "Plugin:"))
+		color = ANSI_BRIGHT_CYAN;
 
-	fprintf(stderr, "%slitest %s ", color, priority);
-	vfprintf(stderr, format, args);
-	if (use_colors)
-		fprintf(stderr, ANSI_NORMAL);
+	fprintf(stderr, "%slitest %s %s%s", color, priority, msg, color_reset);
 
-	if (strstr(format, "client bug: ") ||
-	    strstr(format, "libinput bug: ")) {
+	if (strstr(msg, "client bug: ") || strstr(msg, "libinput bug: ") ||
+	    strstr(msg, "plugin bug: ")) {
 		/* valgrind is too slow and some of our offsets are too
 		 * short, don't abort if during a valgrind run we get a
 		 * negative offset */
-		if ((RUNNING_ON_VALGRIND && in_debugger) ||
+		if ((RUNNING_ON_VALGRIND || in_debugger) &&
 		    strstr(format, "scheduled expiry is in the past")) {
 			/* noop */
 		} else if (strstr(format, "event processing lagging behind")) {
@@ -1353,7 +1369,7 @@ litest_log_handler(struct libinput *libi
 		}
 	}
 
-	if (strstr(format, "Touch jump detected and discarded")) {
+	if (strstr(msg, "Touch jump detected and discarded")) {
 		litest_abort_msg("libinput touch jump triggered, aborting.");
 	}
 }
@@ -1394,13 +1410,15 @@ litest_init_device_udev_rules(struct lit
 	 * ENV variables aren't set yet by the time the builtin runs.
 	 */
 	if (need_keyboard_builtin) {
-		fprintf(f, ""
+		fprintf(f,
+			""
 			"ATTRS{name}==\"litest %s*\","
 			" IMPORT{builtin}=\"keyboard\"\n",
 			dev->name);
 	}
 
-	fprintf(f, "LABEL=\"rule%d_end\"\n\n", count);;
+	fprintf(f, "LABEL=\"rule%d_end\"\n\n", count);
+	;
 }
 
 static void
@@ -1408,15 +1426,11 @@ litest_init_all_device_udev_rules(struct
 {
 	struct created_file *file = zalloc(sizeof(*file));
 	struct litest_test_device *dev;
-	char *path = NULL;
 	FILE *f;
-	int rc;
 	int fd;
 
-	rc = xasprintf(&path,
-		      "%s/99-litest-XXXXXX.rules",
-		      UDEV_RULES_D);
-	litest_assert_errno_success(rc);
+	char *path = strdup_printf("%s/99-litest-XXXXXX.rules", UDEV_RULES_D);
+	litest_assert_ptr_notnull(path);
 
 	fd = mkstemps(path, 6);
 	litest_assert_errno_success(fd);
@@ -1525,9 +1539,9 @@ litest_run_suite(struct list *suites, in
 	size_t ntests = 0;
 	enum litest_runner_result result = LITEST_SKIP;
 	struct suite *s;
-	struct litest_runner *runner = litest_runner_new();
+	_destroy_(litest_runner) *runner = litest_runner_new();
 
-	litest_runner_set_num_parallel(runner, jobs > 0 ? jobs : 0);
+	litest_runner_set_num_parallel(runner, njobs > 0 ? njobs : 0);
 	if (outfile)
 		litest_runner_set_output_file(runner, outfile);
 	litest_runner_set_verbose(runner, verbose);
@@ -1539,39 +1553,46 @@ litest_run_suite(struct list *suites, in
 	list_for_each(s, suites, node) {
 		struct test *t;
 		list_for_each(t, &s->tests, node) {
-			struct litest_runner_test_description tdesc = {0};
+			struct litest_runner_test_description tdesc = { 0 };
 
 			if (range_is_valid(&t->range)) {
-				snprintf(tdesc.name, sizeof(tdesc.name),
-					  "%s:%s:%s:%d",
-					  s->name,
-					  t->name,
-					  t->devname,
-					  t->rangeval);
+				snprintf(tdesc.name,
+					 sizeof(tdesc.name),
+					 "%s:%s:%s:%d",
+					 s->name,
+					 t->name,
+					 t->devname,
+					 t->rangeval);
 			} else if (t->params) {
-				char buf[256] = {0};
+				char buf[256] = { 0 };
 
 				struct litest_test_param *tp;
 				bool is_first = true;
 				list_for_each(tp, &t->params->test_params, link) {
-					char *val = multivalue_as_str(&tp->value);
-					snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
-						 "%s%s:%s", is_first ? "" : ",", tp->name, val);
-					free(val);
+					_autofree_ char *val =
+						multivalue_as_str(&tp->value);
+					snprintf(buf + strlen(buf),
+						 sizeof(buf) - strlen(buf),
+						 "%s%s:%s",
+						 is_first ? "" : ",",
+						 tp->name,
+						 val);
 					is_first = false;
 				}
-				snprintf(tdesc.name, sizeof(tdesc.name),
-					  "%s:%s:%s:%s",
-					  s->name,
-					  t->name,
-					  t->devname,
-					  buf);
+				snprintf(tdesc.name,
+					 sizeof(tdesc.name),
+					 "%s:%s:%s:%s",
+					 s->name,
+					 t->name,
+					 t->devname,
+					 buf);
 			} else {
-				snprintf(tdesc.name, sizeof(tdesc.name),
-					  "%s:%s:%s",
-					  s->name,
-					  t->name,
-					  t->devname);
+				snprintf(tdesc.name,
+					 sizeof(tdesc.name),
+					 "%s:%s:%s",
+					 s->name,
+					 t->name,
+					 t->devname);
 			}
 			tdesc.func = t->func;
 			tdesc.setup = t->setup;
@@ -1587,8 +1608,6 @@ litest_run_suite(struct list *suites, in
 	if (ntests > 0)
 		result = litest_runner_run_tests(runner);
 
-	litest_runner_destroy(runner);
-
 	return result;
 }
 
@@ -1596,10 +1615,10 @@ static inline int
 inhibit(void)
 {
 	int lock_fd = -1;
-#if HAVE_LIBSYSTEMD
-	sd_bus_error error = SD_BUS_ERROR_NULL;
-	sd_bus_message *m = NULL;
-	sd_bus *bus = NULL;
+#ifdef HAVE_LIBSYSTEMD
+	_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+	_unref_(sd_bus_message) *m = NULL;
+	_unref_(sd_bus) *bus = NULL;
 	int rc;
 
 	if (run_deviceless)
@@ -1611,18 +1630,20 @@ inhibit(void)
 		goto out;
 	}
 
-	rc = sd_bus_call_method(bus,
-				"org.freedesktop.login1",
-				"/org/freedesktop/login1",
-				"org.freedesktop.login1.Manager",
-				"Inhibit",
-				&error,
-				&m,
-				"ssss",
-				"sleep:shutdown:handle-lid-switch:handle-power-key:handle-suspend-key:handle-hibernate-key",
-				"libinput test-suite runner",
-				"testing in progress",
-				"block");
+	rc = sd_bus_call_method(
+		bus,
+		"org.freedesktop.login1",
+		"/org/freedesktop/login1",
+		"org.freedesktop.login1.Manager",
+		"Inhibit",
+		&error,
+		&m,
+		"ssss",
+		"sleep:shutdown:handle-lid-switch:handle-power-key:handle-suspend-key:"
+		"handle-hibernate-key",
+		"libinput test-suite runner",
+		"testing in progress",
+		"block");
 	if (rc < 0) {
 		fprintf(stderr, "Warning: inhibit failed: %s\n", error.message);
 		goto out;
@@ -1636,10 +1657,7 @@ inhibit(void)
 
 	lock_fd = dup(lock_fd);
 out:
-	sd_bus_error_free(&error);
-	sd_bus_message_unref(m);
 	sd_bus_close(bus);
-	sd_bus_unref(bus);
 #endif
 	return lock_fd;
 }
@@ -1658,10 +1676,16 @@ disable_tty(void)
 		/* Put the tty into raw mode */
 		struct termios tios;
 		if (tcgetattr(STDIN_FILENO, &tios))
-				fprintf(stderr, "Failed to get terminal attribute: %d - %s\n", errno, strerror(errno));
+			fprintf(stderr,
+				"Failed to get terminal attribute: %d - %s\n",
+				errno,
+				strerror(errno));
 		cfmakeraw(&tios);
 		if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tios))
-				fprintf(stderr, "Failed to set terminal attribute: %d - %s\n", errno, strerror(errno));
+			fprintf(stderr,
+				"Failed to set terminal attribute: %d - %s\n",
+				errno,
+				strerror(errno));
 #endif
 	}
 
@@ -1677,16 +1701,22 @@ restore_tty(int tty_mode)
 		/* Put the tty into "sane" mode */
 		struct termios tios;
 		if (tcgetattr(STDIN_FILENO, &tios))
-				fprintf(stderr, "Failed to get terminal attribute: %d - %s\n", errno, strerror(errno));
+			fprintf(stderr,
+				"Failed to get terminal attribute: %d - %s\n",
+				errno,
+				strerror(errno));
 		cfmakesane(&tios);
 		if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tios))
-				fprintf(stderr, "Failed to set terminal attribute: %d - %s\n", errno, strerror(errno));
+			fprintf(stderr,
+				"Failed to set terminal attribute: %d - %s\n",
+				errno,
+				strerror(errno));
 #endif
 	}
 }
 
-static inline enum litest_runner_result
-litest_run(struct list *suites)
+enum litest_runner_result
+litest_run(struct list *suites, int njobs)
 {
 	const struct rlimit corelimit = { 0, 0 };
 	int inhibit_lock_fd;
@@ -1700,15 +1730,13 @@ litest_run(struct list *suites)
 	struct list created_files_list = LIST_INIT(created_files_list);
 
 	if (run_deviceless) {
-		litest_setup_quirks(&created_files_list,
-				    QUIRKS_SETUP_USE_SRCDIR);
+		litest_setup_quirks(&created_files_list, QUIRKS_SETUP_USE_SRCDIR);
 	} else {
 		enum quirks_setup_mode mode;
 		litest_init_udev_rules(&created_files_list);
 
-		mode = use_system_rules_quirks ?
-				QUIRKS_SETUP_ONLY_DEVICE :
-				QUIRKS_SETUP_FULL;
+		mode = use_system_rules_quirks ? QUIRKS_SETUP_ONLY_DEVICE
+					       : QUIRKS_SETUP_FULL;
 		litest_setup_quirks(&created_files_list, mode);
 	}
 
@@ -1716,12 +1744,12 @@ litest_run(struct list *suites)
 	 * avoid messing up our host. But if we're inside gdb or running
 	 * without forking, leave it as-is.
 	 */
-	if (!run_deviceless && jobs > 1 && !in_debugger)
+	if (!run_deviceless && njobs > 1 && !in_debugger)
 		tty_mode = disable_tty();
 
 	inhibit_lock_fd = inhibit();
 
-	enum litest_runner_result result = litest_run_suite(suites, jobs);
+	enum litest_runner_result result = litest_run_suite(suites, njobs);
 
 	close(inhibit_lock_fd);
 
@@ -1733,8 +1761,7 @@ litest_run(struct list *suites)
 }
 
 static struct input_absinfo *
-merge_absinfo(const struct input_absinfo *orig,
-	      const struct input_absinfo *override)
+merge_absinfo(const struct input_absinfo *orig, const struct input_absinfo *override)
 {
 	struct input_absinfo *abs;
 	unsigned int nelem, i;
@@ -1767,7 +1794,7 @@ merge_absinfo(const struct input_absinfo
 	return abs;
 }
 
-static int*
+static int *
 merge_events(const int *orig, const int *override)
 {
 	int *events;
@@ -1813,12 +1840,10 @@ litest_copy_file(const char *dest, const
 	if (strstr(dest, "XXXXXX")) {
 		int suffixlen;
 
-		suffixlen = file->path +
-				strlen(file->path) -
-				rindex(file->path, '.');
+		suffixlen = file->path + strlen(file->path) - rindex(file->path, '.');
 		out = mkstemps(file->path, suffixlen);
 	} else {
-		out = open(file->path, O_CREAT|O_WRONLY, 0644);
+		out = open(file->path, O_CREAT | O_WRONLY, 0644);
 	}
 	if (out == -1)
 		litest_abort_msg("Failed to write to file %s (%s)",
@@ -1843,7 +1868,6 @@ litest_copy_file(const char *dest, const
 	} else {
 		size_t written = write(out, src, strlen(src));
 		litest_assert_int_eq(written, strlen(src));
-
 	}
 	close(out);
 
@@ -1854,16 +1878,17 @@ static inline void
 litest_install_model_quirks(struct list *created_files_list)
 {
 	const char *warning =
-			 "#################################################################\n"
-			 "# WARNING: REMOVE THIS FILE\n"
-			 "# This is a run-time file for the libinput test suite and\n"
-			 "# should be removed on exit. If the test-suite is not currently \n"
-			 "# running, remove this file\n"
-			 "#################################################################\n\n";
+		"#################################################################\n"
+		"# WARNING: REMOVE THIS FILE\n"
+		"# This is a run-time file for the libinput test suite and\n"
+		"# should be removed on exit. If the test-suite is not currently \n"
+		"# running, remove this file\n"
+		"#################################################################\n\n";
 	struct created_file *file;
-	const char *test_device_udev_rule = "KERNELS==\"*input*\", "
-					    "ATTRS{name}==\"litest *\", "
-					    "ENV{LIBINPUT_TEST_DEVICE}=\"1\"";
+	const char *test_device_udev_rule =
+		"KERNELS==\"*input*\", "
+		"ATTRS{name}==\"litest *\", "
+		"ENV{LIBINPUT_TEST_DEVICE}=\"1\"";
 
 	file = litest_copy_file(UDEV_TEST_DEVICE_RULE_FILE,
 				test_device_udev_rule,
@@ -1890,8 +1915,7 @@ litest_install_model_quirks(struct list
 }
 
 static char *
-litest_init_device_quirk_file(const char *data_dir,
-			      struct litest_test_device *dev)
+litest_init_device_quirk_file(const char *data_dir, struct litest_test_device *dev)
 {
 	int fd;
 	FILE *f;
@@ -1901,12 +1925,13 @@ litest_init_device_quirk_file(const char
 	if (!dev->quirk_file)
 		return NULL;
 
-	snprintf(path, sizeof(path),
+	snprintf(path,
+		 sizeof(path),
 		 "%s/99-%03d-%s.quirks",
 		 data_dir,
 		 ++count,
 		 dev->shortname);
-	fd = open(path, O_CREAT|O_WRONLY, 0644);
+	fd = open(path, O_CREAT | O_WRONLY, 0644);
 	litest_assert_errno_success(fd);
 	f = fdopen(fd, "w");
 	litest_assert_notnull(f);
@@ -1916,7 +1941,9 @@ litest_init_device_quirk_file(const char
 	return safe_strdup(path);
 }
 
-static int is_quirks_file(const struct dirent *dir) {
+static int
+is_quirks_file(const struct dirent *dir)
+{
 	return strendswith(dir->d_name, ".quirks");
 }
 
@@ -1924,16 +1951,12 @@ static int is_quirks_file(const struct d
  * Install the quirks from the quirks/ source directory.
  */
 static void
-litest_install_source_quirks(struct list *created_files_list,
-			     const char *dirname)
+litest_install_source_quirks(struct list *created_files_list, const char *dirname)
 {
-	struct dirent **namelist;
+	_autofree_ struct dirent **namelist;
 	int ndev;
 
-	ndev = scandir(LIBINPUT_QUIRKS_SRCDIR,
-		       &namelist,
-		       is_quirks_file,
-		       versionsort);
+	ndev = scandir(LIBINPUT_QUIRKS_SRCDIR, &namelist, is_quirks_file, versionsort);
 	litest_assert_int_ge(ndev, 0);
 
 	for (int idx = 0; idx < ndev; idx++) {
@@ -1942,23 +1965,20 @@ litest_install_source_quirks(struct list
 		char dest[PATH_MAX];
 		char src[PATH_MAX];
 
-		filename = namelist[idx]->d_name;
-		snprintf(src, sizeof(src), "%s/%s",
-			 LIBINPUT_QUIRKS_SRCDIR, filename);
+		_autofree_ struct dirent *entry = namelist[idx];
+		filename = entry->d_name;
+		snprintf(src, sizeof(src), "%s/%s", LIBINPUT_QUIRKS_SRCDIR, filename);
 		snprintf(dest, sizeof(dest), "%s/%s", dirname, filename);
 		file = litest_copy_file(dest, src, NULL, true);
 		list_append(created_files_list, &file->link);
-		free(namelist[idx]);
 	}
-	free(namelist);
 }
 
 /**
  * Install the quirks from the various litest test devices
  */
 static void
-litest_install_device_quirks(struct list *created_files_list,
-			     const char *dirname)
+litest_install_device_quirks(struct list *created_files_list, const char *dirname)
 {
 	struct litest_test_device *dev;
 
@@ -1975,8 +1995,7 @@ litest_install_device_quirks(struct list
 }
 
 static void
-litest_setup_quirks(struct list *created_files_list,
-		    enum quirks_setup_mode mode)
+litest_setup_quirks(struct list *created_files_list, enum quirks_setup_mode mode)
 {
 	struct created_file *file = NULL;
 	const char *dirname;
@@ -2047,8 +2066,9 @@ litest_create(enum litest_device_type wh
 	struct litest_test_device *dev;
 	const char *name;
 	const struct input_id *id;
-	struct input_absinfo *abs;
-	int *events, *e;
+	_autofree_ struct input_absinfo *abs;
+	_autofree_ int *events;
+	int *e;
 	const char *path;
 	int fd, rc;
 	bool found = false;
@@ -2088,34 +2108,28 @@ litest_create(enum litest_device_type wh
 		d->interface = dev->interface;
 
 		for (e = events; *e != -1; e += 2) {
-			unsigned int type = *e,
-				     code = *(e + 1);
+			unsigned int type = *e, code = *(e + 1);
 
-			if (type == INPUT_PROP_MAX &&
-			    code == INPUT_PROP_SEMI_MT) {
+			if (type == INPUT_PROP_MAX && code == INPUT_PROP_SEMI_MT) {
 				d->semi_mt.is_semi_mt = true;
 				break;
 			}
 		}
 	}
 
-	free(abs);
-	free(events);
-
 	path = libevdev_uinput_get_devnode(d->uinput);
 	litest_assert_ptr_notnull(path);
-	fd = open(path, O_RDWR|O_NONBLOCK);
+	fd = open(path, O_RDWR | O_NONBLOCK);
 	litest_assert_errno_success(fd);
 
 	rc = libevdev_new_from_fd(fd, &d->evdev);
 	litest_assert_neg_errno_success(rc);
 
 	return d;
-
 }
 
 struct libinput *
-litest_create_context(void)
+litest_create_context_with_plugindir(const char *plugindir)
 {
 	struct libinput *libinput;
 	struct litest_context *ctx;
@@ -2130,23 +2144,51 @@ litest_create_context(void)
 	if (verbose)
 		libinput_log_set_priority(libinput, LIBINPUT_LOG_PRIORITY_DEBUG);
 
+	if (plugindir)
+		libinput_plugin_system_append_path(libinput, plugindir);
+
 	return libinput;
 }
 
+struct libinput *
+litest_create_context(void)
+{
+	struct libinput *li = litest_create_context_with_plugindir(NULL);
+
+	libinput_plugin_system_load_plugins(li, LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+
+	return li;
+}
+
 void
 litest_destroy_context(struct libinput *li)
 {
-	struct path *p;
-	struct litest_context *ctx;
-
-	ctx = libinput_get_user_data(li);
-	litest_assert_ptr_notnull(ctx);
-	libinput_unref(li);
+	if (li) {
+		_autofree_ struct litest_context *ctx = libinput_get_user_data(li);
+		litest_assert_ptr_notnull(ctx);
+		libinput_unref(li);
 
-	list_for_each_safe(p, &ctx->paths, link) {
-		litest_abort_msg("Device paths should be removed by now");
+		struct path *p;
+		list_for_each_safe(p, &ctx->paths, link) {
+			litest_abort_msg("Device paths should be removed by now");
+		}
 	}
-	free(ctx);
+}
+
+void
+litest_context_set_user_data(struct libinput *li, void *data)
+{
+	struct litest_user_data *litest_data = libinput_get_user_data(li);
+	litest_assert_ptr_notnull(litest_data);
+	litest_data->private = data;
+}
+
+void *
+litest_context_get_user_data(struct libinput *li)
+{
+	struct litest_user_data *litest_data = libinput_get_user_data(li);
+	litest_assert_ptr_notnull(litest_data);
+	return litest_data->private;
 }
 
 void
@@ -2170,12 +2212,17 @@ litest_bug_log_handler(struct libinput *
 		       const char *format,
 		       va_list args)
 {
-	if (strstr(format, "client bug: ") ||
-	    strstr(format, "libinput bug: ") ||
-	    strstr(format, "kernel bug: "))
+	if (pri != LIBINPUT_LOG_PRIORITY_ERROR || strstr(format, "client bug: ") ||
+	    strstr(format, "libinput bug: ") || strstr(format, "kernel bug: "))
 		return;
 
-	litest_abort_msg("Expected bug statement in log msg, aborting.");
+	/* messages from plugins don't have the string in the format, it's one of the
+	 * args... */
+	_autofree_ char *msg = strdup_vprintf(format, args);
+	if (strstr(msg, "plugin bug:"))
+		return;
+
+	litest_abort_msg("Expected bug statement in log msg ('%s'), aborting.", msg);
 }
 
 void
@@ -2192,7 +2239,6 @@ litest_add_device_with_overrides(struct
 				 const struct input_absinfo *abs_override,
 				 const int *events_override)
 {
-	struct udev_device *ud;
 	struct litest_device *d;
 	const char *path;
 
@@ -2208,9 +2254,8 @@ litest_add_device_with_overrides(struct
 	d->libinput = libinput;
 	d->libinput_device = libinput_path_add_device(d->libinput, path);
 	litest_assert_ptr_notnull(d->libinput_device);
-	ud = libinput_device_get_udev_device(d->libinput_device);
+	_unref_(udev_device) *ud = libinput_device_get_udev_device(d->libinput_device);
 	d->quirks = quirks_fetch_for_device(quirks_context, ud);
-	udev_device_unref(ud);
 
 	libinput_device_ref(d->libinput_device);
 
@@ -2221,16 +2266,20 @@ litest_add_device_with_overrides(struct
 		if (!libevdev_has_event_code(d->evdev, EV_ABS, code))
 			code = ABS_MT_POSITION_X;
 		if (libevdev_has_event_code(d->evdev, EV_ABS, code)) {
-			d->interface->min[ABS_X] = libevdev_get_abs_minimum(d->evdev, code);
-			d->interface->max[ABS_X] = libevdev_get_abs_maximum(d->evdev, code);
+			d->interface->min[ABS_X] =
+				libevdev_get_abs_minimum(d->evdev, code);
+			d->interface->max[ABS_X] =
+				libevdev_get_abs_maximum(d->evdev, code);
 		}
 
 		code = ABS_Y;
 		if (!libevdev_has_event_code(d->evdev, EV_ABS, code))
 			code = ABS_MT_POSITION_Y;
 		if (libevdev_has_event_code(d->evdev, EV_ABS, code)) {
-			d->interface->min[ABS_Y] = libevdev_get_abs_minimum(d->evdev, code);
-			d->interface->max[ABS_Y] = libevdev_get_abs_maximum(d->evdev, code);
+			d->interface->min[ABS_Y] =
+				libevdev_get_abs_minimum(d->evdev, code);
+			d->interface->max[ABS_Y] =
+				libevdev_get_abs_maximum(d->evdev, code);
 		}
 		d->interface->tool_type = BTN_TOOL_PEN;
 	}
@@ -2238,8 +2287,7 @@ litest_add_device_with_overrides(struct
 }
 
 struct litest_device *
-litest_add_device(struct libinput *libinput,
-		  enum litest_device_type which)
+litest_add_device(struct libinput *libinput, enum litest_device_type which)
 {
 	return litest_add_device_with_overrides(libinput,
 						which,
@@ -2276,25 +2324,21 @@ litest_create_device(enum litest_device_
 static struct udev_monitor *
 udev_setup_monitor(void)
 {
-	struct udev *udev;
-	struct udev_monitor *udev_monitor;
+	_unref_(udev) *udev = udev_new();
+	_unref_(udev_monitor) *udev_monitor = NULL;
 	int rc;
 
-	udev = udev_new();
 	litest_assert_notnull(udev);
 	udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
 	litest_assert_notnull(udev_monitor);
-	udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "input",
-							NULL);
+	udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "input", NULL);
 
 	/* remove O_NONBLOCK */
 	rc = fcntl(udev_monitor_get_fd(udev_monitor), F_SETFL, 0);
 	litest_assert_errno_success(rc);
-	litest_assert_int_eq(udev_monitor_enable_receiving(udev_monitor),
-			     0);
-	udev_unref(udev);
+	litest_assert_int_eq(udev_monitor_enable_receiving(udev_monitor), 0);
 
-	return udev_monitor;
+	return steal(&udev_monitor);
 }
 
 static struct udev_device *
@@ -2302,44 +2346,47 @@ udev_wait_for_device_event(struct udev_m
 			   const char *udev_event,
 			   const char *syspath)
 {
-	struct udev_device *udev_device = NULL;
-
 	/* blocking, we don't want to continue until udev is ready */
 	while (1) {
+		_unref_(udev_device) *udev_device = NULL;
 		const char *udev_syspath = NULL;
 		const char *udev_action;
 
 		udev_device = udev_monitor_receive_device(udev_monitor);
-		litest_assert_notnull(udev_device);
+		if (!udev_device) {
+			if (errno == EAGAIN)
+				continue;
+
+			litest_abort_msg(
+				"Failed to receive udev device from monitor: %s (%d)",
+				strerror(errno),
+				errno);
+		}
 		udev_action = udev_device_get_action(udev_device);
 		if (!udev_action || !streq(udev_action, udev_event)) {
-			udev_device_unref(udev_device);
 			continue;
 		}
 
 		udev_syspath = udev_device_get_syspath(udev_device);
 		if (strstartswith(udev_syspath, syspath))
-			break;
-
-		udev_device_unref(udev_device);
+			return steal(&udev_device);
 	}
-
-	return udev_device;
 }
 
 void
-litest_delete_device(struct litest_device *d)
+litest_device_destroy(struct litest_device *d)
 {
 
-	struct udev_monitor *udev_monitor;
-	struct udev_device *udev_device;
+	_unref_(udev_monitor) *udev_monitor = NULL;
+	_unref_(udev_device) *udev_device = NULL;
 	char path[PATH_MAX];
 
 	if (!d)
 		return;
 
 	udev_monitor = udev_setup_monitor();
-	snprintf(path, sizeof(path),
+	snprintf(path,
+		 sizeof(path),
 		 "%s/event",
 		 libevdev_uinput_get_syspath(d->uinput));
 
@@ -2359,30 +2406,70 @@ litest_delete_device(struct litest_devic
 	libevdev_free(d->evdev);
 	libevdev_uinput_destroy(d->uinput);
 	free(d->private);
-	memset(d,0, sizeof(*d));
+	memset(d, 0, sizeof(*d));
 	free(d);
 
-	udev_device = udev_wait_for_device_event(udev_monitor,
-						 "remove",
-						 path);
-	udev_device_unref(udev_device);
-	udev_monitor_unref(udev_monitor);
+	udev_device = // NOLINT: deadcode.DeadStores
+		udev_wait_for_device_event(udev_monitor, "remove", path);
 }
 
 void
-litest_event(struct litest_device *d, unsigned int type,
-	     unsigned int code, int value)
+litest_event_unchecked(struct litest_device *d,
+		       unsigned int type,
+		       unsigned int code,
+		       int value)
 {
-	int ret;
+	int ret = libevdev_uinput_write_event(d->uinput, type, code, value);
+	litest_assert_neg_errno_success(ret);
+}
 
+void
+litest_event(struct litest_device *d, unsigned int type, unsigned int code, int value)
+{
 	if (!libevdev_has_event_code(d->evdev, type, code))
 		return;
 
-	if (d->skip_ev_syn && type == EV_SYN && code == SYN_REPORT)
-		return;
+	if (type == EV_SYN && code == SYN_REPORT) {
+		if (d->skip_ev_syn)
+			return;
 
-	ret = libevdev_uinput_write_event(d->uinput, type, code, value);
-	litest_assert_neg_errno_success(ret);
+		for (size_t i = 0; i < d->frame.nevents; i++) {
+			struct input_event *e = &d->frame.events[i];
+			int ret = libevdev_uinput_write_event(d->uinput,
+							      e->type,
+							      e->code,
+							      e->value);
+			litest_assert_neg_errno_success(ret);
+		}
+
+		int ret = libevdev_uinput_write_event(d->uinput,
+						      EV_SYN,
+						      SYN_REPORT,
+						      value);
+		litest_assert_neg_errno_success(ret);
+
+		d->frame.nevents = 0;
+	} else {
+		size_t i;
+
+		if (type == EV_SYN || (type == EV_ABS && code >= ABS_MT_SLOT)) {
+			i = d->frame.nevents;
+		} else {
+			for (i = 0; i < d->frame.nevents; i++) {
+				if (d->frame.events[i].type == type &&
+				    d->frame.events[i].code == code)
+					break;
+			}
+		}
+		litest_assert_int_lt(i, ARRAY_LENGTH(d->frame.events));
+		d->frame.events[i] = (struct input_event){
+			.type = type,
+			.code = code,
+			.value = value,
+		};
+		if (i >= d->frame.nevents)
+			d->frame.nevents++;
+	}
 }
 
 static bool
@@ -2419,7 +2506,9 @@ axis_replacement_value(struct litest_dev
 int
 litest_auto_assign_value(struct litest_device *d,
 			 const struct input_event *ev,
-			 int slot, double x, double y,
+			 int slot,
+			 double x,
+			 double y,
 			 struct axis_replacement *axes,
 			 bool touching)
 {
@@ -2454,13 +2543,12 @@ litest_auto_assign_value(struct litest_d
 	default:
 		if (!axis_replacement_value(d, axes, ev->code, &value) &&
 		    d->interface->get_axis_default) {
-			int error = d->interface->get_axis_default(d,
-								   ev->code,
-								   &value);
+			int error = d->interface->get_axis_default(d, ev->code, &value);
 			if (error) {
-				litest_abort_msg("Failed to get default axis value for %s (%d)",
-						 libevdev_event_code_get_name(EV_ABS, ev->code),
-						 ev->code);
+				litest_abort_msg(
+					"Failed to get default axis value for %s (%d)",
+					libevdev_event_code_get_name(EV_ABS, ev->code),
+					ev->code);
 			}
 		}
 		break;
@@ -2498,20 +2586,13 @@ slot_start(struct litest_device *d,
 
 	/* If the test device overrides touch_down and says it didn't
 	 * handle the event, let's continue normally */
-	if (d->interface->touch_down &&
-	    d->interface->touch_down(d, slot, x, y))
-	    return;
+	if (d->interface->touch_down && d->interface->touch_down(d, slot, x, y))
+		return;
 
 	for (ev = d->interface->touch_down_events;
 	     ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1;
 	     ev++) {
-		int value = litest_auto_assign_value(d,
-						     ev,
-						     slot,
-						     x,
-						     y,
-						     axes,
-						     touching);
+		int value = litest_auto_assign_value(d, ev, slot, x, y, axes, touching);
 		if (value == LITEST_AUTO_ASSIGN)
 			continue;
 
@@ -2534,20 +2615,13 @@ slot_move(struct litest_device *d,
 {
 	struct input_event *ev;
 
-	if (d->interface->touch_move &&
-	    d->interface->touch_move(d, slot, x, y))
+	if (d->interface->touch_move && d->interface->touch_move(d, slot, x, y))
 		return;
 
 	for (ev = d->interface->touch_move_events;
 	     ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1;
 	     ev++) {
-		int value = litest_auto_assign_value(d,
-						     ev,
-						     slot,
-						     x,
-						     y,
-						     axes,
-						     touching);
+		int value = litest_auto_assign_value(d, ev, slot, x, y, axes, touching);
 		if (value == LITEST_AUTO_ASSIGN)
 			continue;
 
@@ -2578,24 +2652,15 @@ touch_up(struct litest_device *d, unsign
 
 	send_btntool(d, false);
 
-	if (d->interface->touch_up &&
-	    d->interface->touch_up(d, slot)) {
+	if (d->interface->touch_up && d->interface->touch_up(d, slot)) {
 		return;
 	} else if (d->interface->touch_up_events) {
 		ev = d->interface->touch_up_events;
 	} else
 		ev = up;
 
-	for ( /* */;
-	     ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1;
-	     ev++) {
-		int value = litest_auto_assign_value(d,
-						     ev,
-						     slot,
-						     0,
-						     0,
-						     NULL,
-						     false);
+	for (/* */; ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1; ev++) {
+		int value = litest_auto_assign_value(d, ev, slot, 0, 0, NULL, false);
 		litest_event(d, ev->type, ev->code, value);
 	}
 }
@@ -2632,19 +2697,18 @@ litest_slot_start(struct litest_device *
 		b = max(y, d->semi_mt.touches[other].y);
 	}
 
-	litest_push_event_frame(d);
-	if (d->ntouches_down == 0)
-		slot_start(d, 0, l, t, axes, touching, filter_abs_xy);
-	else
-		slot_move(d, 0, l, t, axes, touching, filter_abs_xy);
+	litest_with_event_frame(d) {
+		if (d->ntouches_down == 0)
+			slot_start(d, 0, l, t, axes, touching, filter_abs_xy);
+		else
+			slot_move(d, 0, l, t, axes, touching, filter_abs_xy);
 
-	if (slot == 1) {
-		filter_abs_xy = true;
-		slot_start(d, 1, r, b, axes, touching, filter_abs_xy);
+		if (slot == 1) {
+			filter_abs_xy = true;
+			slot_start(d, 1, r, b, axes, touching, filter_abs_xy);
+		}
 	}
 
-	litest_pop_event_frame(d);
-
 	d->semi_mt.touches[slot].x = x;
 	d->semi_mt.touches[slot].y = y;
 }
@@ -2659,18 +2723,12 @@ litest_touch_sequence(struct litest_devi
 		      int steps)
 {
 	litest_touch_down(d, slot, x_from, y_from);
-	litest_touch_move_to(d, slot,
-			     x_from, y_from,
-			     x_to, y_to,
-			     steps);
+	litest_touch_move_to(d, slot, x_from, y_from, x_to, y_to, steps);
 	litest_touch_up(d, slot);
 }
 
 void
-litest_touch_down(struct litest_device *d,
-		  unsigned int slot,
-		  double x,
-		  double y)
+litest_touch_down(struct litest_device *d, unsigned int slot, double x, double y)
 {
 	litest_slot_start(d, slot, x, y, NULL, true);
 }
@@ -2715,16 +2773,15 @@ litest_slot_move(struct litest_device *d
 		b = max(y, d->semi_mt.touches[other].y);
 	}
 
-	litest_push_event_frame(d);
-	slot_move(d, 0, l, t, axes, touching, filter_abs_xy);
+	litest_with_event_frame(d) {
+		slot_move(d, 0, l, t, axes, touching, filter_abs_xy);
 
-	if (d->ntouches_down == 2) {
-		filter_abs_xy = true;
-		slot_move(d, 1, r, b, axes, touching, filter_abs_xy);
+		if (d->ntouches_down == 2) {
+			filter_abs_xy = true;
+			slot_move(d, 1, r, b, axes, touching, filter_abs_xy);
+		}
 	}
 
-	litest_pop_event_frame(d);
-
 	d->semi_mt.touches[slot].x = x;
 	d->semi_mt.touches[slot].y = y;
 }
@@ -2740,33 +2797,29 @@ litest_touch_up(struct litest_device *d,
 	if (d->ntouches_down > 2 || slot > 1)
 		return;
 
-	litest_push_event_frame(d);
-	touch_up(d, d->ntouches_down - 1);
+	litest_with_event_frame(d) {
+		touch_up(d, d->ntouches_down - 1);
 
-	/* if we have one finger left, send x/y coords for that finger left.
-	   this is likely to happen with a real touchpad */
-	if (d->ntouches_down == 1) {
-		bool touching = true;
-		bool filter_abs_xy = false;
+		/* if we have one finger left, send x/y coords for that finger left.
+		this is likely to happen with a real touchpad */
+		if (d->ntouches_down == 1) {
+			bool touching = true;
+			bool filter_abs_xy = false;
 
-		int other = (slot + 1) % 2;
-		slot_move(d,
-			  0,
-			  d->semi_mt.touches[other].x,
-			  d->semi_mt.touches[other].y,
-			  NULL,
-			  touching,
-			  filter_abs_xy);
+			int other = (slot + 1) % 2;
+			slot_move(d,
+				  0,
+				  d->semi_mt.touches[other].x,
+				  d->semi_mt.touches[other].y,
+				  NULL,
+				  touching,
+				  filter_abs_xy);
+		}
 	}
-
-	litest_pop_event_frame(d);
 }
 
 void
-litest_touch_move(struct litest_device *d,
-		  unsigned int slot,
-		  double x,
-		  double y)
+litest_touch_move(struct litest_device *d, unsigned int slot, double x, double y)
 {
 	litest_slot_move(d, slot, x, y, NULL, true);
 }
@@ -2784,31 +2837,32 @@ litest_touch_move_extended(struct litest
 void
 litest_touch_move_to(struct litest_device *d,
 		     unsigned int slot,
-		     double x_from, double y_from,
-		     double x_to, double y_to,
+		     double x_from,
+		     double y_from,
+		     double x_to,
+		     double y_to,
 		     int steps)
 {
-	litest_touch_move_to_extended(d, slot,
-				      x_from, y_from,
-				      x_to, y_to,
-				      NULL,
-				      steps);
+	litest_touch_move_to_extended(d, slot, x_from, y_from, x_to, y_to, NULL, steps);
 }
 
 void
 litest_touch_move_to_extended(struct litest_device *d,
 			      unsigned int slot,
-			      double x_from, double y_from,
-			      double x_to, double y_to,
+			      double x_from,
+			      double y_from,
+			      double x_to,
+			      double y_to,
 			      struct axis_replacement *axes,
 			      int steps)
 {
 	int sleep_ms = 10;
 
 	for (int i = 1; i < steps; i++) {
-		litest_touch_move_extended(d, slot,
-					   x_from + (x_to - x_from)/steps * i,
-					   y_from + (y_to - y_from)/steps * i,
+		litest_touch_move_extended(d,
+					   slot,
+					   x_from + (x_to - x_from) / steps * i,
+					   y_from + (y_to - y_from) / steps * i,
 					   axes);
 		libinput_dispatch(d->libinput);
 		msleep(sleep_ms);
@@ -2820,7 +2874,8 @@ litest_touch_move_to_extended(struct lit
 static int
 auto_assign_tablet_value(struct litest_device *d,
 			 const struct input_event *ev,
-			 int x, int y,
+			 int x,
+			 int y,
 			 struct axis_replacement *axes)
 {
 	static int tracking_id;
@@ -2846,9 +2901,10 @@ auto_assign_tablet_value(struct litest_d
 		    d->interface->get_axis_default) {
 			int error = d->interface->get_axis_default(d, ev->code, &value);
 			if (error) {
-				litest_abort_msg("Failed to get default axis value for %s (%d)",
-						 libevdev_event_code_get_name(EV_ABS, ev->code),
-						 ev->code);
+				litest_abort_msg(
+					"Failed to get default axis value for %s (%d)",
+					libevdev_event_code_get_name(EV_ABS, ev->code),
+					ev->code);
 			}
 		}
 		break;
@@ -2892,7 +2948,8 @@ litest_tool_event(struct litest_device *
 
 void
 litest_tablet_proximity_in(struct litest_device *d,
-			   double x, double y,
+			   double x,
+			   double y,
 			   struct axis_replacement *axes)
 {
 	struct input_event *ev;
@@ -2951,15 +3008,15 @@ litest_tablet_proximity_out(struct lites
 
 void
 litest_tablet_motion(struct litest_device *d,
-		     double x, double y,
+		     double x,
+		     double y,
 		     struct axis_replacement *axes)
 {
 	struct input_event *ev;
 
 	/* If the test device overrides proximity_out and says it didn't
 	 * handle the event, let's continue normally */
-	if (d->interface->tablet_motion &&
-	    d->interface->tablet_motion(d, &x, &y, axes))
+	if (d->interface->tablet_motion && d->interface->tablet_motion(d, &x, &y, axes))
 		return;
 
 	ev = d->interface->tablet_motion_events;
@@ -2973,7 +3030,8 @@ litest_tablet_motion(struct litest_devic
 
 void
 litest_tablet_tip_down(struct litest_device *d,
-		       double x, double y,
+		       double x,
+		       double y,
 		       struct axis_replacement *axes)
 {
 	/* If the test device overrides tip_down and says it didn't
@@ -2988,13 +3046,13 @@ litest_tablet_tip_down(struct litest_dev
 
 void
 litest_tablet_tip_up(struct litest_device *d,
-		     double x, double y,
+		     double x,
+		     double y,
 		     struct axis_replacement *axes)
 {
 	/* If the test device overrides tip_down and says it didn't
 	 * handle the event, let's continue normally */
-	if (d->interface->tablet_tip_up &&
-	    d->interface->tablet_tip_up(d, &x, &y, axes))
+	if (d->interface->tablet_tip_up && d->interface->tablet_tip_up(d, &x, &y, axes))
 		return;
 
 	litest_event(d, EV_KEY, BTN_TOUCH, 0);
@@ -3003,36 +3061,47 @@ litest_tablet_tip_up(struct litest_devic
 
 void
 litest_touch_move_two_touches(struct litest_device *d,
-			      double x0, double y0,
-			      double x1, double y1,
-			      double dx, double dy,
+			      double x0,
+			      double y0,
+			      double x1,
+			      double y1,
+			      double dx,
+			      double dy,
 			      int steps)
 {
 	int sleep_ms = 10;
 
 	for (int i = 1; i < steps; i++) {
-		litest_push_event_frame(d);
-		litest_touch_move(d, 0, x0 + dx / steps * i,
-					y0 + dy / steps * i);
-		litest_touch_move(d, 1, x1 + dx / steps * i,
-					y1 + dy / steps * i);
-		litest_pop_event_frame(d);
+		litest_with_event_frame(d) {
+			litest_touch_move(d,
+					  0,
+					  x0 + dx / steps * i,
+					  y0 + dy / steps * i);
+			litest_touch_move(d,
+					  1,
+					  x1 + dx / steps * i,
+					  y1 + dy / steps * i);
+		}
 		libinput_dispatch(d->libinput);
 		msleep(sleep_ms);
 		libinput_dispatch(d->libinput);
 	}
-	litest_push_event_frame(d);
-	litest_touch_move(d, 0, x0 + dx, y0 + dy);
-	litest_touch_move(d, 1, x1 + dx, y1 + dy);
-	litest_pop_event_frame(d);
+	litest_with_event_frame(d) {
+		litest_touch_move(d, 0, x0 + dx, y0 + dy);
+		litest_touch_move(d, 1, x1 + dx, y1 + dy);
+	}
 }
 
 void
 litest_touch_move_three_touches(struct litest_device *d,
-				double x0, double y0,
-				double x1, double y1,
-				double x2, double y2,
-				double dx, double dy,
+				double x0,
+				double y0,
+				double x1,
+				double y1,
+				double x2,
+				double y2,
+				double dx,
+				double dy,
 				int steps)
 {
 	int sleep_ms = 10;
@@ -3041,11 +3110,11 @@ litest_touch_move_three_touches(struct l
 		double step_x = dx / steps * i;
 		double step_y = dy / steps * i;
 
-		litest_push_event_frame(d);
-		litest_touch_move(d, 0, x0 + step_x, y0 + step_y);
-		litest_touch_move(d, 1, x1 + step_x, y1 + step_y);
-		litest_touch_move(d, 2, x2 + step_x, y2 + step_y);
-		litest_pop_event_frame(d);
+		litest_with_event_frame(d) {
+			litest_touch_move(d, 0, x0 + step_x, y0 + step_y);
+			litest_touch_move(d, 1, x1 + step_x, y1 + step_y);
+			litest_touch_move(d, 2, x2 + step_x, y2 + step_y);
+		}
 
 		libinput_dispatch(d->libinput);
 		msleep(sleep_ms);
@@ -3054,15 +3123,12 @@ litest_touch_move_three_touches(struct l
 }
 
 void
-litest_hover_start(struct litest_device *d,
-		   unsigned int slot,
-		   double x,
-		   double y)
+litest_hover_start(struct litest_device *d, unsigned int slot, double x, double y)
 {
 	struct axis_replacement axes[] = {
-		{ABS_MT_PRESSURE, 0 },
-		{ABS_PRESSURE, 0 },
-		{-1, -1 },
+		{ ABS_MT_PRESSURE, 0 },
+		{ ABS_PRESSURE, 0 },
+		{ -1, -1 },
 	};
 
 	litest_slot_start(d, slot, x, y, axes, 0);
@@ -3101,13 +3167,12 @@ litest_hover_end(struct litest_device *d
 }
 
 void
-litest_hover_move(struct litest_device *d, unsigned int slot,
-		  double x, double y)
+litest_hover_move(struct litest_device *d, unsigned int slot, double x, double y)
 {
 	struct axis_replacement axes[] = {
-		{ABS_MT_PRESSURE, 0 },
-		{ABS_PRESSURE, 0 },
-		{-1, -1 },
+		{ ABS_MT_PRESSURE, 0 },
+		{ ABS_PRESSURE, 0 },
+		{ -1, -1 },
 	};
 
 	litest_slot_move(d, slot, x, y, axes, false);
@@ -3116,16 +3181,19 @@ litest_hover_move(struct litest_device *
 void
 litest_hover_move_to(struct litest_device *d,
 		     unsigned int slot,
-		     double x_from, double y_from,
-		     double x_to, double y_to,
+		     double x_from,
+		     double y_from,
+		     double x_to,
+		     double y_to,
 		     int steps)
 {
 	int sleep_ms = 10;
 
 	for (int i = 0; i < steps - 1; i++) {
-		litest_hover_move(d, slot,
-				  x_from + (x_to - x_from)/steps * i,
-				  y_from + (y_to - y_from)/steps * i);
+		litest_hover_move(d,
+				  slot,
+				  x_from + (x_to - x_from) / steps * i,
+				  y_from + (y_to - y_from) / steps * i);
 		libinput_dispatch(d->libinput);
 		msleep(sleep_ms);
 		libinput_dispatch(d->libinput);
@@ -3135,34 +3203,39 @@ litest_hover_move_to(struct litest_devic
 
 void
 litest_hover_move_two_touches(struct litest_device *d,
-			      double x0, double y0,
-			      double x1, double y1,
-			      double dx, double dy,
+			      double x0,
+			      double y0,
+			      double x1,
+			      double y1,
+			      double dx,
+			      double dy,
 			      int steps)
 {
 	int sleep_ms = 10;
 
 	for (int i = 0; i < steps - 1; i++) {
-		litest_push_event_frame(d);
-		litest_hover_move(d, 0, x0 + dx / steps * i,
-					y0 + dy / steps * i);
-		litest_hover_move(d, 1, x1 + dx / steps * i,
-					y1 + dy / steps * i);
-		litest_pop_event_frame(d);
+		litest_with_event_frame(d) {
+			litest_hover_move(d,
+					  0,
+					  x0 + dx / steps * i,
+					  y0 + dy / steps * i);
+			litest_hover_move(d,
+					  1,
+					  x1 + dx / steps * i,
+					  y1 + dy / steps * i);
+		}
 		libinput_dispatch(d->libinput);
 		msleep(sleep_ms);
 		libinput_dispatch(d->libinput);
 	}
-	litest_push_event_frame(d);
-	litest_hover_move(d, 0, x0 + dx, y0 + dy);
-	litest_hover_move(d, 1, x1 + dx, y1 + dy);
-	litest_pop_event_frame(d);
+	litest_with_event_frame(d) {
+		litest_hover_move(d, 0, x0 + dx, y0 + dy);
+		litest_hover_move(d, 1, x1 + dx, y1 + dy);
+	}
 }
 
 void
-litest_button_click(struct litest_device *d,
-		    unsigned int button,
-		    bool is_press)
+litest_button_click(struct litest_device *d, unsigned int button, bool is_press)
 {
 	struct input_event click[] = {
 		{ .type = EV_KEY, .code = button, .value = is_press ? 1 : 0 },
@@ -3180,24 +3253,20 @@ litest_button_click_debounced(struct lit
 			      bool is_press)
 {
 	litest_button_click(d, button, is_press);
-
-	libinput_dispatch(li);
-	litest_timeout_debounce();
-	libinput_dispatch(li);
+	litest_timeout_debounce(li);
 }
 
 void
 litest_button_scroll(struct litest_device *dev,
 		     unsigned int button,
-		     double dx, double dy)
+		     double dx,
+		     double dy)
 {
 	struct libinput *li = dev->libinput;
 
 	litest_button_click_debounced(dev, li, button, 1);
 
-	libinput_dispatch(li);
-	litest_timeout_buttonscroll();
-	libinput_dispatch(li);
+	litest_timeout_buttonscroll(li);
 
 	litest_event(dev, EV_REL, REL_X, dx);
 	litest_event(dev, EV_REL, REL_Y, dy);
@@ -3211,16 +3280,15 @@ litest_button_scroll(struct litest_devic
 void
 litest_button_scroll_locked(struct litest_device *dev,
 			    unsigned int button,
-			    double dx, double dy)
+			    double dx,
+			    double dy)
 {
 	struct libinput *li = dev->libinput;
 
 	litest_button_click_debounced(dev, li, button, 1);
 	litest_button_click_debounced(dev, li, button, 0);
 
-	libinput_dispatch(li);
-	litest_timeout_buttonscroll();
-	libinput_dispatch(li);
+	litest_timeout_buttonscroll(li);
 
 	litest_event(dev, EV_REL, REL_X, dx);
 	litest_event(dev, EV_REL, REL_Y, dy);
@@ -3264,23 +3332,20 @@ litest_switch_action(struct litest_devic
 }
 
 static int
-litest_scale_axis(const struct litest_device *d,
-		  unsigned int axis,
-		  double val)
+litest_scale_axis(const struct litest_device *d, unsigned int axis, double val)
 {
 	const struct input_absinfo *abs;
 
 	litest_assert_double_ge(val, 0.0);
 	/* major/minor must be able to beyond 100% for large fingers */
-	if (axis != ABS_MT_TOUCH_MAJOR &&
-	    axis != ABS_MT_TOUCH_MINOR) {
+	if (axis != ABS_MT_TOUCH_MAJOR && axis != ABS_MT_TOUCH_MINOR) {
 		litest_assert_double_le(val, 100.0);
 	}
 
 	abs = libevdev_get_abs_info(d->evdev, axis);
 	litest_assert_notnull(abs);
 
-	return (abs->maximum - abs->minimum) * val/100.0 + abs->minimum;
+	return (abs->maximum - abs->minimum) * val / 100.0 + abs->minimum;
 }
 
 static inline int
@@ -3289,7 +3354,7 @@ litest_scale_range(int min, int max, dou
 	litest_assert_int_ge((int)val, 0);
 	litest_assert_int_le((int)val, 100);
 
-	return (max - min) * val/100.0 + min;
+	return (max - min) * val / 100.0 + min;
 }
 
 int
@@ -3299,8 +3364,7 @@ litest_scale(const struct litest_device
 
 	litest_assert_double_ge(val, 0.0);
 	/* major/minor must be able to beyond 100% for large fingers */
-	if (axis != ABS_MT_TOUCH_MAJOR &&
-	    axis != ABS_MT_TOUCH_MINOR)
+	if (axis != ABS_MT_TOUCH_MAJOR && axis != ABS_MT_TOUCH_MINOR)
 		litest_assert_double_le(val, 100.0);
 
 	if (axis <= ABS_Y) {
@@ -3314,9 +3378,7 @@ litest_scale(const struct litest_device
 }
 
 static inline int
-auto_assign_pad_value(struct litest_device *dev,
-		      struct input_event *ev,
-		      double value)
+auto_assign_pad_value(struct litest_device *dev, struct input_event *ev, double value)
 {
 	const struct input_absinfo *abs;
 
@@ -3328,8 +3390,9 @@ auto_assign_pad_value(struct litest_devi
 		case REL_WHEEL:
 		case REL_HWHEEL:
 		case REL_DIAL:
-			assert (fmod(value, 120.0) == 0.0); /* Fractions not supported yet */
-			return value/120.0;
+			assert(fmod(value, 120.0) ==
+			       0.0); /* Fractions not supported yet */
+			return value / 120.0;
 		default:
 			return value;
 		}
@@ -3357,30 +3420,28 @@ auto_assign_pad_value(struct litest_devi
 	}
 }
 
+static void
+litest_pad_events(struct litest_device *d, struct input_event *evs, double value)
+{
+	while (evs && (int16_t)evs->type != -1 && (int16_t)evs->code != -1) {
+		if (evs->type == EV_SYN && evs->code == SYN_REPORT)
+			value = 0;
+		value = auto_assign_pad_value(d, evs, value);
+		litest_event(d, evs->type, evs->code, value);
+		evs++;
+	}
+}
+
 void
 litest_pad_ring_start(struct litest_device *d, double value)
 {
-	struct input_event *ev;
-
-	ev = d->interface->pad_ring_start_events;
-	while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
-		value = auto_assign_pad_value(d, ev, value);
-		litest_event(d, ev->type, ev->code, value);
-		ev++;
-	}
+	litest_pad_events(d, d->interface->pad_ring_start_events, value);
 }
 
 void
 litest_pad_ring_change(struct litest_device *d, double value)
 {
-	struct input_event *ev;
-
-	ev = d->interface->pad_ring_change_events;
-	while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
-		value = auto_assign_pad_value(d, ev, value);
-		litest_event(d, ev->type, ev->code, value);
-		ev++;
-	}
+	litest_pad_events(d, d->interface->pad_ring_change_events, value);
 }
 
 void
@@ -3398,27 +3459,13 @@ litest_pad_ring_end(struct litest_device
 void
 litest_pad_strip_start(struct litest_device *d, double value)
 {
-	struct input_event *ev;
-
-	ev = d->interface->pad_strip_start_events;
-	while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
-		value = auto_assign_pad_value(d, ev, value);
-		litest_event(d, ev->type, ev->code, value);
-		ev++;
-	}
+	litest_pad_events(d, d->interface->pad_strip_start_events, value);
 }
 
 void
 litest_pad_strip_change(struct litest_device *d, double value)
 {
-	struct input_event *ev;
-
-	ev = d->interface->pad_strip_change_events;
-	while (ev && (int16_t)ev->type != -1 && (int16_t)ev->code != -1) {
-		value = auto_assign_pad_value(d, ev, value);
-		litest_event(d, ev->type, ev->code, value);
-		ev++;
-	}
+	litest_pad_events(d, d->interface->pad_strip_change_events, value);
 }
 
 void
@@ -3440,13 +3487,10 @@ litest_wait_for_event(struct libinput *l
 }
 
 void
-_litest_wait_for_event_of_type(struct libinput *li,
-			       const char *func,
-			       int lineno,
-			       ...)
+_litest_wait_for_event_of_type(struct libinput *li, const char *func, int lineno, ...)
 {
 	va_list args;
-	enum libinput_event_type types[32] = {LIBINPUT_EVENT_NONE};
+	enum libinput_event_type types[32] = { LIBINPUT_EVENT_NONE };
 	size_t ntypes = 0;
 	enum libinput_event_type type;
 	struct pollfd fds;
@@ -3465,15 +3509,34 @@ _litest_wait_for_event_of_type(struct li
 	fds.events = POLLIN;
 	fds.revents = 0;
 
+	const int timeout = 2000;
+	uint64_t expiry = 0;
+	int rc = now_in_us(&expiry);
+	expiry += ms2us(timeout);
+	litest_assert_errno_success(rc);
+
 	while (1) {
 		size_t i;
-		struct libinput_event *event;
+		enum libinput_event_type type;
 
 		while ((type = libinput_next_event_type(li)) == LIBINPUT_EVENT_NONE) {
-			int rc = poll(&fds, 1, 2000);
+			int rc = poll(&fds, 1, timeout);
 			litest_assert_errno_success(rc);
 			litest_assert_int_gt(rc, 0);
-			libinput_dispatch(li);
+			litest_dispatch(li);
+		}
+
+		if (type == LIBINPUT_EVENT_NONE) {
+			uint64_t now;
+			now_in_us(&now);
+			if (now > expiry) {
+				_litest_abort_msg(
+					NULL,
+					lineno,
+					func,
+					"Waited >%dms for events, but no events are pending",
+					timeout);
+			}
 		}
 
 		/* no event mask means wait for any event */
@@ -3485,34 +3548,34 @@ _litest_wait_for_event_of_type(struct li
 				return;
 		}
 
-		event = libinput_get_event(li);
+		_destroy_(libinput_event) *event = libinput_get_event(li);
 		if (verbose) {
 			litest_print_event(event, "Discarding event while waiting: ");
 		}
-		libinput_event_destroy(event);
 	}
 }
 
 void
 litest_drain_events(struct libinput *li)
 {
-	struct libinput_event *event;
+	do {
+		libinput_dispatch(li);
+
+		_destroy_(libinput_event) *event = libinput_get_event(li);
+		if (!event)
+			break;
 
-	libinput_dispatch(li);
-	while ((event = libinput_get_event(li))) {
 		if (verbose) {
 			litest_print_event(event, "litest: draining event: ");
 		}
-		libinput_event_destroy(event);
-		libinput_dispatch(li);
-	}
+	} while (true);
 }
 
 void
 _litest_drain_events_of_type(struct libinput *li, ...)
 {
 	enum libinput_event_type type;
-	enum libinput_event_type types[32] = {LIBINPUT_EVENT_NONE};
+	enum libinput_event_type types[32] = { LIBINPUT_EVENT_NONE };
 	size_t ntypes = 0;
 	va_list args;
 
@@ -3529,7 +3592,6 @@ _litest_drain_events_of_type(struct libi
 	libinput_dispatch(li);
 	type = libinput_next_event_type(li);
 	while (type != LIBINPUT_EVENT_NONE) {
-		struct libinput_event *event;
 		bool found = false;
 
 		type = libinput_next_event_type(li);
@@ -3543,8 +3605,9 @@ _litest_drain_events_of_type(struct libi
 		if (!found)
 			return;
 
-		event = libinput_get_event(li);
-		libinput_event_destroy(event);
+		_destroy_(libinput_event) *event = libinput_get_event(li);
+		if (verbose)
+			litest_print_event(event, "litest: draining typed event: ");
 		libinput_dispatch(li);
 	}
 }
@@ -3669,9 +3732,8 @@ litest_event_get_type_str(struct libinpu
 static void
 litest_print_event(struct libinput_event *event, const char *message)
 {
-	char *event_str = libinput_event_to_str(event, 0, NULL);
+	_autofree_ char *event_str = libinput_event_to_str(event, 0, NULL);
 	fprintf(stderr, "litest: %s %s\n", message, event_str);
-	free(event_str);
 }
 
 void
@@ -3759,15 +3821,13 @@ _litest_assert_event_type_not_one_of(str
 		litest_event_get_type_str(event),
 		libinput_event_get_type(event));
 
-	litest_print_event(event,"\nWrong event is: ");
+	litest_print_event(event, "\nWrong event is: ");
 	litest_backtrace(func);
 	litest_runner_abort();
 }
 
 void
-_litest_assert_empty_queue(struct libinput *li,
-			   const char *func,
-			   int line)
+_litest_assert_empty_queue(struct libinput *li, const char *func, int line)
 {
 	bool empty_queue = true;
 	struct libinput_event *event;
@@ -3792,30 +3852,26 @@ litest_create_uinput(const char *name,
 		     const int *events)
 {
 	struct libevdev_uinput *uinput;
-	struct libevdev *dev;
+	_free_(libevdev) *dev = libevdev_new();
 	int type, code;
 	int rc;
 	const struct input_absinfo *abs;
-	const struct input_absinfo default_abs = {
-		.value = 0,
-		.minimum = 0,
-		.maximum = 100,
-		.fuzz = 0,
-		.flat = 0,
-		.resolution = 100
-	};
-	/* See kernel commit 206f533a0a7c ("Input: uinput - reject requests with unreasonable number of slots") */
-	const struct input_absinfo default_abs_mt_slot = {
-		.value = 0,
-		.minimum = 0,
-		.maximum = 64,
-		.fuzz = 0,
-		.flat = 0,
-		.resolution = 100
-	};
+	const struct input_absinfo default_abs = { .value = 0,
+						   .minimum = 0,
+						   .maximum = 100,
+						   .fuzz = 0,
+						   .flat = 0,
+						   .resolution = 100 };
+	/* See kernel commit 206f533a0a7c ("Input: uinput - reject requests with
+	 * unreasonable number of slots") */
+	const struct input_absinfo default_abs_mt_slot = { .value = 0,
+							   .minimum = 0,
+							   .maximum = 64,
+							   .fuzz = 0,
+							   .flat = 0,
+							   .resolution = 100 };
 	char buf[512];
 
-	dev = libevdev_new();
 	litest_assert_ptr_notnull(dev);
 
 	snprintf(buf, sizeof(buf), "litest %s", name);
@@ -3839,26 +3895,27 @@ litest_create_uinput(const char *name,
 		abs++;
 	}
 
-	while (events &&
-	       (type = *events++) != -1 &&
-	       (code = *events++) != -1) {
+	while (events && (type = *events++) != -1 && (code = *events++) != -1) {
 		if (type == INPUT_PROP_MAX) {
 			rc = libevdev_enable_property(dev, code);
 		} else {
-			const struct input_absinfo *abs =
-				(code == ABS_MT_SLOT) ? &default_abs_mt_slot : &default_abs;
-			rc = libevdev_enable_event_code(dev, type, code,
+			const struct input_absinfo *abs = (code == ABS_MT_SLOT)
+								  ? &default_abs_mt_slot
+								  : &default_abs;
+			rc = libevdev_enable_event_code(dev,
+							type,
+							code,
 							type == EV_ABS ? abs : NULL);
 		}
 		litest_assert_int_eq(rc, 0);
 	}
 
 	rc = libevdev_uinput_create_from_device(dev,
-					        LIBEVDEV_UINPUT_OPEN_MANAGED,
+						LIBEVDEV_UINPUT_OPEN_MANAGED,
 						&uinput);
-	litest_assert_msg(rc == 0, "Failed to create uinput device: %s\n", strerror(-rc));
-
-	libevdev_free(dev);
+	litest_assert_msg(rc == 0,
+			  "Failed to create uinput device: %s\n",
+			  strerror(-rc));
 
 	return uinput;
 }
@@ -3873,10 +3930,8 @@ litest_create_uinput_device_from_descrip
 	const char *syspath;
 	char path[PATH_MAX];
 
-	struct udev_monitor *udev_monitor;
-	struct udev_device *udev_device;
-
-	udev_monitor = udev_setup_monitor();
+	_unref_(udev_monitor) *udev_monitor = udev_setup_monitor();
+	_unref_(udev_device) *udev_device = NULL;
 
 	uinput = litest_create_uinput(name, id, abs_info, events);
 
@@ -3887,9 +3942,6 @@ litest_create_uinput_device_from_descrip
 
 	litest_assert(udev_device_get_property_value(udev_device, "ID_INPUT"));
 
-	udev_device_unref(udev_device);
-	udev_monitor_unref(udev_monitor);
-
 	return uinput;
 }
 
@@ -3903,8 +3955,7 @@ litest_create_uinput_abs_device_v(const
 	int *event = events;
 	int type, code;
 
-	while ((type = va_arg(args, int)) != -1 &&
-	       (code = va_arg(args, int)) != -1) {
+	while ((type = va_arg(args, int)) != -1 && (code = va_arg(args, int)) != -1) {
 		*event++ = type;
 		*event++ = code;
 		litest_assert(event < &events[ARRAY_LENGTH(events) - 2]);
@@ -3913,8 +3964,7 @@ litest_create_uinput_abs_device_v(const
 	*event++ = -1;
 	*event++ = -1;
 
-	return litest_create_uinput_device_from_description(name, id,
-							    abs, events);
+	return litest_create_uinput_device_from_description(name, id, abs, events);
 }
 
 struct libevdev_uinput *
@@ -3946,7 +3996,7 @@ litest_create_uinput_device(const char *
 	return uinput;
 }
 
-struct libinput_event_pointer*
+struct libinput_event_pointer *
 litest_is_button_event(struct libinput_event *event,
 		       unsigned int button,
 		       enum libinput_button_state state)
@@ -3957,10 +4007,8 @@ litest_is_button_event(struct libinput_e
 	litest_assert_ptr_notnull(event);
 	litest_assert_event_type(event, type);
 	ptrev = libinput_event_get_pointer_event(event);
-	litest_assert_int_eq(libinput_event_pointer_get_button(ptrev),
-			     button);
-	litest_assert_int_eq(libinput_event_pointer_get_button_state(ptrev),
-			     state);
+	litest_assert_int_eq(libinput_event_pointer_get_button(ptrev), button);
+	litest_assert_int_eq(libinput_event_pointer_get_button_state(ptrev), state);
 
 	return ptrev;
 }
@@ -4020,8 +4068,7 @@ litest_is_motion_event(struct libinput_e
 	uy = libinput_event_pointer_get_dy_unaccelerated(ptrev);
 
 	/* No 0 delta motion events */
-	litest_assert(x != 0.0 || y != 0.0 ||
-		      ux != 0.0 || uy != 0.0);
+	litest_assert(x != 0.0 || y != 0.0 || ux != 0.0 || uy != 0.0);
 
 	return ptrev;
 }
@@ -4033,23 +4080,18 @@ _litest_assert_key_event(struct libinput
 			 const char *func,
 			 int lineno)
 {
-	struct libinput_event *event;
-
 	litest_wait_for_event(li);
-	event = libinput_get_event(li);
-
+	_destroy_(libinput_event) *event = libinput_get_event(li);
 	litest_is_keyboard_event(event, key, state);
-
-	libinput_event_destroy(event);
 }
 
 void
-_litest_assert_button_event(struct libinput *li, unsigned int button,
+_litest_assert_button_event(struct libinput *li,
+			    unsigned int button,
 			    enum libinput_button_state state,
-			    const char *func, int line)
+			    const char *func,
+			    int line)
 {
-	struct libinput_event *event;
-
 	_litest_checkpoint(func,
 			   line,
 			   ANSI_CYAN,
@@ -4059,16 +4101,12 @@ _litest_assert_button_event(struct libin
 			   state);
 
 	litest_wait_for_event(li);
-	event = libinput_get_event(li);
-
+	_destroy_(libinput_event) *event = libinput_get_event(li);
 	litest_is_button_event(event, button, state);
-
-	libinput_event_destroy(event);
 }
 
 struct libinput_event_touch *
-litest_is_touch_event(struct libinput_event *event,
-		      enum libinput_event_type type)
+litest_is_touch_event(struct libinput_event *event, enum libinput_event_type type)
 {
 	struct libinput_event_touch *touch;
 
@@ -4109,8 +4147,7 @@ litest_is_keyboard_event(struct libinput
 	litest_assert_ptr_notnull(kevent);
 
 	litest_assert_int_eq(libinput_event_keyboard_get_key(kevent), key);
-	litest_assert_int_eq(libinput_event_keyboard_get_key_state(kevent),
-			     state);
+	litest_assert_int_eq(libinput_event_keyboard_get_key_state(kevent), state);
 	return kevent;
 }
 
@@ -4140,7 +4177,6 @@ _litest_assert_gesture_event(struct libi
 			     const char *func,
 			     int line)
 {
-	struct libinput_event *event;
 
 	_litest_checkpoint(func,
 			   line,
@@ -4150,15 +4186,13 @@ _litest_assert_gesture_event(struct libi
 			   nfingers);
 
 	litest_wait_for_event(li);
-	event = libinput_get_event(li);
 
+	_destroy_(libinput_event) *event = libinput_get_event(li);
 	litest_is_gesture_event(event, type, nfingers);
-	libinput_event_destroy(event);
 }
 
 struct libinput_event_tablet_tool *
-litest_is_tablet_event(struct libinput_event *event,
-		       enum libinput_event_type type)
+litest_is_tablet_event(struct libinput_event *event, enum libinput_event_type type)
 {
 	struct libinput_event_tablet_tool *tevent;
 
@@ -4172,26 +4206,30 @@ litest_is_tablet_event(struct libinput_e
 }
 
 void
-_litest_assert_tablet_button_event(struct libinput *li, unsigned int button,
+_litest_assert_tablet_button_event(struct libinput *li,
+				   unsigned int button,
 				   enum libinput_button_state state,
 				   const char *func,
 				   int lineno)
 {
-	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tev;
 	enum libinput_event_type type = LIBINPUT_EVENT_TABLET_TOOL_BUTTON;
 
+	_litest_checkpoint(func,
+			   lineno,
+			   ANSI_CYAN,
+			   "asserting tablet button event button %d down: %s",
+			   button,
+			   yesno(state));
+
 	litest_wait_for_event(li);
-	event = libinput_get_event(li);
 
+	_destroy_(libinput_event) *event = libinput_get_event(li);
 	litest_assert_notnull(event);
 	litest_assert_event_type(event, type);
 	tev = libinput_event_get_tablet_tool_event(event);
-	litest_assert_int_eq(libinput_event_tablet_tool_get_button(tev),
-			     button);
-	litest_assert_int_eq(libinput_event_tablet_tool_get_button_state(tev),
-			     state);
-	libinput_event_destroy(event);
+	litest_assert_int_eq(libinput_event_tablet_tool_get_button(tev), button);
+	litest_assert_int_eq(libinput_event_tablet_tool_get_button_state(tev), state);
 }
 
 struct libinput_event_tablet_tool *
@@ -4257,17 +4295,30 @@ litest_event_pointer_get_axis_source(str
 }
 
 void
+_litest_assert_tablet_axis_event(struct libinput *li, const char *func, int lineno)
+{
+	_litest_checkpoint(func, lineno, ANSI_CYAN, "asserting axis event");
+
+	litest_wait_for_event(li);
+	_destroy_(libinput_event) *event = libinput_get_event(li);
+	litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+}
+
+void
 _litest_assert_tablet_proximity_event(struct libinput *li,
 				      enum libinput_tablet_tool_proximity_state state,
 				      const char *func,
 				      int lineno)
 {
-	struct libinput_event *event;
+	_litest_checkpoint(func,
+			   lineno,
+			   ANSI_CYAN,
+			   "asserting proximity %s event",
+			   state ? "in" : "out");
 
 	litest_wait_for_event(li);
-	event = libinput_get_event(li);
+	_destroy_(libinput_event) *event = libinput_get_event(li);
 	litest_is_proximity_event(event, state);
-	libinput_event_destroy(event);
 }
 
 void
@@ -4276,19 +4327,22 @@ _litest_assert_tablet_tip_event(struct l
 				const char *func,
 				int lineno)
 {
-	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tev;
 	enum libinput_event_type type = LIBINPUT_EVENT_TABLET_TOOL_TIP;
 
+	_litest_checkpoint(func,
+			   lineno,
+			   ANSI_CYAN,
+			   "asserting tip %s event",
+			   state ? "down" : "up");
+
 	litest_wait_for_event(li);
-	event = libinput_get_event(li);
 
+	_destroy_(libinput_event) *event = libinput_get_event(li);
 	litest_assert_notnull(event);
 	litest_assert_event_type(event, type);
 	tev = libinput_event_get_tablet_tool_event(event);
-	litest_assert_int_eq(libinput_event_tablet_tool_get_tip_state(tev),
-			     state);
-	libinput_event_destroy(event);
+	litest_assert_int_eq(libinput_event_tablet_tool_get_tip_state(tev), state);
 }
 
 struct libinput_event_tablet_pad *
@@ -4305,17 +4359,14 @@ litest_is_pad_button_event(struct libinp
 	p = libinput_event_get_tablet_pad_event(event);
 	litest_assert_ptr_notnull(p);
 
-	litest_assert_int_eq(libinput_event_tablet_pad_get_button_number(p),
-			     button);
-	litest_assert_int_eq(libinput_event_tablet_pad_get_button_state(p),
-			     state);
+	litest_assert_int_eq(libinput_event_tablet_pad_get_button_number(p), button);
+	litest_assert_int_eq(libinput_event_tablet_pad_get_button_state(p), state);
 
 	return p;
 }
 
 struct libinput_event_tablet_pad *
-litest_is_pad_dial_event(struct libinput_event *event,
-			 unsigned int number)
+litest_is_pad_dial_event(struct libinput_event *event, unsigned int number)
 {
 	struct libinput_event_tablet_pad *p;
 	enum libinput_event_type type = LIBINPUT_EVENT_TABLET_PAD_DIAL;
@@ -4324,8 +4375,7 @@ litest_is_pad_dial_event(struct libinput
 	litest_assert_event_type(event, type);
 	p = libinput_event_get_tablet_pad_event(event);
 
-	litest_assert_int_eq(libinput_event_tablet_pad_get_dial_number(p),
-			     number);
+	litest_assert_int_eq(libinput_event_tablet_pad_get_dial_number(p), number);
 
 	return p;
 }
@@ -4342,10 +4392,8 @@ litest_is_pad_ring_event(struct libinput
 	litest_assert_event_type(event, type);
 	p = libinput_event_get_tablet_pad_event(event);
 
-	litest_assert_int_eq(libinput_event_tablet_pad_get_ring_number(p),
-			     number);
-	litest_assert_int_eq(libinput_event_tablet_pad_get_ring_source(p),
-			     source);
+	litest_assert_int_eq(libinput_event_tablet_pad_get_ring_number(p), number);
+	litest_assert_int_eq(libinput_event_tablet_pad_get_ring_source(p), source);
 
 	return p;
 }
@@ -4362,10 +4410,8 @@ litest_is_pad_strip_event(struct libinpu
 	litest_assert_event_type(event, type);
 	p = libinput_event_get_tablet_pad_event(event);
 
-	litest_assert_int_eq(libinput_event_tablet_pad_get_strip_number(p),
-			     number);
-	litest_assert_int_eq(libinput_event_tablet_pad_get_strip_source(p),
-			     source);
+	litest_assert_int_eq(libinput_event_tablet_pad_get_strip_number(p), number);
+	litest_assert_int_eq(libinput_event_tablet_pad_get_strip_source(p), source);
 
 	return p;
 }
@@ -4385,8 +4431,7 @@ litest_is_pad_key_event(struct libinput_
 	litest_assert(p != NULL);
 
 	litest_assert_int_eq(libinput_event_tablet_pad_get_key(p), key);
-	litest_assert_int_eq(libinput_event_tablet_pad_get_key_state(p),
-			     state);
+	litest_assert_int_eq(libinput_event_tablet_pad_get_key_state(p), state);
 
 	return p;
 }
@@ -4404,8 +4449,7 @@ litest_is_switch_event(struct libinput_e
 	swev = libinput_event_get_switch_event(event);
 
 	litest_assert_int_eq(libinput_event_switch_get_switch(swev), sw);
-	litest_assert_int_eq(libinput_event_switch_get_switch_state(swev),
-			     state);
+	litest_assert_int_eq(libinput_event_switch_get_switch_state(swev), state);
 
 	return swev;
 }
@@ -4417,14 +4461,10 @@ _litest_assert_switch_event(struct libin
 			    const char *func,
 			    int lineno)
 {
-	struct libinput_event *event;
-
 	litest_wait_for_event(li);
-	event = libinput_get_event(li);
 
+	_destroy_(libinput_event) *event = libinput_get_event(li);
 	litest_is_switch_event(event, sw, state);
-
-	libinput_event_destroy(event);
 }
 
 void
@@ -4434,13 +4474,10 @@ _litest_assert_pad_button_event(struct l
 				const char *func,
 				int lineno)
 {
-	struct libinput_event *event;
-
 	litest_wait_for_event(li);
-	event = libinput_get_event(li);
 
+	_destroy_(libinput_event) *event = libinput_get_event(li);
 	litest_is_pad_button_event(event, button, state);
-	libinput_event_destroy(event);
 }
 
 void
@@ -4450,13 +4487,10 @@ _litest_assert_pad_key_event(struct libi
 			     const char *func,
 			     int lineno)
 {
-	struct libinput_event *event;
-
 	litest_wait_for_event(li);
-	event = libinput_get_event(li);
 
+	_destroy_(libinput_event) *event = libinput_get_event(li);
 	litest_is_pad_key_event(event, key, state);
-	libinput_event_destroy(event);
 }
 
 void
@@ -4492,7 +4526,7 @@ litest_assert_scroll(struct libinput *li
 		 * go under the minimum we expect for all other
 		 * events */
 		if (nevents == 1)
-			min = minimum_movement/2;
+			min = minimum_movement / 2;
 
 		value = litest_event_pointer_get_value(ptrev, axis);
 		if (litest_is_high_res_axis_event(event)) {
@@ -4531,7 +4565,6 @@ litest_assert_axis_end_sequence(struct l
 				enum libinput_pointer_axis axis,
 				enum libinput_pointer_axis_source source)
 {
-	struct libinput_event *event;
 	struct libinput_event_pointer *ptrev;
 	bool last_hi_res_event_found, last_low_res_event_found;
 	double val;
@@ -4546,7 +4579,7 @@ litest_assert_axis_end_sequence(struct l
 
 	/* both high and low scroll end events must be sent */
 	for (i = 0; i < 2; i++) {
-		event = libinput_get_event(li);
+		_destroy_(libinput_event) *event = libinput_get_event(li);
 		ptrev = litest_is_axis_event(event, axis_type, axis, source);
 		val = litest_event_pointer_get_value(ptrev, axis);
 		litest_assert(val == 0.0);
@@ -4558,8 +4591,6 @@ litest_assert_axis_end_sequence(struct l
 			litest_assert(!last_low_res_event_found);
 			last_low_res_event_found = true;
 		}
-
-		libinput_event_destroy(event);
 	}
 
 	litest_assert(last_low_res_event_found);
@@ -4595,8 +4626,7 @@ _litest_assert_only_typed_events(struct
 }
 
 void
-litest_assert_only_axis_events(struct libinput *li,
-			       enum libinput_event_type axis_type)
+litest_assert_only_axis_events(struct libinput *li, enum libinput_event_type axis_type)
 {
 	struct libinput_event *event;
 
@@ -4619,8 +4649,7 @@ litest_assert_only_axis_events(struct li
 }
 
 void
-litest_assert_no_typed_events(struct libinput *li,
-			      enum libinput_event_type type)
+litest_assert_no_typed_events(struct libinput *li, enum libinput_event_type type)
 {
 	struct libinput_event *event;
 
@@ -4630,8 +4659,7 @@ litest_assert_no_typed_events(struct lib
 	event = libinput_get_event(li);
 
 	while (event) {
-		litest_assert_int_ne(libinput_event_get_type(event),
-                                     type);
+		litest_assert_int_ne(libinput_event_get_type(event), type);
 		libinput_event_destroy(event);
 		libinput_dispatch(li);
 		event = libinput_get_event(li);
@@ -4706,163 +4734,195 @@ litest_assert_touch_motion_frame(struct
 void
 litest_assert_touch_down_frame(struct libinput *li)
 {
-	struct libinput_event *event;
+	_destroy_(libinput_event) *down = libinput_get_event(li);
+	litest_is_touch_event(down, LIBINPUT_EVENT_TOUCH_DOWN);
 
-	event = libinput_get_event(li);
-	litest_is_touch_event(event, LIBINPUT_EVENT_TOUCH_DOWN);
-	libinput_event_destroy(event);
-
-	event = libinput_get_event(li);
-	litest_is_touch_event(event, LIBINPUT_EVENT_TOUCH_FRAME);
-	libinput_event_destroy(event);
+	_destroy_(libinput_event) *frame = libinput_get_event(li);
+	litest_is_touch_event(frame, LIBINPUT_EVENT_TOUCH_FRAME);
 }
 
 void
 litest_assert_touch_up_frame(struct libinput *li)
 {
-	struct libinput_event *event;
-
-	event = libinput_get_event(li);
-	litest_is_touch_event(event, LIBINPUT_EVENT_TOUCH_UP);
-	libinput_event_destroy(event);
+	_destroy_(libinput_event) *up = libinput_get_event(li);
+	litest_is_touch_event(up, LIBINPUT_EVENT_TOUCH_UP);
 
-	event = libinput_get_event(li);
-	litest_is_touch_event(event, LIBINPUT_EVENT_TOUCH_FRAME);
-	libinput_event_destroy(event);
+	_destroy_(libinput_event) *frame = libinput_get_event(li);
+	litest_is_touch_event(frame, LIBINPUT_EVENT_TOUCH_FRAME);
 }
 
 void
 litest_assert_touch_cancel(struct libinput *li)
 {
-	struct libinput_event *event;
+	_destroy_(libinput_event) *cancel = libinput_get_event(li);
+	litest_is_touch_event(cancel, LIBINPUT_EVENT_TOUCH_CANCEL);
 
-	event = libinput_get_event(li);
-	litest_is_touch_event(event, LIBINPUT_EVENT_TOUCH_CANCEL);
-	libinput_event_destroy(event);
-
-	event = libinput_get_event(li);
-	litest_is_touch_event(event, LIBINPUT_EVENT_TOUCH_FRAME);
-	libinput_event_destroy(event);
-}
-
-void
-litest_timeout_tap(void)
-{
-	msleep(300);
+	_destroy_(libinput_event) *frame = libinput_get_event(li);
+	litest_is_touch_event(frame, LIBINPUT_EVENT_TOUCH_FRAME);
 }
 
 void
-litest_timeout_tapndrag(void)
+_litest_timeout(struct libinput *li, const char *func, int lineno, int millis)
 {
-	msleep(520);
+	if (li)
+		_litest_dispatch(li, func, lineno);
+	msleep(millis);
+	if (li)
+		_litest_dispatch(li, func, lineno);
 }
 
 void
-litest_timeout_debounce(void)
-{
-	msleep(30);
-}
-
-void
-litest_timeout_softbuttons(void)
-{
-	msleep(300);
-}
-
-void
-litest_timeout_buttonscroll(void)
-{
-	msleep(300);
-}
-
-void
-litest_timeout_finger_switch(void)
+_litest_assert_logcapture_no_errors(struct litest_logcapture *capture,
+				    const char *file,
+				    const char *func,
+				    int line)
 {
-	msleep(140);
-}
+	litest_assert_ptr_notnull(capture);
 
-void
-litest_timeout_wheel_scroll(void)
-{
-	msleep(600);
+	if (capture->errors &&
+	    strv_for_each((const char **)capture->errors, is_actual_error, NULL)) {
+		_autofree_ char *errors = strv_join(capture->errors, "\n");
+		_litest_abort_msg(file,
+				  line,
+				  func,
+				  "Unexpected errors in log capture:\n%s\n",
+				  errors);
+	}
 }
 
 void
-litest_timeout_edgescroll(void)
+litest_logcapture_destroy(struct litest_logcapture *c)
 {
-	msleep(300);
+	strv_free(c->errors);
+	strv_free(c->infos);
+	strv_free(c->debugs);
+	strv_free(c->bugs);
+	free(c);
 }
 
-void
-litest_timeout_middlebutton(void)
+LIBINPUT_ATTRIBUTE_PRINTF(3, 0)
+static void
+litest_log_handler_msgcapture(struct libinput *libinput,
+			      enum libinput_log_priority pri,
+			      const char *format,
+			      va_list args)
 {
-	msleep(70);
-}
+	struct litest_user_data *user_data = libinput_get_user_data(libinput);
+	struct litest_logcapture *capture = user_data->private;
+	const char *priority = NULL;
+	const char *color =
+		use_colors ? ANSI_RGB(255, 255, 70) ANSI_RGB_BG(40, 40, 40) : "";
 
-void
-litest_timeout_dwt_short(void)
-{
-	msleep(220);
-}
+	switch (pri) {
+	case LIBINPUT_LOG_PRIORITY_ERROR:
+		priority = "error ";
+		break;
+	case LIBINPUT_LOG_PRIORITY_INFO:
+		priority = "info ";
+		break;
+	case LIBINPUT_LOG_PRIORITY_DEBUG:
+		priority = "debug ";
+		break;
+	}
 
-void
-litest_timeout_dwt_long(void)
-{
-	msleep(520);
-}
+	_autofree_ char *message = strdup_vprintf(format, args);
 
-void
-litest_timeout_gesture(void)
-{
-	msleep(120);
-}
+	fprintf(stderr,
+		"%slitest captured: %-6s%s %s",
+		color,
+		priority,
+		use_colors ? ANSI_NORMAL : "",
+		message);
+
+	if (strstr(message, "kernel bug: ") || strstr(message, "client bug: ") ||
+	    strstr(message, "libinput bug: ") || strstr(message, "plugin bug: ")) {
+		capture->bugs = strv_append_strdup(capture->bugs, message);
+	}
 
-void
-litest_timeout_gesture_scroll(void)
-{
-	msleep(180);
+	switch (pri) {
+	case LIBINPUT_LOG_PRIORITY_ERROR:
+		capture->errors = strv_append_take(capture->errors, &message);
+		break;
+	case LIBINPUT_LOG_PRIORITY_INFO:
+		capture->infos = strv_append_take(capture->infos, &message);
+		break;
+	case LIBINPUT_LOG_PRIORITY_DEBUG:
+		capture->debugs = strv_append_take(capture->debugs, &message);
+		break;
+	}
 }
 
-void
-litest_timeout_gesture_hold(void)
+struct litest_logcapture *
+litest_logcapture_setup(struct libinput *li)
 {
-	msleep(300);
+	struct litest_logcapture *c = zalloc(sizeof(*c));
+	litest_context_set_user_data(li, c);
+	libinput_log_set_handler(li, litest_log_handler_msgcapture);
+	return c;
 }
 
-void
-litest_timeout_gesture_quick_hold(void)
+struct litest_logcapture *
+litest_logcapture_remove(struct libinput *li, struct litest_logcapture *capture)
 {
-	msleep(60);
+	litest_restore_log_handler(li);
+	litest_logcapture_destroy(capture);
+	return NULL;
 }
 
-void
-litest_timeout_trackpoint(void)
-{
-	msleep(320);
-}
+#define litest_with_logcapture(li_, capture_)                                  \
+  for (struct litest_logcapture *capture_ = litest_logcapture_setup(li_);      \
+       capture_ != NULL; capture_ = litest_logcapture_remove(li_, capture_))
 
 void
-litest_timeout_tablet_proxout(void)
+_litest_assert_strv_substring(char **strv,
+			      char *substring,
+			      const char *file,
+			      const char *func,
+			      int line)
 {
-	msleep(170);
-}
+	if (!strv) {
+		_litest_abort_msg(file,
+				  line,
+				  func,
+				  "Expected substring '%s' but strv is NULL",
+				  substring);
+	}
 
-void
-litest_timeout_touch_arbitration(void)
-{
-	msleep(100);
+	bool found = strv_find_substring(strv, substring, NULL);
+	if (!found) {
+		_autofree_ char *strv_str = strv_join(strv, "', '");
+		_litest_abort_msg(file,
+				  line,
+				  func,
+				  "Expected substring '%s' not found in strv: ['%s']",
+				  substring,
+				  strv_str);
+	}
 }
 
 void
-litest_timeout_hysteresis(void)
+_litest_assert_strv_no_substring(char **strv,
+				 char *substring,
+				 const char *file,
+				 const char *func,
+				 int line)
 {
-	msleep(90);
-}
+	if (!strv)
+		return;
 
-void
-litest_timeout_3fg_drag(void)
-{
-	msleep(800);
+	size_t index;
+	bool found = strv_find_substring(strv, substring, &index);
+	if (found) {
+		_autofree_ char *strv_str = strv_join(strv, "', '");
+		_litest_abort_msg(
+			file,
+			line,
+			func,
+			"Unexpected substring '%s' present at index %zd in strv: ['%s']",
+			substring,
+			index,
+			strv_str);
+	}
 }
 
 void
@@ -4882,17 +4942,13 @@ litest_pop_event_frame(struct litest_dev
 }
 
 void
-litest_filter_event(struct litest_device *dev,
-		    unsigned int type,
-		    unsigned int code)
+litest_filter_event(struct litest_device *dev, unsigned int type, unsigned int code)
 {
 	libevdev_disable_event_code(dev->evdev, type, code);
 }
 
 void
-litest_unfilter_event(struct litest_device *dev,
-		      unsigned int type,
-		      unsigned int code)
+litest_unfilter_event(struct litest_device *dev, unsigned int type, unsigned int code)
 {
 	/* would need an non-NULL argument for re-enabling, so simply abort
 	 * until we need to be more sophisticated */
@@ -4940,7 +4996,8 @@ void
 litest_semi_mt_touch_down(struct litest_device *d,
 			  struct litest_semi_mt *semi_mt,
 			  unsigned int slot,
-			  double x, double y)
+			  double x,
+			  double y)
 {
 	double t, l, r = 0, b = 0; /* top, left, right, bottom */
 
@@ -4984,7 +5041,8 @@ void
 litest_semi_mt_touch_move(struct litest_device *d,
 			  struct litest_semi_mt *semi_mt,
 			  unsigned int slot,
-			  double x, double y)
+			  double x,
+			  double y)
 {
 	double t, l, r = 0, b = 0; /* top, left, right, bottom */
 
@@ -5042,14 +5100,8 @@ litest_semi_mt_touch_up(struct litest_de
 	litest_event(d, EV_SYN, SYN_REPORT, 0);
 }
 
-enum litest_mode {
-	LITEST_MODE_ERROR,
-	LITEST_MODE_TEST,
-	LITEST_MODE_LIST,
-};
-
-static inline enum litest_mode
-litest_parse_argv(int argc, char **argv)
+enum litest_mode
+litest_parse_argv(int argc, char **argv, int *njobs_out)
 {
 	enum {
 		OPT_EXIT_FIRST,
@@ -5076,42 +5128,42 @@ litest_parse_argv(int argc, char **argv)
 		{ "jobs", 1, 0, OPT_JOBS },
 		{ "list", 0, 0, OPT_LIST },
 		{ "verbose", 0, 0, OPT_VERBOSE },
-		{ "help", 0, 0, 'h'},
-		{ 0, 0, 0, 0}
+		{ "help", 0, 0, 'h' },
+		{ 0, 0, 0, 0 }
 	};
 	enum {
 		JOBS_DEFAULT,
+		JOBS_NONE,
 		JOBS_SINGLE,
 		JOBS_CUSTOM
 	} want_jobs = JOBS_DEFAULT;
-	char *builddir;
 	char *jobs_env;
+	int jobs = 0;
 
 	/* If we are not running from the builddir, we assume we're running
 	 * against the system as installed */
-	builddir = builddir_lookup();
-	if (!builddir)
+	if (!builddir_lookup(NULL))
 		use_system_rules_quirks = true;
-	free(builddir);
 
 	if (in_debugger)
-		want_jobs = JOBS_SINGLE;
+		want_jobs = JOBS_NONE;
 
 	if ((jobs_env = getenv("LITEST_JOBS"))) {
 		if (!safe_atoi(jobs_env, &jobs)) {
-			fprintf(stderr, "LITEST_JOBS environment variable must be positive integer\n");
+			fprintf(stderr,
+				"LITEST_JOBS environment variable must be positive integer\n");
 			exit(EXIT_FAILURE);
 		}
 	}
 
-	while(1) {
+	while (1) {
 		int c;
 		int option_index = 0;
 
 		c = getopt_long(argc, argv, "j:x", opts, &option_index);
 		if (c == -1)
 			break;
-		switch(c) {
+		switch (c) {
 		default:
 		case 'h':
 			printf("Usage: %s [--verbose] [--jobs] [--filter-...]\n"
@@ -5130,7 +5182,8 @@ litest_parse_argv(int argc, char **argv)
 			       "    --filter-deviceless=.... \n"
 			       "          Glob to filter on tests that do not create test devices\n"
 			       "    --filter-parameter=param1:glob,param2:glob,... \n"
-			       "          Glob(s) to filter on the given parameters in their string representation.\n"
+			       "          Glob(s) to filter on the given parameters in their string "
+			       "representation.\n"
 			       "          Boolean parameters are filtered via 'true' and 'false'.\n"
 			       "    --verbose\n"
 			       "          Enable verbose output\n"
@@ -5165,15 +5218,19 @@ litest_parse_argv(int argc, char **argv)
 			break;
 		case OPT_FILTER_PARAMETER: {
 			size_t nelems;
-			char **params = strv_from_string(optarg, ",", &nelems);
+			_autostrvfree_ char **params =
+				strv_from_string(optarg, ",", &nelems);
 			const size_t max_filters = ARRAY_LENGTH(filter_params) - 1;
-			if (nelems >=  max_filters) {
-				fprintf(stderr, "Only %zd parameter filters are supported\n", max_filters);
+			if (nelems >= max_filters) {
+				fprintf(stderr,
+					"Only %zd parameter filters are supported\n",
+					max_filters);
 				exit(1);
 			}
-			for (size_t i = 0; i < nelems; i++)  {
+			for (size_t i = 0; i < nelems; i++) {
 				size_t n;
-				char **strv = strv_from_string(params[i], ":", &n);
+				_autostrvfree_ char **strv =
+					strv_from_string(params[i], ":", &n);
 				assert(n == 2);
 
 				const char *name = strv[0];
@@ -5182,10 +5239,7 @@ litest_parse_argv(int argc, char **argv)
 				struct param_filter *f = &filter_params[i];
 				snprintf(f->name, sizeof(f->name), "%s", name);
 				snprintf(f->glob, sizeof(f->glob), "%s", glob);
-
-				strv_free(strv);
 			}
-			strv_free(params);
 			break;
 		}
 		case 'j':
@@ -5212,204 +5266,18 @@ litest_parse_argv(int argc, char **argv)
 		}
 	}
 
-	if (want_jobs == JOBS_SINGLE)
+	switch (want_jobs) {
+	case JOBS_SINGLE:
 		jobs = 1;
-
-	return LITEST_MODE_TEST;
-}
-
-#ifndef LITEST_NO_MAIN
-static bool
-is_debugger_attached(void)
-{
-	int status;
-	bool rc;
-	int pid = fork();
-
-	if (pid == -1)
-		return 0;
-
-	if (pid == 0) {
-		int ppid = getppid();
-		if (ptrace(PTRACE_ATTACH, ppid, NULL, 0) == 0) {
-			waitpid(ppid, NULL, 0);
-			ptrace(PTRACE_CONT, ppid, NULL, 0);
-			ptrace(PTRACE_DETACH, ppid, NULL, 0);
-			rc = false;
-		} else {
-			rc = true;
-		}
-		_exit(rc);
-	} else {
-		waitpid(pid, &status, 0);
-		rc = WEXITSTATUS(status);
-	}
-
-	return !!rc;
-}
-
-static void
-litest_list_tests(struct list *tests)
-{
-	struct suite *s;
-	const char *last_test_name = "<invalid>";
-	const char *last_dev_name = "<invalid>";
-
-	printf("groups:\n");
-	list_for_each(s, tests, node) {
-		struct test *t;
-		printf("  - group: \"%s\"\n", s->name);
-		printf("    tests:\n");
-		list_for_each(t, &s->tests, node) {
-			bool same_test = streq(last_test_name, t->name);
-			bool same_dev = streq(last_dev_name, t->devname);
-
-			if (!same_test) {
-				printf("      - name: \"%s\"\n", t->name);
-				printf("        devices:\n");
-			}
-
-			if (!same_test || !same_dev) {
-				last_test_name = t->name;
-				last_dev_name = t->devname;
-				printf("          - name: \"%s\"\n", t->devname);
-			}
-		}
-	}
-}
-
-extern const struct test_device __start_test_device_section, __stop_test_device_section;
-
-static void
-litest_init_test_devices(struct list *devices)
-{
-	const struct test_device *t;
-	for (t = &__start_test_device_section; t < &__stop_test_device_section; t++)
-		list_append(devices, &t->device->node);
-}
-
-extern const struct test_collection __start_test_collection_section,
-				    __stop_test_collection_section;
-
-static void
-setup_tests(void)
-{
-	const struct test_collection *c;
-
-	for (c = &__start_test_collection_section;
-	     c < &__stop_test_collection_section;
-	     c++) {
-		struct suite *s;
-		s = zalloc(sizeof(*s));
-		s->name = safe_strdup(c->name);
-
-		list_init(&s->tests);
-		list_append(&all_test_suites, &s->node);
-
-		current_suite = s;
-		c->setup();
-		current_suite = NULL;
-	}
-}
-
-static int
-check_device_access(void)
-{
-	if (getuid() != 0) {
-		fprintf(stderr,
-			"%s must be run as root.\n",
-			program_invocation_short_name);
-		return 77;
-	}
-
-	if (access("/dev/uinput", F_OK) == -1 &&
-	    access("/dev/input/uinput", F_OK) == -1) {
-		fprintf(stderr,
-			"uinput device is missing, skipping tests.\n");
-		return 77;
-	}
-
-	return 0;
-}
-
-static void
-litest_free_test_list(struct list *tests)
-{
-	struct suite *s;
-
-	list_for_each_safe(s, tests, node) {
-		struct test *t;
-
-		list_for_each_safe(t, &s->tests, node) {
-			litest_test_parameters_unref(t->params);
-			free(t->name);
-			free(t->devname);
-			list_remove(&t->node);
-			free(t);
-		}
-
-		list_remove(&s->node);
-		free(s->name);
-		free(s);
-	}
-}
-
-int
-main(int argc, char **argv)
-{
-	enum litest_mode mode;
-	int rc;
-	const char *meson_testthreads;
-
-	use_colors = getenv("FORCE_COLOR") || isatty(STDERR_FILENO);
-	if (getenv("NO_COLOR"))
-		use_colors = false;
-
-	in_debugger = is_debugger_attached();
-	if (in_debugger) {
+		break;
+	case JOBS_NONE:
 		jobs = 0;
-	} else if ((meson_testthreads = getenv("MESON_TESTTHREADS")) == NULL ||
-		   !safe_atoi(meson_testthreads, &jobs)) {
-		jobs = get_nprocs();
-		if (!RUNNING_ON_VALGRIND)
-			jobs *= 2;
-	}
-
-	if (getenv("LITEST_VERBOSE"))
-		verbose = true;
-
-	mode = litest_parse_argv(argc, argv);
-	if (mode == LITEST_MODE_ERROR)
-		return EXIT_FAILURE;
-
-	litest_init_test_devices(&devices);
-
-	setup_tests();
-	if (list_empty(&all_test_suites)) {
-		fprintf(stderr,
-			"Error: filters are too strict, no tests to run.\n");
-		return EXIT_FAILURE;
-	}
-
-	if (mode == LITEST_MODE_LIST) {
-		litest_list_tests(&all_test_suites);
-		return EXIT_SUCCESS;
+		break;
+	default:
+		break;
 	}
 
-	if (!run_deviceless && (rc = check_device_access()) != 0)
-		return rc;
-
-	enum litest_runner_result result = litest_run(&all_test_suites);
+	*njobs_out = jobs;
 
-	litest_free_test_list(&all_test_suites);
-
-	switch (result) {
-		case LITEST_PASS:
-			return EXIT_SUCCESS;
-		case LITEST_SKIP:
-			return 77;
-		default:
-			return result;
-	}
+	return LITEST_MODE_TEST;
 }
-#endif
diff -pruN 1.28.1-1/test/litest.h 1.30.0-1/test/litest.h
--- 1.28.1-1/test/litest.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/litest.h	2025-11-25 03:40:43.000000000 +0000
@@ -22,27 +22,27 @@
  */
 
 #include "config.h"
+
 #include "litest-config.h"
 
 #ifndef LITEST_H
 #define LITEST_H
 
-#include <stdbool.h>
-#include <stdarg.h>
-#include <libevdev/libevdev.h>
 #include <libevdev/libevdev-uinput.h>
+#include <libevdev/libevdev.h>
 #include <libinput.h>
 #include <math.h>
+#include <stdarg.h>
+#include <stdbool.h>
 
 #include "libinput-private-config.h"
 #include "libinput-util.h"
-#include "quirks.h"
-
 #include "litest-runner.h"
+#include "quirks.h"
 
 #define START_TEST(func_)  \
    static enum litest_runner_result func_(const struct litest_runner_test_env *test_env) { \
-	int _i __attribute__((unused)) = test_env->rangeval;
+	int _i _unused_ = test_env->rangeval;
 
 #define END_TEST \
 	return LITEST_PASS; \
@@ -90,15 +90,23 @@ struct test_collection {
 #define litest_mark_test_start() \
 	litest_checkpoint("==================== BOILERPLATE END. TEST CONTENT STARTING NOW ====================");
 
-__attribute__ ((format (printf, 3, 0)))
-void _litest_checkpoint(const char *func,
-			int line,
-			const char *color,
-			const char *format,
-			...);
+__attribute__((format(printf, 3, 0))) void
+_litest_checkpoint(const char *func,
+		   int line,
+		   const char *color,
+		   const char *format,
+		   ...);
 #define litest_checkpoint(...) \
 	_litest_checkpoint(__func__, __LINE__, ANSI_GREEN, __VA_ARGS__)
 
+#define litest_log_group(...) \
+	for (bool i_ = ({ \
+			litest_checkpoint("🭋🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂 %s:%3d 🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🬂🭀", __func__, __LINE__); \
+			litest_checkpoint(" " __VA_ARGS__); true; }); \
+		i_; \
+		i_ = ({litest_checkpoint("🭦🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭 %s:%3d 🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🬭🭛", __func__, __LINE__);  \
+			  false; }))
+
 /**
  * litest itself needs the user_data to store some test-suite-specific
  * information. Tests must not override this pointer, any data they need
@@ -145,7 +153,7 @@ litest_fail_comparison_str(const char *f
 			   const char *func,
 			   const char *comparison,
 			   const char *operator,
-			   const char *astr,
+			   const char * astr,
 			   const char *bstr);
 
 #define litest_assert(cond) \
@@ -162,11 +170,17 @@ litest_fail_comparison_str(const char *f
 					      #cond, __VA_ARGS__); \
 	} while(0)
 
-#define litest_abort_msg(...) {\
-	litest_fail_condition(__FILE__, __LINE__, __func__, \
+#define litest_assert_not_reached() \
+	litest_abort_msg("Triggered unreachable code\n")
+
+#define _litest_abort_msg(file_, line_, func_, ...) do {\
+	litest_fail_condition(file_, line_, func_, \
 			      "aborting", __VA_ARGS__); \
 	abort(); \
-}
+} while (0)
+
+#define litest_abort_msg(...) \
+	_litest_abort_msg(__FILE__, __LINE__, __func__, __VA_ARGS__)
 
 #define litest_assert_notnull(cond) \
 	do { \
@@ -279,6 +293,28 @@ litest_fail_comparison_str(const char *f
 						   _a, _b); \
 	} while(0)
 
+#define litest_assert_str_in(needle_, haystack_) \
+	do { \
+		const char *_needle = needle_; \
+		const char *_haystack = haystack_; \
+		if (!strstr(_haystack, _needle)) \
+			litest_fail_comparison_str(__FILE__, __LINE__, __func__,\
+						   "'" #needle_ "' in: '" #haystack_ "'", \
+						   "in", \
+						   _needle, _haystack); \
+	} while(0)
+
+#define litest_assert_str_not_in(needle_, haystack_) \
+	do { \
+		const char *_needle = needle_; \
+		const char *_haystack = haystack_; \
+		if (strstr(_haystack, _needle)) \
+			litest_fail_comparison_str(__FILE__, __LINE__, __func__,\
+						   "'" #needle_ "' not in: '" #haystack_ "'", \
+						   "not in", \
+						   _needle, _haystack); \
+	} while(0)
+
 #define LITEST_DEFAULT_EPSILON  0.001
 
 #define litest_assert_double_eq_epsilon(a_, b_, epsilon_)\
@@ -362,7 +398,28 @@ litest_fail_comparison_str(const char *f
 #define litest_assert_double_ge(a_, b_)\
 	litest_assert_double_ge_epsilon((a_), (b_),LITEST_DEFAULT_EPSILON)
 
-void litest_backtrace(const char *func);
+void
+_litest_assert_strv_substring(char **strv,
+			      char *substring,
+			      const char *file,
+			      const char *func,
+			      int line);
+
+#define litest_assert_strv_substring(strv_, substring_) \
+	_litest_assert_strv_substring(strv_, substring_, __FILE__, __func__, __LINE__)
+
+void
+_litest_assert_strv_no_substring(char **strv,
+				 char *substring,
+				 const char *file,
+				 const char *func,
+				 int line);
+
+#define litest_assert_strv_no_substring(strv_, substring_) \
+	_litest_assert_strv_no_substring(strv_, substring_, __FILE__, __func__, __LINE__)
+
+void
+litest_backtrace(const char *func);
 
 enum litest_device_type {
 	LITEST_NO_DEVICE = -1,
@@ -403,6 +460,7 @@ enum litest_device_type {
 
 	/* Pointing devices and keyboards */
 	LITEST_MOUSE,
+	LITEST_MOUSE_PS2,
 	LITEST_KEYBOARD,
 	LITEST_TRACKPOINT,
 	LITEST_ABSINFO_OVERRIDE,
@@ -425,9 +483,11 @@ enum litest_device_type {
 	LITEST_MOUSE_GLADIUS,
 	LITEST_MOUSE_LOW_DPI,
 	LITEST_MOUSE_ROCCAT,
+	LITEST_MOUSE_VIRTUAL,
 	LITEST_MOUSE_WHEEL_CLICK_ANGLE,
 	LITEST_MOUSE_WHEEL_CLICK_COUNT,
 	LITEST_MOUSE_WHEEL_TILT,
+	LITEST_MOUSE_WHEEL_HIRES_DISABLED,
 	LITEST_MS_NANO_TRANSCEIVER_MOUSE,
 	LITEST_SONY_VAIO_KEYS,
 	LITEST_SYNAPTICS_TRACKPOINT_BUTTONS,
@@ -451,9 +511,10 @@ enum litest_device_type {
 	LITEST_ELAN_TABLET,
 	LITEST_HUION_TABLET,
 	LITEST_HUION_Q620M_DIAL,
+	LITEST_PLOOPY_PAVONIS_STYLUS,
+	LITEST_QEMU_TABLET,
 	LITEST_TABLET_DOUBLEDIAL_PAD,
 	LITEST_TABLET_REL_DIAL_PAD,
-	LITEST_QEMU_TABLET,
 	LITEST_UCLOGIC_TABLET,
 	LITEST_WACOM_BAMBOO_16FG_PEN,
 	LITEST_WACOM_BAMBOO_2FG_FINGER,
@@ -464,8 +525,8 @@ enum litest_device_type {
 	LITEST_WACOM_CINTIQ_13HDT_FINGER,
 	LITEST_WACOM_CINTIQ_13HDT_PAD,
 	LITEST_WACOM_CINTIQ_13HDT_PEN,
-	LITEST_WACOM_CINTIQ_24HD_PEN,
 	LITEST_WACOM_CINTIQ_24HDT_PAD,
+	LITEST_WACOM_CINTIQ_24HD_PEN,
 	LITEST_WACOM_CINTIQ_PRO16_FINGER,
 	LITEST_WACOM_CINTIQ_PRO16_PAD,
 	LITEST_WACOM_CINTIQ_PRO16_PEN,
@@ -474,9 +535,9 @@ enum litest_device_type {
 	LITEST_WACOM_INTUOS3_PAD,
 	LITEST_WACOM_INTUOS5_PAD,
 	LITEST_WACOM_INTUOS5_PEN,
-	LITEST_WACOM_ISDV4_E6_PEN,
 	LITEST_WACOM_ISDV4_4200_PEN,
 	LITEST_WACOM_ISDV4_524C_PEN,
+	LITEST_WACOM_ISDV4_E6_PEN,
 	LITEST_WACOM_MOBILESTUDIO_PRO_16_PAD,
 	LITEST_WALTOP,
 };
@@ -547,6 +608,11 @@ struct litest_device {
 	struct libinput_device *libinput_device;
 	struct litest_device_interface *interface;
 
+	struct {
+		struct input_event events[64];
+		size_t nevents;
+	} frame;
+
 	int ntouches_down;
 	int skip_ev_syn;
 	struct litest_semi_mt semi_mt; /** only used for semi-mt device */
@@ -565,7 +631,7 @@ struct axis_replacement {
 static inline void
 litest_axis_set_value_unchecked(struct axis_replacement *axes, int code, double value)
 {
-	while (axes->evcode != -1) {
+	while (axes->evcode != -1) { /* NOLINT(clang-analyzer-security.ArrayBound) */
 		if (axes->evcode == code) {
 			axes->value = value;
 			return;
@@ -588,11 +654,31 @@ litest_axis_set_value(struct axis_replac
 	litest_axis_set_value_unchecked(axes, code, value);
 }
 
-struct libinput *litest_create_context(void);
-void litest_destroy_context(struct libinput *li);
-void litest_disable_log_handler(struct libinput *libinput);
-void litest_restore_log_handler(struct libinput *libinput);
-void litest_set_log_handler_bug(struct libinput *libinput);
+struct libinput *
+litest_create_context(void);
+
+/* Adds the given plugin dir to the context but does *not* initialize
+ * the plugins */
+struct libinput *
+litest_create_context_with_plugindir(const char *plugindir);
+
+void
+litest_destroy_context(struct libinput *li);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct libinput *, litest_destroy_context);
+
+#define _litest_context_destroy_ _cleanup_(litest_destroy_contextp)
+
+void
+litest_context_set_user_data(struct libinput *li, void *data);
+void *
+litest_context_get_user_data(struct libinput *li);
+
+void
+litest_disable_log_handler(struct libinput *libinput);
+void
+litest_restore_log_handler(struct libinput *libinput);
+void
+litest_set_log_handler_bug(struct libinput *libinput);
 
 struct litest_parameters;
 
@@ -651,7 +737,9 @@ struct litest_parameters_permutation {
 /**
  * Callback function invoked for each permutation of a struct litest_parameters.
  */
-typedef int (*litest_parameters_permutation_func_t)(struct litest_parameters_permutation *permutation, void *userdata);
+typedef int (*litest_parameters_permutation_func_t)(
+	struct litest_parameters_permutation *permutation,
+	void *userdata);
 
 /**
  * Permutates the given parameters and calls func for every possible
@@ -713,7 +801,7 @@ _litest_add_parametrized(const char *nam
 			 const void *func,
 			 int64_t required,
 			 int64_t excluded,
-			 struct  litest_parameters *params);
+			 struct litest_parameters *params);
 void
 _litest_add_for_device(const char *name,
 		       const char *funcname,
@@ -732,9 +820,7 @@ _litest_add_parametrized_for_device(cons
 				    enum litest_device_type type,
 				    struct litest_parameters *params);
 void
-_litest_add_no_device(const char *name,
-		      const char *funcname,
-		      const void *func);
+_litest_add_no_device(const char *name, const char *funcname, const void *func);
 void
 _litest_add_parametrized_no_device(const char *name,
 				   const char *funcname,
@@ -746,9 +832,7 @@ _litest_add_ranged_no_device(const char
 			     const void *func,
 			     const struct range *range);
 void
-_litest_add_deviceless(const char *name,
-		       const char *funcname,
-		       const void *func);
+_litest_add_deviceless(const char *name, const char *funcname, const void *func);
 void
 _litest_add_parametrized_deviceless(const char *name,
 				    const char *funcname,
@@ -759,8 +843,7 @@ struct litest_device *
 litest_create_device(enum litest_device_type which);
 
 struct litest_device *
-litest_add_device(struct libinput *libinput,
-		  enum litest_device_type which);
+litest_add_device(struct libinput *libinput, enum litest_device_type which);
 struct libevdev_uinput *
 litest_create_uinput_device_from_description(const char *name,
 					     const struct input_id *id,
@@ -797,7 +880,9 @@ void
 litest_ungrab_device(struct litest_device *d);
 
 void
-litest_delete_device(struct litest_device *d);
+litest_device_destroy(struct litest_device *d);
+
+DEFINE_DESTROY_CLEANUP_FUNC(litest_device);
 
 const char *
 litest_event_type_str(enum libinput_event_type type);
@@ -809,24 +894,27 @@ _litest_dispatch(struct libinput *li, co
 	_litest_dispatch(li_, __func__, __LINE__)
 
 void
-litest_event(struct litest_device *t,
-	     unsigned int type,
-	     unsigned int code,
-	     int value);
+litest_event(struct litest_device *t, unsigned int type, unsigned int code, int value);
+
+void
+litest_event_unchecked(struct litest_device *d,
+		       unsigned int type,
+		       unsigned int code,
+		       int value);
+
 int
 litest_auto_assign_value(struct litest_device *d,
 			 const struct input_event *ev,
-			 int slot, double x, double y,
+			 int slot,
+			 double x,
+			 double y,
 			 struct axis_replacement *axes,
 			 bool touching);
 void
 litest_touch_up(struct litest_device *d, unsigned int slot);
 
 void
-litest_touch_move(struct litest_device *d,
-		  unsigned int slot,
-		  double x,
-		  double y);
+litest_touch_move(struct litest_device *d, unsigned int slot, double x, double y);
 
 void
 litest_touch_move_extended(struct litest_device *d,
@@ -845,10 +933,7 @@ litest_touch_sequence(struct litest_devi
 		      int steps);
 
 void
-litest_touch_down(struct litest_device *d,
-		  unsigned int slot,
-		  double x,
-		  double y);
+litest_touch_down(struct litest_device *d, unsigned int slot, double x, double y);
 
 void
 litest_touch_down_extended(struct litest_device *d,
@@ -860,40 +945,51 @@ litest_touch_down_extended(struct litest
 void
 litest_touch_move_to(struct litest_device *d,
 		     unsigned int slot,
-		     double x_from, double y_from,
-		     double x_to, double y_to,
+		     double x_from,
+		     double y_from,
+		     double x_to,
+		     double y_to,
 		     int steps);
 
 void
 litest_touch_move_to_extended(struct litest_device *d,
 			      unsigned int slot,
-			      double x_from, double y_from,
-			      double x_to, double y_to,
+			      double x_from,
+			      double y_from,
+			      double x_to,
+			      double y_to,
 			      struct axis_replacement *axes,
 			      int steps);
 
 void
 litest_touch_move_two_touches(struct litest_device *d,
-			      double x0, double y0,
-			      double x1, double y1,
-			      double dx, double dy,
+			      double x0,
+			      double y0,
+			      double x1,
+			      double y1,
+			      double dx,
+			      double dy,
 			      int steps);
 
 void
 litest_touch_move_three_touches(struct litest_device *d,
-				double x0, double y0,
-				double x1, double y1,
-				double x2, double y2,
-				double dx, double dy,
+				double x0,
+				double y0,
+				double x1,
+				double y1,
+				double x2,
+				double y2,
+				double dx,
+				double dy,
 				int steps);
 
 void
-litest_tablet_set_tool_type(struct litest_device *d,
-			    unsigned int code);
+litest_tablet_set_tool_type(struct litest_device *d, unsigned int code);
 
 void
 litest_tablet_proximity_in(struct litest_device *d,
-			   double x, double y,
+			   double x,
+			   double y,
 			   struct axis_replacement *axes);
 
 void
@@ -901,17 +997,20 @@ litest_tablet_proximity_out(struct lites
 
 void
 litest_tablet_tip_down(struct litest_device *d,
-		       double x, double y,
+		       double x,
+		       double y,
 		       struct axis_replacement *axes);
 
 void
 litest_tablet_tip_up(struct litest_device *d,
-		     double x, double y,
+		     double x,
+		     double y,
 		     struct axis_replacement *axes);
 
 void
 litest_tablet_motion(struct litest_device *d,
-		     double x, double y,
+		     double x,
+		     double y,
 		     struct axis_replacement *axes);
 
 void
@@ -933,31 +1032,31 @@ void
 litest_pad_strip_end(struct litest_device *d);
 
 void
-litest_hover_start(struct litest_device *d,
-		   unsigned int slot,
-		   double x,
-		   double y);
+litest_hover_start(struct litest_device *d, unsigned int slot, double x, double y);
 
 void
 litest_hover_end(struct litest_device *d, unsigned int slot);
 
-void litest_hover_move(struct litest_device *d,
-		       unsigned int slot,
-		       double x,
-		       double y);
+void
+litest_hover_move(struct litest_device *d, unsigned int slot, double x, double y);
 
 void
 litest_hover_move_to(struct litest_device *d,
 		     unsigned int slot,
-		     double x_from, double y_from,
-		     double x_to, double y_to,
+		     double x_from,
+		     double y_from,
+		     double x_to,
+		     double y_to,
 		     int steps);
 
 void
 litest_hover_move_two_touches(struct litest_device *d,
-			      double x0, double y0,
-			      double x1, double y1,
-			      double dx, double dy,
+			      double x0,
+			      double y0,
+			      double x1,
+			      double y1,
+			      double dx,
+			      double dy,
 			      int steps);
 
 void
@@ -967,36 +1066,32 @@ litest_button_click_debounced(struct lit
 			      bool is_press);
 
 void
-litest_button_click(struct litest_device *d,
-		    unsigned int button,
-		    bool is_press);
+litest_button_click(struct litest_device *d, unsigned int button, bool is_press);
 
 void
 litest_button_scroll(struct litest_device *d,
 		     unsigned int button,
-		     double dx, double dy);
+		     double dx,
+		     double dy);
 void
 litest_button_scroll_locked(struct litest_device *d,
 			    unsigned int button,
-			    double dx, double dy);
+			    double dx,
+			    double dy);
 
 void
-litest_keyboard_key(struct litest_device *d,
-		    unsigned int key,
-		    bool is_press);
+litest_keyboard_key(struct litest_device *d, unsigned int key, bool is_press);
 
-void litest_switch_action(struct litest_device *d,
-			  enum libinput_switch sw,
-			  enum libinput_switch_state state);
+void
+litest_switch_action(struct litest_device *d,
+		     enum libinput_switch sw,
+		     enum libinput_switch_state state);
 
 void
 litest_wait_for_event(struct libinput *li);
 
 void
-_litest_wait_for_event_of_type(struct libinput *li,
-			       const char *func,
-			       int lineno,
-			       ...);
+_litest_wait_for_event_of_type(struct libinput *li, const char *func, int lineno, ...);
 
 #define litest_wait_for_event_of_type(li_, ...) \
 	_litest_wait_for_event_of_type(li_, __func__, __LINE__, __VA_ARGS__, -1)
@@ -1039,9 +1134,7 @@ _litest_assert_event_type_not_one_of(str
 	_litest_assert_empty_queue(li_, __func__, __LINE__)
 
 void
-_litest_assert_empty_queue(struct libinput *li,
-			   const char *func,
-			   int line);
+_litest_assert_empty_queue(struct libinput *li, const char *func, int line);
 
 void
 litest_assert_touch_sequence(struct libinput *li);
@@ -1073,8 +1166,7 @@ struct libinput_event_pointer *
 litest_is_motion_event(struct libinput_event *event);
 
 struct libinput_event_touch *
-litest_is_touch_event(struct libinput_event *event,
-		      enum libinput_event_type type);
+litest_is_touch_event(struct libinput_event *event, enum libinput_event_type type);
 
 struct libinput_event_keyboard *
 litest_is_keyboard_event(struct libinput_event *event,
@@ -1087,16 +1179,14 @@ litest_is_gesture_event(struct libinput_
 			int nfingers);
 
 struct libinput_event_tablet_tool *
-litest_is_tablet_event(struct libinput_event *event,
-		       enum libinput_event_type type);
+litest_is_tablet_event(struct libinput_event *event, enum libinput_event_type type);
 
 struct libinput_event_tablet_pad *
 litest_is_pad_button_event(struct libinput_event *event,
 			   unsigned int button,
 			   enum libinput_button_state state);
 struct libinput_event_tablet_pad *
-litest_is_pad_dial_event(struct libinput_event *event,
-			 unsigned int number);
+litest_is_pad_dial_event(struct libinput_event *event, unsigned int number);
 struct libinput_event_tablet_pad *
 litest_is_pad_ring_event(struct libinput_event *event,
 			 unsigned int number,
@@ -1143,7 +1233,8 @@ void
 _litest_assert_button_event(struct libinput *li,
 			    unsigned int button,
 			    enum libinput_button_state state,
-			    const char *func, int line);
+			    const char *func,
+			    int line);
 
 #define litest_assert_switch_event(li_, sw_, state_)  \
 	_litest_assert_switch_event(li_, sw_, state_, __func__, __LINE__)
@@ -1176,12 +1267,10 @@ _litest_assert_only_typed_events(struct
 				 int line);
 
 void
-litest_assert_only_axis_events(struct libinput *li,
-			       enum libinput_event_type axis_type);
+litest_assert_only_axis_events(struct libinput *li, enum libinput_event_type axis_type);
 
 void
-litest_assert_no_typed_events(struct libinput *li,
-			      enum libinput_event_type type);
+litest_assert_no_typed_events(struct libinput *li, enum libinput_event_type type);
 
 #define litest_assert_tablet_button_event(li_, button_, state_) \
 	_litest_assert_tablet_button_event(li_, button_, state_, __func__, __LINE__)
@@ -1193,6 +1282,12 @@ _litest_assert_tablet_button_event(struc
 				   const char *func,
 				   int lineno);
 
+#define litest_assert_tablet_axis_event(li_) \
+	_litest_assert_tablet_axis_event(li_, __func__, __LINE__)
+
+void
+_litest_assert_tablet_axis_event(struct libinput *li, const char *func, int lineno);
+
 #define litest_assert_tablet_proximity_event(li_, state_) \
 	_litest_assert_tablet_proximity_event(li_, state_, __func__, __LINE__)
 
@@ -1242,9 +1337,7 @@ _litest_assert_gesture_event(struct libi
 			     int line);
 
 struct libevdev_uinput *
-litest_create_uinput_device(const char *name,
-			    struct input_id *id,
-			    ...);
+litest_create_uinput_device(const char *name, struct input_id *id, ...);
 
 struct libevdev_uinput *
 litest_create_uinput_abs_device(const char *name,
@@ -1253,64 +1346,69 @@ litest_create_uinput_abs_device(const ch
 				...);
 
 void
-litest_timeout_tap(void);
-
-void
-litest_timeout_tapndrag(void);
-
-void
-litest_timeout_debounce(void);
-
-void
-litest_timeout_softbuttons(void);
-
-void
-litest_timeout_buttonscroll(void);
-
-void
-litest_timeout_wheel_scroll(void);
-
-void
-litest_timeout_edgescroll(void);
-
-void
-litest_timeout_finger_switch(void);
-
-void
-litest_timeout_middlebutton(void);
-
-void
-litest_timeout_dwt_short(void);
+_litest_timeout(struct libinput *li, const char *func, int lineno, int millis);
 
-void
-litest_timeout_dwt_long(void);
+#define litest_timeout(li_, millis) \
+	_litest_timeout(li_, __func__, __LINE__, millis)
 
-void
-litest_timeout_gesture(void);
+#define litest_timeout_tap(li_) litest_timeout(li_, 300)
+#define litest_timeout_tapndrag(li_) litest_timeout(li_, 520)
+#define litest_timeout_debounce(li_) litest_timeout(li_, 30)
+#define litest_timeout_softbuttons(li_) litest_timeout(li_, 300)
+#define litest_timeout_buttonscroll(li_) litest_timeout(li_, 300)
+#define litest_timeout_wheel_scroll(li_) litest_timeout(li_, 600)
+#define litest_timeout_edgescroll(li_) litest_timeout(li_, 300)
+#define litest_timeout_finger_switch(li_) litest_timeout(li_, 140)
+#define litest_timeout_middlebutton(li_) litest_timeout(li_, 70)
+#define litest_timeout_dwt_short(li_) litest_timeout(li_, 220)
+#define litest_timeout_dwt_long(li_) litest_timeout(li_, 520)
+#define litest_timeout_gesture(li_) litest_timeout(li_, 120)
+#define litest_timeout_gesture_scroll(li_) litest_timeout(li_, 180)
+#define litest_timeout_gesture_hold(li_) litest_timeout(li_, 300)
+#define litest_timeout_gesture_quick_hold(li_) litest_timeout(li_, 60)
+#define litest_timeout_trackpoint(li_) litest_timeout(li_, 320)
+#define litest_timeout_tablet_proxout(li_) litest_timeout(li_, 170)
+#define litest_timeout_touch_arbitration(li_) litest_timeout(li_, 100)
+#define litest_timeout_hysteresis(li_) litest_timeout(li_, 90)
+#define litest_timeout_3fg_drag(li_) litest_timeout(li_, 800)
+#define litest_timeout_eraser_button(li_) litest_timeout(li_, 50)
+
+struct litest_logcapture {
+	char **errors;
+	char **infos;
+	char **debugs;
+	char **bugs;
+};
 
 void
-litest_timeout_gesture_scroll(void);
+litest_logcapture_destroy(struct litest_logcapture *c);
 
-void
-litest_timeout_gesture_hold(void);
+DEFINE_DESTROY_CLEANUP_FUNC(litest_logcapture);
 
-void
-litest_timeout_gesture_quick_hold(void);
+struct litest_logcapture *
+litest_logcapture_setup(struct libinput *li);
 
-void
-litest_timeout_trackpoint(void);
+struct litest_logcapture *
+litest_logcapture_remove(struct libinput *li, struct litest_logcapture *capture);
 
-void
-litest_timeout_tablet_proxout(void);
+#define litest_with_logcapture(li_, capture_) \
+	for (struct litest_logcapture *capture_ = litest_logcapture_setup(li_); \
+	     capture_ != NULL; \
+	     capture_ = litest_logcapture_remove(li_, capture_))
 
 void
-litest_timeout_touch_arbitration(void);
+_litest_assert_logcapture_no_errors(struct litest_logcapture *c,
+				    const char *file,
+				    const char *func,
+				    int lineno);
 
-void
-litest_timeout_hysteresis(void);
+#define litest_assert_logcapture_no_errors(c_) \
+	_litest_assert_logcapture_no_errors(c_, __FILE__, __func__, __LINE__)
 
-void
-litest_timeout_3fg_drag(void);
+#define litest_with_event_frame(dev_) \
+	for (bool _i = ({litest_push_event_frame(dev_); true; }); \
+	     _i; \
+	     ({ litest_pop_event_frame(dev_); _i = false; }))
 
 void
 litest_push_event_frame(struct litest_device *dev);
@@ -1319,34 +1417,31 @@ void
 litest_pop_event_frame(struct litest_device *dev);
 
 void
-litest_filter_event(struct litest_device *dev,
-		    unsigned int type,
-		    unsigned int code);
+litest_filter_event(struct litest_device *dev, unsigned int type, unsigned int code);
 
 void
-litest_unfilter_event(struct litest_device *dev,
-		      unsigned int type,
-		      unsigned int code);
+litest_unfilter_event(struct litest_device *dev, unsigned int type, unsigned int code);
 void
 litest_semi_mt_touch_down(struct litest_device *d,
 			  struct litest_semi_mt *semi_mt,
 			  unsigned int slot,
-			  double x, double y);
+			  double x,
+			  double y);
 
 void
 litest_semi_mt_touch_move(struct litest_device *d,
 			  struct litest_semi_mt *semi_mt,
 			  unsigned int slot,
-			  double x, double y);
+			  double x,
+			  double y);
 
 void
 litest_semi_mt_touch_up(struct litest_device *d,
 			struct litest_semi_mt *semi_mt,
 			unsigned int slot);
 
-static inline
-void litest_enable_3fg_drag(struct libinput_device *device,
-			    unsigned int nfingers)
+static inline void
+litest_enable_3fg_drag(struct libinput_device *device, unsigned int nfingers)
 {
 	enum libinput_config_3fg_drag_state enabled;
 
@@ -1408,8 +1503,9 @@ litest_enable_tap_drag(struct libinput_d
 	enum libinput_config_status status, expected;
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
-	status = libinput_device_config_tap_set_drag_enabled(device,
-							     LIBINPUT_CONFIG_DRAG_ENABLED);
+	status = libinput_device_config_tap_set_drag_enabled(
+		device,
+		LIBINPUT_CONFIG_DRAG_ENABLED);
 
 	litest_assert_int_eq(status, expected);
 }
@@ -1420,8 +1516,9 @@ litest_disable_tap_drag(struct libinput_
 	enum libinput_config_status status, expected;
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
-	status = libinput_device_config_tap_set_drag_enabled(device,
-							     LIBINPUT_CONFIG_DRAG_DISABLED);
+	status = libinput_device_config_tap_set_drag_enabled(
+		device,
+		LIBINPUT_CONFIG_DRAG_DISABLED);
 
 	litest_assert_int_eq(status, expected);
 }
@@ -1442,7 +1539,7 @@ litest_enable_2fg_scroll(struct litest_d
 	struct libinput_device *device = dev->libinput_device;
 
 	status = libinput_device_config_scroll_set_method(device,
-					  LIBINPUT_CONFIG_SCROLL_2FG);
+							  LIBINPUT_CONFIG_SCROLL_2FG);
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
 	litest_assert_int_eq(status, expected);
@@ -1457,7 +1554,7 @@ litest_enable_edge_scroll(struct litest_
 	struct libinput_device *device = dev->libinput_device;
 
 	status = libinput_device_config_scroll_set_method(device,
-					  LIBINPUT_CONFIG_SCROLL_EDGE);
+							  LIBINPUT_CONFIG_SCROLL_EDGE);
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
 	litest_assert_int_eq(status, expected);
@@ -1489,8 +1586,9 @@ litest_enable_clickfinger(struct litest_
 	enum libinput_config_status status, expected;
 	struct libinput_device *device = dev->libinput_device;
 
-	status = libinput_device_config_click_set_method(device,
-				 LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
+	status = libinput_device_config_click_set_method(
+		device,
+		LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
 	litest_assert_int_eq(status, expected);
 }
@@ -1513,8 +1611,9 @@ litest_enable_buttonareas(struct litest_
 	enum libinput_config_status status, expected;
 	struct libinput_device *device = dev->libinput_device;
 
-	status = libinput_device_config_click_set_method(device,
-				 LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS);
+	status = libinput_device_config_click_set_method(
+		device,
+		LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS);
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
 	litest_assert_int_eq(status, expected);
 }
@@ -1525,8 +1624,9 @@ litest_enable_drag_lock_sticky(struct li
 	enum libinput_config_status status, expected;
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
-	status = libinput_device_config_tap_set_drag_lock_enabled(device,
-								  LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY);
+	status = libinput_device_config_tap_set_drag_lock_enabled(
+		device,
+		LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY);
 
 	litest_assert_int_eq(status, expected);
 }
@@ -1537,8 +1637,9 @@ litest_enable_drag_lock(struct libinput_
 	enum libinput_config_status status, expected;
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
-	status = libinput_device_config_tap_set_drag_lock_enabled(device,
-								  LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
+	status = libinput_device_config_tap_set_drag_lock_enabled(
+		device,
+		LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
 
 	litest_assert_int_eq(status, expected);
 }
@@ -1549,8 +1650,9 @@ litest_disable_drag_lock(struct libinput
 	enum libinput_config_status status, expected;
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
-	status = libinput_device_config_tap_set_drag_lock_enabled(device,
-								  LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
+	status = libinput_device_config_tap_set_drag_lock_enabled(
+		device,
+		LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
 
 	litest_assert_int_eq(status, expected);
 }
@@ -1562,8 +1664,9 @@ litest_enable_middleemu(struct litest_de
 	enum libinput_config_status status, expected;
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
-	status = libinput_device_config_middle_emulation_set_enabled(device,
-								     LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+	status = libinput_device_config_middle_emulation_set_enabled(
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 
 	litest_assert_int_eq(status, expected);
 }
@@ -1575,8 +1678,9 @@ litest_disable_middleemu(struct litest_d
 	enum libinput_config_status status, expected;
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
-	status = libinput_device_config_middle_emulation_set_enabled(device,
-								     LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
+	status = libinput_device_config_middle_emulation_set_enabled(
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
 
 	litest_assert_int_eq(status, expected);
 }
@@ -1588,8 +1692,9 @@ litest_sendevents_off(struct litest_devi
 	enum libinput_config_status status, expected;
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
-	status = libinput_device_config_send_events_set_mode(device,
-				    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_int_eq(status, expected);
 }
 
@@ -1600,8 +1705,9 @@ litest_sendevents_on(struct litest_devic
 	enum libinput_config_status status, expected;
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
-	status = libinput_device_config_send_events_set_mode(device,
-				    LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 	litest_assert_int_eq(status, expected);
 }
 
@@ -1612,8 +1718,9 @@ litest_sendevents_ext_mouse(struct lites
 	enum libinput_config_status status, expected;
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
-	status = libinput_device_config_send_events_set_mode(device,
-				    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE);
 	litest_assert_int_eq(status, expected);
 }
 
@@ -1623,8 +1730,9 @@ litest_enable_hold_gestures(struct libin
 	enum libinput_config_status status, expected;
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
-	status = libinput_device_config_gesture_set_hold_enabled(device,
-								 LIBINPUT_CONFIG_HOLD_ENABLED);
+	status = libinput_device_config_gesture_set_hold_enabled(
+		device,
+		LIBINPUT_CONFIG_HOLD_ENABLED);
 
 	litest_assert_int_eq(status, expected);
 }
@@ -1635,8 +1743,9 @@ litest_disable_hold_gestures(struct libi
 	enum libinput_config_status status, expected;
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
-	status = libinput_device_config_gesture_set_hold_enabled(device,
-								 LIBINPUT_CONFIG_HOLD_DISABLED);
+	status = libinput_device_config_gesture_set_hold_enabled(
+		device,
+		LIBINPUT_CONFIG_HOLD_DISABLED);
 
 	litest_assert_int_eq(status, expected);
 }
diff -pruN 1.28.1-1/test/test-builddir-lookup.c 1.30.0-1/test/test-builddir-lookup.c
--- 1.28.1-1/test/test-builddir-lookup.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-builddir-lookup.c	2025-11-25 03:40:43.000000000 +0000
@@ -22,27 +22,31 @@
  */
 
 #include "config.h"
-#include "libinput-util.h"
+
 #include "builddir.h"
+#include "libinput-util.h"
 
 int
 main(int argc, char **argv)
 {
-	char *builddir;
+	char *builddir = NULL;
 	char *mode;
 
 	assert(argc == 2);
 	mode = argv[1];
 
-	builddir = builddir_lookup();
+	bool is_builddir = builddir_lookup(&builddir);
 	if (streq(mode, "--builddir-is-null")) {
+		assert(!is_builddir);
 		assert(builddir == NULL);
 	} else if (streq(mode, "--builddir-is-set")) {
 		/* In the case of release builds, the builddir is
 		   the empty string */
 		if (streq(MESON_BUILD_ROOT, "")) {
+			assert(!is_builddir);
 			assert(builddir == NULL);
 		} else {
+			assert(is_builddir);
 			assert(builddir);
 			assert(streq(MESON_BUILD_ROOT, builddir));
 		}
diff -pruN 1.28.1-1/test/test-device.c 1.30.0-1/test/test-device.c
--- 1.28.1-1/test/test-device.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-device.c	2025-11-25 03:40:43.000000000 +0000
@@ -29,8 +29,8 @@
 #include <libudev.h>
 #include <unistd.h>
 
-#include "litest.h"
 #include "libinput-util.h"
+#include "litest.h"
 
 START_TEST(device_sendevents_config)
 {
@@ -41,8 +41,7 @@ START_TEST(device_sendevents_config)
 	device = dev->libinput_device;
 
 	modes = libinput_device_config_send_events_get_modes(device);
-	litest_assert_int_eq(modes,
-			 (uint32_t)LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	litest_assert_int_eq(modes, (uint32_t)LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 }
 END_TEST
 
@@ -54,8 +53,9 @@ START_TEST(device_sendevents_config_inva
 
 	device = dev->libinput_device;
 
-	status = libinput_device_config_send_events_set_mode(device,
-			     LIBINPUT_CONFIG_SEND_EVENTS_DISABLED | bit(4));
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED | bit(4));
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 }
 END_TEST
@@ -71,8 +71,7 @@ START_TEST(device_sendevents_config_touc
 	/* The wacom devices in the test suite are external */
 	if (libevdev_get_id_vendor(dev->evdev) != VENDOR_ID_WACOM &&
 	    !litest_touchpad_is_external(dev))
-		expected |=
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
+		expected |= LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
 
 	device = dev->libinput_device;
 
@@ -98,8 +97,7 @@ START_TEST(device_sendevents_config_touc
 	modes = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED |
 		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
 
-	status = libinput_device_config_send_events_set_mode(device,
-							     modes);
+	status = libinput_device_config_send_events_set_mode(device, modes);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	/* DISABLED supersedes the rest, expect the rest to be dropped */
@@ -117,12 +115,10 @@ START_TEST(device_sendevents_config_defa
 	device = dev->libinput_device;
 
 	mode = libinput_device_config_send_events_get_mode(device);
-	litest_assert_enum_eq(mode,
-			 LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+	litest_assert_enum_eq(mode, LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 
 	mode = libinput_device_config_send_events_get_default_mode(device);
-	litest_assert_enum_eq(mode,
-			 LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+	litest_assert_enum_eq(mode, LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 }
 END_TEST
 
@@ -139,8 +135,9 @@ START_TEST(device_disable)
 
 	litest_drain_events(li);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	/* no event from disabling */
@@ -158,8 +155,9 @@ START_TEST(device_disable)
 	litest_drain_events(li);
 
 	/* no event from resuming */
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	litest_assert_empty_queue(li);
 
@@ -171,10 +169,10 @@ START_TEST(device_disable)
 	event = libinput_get_event(li);
 	litest_assert_notnull(event);
 	litest_assert_enum_eq(libinput_event_get_type(event),
-			 LIBINPUT_EVENT_POINTER_MOTION);
+			      LIBINPUT_EVENT_POINTER_MOTION);
 	libinput_event_destroy(event);
 
-	litest_delete_device(tmp);
+	litest_device_destroy(tmp);
 }
 END_TEST
 
@@ -187,15 +185,16 @@ START_TEST(device_disable_tablet)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	device = dev->libinput_device;
 
 	litest_drain_events(li);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	/* no event from disabling */
@@ -211,8 +210,9 @@ START_TEST(device_disable_tablet)
 	litest_assert_empty_queue(li);
 
 	/* no event from resuming */
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	litest_assert_empty_queue(li);
 }
@@ -229,8 +229,9 @@ START_TEST(device_disable_touchpad)
 
 	litest_drain_events(li);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	/* no event from disabling */
@@ -243,8 +244,9 @@ START_TEST(device_disable_touchpad)
 	litest_assert_empty_queue(li);
 
 	/* no event from resuming */
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	litest_assert_empty_queue(li);
 }
@@ -261,8 +263,9 @@ START_TEST(device_disable_touch)
 
 	litest_drain_events(li);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	/* no event from disabling */
@@ -275,8 +278,9 @@ START_TEST(device_disable_touch)
 	litest_assert_empty_queue(li);
 
 	/* no event from resuming */
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	litest_assert_empty_queue(li);
 }
@@ -296,8 +300,9 @@ START_TEST(device_disable_touch_during_t
 	litest_touch_move_to(dev, 0, 50, 50, 90, 90, 10);
 	litest_drain_events(li);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	/* after disabling sendevents we require a touch up */
@@ -322,8 +327,9 @@ START_TEST(device_disable_touch_during_t
 	litest_assert_empty_queue(li);
 
 	/* no event from resuming */
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	litest_assert_empty_queue(li);
 }
@@ -350,17 +356,18 @@ START_TEST(device_disable_events_pending
 	}
 	litest_dispatch(li);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	/* expect above events */
 	litest_wait_for_event(li);
 	while ((event = libinput_get_event(li)) != NULL) {
-	       litest_assert_enum_eq(libinput_event_get_type(event),
-				LIBINPUT_EVENT_POINTER_MOTION);
-	       libinput_event_destroy(event);
-       }
+		litest_assert_enum_eq(libinput_event_get_type(event),
+				      LIBINPUT_EVENT_POINTER_MOTION);
+		libinput_event_destroy(event);
+	}
 }
 END_TEST
 
@@ -375,12 +382,14 @@ START_TEST(device_double_disable)
 
 	litest_drain_events(li);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_assert_empty_queue(li);
@@ -398,12 +407,14 @@ START_TEST(device_double_enable)
 
 	litest_drain_events(li);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_assert_empty_queue(li);
@@ -412,30 +423,31 @@ END_TEST
 
 START_TEST(device_reenable_syspath_changed)
 {
-	struct libinput *li;
 	struct litest_device *litest_device;
 	struct libinput_device *device1;
 	enum libinput_config_status status;
 	struct libinput_event *event;
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	litest_device = litest_add_device(li, LITEST_MOUSE);
 	device1 = litest_device->libinput_device;
 
 	libinput_device_ref(device1);
-	status = libinput_device_config_send_events_set_mode(device1,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device1,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_drain_events(li);
 
-	litest_delete_device(litest_device);
+	litest_device_destroy(litest_device);
 	litest_drain_events(li);
 
 	litest_device = litest_add_device(li, LITEST_MOUSE);
 
-	status = libinput_device_config_send_events_set_mode(device1,
-			LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device1,
+		LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	/* can't really check for much here, other than that if we pump
@@ -451,35 +463,35 @@ START_TEST(device_reenable_syspath_chang
 		libinput_event_destroy(event);
 	}
 
-	litest_delete_device(litest_device);
+	litest_device_destroy(litest_device);
 	libinput_device_unref(device1);
-	litest_destroy_context(li);
 }
 END_TEST
 
 START_TEST(device_reenable_device_removed)
 {
-	struct libinput *li;
 	struct litest_device *litest_device;
 	struct libinput_device *device;
 	enum libinput_config_status status;
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	litest_device = litest_add_device(li, LITEST_MOUSE);
 	device = litest_device->libinput_device;
 
 	libinput_device_ref(device);
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_drain_events(li);
 
-	litest_delete_device(litest_device);
+	litest_device_destroy(litest_device);
 	litest_drain_events(li);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	/* can't really check for much here, this really just exercises the
@@ -487,7 +499,6 @@ START_TEST(device_reenable_device_remove
 	litest_assert_empty_queue(li);
 
 	libinput_device_unref(device);
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -505,20 +516,21 @@ START_TEST(device_disable_release_button
 	litest_button_click_debounced(dev, li, BTN_LEFT, true);
 	litest_drain_events(li);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_wait_for_event(li);
 	event = libinput_get_event(li);
 
 	litest_assert_enum_eq(libinput_event_get_type(event),
-			 LIBINPUT_EVENT_POINTER_BUTTON);
+			      LIBINPUT_EVENT_POINTER_BUTTON);
 	ptrevent = libinput_event_get_pointer_event(event);
 	litest_assert_int_eq(libinput_event_pointer_get_button(ptrevent),
 			     (unsigned int)BTN_LEFT);
 	litest_assert_enum_eq(libinput_event_pointer_get_button_state(ptrevent),
-			 LIBINPUT_BUTTON_STATE_RELEASED);
+			      LIBINPUT_BUTTON_STATE_RELEASED);
 
 	libinput_event_destroy(event);
 	litest_assert_empty_queue(li);
@@ -539,20 +551,21 @@ START_TEST(device_disable_release_keys)
 	litest_keyboard_key(dev, KEY_A, true);
 	litest_drain_events(li);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_wait_for_event(li);
 	event = libinput_get_event(li);
 
 	litest_assert_enum_eq(libinput_event_get_type(event),
-			 LIBINPUT_EVENT_KEYBOARD_KEY);
+			      LIBINPUT_EVENT_KEYBOARD_KEY);
 	kbdevent = libinput_event_get_keyboard_event(event);
 	litest_assert_int_eq(libinput_event_keyboard_get_key(kbdevent),
 			     (unsigned int)KEY_A);
 	litest_assert_enum_eq(libinput_event_keyboard_get_key_state(kbdevent),
-			 LIBINPUT_KEY_STATE_RELEASED);
+			      LIBINPUT_KEY_STATE_RELEASED);
 
 	libinput_event_destroy(event);
 	litest_assert_empty_queue(li);
@@ -568,8 +581,7 @@ START_TEST(device_disable_release_tap)
 
 	device = dev->libinput_device;
 
-	libinput_device_config_tap_set_enabled(device,
-					       LIBINPUT_CONFIG_TAP_ENABLED);
+	libinput_device_config_tap_set_enabled(device, LIBINPUT_CONFIG_TAP_ENABLED);
 
 	litest_drain_events(li);
 
@@ -578,29 +590,26 @@ START_TEST(device_disable_release_tap)
 
 	litest_dispatch(li);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	/* tap happened before suspending, so we still expect the event */
 
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 
 	/* resume, make sure we don't get anything */
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
-
 }
 END_TEST
 
@@ -613,31 +622,25 @@ START_TEST(device_disable_release_tap_n_
 
 	device = dev->libinput_device;
 
-	libinput_device_config_tap_set_enabled(device,
-					       LIBINPUT_CONFIG_TAP_ENABLED);
+	libinput_device_config_tap_set_enabled(device, LIBINPUT_CONFIG_TAP_ENABLED);
 
 	litest_drain_events(li);
 
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_up(dev, 0);
 	litest_touch_down(dev, 0, 50, 50);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_dispatch(li);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -658,17 +661,14 @@ START_TEST(device_disable_release_softbu
 	litest_button_click_debounced(dev, li, BTN_LEFT, true);
 
 	/* make sure softbutton works */
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 	/* disable */
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 
@@ -678,12 +678,12 @@ START_TEST(device_disable_release_softbu
 	litest_assert_empty_queue(li);
 
 	/* resume, make sure we don't get anything */
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
-
 }
 END_TEST
 
@@ -702,8 +702,9 @@ START_TEST(device_disable_topsoftbutton)
 
 	trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
 
-	status = libinput_device_config_send_events_set_mode(device,
-			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	status = libinput_device_config_send_events_set_mode(
+		device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	litest_drain_events(li);
 
@@ -715,31 +716,31 @@ START_TEST(device_disable_topsoftbutton)
 	litest_wait_for_event(li);
 	event = libinput_get_event(li);
 	litest_assert_enum_eq(libinput_event_get_type(event),
-			 LIBINPUT_EVENT_POINTER_BUTTON);
+			      LIBINPUT_EVENT_POINTER_BUTTON);
 	litest_assert_ptr_eq(libinput_event_get_device(event),
-			 trackpoint->libinput_device);
+			     trackpoint->libinput_device);
 	ptrevent = libinput_event_get_pointer_event(event);
 	litest_assert_int_eq(libinput_event_pointer_get_button(ptrevent),
 			     (unsigned int)BTN_RIGHT);
 	litest_assert_enum_eq(libinput_event_pointer_get_button_state(ptrevent),
-			 LIBINPUT_BUTTON_STATE_PRESSED);
+			      LIBINPUT_BUTTON_STATE_PRESSED);
 	libinput_event_destroy(event);
 
 	event = libinput_get_event(li);
 	litest_assert_enum_eq(libinput_event_get_type(event),
-			 LIBINPUT_EVENT_POINTER_BUTTON);
+			      LIBINPUT_EVENT_POINTER_BUTTON);
 	litest_assert_ptr_eq(libinput_event_get_device(event),
-			 trackpoint->libinput_device);
+			     trackpoint->libinput_device);
 	ptrevent = libinput_event_get_pointer_event(event);
 	litest_assert_int_eq(libinput_event_pointer_get_button(ptrevent),
 			     (unsigned int)BTN_RIGHT);
 	litest_assert_enum_eq(libinput_event_pointer_get_button_state(ptrevent),
-			 LIBINPUT_BUTTON_STATE_RELEASED);
+			      LIBINPUT_BUTTON_STATE_RELEASED);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(trackpoint);
+	litest_device_destroy(trackpoint);
 }
 END_TEST
 
@@ -754,14 +755,10 @@ START_TEST(device_ids)
 	pid = libevdev_get_id_product(dev->evdev);
 	vid = libevdev_get_id_vendor(dev->evdev);
 
-	litest_assert_str_eq(name,
-			 libinput_device_get_name(dev->libinput_device));
-	litest_assert_int_eq(bus,
-			 libinput_device_get_id_bustype(dev->libinput_device));
-	litest_assert_int_eq(pid,
-			 libinput_device_get_id_product(dev->libinput_device));
-	litest_assert_int_eq(vid,
-			 libinput_device_get_id_vendor(dev->libinput_device));
+	litest_assert_str_eq(name, libinput_device_get_name(dev->libinput_device));
+	litest_assert_int_eq(bus, libinput_device_get_id_bustype(dev->libinput_device));
+	litest_assert_int_eq(pid, libinput_device_get_id_product(dev->libinput_device));
+	litest_assert_int_eq(vid, libinput_device_get_id_vendor(dev->libinput_device));
 }
 END_TEST
 
@@ -781,7 +778,8 @@ START_TEST(device_context)
 	struct litest_device *dev = litest_current_device();
 	struct libinput_seat *seat;
 
-	litest_assert(dev->libinput == libinput_device_get_context(dev->libinput_device));
+	litest_assert(dev->libinput ==
+		      libinput_device_get_context(dev->libinput_device));
 	seat = libinput_device_get_seat(dev->libinput_device);
 	litest_assert(dev->libinput == libinput_seat_get_context(seat));
 }
@@ -814,8 +812,7 @@ START_TEST(device_group_get)
 	libinput_device_group_ref(group);
 
 	libinput_device_group_set_user_data(group, &userdata);
-	litest_assert_ptr_eq(&userdata,
-			 libinput_device_group_get_user_data(group));
+	litest_assert_ptr_eq(&userdata, libinput_device_group_get_user_data(group));
 
 	libinput_device_group_unref(group);
 }
@@ -823,9 +820,8 @@ END_TEST
 
 START_TEST(device_group_ref)
 {
-	struct libinput *li = litest_create_context();
-	struct litest_device *dev = litest_add_device(li,
-						      LITEST_MOUSE);
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	struct litest_device *dev = litest_add_device(li, LITEST_MOUSE);
 	struct libinput_device *device = dev->libinput_device;
 	struct libinput_device_group *group;
 
@@ -835,7 +831,7 @@ START_TEST(device_group_ref)
 
 	libinput_device_ref(device);
 	litest_drain_events(li);
-	litest_delete_device(dev);
+	litest_device_destroy(dev);
 	litest_drain_events(li);
 
 	/* make sure the device is dead but the group is still around */
@@ -844,28 +840,26 @@ START_TEST(device_group_ref)
 	libinput_device_group_ref(group);
 	litest_assert_notnull(libinput_device_group_unref(group));
 	litest_assert(libinput_device_group_unref(group) == NULL);
-
-	litest_destroy_context(li);
 }
 END_TEST
 
 START_TEST(device_group_leak)
 {
-	struct libinput *li;
 	struct libinput_device *device;
 	struct libevdev_uinput *uinput;
 	struct libinput_device_group *group;
 
+	/* clang-format off */
 	uinput = litest_create_uinput_device("test device", NULL,
 					     EV_KEY, BTN_LEFT,
 					     EV_KEY, BTN_RIGHT,
 					     EV_REL, REL_X,
 					     EV_REL, REL_Y,
 					     -1);
+	/* clang-format on */
 
-	li = litest_create_context();
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 
 	group = libinput_device_get_device_group(device);
 	libinput_device_group_ref(group);
@@ -873,105 +867,56 @@ START_TEST(device_group_leak)
 	libinput_path_remove_device(device);
 
 	libevdev_uinput_destroy(uinput);
-	litest_destroy_context(li);
 
 	/* the device group leaks, check valgrind */
 }
 END_TEST
 
-START_TEST(abs_device_no_absx)
+START_TEST(abs_device_missing_one_abs)
 {
 	struct libevdev_uinput *uinput;
-	struct libinput *li;
 	struct libinput_device *device;
 
-	uinput = litest_create_uinput_device("test device", NULL,
-					     EV_KEY, BTN_LEFT,
-					     EV_KEY, BTN_RIGHT,
-					     EV_ABS, ABS_Y,
-					     -1);
-	li = litest_create_context();
-	litest_disable_log_handler(li);
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
-	litest_restore_log_handler(li);
-	litest_assert(device == NULL);
-	litest_destroy_context(li);
-
-	libevdev_uinput_destroy(uinput);
-}
-END_TEST
-
-START_TEST(abs_device_no_absy)
-{
-	struct libevdev_uinput *uinput;
-	struct libinput *li;
-	struct libinput_device *device;
-
-	uinput = litest_create_uinput_device("test device", NULL,
-					     EV_KEY, BTN_LEFT,
-					     EV_KEY, BTN_RIGHT,
-					     EV_ABS, ABS_X,
-					     -1);
-	li = litest_create_context();
-	litest_disable_log_handler(li);
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
-	litest_restore_log_handler(li);
-	litest_assert(device == NULL);
-	litest_destroy_context(li);
-
-	libevdev_uinput_destroy(uinput);
-}
-END_TEST
-
-START_TEST(abs_mt_device_no_absy)
-{
-	struct libevdev_uinput *uinput;
-	struct libinput *li;
-	struct libinput_device *device;
+	int axis = litest_test_param_get_i32(test_env->params, "axis");
 
+	/* clang-format off */
 	uinput = litest_create_uinput_device("test device", NULL,
 					     EV_KEY, BTN_LEFT,
 					     EV_KEY, BTN_RIGHT,
-					     EV_ABS, ABS_X,
-					     EV_ABS, ABS_Y,
-					     EV_ABS, ABS_MT_SLOT,
-					     EV_ABS, ABS_MT_POSITION_X,
+					     EV_ABS, axis,
 					     -1);
-	li = litest_create_context();
+	/* clang-format on */
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	litest_disable_log_handler(li);
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_restore_log_handler(li);
 	litest_assert(device == NULL);
-	litest_destroy_context(li);
 
 	libevdev_uinput_destroy(uinput);
 }
 END_TEST
 
-START_TEST(abs_mt_device_no_absx)
+START_TEST(abs_mt_device_missing_one_mt_abs)
 {
 	struct libevdev_uinput *uinput;
-	struct libinput *li;
 	struct libinput_device *device;
+	int axis = litest_test_param_get_i32(test_env->params, "axis");
 
+	/* clang-format off */
 	uinput = litest_create_uinput_device("test device", NULL,
 					     EV_KEY, BTN_LEFT,
 					     EV_KEY, BTN_RIGHT,
 					     EV_ABS, ABS_X,
 					     EV_ABS, ABS_Y,
 					     EV_ABS, ABS_MT_SLOT,
-					     EV_ABS, ABS_MT_POSITION_Y,
+					     EV_ABS, axis,
 					     -1);
-	li = litest_create_context();
+	/* clang-format on */
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	litest_disable_log_handler(li);
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_restore_log_handler(li);
 	litest_assert(device == NULL);
-	litest_destroy_context(li);
 
 	libevdev_uinput_destroy(uinput);
 }
@@ -983,42 +928,40 @@ assert_device_ignored(struct libinput *l
 	struct libevdev_uinput *uinput;
 	struct libinput_device *device;
 
+	/* clang-format off */
 	uinput = litest_create_uinput_abs_device("test device", NULL,
 						 absinfo,
 						 EV_KEY, BTN_LEFT,
 						 EV_KEY, BTN_RIGHT,
 						 -1);
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	/* clang-format on */
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_assert_ptr_null(device);
 	libevdev_uinput_destroy(uinput);
 }
 
 START_TEST(abs_device_no_range)
 {
-	struct libinput *li;
 	int code = _i; /* looped test */
 	/* set x/y so libinput doesn't just reject for missing axes */
 	struct input_absinfo absinfo[] = {
 		{ ABS_X, 0, 10, 0, 0, 0 },
 		{ ABS_Y, 0, 10, 0, 0, 0 },
 		{ code, 0, 0, 0, 0, 0 },
-		{ -1, -1, -1, -1, -1, -1 }
+		{ -1, -1, -1, -1, -1, -1 },
 	};
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	litest_disable_log_handler(li);
 
 	assert_device_ignored(li, absinfo);
 
 	litest_restore_log_handler(li);
-	litest_destroy_context(li);
 }
 END_TEST
 
 START_TEST(abs_mt_device_no_range)
 {
-	struct libinput *li;
 	int code = _i; /* looped test */
 	/* set x/y so libinput doesn't just reject for missing axes */
 	struct input_absinfo absinfo[] = {
@@ -1029,10 +972,10 @@ START_TEST(abs_mt_device_no_range)
 		{ ABS_MT_POSITION_X, 0, 10, 0, 0, 0 },
 		{ ABS_MT_POSITION_Y, 0, 10, 0, 0, 0 },
 		{ code, 0, 0, 0, 0, 0 },
-		{ -1, -1, -1, -1, -1, -1 }
+		{ -1, -1, -1, -1, -1, -1 },
 	};
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	litest_disable_log_handler(li);
 
 	if (code != ABS_MT_TOOL_TYPE &&
@@ -1040,92 +983,97 @@ START_TEST(abs_mt_device_no_range)
 		assert_device_ignored(li, absinfo);
 
 	litest_restore_log_handler(li);
-	litest_destroy_context(li);
 }
 END_TEST
 
 START_TEST(abs_device_missing_res)
 {
-	struct libinput *li;
 	struct input_absinfo absinfo[] = {
 		{ ABS_X, 0, 10, 0, 0, 10 },
-		{ ABS_Y, 0, 10, 0, 0, 0 },
-		{ -1, -1, -1, -1, -1, -1 }
+		{ ABS_Y, 0, 10, 0, 0, 10 },
+		{ -1, -1, -1, -1, -1, -1 },
 	};
+	int axis = litest_test_param_get_i32(test_env->params, "axis");
 
-	li = litest_create_context();
-	litest_disable_log_handler(li);
-
-	assert_device_ignored(li, absinfo);
+	switch (axis) {
+	case ABS_X:
+		absinfo[0].resolution = 0;
+		break;
+	case ABS_Y:
+		absinfo[1].resolution = 0;
+		break;
+	default:
+		litest_abort_msg("Invalid test parameter %d", axis);
+	}
 
-	absinfo[0].resolution = 0;
-	absinfo[1].resolution = 20;
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	litest_disable_log_handler(li);
 
 	assert_device_ignored(li, absinfo);
 
 	litest_restore_log_handler(li);
-	litest_destroy_context(li);
 }
 END_TEST
 
 START_TEST(abs_mt_device_missing_res)
 {
-	struct libinput *li;
 	struct input_absinfo absinfo[] = {
 		{ ABS_X, 0, 10, 0, 0, 10 },
 		{ ABS_Y, 0, 10, 0, 0, 10 },
 		{ ABS_MT_SLOT, 0, 2, 0, 0, 0 },
 		{ ABS_MT_TRACKING_ID, 0, 255, 0, 0, 0 },
 		{ ABS_MT_POSITION_X, 0, 10, 0, 0, 10 },
-		{ ABS_MT_POSITION_Y, 0, 10, 0, 0, 0 },
-		{ -1, -1, -1, -1, -1, -1 }
+		{ ABS_MT_POSITION_Y, 0, 10, 0, 0, 10 },
+		{ -1, -1, -1, -1, -1, -1 },
 	};
 
-	li = litest_create_context();
-	litest_disable_log_handler(li);
-	assert_device_ignored(li, absinfo);
+	int axis = litest_test_param_get_i32(test_env->params, "axis");
 
-	absinfo[4].resolution = 0;
-	absinfo[5].resolution = 20;
+	switch (axis) {
+	case ABS_MT_POSITION_X:
+		absinfo[4].resolution = 0;
+		break;
+	case ABS_MT_POSITION_Y:
+		absinfo[5].resolution = 0;
+		break;
+	default:
+		litest_abort_msg("Invalid test parameter %d", axis);
+	}
 
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	litest_disable_log_handler(li);
 	assert_device_ignored(li, absinfo);
 
 	litest_restore_log_handler(li);
-	litest_destroy_context(li);
-
 }
 END_TEST
 
 START_TEST(ignore_joystick)
 {
-	struct libinput *li;
 	struct libevdev_uinput *uinput;
 	struct libinput_device *device;
 	struct input_absinfo absinfo[] = {
-		{ ABS_X, 0, 10, 0, 0, 10 },
-		{ ABS_Y, 0, 10, 0, 0, 10 },
-		{ ABS_RX, 0, 10, 0, 0, 10 },
-		{ ABS_RY, 0, 10, 0, 0, 10 },
-		{ ABS_THROTTLE, 0, 2, 0, 0, 0 },
-		{ ABS_RUDDER, 0, 255, 0, 0, 0 },
-		{ -1, -1, -1, -1, -1, -1 }
+		{ ABS_X, 0, 10, 0, 0, 10 },      { ABS_Y, 0, 10, 0, 0, 10 },
+		{ ABS_RX, 0, 10, 0, 0, 10 },     { ABS_RY, 0, 10, 0, 0, 10 },
+		{ ABS_THROTTLE, 0, 2, 0, 0, 0 }, { ABS_RUDDER, 0, 255, 0, 0, 0 },
+		{ -1, -1, -1, -1, -1, -1 },
 	};
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	litest_disable_log_handler(li);
 	litest_drain_events(li);
 
+	/* clang-format off */
 	uinput = litest_create_uinput_abs_device("joystick test device", NULL,
 						 absinfo,
 						 EV_KEY, BTN_TRIGGER,
 						 EV_KEY, BTN_A,
 						 -1);
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	/* clang-format on */
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_assert_ptr_null(device);
 	libevdev_uinput_destroy(uinput);
 	litest_restore_log_handler(li);
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -1134,14 +1082,13 @@ START_TEST(device_wheel_only)
 	struct litest_device *dev = litest_current_device();
 	struct libinput_device *device = dev->libinput_device;
 
-	litest_assert(libinput_device_has_capability(device,
-						 LIBINPUT_DEVICE_CAP_POINTER));
+	litest_assert(
+		libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER));
 }
 END_TEST
 
 START_TEST(device_accelerometer)
 {
-	struct libinput *li;
 	struct libevdev_uinput *uinput;
 	struct libinput_device *device;
 
@@ -1149,21 +1096,17 @@ START_TEST(device_accelerometer)
 		{ ABS_X, 0, 10, 0, 0, 10 },
 		{ ABS_Y, 0, 10, 0, 0, 10 },
 		{ ABS_Z, 0, 10, 0, 0, 10 },
-		{ -1, -1, -1, -1, -1, -1 }
+		{ -1, -1, -1, -1, -1, -1 },
 	};
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	litest_disable_log_handler(li);
 
-	uinput = litest_create_uinput_abs_device("test device", NULL,
-						 absinfo,
-						 -1);
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	uinput = litest_create_uinput_abs_device("test device", NULL, absinfo, -1);
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_assert_ptr_null(device);
 	libevdev_uinput_destroy(uinput);
 	litest_restore_log_handler(li);
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -1175,8 +1118,7 @@ START_TEST(device_udev_tag_wacom_tablet)
 	const char *prop;
 
 	d = libinput_device_get_udev_device(device);
-	prop = udev_device_get_property_value(d,
-					      "ID_INPUT_TABLET");
+	prop = udev_device_get_property_value(d, "ID_INPUT_TABLET");
 
 	litest_assert_notnull(prop);
 	udev_device_unref(d);
@@ -1186,10 +1128,10 @@ END_TEST
 START_TEST(device_nonpointer_rel)
 {
 	struct libevdev_uinput *uinput;
-	struct libinput *li;
 	struct libinput_device *device;
 	int i;
 
+	/* clang-format off */
 	uinput = litest_create_uinput_device("test device",
 					     NULL,
 					     EV_KEY, KEY_A,
@@ -1197,9 +1139,9 @@ START_TEST(device_nonpointer_rel)
 					     EV_REL, REL_X,
 					     EV_REL, REL_Y,
 					     -1);
-	li = litest_create_context();
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	/* clang-format on */
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_assert_notnull(device);
 
 	litest_disable_log_handler(li);
@@ -1211,7 +1153,6 @@ START_TEST(device_nonpointer_rel)
 	}
 	litest_restore_log_handler(li);
 
-	litest_destroy_context(li);
 	libevdev_uinput_destroy(uinput);
 }
 END_TEST
@@ -1219,7 +1160,6 @@ END_TEST
 START_TEST(device_touchpad_rel)
 {
 	struct libevdev_uinput *uinput;
-	struct libinput *li;
 	struct libinput_device *device;
 	const struct input_absinfo abs[] = {
 		{ ABS_X, 0, 10, 0, 0, 10 },
@@ -1228,10 +1168,11 @@ START_TEST(device_touchpad_rel)
 		{ ABS_MT_TRACKING_ID, 0, 255, 0, 0, 0 },
 		{ ABS_MT_POSITION_X, 0, 10, 0, 0, 10 },
 		{ ABS_MT_POSITION_Y, 0, 10, 0, 0, 10 },
-		{ -1, -1, -1, -1, -1, -1 }
+		{ -1, -1, -1, -1, -1, -1 },
 	};
 	int i;
 
+	/* clang-format off */
 	uinput = litest_create_uinput_abs_device("test device",
 						 NULL, abs,
 						 EV_KEY, BTN_TOOL_FINGER,
@@ -1239,9 +1180,9 @@ START_TEST(device_touchpad_rel)
 						 EV_REL, REL_X,
 						 EV_REL, REL_Y,
 						 -1);
-	li = litest_create_context();
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	/* clang-format on */
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_assert_notnull(device);
 
 	for (i = 0; i < 100; i++) {
@@ -1251,7 +1192,6 @@ START_TEST(device_touchpad_rel)
 		litest_dispatch(li);
 	}
 
-	litest_destroy_context(li);
 	libevdev_uinput_destroy(uinput);
 }
 END_TEST
@@ -1259,7 +1199,6 @@ END_TEST
 START_TEST(device_touch_rel)
 {
 	struct libevdev_uinput *uinput;
-	struct libinput *li;
 	struct libinput_device *device;
 	const struct input_absinfo abs[] = {
 		{ ABS_X, 0, 10, 0, 0, 10 },
@@ -1268,19 +1207,20 @@ START_TEST(device_touch_rel)
 		{ ABS_MT_TRACKING_ID, 0, 255, 0, 0, 0 },
 		{ ABS_MT_POSITION_X, 0, 10, 0, 0, 10 },
 		{ ABS_MT_POSITION_Y, 0, 10, 0, 0, 10 },
-		{ -1, -1, -1, -1, -1, -1 }
+		{ -1, -1, -1, -1, -1, -1 },
 	};
 	int i;
 
+	/* clang-format off */
 	uinput = litest_create_uinput_abs_device("test device",
 						 NULL, abs,
 						 EV_KEY, BTN_TOUCH,
 						 EV_REL, REL_X,
 						 EV_REL, REL_Y,
 						 -1);
-	li = litest_create_context();
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	/* clang-format on */
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_assert_notnull(device);
 
 	litest_disable_log_handler(li);
@@ -1292,7 +1232,6 @@ START_TEST(device_touch_rel)
 	}
 	litest_restore_log_handler(li);
 
-	litest_destroy_context(li);
 	libevdev_uinput_destroy(uinput);
 }
 END_TEST
@@ -1300,15 +1239,15 @@ END_TEST
 START_TEST(device_abs_rel)
 {
 	struct libevdev_uinput *uinput;
-	struct libinput *li;
 	struct libinput_device *device;
 	const struct input_absinfo abs[] = {
 		{ ABS_X, 0, 10, 0, 0, 10 },
 		{ ABS_Y, 0, 10, 0, 0, 10 },
-		{ -1, -1, -1, -1, -1, -1 }
+		{ -1, -1, -1, -1, -1, -1 },
 	};
 	int i;
 
+	/* clang-format off */
 	uinput = litest_create_uinput_abs_device("test device",
 						 NULL, abs,
 						 EV_KEY, BTN_TOUCH,
@@ -1316,9 +1255,9 @@ START_TEST(device_abs_rel)
 						 EV_REL, REL_X,
 						 EV_REL, REL_Y,
 						 -1);
-	li = litest_create_context();
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	/* clang-format on */
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_assert_notnull(device);
 
 	for (i = 0; i < 100; i++) {
@@ -1328,7 +1267,6 @@ START_TEST(device_abs_rel)
 		litest_dispatch(li);
 	}
 
-	litest_destroy_context(li);
 	libevdev_uinput_destroy(uinput);
 }
 END_TEST
@@ -1348,6 +1286,7 @@ START_TEST(device_quirks_no_abs_mt_y)
 	litest_drain_events(li);
 
 	litest_event(dev, EV_REL, REL_HWHEEL, 1);
+	litest_event(dev, EV_REL, REL_HWHEEL_HI_RES, 120);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 
@@ -1378,7 +1317,6 @@ START_TEST(device_quirks_no_abs_mt_y)
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
 		litest_assert_empty_queue(li);
 	}
-
 }
 END_TEST
 
@@ -1436,8 +1374,8 @@ START_TEST(device_quirks_logitech_marble
 
 	litest_drain_events(li);
 
-	litest_assert(!libinput_device_pointer_has_button(dev->libinput_device,
-						      BTN_MIDDLE));
+	litest_assert(
+		!libinput_device_pointer_has_button(dev->libinput_device, BTN_MIDDLE));
 }
 END_TEST
 
@@ -1471,39 +1409,27 @@ debug_log_handler(struct libinput *libin
 
 START_TEST(device_quirks)
 {
-	struct libinput *li;
 	struct litest_device *dev;
 	struct libinput_device *device;
 	char **message;
-	bool disable_key_f1 = false,
-	     enable_btn_left = false;
-#if HAVE_LIBEVDEV_DISABLE_PROPERTY
-	bool disable_pointingstick = false,
-	     enable_buttonpad = false,
-	     enable_direct = false,
-	     disable_direct = false,
-	     enable_semi_mt = false,
+	bool disable_key_f1 = false, enable_btn_left = false,
+	     disable_pointingstick = false, enable_buttonpad = false,
+	     enable_direct = false, disable_direct = false, enable_semi_mt = false,
 	     disable_semi_mt = false;
-#endif
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);
 	libinput_log_set_handler(li, debug_log_handler);
 	dev = litest_add_device(li, LITEST_KEYBOARD_QUIRKED);
 	device = dev->libinput_device;
 
-	litest_assert(libinput_device_pointer_has_button(device,
-						     BTN_LEFT));
-	litest_assert(libinput_device_pointer_has_button(dev->libinput_device,
-						     BTN_RIGHT));
-	litest_assert(!libinput_device_pointer_has_button(device,
-						      BTN_MIDDLE));
-	litest_assert(!libinput_device_keyboard_has_key(dev->libinput_device,
-						    KEY_F1));
-	litest_assert(!libinput_device_keyboard_has_key(dev->libinput_device,
-						    KEY_F2));
-	litest_assert(!libinput_device_keyboard_has_key(dev->libinput_device,
-						    KEY_F3));
+	litest_assert(libinput_device_pointer_has_button(device, BTN_LEFT));
+	litest_assert(
+		libinput_device_pointer_has_button(dev->libinput_device, BTN_RIGHT));
+	litest_assert(!libinput_device_pointer_has_button(device, BTN_MIDDLE));
+	litest_assert(!libinput_device_keyboard_has_key(dev->libinput_device, KEY_F1));
+	litest_assert(!libinput_device_keyboard_has_key(dev->libinput_device, KEY_F2));
+	litest_assert(!libinput_device_keyboard_has_key(dev->libinput_device, KEY_F3));
 
 	/* Scrape the debug messages for confirmation that our quirks are
 	 * triggered, the above checks cannot work non-key codes */
@@ -1513,7 +1439,6 @@ START_TEST(device_quirks)
 			disable_key_f1 = true;
 		if (strstr(*message, "enabling EV_KEY BTN_LEFT"))
 			enable_btn_left = true;
-#if HAVE_LIBEVDEV_DISABLE_PROPERTY
 		if (strstr(*message, "enabling INPUT_PROP_BUTTONPAD"))
 			enable_buttonpad = true;
 		if (strstr(*message, "disabling INPUT_PROP_POINTING_STICK"))
@@ -1534,26 +1459,22 @@ START_TEST(device_quirks)
 			litest_assert(!enable_semi_mt);
 			disable_semi_mt = true;
 		}
-#endif
 		free(*message);
 		message++;
 	}
 
 	litest_assert(disable_key_f1);
 	litest_assert(enable_btn_left);
-#if HAVE_LIBEVDEV_DISABLE_PROPERTY
 	litest_assert(enable_buttonpad);
 	litest_assert(disable_pointingstick);
 	litest_assert(enable_direct);
 	litest_assert(disable_direct);
 	litest_assert(enable_semi_mt);
 	litest_assert(disable_semi_mt);
-#endif
 
 	litest_disable_log_handler(li);
 
-	litest_delete_device(dev);
-	litest_destroy_context(li);
+	litest_device_destroy(dev);
 }
 END_TEST
 
@@ -1562,12 +1483,9 @@ START_TEST(device_capability_at_least_on
 	struct litest_device *dev = litest_current_device();
 	struct libinput_device *device = dev->libinput_device;
 	enum libinput_device_capability caps[] = {
-		LIBINPUT_DEVICE_CAP_KEYBOARD,
-		LIBINPUT_DEVICE_CAP_POINTER,
-		LIBINPUT_DEVICE_CAP_TOUCH,
-		LIBINPUT_DEVICE_CAP_TABLET_TOOL,
-		LIBINPUT_DEVICE_CAP_TABLET_PAD,
-		LIBINPUT_DEVICE_CAP_GESTURE,
+		LIBINPUT_DEVICE_CAP_KEYBOARD,   LIBINPUT_DEVICE_CAP_POINTER,
+		LIBINPUT_DEVICE_CAP_TOUCH,      LIBINPUT_DEVICE_CAP_TABLET_TOOL,
+		LIBINPUT_DEVICE_CAP_TABLET_PAD, LIBINPUT_DEVICE_CAP_GESTURE,
 		LIBINPUT_DEVICE_CAP_SWITCH,
 	};
 	int ncaps = 0;
@@ -1577,7 +1495,6 @@ START_TEST(device_capability_at_least_on
 			ncaps++;
 	}
 	litest_assert_int_gt(ncaps, 0);
-
 }
 END_TEST
 
@@ -1589,28 +1506,26 @@ START_TEST(device_capability_check_inval
 	litest_assert(!libinput_device_has_capability(device, -1));
 	litest_assert(!libinput_device_has_capability(device, 7));
 	litest_assert(!libinput_device_has_capability(device, 0xffff));
-
 }
 END_TEST
 
 START_TEST(device_capability_nocaps_ignored)
 {
 	struct libevdev_uinput *uinput;
-	struct libinput *li;
 	struct libinput_device *device;
 
 	/* SW_PEN_INSERTED isn't handled in libinput so the device is
 	 * processed but ends up without seat capabilities and is ignored.
 	 */
+	/* clang-format off */
 	uinput = litest_create_uinput_device("test device", NULL,
 					     EV_SW, SW_PEN_INSERTED,
 					     -1);
-	li = litest_create_context();
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	/* clang-format on */
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_assert_ptr_null(device);
 
-	litest_destroy_context(li);
 	libevdev_uinput_destroy(uinput);
 }
 END_TEST
@@ -1683,26 +1598,23 @@ START_TEST(device_button_down_remove)
 {
 	struct litest_device *lidev = litest_current_device();
 	struct litest_device *dev;
-	struct libinput *li;
 
 	for (int code = 0; code < KEY_MAX; code++) {
 		struct libinput_event *event;
 		struct libinput_event_pointer *p;
-		bool have_down = false,
-		     have_up = false;
+		bool have_down = false, have_up = false;
 		const char *keyname;
 		int button_down = 0, button_up = 0;
 
 		keyname = libevdev_event_code_get_name(EV_KEY, code);
-		if (!keyname ||
-		    !strstartswith(keyname, "BTN_") ||
+		if (!keyname || !strstartswith(keyname, "BTN_") ||
 		    strstartswith(keyname, "BTN_TOOL_"))
 			continue;
 
 		if (!libevdev_has_event_code(lidev->evdev, EV_KEY, code))
 			continue;
 
-		li = litest_create_context();
+		_litest_context_destroy_ struct libinput *li = litest_create_context();
 		dev = litest_add_device(li, lidev->which);
 		litest_drain_events(li);
 
@@ -1717,7 +1629,7 @@ START_TEST(device_button_down_remove)
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
 		litest_dispatch(li);
 
-		litest_delete_device(dev);
+		litest_device_destroy(dev);
 		litest_dispatch(li);
 
 		while ((event = libinput_get_event(li))) {
@@ -1739,7 +1651,6 @@ START_TEST(device_button_down_remove)
 			libinput_event_destroy(event);
 		}
 
-		litest_destroy_context(li);
 		litest_assert_int_eq(have_down, have_up);
 	}
 }
@@ -1747,6 +1658,7 @@ END_TEST
 
 TEST_COLLECTION(device)
 {
+	/* clang-format off */
 	struct range abs_range = range_init_exclusive(0, ABS_MISC);
 	struct range abs_mt_range = range_init_exclusive(ABS_MT_SLOT + 1, ABS_CNT);
 
@@ -1783,14 +1695,20 @@ TEST_COLLECTION(device)
 	litest_add_no_device(device_group_ref);
 	litest_add_no_device(device_group_leak);
 
-	litest_add_no_device(abs_device_no_absx);
-	litest_add_no_device(abs_device_no_absy);
-	litest_add_no_device(abs_mt_device_no_absx);
-	litest_add_no_device(abs_mt_device_no_absy);
+	litest_with_parameters(params, "axis", 'I', 2,
+			       litest_named_i32(ABS_X),
+			       litest_named_i32(ABS_Y)) {
+		litest_add_parametrized_no_device(abs_device_missing_one_abs, params);
+		litest_add_parametrized_no_device(abs_device_missing_res, params);
+	}
+	litest_with_parameters(params, "axis", 'I', 2,
+			       litest_named_i32(ABS_MT_POSITION_X),
+			       litest_named_i32(ABS_MT_POSITION_Y)) {
+		litest_add_parametrized_no_device(abs_mt_device_missing_one_mt_abs, params);
+		litest_add_parametrized_no_device(abs_mt_device_missing_res, params);
+	}
 	litest_add_ranged_no_device(abs_device_no_range, &abs_range);
 	litest_add_ranged_no_device(abs_mt_device_no_range, &abs_mt_range);
-	litest_add_no_device(abs_device_missing_res);
-	litest_add_no_device(abs_mt_device_missing_res);
 	litest_add_no_device(ignore_joystick);
 
 	litest_add(device_wheel_only, LITEST_WHEEL, LITEST_RELATIVE|LITEST_ABSOLUTE|LITEST_TABLET);
@@ -1825,4 +1743,6 @@ TEST_COLLECTION(device)
 	litest_add(device_seat_phys_name, LITEST_ANY, LITEST_ANY);
 
 	litest_add(device_button_down_remove, LITEST_BUTTON, LITEST_ANY);
+
+	/* clang-format off */
 }
diff -pruN 1.28.1-1/test/test-gestures.c 1.30.0-1/test/test-gestures.c
--- 1.28.1-1/test/test-gestures.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-gestures.c	2025-11-25 03:40:43.000000000 +0000
@@ -29,13 +29,11 @@
 #include "libinput-util.h"
 #include "litest.h"
 
-enum cardinal {
-	N, NE, E, SE, S, SW, W, NW, NCARDINALS
-};
+enum cardinal { N, NE, E, SE, S, SW, W, NW, NCARDINALS };
 
 enum hold_gesture_behaviour {
-   HOLD_GESTURE_IGNORE,
-   HOLD_GESTURE_REQUIRE,
+	HOLD_GESTURE_IGNORE,
+	HOLD_GESTURE_REQUIRE,
 };
 
 static void
@@ -48,14 +46,8 @@ test_gesture_swipe_3fg(enum cardinal car
 	double dx, dy;
 	double dir_x, dir_y;
 	int cardinals[NCARDINALS][2] = {
-		{ 0, 30 },
-		{ 30, 30 },
-		{ 30, 0 },
-		{ 30, -30 },
-		{ 0, -30 },
-		{ -30, -30 },
-		{ -30, 0 },
-		{ -30, 30 },
+		{ 0, 30 },  { 30, 30 },   { 30, 0 },  { 30, -30 },
+		{ 0, -30 }, { -30, -30 }, { -30, 0 }, { -30, 30 },
 	};
 
 	if (litest_slot_count(dev) < 3)
@@ -72,25 +64,18 @@ test_gesture_swipe_3fg(enum cardinal car
 	litest_dispatch(li);
 
 	if (hold == HOLD_GESTURE_REQUIRE)
-		litest_timeout_gesture_hold();
+		litest_timeout_gesture_hold(li);
 
-	litest_touch_move_three_touches(dev, 40, 40, 50, 40, 60, 40, dir_x,
-					dir_y, 10);
+	litest_touch_move_three_touches(dev, 40, 40, 50, 40, 60, 40, dir_x, dir_y, 10);
 	litest_dispatch(li);
 
 	if (hold == HOLD_GESTURE_REQUIRE) {
-		litest_assert_gesture_event(li,
-					    LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
-					    3);
-		litest_assert_gesture_event(li,
-					    LIBINPUT_EVENT_GESTURE_HOLD_END,
-					    3);
+		litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, 3);
+		litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_END, 3);
 	}
 
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
-					 3);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, 3);
 	dx = libinput_event_gesture_get_dx(gevent);
 	dy = libinput_event_gesture_get_dy(gevent);
 	litest_assert(dx == 0.0);
@@ -142,9 +127,7 @@ test_gesture_swipe_3fg(enum cardinal car
 	litest_touch_up(dev, 2);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_SWIPE_END,
-					 3);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_SWIPE_END, 3);
 	litest_assert(!libinput_event_gesture_get_cancelled(gevent));
 	libinput_event_destroy(event);
 }
@@ -159,14 +142,8 @@ test_gesture_swipe_4fg(enum cardinal car
 	double dx, dy;
 	double dir_x, dir_y;
 	int cardinals[NCARDINALS][2] = {
-		{ 0, 3 },
-		{ 3, 3 },
-		{ 3, 0 },
-		{ 3, -3 },
-		{ 0, -3 },
-		{ -3, -3 },
-		{ -3, 0 },
-		{ -3, 3 },
+		{ 0, 3 },  { 3, 3 },   { 3, 0 },  { 3, -3 },
+		{ 0, -3 }, { -3, -3 }, { -3, 0 }, { -3, 3 },
 	};
 	int i;
 
@@ -185,7 +162,7 @@ test_gesture_swipe_4fg(enum cardinal car
 	litest_dispatch(li);
 
 	if (hold == HOLD_GESTURE_REQUIRE)
-		litest_timeout_gesture_hold();
+		litest_timeout_gesture_hold(li);
 
 	for (i = 0; i < 8; i++) {
 		litest_push_event_frame(dev);
@@ -193,22 +170,10 @@ test_gesture_swipe_4fg(enum cardinal car
 		dir_x += cardinals[cardinal][0];
 		dir_y += cardinals[cardinal][1];
 
-		litest_touch_move(dev,
-				  0,
-				  40 + dir_x,
-				  40 + dir_y);
-		litest_touch_move(dev,
-				  1,
-				  50 + dir_x,
-				  40 + dir_y);
-		litest_touch_move(dev,
-				  2,
-				  60 + dir_x,
-				  40 + dir_y);
-		litest_touch_move(dev,
-				  3,
-				  70 + dir_x,
-				  40 + dir_y);
+		litest_touch_move(dev, 0, 40 + dir_x, 40 + dir_y);
+		litest_touch_move(dev, 1, 50 + dir_x, 40 + dir_y);
+		litest_touch_move(dev, 2, 60 + dir_x, 40 + dir_y);
+		litest_touch_move(dev, 3, 70 + dir_x, 40 + dir_y);
 		litest_pop_event_frame(dev);
 		litest_dispatch(li);
 	}
@@ -216,18 +181,12 @@ test_gesture_swipe_4fg(enum cardinal car
 	litest_dispatch(li);
 
 	if (hold == HOLD_GESTURE_REQUIRE) {
-		litest_assert_gesture_event(li,
-					    LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
-					    4);
-		litest_assert_gesture_event(li,
-					    LIBINPUT_EVENT_GESTURE_HOLD_END,
-					    4);
+		litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, 4);
+		litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_END, 4);
 	}
 
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
-					 4);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, 4);
 	dx = libinput_event_gesture_get_dx(gevent);
 	dy = libinput_event_gesture_get_dy(gevent);
 	litest_assert(dx == 0.0);
@@ -280,9 +239,7 @@ test_gesture_swipe_4fg(enum cardinal car
 	litest_touch_up(dev, 3);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_SWIPE_END,
-					 4);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_SWIPE_END, 4);
 	litest_assert(!libinput_event_gesture_get_cancelled(gevent));
 	libinput_event_destroy(event);
 }
@@ -300,14 +257,8 @@ test_gesture_pinch_2fg(enum cardinal car
 	double scale, oldscale;
 	double angle;
 	int cardinals[NCARDINALS][2] = {
-		{ 0, 30 },
-		{ 30, 30 },
-		{ 30, 0 },
-		{ 30, -30 },
-		{ 0, -30 },
-		{ -30, -30 },
-		{ -30, 0 },
-		{ -30, 30 },
+		{ 0, 30 },  { 30, 30 },   { 30, 0 },  { 30, -30 },
+		{ 0, -30 }, { -30, -30 }, { -30, 0 }, { -30, 30 },
 	};
 
 	if (litest_slot_count(dev) < 2 ||
@@ -336,7 +287,7 @@ test_gesture_pinch_2fg(enum cardinal car
 	litest_dispatch(li);
 
 	if (hold == HOLD_GESTURE_REQUIRE)
-		litest_timeout_gesture_hold();
+		litest_timeout_gesture_hold(li);
 
 	for (i = 0; i < 8; i++) {
 		litest_push_event_frame(dev);
@@ -348,31 +299,19 @@ test_gesture_pinch_2fg(enum cardinal car
 			dir_y -= 2;
 		else if (dir_y < 0.0)
 			dir_y += 2;
-		litest_touch_move(dev,
-				  0,
-				  50 + dir_x,
-				  50 + dir_y);
-		litest_touch_move(dev,
-				  1,
-				  50 - dir_x,
-				  50 - dir_y);
+		litest_touch_move(dev, 0, 50 + dir_x, 50 + dir_y);
+		litest_touch_move(dev, 1, 50 - dir_x, 50 - dir_y);
 		litest_pop_event_frame(dev);
 		litest_dispatch(li);
 	}
 
 	if (hold == HOLD_GESTURE_REQUIRE) {
-		litest_assert_gesture_event(li,
-					LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
-					2);
-		litest_assert_gesture_event(li,
-					LIBINPUT_EVENT_GESTURE_HOLD_END,
-					2);
+		litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, 2);
+		litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_END, 2);
 	}
 
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
-					 2);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, 2);
 	dx = libinput_event_gesture_get_dx(gevent);
 	dy = libinput_event_gesture_get_dy(gevent);
 	scale = libinput_event_gesture_get_scale(gevent);
@@ -403,9 +342,7 @@ test_gesture_pinch_2fg(enum cardinal car
 	litest_touch_up(dev, 1);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_PINCH_END,
-					 2);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_PINCH_END, 2);
 	litest_assert(!libinput_event_gesture_get_cancelled(gevent));
 	libinput_event_destroy(event);
 }
@@ -423,14 +360,8 @@ test_gesture_pinch_3fg(enum cardinal car
 	double scale, oldscale;
 	double angle;
 	int cardinals[NCARDINALS][2] = {
-		{ 0, 30 },
-		{ 30, 30 },
-		{ 30, 0 },
-		{ 30, -30 },
-		{ 0, -30 },
-		{ -30, -30 },
-		{ -30, 0 },
-		{ -30, 30 },
+		{ 0, 30 },  { 30, 30 },   { 30, 0 },  { 30, -30 },
+		{ 0, -30 }, { -30, -30 }, { -30, 0 }, { -30, 30 },
 	};
 
 	if (litest_slot_count(dev) < 3)
@@ -447,7 +378,7 @@ test_gesture_pinch_3fg(enum cardinal car
 	litest_dispatch(li);
 
 	if (hold == HOLD_GESTURE_REQUIRE)
-		litest_timeout_gesture_hold();
+		litest_timeout_gesture_hold(li);
 
 	for (i = 0; i < 8; i++) {
 		litest_push_event_frame(dev);
@@ -459,34 +390,19 @@ test_gesture_pinch_3fg(enum cardinal car
 			dir_y -= 2;
 		else if (dir_y < 0.0)
 			dir_y += 2;
-		litest_touch_move(dev,
-				  0,
-				  50 + dir_x,
-				  50 + dir_y);
-		litest_touch_move(dev,
-				  1,
-				  50 - dir_x,
-				  50 - dir_y);
-		litest_touch_move(dev,
-				  2,
-				  51 - dir_x,
-				  51 - dir_y);
+		litest_touch_move(dev, 0, 50 + dir_x, 50 + dir_y);
+		litest_touch_move(dev, 1, 50 - dir_x, 50 - dir_y);
+		litest_touch_move(dev, 2, 51 - dir_x, 51 - dir_y);
 		litest_pop_event_frame(dev);
 		litest_dispatch(li);
 	}
 
 	if (hold == HOLD_GESTURE_REQUIRE) {
-		litest_assert_gesture_event(li,
-					LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
-					3);
-		litest_assert_gesture_event(li,
-					LIBINPUT_EVENT_GESTURE_HOLD_END,
-					3);
+		litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, 3);
+		litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_END, 3);
 	}
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
-					 3);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, 3);
 	dx = libinput_event_gesture_get_dx(gevent);
 	dy = libinput_event_gesture_get_dy(gevent);
 	scale = libinput_event_gesture_get_scale(gevent);
@@ -518,9 +434,7 @@ test_gesture_pinch_3fg(enum cardinal car
 	litest_touch_up(dev, 2);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_PINCH_END,
-					 3);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_PINCH_END, 3);
 	litest_assert(!libinput_event_gesture_get_cancelled(gevent));
 	libinput_event_destroy(event);
 }
@@ -538,14 +452,8 @@ test_gesture_pinch_4fg(enum cardinal car
 	double scale, oldscale;
 	double angle;
 	int cardinals[NCARDINALS][2] = {
-		{ 0, 30 },
-		{ 30, 30 },
-		{ 30, 0 },
-		{ 30, -30 },
-		{ 0, -30 },
-		{ -30, -30 },
-		{ -30, 0 },
-		{ -30, 30 },
+		{ 0, 30 },  { 30, 30 },   { 30, 0 },  { 30, -30 },
+		{ 0, -30 }, { -30, -30 }, { -30, 0 }, { -30, 30 },
 	};
 
 	if (litest_slot_count(dev) < 4)
@@ -563,7 +471,7 @@ test_gesture_pinch_4fg(enum cardinal car
 	litest_dispatch(li);
 
 	if (hold == HOLD_GESTURE_REQUIRE)
-		litest_timeout_gesture_hold();
+		litest_timeout_gesture_hold(li);
 
 	for (i = 0; i < 7; i++) {
 		litest_push_event_frame(dev);
@@ -575,39 +483,21 @@ test_gesture_pinch_4fg(enum cardinal car
 			dir_y -= 2;
 		else if (dir_y < 0.0)
 			dir_y += 2;
-		litest_touch_move(dev,
-				  0,
-				  50 + dir_x,
-				  50 + dir_y);
-		litest_touch_move(dev,
-				  1,
-				  50 - dir_x,
-				  50 - dir_y);
-		litest_touch_move(dev,
-				  2,
-				  51 - dir_x,
-				  51 - dir_y);
-		litest_touch_move(dev,
-				  3,
-				  52 - dir_x,
-				  52 - dir_y);
+		litest_touch_move(dev, 0, 50 + dir_x, 50 + dir_y);
+		litest_touch_move(dev, 1, 50 - dir_x, 50 - dir_y);
+		litest_touch_move(dev, 2, 51 - dir_x, 51 - dir_y);
+		litest_touch_move(dev, 3, 52 - dir_x, 52 - dir_y);
 		litest_pop_event_frame(dev);
 		litest_dispatch(li);
 	}
 
 	if (hold == HOLD_GESTURE_REQUIRE) {
-		litest_assert_gesture_event(li,
-					LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
-					4);
-		litest_assert_gesture_event(li,
-					LIBINPUT_EVENT_GESTURE_HOLD_END,
-					4);
+		litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, 4);
+		litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_END, 4);
 	}
 
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
-					 4);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, 4);
 	dx = libinput_event_gesture_get_dx(gevent);
 	dy = libinput_event_gesture_get_dy(gevent);
 	scale = libinput_event_gesture_get_scale(gevent);
@@ -640,9 +530,7 @@ test_gesture_pinch_4fg(enum cardinal car
 	litest_touch_up(dev, 3);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_PINCH_END,
-					 4);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_PINCH_END, 4);
 	litest_assert(!libinput_event_gesture_get_cancelled(gevent));
 	libinput_event_destroy(event);
 }
@@ -660,14 +548,8 @@ test_gesture_spread(enum cardinal cardin
 	double scale, oldscale;
 	double angle;
 	int cardinals[NCARDINALS][2] = {
-		{ 0, 30 },
-		{ 30, 30 },
-		{ 30, 0 },
-		{ 30, -30 },
-		{ 0, -30 },
-		{ -30, -30 },
-		{ -30, 0 },
-		{ -30, 30 },
+		{ 0, 30 },  { 30, 30 },   { 30, 0 },  { 30, -30 },
+		{ 0, -30 }, { -30, -30 }, { -30, 0 }, { -30, 30 },
 	};
 
 	if (litest_slot_count(dev) < 2 ||
@@ -696,7 +578,7 @@ test_gesture_spread(enum cardinal cardin
 	litest_dispatch(li);
 
 	if (hold == HOLD_GESTURE_REQUIRE)
-		litest_timeout_gesture_hold();
+		litest_timeout_gesture_hold(li);
 
 	for (i = 0; i < 15; i++) {
 		litest_push_event_frame(dev);
@@ -708,31 +590,19 @@ test_gesture_spread(enum cardinal cardin
 			dir_y += 1;
 		else if (dir_y < 0.0)
 			dir_y -= 1;
-		litest_touch_move(dev,
-				  0,
-				  50 + dir_x,
-				  50 + dir_y);
-		litest_touch_move(dev,
-				  1,
-				  50 - dir_x,
-				  50 - dir_y);
+		litest_touch_move(dev, 0, 50 + dir_x, 50 + dir_y);
+		litest_touch_move(dev, 1, 50 - dir_x, 50 - dir_y);
 		litest_pop_event_frame(dev);
 		litest_dispatch(li);
 	}
 
 	if (hold == HOLD_GESTURE_REQUIRE) {
-		litest_assert_gesture_event(li,
-					LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
-					2);
-		litest_assert_gesture_event(li,
-					LIBINPUT_EVENT_GESTURE_HOLD_END,
-					2);
+		litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, 2);
+		litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_END, 2);
 	}
 
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_PINCH_BEGIN,
-					 2);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_PINCH_BEGIN, 2);
 	dx = libinput_event_gesture_get_dx(gevent);
 	dy = libinput_event_gesture_get_dy(gevent);
 	scale = libinput_event_gesture_get_scale(gevent);
@@ -761,9 +631,7 @@ test_gesture_spread(enum cardinal cardin
 	litest_touch_up(dev, 1);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_PINCH_END,
-					 2);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_PINCH_END, 2);
 	litest_assert(!libinput_event_gesture_get_cancelled(gevent));
 	libinput_event_destroy(event);
 }
@@ -788,7 +656,7 @@ test_gesture_3fg_buttonarea_scroll(enum
 	litest_dispatch(li);
 
 	if (hold == HOLD_GESTURE_REQUIRE)
-		litest_timeout_gesture_hold();
+		litest_timeout_gesture_hold(li);
 
 	litest_touch_move_two_touches(dev, 40, 20, 30, 20, 0, 40, 10);
 
@@ -797,12 +665,8 @@ test_gesture_3fg_buttonarea_scroll(enum
 	litest_dispatch(li);
 
 	if (hold == HOLD_GESTURE_REQUIRE) {
-		litest_assert_gesture_event(li,
-					LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
-					2);
-		litest_assert_gesture_event(li,
-					LIBINPUT_EVENT_GESTURE_HOLD_END,
-					2);
+		litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, 2);
+		litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_END, 2);
 	}
 
 	litest_assert_scroll(li,
@@ -837,8 +701,7 @@ test_gesture_hold(int nfingers)
 		break;
 	}
 
-	litest_dispatch(li);
-	litest_timeout_gesture_hold();
+	litest_timeout_gesture_hold(li);
 
 	if (libinput_device_has_capability(dev->libinput_device,
 					   LIBINPUT_DEVICE_CAP_GESTURE)) {
@@ -902,8 +765,7 @@ test_gesture_hold_cancel(int nfingers)
 		break;
 	}
 
-	litest_dispatch(li);
-	litest_timeout_gesture_hold();
+	litest_timeout_gesture_hold(li);
 
 	litest_touch_up(dev, last_finger);
 
@@ -926,11 +788,13 @@ START_TEST(gestures_cap)
 	struct libinput_device *device = dev->libinput_device;
 
 	if (libevdev_has_property(dev->evdev, INPUT_PROP_SEMI_MT))
-		litest_assert(!libinput_device_has_capability(device,
-					  LIBINPUT_DEVICE_CAP_GESTURE));
+		litest_assert(
+			!libinput_device_has_capability(device,
+							LIBINPUT_DEVICE_CAP_GESTURE));
 	else
-		litest_assert(libinput_device_has_capability(device,
-					 LIBINPUT_DEVICE_CAP_GESTURE));
+		litest_assert(
+			libinput_device_has_capability(device,
+						       LIBINPUT_DEVICE_CAP_GESTURE));
 }
 END_TEST
 
@@ -939,14 +803,15 @@ START_TEST(gestures_nocap)
 	struct litest_device *dev = litest_current_device();
 	struct libinput_device *device = dev->libinput_device;
 
-	litest_assert(!libinput_device_has_capability(device,
-						  LIBINPUT_DEVICE_CAP_GESTURE));
+	litest_assert(
+		!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_GESTURE));
 }
 END_TEST
 
 START_TEST(gestures_swipe_3fg)
 {
-	enum cardinal cardinal = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal cardinal =
+		litest_test_param_get_i32(test_env->params, "direction");
 	test_gesture_swipe_3fg(cardinal, HOLD_GESTURE_IGNORE);
 }
 END_TEST
@@ -958,17 +823,12 @@ START_TEST(gestures_swipe_3fg_btntool)
 	struct libinput_event *event;
 	struct libinput_event_gesture *gevent;
 	double dx, dy;
-	enum cardinal cardinal = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal cardinal =
+		litest_test_param_get_i32(test_env->params, "direction");
 	double dir_x, dir_y;
 	int cardinals[NCARDINALS][2] = {
-		{ 0, 30 },
-		{ 30, 30 },
-		{ 30, 0 },
-		{ 30, -30 },
-		{ 0, -30 },
-		{ -30, -30 },
-		{ -30, 0 },
-		{ -30, 30 },
+		{ 0, 30 },  { 30, 30 },   { 30, 0 },  { 30, -30 },
+		{ 0, -30 }, { -30, -30 }, { -30, 0 }, { -30, 30 },
 	};
 
 	if (litest_slot_count(dev) > 2 ||
@@ -993,9 +853,7 @@ START_TEST(gestures_swipe_3fg_btntool)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
-					 3);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, 3);
 	dx = libinput_event_gesture_get_dx(gevent);
 	dy = libinput_event_gesture_get_dy(gevent);
 	litest_assert(dx == 0.0);
@@ -1046,9 +904,7 @@ START_TEST(gestures_swipe_3fg_btntool)
 	litest_touch_up(dev, 1);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_SWIPE_END,
-					 3);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_SWIPE_END, 3);
 	litest_assert(!libinput_event_gesture_get_cancelled(gevent));
 	libinput_event_destroy(event);
 }
@@ -1086,9 +942,7 @@ START_TEST(gestures_swipe_3fg_btntool_pi
 	libinput_event_destroy(event);
 
 	while ((event = libinput_get_event(li)) != NULL) {
-		litest_is_gesture_event(event,
-					LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE,
-					3);
+		litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE, 3);
 		libinput_event_destroy(event);
 	}
 
@@ -1096,9 +950,7 @@ START_TEST(gestures_swipe_3fg_btntool_pi
 	litest_touch_up(dev, 1);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_SWIPE_END,
-					 3);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_SWIPE_END, 3);
 	litest_assert(!libinput_event_gesture_get_cancelled(gevent));
 	libinput_event_destroy(event);
 }
@@ -1106,7 +958,8 @@ END_TEST
 
 START_TEST(gestures_swipe_4fg)
 {
-	enum cardinal cardinal = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal cardinal =
+		litest_test_param_get_i32(test_env->params, "direction");
 	test_gesture_swipe_4fg(cardinal, HOLD_GESTURE_IGNORE);
 }
 END_TEST
@@ -1118,17 +971,12 @@ START_TEST(gestures_swipe_4fg_btntool)
 	struct libinput_event *event;
 	struct libinput_event_gesture *gevent;
 	double dx, dy;
-	enum cardinal cardinal = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal cardinal =
+		litest_test_param_get_i32(test_env->params, "direction");
 	double dir_x, dir_y;
 	int cardinals[NCARDINALS][2] = {
-		{ 0, 30 },
-		{ 30, 30 },
-		{ 30, 0 },
-		{ 30, -30 },
-		{ 0, -30 },
-		{ -30, -30 },
-		{ -30, 0 },
-		{ -30, 30 },
+		{ 0, 30 },  { 30, 30 },   { 30, 0 },  { 30, -30 },
+		{ 0, -30 }, { -30, -30 }, { -30, 0 }, { -30, 30 },
 	};
 
 	if (litest_slot_count(dev) > 2 ||
@@ -1153,9 +1001,7 @@ START_TEST(gestures_swipe_4fg_btntool)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
-					 4);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, 4);
 	dx = libinput_event_gesture_get_dx(gevent);
 	dy = libinput_event_gesture_get_dy(gevent);
 	litest_assert(dx == 0.0);
@@ -1206,9 +1052,7 @@ START_TEST(gestures_swipe_4fg_btntool)
 	litest_touch_up(dev, 1);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_SWIPE_END,
-					 4);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_SWIPE_END, 4);
 	litest_assert(!libinput_event_gesture_get_cancelled(gevent));
 	libinput_event_destroy(event);
 }
@@ -1216,28 +1060,32 @@ END_TEST
 
 START_TEST(gestures_pinch)
 {
-	enum cardinal cardinal = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal cardinal =
+		litest_test_param_get_i32(test_env->params, "direction");
 	test_gesture_pinch_2fg(cardinal, HOLD_GESTURE_IGNORE);
 }
 END_TEST
 
 START_TEST(gestures_pinch_3fg)
 {
-	enum cardinal cardinal = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal cardinal =
+		litest_test_param_get_i32(test_env->params, "direction");
 	test_gesture_pinch_3fg(cardinal, HOLD_GESTURE_IGNORE);
 }
 END_TEST
 
 START_TEST(gestures_pinch_4fg)
 {
-	enum cardinal cardinal = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal cardinal =
+		litest_test_param_get_i32(test_env->params, "direction");
 	test_gesture_pinch_4fg(cardinal, HOLD_GESTURE_IGNORE);
 }
 END_TEST
 
 START_TEST(gestures_spread)
 {
-	enum cardinal cardinal = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal cardinal =
+		litest_test_param_get_i32(test_env->params, "direction");
 	test_gesture_spread(cardinal, HOLD_GESTURE_IGNORE);
 }
 END_TEST
@@ -1259,17 +1107,14 @@ START_TEST(gestures_time_usec)
 	litest_touch_down(dev, 1, 50, 40);
 	litest_touch_down(dev, 2, 60, 40);
 	litest_dispatch(li);
-	litest_touch_move_three_touches(dev, 40, 40, 50, 40, 60, 40, 0, 30,
-					30);
+	litest_touch_move_three_touches(dev, 40, 40, 50, 40, 60, 40, 0, 30, 30);
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	gevent = litest_is_gesture_event(event,
-					 LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
-					 3);
+	gevent = litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, 3);
 	time_usec = libinput_event_gesture_get_time_usec(gevent);
 	litest_assert_int_eq(libinput_event_gesture_get_time(gevent),
-			 (uint32_t) (time_usec / 1000));
+			     (uint32_t)(time_usec / 1000));
 	libinput_event_destroy(event);
 }
 END_TEST
@@ -1343,18 +1188,11 @@ START_TEST(gestures_swipe_3fg_unaccel)
 	litest_touch_down(dev, 1, 50, 20);
 	litest_touch_down(dev, 2, 60, 20);
 	litest_dispatch(li);
-	litest_touch_move_three_touches(dev,
-					40, 20,
-					50, 20,
-					60, 20,
-					30, 40,
-					10);
+	litest_touch_move_three_touches(dev, 40, 20, 50, 20, 60, 20, 30, 40, 10);
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	litest_is_gesture_event(event,
-				LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
-				3);
+	litest_is_gesture_event(event, LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, 3);
 	libinput_event_destroy(event);
 	event = libinput_get_event(li);
 	do {
@@ -1413,11 +1251,13 @@ START_TEST(gestures_hold_config_default_
 	struct libinput_device *device = dev->libinput_device;
 
 	litest_assert_int_eq(libinput_device_config_gesture_hold_is_available(device),
-			 0);
-	litest_assert_enum_eq(libinput_device_config_gesture_get_hold_default_enabled(device),
-			 LIBINPUT_CONFIG_HOLD_DISABLED);
-	litest_assert_enum_eq(libinput_device_config_gesture_get_hold_default_enabled(device),
-			 LIBINPUT_CONFIG_HOLD_DISABLED);
+			     0);
+	litest_assert_enum_eq(
+		libinput_device_config_gesture_get_hold_default_enabled(device),
+		LIBINPUT_CONFIG_HOLD_DISABLED);
+	litest_assert_enum_eq(
+		libinput_device_config_gesture_get_hold_default_enabled(device),
+		LIBINPUT_CONFIG_HOLD_DISABLED);
 }
 END_TEST
 
@@ -1427,11 +1267,12 @@ START_TEST(gestures_hold_config_default_
 	struct libinput_device *device = dev->libinput_device;
 
 	litest_assert_int_eq(libinput_device_config_gesture_hold_is_available(device),
-			 1);
-	litest_assert_enum_eq(libinput_device_config_gesture_get_hold_default_enabled(device),
-			 LIBINPUT_CONFIG_HOLD_ENABLED);
+			     1);
+	litest_assert_enum_eq(
+		libinput_device_config_gesture_get_hold_default_enabled(device),
+		LIBINPUT_CONFIG_HOLD_ENABLED);
 	litest_assert_enum_eq(libinput_device_config_gesture_get_hold_enabled(device),
-			 LIBINPUT_CONFIG_HOLD_ENABLED);
+			      LIBINPUT_CONFIG_HOLD_ENABLED);
 }
 END_TEST
 
@@ -1440,10 +1281,12 @@ START_TEST(gestures_hold_config_set_inva
 	struct litest_device *dev = litest_current_device();
 	struct libinput_device *device = dev->libinput_device;
 
-	litest_assert_enum_eq(libinput_device_config_gesture_set_hold_enabled(device, -1),
-			 LIBINPUT_CONFIG_STATUS_INVALID);
-	litest_assert_enum_eq(libinput_device_config_gesture_set_hold_enabled(device, 2),
-			 LIBINPUT_CONFIG_STATUS_INVALID);
+	litest_assert_enum_eq(
+		libinput_device_config_gesture_set_hold_enabled(device, -1),
+		LIBINPUT_CONFIG_STATUS_INVALID);
+	litest_assert_enum_eq(
+		libinput_device_config_gesture_set_hold_enabled(device, 2),
+		LIBINPUT_CONFIG_STATUS_INVALID);
 }
 END_TEST
 
@@ -1453,13 +1296,15 @@ START_TEST(gestures_hold_config_is_avail
 	struct libinput_device *device = dev->libinput_device;
 
 	litest_assert_int_eq(libinput_device_config_gesture_hold_is_available(device),
-			 1);
+			     1);
 	litest_assert_enum_eq(libinput_device_config_gesture_get_hold_enabled(device),
-			 LIBINPUT_CONFIG_HOLD_ENABLED);
-	litest_assert_enum_eq(libinput_device_config_gesture_set_hold_enabled(device, LIBINPUT_CONFIG_HOLD_DISABLED),
-			 LIBINPUT_CONFIG_STATUS_SUCCESS);
+			      LIBINPUT_CONFIG_HOLD_ENABLED);
+	litest_assert_enum_eq(libinput_device_config_gesture_set_hold_enabled(
+				      device,
+				      LIBINPUT_CONFIG_HOLD_DISABLED),
+			      LIBINPUT_CONFIG_STATUS_SUCCESS);
 	litest_assert_enum_eq(libinput_device_config_gesture_get_hold_enabled(device),
-			 LIBINPUT_CONFIG_HOLD_DISABLED);
+			      LIBINPUT_CONFIG_HOLD_DISABLED);
 }
 END_TEST
 
@@ -1469,13 +1314,17 @@ START_TEST(gestures_hold_config_is_not_a
 	struct libinput_device *device = dev->libinput_device;
 
 	litest_assert_int_eq(libinput_device_config_gesture_hold_is_available(device),
-			 0);
+			     0);
 	litest_assert_enum_eq(libinput_device_config_gesture_get_hold_enabled(device),
-			 LIBINPUT_CONFIG_HOLD_DISABLED);
-	litest_assert_enum_eq(libinput_device_config_gesture_set_hold_enabled(device, LIBINPUT_CONFIG_HOLD_ENABLED),
-			 LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
-	litest_assert_enum_eq(libinput_device_config_gesture_set_hold_enabled(device, LIBINPUT_CONFIG_HOLD_DISABLED),
-			 LIBINPUT_CONFIG_STATUS_SUCCESS);
+			      LIBINPUT_CONFIG_HOLD_DISABLED);
+	litest_assert_enum_eq(libinput_device_config_gesture_set_hold_enabled(
+				      device,
+				      LIBINPUT_CONFIG_HOLD_ENABLED),
+			      LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+	litest_assert_enum_eq(libinput_device_config_gesture_set_hold_enabled(
+				      device,
+				      LIBINPUT_CONFIG_HOLD_DISABLED),
+			      LIBINPUT_CONFIG_STATUS_SUCCESS);
 }
 END_TEST
 
@@ -1533,42 +1382,48 @@ END_TEST
 
 START_TEST(gestures_hold_then_swipe_3fg)
 {
-	enum cardinal cardinal = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal cardinal =
+		litest_test_param_get_i32(test_env->params, "direction");
 	test_gesture_swipe_3fg(cardinal, HOLD_GESTURE_REQUIRE);
 }
 END_TEST
 
 START_TEST(gestures_hold_then_swipe_4fg)
 {
-	enum cardinal cardinal = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal cardinal =
+		litest_test_param_get_i32(test_env->params, "direction");
 	test_gesture_swipe_4fg(cardinal, HOLD_GESTURE_REQUIRE);
 }
 END_TEST
 
 START_TEST(gestures_hold_then_pinch_2fg)
 {
-	enum cardinal cardinal = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal cardinal =
+		litest_test_param_get_i32(test_env->params, "direction");
 	test_gesture_pinch_2fg(cardinal, HOLD_GESTURE_REQUIRE);
 }
 END_TEST
 
 START_TEST(gestures_hold_then_pinch_3fg)
 {
-	enum cardinal cardinal = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal cardinal =
+		litest_test_param_get_i32(test_env->params, "direction");
 	test_gesture_pinch_3fg(cardinal, HOLD_GESTURE_REQUIRE);
 }
 END_TEST
 
 START_TEST(gestures_hold_then_pinch_4fg)
 {
-	enum cardinal cardinal = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal cardinal =
+		litest_test_param_get_i32(test_env->params, "direction");
 	test_gesture_pinch_4fg(cardinal, HOLD_GESTURE_REQUIRE);
 }
 END_TEST
 
 START_TEST(gestures_hold_then_spread)
 {
-	enum cardinal cardinal = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal cardinal =
+		litest_test_param_get_i32(test_env->params, "direction");
 	test_gesture_spread(cardinal, HOLD_GESTURE_REQUIRE);
 }
 END_TEST
@@ -1593,33 +1448,23 @@ START_TEST(gestures_hold_once_on_double_
 
 	/* First tap, a hold gesture must be generated */
 	litest_touch_down(dev, 0, 50, 50);
-	litest_dispatch(li);
-	litest_timeout_gesture_quick_hold();
+	litest_timeout_gesture_quick_hold(li);
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_assert_gesture_event(li,
-				    LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
-				    1);
-	litest_assert_gesture_event(li,
-				    LIBINPUT_EVENT_GESTURE_HOLD_END,
-				    1);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, 1);
+	litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_END, 1);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 
 	/* Double tap, don't generate an extra hold gesture */
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-	litest_timeout_gesture_quick_hold();
+	litest_timeout_gesture_quick_hold(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1654,7 +1499,7 @@ START_TEST(gestures_hold_once_tap_n_drag
 		button = BTN_MIDDLE;
 		break;
 	default:
-		abort();
+		litest_assert_not_reached();
 	}
 
 	switch (nfingers) {
@@ -1668,8 +1513,7 @@ START_TEST(gestures_hold_once_tap_n_drag
 		litest_touch_down(dev, 0, 40, 30);
 		break;
 	}
-	litest_dispatch(li);
-	litest_timeout_gesture_quick_hold();
+	litest_timeout_gesture_quick_hold(li);
 
 	switch (nfingers) {
 	case 3:
@@ -1699,15 +1543,13 @@ START_TEST(gestures_hold_once_tap_n_drag
 	litest_touch_move_to(dev, 0, 50, 50, 80, 80, 20);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -1728,22 +1570,17 @@ START_TEST(gestures_hold_and_motion_befo
 
 	litest_touch_move_to(dev, 0, 50, 50, 51, 51, 1);
 	litest_touch_move_to(dev, 0, 51, 51, 50, 50, 1);
-	litest_dispatch(li);
 
-	litest_timeout_gesture_quick_hold();
+	litest_timeout_gesture_quick_hold(li);
 
 	litest_drain_events_of_type(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_assert_gesture_event(li,
-				    LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
-				    1);
+	litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, 1);
 
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_assert_gesture_event(li,
-				    LIBINPUT_EVENT_GESTURE_HOLD_END,
-				    1);
+	litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_END, 1);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -1760,12 +1597,9 @@ START_TEST(gestures_hold_and_motion_afte
 	litest_drain_events(li);
 
 	litest_touch_down(dev, 0, 50, 50);
-	litest_dispatch(li);
-	litest_timeout_gesture_quick_hold();
+	litest_timeout_gesture_quick_hold(li);
 
-	litest_assert_gesture_event(li,
-				    LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
-				    1);
+	litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, 1);
 
 	litest_touch_move_to(dev, 0, 50, 50, 51, 51, 1);
 	litest_touch_move_to(dev, 0, 51, 51, 50, 50, 1);
@@ -1775,9 +1609,7 @@ START_TEST(gestures_hold_and_motion_afte
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_assert_gesture_event(li,
-				    LIBINPUT_EVENT_GESTURE_HOLD_END,
-				    1);
+	litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_HOLD_END, 1);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -1787,16 +1619,14 @@ START_TEST(gestures_3fg_drag)
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	uint32_t finger_count;
-	bool tap_enabled;
-	litest_test_param_fetch(test_env->params,
-				"fingers", 'u', &finger_count,
-				"tap-enabled", 'b', &tap_enabled);
+	uint32_t finger_count = litest_test_param_get_u32(test_env->params, "fingers");
+	bool tap_enabled = litest_test_param_get_bool(test_env->params, "tap-enabled");
 
 	if (litest_slot_count(dev) < 3)
 		return LITEST_NOT_APPLICABLE;
 
-	if (libinput_device_config_3fg_drag_get_finger_count(dev->libinput_device) < (int)finger_count)
+	if (libinput_device_config_3fg_drag_get_finger_count(dev->libinput_device) <
+	    (int)finger_count)
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_3fg_drag(dev->libinput_device, finger_count);
@@ -1813,13 +1643,17 @@ START_TEST(gestures_3fg_drag)
 
 	litest_dispatch(li);
 
-	litest_drain_events_of_type(li, LIBINPUT_EVENT_GESTURE_HOLD_BEGIN, LIBINPUT_EVENT_GESTURE_HOLD_END);
+	litest_drain_events_of_type(li,
+				    LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
+				    LIBINPUT_EVENT_GESTURE_HOLD_END);
 
 	if (tap_enabled) {
-		litest_checkpoint("Expecting no immediate button press as tapping is enabled");
+		litest_checkpoint(
+			"Expecting no immediate button press as tapping is enabled");
 		litest_assert_empty_queue(li);
 	} else {
-		litest_checkpoint("Expecting immediate button press as tapping is disabled");
+		litest_checkpoint(
+			"Expecting immediate button press as tapping is disabled");
 		litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 	}
 
@@ -1842,8 +1676,7 @@ START_TEST(gestures_3fg_drag)
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_3fg_drag();
-	litest_dispatch(li);
+	litest_timeout_3fg_drag(li);
 
 	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
@@ -1854,18 +1687,15 @@ START_TEST(gestures_3fg_drag_lock_resume
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	uint32_t finger_count;
-	bool tap_enabled;
-	bool wait_for_timeout;
-	litest_test_param_fetch(test_env->params,
-				"fingers", 'u', &finger_count,
-				"tap-enabled", 'b', &tap_enabled,
-				"wait", 'b', &wait_for_timeout);
+	uint32_t finger_count = litest_test_param_get_u32(test_env->params, "fingers");
+	bool tap_enabled = litest_test_param_get_bool(test_env->params, "tap-enabled");
+	bool wait_for_timeout = litest_test_param_get_bool(test_env->params, "wait");
 
 	if (litest_slot_count(dev) < 3)
 		return LITEST_NOT_APPLICABLE;
 
-	if (libinput_device_config_3fg_drag_get_finger_count(dev->libinput_device) < (int)finger_count)
+	if (libinput_device_config_3fg_drag_get_finger_count(dev->libinput_device) <
+	    (int)finger_count)
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_3fg_drag(dev->libinput_device, finger_count);
@@ -1909,13 +1739,11 @@ START_TEST(gestures_3fg_drag_lock_resume
 	litest_dispatch(li);
 
 	litest_checkpoint("Waiting past finger switch timeout");
-	litest_timeout_finger_switch();
-	litest_dispatch(li);
+	litest_timeout_finger_switch(li);
 
 	if (wait_for_timeout) {
 		litest_checkpoint("Waiting past tap/3fg drag timeout");
-		litest_timeout_3fg_drag();
-		litest_dispatch(li);
+		litest_timeout_3fg_drag(li);
 		litest_assert_empty_queue(li);
 	}
 
@@ -1934,8 +1762,7 @@ START_TEST(gestures_3fg_drag_lock_resume
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_3fg_drag();
-	litest_dispatch(li);
+	litest_timeout_3fg_drag(li);
 
 	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
@@ -1946,13 +1773,9 @@ START_TEST(gestures_3fg_drag_lock_resume
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	uint32_t finger_count;
-	bool tap_enabled;
-	bool wait_for_timeout;
-	litest_test_param_fetch(test_env->params,
-				"fingers", 'u', &finger_count,
-				"tap-enabled", 'b', &tap_enabled,
-				"wait", 'b', &wait_for_timeout);
+	uint32_t finger_count = litest_test_param_get_u32(test_env->params, "fingers");
+	bool tap_enabled = litest_test_param_get_bool(test_env->params, "tap-enabled");
+	bool wait_for_timeout = litest_test_param_get_bool(test_env->params, "wait");
 
 	/* tap-enabled for 4fg finger count doesn't make a difference */
 	bool expect_tap = finger_count <= 3 && tap_enabled && !wait_for_timeout;
@@ -1960,7 +1783,8 @@ START_TEST(gestures_3fg_drag_lock_resume
 	if (litest_slot_count(dev) < 3)
 		return LITEST_NOT_APPLICABLE;
 
-	if (libinput_device_config_3fg_drag_get_finger_count(dev->libinput_device) < (int)finger_count)
+	if (libinput_device_config_3fg_drag_get_finger_count(dev->libinput_device) <
+	    (int)finger_count)
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_3fg_drag(dev->libinput_device, finger_count);
@@ -2001,13 +1825,11 @@ START_TEST(gestures_3fg_drag_lock_resume
 	litest_assert_empty_queue(li);
 
 	litest_checkpoint("Waiting past finger switch timeout");
-	litest_timeout_finger_switch();
-	litest_dispatch(li);
+	litest_timeout_finger_switch(li);
 
 	if (wait_for_timeout) {
 		litest_checkpoint("Waiting past tap/3fg drag timeout");
-		litest_timeout_3fg_drag();
-		litest_dispatch(li);
+		litest_timeout_3fg_drag(li);
 		litest_assert_empty_queue(li);
 	}
 
@@ -2020,18 +1842,25 @@ START_TEST(gestures_3fg_drag_lock_resume
 		/* If we're not waiting and tapping is enabled, this is
 		 * the equivalent of a 3fg tap within the drag timeout */
 		litest_checkpoint("Expecting 3fg drag release");
-		litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_button_event(li,
+					   BTN_LEFT,
+					   LIBINPUT_BUTTON_STATE_RELEASED);
 		litest_checkpoint("Expecting 3fg tap");
-		litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
-		litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_button_event(li,
+					   BTN_MIDDLE,
+					   LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li,
+					   BTN_MIDDLE,
+					   LIBINPUT_BUTTON_STATE_RELEASED);
 	}
 
 	litest_assert_empty_queue(li);
-	litest_timeout_3fg_drag();
-	litest_dispatch(li);
+	litest_timeout_3fg_drag(li);
 
 	if (!expect_tap)
-		litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_button_event(li,
+					   BTN_LEFT,
+					   LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -2042,16 +1871,14 @@ START_TEST(gestures_3fg_drag_lock_resume
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	uint32_t finger_count;
-	bool tap_enabled;
-	litest_test_param_fetch(test_env->params,
-				"fingers", 'u', &finger_count,
-				"tap-enabled", 'b', &tap_enabled);
+	uint32_t finger_count = litest_test_param_get_u32(test_env->params, "fingers");
+	bool tap_enabled = litest_test_param_get_bool(test_env->params, "tap-enabled");
 
 	if (litest_slot_count(dev) < 3)
 		return LITEST_NOT_APPLICABLE;
 
-	if (libinput_device_config_3fg_drag_get_finger_count(dev->libinput_device) < (int)finger_count)
+	if (libinput_device_config_3fg_drag_get_finger_count(dev->libinput_device) <
+	    (int)finger_count)
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_3fg_drag(dev->libinput_device, finger_count);
@@ -2092,8 +1919,7 @@ START_TEST(gestures_3fg_drag_lock_resume
 	litest_assert_empty_queue(li);
 
 	/* We need to wait until the gesture code accepts this is one finger only */
-	litest_timeout_finger_switch();
-	litest_dispatch(li);
+	litest_timeout_finger_switch(li);
 
 	while (y < 60.0) {
 		y += 2;
@@ -2109,8 +1935,7 @@ START_TEST(gestures_3fg_drag_lock_resume
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_3fg_drag();
-	litest_dispatch(li);
+	litest_timeout_3fg_drag(li);
 }
 END_TEST
 
@@ -2119,16 +1944,14 @@ START_TEST(gestures_3fg_drag_lock_resume
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	uint32_t finger_count;
-	bool tap_enabled;
-	litest_test_param_fetch(test_env->params,
-				"fingers", 'u', &finger_count,
-				"tap-enabled", 'b', &tap_enabled);
+	uint32_t finger_count = litest_test_param_get_u32(test_env->params, "fingers");
+	bool tap_enabled = litest_test_param_get_bool(test_env->params, "tap-enabled");
 
 	if (litest_slot_count(dev) < 3)
 		return LITEST_NOT_APPLICABLE;
 
-	if (libinput_device_config_3fg_drag_get_finger_count(dev->libinput_device) < (int)finger_count)
+	if (libinput_device_config_3fg_drag_get_finger_count(dev->libinput_device) <
+	    (int)finger_count)
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_3fg_drag(dev->libinput_device, finger_count);
@@ -2168,8 +1991,7 @@ START_TEST(gestures_3fg_drag_lock_resume
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_finger_switch();
-	litest_dispatch(li);
+	litest_timeout_finger_switch(li);
 
 	while (y < 60.0) {
 		y += 2;
@@ -2186,8 +2008,7 @@ START_TEST(gestures_3fg_drag_lock_resume
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_3fg_drag();
-	litest_dispatch(li);
+	litest_timeout_3fg_drag(li);
 }
 END_TEST
 
@@ -2200,7 +2021,8 @@ START_TEST(gestures_3fg_drag_lock_resume
 	if (litest_slot_count(dev) < 3)
 		return LITEST_NOT_APPLICABLE;
 
-	if (libinput_device_config_3fg_drag_get_finger_count(dev->libinput_device) < finger_count)
+	if (libinput_device_config_3fg_drag_get_finger_count(dev->libinput_device) <
+	    finger_count)
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_3fg_drag(dev->libinput_device, finger_count);
@@ -2244,8 +2066,7 @@ START_TEST(gestures_3fg_drag_lock_resume
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	litest_checkpoint("Expecting drag release followed by 1fg tap");
 
@@ -2256,13 +2077,13 @@ START_TEST(gestures_3fg_drag_lock_resume
 	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_3fg_drag();
-	litest_dispatch(li);
+	litest_timeout_3fg_drag(li);
 }
 END_TEST
 
 TEST_COLLECTION(gestures)
 {
+	/* clang-format off */
 	litest_add(gestures_cap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
 	litest_add(gestures_nocap, LITEST_ANY, LITEST_TOUCHPAD);
 
@@ -2317,29 +2138,27 @@ TEST_COLLECTION(gestures)
 	litest_add(gestures_hold_and_motion_before_timeout, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
 	litest_add(gestures_hold_and_motion_after_timeout, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
 
-	{
-		struct litest_parameters *params = litest_parameters_new("fingers", 'u', 2, 3, 4,
-									 "tap-enabled", 'b');
+	litest_with_parameters(params,
+			       "fingers", 'u', 2, 3, 4,
+			       "tap-enabled", 'b') {
 		litest_add_parametrized(gestures_3fg_drag, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, params);
-		litest_parameters_unref(params);
 	}
 
-	{
-		struct litest_parameters *params = litest_parameters_new("fingers", 'u', 2, 3, 4,
-									 "tap-enabled", 'b',
-									 "wait", 'b');
+	litest_with_parameters(params,
+			       "fingers", 'u', 2, 3, 4,
+			       "tap-enabled", 'b',
+			       "wait", 'b') {
 		litest_add_parametrized(gestures_3fg_drag_lock_resume_3fg_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, params);
 		litest_add_parametrized(gestures_3fg_drag_lock_resume_3fg_release_no_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, params);
-		litest_parameters_unref(params);
 	}
 
-	{
-		struct litest_parameters *params = litest_parameters_new("fingers", 'u', 2, 3, 4,
-									 "tap-enabled", 'b');
+	litest_with_parameters(params,
+			       "fingers", 'u', 2, 3, 4,
+			       "tap-enabled", 'b') {
 		litest_add_parametrized(gestures_3fg_drag_lock_resume_1fg_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, params);
 		litest_add_parametrized(gestures_3fg_drag_lock_resume_2fg_scroll, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, params);
-		litest_parameters_unref(params);
 	}
+
 	litest_with_parameters(params, "fingers", 'i', 2, 3, 4) {
 		litest_add_parametrized(gestures_3fg_drag_lock_resume_1fg_tap, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, params);
 	}
@@ -2347,4 +2166,5 @@ TEST_COLLECTION(gestures)
 	/* Timing-sensitive test, valgrind is too slow */
 	if (!RUNNING_ON_VALGRIND)
 		litest_add(gestures_swipe_3fg_unaccel, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-keyboard.c 1.30.0-1/test/test-keyboard.c
--- 1.28.1-1/test/test-keyboard.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-keyboard.c	2025-11-25 03:40:43.000000000 +0000
@@ -32,7 +32,6 @@ START_TEST(keyboard_seat_key_count)
 {
 	struct litest_device *devices[4];
 	const int num_devices = ARRAY_LENGTH(devices);
-	struct libinput *libinput;
 	struct libinput_event *ev;
 	struct libinput_event_keyboard *kev;
 	int i;
@@ -40,13 +39,15 @@ START_TEST(keyboard_seat_key_count)
 	int expected_key_button_count = 0;
 	char device_name[255];
 
-	libinput = litest_create_context();
+	_litest_context_destroy_ struct libinput *libinput = litest_create_context();
 	for (i = 0; i < num_devices; ++i) {
 		sprintf(device_name, "litest Generic keyboard (%d)", i);
 		devices[i] = litest_add_device_with_overrides(libinput,
 							      LITEST_KEYBOARD,
 							      device_name,
-							      NULL, NULL, NULL);
+							      NULL,
+							      NULL,
+							      NULL);
 	}
 
 	litest_drain_events(libinput);
@@ -56,13 +57,10 @@ START_TEST(keyboard_seat_key_count)
 
 	litest_dispatch(libinput);
 	while ((ev = libinput_get_event(libinput))) {
-		kev = litest_is_keyboard_event(ev,
-					       KEY_A,
-					       LIBINPUT_KEY_STATE_PRESSED);
+		kev = litest_is_keyboard_event(ev, KEY_A, LIBINPUT_KEY_STATE_PRESSED);
 
 		++expected_key_button_count;
-		seat_key_count =
-			libinput_event_keyboard_get_seat_key_count(kev);
+		seat_key_count = libinput_event_keyboard_get_seat_key_count(kev);
 		litest_assert_int_eq(expected_key_button_count, seat_key_count);
 
 		libinput_event_destroy(ev);
@@ -81,11 +79,10 @@ START_TEST(keyboard_seat_key_count)
 		litest_assert_int_eq(libinput_event_keyboard_get_key(kev),
 				     (unsigned int)KEY_A);
 		litest_assert_enum_eq(libinput_event_keyboard_get_key_state(kev),
-				 LIBINPUT_KEY_STATE_RELEASED);
+				      LIBINPUT_KEY_STATE_RELEASED);
 
 		--expected_key_button_count;
-		seat_key_count =
-			libinput_event_keyboard_get_seat_key_count(kev);
+		seat_key_count = libinput_event_keyboard_get_seat_key_count(kev);
 		litest_assert_int_eq(expected_key_button_count, seat_key_count);
 
 		libinput_event_destroy(ev);
@@ -95,21 +92,20 @@ START_TEST(keyboard_seat_key_count)
 	litest_assert_int_eq(seat_key_count, 0);
 
 	for (i = 0; i < num_devices; ++i)
-		litest_delete_device(devices[i]);
-	litest_destroy_context(libinput);
+		litest_device_destroy(devices[i]);
 }
 END_TEST
 
 START_TEST(keyboard_ignore_no_pressed_release)
 {
 	struct litest_device *dev;
-	struct libinput *unused_libinput;
-	struct libinput *libinput;
 	struct libinput_event *event;
 	struct libinput_event_keyboard *kevent;
 	int events[] = {
-		EV_KEY, KEY_A,
-		-1, -1,
+		EV_KEY,
+		KEY_A,
+		-1,
+		-1,
 	};
 	enum libinput_key_state expected_states[] = {
 		LIBINPUT_KEY_STATE_PRESSED,
@@ -120,18 +116,20 @@ START_TEST(keyboard_ignore_no_pressed_re
 	 * as such non-symmetric events are dropped. Work-around this by first
 	 * adding the test device to the tested context after having sent an
 	 * initial pressed event. */
-	unused_libinput = litest_create_context();
+	_litest_context_destroy_ struct libinput *unused_libinput =
+		litest_create_context();
 	dev = litest_add_device_with_overrides(unused_libinput,
 					       LITEST_KEYBOARD,
 					       "Generic keyboard",
-					       NULL, NULL, events);
+					       NULL,
+					       NULL,
+					       events);
 
 	litest_keyboard_key(dev, KEY_A, true);
 	litest_drain_events(unused_libinput);
 
-	libinput = litest_create_context();
-	libinput_path_add_device(libinput,
-				 libevdev_uinput_get_devnode(dev->uinput));
+	_litest_context_destroy_ struct libinput *libinput = litest_create_context();
+	libinput_path_add_device(libinput, libevdev_uinput_get_devnode(dev->uinput));
 	litest_drain_events(libinput);
 
 	litest_keyboard_key(dev, KEY_A, false);
@@ -148,21 +146,18 @@ START_TEST(keyboard_ignore_no_pressed_re
 		litest_assert_int_eq(libinput_event_keyboard_get_key(kevent),
 				     (unsigned int)KEY_A);
 		litest_assert_int_eq(libinput_event_keyboard_get_key_state(kevent),
-				 *state);
+				     *state);
 		libinput_event_destroy(event);
 		litest_dispatch(libinput);
 	}
 
 	litest_assert_empty_queue(libinput);
-	litest_delete_device(dev);
-	litest_destroy_context(libinput);
-	litest_destroy_context(unused_libinput);
+	litest_device_destroy(dev);
 }
 END_TEST
 
 START_TEST(keyboard_key_auto_release)
 {
-	struct libinput *libinput;
 	struct litest_device *dev;
 	struct libinput_event *event;
 	enum libinput_event_type type;
@@ -171,13 +166,27 @@ START_TEST(keyboard_key_auto_release)
 		int code;
 		int released;
 	} keys[] = {
-		{ .code = KEY_A, },
-		{ .code = KEY_S, },
-		{ .code = KEY_D, },
-		{ .code = KEY_G, },
-		{ .code = KEY_Z, },
-		{ .code = KEY_DELETE, },
-		{ .code = KEY_F24, },
+		{
+			.code = KEY_A,
+		},
+		{
+			.code = KEY_S,
+		},
+		{
+			.code = KEY_D,
+		},
+		{
+			.code = KEY_G,
+		},
+		{
+			.code = KEY_Z,
+		},
+		{
+			.code = KEY_DELETE,
+		},
+		{
+			.code = KEY_F24,
+		},
 	};
 	int events[2 * (ARRAY_LENGTH(keys) + 1)];
 	unsigned i;
@@ -194,11 +203,13 @@ START_TEST(keyboard_key_auto_release)
 	events[i++] = -1;
 	events[i++] = -1;
 
-	libinput = litest_create_context();
+	_litest_context_destroy_ struct libinput *libinput = litest_create_context();
 	dev = litest_add_device_with_overrides(libinput,
 					       LITEST_KEYBOARD,
 					       "Generic keyboard",
-					       NULL, NULL, events);
+					       NULL,
+					       NULL,
+					       events);
 
 	litest_drain_events(libinput);
 
@@ -211,16 +222,14 @@ START_TEST(keyboard_key_auto_release)
 		litest_dispatch(libinput);
 
 		event = libinput_get_event(libinput);
-		litest_is_keyboard_event(event,
-					 key,
-					 LIBINPUT_KEY_STATE_PRESSED);
+		litest_is_keyboard_event(event, key, LIBINPUT_KEY_STATE_PRESSED);
 		libinput_event_destroy(event);
 	}
 
 	litest_drain_events(libinput);
 
 	/* "Disconnect" device */
-	litest_delete_device(dev);
+	litest_device_destroy(dev);
 
 	/* Mark all released keys until device is removed */
 	while (1) {
@@ -255,8 +264,6 @@ START_TEST(keyboard_key_auto_release)
 	for (i = 0; i < ARRAY_LENGTH(keys); ++i) {
 		litest_assert_int_eq(keys[i].released, 1);
 	}
-
-	litest_destroy_context(libinput);
 }
 END_TEST
 
@@ -267,9 +274,8 @@ START_TEST(keyboard_has_key)
 	unsigned int code;
 	int evdev_has, libinput_has;
 
-	litest_assert(libinput_device_has_capability(
-					 device,
-					 LIBINPUT_DEVICE_CAP_KEYBOARD));
+	litest_assert(
+		libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD));
 
 	for (code = 0; code < KEY_CNT; code++) {
 		evdev_has = libevdev_has_event_code(dev->evdev, EV_KEY, code);
@@ -286,8 +292,7 @@ START_TEST(keyboard_keys_bad_device)
 	unsigned int code;
 	int has_key;
 
-	if (libinput_device_has_capability(device,
-					   LIBINPUT_DEVICE_CAP_KEYBOARD))
+	if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD))
 		return LITEST_NOT_APPLICABLE;
 
 	for (code = 0; code < KEY_CNT; code++) {
@@ -314,13 +319,11 @@ START_TEST(keyboard_time_usec)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	kev = litest_is_keyboard_event(event,
-				       KEY_A,
-				       LIBINPUT_KEY_STATE_PRESSED);
+	kev = litest_is_keyboard_event(event, KEY_A, LIBINPUT_KEY_STATE_PRESSED);
 
 	time_usec = libinput_event_keyboard_get_time_usec(kev);
 	litest_assert_int_eq(libinput_event_keyboard_get_time(kev),
-			 (uint32_t) (time_usec / 1000));
+			     (uint32_t)(time_usec / 1000));
 
 	libinput_event_destroy(event);
 	litest_drain_events(dev->libinput);
@@ -350,14 +353,10 @@ START_TEST(keyboard_no_buttons)
 		litest_dispatch(li);
 
 		event = libinput_get_event(li);
-		litest_is_keyboard_event(event,
-					 code,
-					 LIBINPUT_KEY_STATE_PRESSED);
+		litest_is_keyboard_event(event, code, LIBINPUT_KEY_STATE_PRESSED);
 		libinput_event_destroy(event);
 		event = libinput_get_event(li);
-		litest_is_keyboard_event(event,
-					 code,
-					 LIBINPUT_KEY_STATE_RELEASED);
+		litest_is_keyboard_event(event, code, LIBINPUT_KEY_STATE_RELEASED);
 		libinput_event_destroy(event);
 	}
 }
@@ -379,9 +378,7 @@ START_TEST(keyboard_frame_order)
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 
-	litest_assert_key_event(li,
-				KEY_LEFTSHIFT,
-				LIBINPUT_KEY_STATE_PRESSED);
+	litest_assert_key_event(li, KEY_LEFTSHIFT, LIBINPUT_KEY_STATE_PRESSED);
 	litest_assert_key_event(li, KEY_A, LIBINPUT_KEY_STATE_PRESSED);
 
 	litest_event(dev, EV_KEY, KEY_LEFTSHIFT, 0);
@@ -389,9 +386,7 @@ START_TEST(keyboard_frame_order)
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 
-	litest_assert_key_event(li,
-				KEY_LEFTSHIFT,
-				LIBINPUT_KEY_STATE_RELEASED);
+	litest_assert_key_event(li, KEY_LEFTSHIFT, LIBINPUT_KEY_STATE_RELEASED);
 	litest_assert_key_event(li, KEY_A, LIBINPUT_KEY_STATE_RELEASED);
 
 	litest_event(dev, EV_KEY, KEY_A, 1);
@@ -400,9 +395,7 @@ START_TEST(keyboard_frame_order)
 	litest_dispatch(li);
 
 	litest_assert_key_event(li, KEY_A, LIBINPUT_KEY_STATE_PRESSED);
-	litest_assert_key_event(li,
-				KEY_LEFTSHIFT,
-				LIBINPUT_KEY_STATE_PRESSED);
+	litest_assert_key_event(li, KEY_LEFTSHIFT, LIBINPUT_KEY_STATE_PRESSED);
 
 	litest_event(dev, EV_KEY, KEY_A, 0);
 	litest_event(dev, EV_KEY, KEY_LEFTSHIFT, 0);
@@ -410,9 +403,7 @@ START_TEST(keyboard_frame_order)
 	litest_dispatch(li);
 
 	litest_assert_key_event(li, KEY_A, LIBINPUT_KEY_STATE_RELEASED);
-	litest_assert_key_event(li,
-				KEY_LEFTSHIFT,
-				LIBINPUT_KEY_STATE_RELEASED);
+	litest_assert_key_event(li, KEY_LEFTSHIFT, LIBINPUT_KEY_STATE_RELEASED);
 
 	litest_dispatch(li);
 }
@@ -428,30 +419,21 @@ START_TEST(keyboard_leds)
 	 * with and without LEDs and check that it doesn't go boom
 	 */
 
-	libinput_device_led_update(device,
-				   LIBINPUT_LED_NUM_LOCK);
-	libinput_device_led_update(device,
-				   LIBINPUT_LED_CAPS_LOCK);
-	libinput_device_led_update(device,
-				   LIBINPUT_LED_SCROLL_LOCK);
-	libinput_device_led_update(device,
-				   LIBINPUT_LED_COMPOSE);
-	libinput_device_led_update(device,
-				   LIBINPUT_LED_KANA);
+	libinput_device_led_update(device, LIBINPUT_LED_NUM_LOCK);
+	libinput_device_led_update(device, LIBINPUT_LED_CAPS_LOCK);
+	libinput_device_led_update(device, LIBINPUT_LED_SCROLL_LOCK);
+	libinput_device_led_update(device, LIBINPUT_LED_COMPOSE);
+	libinput_device_led_update(device, LIBINPUT_LED_KANA);
 
 	libinput_device_led_update(device,
-				   LIBINPUT_LED_NUM_LOCK |
-				   LIBINPUT_LED_CAPS_LOCK);
+				   LIBINPUT_LED_NUM_LOCK | LIBINPUT_LED_CAPS_LOCK);
 	libinput_device_led_update(device,
-				   LIBINPUT_LED_NUM_LOCK |
-				   LIBINPUT_LED_CAPS_LOCK |
-				   LIBINPUT_LED_SCROLL_LOCK);
+				   LIBINPUT_LED_NUM_LOCK | LIBINPUT_LED_CAPS_LOCK |
+					   LIBINPUT_LED_SCROLL_LOCK);
 	libinput_device_led_update(device,
-				   LIBINPUT_LED_NUM_LOCK |
-				   LIBINPUT_LED_CAPS_LOCK |
-				   LIBINPUT_LED_SCROLL_LOCK |
-				   LIBINPUT_LED_COMPOSE |
-				   LIBINPUT_LED_KANA);
+				   LIBINPUT_LED_NUM_LOCK | LIBINPUT_LED_CAPS_LOCK |
+					   LIBINPUT_LED_SCROLL_LOCK |
+					   LIBINPUT_LED_COMPOSE | LIBINPUT_LED_KANA);
 	libinput_device_led_update(device, 0);
 	libinput_device_led_update(device, -1);
 }
@@ -470,22 +452,167 @@ START_TEST(keyboard_no_scroll)
 	litest_assert_enum_eq(method, LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
 
 	status = libinput_device_config_scroll_set_method(device,
-				 LIBINPUT_CONFIG_SCROLL_2FG);
+							  LIBINPUT_CONFIG_SCROLL_2FG);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 	status = libinput_device_config_scroll_set_method(device,
-				 LIBINPUT_CONFIG_SCROLL_EDGE);
+							  LIBINPUT_CONFIG_SCROLL_EDGE);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
-	status = libinput_device_config_scroll_set_method(device,
-				 LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
+	status = libinput_device_config_scroll_set_method(
+		device,
+		LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
-	status = libinput_device_config_scroll_set_method(device,
-				 LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
+	status = libinput_device_config_scroll_set_method(
+		device,
+		LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 }
 END_TEST
 
+START_TEST(keyboard_alt_printscreen)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+
+	litest_drain_events(li);
+
+	/* normal key press, not ignored */
+	litest_event(dev, EV_KEY, KEY_LEFTALT, 1);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	litest_dispatch(li);
+	litest_assert_key_event(li, KEY_LEFTALT, LIBINPUT_KEY_STATE_PRESSED);
+
+	/* normal key repeat, ignored */
+	litest_event(dev, EV_KEY, KEY_LEFTALT, 2);
+	litest_event(dev, EV_SYN, SYN_REPORT, 1);
+	litest_dispatch(li);
+	litest_assert_empty_queue(li);
+
+	/* not a repeat, not ignored */
+	litest_event(dev, EV_KEY, KEY_LEFTALT, 0);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	litest_dispatch(li);
+	litest_assert_key_event(li, KEY_LEFTALT, LIBINPUT_KEY_STATE_RELEASED);
+	litest_assert_empty_queue(li);
+
+	/* special alt+printscreen frame, *not* ignored */
+	litest_event(dev, EV_KEY, KEY_LEFTALT, 1);
+	litest_event(dev, EV_KEY, KEY_SYSRQ, 1);
+	litest_event(dev, EV_SYN, SYN_REPORT, 1);
+	litest_dispatch(li);
+
+	/* special alt+printscreen frame, *not* ignored */
+	litest_event(dev, EV_KEY, KEY_LEFTALT, 0);
+	litest_event(dev, EV_KEY, KEY_SYSRQ, 0);
+	litest_event(dev, EV_SYN, SYN_REPORT, 1);
+	litest_dispatch(li);
+
+	/* Note: The kernel first releases KEY_LEFTALT when pressing KEY_SYSRQ,
+	 * then later generates press/release for KEY_LEFTALT + KEY_SYSRQ
+	 * once *both* keys are released. The order is reshuffled so we have
+	 * alt down, sysrq down, sysrq up, alt up.
+	 */
+	litest_assert_key_event(li, KEY_LEFTALT, LIBINPUT_KEY_STATE_PRESSED);
+	litest_assert_key_event(li, KEY_LEFTALT, LIBINPUT_KEY_STATE_RELEASED);
+
+	litest_assert_key_event(li, KEY_LEFTALT, LIBINPUT_KEY_STATE_PRESSED);
+	litest_assert_key_event(li, KEY_SYSRQ, LIBINPUT_KEY_STATE_PRESSED);
+	litest_assert_key_event(li, KEY_SYSRQ, LIBINPUT_KEY_STATE_RELEASED);
+	litest_assert_key_event(li, KEY_LEFTALT, LIBINPUT_KEY_STATE_RELEASED);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(keyboard_keycode_obfuscation)
+{
+#ifdef EVENT_DEBUGGING
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+
+	litest_drain_events(li);
+
+	litest_with_logcapture(li, capture) {
+		litest_event(dev, EV_KEY, KEY_Q, 1);
+		litest_event(dev, EV_SYN, SYN_REPORT, 0);
+		litest_event(dev, EV_KEY, KEY_Q, 0);
+		litest_event(dev, EV_SYN, SYN_REPORT, 0);
+		litest_dispatch(li);
+		litest_drain_events(li);
+
+		/* clang-format off */
+		/* We get two possible debug messages:
+		 *  Queuing  event14  KEYBOARD_KEY                 +0.000s KEY_Q (16) released
+		 *  event14: plugin evdev           - 0.000 EV_KEY           KEY_Q                   0
+		 *
+		 * Both KEY_Q must be obfuscated to KEY_A
+		 */
+		/* clang-format on */
+		char **strv = capture->debugs;
+		litest_assert_strv_no_substring(strv, "KEY_Q");
+
+		strv = capture->debugs;
+		size_t index;
+		litest_assert(strv_find_substring(strv, "KEY_A", &index));
+		do {
+			litest_assert_str_in("EV_KEY", strv[index]);
+			strv += index + 1;
+		} while (strv_find_substring(strv, "KEY_A", &index));
+	}
+#else
+	return LITEST_SKIP;
+#endif
+}
+END_TEST
+
+START_TEST(keyboard_nkey_rollover)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+
+	int nkeys = litest_test_param_get_i32(test_env->params, "nkeys");
+
+	litest_drain_events(li);
+
+	/* The kernel allocates a 7 + 1 buffer for devices without EV_ABS and
+	 * EV_REL, see
+	 * drivers/input/input.c:input_estimate_events_per_packet()
+	 *
+	 * If the device exceeds that buffer the current set is flushed out
+	 * as SYN_REPORT 1 followed by (if any) the remaining events immediately
+	 * after.
+	 *
+	 * Either way, we expect n keys to arrive, regardless what the kernel
+	 * does.
+	 */
+	for (int i = 0; i < nkeys; i++) {
+		litest_event_unchecked(dev, EV_KEY, KEY_A + i, 1);
+		litest_event_unchecked(dev, EV_MSC, MSC_SCAN, 0x1000 + i);
+	}
+	litest_event_unchecked(dev, EV_SYN, SYN_REPORT, 0);
+
+	for (int i = 0; i < nkeys; i++) {
+		litest_event_unchecked(dev, EV_KEY, KEY_A + i, 0);
+		litest_event_unchecked(dev, EV_MSC, MSC_SCAN, 0x1000 + i);
+	}
+	litest_event_unchecked(dev, EV_SYN, SYN_REPORT, 0);
+
+	litest_dispatch(li);
+
+	for (int i = 0; i < nkeys; i++) {
+		litest_checkpoint("here for %d", i);
+		litest_assert_key_event(li, KEY_A + i, LIBINPUT_KEY_STATE_PRESSED);
+	}
+	for (int i = 0; i < nkeys; i++) {
+		litest_assert_key_event(li, KEY_A + i, LIBINPUT_KEY_STATE_RELEASED);
+	}
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
 TEST_COLLECTION(keyboard)
 {
+	/* clang-format off */
 	litest_add_no_device(keyboard_seat_key_count);
 	litest_add_no_device(keyboard_ignore_no_pressed_release);
 	litest_add_no_device(keyboard_key_auto_release);
@@ -499,4 +626,13 @@ TEST_COLLECTION(keyboard)
 	litest_add(keyboard_leds, LITEST_ANY, LITEST_ANY);
 
 	litest_add(keyboard_no_scroll, LITEST_KEYS, LITEST_WHEEL);
+
+	litest_add_for_device(keyboard_alt_printscreen, LITEST_KEYBOARD);
+	litest_add_for_device(keyboard_keycode_obfuscation, LITEST_KEYBOARD);
+
+	/* Adding for one device only to be able to hardcode buffer sizes */
+	litest_with_parameters(params, "nkeys", 'i', 4, 3, 4, 5, 6)
+		litest_add_parametrized_for_device(keyboard_nkey_rollover, LITEST_KEYBOARD, params);
+
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-library-version.c 1.30.0-1/test/test-library-version.c
--- 1.28.1-1/test/test-library-version.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-library-version.c	2025-11-25 03:40:43.000000000 +0000
@@ -1,7 +1,9 @@
 #include <assert.h>
 #include <stdio.h>
 
-int main(void) {
+int
+main(void)
+{
 	const char *version = LIBINPUT_LT_VERSION;
 	int C, R, A;
 	int rc;
diff -pruN 1.28.1-1/test/test-log.c 1.30.0-1/test/test-log.c
--- 1.28.1-1/test/test-log.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-log.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,8 +26,8 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <libinput.h>
-#include <unistd.h>
 #include <stdarg.h>
+#include <unistd.h>
 
 #include "litest.h"
 
@@ -46,44 +46,41 @@ simple_log_handler(struct libinput *libi
 	litest_assert_notnull(format);
 }
 
-static int open_restricted(const char *path, int flags, void *data)
+static int
+open_restricted(const char *path, int flags, void *data)
 {
-       int fd;
-       fd = open(path, flags);
-       return fd < 0 ? -errno : fd;
+	int fd;
+	fd = open(path, flags);
+	return fd < 0 ? -errno : fd;
 }
-static void close_restricted(int fd, void *data)
+static void
+close_restricted(int fd, void *data)
 {
-       close(fd);
+	close(fd);
 }
 
 static const struct libinput_interface simple_interface = {
-       .open_restricted = open_restricted,
-       .close_restricted = close_restricted,
+	.open_restricted = open_restricted,
+	.close_restricted = close_restricted,
 };
 
 START_TEST(log_default_priority)
 {
 	enum libinput_log_priority pri;
-	struct libinput *li;
 
-	li = libinput_path_create_context(&simple_interface, NULL);
+	_unref_(libinput) *li = libinput_path_create_context(&simple_interface, NULL);
 	pri = libinput_log_get_priority(li);
 
 	litest_assert_enum_eq(pri, LIBINPUT_LOG_PRIORITY_ERROR);
-
-	libinput_unref(li);
 }
 END_TEST
 
 START_TEST(log_handler_invoked)
 {
-	struct libinput *li;
-
 	log_handler_context = NULL;
 	log_handler_called = 0;
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 
 	libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);
 	libinput_log_set_handler(li, simple_log_handler);
@@ -93,8 +90,6 @@ START_TEST(log_handler_invoked)
 
 	litest_assert_int_gt(log_handler_called, 0);
 
-	litest_destroy_context(li);
-
 	log_handler_context = NULL;
 	log_handler_called = 0;
 }
@@ -102,11 +97,9 @@ END_TEST
 
 START_TEST(log_handler_NULL)
 {
-	struct libinput *li;
-
 	log_handler_called = 0;
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);
 	libinput_log_set_handler(li, NULL);
 
@@ -114,20 +107,16 @@ START_TEST(log_handler_NULL)
 
 	litest_assert_int_eq(log_handler_called, 0);
 
-	litest_destroy_context(li);
-
 	log_handler_called = 0;
 }
 END_TEST
 
 START_TEST(log_priority)
 {
-	struct libinput *li;
-
 	log_handler_context = NULL;
 	log_handler_called = 0;
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_ERROR);
 	libinput_log_set_handler(li, simple_log_handler);
 	log_handler_context = li;
@@ -142,8 +131,6 @@ START_TEST(log_priority)
 	libinput_path_add_device(li, "/dev/input/event0");
 	litest_assert_int_gt(log_handler_called, 1);
 
-	litest_destroy_context(li);
-
 	log_handler_context = NULL;
 	log_handler_called = 0;
 }
@@ -157,13 +144,10 @@ axisrange_warning_log_handler(struct lib
 			      const char *format,
 			      va_list args)
 {
-	const char *substr;
-
 	axisrange_log_handler_called++;
 	litest_assert_notnull(format);
 
-	substr = strstr(format, "is outside expected range");
-	litest_assert_notnull(substr);
+	litest_assert_str_in("is outside expected range", format);
 }
 
 START_TEST(log_axisrange_warning)
@@ -182,7 +166,8 @@ START_TEST(log_axisrange_warning)
 	abs = libevdev_get_abs_info(dev->evdev, axis);
 
 	for (int i = 0; i < 100; i++) {
-		litest_event(dev, EV_ABS,
+		litest_event(dev,
+			     EV_ABS,
 			     ABS_MT_POSITION_X + axis,
 			     abs->maximum * 2 + i);
 		litest_event(dev, EV_ABS, axis, abs->maximum * 2);
@@ -201,6 +186,7 @@ END_TEST
 
 TEST_COLLECTION(log)
 {
+	/* clang-format off */
 	litest_add_deviceless(log_default_priority);
 	litest_add_deviceless(log_handler_invoked);
 	litest_add_deviceless(log_handler_NULL);
@@ -211,4 +197,5 @@ TEST_COLLECTION(log)
 		litest_add_parametrized(log_axisrange_warning, LITEST_TOUCH, LITEST_PROTOCOL_A, params);
 		litest_add_parametrized(log_axisrange_warning, LITEST_TOUCHPAD, LITEST_ANY, params);
 	}
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-misc.c 1.30.0-1/test/test-misc.c
--- 1.28.1-1/test/test-misc.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-misc.c	2025-11-25 03:40:43.000000000 +0000
@@ -25,20 +25,22 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <libinput.h>
 #include <libinput-util.h>
-#include <unistd.h>
+#include <libinput.h>
 #include <stdarg.h>
+#include <unistd.h>
 
-#include "litest.h"
 #include "libinput-util.h"
+#include "litest.h"
 
-static int open_restricted(const char *path, int flags, void *data)
+static int
+open_restricted(const char *path, int flags, void *data)
 {
 	int fd = open(path, flags);
 	return fd < 0 ? -errno : fd;
 }
-static void close_restricted(int fd, void *data)
+static void
+close_restricted(int fd, void *data)
 {
 	close(fd);
 }
@@ -93,10 +95,10 @@ create_simple_test_device(const char *na
 START_TEST(event_conversion_device_notify)
 {
 	struct libevdev_uinput *uinput;
-	struct libinput *li;
 	struct libinput_event *event;
 	int device_added = 0, device_removed = 0;
 
+	/* clang-format off */
 	uinput = create_simple_test_device("litest test device",
 					   EV_REL, REL_X,
 					   EV_REL, REL_Y,
@@ -104,7 +106,8 @@ START_TEST(event_conversion_device_notif
 					   EV_KEY, BTN_MIDDLE,
 					   EV_KEY, BTN_LEFT,
 					   -1, -1);
-	li = litest_create_context();
+	/* clang-format on */
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	litest_restore_log_handler(li); /* use the default litest handler */
 	libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 
@@ -134,8 +137,10 @@ START_TEST(event_conversion_device_notif
 			litest_assert(libinput_event_get_keyboard_event(event) == NULL);
 			litest_assert(libinput_event_get_touch_event(event) == NULL);
 			litest_assert(libinput_event_get_gesture_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_tool_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_pad_event(event) == NULL);
+			litest_assert(libinput_event_get_tablet_tool_event(event) ==
+				      NULL);
+			litest_assert(libinput_event_get_tablet_pad_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_switch_event(event) == NULL);
 			litest_restore_log_handler(li);
 		}
@@ -143,7 +148,6 @@ START_TEST(event_conversion_device_notif
 		libinput_event_destroy(event);
 	}
 
-	litest_destroy_context(li);
 	libevdev_uinput_destroy(uinput);
 
 	litest_assert_int_gt(device_added, 0);
@@ -188,12 +192,15 @@ START_TEST(event_conversion_pointer)
 				button++;
 
 			litest_disable_log_handler(li);
-			litest_assert(libinput_event_get_device_notify_event(event) == NULL);
+			litest_assert(libinput_event_get_device_notify_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_keyboard_event(event) == NULL);
 			litest_assert(libinput_event_get_touch_event(event) == NULL);
 			litest_assert(libinput_event_get_gesture_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_tool_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_pad_event(event) == NULL);
+			litest_assert(libinput_event_get_tablet_tool_event(event) ==
+				      NULL);
+			litest_assert(libinput_event_get_tablet_pad_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_switch_event(event) == NULL);
 			litest_restore_log_handler(li);
 		}
@@ -240,12 +247,15 @@ START_TEST(event_conversion_pointer_abs)
 				button++;
 
 			litest_disable_log_handler(li);
-			litest_assert(libinput_event_get_device_notify_event(event) == NULL);
+			litest_assert(libinput_event_get_device_notify_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_keyboard_event(event) == NULL);
 			litest_assert(libinput_event_get_touch_event(event) == NULL);
 			litest_assert(libinput_event_get_gesture_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_tool_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_pad_event(event) == NULL);
+			litest_assert(libinput_event_get_tablet_tool_event(event) ==
+				      NULL);
+			litest_assert(libinput_event_get_tablet_pad_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_switch_event(event) == NULL);
 			litest_restore_log_handler(li);
 		}
@@ -285,12 +295,15 @@ START_TEST(event_conversion_key)
 			key++;
 
 			litest_disable_log_handler(li);
-			litest_assert(libinput_event_get_device_notify_event(event) == NULL);
+			litest_assert(libinput_event_get_device_notify_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_pointer_event(event) == NULL);
 			litest_assert(libinput_event_get_touch_event(event) == NULL);
 			litest_assert(libinput_event_get_gesture_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_tool_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_pad_event(event) == NULL);
+			litest_assert(libinput_event_get_tablet_tool_event(event) ==
+				      NULL);
+			litest_assert(libinput_event_get_tablet_pad_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_switch_event(event) == NULL);
 			litest_restore_log_handler(li);
 		}
@@ -337,12 +350,15 @@ START_TEST(event_conversion_touch)
 			touch++;
 
 			litest_disable_log_handler(li);
-			litest_assert(libinput_event_get_device_notify_event(event) == NULL);
+			litest_assert(libinput_event_get_device_notify_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_pointer_event(event) == NULL);
 			litest_assert(libinput_event_get_keyboard_event(event) == NULL);
 			litest_assert(libinput_event_get_gesture_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_tool_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_pad_event(event) == NULL);
+			litest_assert(libinput_event_get_tablet_tool_event(event) ==
+				      NULL);
+			litest_assert(libinput_event_get_tablet_pad_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_switch_event(event) == NULL);
 			litest_restore_log_handler(li);
 		}
@@ -365,8 +381,7 @@ START_TEST(event_conversion_gesture)
 
 	litest_touch_down(dev, 0, 70, 30);
 	litest_touch_down(dev, 1, 30, 70);
-	litest_dispatch(li);
-	litest_timeout_gesture_hold();
+	litest_timeout_gesture_hold(li);
 
 	for (i = 0; i < 8; i++) {
 		litest_push_event_frame(dev);
@@ -391,11 +406,13 @@ START_TEST(event_conversion_gesture)
 			gestures++;
 
 			litest_disable_log_handler(li);
-			litest_assert(libinput_event_get_device_notify_event(event) == NULL);
+			litest_assert(libinput_event_get_device_notify_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_pointer_event(event) == NULL);
 			litest_assert(libinput_event_get_keyboard_event(event) == NULL);
 			litest_assert(libinput_event_get_touch_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_pad_event(event) == NULL);
+			litest_assert(libinput_event_get_tablet_pad_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_switch_event(event) == NULL);
 			litest_restore_log_handler(li);
 		}
@@ -414,7 +431,7 @@ START_TEST(event_conversion_tablet)
 	int events = 0;
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_tablet_proximity_in(dev, 50, 50, axes);
@@ -439,11 +456,13 @@ START_TEST(event_conversion_tablet)
 			events++;
 
 			litest_disable_log_handler(li);
-			litest_assert(libinput_event_get_device_notify_event(event) == NULL);
+			litest_assert(libinput_event_get_device_notify_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_pointer_event(event) == NULL);
 			litest_assert(libinput_event_get_keyboard_event(event) == NULL);
 			litest_assert(libinput_event_get_touch_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_pad_event(event) == NULL);
+			litest_assert(libinput_event_get_tablet_pad_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_switch_event(event) == NULL);
 			litest_restore_log_handler(li);
 		}
@@ -483,11 +502,13 @@ START_TEST(event_conversion_tablet_pad)
 			events++;
 
 			litest_disable_log_handler(li);
-			litest_assert(libinput_event_get_device_notify_event(event) == NULL);
+			litest_assert(libinput_event_get_device_notify_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_pointer_event(event) == NULL);
 			litest_assert(libinput_event_get_keyboard_event(event) == NULL);
 			litest_assert(libinput_event_get_touch_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_tool_event(event) == NULL);
+			litest_assert(libinput_event_get_tablet_tool_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_switch_event(event) == NULL);
 			litest_restore_log_handler(li);
 		}
@@ -505,12 +526,8 @@ START_TEST(event_conversion_switch)
 	struct libinput_event *event;
 	int sw = 0;
 
-	litest_switch_action(dev,
-			     LIBINPUT_SWITCH_LID,
-			     LIBINPUT_SWITCH_STATE_ON);
-	litest_switch_action(dev,
-			     LIBINPUT_SWITCH_LID,
-			     LIBINPUT_SWITCH_STATE_OFF);
+	litest_switch_action(dev, LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(dev, LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_OFF);
 	litest_dispatch(li);
 
 	while ((event = libinput_get_event(li))) {
@@ -527,13 +544,16 @@ START_TEST(event_conversion_switch)
 			sw++;
 
 			litest_disable_log_handler(li);
-			litest_assert(libinput_event_get_device_notify_event(event) == NULL);
+			litest_assert(libinput_event_get_device_notify_event(event) ==
+				      NULL);
 			litest_assert(libinput_event_get_keyboard_event(event) == NULL);
 			litest_assert(libinput_event_get_pointer_event(event) == NULL);
 			litest_assert(libinput_event_get_touch_event(event) == NULL);
 			litest_assert(libinput_event_get_gesture_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_tool_event(event) == NULL);
-			litest_assert(libinput_event_get_tablet_pad_event(event) == NULL);
+			litest_assert(libinput_event_get_tablet_tool_event(event) ==
+				      NULL);
+			litest_assert(libinput_event_get_tablet_pad_event(event) ==
+				      NULL);
 			litest_restore_log_handler(li);
 		}
 		libinput_event_destroy(event);
@@ -583,12 +603,14 @@ START_TEST(config_status_string)
 }
 END_TEST
 
-static int open_restricted_leak(const char *path, int flags, void *data)
+static int
+open_restricted_leak(const char *path, int flags, void *data)
 {
-	return *(int*)data;
+	return *(int *)data;
 }
 
-static void close_restricted_leak(int fd, void *data)
+static void
+close_restricted_leak(int fd, void *data)
 {
 	/* noop */
 }
@@ -607,6 +629,7 @@ START_TEST(fd_no_event_leak)
 	const char *path;
 	struct libinput_event *event;
 
+	/* clang-format off */
 	uinput = create_simple_test_device("litest test device",
 					   EV_REL, REL_X,
 					   EV_REL, REL_Y,
@@ -614,6 +637,7 @@ START_TEST(fd_no_event_leak)
 					   EV_KEY, BTN_MIDDLE,
 					   EV_KEY, BTN_LEFT,
 					   -1, -1);
+	/* clang-format on */
 	path = libevdev_uinput_get_devnode(uinput);
 
 	fd = open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC);
@@ -653,10 +677,11 @@ START_TEST(fd_no_event_leak)
 }
 END_TEST
 
-static void timer_offset_warning(struct libinput *libinput,
-				 enum libinput_log_priority priority,
-				 const char *format,
-				 va_list args)
+static void
+timer_offset_warning(struct libinput *libinput,
+		     enum libinput_log_priority priority,
+		     const char *format,
+		     va_list args)
 {
 	struct litest_user_data *user_data = libinput_get_user_data(libinput);
 	int *warning_triggered = user_data->private;
@@ -679,7 +704,7 @@ START_TEST(timer_offset_bug_warning)
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_up(dev, 0);
 
-	litest_timeout_tap();
+	litest_timeout_tap(NULL);
 
 	user_data->private = &warning_triggered;
 	libinput_log_set_handler(li, timer_offset_warning);
@@ -691,10 +716,11 @@ START_TEST(timer_offset_bug_warning)
 }
 END_TEST
 
-static void timer_delay_warning(struct libinput *libinput,
-				enum libinput_log_priority priority,
-				const char *format,
-				va_list args)
+static void
+timer_delay_warning(struct libinput *libinput,
+		    enum libinput_log_priority priority,
+		    const char *format,
+		    va_list args)
 {
 	struct litest_user_data *user_data = libinput_get_user_data(libinput);
 	int *warning_triggered = user_data->private;
@@ -730,29 +756,23 @@ END_TEST
 
 START_TEST(timer_flush)
 {
-	struct libinput *li;
-	struct litest_device *keyboard, *touchpad;
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 
-	li = litest_create_context();
-
-	touchpad = litest_add_device(li, LITEST_SYNAPTICS_TOUCHPAD);
+	_destroy_(litest_device) *touchpad =
+		litest_add_device(li, LITEST_SYNAPTICS_TOUCHPAD);
 	litest_enable_tap(touchpad->libinput_device);
 	litest_dispatch(li);
-	keyboard = litest_add_device(li, LITEST_KEYBOARD);
+	_destroy_(litest_device) *keyboard = litest_add_device(li, LITEST_KEYBOARD);
 	litest_dispatch(li);
 	litest_drain_events(li);
 
 	/* make sure tapping works */
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_up(touchpad, 0);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 
 	/* make sure dwt-tap is ignored */
@@ -761,9 +781,7 @@ START_TEST(timer_flush)
 	litest_dispatch(li);
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_up(touchpad, 0);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
 
 	/* Ignore 'timer offset negative' warnings */
@@ -779,25 +797,16 @@ START_TEST(timer_flush)
 	 */
 	litest_keyboard_key(keyboard, KEY_A, true);
 	litest_keyboard_key(keyboard, KEY_A, false);
-	litest_timeout_dwt_long();
+	litest_timeout_dwt_long(li);
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_up(touchpad, 0);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 	litest_restore_log_handler(li);
 
 	litest_assert_key_event(li, KEY_A, LIBINPUT_KEY_STATE_PRESSED);
 	litest_assert_key_event(li, KEY_A, LIBINPUT_KEY_STATE_RELEASED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-
-	litest_delete_device(keyboard);
-	litest_delete_device(touchpad);
-
-	litest_destroy_context(li);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -808,8 +817,7 @@ START_TEST(udev_absinfo_override)
 	const struct input_absinfo *abs;
 	struct udev_device *ud;
 	struct udev_list_entry *entry;
-	bool found_x = false, found_y = false,
-	     found_mt_x = false, found_mt_y = false;
+	bool found_x = false, found_y = false, found_mt_x = false, found_mt_y = false;
 
 	ud = libinput_device_get_udev_device(dev->libinput_device);
 	litest_assert_notnull(ud);
@@ -881,6 +889,7 @@ END_TEST
 
 TEST_COLLECTION(misc)
 {
+	/* clang-format off */
 	litest_add_no_device(event_conversion_device_notify);
 	litest_add_for_device(event_conversion_pointer, LITEST_MOUSE);
 	litest_add_for_device(event_conversion_pointer_abs, LITEST_XEN_VIRTUAL_POINTER);
@@ -901,4 +910,5 @@ TEST_COLLECTION(misc)
 	litest_add_no_device(fd_no_event_leak);
 
 	litest_add_for_device(udev_absinfo_override, LITEST_ABSINFO_OVERRIDE);
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-pad.c 1.30.0-1/test/test-pad.c
--- 1.28.1-1/test/test-pad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-pad.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,10 +26,10 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <libinput.h>
-#include <unistd.h>
 #include <stdbool.h>
+#include <unistd.h>
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 #include <libwacom/libwacom.h>
 #endif
 
@@ -41,9 +41,8 @@ START_TEST(pad_cap)
 	struct litest_device *dev = litest_current_device();
 	struct libinput_device *device = dev->libinput_device;
 
-	litest_assert(libinput_device_has_capability(device,
-						 LIBINPUT_DEVICE_CAP_TABLET_PAD));
-
+	litest_assert(
+		libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_PAD));
 }
 END_TEST
 
@@ -53,7 +52,7 @@ START_TEST(pad_no_cap)
 	struct libinput_device *device = dev->libinput_device;
 
 	litest_assert(!libinput_device_has_capability(device,
-						  LIBINPUT_DEVICE_CAP_TABLET_PAD));
+						      LIBINPUT_DEVICE_CAP_TABLET_PAD));
 }
 END_TEST
 
@@ -93,7 +92,7 @@ START_TEST(pad_time)
 	time_usec = libinput_event_tablet_pad_get_time_usec(pev);
 
 	litest_assert(time != 0);
-	litest_assert(time == time_usec/1000);
+	litest_assert(time == time_usec / 1000);
 
 	libinput_event_destroy(ev);
 
@@ -114,7 +113,7 @@ START_TEST(pad_time)
 
 	litest_assert(time > oldtime);
 	litest_assert(time != 0);
-	litest_assert(time == time_usec/1000);
+	litest_assert(time == time_usec / 1000);
 
 	libinput_event_destroy(ev);
 }
@@ -122,7 +121,7 @@ END_TEST
 
 START_TEST(pad_num_buttons_libwacom)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *dev = litest_current_device();
 	struct libinput_device *device = dev->libinput_device;
 	WacomDeviceDatabase *db = NULL;
@@ -167,13 +166,13 @@ START_TEST(pad_num_buttons)
 	}
 
 	litest_assert_int_eq(libinput_device_tablet_pad_get_num_buttons(device),
-			 nbuttons);
+			     nbuttons);
 }
 END_TEST
 
 START_TEST(pad_button_intuos)
 {
-#if !HAVE_LIBWACOM
+#ifndef HAVE_LIBWACOM
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	unsigned int code;
@@ -191,10 +190,9 @@ START_TEST(pad_button_intuos)
 
 	for (code = BTN_0; code < BTN_DIGI; code++) {
 		/* Skip over the BTN_MOUSE and BTN_JOYSTICK range */
-		if ((code >= BTN_MOUSE && code < BTN_JOYSTICK) ||
-		    (code >= BTN_DIGI)) {
-			litest_assert(!libevdev_has_event_code(dev->evdev,
-							   EV_KEY, code));
+		if ((code >= BTN_MOUSE && code < BTN_JOYSTICK) || (code >= BTN_DIGI)) {
+			litest_assert(
+				!libevdev_has_event_code(dev->evdev, EV_KEY, code));
 			continue;
 		}
 
@@ -233,7 +231,7 @@ END_TEST
 
 START_TEST(pad_button_bamboo)
 {
-#if !HAVE_LIBWACOM
+#ifndef HAVE_LIBWACOM
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	unsigned int code;
@@ -283,7 +281,7 @@ END_TEST
 
 START_TEST(pad_button_libwacom)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	WacomDeviceDatabase *db = NULL;
@@ -309,12 +307,8 @@ START_TEST(pad_button_libwacom)
 		litest_button_click(dev, code, 0);
 		litest_dispatch(li);
 
-		litest_assert_pad_button_event(li,
-					       i,
-					       LIBINPUT_BUTTON_STATE_PRESSED);
-		litest_assert_pad_button_event(li,
-					       i,
-					       LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_pad_button_event(li, i, LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_pad_button_event(li, i, LIBINPUT_BUTTON_STATE_RELEASED);
 	}
 
 	libwacom_destroy(wacom);
@@ -330,8 +324,8 @@ START_TEST(pad_button_mode_groups)
 	struct libinput_event *ev;
 	struct libinput_event_tablet_pad *pev;
 	unsigned int expected_mode = 0;
-	int evdev_codes[KEY_OK - BTN_0] = {0};
-#if HAVE_LIBWACOM
+	int evdev_codes[KEY_OK - BTN_0] = { 0 };
+#ifdef HAVE_LIBWACOM
 	WacomDeviceDatabase *db = NULL;
 	WacomDevice *wacom = NULL;
 
@@ -358,7 +352,8 @@ START_TEST(pad_button_mode_groups)
 
 	litest_drain_events(li);
 
-	for (unsigned int b = 0; b < ARRAY_LENGTH(evdev_codes) && evdev_codes[b] != 0; b++) {
+	for (unsigned int b = 0; b < ARRAY_LENGTH(evdev_codes) && evdev_codes[b] != 0;
+	     b++) {
 		unsigned int code = evdev_codes[b];
 		unsigned int mode, index;
 		struct libinput_tablet_pad_mode_group *group;
@@ -388,17 +383,26 @@ START_TEST(pad_button_mode_groups)
 		 * so we hardcode these here in the test */
 		if (dev->which == LITEST_WACOM_MOBILESTUDIO_PRO_16_PAD) {
 			switch (code) {
-			case BTN_9: expected_mode = 0; break;
-			case BTN_A: expected_mode = 1; break;
-			case BTN_B: expected_mode = 2; break;
-			case BTN_C: expected_mode = 3; break;
+			case BTN_9:
+				expected_mode = 0;
+				break;
+			case BTN_A:
+				expected_mode = 1;
+				break;
+			case BTN_B:
+				expected_mode = 2;
+				break;
+			case BTN_C:
+				expected_mode = 3;
+				break;
 			default:
 				break;
 			}
 		} else
 #endif
-		if (libinput_tablet_pad_mode_group_button_is_toggle(group, b)) {
-			int num_modes = libinput_tablet_pad_mode_group_get_num_modes(group);
+			if (libinput_tablet_pad_mode_group_button_is_toggle(group, b)) {
+			int num_modes =
+				libinput_tablet_pad_mode_group_get_num_modes(group);
 			expected_mode = (expected_mode + 1) % num_modes;
 		}
 		mode = libinput_event_tablet_pad_get_mode(pev);
@@ -457,7 +461,7 @@ START_TEST(pad_ring)
 
 	min = libevdev_get_abs_minimum(dev->evdev, ABS_WHEEL);
 	max = libevdev_get_abs_maximum(dev->evdev, ABS_WHEEL);
-	step_size = 360/(max - min + 1);
+	step_size = 360 / (max - min + 1);
 
 	/* This is a bit strange because we rely on kernel filtering here.
 	   The litest_*() functions take a percentage, but mapping this to
@@ -491,7 +495,7 @@ START_TEST(pad_ring)
 		expected = fmod(degrees + step_size, 360);
 	}
 
-	litest_assert_int_eq(nevents, 360/step_size - 1);
+	litest_assert_int_eq(nevents, 360 / step_size - 1);
 
 	litest_pad_ring_end(dev);
 }
@@ -513,9 +517,7 @@ START_TEST(pad_ring_finger_up)
 	litest_dispatch(li);
 
 	ev = libinput_get_event(li);
-	pev = litest_is_pad_ring_event(ev,
-				       0,
-				       LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
+	pev = litest_is_pad_ring_event(ev, 0, LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER);
 
 	degrees = libinput_event_tablet_pad_get_ring_position(pev);
 	litest_assert_double_eq(degrees, -1.0);
@@ -643,9 +645,10 @@ START_TEST(pad_strip)
 		litest_dispatch(li);
 
 		ev = libinput_get_event(li);
-		pev = litest_is_pad_strip_event(ev,
-						0,
-						LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
+		pev = litest_is_pad_strip_event(
+			ev,
+			0,
+			LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
 
 		pos = libinput_event_tablet_pad_get_strip_position(pev);
 		litest_assert_double_ge(pos, 0.0);
@@ -664,9 +667,7 @@ START_TEST(pad_strip)
 	litest_dispatch(li);
 
 	ev = libinput_get_event(li);
-	pev = litest_is_pad_strip_event(ev,
-					   0,
-					   LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
+	pev = litest_is_pad_strip_event(ev, 0, LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
 	pos = libinput_event_tablet_pad_get_strip_position(pev);
 	litest_assert_double_eq(pos, 1.0);
 	libinput_event_destroy(ev);
@@ -690,9 +691,7 @@ START_TEST(pad_strip_finger_up)
 	litest_dispatch(li);
 
 	ev = libinput_get_event(li);
-	pev = litest_is_pad_strip_event(ev,
-					0,
-					LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
+	pev = litest_is_pad_strip_event(ev, 0, LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER);
 
 	pos = libinput_event_tablet_pad_get_strip_position(pev);
 	litest_assert_double_eq(pos, -1.0);
@@ -704,33 +703,27 @@ END_TEST
 
 START_TEST(pad_left_handed_default)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *dev = litest_current_device();
 	struct libinput_device *device = dev->libinput_device;
 	enum libinput_config_status status;
 
 	litest_assert(libinput_device_config_left_handed_is_available(device));
 
-	litest_assert_int_eq(libinput_device_config_left_handed_get_default(device),
-			 0);
-	litest_assert_int_eq(libinput_device_config_left_handed_get(device),
-			 0);
+	litest_assert_int_eq(libinput_device_config_left_handed_get_default(device), 0);
+	litest_assert_int_eq(libinput_device_config_left_handed_get(device), 0);
 
 	status = libinput_device_config_left_handed_set(dev->libinput_device, 1);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
-	litest_assert_int_eq(libinput_device_config_left_handed_get(device),
-			 1);
-	litest_assert_int_eq(libinput_device_config_left_handed_get_default(device),
-			 0);
+	litest_assert_int_eq(libinput_device_config_left_handed_get(device), 1);
+	litest_assert_int_eq(libinput_device_config_left_handed_get_default(device), 0);
 
 	status = libinput_device_config_left_handed_set(dev->libinput_device, 0);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
-	litest_assert_int_eq(libinput_device_config_left_handed_get(device),
-			 0);
-	litest_assert_int_eq(libinput_device_config_left_handed_get_default(device),
-			 0);
+	litest_assert_int_eq(libinput_device_config_left_handed_get(device), 0);
+	litest_assert_int_eq(libinput_device_config_left_handed_get_default(device), 0);
 
 #endif
 }
@@ -742,41 +735,35 @@ START_TEST(pad_no_left_handed)
 	struct libinput_device *device = dev->libinput_device;
 
 	/* Without libwacom we default to left-handed being available */
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	litest_assert(!libinput_device_config_left_handed_is_available(device));
 #else
 	litest_assert(libinput_device_config_left_handed_is_available(device));
 #endif
 
-	litest_assert_int_eq(libinput_device_config_left_handed_get_default(device),
-			 0);
-	litest_assert_int_eq(libinput_device_config_left_handed_get(device),
-			 0);
+	litest_assert_int_eq(libinput_device_config_left_handed_get_default(device), 0);
+	litest_assert_int_eq(libinput_device_config_left_handed_get(device), 0);
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	enum libinput_config_status status;
 	status = libinput_device_config_left_handed_set(dev->libinput_device, 1);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 
-	litest_assert_int_eq(libinput_device_config_left_handed_get(device),
-			 0);
-	litest_assert_int_eq(libinput_device_config_left_handed_get_default(device),
-			 0);
+	litest_assert_int_eq(libinput_device_config_left_handed_get(device), 0);
+	litest_assert_int_eq(libinput_device_config_left_handed_get_default(device), 0);
 
 	status = libinput_device_config_left_handed_set(dev->libinput_device, 0);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 
-	litest_assert_int_eq(libinput_device_config_left_handed_get(device),
-			 0);
-	litest_assert_int_eq(libinput_device_config_left_handed_get_default(device),
-			 0);
+	litest_assert_int_eq(libinput_device_config_left_handed_get(device), 0);
+	litest_assert_int_eq(libinput_device_config_left_handed_get_default(device), 0);
 #endif
 }
 END_TEST
 
 START_TEST(pad_left_handed_ring)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	struct libinput_event *ev;
@@ -824,7 +811,7 @@ static bool
 pad_has_groups(struct litest_device *dev)
 {
 	bool rc = false;
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	WacomDeviceDatabase *db = NULL;
 	WacomDevice *wacom = NULL;
 
@@ -835,10 +822,9 @@ pad_has_groups(struct litest_device *dev
 					libevdev_get_id_vendor(dev->evdev),
 					libevdev_get_id_product(dev->evdev),
 					NULL);
-	if (wacom &&
-	    (libwacom_get_ring_num_modes(wacom) != 0 ||
-	    libwacom_get_ring2_num_modes(wacom) != 0 ||
-	    libwacom_get_strips_num_modes(wacom) != 0))
+	if (wacom && (libwacom_get_ring_num_modes(wacom) != 0 ||
+		      libwacom_get_ring2_num_modes(wacom) != 0 ||
+		      libwacom_get_strips_num_modes(wacom) != 0))
 		rc = true;
 
 	libwacom_destroy(wacom);
@@ -862,7 +848,7 @@ START_TEST(pad_mode_groups)
 		group = libinput_device_tablet_pad_get_mode_group(device, i);
 		litest_assert_notnull(group);
 		litest_assert_int_eq(libinput_tablet_pad_mode_group_get_index(group),
-				 i);
+				     i);
 	}
 
 	group = libinput_device_tablet_pad_get_mode_group(device, ngroups);
@@ -881,15 +867,12 @@ START_TEST(pad_mode_groups_userdata)
 	void *userdata = &rc;
 
 	group = libinput_device_tablet_pad_get_mode_group(device, 0);
-	litest_assert(libinput_tablet_pad_mode_group_get_user_data(group) ==
-		  NULL);
+	litest_assert(libinput_tablet_pad_mode_group_get_user_data(group) == NULL);
 	libinput_tablet_pad_mode_group_set_user_data(group, userdata);
-	litest_assert(libinput_tablet_pad_mode_group_get_user_data(group) ==
-		  &rc);
+	litest_assert(libinput_tablet_pad_mode_group_get_user_data(group) == &rc);
 
 	libinput_tablet_pad_mode_group_set_user_data(group, NULL);
-	litest_assert(libinput_tablet_pad_mode_group_get_user_data(group) ==
-		  NULL);
+	litest_assert(libinput_tablet_pad_mode_group_get_user_data(group) == NULL);
 }
 END_TEST
 
@@ -952,10 +935,8 @@ START_TEST(pad_mode_group_has)
 	for (b = 0; b < nbuttons; b++) {
 		bool found = false;
 		for (i = 0; i < ngroups; i++) {
-			group = libinput_device_tablet_pad_get_mode_group(device,
-									  i);
-			if (libinput_tablet_pad_mode_group_has_button(group,
-								      b)) {
+			group = libinput_device_tablet_pad_get_mode_group(device, i);
+			if (libinput_tablet_pad_mode_group_has_button(group, b)) {
 				litest_assert(!found);
 				found = true;
 			}
@@ -966,10 +947,8 @@ START_TEST(pad_mode_group_has)
 	for (s = 0; s < nstrips; s++) {
 		bool found = false;
 		for (i = 0; i < ngroups; i++) {
-			group = libinput_device_tablet_pad_get_mode_group(device,
-									  i);
-			if (libinput_tablet_pad_mode_group_has_strip(group,
-								     s)) {
+			group = libinput_device_tablet_pad_get_mode_group(device, i);
+			if (libinput_tablet_pad_mode_group_has_strip(group, s)) {
 				litest_assert(!found);
 				found = true;
 			}
@@ -980,10 +959,8 @@ START_TEST(pad_mode_group_has)
 	for (r = 0; r < nrings; r++) {
 		bool found = false;
 		for (i = 0; i < ngroups; i++) {
-			group = libinput_device_tablet_pad_get_mode_group(device,
-									  i);
-			if (libinput_tablet_pad_mode_group_has_ring(group,
-								    r)) {
+			group = libinput_device_tablet_pad_get_mode_group(device, i);
+			if (libinput_tablet_pad_mode_group_has_ring(group, r)) {
 				litest_assert(!found);
 				found = true;
 			}
@@ -997,7 +974,7 @@ START_TEST(pad_mode_group_has_invalid)
 {
 	struct litest_device *dev = litest_current_device();
 	struct libinput_device *device = dev->libinput_device;
-	struct libinput_tablet_pad_mode_group* group;
+	struct libinput_tablet_pad_mode_group *group;
 	int ngroups, nbuttons, nrings, nstrips;
 	int i;
 	int rc;
@@ -1011,40 +988,31 @@ START_TEST(pad_mode_group_has_invalid)
 
 	for (i = 0; i < ngroups; i++) {
 		group = libinput_device_tablet_pad_get_mode_group(device, i);
-		rc = libinput_tablet_pad_mode_group_has_button(group,
-							       nbuttons);
+		rc = libinput_tablet_pad_mode_group_has_button(group, nbuttons);
 		litest_assert_int_eq(rc, 0);
-		rc = libinput_tablet_pad_mode_group_has_button(group,
-							       nbuttons + 1);
+		rc = libinput_tablet_pad_mode_group_has_button(group, nbuttons + 1);
 		litest_assert_int_eq(rc, 0);
-		rc = libinput_tablet_pad_mode_group_has_button(group,
-							       0x1000000);
+		rc = libinput_tablet_pad_mode_group_has_button(group, 0x1000000);
 		litest_assert_int_eq(rc, 0);
 	}
 
 	for (i = 0; i < ngroups; i++) {
 		group = libinput_device_tablet_pad_get_mode_group(device, i);
-		rc = libinput_tablet_pad_mode_group_has_strip(group,
-							      nstrips);
+		rc = libinput_tablet_pad_mode_group_has_strip(group, nstrips);
 		litest_assert_int_eq(rc, 0);
-		rc = libinput_tablet_pad_mode_group_has_strip(group,
-							       nstrips + 1);
+		rc = libinput_tablet_pad_mode_group_has_strip(group, nstrips + 1);
 		litest_assert_int_eq(rc, 0);
-		rc = libinput_tablet_pad_mode_group_has_strip(group,
-							       0x1000000);
+		rc = libinput_tablet_pad_mode_group_has_strip(group, 0x1000000);
 		litest_assert_int_eq(rc, 0);
 	}
 
 	for (i = 0; i < ngroups; i++) {
 		group = libinput_device_tablet_pad_get_mode_group(device, i);
-		rc = libinput_tablet_pad_mode_group_has_ring(group,
-							     nrings);
+		rc = libinput_tablet_pad_mode_group_has_ring(group, nrings);
 		litest_assert_int_eq(rc, 0);
-		rc = libinput_tablet_pad_mode_group_has_ring(group,
-							     nrings + 1);
+		rc = libinput_tablet_pad_mode_group_has_ring(group, nrings + 1);
 		litest_assert_int_eq(rc, 0);
-		rc = libinput_tablet_pad_mode_group_has_ring(group,
-							     0x1000000);
+		rc = libinput_tablet_pad_mode_group_has_ring(group, 0x1000000);
 		litest_assert_int_eq(rc, 0);
 	}
 }
@@ -1052,10 +1020,10 @@ END_TEST
 
 START_TEST(pad_mode_group_has_no_toggle)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *dev = litest_current_device();
 	struct libinput_device *device = dev->libinput_device;
-	struct libinput_tablet_pad_mode_group* group;
+	struct libinput_tablet_pad_mode_group *group;
 	int ngroups, nbuttons;
 	int i, b;
 
@@ -1070,9 +1038,9 @@ START_TEST(pad_mode_group_has_no_toggle)
 	for (i = 0; i < ngroups; i++) {
 		group = libinput_device_tablet_pad_get_mode_group(device, i);
 		for (b = 0; b < nbuttons; b++) {
-			litest_assert(!libinput_tablet_pad_mode_group_button_is_toggle(
-								    group,
-								    b));
+			litest_assert(
+				!libinput_tablet_pad_mode_group_button_is_toggle(group,
+										 b));
 		}
 	}
 #endif
@@ -1086,7 +1054,7 @@ pad_has_keys(struct litest_device *dev)
 
 	return (libevdev_has_event_code(evdev, EV_KEY, KEY_BUTTONCONFIG) ||
 		libevdev_has_event_code(evdev, EV_KEY, KEY_ONSCREEN_KEYBOARD) ||
-	        libevdev_has_event_code(evdev, EV_KEY, KEY_CONTROLPANEL));
+		libevdev_has_event_code(evdev, EV_KEY, KEY_CONTROLPANEL));
 }
 
 static void
@@ -1145,8 +1113,43 @@ START_TEST(pad_keys)
 }
 END_TEST
 
+START_TEST(pad_send_events_disabled)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	enum libinput_config_status status;
+
+	litest_drain_events(li);
+
+	status = libinput_device_config_send_events_set_mode(
+		dev->libinput_device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+
+	litest_pad_strip_start(dev, 10);
+	litest_assert_empty_queue(li);
+	litest_pad_strip_change(dev, 100);
+	litest_assert_empty_queue(li);
+	litest_pad_strip_end(dev);
+	litest_assert_empty_queue(li);
+
+	litest_pad_ring_start(dev, 10);
+	litest_assert_empty_queue(li);
+	litest_pad_ring_change(dev, 100);
+	litest_assert_empty_queue(li);
+	litest_pad_ring_end(dev);
+	litest_assert_empty_queue(li);
+
+	pad_key_down(dev, KEY_BUTTONCONFIG);
+	litest_assert_empty_queue(li);
+	pad_key_up(dev, KEY_BUTTONCONFIG);
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
 TEST_COLLECTION(pad)
 {
+	/* clang-format off */
 	litest_add(pad_cap, LITEST_TABLET_PAD, LITEST_ANY);
 	litest_add(pad_no_cap, LITEST_ANY, LITEST_TABLET_PAD);
 
@@ -1187,4 +1190,7 @@ TEST_COLLECTION(pad)
 	litest_add(pad_mode_group_has_no_toggle, LITEST_TABLET_PAD, LITEST_ANY);
 
 	litest_add(pad_keys, LITEST_TABLET_PAD, LITEST_ANY);
+
+	litest_add(pad_send_events_disabled, LITEST_TABLET_PAD, LITEST_ANY);
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-path.c 1.30.0-1/test/test-path.c
--- 1.28.1-1/test/test-path.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-path.c	2025-11-25 03:40:43.000000000 +0000
@@ -30,8 +30,8 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "litest.h"
 #include "libinput-util.h"
+#include "litest.h"
 
 struct counter {
 	int open_func_count;
@@ -193,10 +193,9 @@ START_TEST(path_create_pathmax_file)
 {
 	struct libinput *li;
 	struct libinput_device *device;
-	char *path;
 	struct counter counter;
 
-	path = zalloc(PATH_MAX * 2);
+	_autofree_ char *path = zalloc(PATH_MAX * 2);
 	memset(path, 'a', PATH_MAX * 2 - 1);
 
 	counter.open_func_count = 0;
@@ -215,8 +214,6 @@ START_TEST(path_create_pathmax_file)
 	litest_restore_log_handler(li);
 	libinput_unref(li);
 	litest_assert_int_eq(counter.close_func_count, 0);
-
-	free(path);
 }
 END_TEST
 
@@ -230,12 +227,14 @@ START_TEST(path_create_destroy)
 	counter.open_func_count = 0;
 	counter.close_func_count = 0;
 
+	/* clang-format off */
 	uinput = litest_create_uinput_device("test device", NULL,
 					     EV_KEY, BTN_LEFT,
 					     EV_KEY, BTN_RIGHT,
 					     EV_REL, REL_X,
 					     EV_REL, REL_Y,
 					     -1);
+	/* clang-format on */
 
 	li = libinput_path_create_context(&counting_interface, &counter);
 	litest_assert_notnull(li);
@@ -244,8 +243,7 @@ START_TEST(path_create_destroy)
 
 	litest_assert(libinput_get_user_data(li) == &counter);
 
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_assert_notnull(device);
 
 	litest_assert_int_eq(counter.open_func_count, 1);
@@ -265,8 +263,7 @@ START_TEST(path_force_destroy)
 	li = libinput_path_create_context(&simple_interface, NULL);
 	litest_assert_notnull(li);
 	libinput_ref(li);
-	device = libinput_path_add_device(li,
-				  libevdev_uinput_get_devnode(dev->uinput));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(dev->uinput));
 	litest_assert_notnull(device);
 
 	while (libinput_unref(li) != NULL)
@@ -343,8 +340,7 @@ START_TEST(path_seat_change)
 
 	litest_drain_events(li);
 
-	rc = libinput_device_set_seat_logical_name(device,
-						   seat2_name);
+	rc = libinput_device_set_seat_logical_name(device, seat2_name);
 	litest_assert_int_eq(rc, 0);
 
 	litest_dispatch(li);
@@ -366,10 +362,8 @@ START_TEST(path_seat_change)
 	device = libinput_event_get_device(event);
 	seat2 = libinput_device_get_seat(device);
 
-	litest_assert_str_ne(libinput_seat_get_logical_name(seat2),
-			 seat1_name);
-	litest_assert_str_eq(libinput_seat_get_logical_name(seat2),
-			 seat2_name);
+	litest_assert_str_ne(libinput_seat_get_logical_name(seat2), seat1_name);
+	litest_assert_str_eq(libinput_seat_get_logical_name(seat2), seat2_name);
 	libinput_event_destroy(event);
 
 	libinput_seat_unref(seat1);
@@ -406,7 +400,6 @@ START_TEST(path_add_device)
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
 	struct libinput_device *device;
-	char *sysname1 = NULL, *sysname2 = NULL;
 
 	litest_dispatch(li);
 
@@ -415,13 +408,12 @@ START_TEST(path_add_device)
 	litest_assert_event_type(event, LIBINPUT_EVENT_DEVICE_ADDED);
 	device = libinput_event_get_device(event);
 	litest_assert_notnull(device);
-	sysname1 = safe_strdup(libinput_device_get_sysname(device));
+	_autofree_ char *sysname1 = safe_strdup(libinput_device_get_sysname(device));
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
 
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(dev->uinput));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(dev->uinput));
 	litest_assert_notnull(device);
 
 	litest_dispatch(li);
@@ -431,22 +423,18 @@ START_TEST(path_add_device)
 	litest_assert_event_type(event, LIBINPUT_EVENT_DEVICE_ADDED);
 	device = libinput_event_get_device(event);
 	litest_assert_notnull(device);
-	sysname2 = safe_strdup(libinput_device_get_sysname(device));
+	_autofree_ char *sysname2 = safe_strdup(libinput_device_get_sysname(device));
 	libinput_event_destroy(event);
 
 	litest_assert_str_eq(sysname1, sysname2);
-
-	free(sysname1);
-	free(sysname2);
 }
 END_TEST
 
 START_TEST(path_add_invalid_path)
 {
-	struct libinput *li;
 	struct libinput_device *device;
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 
 	litest_disable_log_handler(li);
 	device = libinput_path_add_device(li, "/tmp/");
@@ -456,8 +444,6 @@ START_TEST(path_add_invalid_path)
 	litest_dispatch(li);
 
 	litest_assert_empty_queue(li);
-
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -494,8 +480,7 @@ START_TEST(path_remove_device)
 	struct libinput_device *device;
 	int remove_event = 0;
 
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(dev->uinput));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(dev->uinput));
 	litest_assert_notnull(device);
 	litest_drain_events(li);
 
@@ -524,8 +509,7 @@ START_TEST(path_double_remove_device)
 	struct libinput_device *device;
 	int remove_event = 0;
 
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(dev->uinput));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(dev->uinput));
 	litest_assert_notnull(device);
 	litest_drain_events(li);
 
@@ -555,18 +539,19 @@ START_TEST(path_suspend)
 	int rc;
 	void *userdata = &rc;
 
+	/* clang-format off */
 	uinput = litest_create_uinput_device("test device", NULL,
 					     EV_KEY, BTN_LEFT,
 					     EV_KEY, BTN_RIGHT,
 					     EV_REL, REL_X,
 					     EV_REL, REL_Y,
 					     -1);
+	/* clang-format on */
 
 	li = libinput_path_create_context(&simple_interface, userdata);
 	litest_assert_notnull(li);
 
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_assert_notnull(device);
 
 	libinput_suspend(li);
@@ -585,18 +570,19 @@ START_TEST(path_double_suspend)
 	int rc;
 	void *userdata = &rc;
 
+	/* clang-format off */
 	uinput = litest_create_uinput_device("test device", NULL,
 					     EV_KEY, BTN_LEFT,
 					     EV_KEY, BTN_RIGHT,
 					     EV_REL, REL_X,
 					     EV_REL, REL_Y,
 					     -1);
+	/* clang-format on */
 
 	li = libinput_path_create_context(&simple_interface, userdata);
 	litest_assert_notnull(li);
 
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_assert_notnull(device);
 
 	libinput_suspend(li);
@@ -616,18 +602,19 @@ START_TEST(path_double_resume)
 	int rc;
 	void *userdata = &rc;
 
+	/* clang-format off */
 	uinput = litest_create_uinput_device("test device", NULL,
 					     EV_KEY, BTN_LEFT,
 					     EV_KEY, BTN_RIGHT,
 					     EV_REL, REL_X,
 					     EV_REL, REL_Y,
 					     -1);
+	/* clang-format on */
 
 	li = libinput_path_create_context(&simple_interface, userdata);
 	litest_assert_notnull(li);
 
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_assert_notnull(device);
 
 	libinput_suspend(li);
@@ -649,6 +636,7 @@ START_TEST(path_add_device_suspend_resum
 	int nevents;
 	void *userdata = &rc;
 
+	/* clang-format off */
 	uinput1 = litest_create_uinput_device("test device", NULL,
 					      EV_KEY, BTN_LEFT,
 					      EV_KEY, BTN_RIGHT,
@@ -661,12 +649,12 @@ START_TEST(path_add_device_suspend_resum
 					      EV_REL, REL_X,
 					      EV_REL, REL_Y,
 					      -1);
+	/* clang-format on */
 
 	li = libinput_path_create_context(&simple_interface, userdata);
 	litest_assert_notnull(li);
 
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput1));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput1));
 	litest_assert_notnull(device);
 	libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput2));
 
@@ -721,6 +709,7 @@ START_TEST(path_add_device_suspend_resum
 	int nevents;
 	void *userdata = &rc;
 
+	/* clang-format off */
 	uinput1 = litest_create_uinput_device("test device", NULL,
 					      EV_KEY, BTN_LEFT,
 					      EV_KEY, BTN_RIGHT,
@@ -733,15 +722,14 @@ START_TEST(path_add_device_suspend_resum
 					      EV_REL, REL_X,
 					      EV_REL, REL_Y,
 					      -1);
+	/* clang-format on */
 
 	li = libinput_path_create_context(&simple_interface, userdata);
 	litest_assert_notnull(li);
 
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput1));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput1));
 	litest_assert_notnull(device);
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput2));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput2));
 	litest_assert_notnull(device);
 
 	litest_dispatch(li);
@@ -804,6 +792,7 @@ START_TEST(path_add_device_suspend_resum
 	int nevents;
 	void *userdata = &rc;
 
+	/* clang-format off */
 	uinput1 = litest_create_uinput_device("test device", NULL,
 					      EV_KEY, BTN_LEFT,
 					      EV_KEY, BTN_RIGHT,
@@ -816,15 +805,14 @@ START_TEST(path_add_device_suspend_resum
 					      EV_REL, REL_X,
 					      EV_REL, REL_Y,
 					      -1);
+	/* clang-format on */
 
 	li = libinput_path_create_context(&simple_interface, userdata);
 	litest_assert_notnull(li);
 
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput1));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput1));
 	litest_assert_notnull(device);
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput2));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput2));
 
 	libinput_device_ref(device);
 	litest_dispatch(li);
@@ -881,18 +869,19 @@ START_TEST(path_device_gone)
 	struct libevdev_uinput *uinput;
 	struct libinput_event *event;
 
+	/* clang-format off */
 	uinput = litest_create_uinput_device("test device", NULL,
 					     EV_KEY, BTN_LEFT,
 					     EV_KEY, BTN_RIGHT,
 					     EV_REL, REL_X,
 					     EV_REL, REL_Y,
 					     -1);
+	/* clang-format on */
 
 	li = libinput_path_create_context(&simple_interface, NULL);
 	litest_assert_notnull(li);
 
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_assert_notnull(device);
 
 	litest_drain_events(li);
@@ -924,18 +913,19 @@ START_TEST(path_seat_recycle)
 	int found = 0;
 	void *user_data;
 
+	/* clang-format off */
 	uinput = litest_create_uinput_device("test device", NULL,
 					     EV_KEY, BTN_LEFT,
 					     EV_KEY, BTN_RIGHT,
 					     EV_REL, REL_X,
 					     EV_REL, REL_Y,
 					     -1);
+	/* clang-format on */
 
 	li = libinput_path_create_context(&simple_interface, userdata);
 	litest_assert_notnull(li);
 
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 	litest_assert_notnull(device);
 
 	litest_dispatch(li);
@@ -998,7 +988,6 @@ END_TEST
 START_TEST(path_ignore_device)
 {
 	struct litest_device *dev;
-	struct libinput *li;
 	struct libinput_device *device;
 	const char *path;
 
@@ -1006,17 +995,17 @@ START_TEST(path_ignore_device)
 	path = libevdev_uinput_get_devnode(dev->uinput);
 	litest_assert_notnull(path);
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	device = libinput_path_add_device(li, path);
 	litest_assert(device == NULL);
 
-	litest_destroy_context(li);
-	litest_delete_device(dev);
+	litest_device_destroy(dev);
 }
 END_TEST
 
 TEST_COLLECTION(path)
 {
+	/* clang-format off */
 	litest_add_no_device(path_create_NULL);
 	litest_add_no_device(path_create_invalid);
 	litest_add_no_device(path_create_invalid_file);
@@ -1044,4 +1033,5 @@ TEST_COLLECTION(path)
 	litest_add_for_device(path_udev_assign_seat, LITEST_SYNAPTICS_CLICKPAD_X220);
 
 	litest_add_no_device(path_ignore_device);
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-plugins-lua.c 1.30.0-1/test/test-plugins-lua.c
--- 1.28.1-1/test/test-plugins-lua.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/test/test-plugins-lua.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,1120 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include "util-files.h"
+#include "util-strings.h"
+#include "util-time.h"
+
+#include "libinput.h"
+#include "litest.h"
+
+static char *
+_litest_write_plugin(const char *tmpdir, const char *filename, const char *content)
+{
+	static int counter = 0;
+	counter += 10;
+
+	char *path = strdup_printf("%s/%d-%s.lua", tmpdir, counter, filename);
+	_autoclose_ int fd = open(path, O_WRONLY | O_CREAT, 0644);
+	litest_assert_errno_success(fd);
+
+	if (content) {
+		write(fd, content, strlen(content));
+		fsync(fd);
+	}
+
+	return path;
+}
+
+#define litest_write_plugin(tmpdir_, content_) \
+	_litest_write_plugin(tmpdir_, __func__, content_)
+
+START_TEST(lua_load_failure)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	const char *lua = "asfasdk1298'..asdfasdf'123@2;asd"; /* invalid lua */
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		size_t index = 0;
+		litest_assert(
+			strv_find_substring(capture->errors, "Failed to load", &index));
+		litest_assert_str_in(path, capture->errors[index]);
+	}
+}
+END_TEST
+
+enum content {
+	EMPTY,
+	NOTHING,
+	BASIC,
+	COMMENT,
+	DUPLICATE_CALL,
+	VERSION_NOT_A_TABLE,
+	MISSING_REGISTER,
+};
+
+START_TEST(lua_load_success_but_no_register)
+{
+	enum content content = litest_test_param_get_i32(test_env->params, "content");
+
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+
+	const char *lua = NULL;
+	switch (content) {
+	case DUPLICATE_CALL:
+		lua = "v1 = libinput:register({1})\np2 = libinput:register({2})\n";
+		break;
+	case VERSION_NOT_A_TABLE:
+		lua = "v1 = libinput:register(1)\n";
+		break;
+	case MISSING_REGISTER:
+		lua = "libinput:connect(\"new-evdev-device\", function(device) assert(false) end)\n";
+		break;
+	case BASIC:
+		lua = "a = 1 + 10";
+		break;
+	case COMMENT:
+		lua = "-- near-empty file";
+		break;
+	case NOTHING:
+		lua = "";
+		break;
+	case EMPTY:
+		break;
+	}
+
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		switch (content) {
+		case VERSION_NOT_A_TABLE:
+			litest_assert_strv_substring(capture->errors,
+						     "unloading after error");
+			break;
+		case DUPLICATE_CALL:
+			litest_assert_strv_substring(capture->errors,
+						     "plugin already registered");
+			break;
+		default:
+			litest_assert_strv_substring(capture->errors,
+						     "plugin never registered");
+			break;
+		}
+	}
+}
+END_TEST
+
+START_TEST(lua_register_noop)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	const char *lua = "libinput:register({1})";
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+		litest_assert_logcapture_no_errors(capture);
+	}
+}
+END_TEST
+
+START_TEST(lua_unregister_is_last)
+{
+	const char *when = litest_test_param_get_string(test_env->params, "when");
+
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	_autofree_ char *lua = strdup_printf(
+		"libinput:register({1})\n"
+		"libinput:connect(\"new-evdev-device\", function(device)\n  %s\n  libinput:log_error(\"abort abort\")\nend)\n"
+		"%slibinput:log_error(\"must not happen\")",
+		streq(when, "connect") ? "libinput:unregister()" : "",
+		streq(when, "run") ? "libinput:unregister()\n" : "--");
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		_destroy_(litest_device) *device = litest_add_device(li, LITEST_MOUSE);
+		litest_drain_events(li);
+		litest_assert_logcapture_no_errors(capture);
+	}
+}
+END_TEST
+
+START_TEST(lua_test_logging)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+
+	enum libinput_log_priority priority =
+		litest_test_param_get_i32(test_env->params, "priority");
+
+	const char *lua = NULL;
+	switch (priority) {
+	case LIBINPUT_LOG_PRIORITY_DEBUG:
+		lua = "libinput:log_debug(\"deb-ug\");";
+		break;
+	case LIBINPUT_LOG_PRIORITY_INFO:
+		lua = "libinput:log_info(\"inf-o\");";
+		break;
+	case LIBINPUT_LOG_PRIORITY_ERROR:
+		lua = "libinput:log_error(\"err-or\");";
+		break;
+	default:
+		litest_assert_not_reached();
+		break;
+	}
+
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	libinput_log_set_priority(li, priority);
+
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		switch (priority) {
+		case LIBINPUT_LOG_PRIORITY_DEBUG:
+			litest_assert(
+				strv_find_substring(capture->debugs, "deb-ug", NULL));
+			litest_assert(
+				!strv_find_substring(capture->infos, "inf-o", NULL));
+			litest_assert(
+				!strv_find_substring(capture->errors, "err-or", NULL));
+			break;
+		case LIBINPUT_LOG_PRIORITY_INFO:
+			litest_assert(
+				!strv_find_substring(capture->debugs, "deb-ug", NULL));
+			litest_assert(
+				strv_find_substring(capture->infos, "inf-o", NULL));
+			litest_assert(
+				!strv_find_substring(capture->errors, "err-or", NULL));
+			break;
+		case LIBINPUT_LOG_PRIORITY_ERROR:
+			litest_assert(
+				!strv_find_substring(capture->debugs, "deb-ug", NULL));
+			litest_assert(
+				!strv_find_substring(capture->infos, "inf-o", NULL));
+			litest_assert(
+				strv_find_substring(capture->errors, "err-or", NULL));
+			break;
+		}
+	}
+}
+END_TEST
+
+START_TEST(lua_test_evdev_global)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+
+	/* This is generated, if a few of them work the
+	 * rest should work too */
+	const char *lua =
+		"libinput:register({1}); a = evdev.ABS_X\nb = evdev.REL_X\nc = evdev.KEY_A\nd = evdev.EV_SYN\n";
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		litest_assert_logcapture_no_errors(capture);
+	}
+}
+END_TEST
+
+START_TEST(lua_test_libinput_now)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	const char *lua = "libinput:log_error(\">>>\" .. libinput:now())";
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		uint64_t test_now;
+		int rc = now_in_us(&test_now);
+		litest_assert_neg_errno_success(rc);
+
+		size_t index = 0;
+		litest_assert(strv_find_substring(capture->errors, ">>>", &index));
+		size_t nelem = 0;
+		_autostrvfree_ char **tokens =
+			strv_from_string(capture->errors[index], ">>>", &nelem);
+		litest_assert_int_eq(nelem, 2U);
+
+		uint64_t plugin_now = strtoull(tokens[1], NULL, 10);
+
+		litest_assert_int_le(plugin_now, test_now);
+		/* Even a slow test runner hopefully doesn't take >300ms to get to the
+		 * log print */
+		litest_assert_int_gt(plugin_now, test_now - ms2us(300));
+	}
+}
+END_TEST
+
+START_TEST(lua_test_libinput_timer)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+
+	const char *mode = litest_test_param_get_string(test_env->params, "mode");
+	bool reschedule = litest_test_param_get_bool(test_env->params, "reschedule");
+
+	_autofree_ char *timeout =
+		strdup_printf("%s%" PRIu64,
+			      streq(mode, "absolute") ? "libinput:now() + " : "",
+			      ms2us(100));
+	_autofree_ char *reschedule_timeout =
+		strdup_printf("libinput:timer_set_%s(%s%" PRIu64 ")\n",
+			      mode,
+			      streq(mode, "absolute") ? "t + " : "",
+			      ms2us(100));
+	_autofree_ char *lua = strdup_printf(
+		"libinput:register({1})\n"
+		"libinput:connect(\"timer-expired\",\n"
+		"          function(t)\n"
+		"               libinput:log_error(\">>>\" .. t)\n"
+		"               %s\n"
+		"          end)\n"
+		"libinput:timer_set_%s(%s)\n",
+		reschedule ? reschedule_timeout : "",
+		mode,
+		timeout);
+
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		litest_assert_logcapture_no_errors(capture);
+
+		size_t nloops = reschedule ? 4 : 1;
+		for (size_t i = 0; i < nloops; i++) {
+			libinput_dispatch(li);
+			msleep(100);
+			libinput_dispatch(li);
+
+			uint64_t test_now;
+			int rc = now_in_us(&test_now);
+			litest_assert_neg_errno_success(rc);
+
+			_autostrvfree_ char **msg = steal(&capture->errors);
+			litest_assert_ptr_notnull(msg);
+			size_t index;
+			litest_assert(strv_find_substring(msg, ">>>", &index));
+
+			size_t nelem = 0;
+			_autostrvfree_ char **tokens =
+				strv_from_string(msg[index], ">>>", &nelem);
+			litest_assert_int_eq(nelem, 2U);
+
+			uint64_t plugin_now = strtoull(tokens[1], NULL, 10);
+			litest_assert_int_le(plugin_now, test_now);
+			/* Even a slow test runner hopefully doesn't take >300ms between
+			 * dispatch and now_in_us */
+			litest_assert_int_gt(plugin_now, test_now - ms2us(300));
+		}
+
+		if (!reschedule) {
+			libinput_dispatch(li);
+			msleep(120);
+			libinput_dispatch(li);
+		}
+
+		litest_assert_logcapture_no_errors(capture);
+	}
+}
+END_TEST
+
+enum connect_error {
+	BAD_TYPE,
+	TOO_FEW_ARGS,
+	TOO_MANY_ARGS,
+};
+
+START_TEST(lua_bad_connect)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+
+	const char *handler = litest_test_param_get_string(test_env->params, "handler");
+	enum connect_error error = litest_test_param_get_i32(test_env->params, "error");
+
+	const char *func = NULL;
+	switch (error) {
+	case BAD_TYPE:
+		func = "a";
+		break;
+	case TOO_FEW_ARGS:
+		func = "function(p) libinput:log_debug(\"few\"); end";
+		break;
+	case TOO_MANY_ARGS:
+		func = "function(p, a, b) libinput:log_debug(\"many\"); end";
+		break;
+	}
+
+	_autofree_ char *lua = strdup_printf(
+		"libinput:register({1})\n"
+		"a = 10\n"
+		"libinput:connect(\"%s\", %s)\n",
+		handler,
+		func);
+
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		switch (error) {
+		/* These don't trigger a lua erro so we just test they don't segfault us
+		 */
+		case TOO_FEW_ARGS:
+		case TOO_MANY_ARGS:
+			litest_assert_logcapture_no_errors(capture);
+			break;
+		case BAD_TYPE:
+			litest_assert_strv_substring(capture->errors,
+						     "bad argument #2 to 'connect'");
+			break;
+		}
+	}
+}
+END_TEST
+
+START_TEST(lua_register_multiversions)
+{
+
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	const char *lua =
+		"v = libinput:register({1, 3, 4, 10, 15})\nlibinput:log_info(\"VERSION:\" .. v)\n";
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	if (libinput_log_get_priority(li) > LIBINPUT_LOG_PRIORITY_INFO)
+		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_INFO);
+
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		litest_assert_logcapture_no_errors(capture);
+		litest_assert_strv_substring(capture->infos, "VERSION:1");
+	}
+}
+END_TEST
+
+START_TEST(lua_allowed_functions)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	/* This tests on the assumption that if some of these work,
+	 * then the others we allow will work too. */
+	const char *lua =
+		"\n"
+		"libinput:register({1})\n"
+		"a = {10, 20}\n"
+		"for _, v in ipairs(a) do\n"
+		"   v = v + 1\n"
+		"end\n"
+		"b = {foo = 1}"
+		"for k, v in pairs(a) do\n"
+		"   v = v + 1\n"
+		"end\n"
+		"print(math.maxinteger)\n"
+		"table.sort({10, 2, 4})\n"
+		"assert(true)\n"
+		"";
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		litest_assert_logcapture_no_errors(capture);
+	}
+}
+END_TEST
+
+START_TEST(lua_disallowed_functions)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	/* This tests on the assumption that if some of these work,
+	 * then the others we allow will work too. */
+	const char *lua =
+		"\n"
+		"libinput:register({1})\n"
+		"assert(io == nil)\n"
+		"assert(require == nil)\n"
+		"assert(rawget == nil)\n"
+		"assert(rawset == nil)\n"
+		"assert(setfenv == nil)\n"
+		"assert(getmetatable == nil)\n"
+		"assert(setmetatable == nil)\n"
+		"assert(package == nil)\n"
+		"assert(os == nil)\n"
+		"assert(debug == nil)\n"
+		"";
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		litest_assert_logcapture_no_errors(capture);
+	}
+}
+END_TEST
+
+START_TEST(lua_frame_handler)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	const char *lua =
+		"libinput:register({1})\n"
+		"function frame_handler(_, frame, timestamp)\n"
+		"  libinput:log_info(\"T:\" .. timestamp)\n"
+		"  for _, e in ipairs(frame) do\n"
+		"	libinput:log_info(\"E:\" .. e.usage .. \":\" .. e.value)\n"
+		"  end\n"
+		"end\n"
+		"libinput:connect(\"new-evdev-device\", function(device) device:connect(\"evdev-frame\", frame_handler) end)\n";
+
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	if (libinput_log_get_priority(li) > LIBINPUT_LOG_PRIORITY_INFO)
+		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_INFO);
+
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		_destroy_(litest_device) *device = litest_add_device(li, LITEST_MOUSE);
+		litest_drain_events(li);
+
+		uint64_t before, after;
+		now_in_us(&before);
+		msleep(1);
+		litest_button_click_debounced(device, li, BTN_LEFT, 1);
+		litest_button_click_debounced(device, li, BTN_LEFT, 0);
+		litest_assert_logcapture_no_errors(capture);
+		msleep(1);
+		now_in_us(&after);
+
+		/* EV_KEY << 16 | BTN_LEFT -> 65808 */
+
+		litest_assert_strv_substring(capture->infos, "E:65808:1");
+		litest_assert_strv_substring(capture->infos, "E:65808:0");
+		/* SYN_REPORT shouldn't show up in the frame */
+		litest_assert(!strv_find_substring(capture->infos, "E:0:0", NULL));
+
+		size_t idx;
+		litest_assert(strv_find_substring(capture->infos, "T:", &idx));
+
+		_autofree_ char *str = safe_strdup(capture->infos[idx]);
+		for (size_t i = 0; str[i]; i++) {
+			if (str[i] == '\n') {
+				str[i] = '\0';
+				break;
+			}
+		}
+
+		size_t nelems;
+		_autostrvfree_ char **split = strv_from_string(str, ":", &nelems);
+		litest_assert_int_gt(nelems, 1U);
+		char *strtime = split[nelems - 1];
+		uint64_t timestamp = 0;
+		litest_assert(safe_atou64(strtime, &timestamp));
+		litest_assert_int_gt(timestamp, before);
+		litest_assert_int_lt(timestamp, after);
+	}
+}
+END_TEST
+
+START_TEST(lua_device_info)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	const char *lua =
+		"libinput:register({1})\n"
+		"function info_printer(device)\n"
+		"  local info = device:info()\n"
+		"  libinput:log_info(\"BUS:\" .. info.bustype)\n"
+		"  libinput:log_info(\"VID:\" .. info.vid)\n"
+		"  libinput:log_info(\"PID:\" .. info.pid)\n"
+		"end\n"
+		"libinput:connect(\"new-evdev-device\", info_printer)\n";
+
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	if (libinput_log_get_priority(li) > LIBINPUT_LOG_PRIORITY_INFO)
+		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_INFO);
+
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		_destroy_(litest_device) *device = litest_add_device(li, LITEST_MOUSE);
+		litest_drain_events(li);
+
+		litest_assert_strv_substring(capture->infos, "BUS:3");
+		litest_assert_strv_substring(capture->infos, "VID:6127");
+		litest_assert_strv_substring(capture->infos, "PID:24601");
+	}
+}
+END_TEST
+
+START_TEST(lua_set_absinfo)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	const char *lua =
+		"libinput:register({1})\n"
+		"function absinfo_setter(device)\n"
+		"  local absinfos = device:absinfos()\n"
+		"  for u, a in pairs(absinfos) do\n"
+		"	libinput:log_info(\"A:\" .. u .. \":\" .. a.minimum .. \":\" .. a.maximum .. \":\" .. a.resolution .. \":\" .. a.fuzz .. \":\" .. a.flat)\n"
+		"  end\n"
+		"  device:set_absinfo(evdev.ABS_X, { minimum = 0, maximum = 1000, resolution = 100 })\n"
+		"  device:set_absinfo(evdev.ABS_Y, { minimum = 0, maximum = 200, resolution = 10 })\n"
+		"  device:set_absinfo(evdev.ABS_MT_POSITION_X, { minimum = 0, maximum = 1000, resolution = 100 })\n"
+		"  device:set_absinfo(evdev.ABS_MT_POSITION_Y, { minimum = 0, maximum = 200, resolution = 10 })\n"
+		"end\n"
+		"libinput:connect(\"new-evdev-device\", absinfo_setter)\n";
+
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	if (libinput_log_get_priority(li) > LIBINPUT_LOG_PRIORITY_INFO)
+		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_INFO);
+
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		_destroy_(litest_device) *device =
+			litest_add_device(li, LITEST_GENERIC_MULTITOUCH_SCREEN);
+		litest_drain_events(li);
+
+		litest_assert_logcapture_no_errors(capture);
+
+		for (int code = 0; code <= ABS_MAX; code++) {
+			if (!libevdev_has_event_code(device->evdev, EV_ABS, code)) {
+				_autofree_ char *prefix =
+					strdup_printf("A:%u", (EV_ABS << 16) | code);
+				litest_assert(!strv_find_substring(capture->infos,
+								   prefix,
+								   NULL));
+				continue;
+			}
+
+			const struct input_absinfo *absinfo =
+				libevdev_get_abs_info(device->evdev, code);
+			_autofree_ char *message = strdup_printf("A:%u:%d:%d:%d:%d:%d",
+								 (EV_ABS << 16) | code,
+								 absinfo->minimum,
+								 absinfo->maximum,
+								 absinfo->resolution,
+								 absinfo->fuzz,
+								 absinfo->flat);
+			litest_assert_strv_substring(capture->infos, message);
+		}
+
+		/* If the absinfo worked, our device is 10x20mm big */
+		double w, h;
+		libinput_device_get_size(device->libinput_device, &w, &h);
+		litest_assert_double_eq(w, 10.0);
+		litest_assert_double_eq(h, 20.0);
+	}
+}
+END_TEST
+
+START_TEST(lua_enable_disable_evdev_usage)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	/* We have two plugins here, one that enables codes and one that prints
+	 * the frame.
+	 *
+	 * The first plugin also inserts a REL_Z event into the frame since we
+	 * can't send that through the kernel.
+	 */
+	const char *lua1 =
+		"libinput:register({1})\n"
+		"function frame_handler(_, frame, timestamp)\n"
+		"  table.insert(frame, { usage = evdev.REL_Z, value = 3 })\n"
+		"  return frame\n"
+		"end\n"
+		"function enabler(device)\n"
+		"  device:enable_evdev_usage(evdev.REL_Z)\n"
+		"  device:enable_evdev_usage(evdev.BTN_STYLUS2)\n"
+		"  device:disable_evdev_usage(evdev.REL_WHEEL)\n"
+		"  device:connect(\"evdev-frame\", frame_handler)\n"
+		"end\n"
+		"libinput:connect(\"new-evdev-device\", enabler)\n";
+
+	const char *lua2 =
+		"libinput:register({1})\n"
+		"function frame_handler(_, frame, timestamp)\n"
+		"  libinput:log_info(\"frame\")\n"
+		"  for _, e in ipairs(frame) do\n"
+		"	libinput:log_info(\"E:\" .. e.usage .. \":\" .. e.value)\n"
+		"  end\n"
+		"end\n"
+		"function f(device)\n"
+		"  libinput:log_info(\"F: \" .. device:name())\n"
+		"  device:connect(\"evdev-frame\", frame_handler)\n"
+		"end\n"
+		"libinput:connect(\"new-evdev-device\", f)\n";
+
+	_autofree_ char *p1 = litest_write_plugin(tmpdir->path, lua1);
+	_autofree_ char *p2 = litest_write_plugin(tmpdir->path, lua2);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	if (libinput_log_get_priority(li) > LIBINPUT_LOG_PRIORITY_INFO)
+		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_INFO);
+
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		_destroy_(litest_device) *device = litest_add_device(li, LITEST_MOUSE);
+		litest_drain_events(li);
+
+		/* We enabled that one ourselves */
+		litest_assert(
+			libinput_device_pointer_has_button(device->libinput_device,
+							   BTN_STYLUS2));
+
+		litest_assert_logcapture_no_errors(capture);
+
+		litest_event(device, EV_REL, REL_X, 1);
+		litest_event(device, EV_REL, REL_Y, 2);
+		litest_event(device, EV_REL, REL_WHEEL, -1);
+		litest_event(device, EV_SYN, SYN_REPORT, 0);
+		litest_dispatch(li);
+
+		litest_assert_logcapture_no_errors(capture);
+
+		litest_assert_strv_substring(capture->infos, "E:131072:1");
+		litest_assert_strv_substring(capture->infos, "E:131073:2");
+		litest_assert_strv_substring(capture->infos, "E:131074:3");
+		litest_assert(!strv_find_substring(capture->infos, "E:131080", NULL));
+	}
+}
+END_TEST
+
+START_TEST(lua_udev_properties)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	const char *lua =
+		"libinput:register({1})\n"
+		"function prop_printer(device)\n"
+		"  local properties = device:udev_properties()\n"
+		"  for k, v in pairs(properties) do\n"
+		"	libinput:log_info(k .. \"=\" .. v)\n"
+		"  end\n"
+		"end\n"
+		"libinput:connect(\"new-evdev-device\", prop_printer)\n";
+
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	if (libinput_log_get_priority(li) > LIBINPUT_LOG_PRIORITY_INFO)
+		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_INFO);
+
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		enum litest_device_type which =
+			litest_test_param_get_i32(test_env->params, "which");
+		_destroy_(litest_device) *device = litest_add_device(li, which);
+		litest_drain_events(li);
+
+		litest_assert_logcapture_no_errors(capture);
+
+		switch (which) {
+		case LITEST_TRACKPOINT:
+			litest_assert_strv_substring(capture->infos,
+						     "ID_INPUT_POINTINGSTICK=1");
+			_fallthrough_;
+		case LITEST_MOUSE:
+			litest_assert_strv_substring(capture->infos,
+						     "ID_INPUT_MOUSE=1");
+			break;
+		case LITEST_GENERIC_MULTITOUCH_SCREEN:
+			litest_assert_strv_substring(capture->infos,
+						     "ID_INPUT_TOUCHSCREEN=1");
+			break;
+		default:
+			litest_assert_not_reached();
+			break;
+		}
+		litest_assert(!strv_find_substring(capture->infos,
+						   "ID_INPUT_WIDTH_MM",
+						   NULL));
+		litest_assert(!strv_find_substring(capture->infos,
+						   "ID_INPUT_WIDTH_MM",
+						   NULL));
+	}
+}
+END_TEST
+
+START_TEST(lua_append_prepend_frame)
+{
+	bool append = litest_test_param_get_bool(test_env->params, "append");
+	bool in_timer = litest_test_param_get_bool(test_env->params, "in_timer");
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	_autofree_ char *lua = strdup_printf(
+		"libinput:register({1})\n"
+		"mydev = nil\n"
+		"function frame_handler(device, frame, timestamp)\n"
+		"    device:%s_frame({{ usage = evdev.BTN_LEFT, value = 1}})\n" /* commented
+										   out
+										   if
+										   !in_timer
+										 */
+		"    return nil\n"
+		"end\n"
+		"libinput:connect(\"new-evdev-device\", function(device)\n"
+		"    mydev = device\n"
+		"    %sdevice:connect(\"evdev-frame\", frame_handler)\n"
+		"    %slibinput:timer_set_relative(200000)\n" /* commented out if
+								 !in_timer */
+		"end)\n"
+		"function timer_expired(t)\n"
+		"    mydev:%s_frame({{ usage = evdev.BTN_LEFT, value = 1 }})\n"
+		"end\n"
+		"libinput:connect(\"timer-expired\", timer_expired)\n",
+		append ? "append" : "prepend",
+		in_timer ? "-- " : "",
+		in_timer ? "" : "-- ",
+		append ? "append" : "prepend");
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	libinput_plugin_system_load_plugins(li, LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+	litest_drain_events(li);
+
+	_destroy_(litest_device) *device = litest_add_device(li, LITEST_MOUSE);
+	litest_drain_events(li);
+	msleep(10); /* trigger the timer, if any */
+	litest_dispatch(li);
+
+	if (in_timer) {
+		litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	}
+
+	litest_event(device, EV_REL, REL_X, 1);
+	litest_event(device, EV_REL, REL_Y, 2);
+	litest_event(device, EV_SYN, SYN_REPORT, 0);
+	litest_dispatch(li);
+	litest_timeout_debounce(li);
+	litest_dispatch(li);
+
+	if (!in_timer && !append) {
+		litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	}
+
+	_destroy_(libinput_event) *ev = libinput_get_event(li);
+	litest_is_motion_event(ev);
+
+	if (!in_timer && append) {
+		litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	}
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(lua_ignore_unsupported_codes)
+{
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	_autofree_ char *lua = strdup_printf(
+		"libinput:register({1})\n"
+		"function frame_handler(device, frame, timestamp)\n"
+		"    local events = {}\n"
+		"    for _, v in ipairs(frame) do\n"
+		"       table.insert(events, { usage = v.usage, value = v.value })\n"
+		"    end\n"
+		"    table.insert(events, { usage = evdev.ABS_X, value = 1000 })\n"
+		"    table.insert(events, { usage = evdev.ABS_Y, value = 100 })\n"
+		"    table.insert(events, { usage = evdev.BTN_BACK, value = 1 })\n"
+		"    table.insert(events, { usage = evdev.BTN_LEFT, value = 1 })\n" /* this
+										       one actually exists */
+		"    return events\n"
+		"end\n"
+		"libinput:connect(\"new-evdev-device\", function(device)\n"
+		"    device:connect(\"evdev-frame\", frame_handler)\n"
+		"end)\n");
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+	libinput_plugin_system_load_plugins(li, LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+	litest_drain_events(li);
+
+	_destroy_(litest_device) *device = litest_add_device(li, LITEST_MOUSE);
+	litest_drain_events(li);
+
+	litest_event(device, EV_REL, REL_X, 1);
+	litest_event(device, EV_REL, REL_Y, 2);
+	litest_event(device, EV_SYN, SYN_REPORT, 0);
+	litest_dispatch(li);
+	litest_timeout_debounce(li);
+	litest_dispatch(li);
+
+	_destroy_(libinput_event) *ev = libinput_get_event(li);
+	litest_is_motion_event(ev);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+enum when {
+	DEVICE_NEW,
+	FIRST_FRAME,
+};
+
+START_TEST(lua_disable_button_debounce)
+{
+	enum when when = litest_test_param_get_i32(test_env->params, "when");
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	_autofree_ char *lua = strdup_printf(
+		"libinput:register({1})\n"
+		"function frame_handler(device, _, _)\n"
+		"  device:disable_feature(\"button-debouncing\")\n"
+		"end\n"
+		"function new_device(device)\n"
+		"  %s device:disable_feature(\"button-debouncing\")\n"
+		"  %s device:connect(\"evdev-frame\", frame_handler)\n"
+		"end\n"
+		"libinput:connect(\"new-evdev-device\", new_device)\n",
+		when == DEVICE_NEW ? "" : "--",
+		when == FIRST_FRAME ? "" : "--");
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	etrace("%s", lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+
+	if (libinput_log_get_priority(li) > LIBINPUT_LOG_PRIORITY_DEBUG)
+		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);
+
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		_destroy_(litest_device) *dev = litest_add_device(li, LITEST_MOUSE);
+		litest_drain_events(li);
+
+		litest_disable_middleemu(dev);
+
+		litest_event(dev, EV_KEY, BTN_LEFT, 1);
+		litest_event(dev, EV_SYN, SYN_REPORT, 0);
+		litest_event(dev, EV_KEY, BTN_LEFT, 0);
+		litest_event(dev, EV_SYN, SYN_REPORT, 0);
+		litest_event(dev, EV_KEY, BTN_LEFT, 1);
+		litest_event(dev, EV_SYN, SYN_REPORT, 0);
+		litest_event(dev, EV_KEY, BTN_LEFT, 0);
+		litest_event(dev, EV_SYN, SYN_REPORT, 0);
+		litest_timeout_debounce(li);
+
+		litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li,
+					   BTN_LEFT,
+					   LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li,
+					   BTN_LEFT,
+					   LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_empty_queue(li);
+
+		litest_assert_strv_substring(capture->debugs,
+					     "disabled button debouncing on request");
+	}
+}
+END_TEST
+
+START_TEST(lua_disable_touchpad_jump_detection)
+{
+	enum when when = litest_test_param_get_i32(test_env->params, "when");
+	_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+	_autofree_ char *lua = strdup_printf(
+		"libinput:register({1})\n"
+		"function frame_handler(dev, f, ts)\n"
+		"  dev:disable_feature(\"touchpad-jump-detection\")\n"
+		"end\n"
+		"function new_device(device)\n"
+		"  %sdevice:disable_feature(\"touchpad-jump-detection\")\n"
+		"  %sdevice:connect(\"evdev-frame\", frame_handler)\n"
+		"end\n"
+		"libinput:connect(\"new-evdev-device\", new_device)\n",
+		when == DEVICE_NEW ? "" : "-- ",
+		when == FIRST_FRAME ? "" : "-- ");
+
+	etrace("plugin data:\n%s", lua);
+
+	_autofree_ char *path = litest_write_plugin(tmpdir->path, lua);
+	_litest_context_destroy_ struct libinput *li =
+		litest_create_context_with_plugindir(tmpdir->path);
+
+	if (libinput_log_get_priority(li) > LIBINPUT_LOG_PRIORITY_DEBUG)
+		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);
+
+	litest_with_logcapture(li, capture) {
+		libinput_plugin_system_load_plugins(li,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);
+		litest_drain_events(li);
+
+		_destroy_(litest_device) *dev =
+			litest_add_device(li, LITEST_SYNAPTICS_RMI4);
+		litest_drain_events(li);
+
+		litest_touch_down(dev, 0, 40, 50);
+		litest_touch_move(dev, 0, 80, 80);
+		litest_touch_move(dev, 0, 90, 90);
+		litest_touch_up(dev, 0);
+
+		litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
+		litest_assert_empty_queue(li);
+
+		litest_assert(!strv_find_substring(capture->infos,
+						   "Touch jump detected and discarded",
+						   NULL));
+	}
+}
+END_TEST
+
+TEST_COLLECTION(lua)
+{
+	/* clang-format off */
+	litest_add_no_device(lua_load_failure);
+	litest_with_parameters(params,
+			       "content", 'I', 6,
+					litest_named_i32(EMPTY),
+					litest_named_i32(BASIC),
+					litest_named_i32(COMMENT),
+					litest_named_i32(DUPLICATE_CALL),
+					litest_named_i32(MISSING_REGISTER),
+					litest_named_i32(VERSION_NOT_A_TABLE)) {
+		litest_add_parametrized_no_device(lua_load_success_but_no_register,
+						  params);
+	}
+	litest_add_no_device(lua_register_noop);
+	litest_with_parameters(params, "when", 's', 2, "run", "connect") {
+		litest_add_parametrized_no_device(lua_unregister_is_last, params);
+	}
+	litest_add_no_device(lua_test_evdev_global);
+	litest_add_no_device(lua_test_libinput_now);
+	litest_with_parameters(params,
+			       "mode", 's', 2, "absolute", "relative",
+			       "reschedule", 'b') {
+		litest_add_parametrized_no_device(lua_test_libinput_timer, params);
+	}
+
+	litest_with_parameters(params,
+			       "priority", 'I', 3,
+					litest_named_i32(LIBINPUT_LOG_PRIORITY_DEBUG),
+					litest_named_i32(LIBINPUT_LOG_PRIORITY_INFO),
+					litest_named_i32(LIBINPUT_LOG_PRIORITY_ERROR)) {
+		litest_add_parametrized_no_device(lua_test_logging, params);
+	}
+
+	litest_with_parameters(params,
+			       "handler", 's', 2, "new-evdev-device", "timer-expired",
+			       "error", 'I', 3,
+					litest_named_i32(BAD_TYPE),
+					litest_named_i32(TOO_FEW_ARGS),
+					litest_named_i32(TOO_MANY_ARGS)) {
+		litest_add_parametrized_no_device(lua_bad_connect, params);
+	}
+
+	litest_add_no_device(lua_register_multiversions);
+	litest_add_no_device(lua_allowed_functions);
+	litest_add_no_device(lua_disallowed_functions);
+
+	litest_add_no_device(lua_frame_handler);
+	litest_add_no_device(lua_device_info);
+	litest_add_no_device(lua_set_absinfo);
+	litest_add_no_device(lua_enable_disable_evdev_usage);
+	litest_add_no_device(lua_ignore_unsupported_codes);
+
+	litest_with_parameters(params,
+			       "which", 'I', 3,
+					litest_named_i32(LITEST_MOUSE),
+					litest_named_i32(LITEST_TRACKPOINT),
+					litest_named_i32(LITEST_GENERIC_MULTITOUCH_SCREEN)) {
+		litest_add_parametrized_no_device(lua_udev_properties, params);
+	}
+
+	litest_with_parameters(params, "append", 'b', "in_timer", 'b') {
+		litest_add_parametrized_no_device(lua_append_prepend_frame, params);
+	}
+
+	litest_with_parameters(params, "when", 'I', 2,
+					litest_named_i32(DEVICE_NEW),
+					litest_named_i32(FIRST_FRAME)) {
+		litest_add_parametrized_no_device(lua_disable_button_debounce, params);
+		litest_add_parametrized_no_device(lua_disable_touchpad_jump_detection, params);
+	}
+	/* clang-format on */
+}
diff -pruN 1.28.1-1/test/test-pointer.c 1.30.0-1/test/test-pointer.c
--- 1.28.1-1/test/test-pointer.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-pointer.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,20 +23,18 @@
 
 #include <config.h>
 
-#include <stdio.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <libinput.h>
 #include <math.h>
+#include <stdio.h>
 #include <unistd.h>
 #include <valgrind/valgrind.h>
 
 #include "libinput-util.h"
 #include "litest.h"
 
-enum cardinal {
-	N, NE, E, SE, S, SW, W, NW
-};
+enum cardinal { N, NE, E, SE, S, SW, W, NW };
 
 static void
 test_relative_event(struct litest_device *dev, double dx, double dy)
@@ -44,7 +42,6 @@ test_relative_event(struct litest_device
 	struct libinput *li = dev->libinput;
 	struct libinput_event_pointer *ptrev;
 	struct libinput_event *event;
-	struct udev_device *ud;
 	double ev_dx, ev_dy;
 	double expected_dir;
 	double expected_length;
@@ -67,24 +64,24 @@ test_relative_event(struct litest_device
 	 * movement. Work aorund this here by checking for the MOUSE_DPI
 	 * property.
 	 */
-	ud = libinput_device_get_udev_device(dev->libinput_device);
+	_unref_(udev_device) *ud =
+		libinput_device_get_udev_device(dev->libinput_device);
 	litest_assert_ptr_notnull(ud);
 	prop = udev_device_get_property_value(ud, "MOUSE_DPI");
 	if (prop) {
 		dpi = parse_mouse_dpi_property(prop);
 		litest_assert_int_ne(dpi, 0);
 
-		dx *= 1000.0/dpi;
-		dy *= 1000.0/dpi;
+		dx *= 1000.0 / dpi;
+		dy *= 1000.0 / dpi;
 	}
-	udev_device_unref(ud);
 
-	expected_length = sqrt(4 * dx*dx + 4 * dy*dy);
+	expected_length = sqrt(4 * dx * dx + 4 * dy * dy);
 	expected_dir = atan2(dx, dy);
 
 	ev_dx = libinput_event_pointer_get_dx(ptrev);
 	ev_dy = libinput_event_pointer_get_dy(ptrev);
-	actual_length = sqrt(ev_dx*ev_dx + ev_dy*ev_dy);
+	actual_length = sqrt(ev_dx * ev_dx + ev_dy * ev_dy);
 	actual_dir = atan2(ev_dx, ev_dy);
 
 	/* Check the length of the motion vector (tolerate 1.0 indifference). */
@@ -103,11 +100,11 @@ static void
 disable_button_scrolling(struct litest_device *device)
 {
 	struct libinput_device *dev = device->libinput_device;
-	enum libinput_config_status status,
-				    expected;
+	enum libinput_config_status status, expected;
 
-	status = libinput_device_config_scroll_set_method(dev,
-					LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
+	status = libinput_device_config_scroll_set_method(
+		dev,
+		LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
 
 	expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
 	litest_assert_int_eq(status, expected);
@@ -175,7 +172,6 @@ START_TEST(pointer_motion_relative_zero)
 		litest_dispatch(dev->libinput);
 	}
 	litest_assert_empty_queue(li);
-
 }
 END_TEST
 
@@ -188,32 +184,41 @@ START_TEST(pointer_motion_relative_min_d
 	double evx, evy;
 	int dx, dy;
 	double len;
-	enum cardinal direction = litest_test_param_get_i32(test_env->params, "direction");
+	enum cardinal direction =
+		litest_test_param_get_i32(test_env->params, "direction");
 
-	switch(direction) {
+	switch (direction) {
 	case N:
-		dx = 0; dy = 1;
+		dx = 0;
+		dy = 1;
 		break;
 	case NE:
-		dx = 1; dy = 1;
+		dx = 1;
+		dy = 1;
 		break;
 	case E:
-		dx = 1; dy = 0;
+		dx = 1;
+		dy = 0;
 		break;
 	case SE:
-		dx = 1; dy = -1;
+		dx = 1;
+		dy = -1;
 		break;
 	case S:
-		dx = 0; dy = -1;
+		dx = 0;
+		dy = -1;
 		break;
 	case SW:
-		dx = -1; dy = -1;
+		dx = -1;
+		dy = -1;
 		break;
 	case W:
-		dx = -1; dy = 0;
+		dx = -1;
+		dy = 0;
 		break;
 	case NW:
-		dx = -1; dy = 1;
+		dx = -1;
+		dy = 1;
 		break;
 	default:
 		litest_abort_msg("Invalid direction %d", direction);
@@ -283,7 +288,7 @@ END_TEST
 START_TEST(pointer_absolute_initial_state)
 {
 	struct litest_device *dev = litest_current_device();
-	struct libinput *libinput1, *libinput2;
+	struct libinput *libinput1;
 	struct libinput_event *ev1, *ev2;
 	struct libinput_event_pointer *p1, *p2;
 	int axis = litest_test_param_get_i32(test_env->params, "axis");
@@ -295,9 +300,8 @@ START_TEST(pointer_absolute_initial_stat
 	/* device is now on some x/y value */
 	litest_drain_events(libinput1);
 
-	libinput2 = litest_create_context();
-	libinput_path_add_device(libinput2,
-				 libevdev_uinput_get_devnode(dev->uinput));
+	_litest_context_destroy_ struct libinput *libinput2 = litest_create_context();
+	libinput_path_add_device(libinput2, libevdev_uinput_get_devnode(dev->uinput));
 	litest_drain_events(libinput2);
 
 	if (axis == ABS_X)
@@ -314,9 +318,9 @@ START_TEST(pointer_absolute_initial_stat
 		ev2 = libinput_get_event(libinput2);
 
 		litest_assert_enum_eq(libinput_event_get_type(ev1),
-				 LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE);
+				      LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE);
 		litest_assert_enum_eq(libinput_event_get_type(ev1),
-				 libinput_event_get_type(ev2));
+				      libinput_event_get_type(ev2));
 
 		p1 = libinput_event_get_pointer_event(ev1);
 		p2 = libinput_event_get_pointer_event(ev2);
@@ -329,54 +333,52 @@ START_TEST(pointer_absolute_initial_stat
 		libinput_event_destroy(ev1);
 		libinput_event_destroy(ev2);
 	}
-
-	litest_destroy_context(libinput2);
 }
 END_TEST
 
 static void
 test_unaccel_event(struct litest_device *dev, int dx, int dy)
 {
-      struct libinput *li = dev->libinput;
-      struct libinput_event *event;
-      struct libinput_event_pointer *ptrev;
-      double ev_dx, ev_dy;
+	struct libinput *li = dev->libinput;
+	struct libinput_event *event;
+	struct libinput_event_pointer *ptrev;
+	double ev_dx, ev_dy;
 
-      litest_event(dev, EV_REL, REL_X, dx);
-      litest_event(dev, EV_REL, REL_Y, dy);
-      litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	litest_event(dev, EV_REL, REL_X, dx);
+	litest_event(dev, EV_REL, REL_Y, dy);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-      litest_dispatch(li);
+	litest_dispatch(li);
 
-      event = libinput_get_event(li);
-      ptrev = litest_is_motion_event(event);
+	event = libinput_get_event(li);
+	ptrev = litest_is_motion_event(event);
 
-      ev_dx = libinput_event_pointer_get_dx_unaccelerated(ptrev);
-      ev_dy = libinput_event_pointer_get_dy_unaccelerated(ptrev);
+	ev_dx = libinput_event_pointer_get_dx_unaccelerated(ptrev);
+	ev_dy = libinput_event_pointer_get_dy_unaccelerated(ptrev);
 
-      litest_assert_int_eq(dx, ev_dx);
-      litest_assert_int_eq(dy, ev_dy);
+	litest_assert_int_eq(dx, ev_dx);
+	litest_assert_int_eq(dy, ev_dy);
 
-      libinput_event_destroy(event);
+	libinput_event_destroy(event);
 
-      litest_drain_events(dev->libinput);
+	litest_drain_events(dev->libinput);
 }
 
 START_TEST(pointer_motion_unaccel)
 {
-      struct litest_device *dev = litest_current_device();
+	struct litest_device *dev = litest_current_device();
 
-      litest_drain_events(dev->libinput);
+	litest_drain_events(dev->libinput);
 
-      test_unaccel_event(dev, 10, 0);
-      test_unaccel_event(dev, 10, 10);
-      test_unaccel_event(dev, 10, -10);
-      test_unaccel_event(dev, 0, 10);
-
-      test_unaccel_event(dev, -10, 0);
-      test_unaccel_event(dev, -10, 10);
-      test_unaccel_event(dev, -10, -10);
-      test_unaccel_event(dev, 0, -10);
+	test_unaccel_event(dev, 10, 0);
+	test_unaccel_event(dev, 10, 10);
+	test_unaccel_event(dev, 10, -10);
+	test_unaccel_event(dev, 0, 10);
+
+	test_unaccel_event(dev, -10, 0);
+	test_unaccel_event(dev, -10, 10);
+	test_unaccel_event(dev, -10, -10);
+	test_unaccel_event(dev, 0, -10);
 }
 END_TEST
 
@@ -388,9 +390,10 @@ test_button_event(struct litest_device *
 	litest_button_click_debounced(dev, li, button, state);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li, button,
-				   state ?  LIBINPUT_BUTTON_STATE_PRESSED :
-					   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li,
+				   button,
+				   state ? LIBINPUT_BUTTON_STATE_PRESSED
+					 : LIBINPUT_BUTTON_STATE_RELEASED);
 }
 
 START_TEST(pointer_button)
@@ -408,15 +411,13 @@ START_TEST(pointer_button)
 	test_button_event(dev, BTN_LEFT, 1);
 	test_button_event(dev, BTN_LEFT, 0);
 
-	if (libinput_device_pointer_has_button(dev->libinput_device,
-					       BTN_RIGHT)) {
+	if (libinput_device_pointer_has_button(dev->libinput_device, BTN_RIGHT)) {
 		test_button_event(dev, BTN_RIGHT, 1);
 		test_button_event(dev, BTN_RIGHT, 0);
 	}
 
 	/* Skip middle button test on trackpoints (used for scrolling) */
-	if (libinput_device_pointer_has_button(dev->libinput_device,
-					       BTN_MIDDLE)) {
+	if (libinput_device_pointer_has_button(dev->libinput_device, BTN_MIDDLE)) {
 		test_button_event(dev, BTN_MIDDLE, 1);
 		test_button_event(dev, BTN_MIDDLE, 0);
 	}
@@ -425,7 +426,6 @@ END_TEST
 
 START_TEST(pointer_button_auto_release)
 {
-	struct libinput *libinput;
 	struct litest_device *dev;
 	struct libinput_event *event;
 	enum libinput_event_type type;
@@ -434,13 +434,27 @@ START_TEST(pointer_button_auto_release)
 		int code;
 		int released;
 	} buttons[] = {
-		{ .code = BTN_LEFT, },
-		{ .code = BTN_MIDDLE, },
-		{ .code = BTN_EXTRA, },
-		{ .code = BTN_SIDE, },
-		{ .code = BTN_BACK, },
-		{ .code = BTN_FORWARD, },
-		{ .code = BTN_4, },
+		{
+			.code = BTN_LEFT,
+		},
+		{
+			.code = BTN_MIDDLE,
+		},
+		{
+			.code = BTN_EXTRA,
+		},
+		{
+			.code = BTN_SIDE,
+		},
+		{
+			.code = BTN_BACK,
+		},
+		{
+			.code = BTN_FORWARD,
+		},
+		{
+			.code = BTN_4,
+		},
 	};
 	int events[2 * (ARRAY_LENGTH(buttons) + 1)];
 	unsigned i;
@@ -456,11 +470,13 @@ START_TEST(pointer_button_auto_release)
 	events[i++] = -1;
 	events[i++] = -1;
 
-	libinput = litest_create_context();
+	_litest_context_destroy_ struct libinput *libinput = litest_create_context();
 	dev = litest_add_device_with_overrides(libinput,
 					       LITEST_MOUSE,
 					       "Generic mouse",
-					       NULL, NULL, events);
+					       NULL,
+					       NULL,
+					       events);
 
 	litest_drain_events(libinput);
 
@@ -472,7 +488,7 @@ START_TEST(pointer_button_auto_release)
 	litest_drain_events(libinput);
 
 	/* "Disconnect" device */
-	litest_delete_device(dev);
+	litest_device_destroy(dev);
 
 	/* Mark all released buttons until device is removed */
 	while (1) {
@@ -488,7 +504,7 @@ START_TEST(pointer_button_auto_release)
 		litest_assert_event_type(event, LIBINPUT_EVENT_POINTER_BUTTON);
 		pevent = libinput_event_get_pointer_event(event);
 		litest_assert_enum_eq(libinput_event_pointer_get_button_state(pevent),
-				 LIBINPUT_BUTTON_STATE_RELEASED);
+				      LIBINPUT_BUTTON_STATE_RELEASED);
 		button = libinput_event_pointer_get_button(pevent);
 
 		valid_code = 0;
@@ -507,8 +523,6 @@ START_TEST(pointer_button_auto_release)
 	for (i = 0; i < ARRAY_LENGTH(buttons); ++i) {
 		litest_assert_int_eq(buttons[i].released, 1);
 	}
-
-	litest_destroy_context(libinput);
 }
 END_TEST
 
@@ -518,12 +532,12 @@ START_TEST(pointer_button_has_no_button)
 	struct libinput_device *device = dev->libinput_device;
 	unsigned int code;
 
-	litest_assert(!libinput_device_has_capability(device,
-					  LIBINPUT_DEVICE_CAP_POINTER));
+	litest_assert(
+		!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER));
 
 	for (code = BTN_LEFT; code < KEY_OK; code++)
 		litest_assert_int_eq(-1,
-			 libinput_device_pointer_has_button(device, code));
+				     libinput_device_pointer_has_button(device, code));
 }
 END_TEST
 
@@ -539,9 +553,7 @@ START_TEST(pointer_recover_from_lost_but
 
 	litest_button_click_debounced(dev, li, BTN_LEFT, 1);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	/* Grab for the release to make libinput lose count */
 	libevdev_grab(evdev, LIBEVDEV_GRAB);
@@ -554,9 +566,7 @@ START_TEST(pointer_recover_from_lost_but
 	litest_assert_empty_queue(li);
 
 	litest_button_click_debounced(dev, li, BTN_LEFT, 0);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -564,34 +574,31 @@ END_TEST
 static inline double
 wheel_click_count(struct litest_device *dev, int which)
 {
-	struct udev_device *d;
 	const char *prop = NULL;
 	int count;
 	double angle = 0.0;
 
-	d = libinput_device_get_udev_device(dev->libinput_device);
+	_unref_(udev_device) *d = libinput_device_get_udev_device(dev->libinput_device);
 	litest_assert_ptr_notnull(d);
 
 	if (which == REL_HWHEEL)
-		prop = udev_device_get_property_value(d, "MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL");
+		prop = udev_device_get_property_value(
+			d,
+			"MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL");
 	if (!prop)
 		prop = udev_device_get_property_value(d, "MOUSE_WHEEL_CLICK_COUNT");
-	if (!prop)
-		goto out;
-
-	count = parse_mouse_wheel_click_count_property(prop);
-	litest_assert_int_ne(count, 0);
-	angle = 360.0/count;
+	if (prop) {
+		count = parse_mouse_wheel_click_count_property(prop);
+		litest_assert_int_ne(count, 0);
+		angle = 360.0 / count;
+	}
 
-out:
-	udev_device_unref(d);
 	return angle;
 }
 
 static inline double
 wheel_click_angle(struct litest_device *dev, int which)
 {
-	struct udev_device *d;
 	const char *prop = NULL;
 	const int default_angle = 15;
 	double angle;
@@ -601,22 +608,21 @@ wheel_click_angle(struct litest_device *
 		return angle;
 
 	angle = default_angle;
-	d = libinput_device_get_udev_device(dev->libinput_device);
+	_unref_(udev_device) *d = libinput_device_get_udev_device(dev->libinput_device);
 	litest_assert_ptr_notnull(d);
 
 	if (which == REL_HWHEEL)
-		prop = udev_device_get_property_value(d, "MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL");
+		prop = udev_device_get_property_value(
+			d,
+			"MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL");
 	if (!prop)
 		prop = udev_device_get_property_value(d, "MOUSE_WHEEL_CLICK_ANGLE");
-	if (!prop)
-		goto out;
-
-	angle = parse_mouse_wheel_click_angle_property(prop);
-	if (angle == 0.0)
-		angle = default_angle;
+	if (prop) {
+		angle = parse_mouse_wheel_click_angle_property(prop);
+		if (angle == 0.0)
+			angle = default_angle;
+	}
 
-out:
-	udev_device_unref(d);
 	return angle;
 }
 
@@ -635,11 +641,12 @@ test_high_and_low_wheel_events_value(str
 
 	scroll_step = wheel_click_angle(dev, which);
 	source = LIBINPUT_POINTER_AXIS_SOURCE_WHEEL;
-	expected = scroll_step * (v120_amount/120);
-	discrete = v120_amount/120;
+	expected = scroll_step * (v120_amount / 120);
+	discrete = v120_amount / 120;
 	v120 = v120_amount;
 
-	if (libinput_device_config_scroll_get_natural_scroll_enabled(dev->libinput_device)) {
+	if (libinput_device_config_scroll_get_natural_scroll_enabled(
+		    dev->libinput_device)) {
 		expected *= -1;
 		discrete *= -1;
 		v120 *= -1;
@@ -652,34 +659,43 @@ test_high_and_low_wheel_events_value(str
 		v120 *= -1;
 	}
 
-	axis = (which == REL_WHEEL || which == REL_WHEEL_HI_RES) ?
-				LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL :
-				LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
+	axis = (which == REL_WHEEL || which == REL_WHEEL_HI_RES)
+		       ? LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL
+		       : LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
 
 	event = libinput_get_event(li);
 	litest_assert_notnull(event);
 
-	while(event) {
+	bool have_lores = false, have_hires = false;
+
+	while (event) {
 		ptrev = litest_is_axis_event(event,
 					     LIBINPUT_EVENT_POINTER_SCROLL_WHEEL,
 					     axis,
 					     source);
 
-		if (!litest_is_high_res_axis_event(event)) {
+		if (litest_is_high_res_axis_event(event)) {
+			have_hires = true;
 			litest_assert_double_eq(
-					libinput_event_pointer_get_axis_value(ptrev, axis),
-					expected);
-			litest_assert_double_eq(
-					libinput_event_pointer_get_axis_value_discrete(ptrev, axis),
-					discrete);
+				libinput_event_pointer_get_scroll_value_v120(ptrev,
+									     axis),
+				v120);
 		} else {
+			have_lores = true;
+			litest_assert_double_eq(
+				libinput_event_pointer_get_axis_value(ptrev, axis),
+				expected);
 			litest_assert_double_eq(
-					libinput_event_pointer_get_scroll_value_v120(ptrev, axis),
-					v120);
+				libinput_event_pointer_get_axis_value_discrete(ptrev,
+									       axis),
+				discrete);
 		}
 		libinput_event_destroy(event);
 		event = libinput_get_event(li);
 	}
+
+	if (have_lores)
+		litest_assert_msg(have_hires, "Missing high-res wheels events");
 }
 
 static void
@@ -688,6 +704,15 @@ test_wheel_event(struct litest_device *d
 	struct libinput *li = dev->libinput;
 	int event_amount = amount;
 
+	switch (which) {
+	case REL_WHEEL:
+	case REL_HWHEEL:
+		break;
+	default:
+		litest_assert_msg("Invalid axis for %s", __func__);
+		break;
+	}
+
 	/* mouse scroll wheels are 'upside down' */
 	if (which == REL_WHEEL)
 		event_amount *= -1;
@@ -707,7 +732,7 @@ START_TEST(pointer_scroll_wheel)
 
 	/* make sure we hit at least one of the below two conditions */
 	litest_assert(libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL) ||
-		  libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL));
+		      libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL));
 
 	if (libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL)) {
 		test_wheel_event(dev, REL_WHEEL, -1);
@@ -732,16 +757,16 @@ test_hi_res_wheel_event(struct litest_de
 {
 	struct libinput *li = dev->libinput;
 
-	switch(which) {
+	switch (which) {
 	case REL_WHEEL_HI_RES:
 		/* mouse scroll wheels are 'upside down' */
 		litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -1 * v120_amount);
-		litest_event(dev, EV_REL, REL_WHEEL, -1 * v120_amount/120);
+		litest_event(dev, EV_REL, REL_WHEEL, -1 * v120_amount / 120);
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
 		break;
 	case REL_HWHEEL_HI_RES:
 		litest_event(dev, EV_REL, REL_HWHEEL_HI_RES, v120_amount);
-		litest_event(dev, EV_REL, REL_HWHEEL, v120_amount/120);
+		litest_event(dev, EV_REL, REL_HWHEEL, v120_amount / 120);
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
 		break;
 	default:
@@ -756,28 +781,26 @@ test_hi_res_wheel_event(struct litest_de
 START_TEST(pointer_scroll_wheel_hires)
 {
 	struct litest_device *dev = litest_current_device();
+	unsigned int axis = litest_test_param_get_i32(test_env->params, "axis");
 
-	if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL_HI_RES) &&
-	    !libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL_HI_RES))
+	if (!libevdev_has_event_code(dev->evdev, EV_REL, axis))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_drain_events(dev->libinput);
 
-	for (int axis = REL_WHEEL_HI_RES; axis <= REL_HWHEEL_HI_RES; axis++) {
-		if (!libevdev_has_event_code(dev->evdev, EV_REL, axis))
-			continue;
+	test_hi_res_wheel_event(dev, axis, -120);
+	test_hi_res_wheel_event(dev, axis, 120);
 
-		test_hi_res_wheel_event(dev, axis, -120);
-		test_hi_res_wheel_event(dev, axis, 120);
+	test_hi_res_wheel_event(dev, axis, -5 * 120);
+	test_hi_res_wheel_event(dev, axis, 6 * 120);
 
-		test_hi_res_wheel_event(dev, axis, -5 * 120);
-		test_hi_res_wheel_event(dev, axis, 6 * 120);
+	if (dev->which == LITEST_MOUSE_WHEEL_HIRES_DISABLED)
+		return LITEST_NOT_APPLICABLE;
 
-		test_hi_res_wheel_event(dev, axis, 30);
-		test_hi_res_wheel_event(dev, axis, -60);
-		test_hi_res_wheel_event(dev, axis, -40);
-		test_hi_res_wheel_event(dev, axis, 180);
-	}
+	test_hi_res_wheel_event(dev, axis, 30);
+	test_hi_res_wheel_event(dev, axis, -60);
+	test_hi_res_wheel_event(dev, axis, -40);
+	test_hi_res_wheel_event(dev, axis, 180);
 }
 END_TEST
 
@@ -787,7 +810,8 @@ START_TEST(pointer_scroll_wheel_hires_se
 	struct libinput *li = dev->libinput;
 	unsigned int lores_code, hires_code;
 	int direction;
-	enum libinput_pointer_axis axis = litest_test_param_get_i32(test_env->params, "axis");
+	enum libinput_pointer_axis axis =
+		litest_test_param_get_i32(test_env->params, "axis");
 
 	switch (axis) {
 	case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL:
@@ -804,7 +828,10 @@ START_TEST(pointer_scroll_wheel_hires_se
 		litest_abort_msg("Invalid test axis '%d'", axis);
 	}
 
-	if (!libevdev_has_event_code(dev->evdev, EV_REL, lores_code) &&
+	if (dev->which == LITEST_MOUSE_WHEEL_HIRES_DISABLED)
+		return LITEST_NOT_APPLICABLE;
+
+	if (!libevdev_has_event_code(dev->evdev, EV_REL, lores_code) ||
 	    !libevdev_has_event_code(dev->evdev, EV_REL, hires_code))
 		return LITEST_NOT_APPLICABLE;
 
@@ -812,25 +839,86 @@ START_TEST(pointer_scroll_wheel_hires_se
 	 * sure we handle this correctly.
 	 */
 	litest_drain_events(dev->libinput);
-	litest_set_log_handler_bug(li);
+	litest_with_logcapture(li, capture) {
+		litest_event(dev, EV_REL, lores_code, 1);
+		litest_event(dev, EV_SYN, SYN_REPORT, 0);
+		litest_dispatch(li);
+		test_high_and_low_wheel_events_value(dev, lores_code, direction * 120);
 
-	litest_event(dev, EV_REL, lores_code, 1);
-	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	test_high_and_low_wheel_events_value(dev, lores_code, direction * 120);
+		litest_event(dev, EV_REL, lores_code, -1);
+		litest_event(dev, EV_SYN, SYN_REPORT, 0);
+		litest_dispatch(li);
+		test_high_and_low_wheel_events_value(dev, lores_code, direction * -120);
 
-	litest_event(dev, EV_REL, lores_code, -1);
-	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	test_high_and_low_wheel_events_value(dev, lores_code, direction * -120);
+		litest_event(dev, EV_REL, lores_code, 2);
+		litest_event(dev, EV_SYN, SYN_REPORT, 0);
+		litest_dispatch(li);
+		test_high_and_low_wheel_events_value(dev, lores_code, direction * 240);
 
-	litest_event(dev, EV_REL, lores_code, 2);
-	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	test_high_and_low_wheel_events_value(dev, lores_code, direction * 240);
+		litest_assert_empty_queue(li);
 
-	litest_assert_empty_queue(li);
-	litest_restore_log_handler(li);
+		litest_assert_strv_substring(
+			capture->bugs,
+			"only low-resolution events have been received.");
+	}
+}
+END_TEST
+
+START_TEST(pointer_scroll_wheel_hires_disabled)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	int direction;
+	unsigned int lores_code, hires_code;
+	enum libinput_pointer_axis axis =
+		litest_test_param_get_i32(test_env->params, "axis");
+
+	switch (axis) {
+	case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL:
+		lores_code = REL_WHEEL;
+		hires_code = REL_WHEEL_HI_RES;
+		direction = -1;
+		break;
+	case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL:
+		lores_code = REL_HWHEEL;
+		hires_code = REL_HWHEEL_HI_RES;
+		direction = 1;
+		break;
+	default:
+		litest_abort_msg("Invalid test axis '%d'", axis);
+	}
+
+	litest_drain_events(li);
+
+	litest_log_group("High-res events on this device should be ignored") {
+		for (size_t i = 0; i < 4; i++) {
+			litest_event(dev, EV_REL, hires_code, 60);
+			litest_event(dev, EV_SYN, SYN_REPORT, 0);
+		}
+		litest_assert_empty_queue(li);
+	}
+
+	litest_log_group("Only low-res events should be handled") {
+		for (size_t i = 0; i < 4; i++) {
+			litest_event(dev, EV_REL, hires_code, 60);
+			litest_event(dev, EV_REL, lores_code, 1);
+			litest_event(dev, EV_SYN, SYN_REPORT, 0);
+			litest_dispatch(li);
+
+			litest_drain_events_of_type(li, LIBINPUT_EVENT_POINTER_AXIS);
+			_destroy_(libinput_event) *ev = libinput_get_event(li);
+			struct libinput_event_pointer *pev = litest_is_axis_event(
+				ev,
+				LIBINPUT_EVENT_POINTER_SCROLL_WHEEL,
+				axis,
+				0);
+			int v120 =
+				libinput_event_pointer_get_scroll_value_v120(pev, axis);
+			litest_assert_int_eq(v120, direction * 120);
+		}
+		litest_drain_events_of_type(li, LIBINPUT_EVENT_POINTER_AXIS);
+		litest_assert_empty_queue(li);
+	}
 }
 END_TEST
 
@@ -838,84 +926,155 @@ START_TEST(pointer_scroll_wheel_inhibit_
 {
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
+	uint32_t delta = litest_test_param_get_u32(test_env->params, "hires-delta");
 
-	if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL_HI_RES) &&
+	if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL_HI_RES) ||
 	    !libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL_HI_RES))
 		return LITEST_NOT_APPLICABLE;
 
+	if (dev->which == LITEST_MOUSE_WHEEL_HIRES_DISABLED)
+		return LITEST_NOT_APPLICABLE;
+
 	litest_drain_events(dev->libinput);
 
-	/* Scroll deltas below the threshold (60) must be ignored */
-	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 15);
-	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 15);
+	/* A single delta (below the hardcoded threshold 60) is ignored */
+	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, delta);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
-	/* The accumulated scroll is 30, add 30 to trigger scroll */
-	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 30);
+	/* Once we get two events in the same direction trigger scroll */
+	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, delta);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
-	test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -60);
 
-	/* Once the threshold is reached, small scroll deltas are reported */
-	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 5);
+	test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -2 * delta);
+
+	/* Once the threshold is reached, every scroll deltas are reported */
+	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, delta);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
-	test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -5);
+	test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -delta);
 
 	/* When the scroll timeout is triggered, ignore small deltas again */
-	litest_timeout_wheel_scroll();
+	litest_timeout_wheel_scroll(li);
 
-	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -15);
-	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -15);
+	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -delta);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
-	litest_event(dev, EV_REL, REL_HWHEEL_HI_RES, 15);
-	litest_event(dev, EV_REL, REL_HWHEEL_HI_RES, 15);
+	litest_event(dev, EV_REL, REL_HWHEEL_HI_RES, delta);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 }
 END_TEST
 
-START_TEST(pointer_scroll_wheel_inhibit_dir_change)
+START_TEST(pointer_scroll_wheel_inhibit_small_deltas_reduce_delta)
 {
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL_HI_RES) &&
+	if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL_HI_RES) ||
 	    !libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL_HI_RES))
 		return LITEST_NOT_APPLICABLE;
 
+	if (dev->which == LITEST_MOUSE_WHEEL_HIRES_DISABLED)
+		return LITEST_NOT_APPLICABLE;
+
+	litest_drain_events(dev->libinput);
+
+	/* A single delta (below the hardcoded threshold 30) is ignored */
+	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 29);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	litest_dispatch(li);
+	litest_assert_empty_queue(li);
+
+	/* A second smaller delta changes the internal threshold */
+	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 5);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	litest_dispatch(li);
+
+	test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -34);
+
+	litest_timeout_wheel_scroll(li);
+
+	/* Internal threshold is now 5 so two deltas of 5 trigger */
+	litest_log_group("Internal threshold is now 5 so two deltas of 5 trigger") {
+		litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 5);
+		litest_event(dev, EV_SYN, SYN_REPORT, 0);
+		litest_dispatch(li);
+		litest_assert_empty_queue(li);
+		litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 5);
+		litest_event(dev, EV_SYN, SYN_REPORT, 0);
+		litest_dispatch(li);
+		test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -10);
+	}
+
+	litest_timeout_wheel_scroll(li);
+
+	litest_log_group("Internal threshold is now 5 so one delta of 10 trigger") {
+		litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 10);
+		litest_event(dev, EV_SYN, SYN_REPORT, 0);
+		litest_dispatch(li);
+		test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -10);
+	}
+}
+END_TEST
+
+START_TEST(pointer_scroll_wheel_inhibit_dir_change)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	uint32_t delta = litest_test_param_get_u32(test_env->params, "hires-delta");
+
+	if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL_HI_RES))
+		return LITEST_NOT_APPLICABLE;
+
+	if (dev->which == LITEST_MOUSE_WHEEL_HIRES_DISABLED)
+		return LITEST_NOT_APPLICABLE;
+
 	litest_drain_events(dev->libinput);
 
 	/* Scroll one detent and a bit */
-	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 120);
-	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 30);
+	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 120 + delta);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
-	test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -150);
+	test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -120 - delta);
 
 	/* Scroll below the threshold in the oposite direction should be ignored */
-	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -30);
+	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -delta);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
 	/* But should be triggered if the scroll continues in the same direction */
-	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -120);
+	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -2 * delta);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
-	test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, 150);
+	test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, 3 * delta);
 
 	/* Scroll above the threshold in the same dir should be triggered */
-	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 80);
+	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 2 * delta);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	litest_dispatch(li);
+	test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -2 * delta);
+}
+END_TEST
+
+START_TEST(pointer_scroll_wheel_no_inhibit_small_deltas_when_virtual)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+
+	litest_drain_events(li);
+
+	/* Scroll deltas below the threshold (60) must *not* be ignored */
+	litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 15);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
-	test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -80);
+	test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -15);
 }
 END_TEST
 
@@ -946,7 +1105,9 @@ START_TEST(pointer_scroll_wheel_lenovo_s
 				     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
 				     LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
 
-	v = libinput_event_pointer_get_scroll_value(ptrev, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+	v = libinput_event_pointer_get_scroll_value(
+		ptrev,
+		LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
 	litest_assert_double_eq(v, -30.0);
 	libinput_event_destroy(event);
 
@@ -957,7 +1118,9 @@ START_TEST(pointer_scroll_wheel_lenovo_s
 				     LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS,
 				     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
 				     LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
-	v = libinput_event_pointer_get_axis_value(ptrev, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+	v = libinput_event_pointer_get_axis_value(
+		ptrev,
+		LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
 	litest_assert_double_eq(v, -30.0);
 	libinput_event_destroy(event);
 
@@ -968,7 +1131,9 @@ START_TEST(pointer_scroll_wheel_lenovo_s
 				     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
 				     LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
 
-	v = libinput_event_pointer_get_scroll_value(ptrev, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+	v = libinput_event_pointer_get_scroll_value(
+		ptrev,
+		LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
 	litest_assert_double_eq(v, 60.0);
 	libinput_event_destroy(event);
 
@@ -979,10 +1144,11 @@ START_TEST(pointer_scroll_wheel_lenovo_s
 				     LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS,
 				     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
 				     LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
-	v = libinput_event_pointer_get_axis_value(ptrev, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+	v = libinput_event_pointer_get_axis_value(
+		ptrev,
+		LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
 	litest_assert_double_eq(v, 60.0);
 	libinput_event_destroy(event);
-
 }
 END_TEST
 
@@ -990,9 +1156,16 @@ START_TEST(pointer_scroll_natural_defaul
 {
 	struct litest_device *dev = litest_current_device();
 
-	litest_assert_int_ge(libinput_device_config_scroll_has_natural_scroll(dev->libinput_device), 1);
-	litest_assert_int_eq(libinput_device_config_scroll_get_natural_scroll_enabled(dev->libinput_device), 0);
-	litest_assert_int_eq(libinput_device_config_scroll_get_default_natural_scroll_enabled(dev->libinput_device), 0);
+	litest_assert_int_ge(
+		libinput_device_config_scroll_has_natural_scroll(dev->libinput_device),
+		1);
+	litest_assert_int_eq(libinput_device_config_scroll_get_natural_scroll_enabled(
+				     dev->libinput_device),
+			     0);
+	litest_assert_int_eq(
+		libinput_device_config_scroll_get_default_natural_scroll_enabled(
+			dev->libinput_device),
+		0);
 }
 END_TEST
 
@@ -1003,8 +1176,13 @@ START_TEST(pointer_scroll_natural_defaul
 	if (libinput_device_config_scroll_has_natural_scroll(dev->libinput_device))
 		return LITEST_NOT_APPLICABLE;
 
-	litest_assert_int_eq(libinput_device_config_scroll_get_natural_scroll_enabled(dev->libinput_device), 0);
-	litest_assert_int_eq(libinput_device_config_scroll_get_default_natural_scroll_enabled(dev->libinput_device), 0);
+	litest_assert_int_eq(libinput_device_config_scroll_get_natural_scroll_enabled(
+				     dev->libinput_device),
+			     0);
+	litest_assert_int_eq(
+		libinput_device_config_scroll_get_default_natural_scroll_enabled(
+			dev->libinput_device),
+		0);
 }
 END_TEST
 
@@ -1013,13 +1191,21 @@ START_TEST(pointer_scroll_natural_enable
 	struct litest_device *dev = litest_current_device();
 	enum libinput_config_status status;
 
-	status = libinput_device_config_scroll_set_natural_scroll_enabled(dev->libinput_device, 1);
-	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
-	litest_assert_int_eq(libinput_device_config_scroll_get_natural_scroll_enabled(dev->libinput_device), 1);
-
-	status = libinput_device_config_scroll_set_natural_scroll_enabled(dev->libinput_device, 0);
-	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
-	litest_assert_int_eq(libinput_device_config_scroll_get_natural_scroll_enabled(dev->libinput_device), 0);
+	status = libinput_device_config_scroll_set_natural_scroll_enabled(
+		dev->libinput_device,
+		1);
+	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+	litest_assert_int_eq(libinput_device_config_scroll_get_natural_scroll_enabled(
+				     dev->libinput_device),
+			     1);
+
+	status = libinput_device_config_scroll_set_natural_scroll_enabled(
+		dev->libinput_device,
+		0);
+	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+	litest_assert_int_eq(libinput_device_config_scroll_get_natural_scroll_enabled(
+				     dev->libinput_device),
+			     0);
 }
 END_TEST
 
@@ -1034,7 +1220,7 @@ START_TEST(pointer_scroll_natural_wheel)
 
 	/* make sure we hit at least one of the below two conditions */
 	litest_assert(libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL) ||
-		  libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL));
+		      libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL));
 
 	if (libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL)) {
 		test_wheel_event(dev, REL_WHEEL, -1);
@@ -1096,7 +1282,7 @@ START_TEST(pointer_scroll_with_rotation)
 
 	/* make sure we hit at least one of the below two conditions */
 	litest_assert(libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL) ||
-		  libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL));
+		      libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL));
 
 	if (libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL)) {
 		test_wheel_event(dev, REL_WHEEL, -1);
@@ -1120,7 +1306,6 @@ START_TEST(pointer_seat_button_count)
 {
 	struct litest_device *devices[4];
 	const int num_devices = ARRAY_LENGTH(devices);
-	struct libinput *libinput;
 	struct libinput_event *ev;
 	struct libinput_event_pointer *tev;
 	int i;
@@ -1128,25 +1313,23 @@ START_TEST(pointer_seat_button_count)
 	int expected_seat_button_count = 0;
 	char device_name[255];
 
-	libinput = litest_create_context();
+	_litest_context_destroy_ struct libinput *libinput = litest_create_context();
 	for (i = 0; i < num_devices; ++i) {
 		sprintf(device_name, "litest Generic mouse (%d)", i);
 		devices[i] = litest_add_device_with_overrides(libinput,
 							      LITEST_MOUSE,
 							      device_name,
-							      NULL, NULL, NULL);
+							      NULL,
+							      NULL,
+							      NULL);
 	}
 
 	for (i = 0; i < num_devices; ++i)
-		litest_button_click_debounced(devices[i],
-					      libinput,
-					      BTN_LEFT,
-					      true);
+		litest_button_click_debounced(devices[i], libinput, BTN_LEFT, true);
 
 	litest_dispatch(libinput);
 	while ((ev = libinput_get_event(libinput))) {
-		if (libinput_event_get_type(ev) !=
-		    LIBINPUT_EVENT_POINTER_BUTTON) {
+		if (libinput_event_get_type(ev) != LIBINPUT_EVENT_POINTER_BUTTON) {
 			libinput_event_destroy(ev);
 			litest_dispatch(libinput);
 			continue;
@@ -1157,11 +1340,10 @@ START_TEST(pointer_seat_button_count)
 		litest_assert_int_eq(libinput_event_pointer_get_button(tev),
 				     (unsigned int)BTN_LEFT);
 		litest_assert_enum_eq(libinput_event_pointer_get_button_state(tev),
-				 LIBINPUT_BUTTON_STATE_PRESSED);
+				      LIBINPUT_BUTTON_STATE_PRESSED);
 
 		++expected_seat_button_count;
-		seat_button_count =
-			libinput_event_pointer_get_seat_button_count(tev);
+		seat_button_count = libinput_event_pointer_get_seat_button_count(tev);
 		litest_assert_int_eq(expected_seat_button_count, seat_button_count);
 
 		libinput_event_destroy(ev);
@@ -1171,15 +1353,11 @@ START_TEST(pointer_seat_button_count)
 	litest_assert_int_eq(seat_button_count, num_devices);
 
 	for (i = 0; i < num_devices; ++i)
-		litest_button_click_debounced(devices[i],
-					      libinput,
-					      BTN_LEFT,
-					      false);
+		litest_button_click_debounced(devices[i], libinput, BTN_LEFT, false);
 
 	litest_dispatch(libinput);
 	while ((ev = libinput_get_event(libinput))) {
-		if (libinput_event_get_type(ev) !=
-		    LIBINPUT_EVENT_POINTER_BUTTON) {
+		if (libinput_event_get_type(ev) != LIBINPUT_EVENT_POINTER_BUTTON) {
 			libinput_event_destroy(ev);
 			litest_dispatch(libinput);
 			continue;
@@ -1190,11 +1368,10 @@ START_TEST(pointer_seat_button_count)
 		litest_assert_int_eq(libinput_event_pointer_get_button(tev),
 				     (unsigned int)BTN_LEFT);
 		litest_assert_enum_eq(libinput_event_pointer_get_button_state(tev),
-				 LIBINPUT_BUTTON_STATE_RELEASED);
+				      LIBINPUT_BUTTON_STATE_RELEASED);
 
 		--expected_seat_button_count;
-		seat_button_count =
-			libinput_event_pointer_get_seat_button_count(tev);
+		seat_button_count = libinput_event_pointer_get_seat_button_count(tev);
 		litest_assert_int_eq(expected_seat_button_count, seat_button_count);
 
 		libinput_event_destroy(ev);
@@ -1204,8 +1381,7 @@ START_TEST(pointer_seat_button_count)
 	litest_assert_int_eq(seat_button_count, 0);
 
 	for (i = 0; i < num_devices; ++i)
-		litest_delete_device(devices[i]);
-	litest_destroy_context(libinput);
+		litest_device_destroy(devices[i]);
 }
 END_TEST
 
@@ -1215,18 +1391,16 @@ START_TEST(pointer_no_calibration)
 	struct libinput_device *d = dev->libinput_device;
 	enum libinput_config_status status;
 	int rc;
-	float calibration[6] = {0};
+	float calibration[6] = { 0 };
 
 	rc = libinput_device_config_calibration_has_matrix(d);
 	litest_assert_int_eq(rc, 0);
 	rc = libinput_device_config_calibration_get_matrix(d, calibration);
 	litest_assert_int_eq(rc, 0);
-	rc = libinput_device_config_calibration_get_default_matrix(d,
-								   calibration);
+	rc = libinput_device_config_calibration_get_default_matrix(d, calibration);
 	litest_assert_int_eq(rc, 0);
 
-	status = libinput_device_config_calibration_set_matrix(d,
-							       calibration);
+	status = libinput_device_config_calibration_set_matrix(d, calibration);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 }
 END_TEST
@@ -1266,21 +1440,13 @@ START_TEST(pointer_left_handed)
 	litest_button_click_debounced(dev, li, BTN_LEFT, 1);
 	litest_button_click_debounced(dev, li, BTN_LEFT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_button_click_debounced(dev, li, BTN_RIGHT, 1);
 	litest_button_click_debounced(dev, li, BTN_RIGHT, 0);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	if (libinput_device_pointer_has_button(d, BTN_MIDDLE)) {
 		litest_button_click_debounced(dev, li, BTN_MIDDLE, 1);
@@ -1312,12 +1478,8 @@ START_TEST(pointer_left_handed_during_cl
 
 	litest_button_click_debounced(dev, li, BTN_LEFT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -1345,24 +1507,16 @@ START_TEST(pointer_left_handed_during_cl
 	litest_button_click_debounced(dev, li, BTN_RIGHT, 0);
 	litest_button_click_debounced(dev, li, BTN_LEFT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
 START_TEST(pointer_left_handed_disable_with_button_down)
 {
-	struct libinput *li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	struct litest_device *dev = litest_add_device(li, LITEST_MOUSE);
 
 	enum libinput_config_status status;
@@ -1372,23 +1526,17 @@ START_TEST(pointer_left_handed_disable_w
 	litest_drain_events(li);
 	litest_button_click_debounced(dev, li, BTN_LEFT, 1);
 	litest_dispatch(li);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 
-	litest_delete_device(dev);
+	litest_device_destroy(dev);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	struct libinput_event *event = libinput_get_event(li);
 	litest_assert_event_type(event, LIBINPUT_EVENT_DEVICE_REMOVED);
 	litest_assert_empty_queue(li);
 	libinput_event_destroy(event);
-
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -1399,9 +1547,8 @@ START_TEST(pointer_scroll_button)
 
 	/* Make left button switch to scrolling mode */
 	libinput_device_config_scroll_set_method(dev->libinput_device,
-					LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
-	libinput_device_config_scroll_set_button(dev->libinput_device,
-					BTN_LEFT);
+						 LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
+	libinput_device_config_scroll_set_button(dev->libinput_device, BTN_LEFT);
 
 	litest_drain_events(li);
 
@@ -1430,20 +1577,17 @@ START_TEST(pointer_scroll_button)
 	litest_button_scroll(dev, BTN_LEFT, 1, 1);
 
 	litest_button_scroll(dev, BTN_LEFT, 0, 0);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 
 	/* Restore default scroll behavior */
-	libinput_device_config_scroll_set_method(dev->libinput_device,
-		libinput_device_config_scroll_get_default_method(
-			dev->libinput_device));
-	libinput_device_config_scroll_set_button(dev->libinput_device,
-		libinput_device_config_scroll_get_default_button(
-			dev->libinput_device));
+	libinput_device_config_scroll_set_method(
+		dev->libinput_device,
+		libinput_device_config_scroll_get_default_method(dev->libinput_device));
+	libinput_device_config_scroll_set_button(
+		dev->libinput_device,
+		libinput_device_config_scroll_get_default_button(dev->libinput_device));
 }
 END_TEST
 
@@ -1455,14 +1599,15 @@ START_TEST(pointer_scroll_button_noscrol
 	enum libinput_config_status status;
 
 	methods = libinput_device_config_scroll_get_method(device);
-	litest_assert_int_eq((methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN), 0U);
+	litest_assert_int_eq(methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN, 0U);
 	button = libinput_device_config_scroll_get_button(device);
 	litest_assert_int_eq(button, 0U);
 	button = libinput_device_config_scroll_get_default_button(device);
 	litest_assert_int_eq(button, 0U);
 
-	status = libinput_device_config_scroll_set_method(device,
-					LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
+	status = libinput_device_config_scroll_set_method(
+		device,
+		LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 	status = libinput_device_config_scroll_set_button(device, BTN_LEFT);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
@@ -1475,17 +1620,15 @@ START_TEST(pointer_scroll_button_no_even
 	struct libinput *li = device->libinput;
 	int i;
 
-	if (!libinput_device_pointer_has_button(device->libinput_device,
-						BTN_MIDDLE))
+	if (!libinput_device_pointer_has_button(device->libinput_device, BTN_MIDDLE))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_disable_middleemu(device);
 	disable_button_scrolling(device);
 
 	libinput_device_config_scroll_set_method(device->libinput_device,
-					LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
-	libinput_device_config_scroll_set_button(device->libinput_device,
-						 BTN_LEFT);
+						 LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
+	libinput_device_config_scroll_set_button(device->libinput_device, BTN_LEFT);
 	litest_drain_events(li);
 
 	litest_button_click_debounced(device, li, BTN_LEFT, true);
@@ -1497,15 +1640,11 @@ START_TEST(pointer_scroll_button_no_even
 	}
 	litest_assert_empty_queue(li);
 
-	litest_timeout_buttonscroll();
-	litest_dispatch(li);
+	litest_timeout_buttonscroll(li);
 	litest_button_click_debounced(device, li, BTN_LEFT, false);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -1518,14 +1657,16 @@ START_TEST(pointer_scroll_button_middle_
 	enum libinput_config_status status;
 	int i;
 
-	status = libinput_device_config_middle_emulation_set_enabled(device,
-				LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+	status = libinput_device_config_middle_emulation_set_enabled(
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 
 	if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
 		return LITEST_NOT_APPLICABLE;
 
-	status = libinput_device_config_scroll_set_method(device,
-				 LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
+	status = libinput_device_config_scroll_set_method(
+		device,
+		LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	status = libinput_device_config_scroll_set_button(device, BTN_MIDDLE);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
@@ -1534,9 +1675,7 @@ START_TEST(pointer_scroll_button_middle_
 
 	litest_button_click(dev, BTN_LEFT, 1);
 	litest_button_click(dev, BTN_RIGHT, 1);
-	litest_dispatch(li);
-	litest_timeout_buttonscroll();
-	litest_dispatch(li);
+	litest_timeout_buttonscroll(li);
 
 	for (i = 0; i < 10; i++) {
 		litest_event(dev, EV_REL, REL_Y, -1);
@@ -1556,27 +1695,25 @@ START_TEST(pointer_scroll_button_middle_
 	litest_assert_empty_queue(li);
 
 	/* Restore default scroll behavior */
-	libinput_device_config_scroll_set_method(dev->libinput_device,
-		libinput_device_config_scroll_get_default_method(
-			dev->libinput_device));
-	libinput_device_config_scroll_set_button(dev->libinput_device,
-		libinput_device_config_scroll_get_default_button(
-			dev->libinput_device));
+	libinput_device_config_scroll_set_method(
+		dev->libinput_device,
+		libinput_device_config_scroll_get_default_method(dev->libinput_device));
+	libinput_device_config_scroll_set_button(
+		dev->libinput_device,
+		libinput_device_config_scroll_get_default_button(dev->libinput_device));
 }
 END_TEST
 
 START_TEST(pointer_scroll_button_device_remove_while_down)
 {
-	struct libinput *li;
 	struct litest_device *dev;
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 
 	dev = litest_add_device(li, LITEST_MOUSE);
 	libinput_device_config_scroll_set_method(dev->libinput_device,
 						 LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
-	libinput_device_config_scroll_set_button(dev->libinput_device,
-						 BTN_LEFT);
+	libinput_device_config_scroll_set_button(dev->libinput_device, BTN_LEFT);
 	litest_drain_events(li);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
@@ -1584,29 +1721,28 @@ START_TEST(pointer_scroll_button_device_
 	litest_dispatch(li);
 
 	/* delete the device  while the timer is still active */
-	litest_delete_device(dev);
+	litest_device_destroy(dev);
 	litest_dispatch(li);
-
-	litest_destroy_context(li);
 }
 END_TEST
 
 static void
-litest_enable_scroll_button_lock(struct litest_device *dev,
-				 unsigned int button)
+litest_enable_scroll_button_lock(struct litest_device *dev, unsigned int button)
 {
 	struct libinput_device *device = dev->libinput_device;
 	enum libinput_config_status status;
 
-	status = libinput_device_config_scroll_set_method(device,
-							  LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
+	status = libinput_device_config_scroll_set_method(
+		device,
+		LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	status = libinput_device_config_scroll_set_button(device, button);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
-	status = libinput_device_config_scroll_set_button_lock(device,
-							       LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED);
+	status = libinput_device_config_scroll_set_button_lock(
+		device,
+		LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 }
 
@@ -1625,8 +1761,7 @@ START_TEST(pointer_scroll_button_lock)
 
 	litest_assert_empty_queue(li);
 
-	litest_timeout_buttonscroll();
-	litest_dispatch(li);
+	litest_timeout_buttonscroll(li);
 
 	for (int i = 0; i < 10; i++) {
 		litest_event(dev, EV_REL, REL_X, 1);
@@ -1664,7 +1799,8 @@ START_TEST(pointer_scroll_button_lock_de
 
 	state = libinput_device_config_scroll_get_button_lock(dev->libinput_device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED);
-	state = libinput_device_config_scroll_get_default_button_lock(dev->libinput_device);
+	state = libinput_device_config_scroll_get_default_button_lock(
+		dev->libinput_device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED);
 }
 END_TEST
@@ -1677,23 +1813,27 @@ START_TEST(pointer_scroll_button_lock_co
 
 	state = libinput_device_config_scroll_get_button_lock(dev->libinput_device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED);
-	state = libinput_device_config_scroll_get_default_button_lock(dev->libinput_device);
+	state = libinput_device_config_scroll_get_default_button_lock(
+		dev->libinput_device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED);
 
-	status = libinput_device_config_scroll_set_button_lock(dev->libinput_device,
-							       LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED);
+	status = libinput_device_config_scroll_set_button_lock(
+		dev->libinput_device,
+		LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	state = libinput_device_config_scroll_get_button_lock(dev->libinput_device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED);
 
-	status = libinput_device_config_scroll_set_button_lock(dev->libinput_device,
-							       LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED);
+	status = libinput_device_config_scroll_set_button_lock(
+		dev->libinput_device,
+		LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	state = libinput_device_config_scroll_get_button_lock(dev->libinput_device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED);
 
-	status = libinput_device_config_scroll_set_button_lock(dev->libinput_device,
-							       LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED + 1);
+	status = libinput_device_config_scroll_set_button_lock(
+		dev->libinput_device,
+		LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED + 1);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
 }
 END_TEST
@@ -1730,9 +1870,7 @@ START_TEST(pointer_scroll_button_lock_en
 	/* but on the next button press we scroll lock */
 	litest_button_click_debounced(dev, li, BTN_LEFT, true);
 	litest_button_click_debounced(dev, li, BTN_LEFT, false);
-	litest_dispatch(li);
-	litest_timeout_buttonscroll();
-	litest_dispatch(li);
+	litest_timeout_buttonscroll(li);
 
 	for (int i = 0; i < 10; i++) {
 		litest_event(dev, EV_REL, REL_X, 1);
@@ -1771,13 +1909,13 @@ START_TEST(pointer_scroll_button_lock_en
 	/* switch method first, but enable lock when we already have a
 	 * button down */
 	libinput_device_config_scroll_set_method(dev->libinput_device,
-					LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
-	libinput_device_config_scroll_set_button(dev->libinput_device,
-					BTN_LEFT);
+						 LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
+	libinput_device_config_scroll_set_button(dev->libinput_device, BTN_LEFT);
 
 	litest_button_click_debounced(dev, li, BTN_LEFT, true);
-	libinput_device_config_scroll_set_button_lock(dev->libinput_device,
-					LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED);
+	libinput_device_config_scroll_set_button_lock(
+		dev->libinput_device,
+		LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED);
 
 	litest_button_click_debounced(dev, li, BTN_LEFT, false);
 	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
@@ -1796,9 +1934,7 @@ START_TEST(pointer_scroll_button_lock_en
 	/* but on the next button press we scroll lock */
 	litest_button_click_debounced(dev, li, BTN_LEFT, true);
 	litest_button_click_debounced(dev, li, BTN_LEFT, false);
-	litest_dispatch(li);
-	litest_timeout_buttonscroll();
-	litest_dispatch(li);
+	litest_timeout_buttonscroll(li);
 
 	for (int i = 0; i < 10; i++) {
 		litest_event(dev, EV_REL, REL_X, 1);
@@ -1839,8 +1975,7 @@ START_TEST(pointer_scroll_button_lock_ot
 	litest_button_click_debounced(dev, li, BTN_LEFT, true);
 	litest_button_click_debounced(dev, li, BTN_LEFT, false);
 	litest_assert_empty_queue(li);
-	litest_timeout_buttonscroll();
-	litest_dispatch(li);
+	litest_timeout_buttonscroll(li);
 
 	/* other button passes on normally */
 	litest_button_click_debounced(dev, li, BTN_RIGHT, true);
@@ -1854,8 +1989,7 @@ START_TEST(pointer_scroll_button_lock_ot
 		litest_event(dev, EV_REL, REL_Y, 6);
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	}
-	litest_assert_only_axis_events(li,
-				       LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS);
+	litest_assert_only_axis_events(li, LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS);
 
 	/* other button passes on normally */
 	litest_button_click_debounced(dev, li, BTN_RIGHT, true);
@@ -1866,8 +2000,7 @@ START_TEST(pointer_scroll_button_lock_ot
 	/* stop scroll lock */
 	litest_button_click_debounced(dev, li, BTN_LEFT, true);
 	litest_button_click_debounced(dev, li, BTN_LEFT, false);
-	litest_assert_only_axis_events(li,
-				       LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS);
+	litest_assert_only_axis_events(li, LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS);
 
 	/* other button passes on normally */
 	litest_button_click_debounced(dev, li, BTN_RIGHT, true);
@@ -1888,7 +2021,7 @@ START_TEST(pointer_scroll_button_lock_en
 	litest_drain_events(li);
 
 	litest_button_click_debounced(dev, li, BTN_RIGHT, true);
-	litest_timeout_middlebutton();
+	litest_timeout_middlebutton(li);
 	litest_drain_events(li);
 
 	/* Enable lock while button is down */
@@ -1900,7 +2033,9 @@ START_TEST(pointer_scroll_button_lock_en
 		litest_button_click_debounced(dev, li, BTN_LEFT, true);
 		litest_button_click_debounced(dev, li, BTN_LEFT, false);
 		litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
-		litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_button_event(li,
+					   BTN_LEFT,
+					   LIBINPUT_BUTTON_STATE_RELEASED);
 
 		for (int i = 0; i < 10; i++) {
 			litest_event(dev, EV_REL, REL_X, 1);
@@ -1917,7 +2052,7 @@ START_TEST(pointer_scroll_button_lock_en
 	/* now we should trigger it */
 	litest_button_click_debounced(dev, li, BTN_LEFT, true);
 	litest_button_click_debounced(dev, li, BTN_LEFT, false);
-	litest_timeout_buttonscroll();
+	litest_timeout_buttonscroll(li);
 	litest_assert_empty_queue(li);
 
 	for (int i = 0; i < 10; i++) {
@@ -1957,7 +2092,8 @@ START_TEST(pointer_scroll_button_lock_mi
 {
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
-	enum mb_buttonorder buttonorder = litest_test_param_get_i32(test_env->params, "buttonorder");
+	enum mb_buttonorder buttonorder =
+		litest_test_param_get_i32(test_env->params, "buttonorder");
 
 	if (!libinput_device_config_middle_emulation_is_available(dev->libinput_device))
 		return LITEST_NOT_APPLICABLE;
@@ -2012,10 +2148,8 @@ START_TEST(pointer_scroll_button_lock_mi
 		abort();
 	}
 
-	litest_dispatch(li);
-	litest_timeout_middlebutton();
-	litest_timeout_buttonscroll();
-	litest_dispatch(li);
+	litest_timeout_middlebutton(li);
+	litest_timeout_buttonscroll(li);
 
 	/* motion events are the same for all of them */
 	for (int i = 0; i < 10; i++) {
@@ -2041,9 +2175,11 @@ START_TEST(pointer_scroll_button_lock_mi
 	switch (buttonorder) {
 	case LLRR:
 	case RRLL:
-		litest_assert_button_event(li, BTN_RIGHT,
+		litest_assert_button_event(li,
+					   BTN_RIGHT,
 					   LIBINPUT_BUTTON_STATE_PRESSED);
-		litest_assert_button_event(li, BTN_RIGHT,
+		litest_assert_button_event(li,
+					   BTN_RIGHT,
 					   LIBINPUT_BUTTON_STATE_RELEASED);
 		litest_assert_scroll(li,
 				     LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS,
@@ -2055,17 +2191,17 @@ START_TEST(pointer_scroll_button_lock_mi
 	case LRRL:
 	case RLRL:
 	case RLLR:
-		litest_assert_button_event(li, BTN_MIDDLE,
+		litest_assert_button_event(li,
+					   BTN_MIDDLE,
 					   LIBINPUT_BUTTON_STATE_PRESSED);
-		litest_assert_button_event(li, BTN_MIDDLE,
+		litest_assert_button_event(li,
+					   BTN_MIDDLE,
 					   LIBINPUT_BUTTON_STATE_RELEASED);
-		litest_assert_only_typed_events(li,
-						LIBINPUT_EVENT_POINTER_MOTION);
+		litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 		break;
 	default:
 		abort();
 	}
-
 }
 END_TEST
 
@@ -2099,7 +2235,6 @@ START_TEST(pointer_scroll_button_lock_do
 	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
-
 }
 END_TEST
 
@@ -2159,37 +2294,29 @@ START_TEST(pointer_accel_defaults)
 
 	litest_assert(libinput_device_config_accel_is_available(device));
 	litest_assert_double_eq(libinput_device_config_accel_get_default_speed(device),
-			    0.0);
-	litest_assert_double_eq(libinput_device_config_accel_get_speed(device),
-			    0.0);
+				0.0);
+	litest_assert_double_eq(libinput_device_config_accel_get_speed(device), 0.0);
 
 	for (speed = -2.0; speed < -1.0; speed += 0.2) {
-		status = libinput_device_config_accel_set_speed(device,
-								speed);
-		litest_assert_enum_eq(status,
-				 LIBINPUT_CONFIG_STATUS_INVALID);
+		status = libinput_device_config_accel_set_speed(device, speed);
+		litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
 		litest_assert_double_eq(libinput_device_config_accel_get_speed(device),
-				    0.0);
+					0.0);
 	}
 
 	for (speed = -1.0; speed <= 1.0; speed += 0.2) {
-		status = libinput_device_config_accel_set_speed(device,
-								speed);
-		litest_assert_enum_eq(status,
-				 LIBINPUT_CONFIG_STATUS_SUCCESS);
+		status = libinput_device_config_accel_set_speed(device, speed);
+		litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 		litest_assert_double_eq(libinput_device_config_accel_get_speed(device),
-				    speed);
+					speed);
 	}
 
 	for (speed = 1.2; speed <= 2.0; speed += 0.2) {
-		status = libinput_device_config_accel_set_speed(device,
-								speed);
-		litest_assert_enum_eq(status,
-				 LIBINPUT_CONFIG_STATUS_INVALID);
+		status = libinput_device_config_accel_set_speed(device, speed);
+		litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
 		litest_assert_double_eq(libinput_device_config_accel_get_speed(device),
-				    1.0);
+					1.0);
 	}
-
 }
 END_TEST
 
@@ -2201,11 +2328,9 @@ START_TEST(pointer_accel_invalid)
 
 	litest_assert(libinput_device_config_accel_is_available(device));
 
-	status = libinput_device_config_accel_set_speed(device,
-							NAN);
+	status = libinput_device_config_accel_set_speed(device, NAN);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
-	status = libinput_device_config_accel_set_speed(device,
-							INFINITY);
+	status = libinput_device_config_accel_set_speed(device, INFINITY);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
 }
 END_TEST
@@ -2219,21 +2344,18 @@ START_TEST(pointer_accel_defaults_absolu
 
 	litest_assert(!libinput_device_config_accel_is_available(device));
 	litest_assert_double_eq(libinput_device_config_accel_get_default_speed(device),
-			    0.0);
-	litest_assert_double_eq(libinput_device_config_accel_get_speed(device),
-			    0.0);
+				0.0);
+	litest_assert_double_eq(libinput_device_config_accel_get_speed(device), 0.0);
 
 	for (speed = -2.0; speed <= 2.0; speed += 0.2) {
-		status = libinput_device_config_accel_set_speed(device,
-								speed);
+		status = libinput_device_config_accel_set_speed(device, speed);
 		if (speed >= -1.0 && speed <= 1.0)
 			litest_assert_enum_eq(status,
-					 LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+					      LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 		else
-			litest_assert_enum_eq(status,
-					 LIBINPUT_CONFIG_STATUS_INVALID);
+			litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
 		litest_assert_double_eq(libinput_device_config_accel_get_speed(device),
-				    0.0);
+					0.0);
 	}
 }
 END_TEST
@@ -2245,9 +2367,8 @@ START_TEST(pointer_accel_defaults_absolu
 
 	litest_assert(libinput_device_config_accel_is_available(device));
 	litest_assert_double_eq(libinput_device_config_accel_get_default_speed(device),
-			    0.0);
-	litest_assert_double_eq(libinput_device_config_accel_get_speed(device),
-			    0.0);
+				0.0);
+	litest_assert_double_eq(libinput_device_config_accel_get_speed(device), 0.0);
 }
 END_TEST
 
@@ -2308,8 +2429,9 @@ START_TEST(pointer_accel_profile_default
 	litest_assert(profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
 	litest_assert(profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
 
-	status = libinput_device_config_accel_set_profile(device,
-							  LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
+	status = libinput_device_config_accel_set_profile(
+		device,
+		LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	profile = libinput_device_config_accel_get_profile(device);
 	litest_assert_enum_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
@@ -2317,14 +2439,16 @@ START_TEST(pointer_accel_profile_default
 	profile = libinput_device_config_accel_get_default_profile(device);
 	litest_assert_enum_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE);
 
-	status = libinput_device_config_accel_set_profile(device,
-							  LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE);
+	status = libinput_device_config_accel_set_profile(
+		device,
+		LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	profile = libinput_device_config_accel_get_profile(device);
 	litest_assert_enum_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE);
 
-	status = libinput_device_config_accel_set_profile(device,
-							  LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
+	status = libinput_device_config_accel_set_profile(
+		device,
+		LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	profile = libinput_device_config_accel_get_profile(device);
 	litest_assert_enum_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
@@ -2345,17 +2469,20 @@ START_TEST(pointer_accel_config_reset_to
 	};
 
 	ARRAY_FOR_EACH(profiles, profile) {
-		litest_assert_enum_eq(libinput_device_config_accel_set_speed(device, 1.0),
-				 LIBINPUT_CONFIG_STATUS_SUCCESS);
+		litest_assert_enum_eq(
+			libinput_device_config_accel_set_speed(device, 1.0),
+			LIBINPUT_CONFIG_STATUS_SUCCESS);
 
-		litest_assert_double_eq(libinput_device_config_accel_get_speed(device), 1.0);
+		litest_assert_double_eq(libinput_device_config_accel_get_speed(device),
+					1.0);
 
-		struct libinput_config_accel *config =
-			libinput_config_accel_create(LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE);
-		litest_assert_enum_eq(libinput_device_config_accel_apply(device, config),
-				 LIBINPUT_CONFIG_STATUS_SUCCESS);
+		struct libinput_config_accel *config = libinput_config_accel_create(
+			LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE);
+		litest_assert_enum_eq(
+			libinput_device_config_accel_apply(device, config),
+			LIBINPUT_CONFIG_STATUS_SUCCESS);
 		litest_assert_double_eq(libinput_device_config_accel_get_speed(device),
-				    default_speed);
+					default_speed);
 		libinput_config_accel_destroy(config);
 	}
 }
@@ -2379,14 +2506,14 @@ START_TEST(pointer_accel_config)
 		double points[4];
 		enum libinput_config_status expected_status;
 	} tests[] = {
-		{ 0.5,   { 1.0, 2.0, 2.5, 2.6 },  valid },
+		{ 0.5, { 1.0, 2.0, 2.5, 2.6 }, valid },
 		{ 0.003, { 0.1, 0.3, 0.4, 0.45 }, valid },
-		{ 2.7,   { 1.0, 3.0, 4.5, 4.5 },  valid },
-		{ 0,     { 1.0, 2.0, 2.5, 2.6 },  invalid },
-		{ -1,    { 1.0, 2.0, 2.5, 2.6 },  invalid },
-		{ 1e10,  { 1.0, 2.0, 2.5, 2.6 },  invalid },
-		{ 1,     { 1.0, 2.0, -2.5, 2.6 }, invalid },
-		{ 1,     { 1.0, 2.0, 1e10, 2.6 }, invalid },
+		{ 2.7, { 1.0, 3.0, 4.5, 4.5 }, valid },
+		{ 0, { 1.0, 2.0, 2.5, 2.6 }, invalid },
+		{ -1, { 1.0, 2.0, 2.5, 2.6 }, invalid },
+		{ 1e10, { 1.0, 2.0, 2.5, 2.6 }, invalid },
+		{ 1, { 1.0, 2.0, -2.5, 2.6 }, invalid },
+		{ 1, { 1.0, 2.0, 1e10, 2.6 }, invalid },
 	};
 
 	litest_assert(libinput_device_config_accel_is_available(device));
@@ -2401,22 +2528,29 @@ START_TEST(pointer_accel_config)
 
 	ARRAY_FOR_EACH(tests, t) {
 		ARRAY_FOR_EACH(accel_types, accel_type) {
-			status = libinput_config_accel_set_points(config_custom_changed,
-								  *accel_type,
-								  t->step,
-								  ARRAY_LENGTH(t->points),
-								  t->points);
+			status = libinput_config_accel_set_points(
+				config_custom_changed,
+				*accel_type,
+				t->step,
+				ARRAY_LENGTH(t->points),
+				t->points);
 			litest_assert_int_eq(status, t->expected_status);
 
-			status = libinput_device_config_accel_apply(device, config_custom_changed);
+			status = libinput_device_config_accel_apply(
+				device,
+				config_custom_changed);
 			litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 			profile = libinput_device_config_accel_get_profile(device);
-			litest_assert_enum_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
+			litest_assert_enum_eq(profile,
+					      LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
 
-			status = libinput_device_config_accel_apply(device, config_custom_default);
+			status = libinput_device_config_accel_apply(
+				device,
+				config_custom_default);
 			litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 			profile = libinput_device_config_accel_get_profile(device);
-			litest_assert_enum_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
+			litest_assert_enum_eq(profile,
+					      LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
 		}
 	}
 
@@ -2433,20 +2567,26 @@ START_TEST(pointer_accel_profile_invalid
 
 	litest_assert(libinput_device_config_accel_is_available(device));
 
-	status = libinput_device_config_accel_set_profile(device,
-					   LIBINPUT_CONFIG_ACCEL_PROFILE_NONE);
+	status = libinput_device_config_accel_set_profile(
+		device,
+		LIBINPUT_CONFIG_ACCEL_PROFILE_NONE);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
 
-	status = libinput_device_config_accel_set_profile(device,
-					   LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE + 1);
+	status = libinput_device_config_accel_set_profile(
+		device,
+		LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE + 1);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
 
-	status = libinput_device_config_accel_set_profile(device,
-			   LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE |LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
+	status = libinput_device_config_accel_set_profile(
+		device,
+		LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE |
+			LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
 
-	status = libinput_device_config_accel_set_profile(device,
-			   LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM |LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
+	status = libinput_device_config_accel_set_profile(
+		device,
+		LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM |
+			LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
 }
 END_TEST
@@ -2466,16 +2606,20 @@ START_TEST(pointer_accel_profile_noaccel
 	profile = libinput_device_config_accel_get_profile(device);
 	litest_assert_enum_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_NONE);
 
-	status = libinput_device_config_accel_set_profile(device,
-					   LIBINPUT_CONFIG_ACCEL_PROFILE_NONE);
+	status = libinput_device_config_accel_set_profile(
+		device,
+		LIBINPUT_CONFIG_ACCEL_PROFILE_NONE);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
 
-	status = libinput_device_config_accel_set_profile(device,
-					   LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE + 1);
+	status = libinput_device_config_accel_set_profile(
+		device,
+		LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE + 1);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
 
-	status = libinput_device_config_accel_set_profile(device,
-			   LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE |LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
+	status = libinput_device_config_accel_set_profile(
+		device,
+		LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE |
+			LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
 }
 END_TEST
@@ -2517,8 +2661,8 @@ START_TEST(middlebutton)
 	disable_button_scrolling(device);
 
 	status = libinput_device_config_middle_emulation_set_enabled(
-					    device->libinput_device,
-					    LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+		device->libinput_device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 	if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
 		return LITEST_NOT_APPLICABLE;
 
@@ -2556,15 +2700,14 @@ START_TEST(middlebutton_nostart_while_do
 		{ BTN_RIGHT, BTN_LEFT, BTN_RIGHT, BTN_LEFT },
 	};
 
-	if (!libinput_device_pointer_has_button(device->libinput_device,
-						BTN_MIDDLE))
+	if (!libinput_device_pointer_has_button(device->libinput_device, BTN_MIDDLE))
 		return LITEST_NOT_APPLICABLE;
 
 	disable_button_scrolling(device);
 
 	status = libinput_device_config_middle_emulation_set_enabled(
-					    device->libinput_device,
-					    LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+		device->libinput_device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 	if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
 		return LITEST_NOT_APPLICABLE;
 
@@ -2609,8 +2752,8 @@ START_TEST(middlebutton_timeout)
 	disable_button_scrolling(device);
 
 	status = libinput_device_config_middle_emulation_set_enabled(
-					    device->libinput_device,
-					    LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+		device->libinput_device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 	if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
 		return LITEST_NOT_APPLICABLE;
 
@@ -2618,16 +2761,12 @@ START_TEST(middlebutton_timeout)
 		litest_drain_events(li);
 		litest_button_click_debounced(device, li, button, true);
 		litest_assert_empty_queue(li);
-		litest_timeout_middlebutton();
+		litest_timeout_middlebutton(li);
 
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 		litest_button_click_debounced(device, li, button, false);
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 		litest_assert_empty_queue(li);
 	}
 }
@@ -2649,8 +2788,8 @@ START_TEST(middlebutton_doubleclick)
 	disable_button_scrolling(device);
 
 	status = libinput_device_config_middle_emulation_set_enabled(
-				    device->libinput_device,
-				    LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+		device->libinput_device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 	if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
 		return LITEST_NOT_APPLICABLE;
 
@@ -2691,13 +2830,12 @@ START_TEST(middlebutton_middleclick)
 
 	disable_button_scrolling(device);
 
-	if (!libinput_device_pointer_has_button(device->libinput_device,
-					       BTN_MIDDLE))
+	if (!libinput_device_pointer_has_button(device->libinput_device, BTN_MIDDLE))
 		return LITEST_NOT_APPLICABLE;
 
 	status = libinput_device_config_middle_emulation_set_enabled(
-					    device->libinput_device,
-					    LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+		device->libinput_device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 	if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
 		return LITEST_NOT_APPLICABLE;
 
@@ -2707,17 +2845,13 @@ START_TEST(middlebutton_middleclick)
 		litest_drain_events(li);
 		litest_button_click_debounced(device, li, button, true);
 		litest_button_click_debounced(device, li, BTN_MIDDLE, true);
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 		litest_assert_button_event(li,
 					   BTN_MIDDLE,
 					   LIBINPUT_BUTTON_STATE_PRESSED);
 		litest_assert_empty_queue(li);
 		litest_button_click_debounced(device, li, button, false);
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 		litest_button_click_debounced(device, li, BTN_MIDDLE, false);
 		litest_assert_button_event(li,
 					   BTN_MIDDLE,
@@ -2727,9 +2861,7 @@ START_TEST(middlebutton_middleclick)
 		/* release middle before button */
 		litest_button_click_debounced(device, li, button, true);
 		litest_button_click_debounced(device, li, BTN_MIDDLE, true);
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 		litest_assert_button_event(li,
 					   BTN_MIDDLE,
 					   LIBINPUT_BUTTON_STATE_PRESSED);
@@ -2739,9 +2871,7 @@ START_TEST(middlebutton_middleclick)
 					   BTN_MIDDLE,
 					   LIBINPUT_BUTTON_STATE_RELEASED);
 		litest_button_click_debounced(device, li, button, false);
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 		litest_assert_empty_queue(li);
 	}
 }
@@ -2756,13 +2886,12 @@ START_TEST(middlebutton_middleclick_duri
 
 	disable_button_scrolling(device);
 
-	if (!libinput_device_pointer_has_button(device->libinput_device,
-						BTN_MIDDLE))
+	if (!libinput_device_pointer_has_button(device->libinput_device, BTN_MIDDLE))
 		return LITEST_NOT_APPLICABLE;
 
 	status = libinput_device_config_middle_emulation_set_enabled(
-					    device->libinput_device,
-					    LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+		device->libinput_device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 	if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
 		return LITEST_NOT_APPLICABLE;
 
@@ -2791,17 +2920,13 @@ START_TEST(middlebutton_middleclick_duri
 		litest_button_click_debounced(device, li, button, false);
 		litest_assert_empty_queue(li);
 		litest_button_click_debounced(device, li, button, true);
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 		litest_assert_empty_queue(li);
 
 		/* release both */
 		litest_button_click_debounced(device, li, BTN_LEFT, false);
 		litest_button_click_debounced(device, li, BTN_RIGHT, false);
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 		litest_assert_empty_queue(li);
 
 		litest_button_click_debounced(device, li, BTN_MIDDLE, false);
@@ -2821,8 +2946,7 @@ START_TEST(middlebutton_default_enabled)
 	int available;
 	enum libinput_config_middle_emulation_state state;
 
-	if (!libinput_device_pointer_has_button(dev->libinput_device,
-						BTN_MIDDLE))
+	if (!libinput_device_pointer_has_button(dev->libinput_device, BTN_MIDDLE))
 		return LITEST_NOT_APPLICABLE;
 
 	available = libinput_device_config_middle_emulation_is_available(device);
@@ -2831,16 +2955,17 @@ START_TEST(middlebutton_default_enabled)
 	state = libinput_device_config_middle_emulation_get_enabled(device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
 
-	state = libinput_device_config_middle_emulation_get_default_enabled(
-					    device);
+	state = libinput_device_config_middle_emulation_get_default_enabled(device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
 
-	status = libinput_device_config_middle_emulation_set_enabled(device,
-					    LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+	status = libinput_device_config_middle_emulation_set_enabled(
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
-	status = libinput_device_config_middle_emulation_set_enabled(device,
-					    LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
+	status = libinput_device_config_middle_emulation_set_enabled(
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	status = libinput_device_config_middle_emulation_set_enabled(device, 3);
@@ -2861,16 +2986,17 @@ START_TEST(middlebutton_default_clickpad
 
 	state = libinput_device_config_middle_emulation_get_enabled(device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
-	state = libinput_device_config_middle_emulation_get_default_enabled(
-					    device);
+	state = libinput_device_config_middle_emulation_get_default_enabled(device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
 
-	status = libinput_device_config_middle_emulation_set_enabled(device,
-					    LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+	status = libinput_device_config_middle_emulation_set_enabled(
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
-	status = libinput_device_config_middle_emulation_set_enabled(device,
-					    LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
+	status = libinput_device_config_middle_emulation_set_enabled(
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	status = libinput_device_config_middle_emulation_set_enabled(device, 3);
@@ -2888,7 +3014,7 @@ START_TEST(middlebutton_default_touchpad
 
 	if (streq(name, "litest AlpsPS/2 ALPS GlidePoint") ||
 	    streq(name, "litest AlpsPS/2 ALPS DualPoint TouchPad"))
-	    return LITEST_NOT_APPLICABLE;
+		return LITEST_NOT_APPLICABLE;
 
 	available = libinput_device_config_middle_emulation_is_available(device);
 	litest_assert(!available);
@@ -2896,11 +3022,9 @@ START_TEST(middlebutton_default_touchpad
 	if (libinput_device_pointer_has_button(device, BTN_MIDDLE))
 		return LITEST_NOT_APPLICABLE;
 
-	state = libinput_device_config_middle_emulation_get_enabled(
-					    device);
+	state = libinput_device_config_middle_emulation_get_enabled(device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
-	state = libinput_device_config_middle_emulation_get_default_enabled(
-					    device);
+	state = libinput_device_config_middle_emulation_get_default_enabled(device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
 }
 END_TEST
@@ -2915,11 +3039,9 @@ START_TEST(middlebutton_default_alps)
 	available = libinput_device_config_middle_emulation_is_available(device);
 	litest_assert(available);
 
-	state = libinput_device_config_middle_emulation_get_enabled(
-					    device);
+	state = libinput_device_config_middle_emulation_get_enabled(device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
-	state = libinput_device_config_middle_emulation_get_default_enabled(
-					    device);
+	state = libinput_device_config_middle_emulation_get_default_enabled(device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 }
 END_TEST
@@ -2936,14 +3058,15 @@ START_TEST(middlebutton_default_disabled
 	litest_assert(!available);
 	state = libinput_device_config_middle_emulation_get_enabled(device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
-	state = libinput_device_config_middle_emulation_get_default_enabled(
-								    device);
+	state = libinput_device_config_middle_emulation_get_default_enabled(device);
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
-	status = libinput_device_config_middle_emulation_set_enabled(device,
-				     LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
+	status = libinput_device_config_middle_emulation_set_enabled(
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
-	status = libinput_device_config_middle_emulation_set_enabled(device,
-				     LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+	status = libinput_device_config_middle_emulation_set_enabled(
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 }
 END_TEST
@@ -2959,13 +3082,14 @@ START_TEST(middlebutton_button_scrolling
 	int i;
 
 	status = libinput_device_config_middle_emulation_set_enabled(
-				device,
-				LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 	if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
 		return LITEST_NOT_APPLICABLE;
 
-	status = libinput_device_config_scroll_set_method(device,
-				LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
+	status = libinput_device_config_scroll_set_method(
+		device,
+		LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
 	if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
 		return LITEST_NOT_APPLICABLE;
 
@@ -2982,13 +3106,11 @@ START_TEST(middlebutton_button_scrolling
 	/* middle emulation discards */
 	litest_assert_empty_queue(li);
 
-	litest_timeout_middlebutton();
-	litest_dispatch(li);
+	litest_timeout_middlebutton(li);
 
 	/* scroll discards */
 	litest_assert_empty_queue(li);
-	litest_timeout_buttonscroll();
-	litest_dispatch(li);
+	litest_timeout_buttonscroll(li);
 
 	for (i = 0; i < 10; i++) {
 		litest_event(dev, EV_REL, REL_Y, 1);
@@ -3002,9 +3124,10 @@ START_TEST(middlebutton_button_scrolling
 					   LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS,
 					   LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
 					   LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
-		litest_assert_double_gt(litest_event_pointer_get_value(pev,
-								   LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
-				    0.0);
+		litest_assert_double_gt(litest_event_pointer_get_value(
+						pev,
+						LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
+					0.0);
 		libinput_event_destroy(ev);
 		ev = libinput_get_event(li);
 	} while (ev);
@@ -3031,13 +3154,14 @@ START_TEST(middlebutton_button_scrolling
 	enum libinput_config_status status;
 
 	status = libinput_device_config_middle_emulation_set_enabled(
-				device,
-				LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 	if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
 		return LITEST_NOT_APPLICABLE;
 
-	status = libinput_device_config_scroll_set_method(device,
-				LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
+	status = libinput_device_config_scroll_set_method(
+		device,
+		LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
 	if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
 		return LITEST_NOT_APPLICABLE;
 
@@ -3055,9 +3179,7 @@ START_TEST(middlebutton_button_scrolling
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
@@ -3065,9 +3187,7 @@ START_TEST(middlebutton_button_scrolling
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -3083,8 +3203,8 @@ START_TEST(middlebutton_device_remove_wh
 	libinput_device_config_scroll_set_method(device,
 						 LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
 	status = libinput_device_config_middle_emulation_set_enabled(
-				device,
-				LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 	if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
 		return LITEST_NOT_APPLICABLE;
 
@@ -3096,9 +3216,7 @@ START_TEST(middlebutton_device_remove_wh
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_assert_empty_queue(li);
 }
@@ -3114,8 +3232,8 @@ START_TEST(middlebutton_device_remove_wh
 	libinput_device_config_scroll_set_method(device,
 						 LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
 	status = libinput_device_config_middle_emulation_set_enabled(
-				device,
-				LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 	if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
 		return LITEST_NOT_APPLICABLE;
 
@@ -3150,7 +3268,7 @@ START_TEST(pointer_time_usec)
 
 	time_usec = libinput_event_pointer_get_time_usec(ptrev);
 	litest_assert_int_eq(libinput_event_pointer_get_time(ptrev),
-			 (uint32_t) (time_usec / 1000));
+			     (uint32_t)(time_usec / 1000));
 
 	libinput_event_destroy(event);
 	litest_drain_events(dev->libinput);
@@ -3163,8 +3281,7 @@ START_TEST(debounce_bounce)
 	struct libinput *li = dev->libinput;
 	unsigned int button = litest_test_param_get_i32(test_env->params, "button");
 
-	if (!libinput_device_pointer_has_button(dev->libinput_device,
-						button))
+	if (!libinput_device_pointer_has_button(dev->libinput_device, button))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_disable_middleemu(dev);
@@ -3177,13 +3294,9 @@ START_TEST(debounce_bounce)
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_event(dev, EV_KEY, button, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_debounce();
-	litest_dispatch(li);
+	litest_timeout_debounce(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_empty_queue(li);
 
 	litest_event(dev, EV_KEY, button, 0);
@@ -3192,13 +3305,9 @@ START_TEST(debounce_bounce)
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_event(dev, EV_KEY, button, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_debounce();
-	litest_dispatch(li);
+	litest_timeout_debounce(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -3210,8 +3319,7 @@ START_TEST(debounce_bounce_high_delay)
 	struct libinput *li = dev->libinput;
 	unsigned int button = litest_test_param_get_i32(test_env->params, "button");
 
-	if (!libinput_device_pointer_has_button(dev->libinput_device,
-						button))
+	if (!libinput_device_pointer_has_button(dev->libinput_device, button))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_disable_middleemu(dev);
@@ -3231,13 +3339,9 @@ START_TEST(debounce_bounce_high_delay)
 	msleep(15);
 	litest_event(dev, EV_KEY, button, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_debounce();
-	litest_dispatch(li);
+	litest_timeout_debounce(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_empty_queue(li);
 
 	litest_event(dev, EV_KEY, button, 0);
@@ -3250,13 +3354,9 @@ START_TEST(debounce_bounce_high_delay)
 	msleep(15);
 	litest_event(dev, EV_KEY, button, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_debounce();
-	litest_dispatch(li);
+	litest_timeout_debounce(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -3274,21 +3374,17 @@ START_TEST(debounce_bounce_check_immedia
 	/* Press must be sent without delay */
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_timeout_debounce();
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_debounce(li);
 	litest_assert_empty_queue(li);
 
 	/* held down & past timeout, we expect releases to be immediate */
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
-	litest_timeout_debounce();
+	litest_timeout_debounce(li);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -3300,40 +3396,26 @@ debounce_trigger_spurious(struct litest_
 {
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_debounce();
-	litest_dispatch(li);
+	litest_timeout_debounce(li);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
 
-	litest_timeout_debounce();
-	litest_dispatch(li);
+	litest_timeout_debounce(li);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	/* gets filtered now */
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_debounce();
-	litest_dispatch(li);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_timeout_debounce(li);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 }
 
@@ -3343,8 +3425,7 @@ START_TEST(debounce_spurious)
 	struct libinput *li = dev->libinput;
 	unsigned int button = litest_test_param_get_i32(test_env->params, "button");
 
-	if (!libinput_device_pointer_has_button(dev->libinput_device,
-						button))
+	if (!libinput_device_pointer_has_button(dev->libinput_device, button))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_disable_middleemu(dev);
@@ -3356,20 +3437,15 @@ START_TEST(debounce_spurious)
 	for (int i = 0; i < 3; i++) {
 		litest_event(dev, EV_KEY, button, 1);
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
-		litest_dispatch(li);
-		litest_timeout_debounce();
-		litest_dispatch(li);
+		litest_timeout_debounce(li);
 
 		/* Not all devices can disable middle button emulation, time out on
 		 * middle button here to make sure the initial button press event
 		 * was flushed.
 		 */
-		litest_timeout_middlebutton();
-		litest_dispatch(li);
+		litest_timeout_middlebutton(li);
 
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 		/* bouncy bouncy bouncy */
 		litest_event(dev, EV_KEY, button, 0);
@@ -3380,12 +3456,8 @@ START_TEST(debounce_spurious)
 
 		litest_event(dev, EV_KEY, button, 0);
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
-		litest_dispatch(li);
-		litest_timeout_debounce();
-		litest_dispatch(li);
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_timeout_debounce(li);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 		litest_assert_empty_queue(li);
 	}
@@ -3411,19 +3483,14 @@ START_TEST(debounce_spurious_multibounce
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_debounce();
+	litest_timeout_debounce(li);
 
 	/* Not all devices can disable middle button emulation, time out on
 	 * middle button here to make sure the initial button press event
 	 * was flushed.
 	 */
-	litest_dispatch(li);
-	litest_timeout_middlebutton();
-	litest_dispatch(li);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_middlebutton(li);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
@@ -3441,11 +3508,9 @@ START_TEST(debounce_spurious_multibounce
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
 	litest_assert_empty_queue(li);
-	litest_timeout_debounce();
+	litest_timeout_debounce(li);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -3461,13 +3526,9 @@ START_TEST(debounce_spurious_trigger_hig
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_debounce();
-	litest_dispatch(li);
+	litest_timeout_debounce(li);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	/* Spurious timeout is 12ms after a button down or up. Make sure we go
 	 * over 12ms for the total bouncing duration, but stay under 12ms for
@@ -3488,15 +3549,10 @@ START_TEST(debounce_spurious_trigger_hig
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 
-	litest_timeout_debounce();
-	litest_dispatch(li);
+	litest_timeout_debounce(li);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	/* gets filtered now */
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
@@ -3504,18 +3560,13 @@ START_TEST(debounce_spurious_trigger_hig
 	litest_dispatch(li);
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_debounce();
+	litest_timeout_debounce(li);
 	litest_assert_empty_queue(li);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_debounce();
-	litest_dispatch(li);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_timeout_debounce(li);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -3536,9 +3587,7 @@ START_TEST(debounce_spurious_dont_enable
 	/* Don't trigger spurious debouncing on otherbutton events */
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_debounce();
-	litest_dispatch(li);
+	litest_timeout_debounce(li);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
@@ -3553,44 +3602,26 @@ START_TEST(debounce_spurious_dont_enable
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 
 	/* Expect release to be immediate */
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_debounce();
-	litest_dispatch(li);
+	litest_timeout_debounce(li);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -3611,9 +3642,7 @@ START_TEST(debounce_spurious_cancel_debo
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_debounce();
-	litest_dispatch(li);
+	litest_timeout_debounce(li);
 
 	/* spurious debouncing is on but the release should get flushed by
 	 * the other button */
@@ -3630,25 +3659,13 @@ START_TEST(debounce_spurious_cancel_debo
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -3668,9 +3685,7 @@ START_TEST(debounce_spurious_switch_to_o
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_debounce();
-	litest_dispatch(li);
+	litest_timeout_debounce(li);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
@@ -3689,19 +3704,11 @@ START_TEST(debounce_spurious_switch_to_o
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -3709,10 +3716,9 @@ END_TEST
 
 START_TEST(debounce_remove_device_button_up)
 {
-	struct libinput *li;
 	struct litest_device *dev;
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 
 	dev = litest_add_device(li, LITEST_MOUSE);
 	litest_drain_events(li);
@@ -3724,22 +3730,17 @@ START_TEST(debounce_remove_device_button
 	litest_dispatch(li);
 
 	/* delete the device  while the timer is still active */
-	litest_delete_device(dev);
-	litest_dispatch(li);
-
-	litest_timeout_debounce();
-	litest_dispatch(li);
+	litest_device_destroy(dev);
 
-	litest_destroy_context(li);
+	litest_timeout_debounce(li);
 }
 END_TEST
 
 START_TEST(debounce_remove_device_button_down)
 {
-	struct libinput *li;
 	struct litest_device *dev;
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 
 	dev = litest_add_device(li, LITEST_MOUSE);
 	litest_drain_events(li);
@@ -3749,18 +3750,15 @@ START_TEST(debounce_remove_device_button
 	litest_dispatch(li);
 
 	/* delete the device the timer is still active */
-	litest_delete_device(dev);
-	litest_dispatch(li);
-
-	litest_timeout_debounce();
-	litest_dispatch(li);
+	litest_device_destroy(dev);
 
-	litest_destroy_context(li);
+	litest_timeout_debounce(li);
 }
 END_TEST
 
 TEST_COLLECTION(pointer)
 {
+	/* clang-format off */
 	litest_add(pointer_motion_relative, LITEST_RELATIVE, LITEST_POINTINGSTICK);
 	litest_add_for_device(pointer_motion_relative_zero, LITEST_MOUSE);
 	litest_with_parameters(params,
@@ -3778,13 +3776,21 @@ TEST_COLLECTION(pointer)
 	litest_add_for_device(pointer_button_has_no_button, LITEST_KEYBOARD);
 	litest_add(pointer_recover_from_lost_button_count, LITEST_BUTTON, LITEST_CLICKPAD);
 	litest_add(pointer_scroll_wheel, LITEST_WHEEL, LITEST_TABLET);
-	litest_add(pointer_scroll_wheel_hires, LITEST_WHEEL, LITEST_TABLET);
+	litest_with_parameters(params, "axis", 'I', 2, litest_named_i32(REL_WHEEL_HI_RES, "vertical"),
+						       litest_named_i32(REL_HWHEEL_HI_RES, "horizontal")) {
+		litest_add_parametrized(pointer_scroll_wheel_hires, LITEST_WHEEL, LITEST_TABLET, params);
+	}
 	litest_with_parameters(params, "axis", 'I', 2, litest_named_i32(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, "vertical"),
 						       litest_named_i32(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, "horizontal")) {
 		litest_add_parametrized(pointer_scroll_wheel_hires_send_only_lores, LITEST_WHEEL, LITEST_TABLET, params);
+		litest_add_parametrized_for_device(pointer_scroll_wheel_hires_disabled, LITEST_MOUSE_WHEEL_HIRES_DISABLED, params);
+	}
+	litest_with_parameters(params, "hires-delta", 'u', 3, 5, 15, 20) {
+		litest_add_parametrized(pointer_scroll_wheel_inhibit_small_deltas, LITEST_WHEEL, LITEST_TABLET, params);
+		litest_add_parametrized(pointer_scroll_wheel_inhibit_dir_change, LITEST_WHEEL, LITEST_TABLET, params);
 	}
-	litest_add(pointer_scroll_wheel_inhibit_small_deltas, LITEST_WHEEL, LITEST_TABLET);
-	litest_add(pointer_scroll_wheel_inhibit_dir_change, LITEST_WHEEL, LITEST_TABLET);
+	litest_add(pointer_scroll_wheel_inhibit_small_deltas_reduce_delta, LITEST_WHEEL, LITEST_TABLET);
+	litest_add_for_device(pointer_scroll_wheel_no_inhibit_small_deltas_when_virtual, LITEST_MOUSE_VIRTUAL);
 	litest_add_for_device(pointer_scroll_wheel_lenovo_scrollpoint, LITEST_LENOVO_SCROLLPOINT);
 	litest_add(pointer_scroll_button, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY);
 	litest_add(pointer_scroll_button_noscroll, LITEST_ABSOLUTE|LITEST_BUTTON, LITEST_RELATIVE);
@@ -3889,4 +3895,5 @@ TEST_COLLECTION(pointer)
 	litest_add(debounce_spurious_switch_to_otherbutton, LITEST_BUTTON, LITEST_TOUCHPAD|LITEST_NO_DEBOUNCE);
 	litest_add_no_device(debounce_remove_device_button_down);
 	litest_add_no_device(debounce_remove_device_button_up);
+	/* clang-format off */
 }
diff -pruN 1.28.1-1/test/test-quirks.c 1.30.0-1/test/test-quirks.c
--- 1.28.1-1/test/test-quirks.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-quirks.c	2025-11-25 03:40:43.000000000 +0000
@@ -45,184 +45,170 @@ struct data_dir {
 	char *filename;
 };
 
-static struct data_dir
-make_data_dir(const char *file_content)
+static struct data_dir *
+data_dir_new(const char *file_content)
 {
-	struct data_dir dir = {0};
+	struct data_dir *dir = zalloc(sizeof(*dir));
 	char dirname[PATH_MAX] = "/tmp/litest-quirk-test-XXXXXX";
-	char *filename;
-	FILE *fp;
-	int rc;
 
 	litest_assert_notnull(mkdtemp(dirname));
-	dir.dirname = safe_strdup(dirname);
+	dir->dirname = safe_strdup(dirname);
 
 	if (file_content) {
-		rc = xasprintf(&filename, "%s/testfile.quirks", dirname);
-		litest_assert_int_eq(rc, (int)(strlen(dirname) + 16));
+		char *filename = strdup_printf("%s/testfile.quirks", dirname);
 
-		fp = fopen(filename, "w+");
+		_autofclose_ FILE *fp = fopen(filename, "w+");
 #ifndef __clang_analyzer__
 		litest_assert_notnull(fp);
 #else
 		assert(fp);
 #endif
-		rc = fputs(file_content, fp);
+		int rc = fputs(file_content, fp); // NOLINT: unix.Stream
 		litest_assert_errno_success(rc);
-		fclose(fp);
-		dir.filename = filename;
+		dir->filename = filename;
 	}
 
 	return dir;
 }
 
 static void
-cleanup_data_dir(struct data_dir dd)
+data_dir_destroy(struct data_dir *dd)
 {
-	if (dd.filename) {
-		unlink(dd.filename);
-		free(dd.filename);
-	}
-	if (dd.dirname) {
-		rmdir(dd.dirname);
-		free(dd.dirname);
+	if (dd->filename) {
+		unlink(dd->filename);
+		free(dd->filename);
+	}
+	if (dd->dirname) {
+		rmdir(dd->dirname);
+		free(dd->dirname);
 	}
+	free(dd);
 }
 
+DEFINE_DESTROY_CLEANUP_FUNC(data_dir);
+
 START_TEST(quirks_invalid_dir)
 {
-	struct quirks_context *ctx;
-
-	ctx = quirks_init_subsystem("/does-not-exist",
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_LIBINPUT_LOGGING);
+	_unref_(quirks_context) *ctx = quirks_init_subsystem("/does-not-exist",
+							     NULL,
+							     log_handler,
+							     NULL,
+							     QLOG_LIBINPUT_LOGGING);
 	litest_assert(ctx == NULL);
 }
 END_TEST
 
 START_TEST(quirks_empty_dir)
 {
-	struct quirks_context *ctx;
-	struct data_dir dd = make_data_dir(NULL);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_LIBINPUT_LOGGING);
+	_destroy_(data_dir) *dd = data_dir_new(NULL);
+	_unref_(quirks_context) *ctx = quirks_init_subsystem(dd->dirname,
+							     NULL,
+							     log_handler,
+							     NULL,
+							     QLOG_LIBINPUT_LOGGING);
 	litest_assert(ctx == NULL);
-
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_section_empty)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] = "[Empty Section]";
-	struct data_dir dd = make_data_dir(quirks_file);
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
 
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_section_double)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] = "[Section name]";
-	struct data_dir dd = make_data_dir(quirks_file);
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
 
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_section_missing_match)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"AttrSizeHint=10x10\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"AttrSizeHint=10x10\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_section_missing_attr)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=mouse\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchUdevType=mouse\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_section_match_after_attr)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=mouse\n"
-	"AttrSizeHint=10x10\n"
-	"MatchName=mouse\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchUdevType=mouse\n"
+		"AttrSizeHint=10x10\n"
+		"MatchName=mouse\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_section_duplicate_match)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=mouse\n"
-	"MatchUdevType=mouse\n"
-	"AttrSizeHint=10x10\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchUdevType=mouse\n"
+		"MatchUdevType=mouse\n"
+		"AttrSizeHint=10x10\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
@@ -230,401 +216,375 @@ START_TEST(quirks_section_duplicate_attr
 {
 	/* This shouldn't be allowed but the current parser
 	   is happy with it */
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=mouse\n"
-	"AttrSizeHint=10x10\n"
-	"AttrSizeHint=10x10\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchUdevType=mouse\n"
+		"AttrSizeHint=10x10\n"
+		"AttrSizeHint=10x10\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert_notnull(ctx);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_error_section)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section Missing Bracket\n"
-	"MatchUdevType=mouse\n"
-	"AttrSizeHint=10x10\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section Missing Bracket\n"
+		"MatchUdevType=mouse\n"
+		"AttrSizeHint=10x10\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_error_trailing_whitespace)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=mouse    \n"
-	"AttrSizeHint=10x10\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchUdevType=mouse    \n"
+		"AttrSizeHint=10x10\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_error_unknown_match)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"Matchblahblah=mouse\n"
-	"AttrSizeHint=10x10\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"Matchblahblah=mouse\n"
+		"AttrSizeHint=10x10\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_error_unknown_attr)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=mouse\n"
-	"Attrblahblah=10x10\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchUdevType=mouse\n"
+		"Attrblahblah=10x10\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_error_unknown_model)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=mouse\n"
-	"Modelblahblah=1\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchUdevType=mouse\n"
+		"Modelblahblah=1\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_error_unknown_prefix)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=mouse\n"
-	"Fooblahblah=10x10\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchUdevType=mouse\n"
+		"Fooblahblah=10x10\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_error_model_not_one)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=mouse\n"
-	"ModelAppleTouchpad=true\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
-	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
+		"[Section name]\n"
+		"MatchUdevType=mouse\n"
+		"ModelAppleTouchpad=true\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 }
 END_TEST
 
 START_TEST(quirks_parse_comment_inline)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name] # some inline comment\n"
-	"MatchUdevType=mouse\t   # another inline comment\n"
-	"ModelAppleTouchpad=1#\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name] # some inline comment\n"
+		"MatchUdevType=mouse\t   # another inline comment\n"
+		"ModelAppleTouchpad=1#\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert_notnull(ctx);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_comment_empty)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"#\n"
-	"   #\n"
-	"MatchUdevType=mouse\n"
-	"ModelAppleTouchpad=1\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"#\n"
+		"   #\n"
+		"MatchUdevType=mouse\n"
+		"ModelAppleTouchpad=1\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert_notnull(ctx);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_string_quotes_single)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=mouse\n"
-	"AttrKeyboardIntegration='internal'\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchUdevType=mouse\n"
+		"AttrKeyboardIntegration='internal'\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_string_quotes_double)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=mouse\n"
-	"AttrKeyboardIntegration=\"internal\"\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchUdevType=mouse\n"
+		"AttrKeyboardIntegration=\"internal\"\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_bustype)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchBus=usb\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchBus=bluetooth\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchBus=i2c\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchBus=rmi\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchBus=ps2\n"
-	"ModelAppleTouchpad=1\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchBus=usb\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchBus=bluetooth\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchBus=i2c\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchBus=rmi\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchBus=ps2\n"
+		"ModelAppleTouchpad=1\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert_notnull(ctx);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_bustype_invalid)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchBus=venga\n"
-	"ModelAppleTouchpad=1\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchBus=venga\n"
+		"ModelAppleTouchpad=1\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_vendor)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchVendor=0x0000\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchVendor=0x0001\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchVendor=0x2343\n"
-	"ModelAppleTouchpad=1\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchVendor=0x0000\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchVendor=0x0001\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchVendor=0x2343\n"
+		"ModelAppleTouchpad=1\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert_notnull(ctx);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_vendor_invalid)
 {
-	struct quirks_context *ctx;
 	const char *quirks_file[] = {
-	"[Section name]\n"
-	"MatchVendor=-1\n"
-	"ModelAppleTouchpad=1\n",
-	"[Section name]\n"
-	"MatchVendor=abc\n"
-	"ModelAppleTouchpad=1\n",
-	"[Section name]\n"
-	"MatchVendor=0xFFFFF\n"
-	"ModelAppleTouchpad=1\n",
-	"[Section name]\n"
-	"MatchVendor=123\n"
-	"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchVendor=-1\n"
+		"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchVendor=abc\n"
+		"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchVendor=0xFFFFF\n"
+		"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchVendor=123\n"
+		"ModelAppleTouchpad=1\n",
 	};
 
 	ARRAY_FOR_EACH(quirks_file, qf) {
-		struct data_dir dd = make_data_dir(*qf);
+		_destroy_(data_dir) *dd = data_dir_new(*qf);
 
-		ctx = quirks_init_subsystem(dd.dirname,
-					    NULL,
-					    log_handler,
-					    NULL,
-					    QLOG_CUSTOM_LOG_PRIORITIES);
+		_unref_(quirks_context) *ctx =
+			quirks_init_subsystem(dd->dirname,
+					      NULL,
+					      log_handler,
+					      NULL,
+					      QLOG_CUSTOM_LOG_PRIORITIES);
 		litest_assert(ctx == NULL);
-		cleanup_data_dir(dd);
 	}
 }
 END_TEST
 
 START_TEST(quirks_parse_product)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchProduct=0x12AB\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchProduct=0x0001;0x1234;0xABCD\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchProduct=0x2343\n"
-	"ModelAppleTouchpad=1\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchProduct=0x12AB\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchProduct=0x0001;0x1234;0xABCD\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchProduct=0x2343\n"
+		"ModelAppleTouchpad=1\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert_notnull(ctx);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_product_too_many)
 {
-	struct quirks_context *ctx;
 	const char prologue[] =
-	"[Section name]\n"
-	"MatchProduct=0x12AB\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchProduct=";
-	const char epilogue[] = "\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchProduct=0x2343\n"
-	"ModelAppleTouchpad=1\n";
+		"[Section name]\n"
+		"MatchProduct=0x12AB\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchProduct=";
+	const char epilogue[] =
+		"\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchProduct=0x2343\n"
+		"ModelAppleTouchpad=1\n";
 
-	char matches[4096] = {0};
+	char matches[4096] = { 0 };
 
 	for (int i = 0; i < 128; i++) {
 		int len = strlen(matches);
@@ -632,371 +592,354 @@ START_TEST(quirks_parse_product_too_many
 		snprintf(&matches[len], remaining, "0x%04X", i);
 	}
 
-	char *quirks_file = NULL;
-	xasprintf(&quirks_file, "%s%s%s", prologue, matches, epilogue);
-	struct data_dir dd = make_data_dir(quirks_file);
+	char *quirks_file = strdup_printf("%s%s%s", prologue, matches, epilogue);
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
 
 	free(quirks_file);
 
 	/* This test will only blow up in valgrind/asan */
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert(ctx == NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_product_invalid)
 {
-	struct quirks_context *ctx;
 	const char *quirks_file[] = {
-	"[Section name]\n"
-	"MatchProduct=-1\n"
-	"ModelAppleTouchpad=1\n",
-	"[Section name]\n"
-	"MatchProduct=abc\n"
-	"ModelAppleTouchpad=1\n",
-	"[Section name]\n"
-	"MatchProduct=0xFFFFF\n"
-	"ModelAppleTouchpad=1\n",
-	"[Section name]\n"
-	"MatchProduct=123\n"
-	"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchProduct=-1\n"
+		"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchProduct=abc\n"
+		"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchProduct=0xFFFFF\n"
+		"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchProduct=123\n"
+		"ModelAppleTouchpad=1\n",
 	};
 
 	ARRAY_FOR_EACH(quirks_file, qf) {
-		struct data_dir dd = make_data_dir(*qf);
+		_destroy_(data_dir) *dd = data_dir_new(*qf);
 
-		ctx = quirks_init_subsystem(dd.dirname,
-					    NULL,
-					    log_handler,
-					    NULL,
-					    QLOG_CUSTOM_LOG_PRIORITIES);
+		_unref_(quirks_context) *ctx =
+			quirks_init_subsystem(dd->dirname,
+					      NULL,
+					      log_handler,
+					      NULL,
+					      QLOG_CUSTOM_LOG_PRIORITIES);
 		litest_assert(ctx == NULL);
-		cleanup_data_dir(dd);
 	}
 }
 END_TEST
 
 START_TEST(quirks_parse_version)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchVersion=0x0000\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchVersion=0x0001\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchVersion=0x2343\n"
-	"ModelAppleTouchpad=1\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchVersion=0x0000\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchVersion=0x0001\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchVersion=0x2343\n"
+		"ModelAppleTouchpad=1\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert_notnull(ctx);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_version_invalid)
 {
-	struct quirks_context *ctx;
 	const char *quirks_file[] = {
-	"[Section name]\n"
-	"MatchVersion=-1\n"
-	"ModelAppleTouchpad=1\n",
-	"[Section name]\n"
-	"MatchVersion=abc\n"
-	"ModelAppleTouchpad=1\n",
-	"[Section name]\n"
-	"MatchVersion=0xFFFFF\n"
-	"ModelAppleTouchpad=1\n",
-	"[Section name]\n"
-	"MatchVersion=123\n"
-	"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchVersion=-1\n"
+		"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchVersion=abc\n"
+		"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchVersion=0xFFFFF\n"
+		"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchVersion=123\n"
+		"ModelAppleTouchpad=1\n",
 	};
 
 	ARRAY_FOR_EACH(quirks_file, qf) {
-		struct data_dir dd = make_data_dir(*qf);
+		_destroy_(data_dir) *dd = data_dir_new(*qf);
 
-		ctx = quirks_init_subsystem(dd.dirname,
-					    NULL,
-					    log_handler,
-					    NULL,
-					    QLOG_CUSTOM_LOG_PRIORITIES);
+		_unref_(quirks_context) *ctx =
+			quirks_init_subsystem(dd->dirname,
+					      NULL,
+					      log_handler,
+					      NULL,
+					      QLOG_CUSTOM_LOG_PRIORITIES);
 		litest_assert(ctx == NULL);
-		cleanup_data_dir(dd);
 	}
 }
 END_TEST
 
 START_TEST(quirks_parse_name)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchName=1235\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchName=abc\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchName=*foo\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchName=foo*\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchName=foo[]\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchName=*foo*\n"
-	"ModelAppleTouchpad=1\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchName=1235\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchName=abc\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchName=*foo\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchName=foo*\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchName=foo[]\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchName=*foo*\n"
+		"ModelAppleTouchpad=1\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert_notnull(ctx);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_name_invalid)
 {
-	struct quirks_context *ctx;
 	const char *quirks_file[] = {
-	"[Section name]\n"
-	"MatchName=\n"
-	"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchName=\n"
+		"ModelAppleTouchpad=1\n",
 	};
 
 	ARRAY_FOR_EACH(quirks_file, qf) {
-		struct data_dir dd = make_data_dir(*qf);
+		_destroy_(data_dir) *dd = data_dir_new(*qf);
 
-		ctx = quirks_init_subsystem(dd.dirname,
-					    NULL,
-					    log_handler,
-					    NULL,
-					    QLOG_CUSTOM_LOG_PRIORITIES);
+		_unref_(quirks_context) *ctx =
+			quirks_init_subsystem(dd->dirname,
+					      NULL,
+					      log_handler,
+					      NULL,
+					      QLOG_CUSTOM_LOG_PRIORITIES);
 		litest_assert(ctx == NULL);
-		cleanup_data_dir(dd);
 	}
 }
 END_TEST
 
 START_TEST(quirks_parse_uniq)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section Uniq]\n"
-	"MatchUniq=1235\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section Uniq]\n"
-	"MatchUniq=abc\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section Uniq]\n"
-	"MatchUniq=*foo\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section Uniq]\n"
-	"MatchUniq=foo*\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section Uniq]\n"
-	"MatchUniq=foo[]\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section Uniq]\n"
-	"MatchUniq=*foo*\n"
-	"ModelAppleTouchpad=1\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section Uniq]\n"
+		"MatchUniq=1235\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section Uniq]\n"
+		"MatchUniq=abc\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section Uniq]\n"
+		"MatchUniq=*foo\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section Uniq]\n"
+		"MatchUniq=foo*\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section Uniq]\n"
+		"MatchUniq=foo[]\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section Uniq]\n"
+		"MatchUniq=*foo*\n"
+		"ModelAppleTouchpad=1\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert_notnull(ctx);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_uniq_invalid)
 {
-	struct quirks_context *ctx;
 	const char *quirks_file[] = {
-	"[Section name]\n"
-	"MatchUniq=\n"
-	"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchUniq=\n"
+		"ModelAppleTouchpad=1\n",
 	};
 
 	ARRAY_FOR_EACH(quirks_file, qf) {
-		struct data_dir dd = make_data_dir(*qf);
+		_destroy_(data_dir) *dd = data_dir_new(*qf);
 
-		ctx = quirks_init_subsystem(dd.dirname,
-					    NULL,
-					    log_handler,
-					    NULL,
-					    QLOG_CUSTOM_LOG_PRIORITIES);
+		_unref_(quirks_context) *ctx =
+			quirks_init_subsystem(dd->dirname,
+					      NULL,
+					      log_handler,
+					      NULL,
+					      QLOG_CUSTOM_LOG_PRIORITIES);
 		litest_assert(ctx == NULL);
-		cleanup_data_dir(dd);
 	}
 }
 END_TEST
 
 START_TEST(quirks_parse_udev)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=touchpad\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchUdevType=mouse\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchUdevType=pointingstick\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchUdevType=tablet\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchUdevType=tablet-pad\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchUdevType=keyboard\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchUdevType=joystick\n"
-	"ModelAppleTouchpad=1\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchUdevType=touchpad\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchUdevType=mouse\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchUdevType=pointingstick\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchUdevType=tablet\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchUdevType=tablet-pad\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchUdevType=keyboard\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchUdevType=joystick\n"
+		"ModelAppleTouchpad=1\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert_notnull(ctx);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_udev_invalid)
 {
-	struct quirks_context *ctx;
 	const char *quirks_file[] = {
-	"[Section name]\n"
-	"MatchUdevType=blah\n"
-	"ModelAppleTouchpad=1\n",
-	"[Section name]\n"
-	"MatchUdevType=\n"
-	"ModelAppleTouchpad=1\n",
-	"[Section name]\n"
-	"MatchUdevType=123\n"
-	"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchUdevType=blah\n"
+		"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchUdevType=\n"
+		"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchUdevType=123\n"
+		"ModelAppleTouchpad=1\n",
 	};
 
 	ARRAY_FOR_EACH(quirks_file, qf) {
-		struct data_dir dd = make_data_dir(*qf);
+		_destroy_(data_dir) *dd = data_dir_new(*qf);
 
-		ctx = quirks_init_subsystem(dd.dirname,
-					    NULL,
-					    log_handler,
-					    NULL,
-					    QLOG_CUSTOM_LOG_PRIORITIES);
+		_unref_(quirks_context) *ctx =
+			quirks_init_subsystem(dd->dirname,
+					      NULL,
+					      log_handler,
+					      NULL,
+					      QLOG_CUSTOM_LOG_PRIORITIES);
 		litest_assert(ctx == NULL);
-		cleanup_data_dir(dd);
 	}
 }
 END_TEST
 
 START_TEST(quirks_parse_dmi)
 {
-	struct quirks_context *ctx;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchDMIModalias=dmi:*\n"
-	"ModelAppleTouchpad=1\n"
-	"\n"
-	"[Section name]\n"
-	"MatchDMIModalias=dmi:*svn*pn*:\n"
-	"ModelAppleTouchpad=1\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+		"[Section name]\n"
+		"MatchDMIModalias=dmi:*\n"
+		"ModelAppleTouchpad=1\n"
+		"\n"
+		"[Section name]\n"
+		"MatchDMIModalias=dmi:*svn*pn*:\n"
+		"ModelAppleTouchpad=1\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert_notnull(ctx);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 START_TEST(quirks_parse_dmi_invalid)
 {
-	struct quirks_context *ctx;
 	const char *quirks_file[] = {
-	"[Section name]\n"
-	"MatchDMIModalias=\n"
-	"ModelAppleTouchpad=1\n",
-	"[Section name]\n"
-	"MatchDMIModalias=*pn*\n"
-	"ModelAppleTouchpad=1\n",
-	"[Section name]\n"
-	"MatchDMIModalias=dmi*pn*\n"
-	"ModelAppleTouchpad=1\n",
-	"[Section name]\n"
-	"MatchDMIModalias=foo\n"
-	"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchDMIModalias=\n"
+		"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchDMIModalias=*pn*\n"
+		"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchDMIModalias=dmi*pn*\n"
+		"ModelAppleTouchpad=1\n",
+		"[Section name]\n"
+		"MatchDMIModalias=foo\n"
+		"ModelAppleTouchpad=1\n",
 	};
 
 	ARRAY_FOR_EACH(quirks_file, qf) {
-		struct data_dir dd = make_data_dir(*qf);
+		_destroy_(data_dir) *dd = data_dir_new(*qf);
 
-		ctx = quirks_init_subsystem(dd.dirname,
-					    NULL,
-					    log_handler,
-					    NULL,
-					    QLOG_CUSTOM_LOG_PRIORITIES);
+		_unref_(quirks_context) *ctx =
+			quirks_init_subsystem(dd->dirname,
+					      NULL,
+					      log_handler,
+					      NULL,
+					      QLOG_CUSTOM_LOG_PRIORITIES);
 		litest_assert(ctx == NULL);
-		cleanup_data_dir(dd);
 	}
 }
 END_TEST
 
-typedef bool (*qparsefunc) (struct quirks *q, enum quirk which, void* data);
+typedef bool (*qparsefunc)(struct quirks *q, enum quirk which, void *data);
 
 /*
    Helper for generic testing, matches on a mouse device with the given
@@ -1004,7 +947,7 @@ typedef bool (*qparsefunc) (struct quirk
    and calls func() to return the value in data. The func has to take the
    right data, otherwise boom. Usage:
    rc = test_attr_parse(dev, QUIRK_ATTR_SIZE_HINT,
-                        "10x30", quirks_get_dimensions,
+			"10x30", quirks_get_dimensions,
 			&some_struct_quirks_dimensions);
    if (rc == false) // failed to parse
    else // struct now contains the 10, 30 values
@@ -1016,9 +959,8 @@ test_attr_parse(struct litest_device *de
 		qparsefunc func,
 		void *data)
 {
-	struct udev_device *ud = libinput_device_get_udev_device(dev->libinput_device);
-	struct quirks_context *ctx;
-	struct data_dir dd;
+	_unref_(udev_device) *ud =
+		libinput_device_get_udev_device(dev->libinput_device);
 	char buf[512];
 	bool result;
 
@@ -1030,34 +972,30 @@ test_attr_parse(struct litest_device *de
 		 quirk_get_name(which),
 		 str);
 
-	dd = make_data_dir(buf);
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+	_destroy_(data_dir) *dd = data_dir_new(buf);
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	if (ctx != NULL) {
-		struct quirks *q;
-		q = quirks_fetch_for_device(ctx, ud);
+		_unref_(quirks) *q = quirks_fetch_for_device(ctx, ud);
 		litest_assert_notnull(q);
 		litest_assert(func(q, which, data));
 		litest_assert(quirks_has_quirk(q, which));
-		quirks_unref(q);
-		quirks_context_unref(ctx);
 		result = true;
 	} else {
 		result = false;
 	}
 
-	cleanup_data_dir(dd);
-	udev_device_unref(ud);
 	return result;
 }
 
 struct qtest_dim {
-		const char *str;
-		bool success;
-		size_t w, h;
+	const char *str;
+	bool success;
+	size_t w, h;
 };
 
 START_TEST(quirks_parse_dimension_attr)
@@ -1067,6 +1005,7 @@ START_TEST(quirks_parse_dimension_attr)
 		QUIRK_ATTR_SIZE_HINT,
 		QUIRK_ATTR_RESOLUTION_HINT,
 	};
+	/* clang-format off */
 	struct qtest_dim test_values[] = {
 		{ "10x10", true, 10, 10 },
 		{ "20x30", true, 20, 30 },
@@ -1076,6 +1015,7 @@ START_TEST(quirks_parse_dimension_attr)
 		{ "0x00", false, 0, 0 },
 		{ "0xa0", false, 0, 0 },
 	};
+	/* clang-format on */
 
 	ARRAY_FOR_EACH(attrs, a) {
 		ARRAY_FOR_EACH(test_values, t) {
@@ -1099,9 +1039,9 @@ START_TEST(quirks_parse_dimension_attr)
 END_TEST
 
 struct qtest_range {
-		const char *str;
-		bool success;
-		int hi, lo;
+	const char *str;
+	bool success;
+	int hi, lo;
 };
 
 START_TEST(quirks_parse_range_attr)
@@ -1111,6 +1051,7 @@ START_TEST(quirks_parse_range_attr)
 		QUIRK_ATTR_TOUCH_SIZE_RANGE,
 		QUIRK_ATTR_PRESSURE_RANGE,
 	};
+	/* clang-format off */
 	struct qtest_range test_values[] = {
 		{ "20:10", true, 20, 10 },
 		{ "30:5", true, 30, 5 },
@@ -1128,6 +1069,7 @@ START_TEST(quirks_parse_range_attr)
 		{ "0xa0", false, 0, 0 },
 		{ "0x10:0x5", false, 0, 0 },
 	};
+	/* clang-format on */
 
 	ARRAY_FOR_EACH(attrs, a) {
 		ARRAY_FOR_EACH(test_values, t) {
@@ -1164,6 +1106,7 @@ START_TEST(quirks_parse_uint_attr)
 		QUIRK_ATTR_PALM_PRESSURE_THRESHOLD,
 		QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD,
 	};
+	/* clang-format off */
 	struct qtest_uint test_values[] = {
 		{ "10", true, 10 },
 		{ "0", true, 0 },
@@ -1175,6 +1118,7 @@ START_TEST(quirks_parse_uint_attr)
 		{ "0xab", false, 0 },
 		{ "ab", false, 0 },
 	};
+	/* clang-format on */
 
 	ARRAY_FOR_EACH(attrs, a) {
 		ARRAY_FOR_EACH(test_values, t) {
@@ -1208,6 +1152,7 @@ START_TEST(quirks_parse_double_attr)
 	enum quirk attrs[] = {
 		QUIRK_ATTR_TRACKPOINT_MULTIPLIER,
 	};
+	/* clang-format off */
 	struct qtest_double test_values[] = {
 		{ "10", true, 10.0 },
 		{ "10.0", true, 10.0 },
@@ -1227,6 +1172,7 @@ START_TEST(quirks_parse_double_attr)
 		{ "10:5", false, 0 },
 		{ "10x5", false, 0 },
 	};
+	/* clang-format on */
 
 	ARRAY_FOR_EACH(attrs, a) {
 		ARRAY_FOR_EACH(test_values, t) {
@@ -1261,6 +1207,7 @@ START_TEST(quirks_parse_string_attr)
 		QUIRK_ATTR_LID_SWITCH_RELIABILITY,
 		QUIRK_ATTR_KEYBOARD_INTEGRATION,
 	};
+	/* clang-format off */
 	struct qtest_str test_values[] = {
 		{ "below", QUIRK_ATTR_TPKBCOMBO_LAYOUT },
 		{ "reliable", QUIRK_ATTR_LID_SWITCH_RELIABILITY },
@@ -1278,6 +1225,7 @@ START_TEST(quirks_parse_string_attr)
 		{ "0xa", 0 },
 		{ "0.0", 0 },
 	};
+	/* clang-format on */
 
 	ARRAY_FOR_EACH(attrs, a) {
 		ARRAY_FOR_EACH(test_values, t) {
@@ -1308,9 +1256,10 @@ START_TEST(quirks_parse_bool_attr)
 {
 	struct litest_device *dev = litest_current_device();
 	enum quirk attrs[] = {
-	        QUIRK_ATTR_USE_VELOCITY_AVERAGING,
+		QUIRK_ATTR_USE_VELOCITY_AVERAGING,
 		QUIRK_ATTR_TABLET_SMOOTHING,
 	};
+	/* clang-format off */
 	struct qtest_bool test_values[] = {
 		{ "0", true, false },
 		{ "1", true, true },
@@ -1318,6 +1267,7 @@ START_TEST(quirks_parse_bool_attr)
 		{ "-1", false, false },
 		{ "a", false, false },
 	};
+	/* clang-format on */
 
 	ARRAY_FOR_EACH(attrs, a) {
 		ARRAY_FOR_EACH(test_values, t) {
@@ -1343,13 +1293,11 @@ START_TEST(quirks_parse_integration_attr
 {
 	struct litest_device *dev = litest_current_device();
 	char *do_not_use; /* freed before we can use it */
-	bool
-
-	rc = test_attr_parse(dev,
-			     QUIRK_ATTR_KEYBOARD_INTEGRATION,
-			     "internal",
-			     (qparsefunc)quirks_get_string,
-			     &do_not_use);
+	bool rc = test_attr_parse(dev,
+				  QUIRK_ATTR_KEYBOARD_INTEGRATION,
+				  "internal",
+				  (qparsefunc)quirks_get_string,
+				  &do_not_use);
 	litest_assert(rc);
 	rc = test_attr_parse(dev,
 			     QUIRK_ATTR_KEYBOARD_INTEGRATION,
@@ -1375,114 +1323,96 @@ END_TEST
 START_TEST(quirks_model_one)
 {
 	struct litest_device *dev = litest_current_device();
-	struct udev_device *ud = libinput_device_get_udev_device(dev->libinput_device);
-	struct quirks_context *ctx;
+	_unref_(udev_device) *ud =
+		libinput_device_get_udev_device(dev->libinput_device);
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=mouse\n"
-	"ModelAppleTouchpad=1\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-	struct quirks *q;
+		"[Section name]\n"
+		"MatchUdevType=mouse\n"
+		"ModelAppleTouchpad=1\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
 	bool isset;
 
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert_notnull(ctx);
 
-	q = quirks_fetch_for_device(ctx, ud);
+	_unref_(quirks) *q = quirks_fetch_for_device(ctx, ud);
 	litest_assert_notnull(q);
 
 	litest_assert(quirks_get_bool(q, QUIRK_MODEL_APPLE_TOUCHPAD, &isset));
 	litest_assert(isset == true);
-
-	quirks_unref(q);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
-	udev_device_unref(ud);
 }
 END_TEST
 
 START_TEST(quirks_model_zero)
 {
 	struct litest_device *dev = litest_current_device();
-	struct udev_device *ud = libinput_device_get_udev_device(dev->libinput_device);
-	struct quirks_context *ctx;
+	_unref_(udev_device) *ud =
+		libinput_device_get_udev_device(dev->libinput_device);
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=mouse\n"
-	"ModelAppleTouchpad=0\n";
-	struct data_dir dd = make_data_dir(quirks_file);
-	struct quirks *q;
+		"[Section name]\n"
+		"MatchUdevType=mouse\n"
+		"ModelAppleTouchpad=0\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
 	bool isset;
 
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert_notnull(ctx);
 
-	q = quirks_fetch_for_device(ctx, ud);
+	_unref_(quirks) *q = quirks_fetch_for_device(ctx, ud);
 	litest_assert_notnull(q);
 
 	litest_assert(quirks_get_bool(q, QUIRK_MODEL_APPLE_TOUCHPAD, &isset));
 	litest_assert(isset == false);
-
-	quirks_unref(q);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
-	udev_device_unref(ud);
 }
 END_TEST
 
 START_TEST(quirks_model_override)
 {
 	struct litest_device *dev = litest_current_device();
-	struct udev_device *ud = libinput_device_get_udev_device(dev->libinput_device);
-	struct quirks_context *ctx;
-	char *quirks_file;
-	struct data_dir dd;
-	struct quirks *q;
+	_unref_(udev_device) *ud =
+		libinput_device_get_udev_device(dev->libinput_device);
 	bool isset;
 	bool set = litest_test_param_get_bool(test_env->params, "enable_model");
 
 	/* Test model quirks override by setting, then unsetting (or the
 	   other way round) */
-	int rc = xasprintf(&quirks_file,
-			   "[first]\n"
-			   "MatchUdevType=mouse\n"
-			   "ModelAppleTouchpad=%d\n"
-			   "\n"
-			   "[second]\n"
-			   "MatchUdevType=mouse\n"
-			   "ModelAppleTouchpad=%d\n",
-			   set ? 0 : 1,
-			   set ? 1 : 0);
-	litest_assert_int_ne(rc, -1);
-
-	dd = make_data_dir(quirks_file);
-
-	ctx = quirks_init_subsystem(dd.dirname,
-				    NULL,
-				    log_handler,
-				    NULL,
-				    QLOG_CUSTOM_LOG_PRIORITIES);
+	_autofree_ char *quirks_file = strdup_printf(
+		"[first]\n"
+		"MatchUdevType=mouse\n"
+		"ModelAppleTouchpad=%d\n"
+		"\n"
+		"[second]\n"
+		"MatchUdevType=mouse\n"
+		"ModelAppleTouchpad=%d\n",
+		set ? 0 : 1,
+		set ? 1 : 0);
+	litest_assert_ptr_notnull(quirks_file);
+
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
+
+	_unref_(quirks_context) *ctx =
+		quirks_init_subsystem(dd->dirname,
+				      NULL,
+				      log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	litest_assert_notnull(ctx);
 
-	q = quirks_fetch_for_device(ctx, ud);
+	_unref_(quirks) *q = quirks_fetch_for_device(ctx, ud);
 	litest_assert_notnull(q);
 
 	litest_assert(quirks_get_bool(q, QUIRK_MODEL_APPLE_TOUCHPAD, &isset));
 	litest_assert(isset == set);
-
-	quirks_unref(q);
-	quirks_context_unref(ctx);
-	cleanup_data_dir(dd);
-	udev_device_unref(ud);
-	free(quirks_file);
 }
 END_TEST
 
@@ -1568,13 +1498,13 @@ START_TEST(quirks_call_NULL)
 {
 	litest_assert(!quirks_fetch_for_device(NULL, NULL));
 
-	litest_assert(!quirks_get_uint32(NULL, 0, NULL));
-	litest_assert(!quirks_get_int32(NULL, 0, NULL));
-	litest_assert(!quirks_get_range(NULL, 0, NULL));
-	litest_assert(!quirks_get_dimensions(NULL, 0, NULL));
-	litest_assert(!quirks_get_double(NULL, 0, NULL));
-	litest_assert(!quirks_get_string(NULL, 0, NULL));
-	litest_assert(!quirks_get_bool(NULL, 0, NULL));
+	litest_assert(!quirks_get_uint32(NULL, QUIRK_NONE, NULL));
+	litest_assert(!quirks_get_int32(NULL, QUIRK_NONE, NULL));
+	litest_assert(!quirks_get_range(NULL, QUIRK_NONE, NULL));
+	litest_assert(!quirks_get_dimensions(NULL, QUIRK_NONE, NULL));
+	litest_assert(!quirks_get_double(NULL, QUIRK_NONE, NULL));
+	litest_assert(!quirks_get_string(NULL, QUIRK_NONE, NULL));
+	litest_assert(!quirks_get_bool(NULL, QUIRK_NONE, NULL));
 }
 END_TEST
 
@@ -1582,12 +1512,12 @@ START_TEST(quirks_ctx_ref)
 {
 	struct quirks_context *ctx, *ctx2;
 	const char quirks_file[] =
-	"[Section name]\n"
-	"MatchUdevType=mouse\n"
-	"AttrSizeHint=10x10\n";
-	struct data_dir dd = make_data_dir(quirks_file);
+		"[Section name]\n"
+		"MatchUdevType=mouse\n"
+		"AttrSizeHint=10x10\n";
+	_destroy_(data_dir) *dd = data_dir_new(quirks_file);
 
-	ctx = quirks_init_subsystem(dd.dirname,
+	ctx = quirks_init_subsystem(dd->dirname,
 				    NULL,
 				    log_handler,
 				    NULL,
@@ -1599,12 +1529,12 @@ START_TEST(quirks_ctx_ref)
 	litest_assert_ptr_eq(ctx2, NULL);
 	ctx2 = quirks_context_unref(ctx);
 	litest_assert_ptr_eq(ctx2, NULL);
-	cleanup_data_dir(dd);
 }
 END_TEST
 
 TEST_COLLECTION(quirks)
 {
+	/* clang-format off */
 	litest_add_deviceless(quirks_invalid_dir);
 	litest_add_deviceless(quirks_empty_dir);
 
@@ -1667,4 +1597,5 @@ TEST_COLLECTION(quirks)
 
 	litest_add_deviceless(quirks_call_NULL);
 	litest_add_deviceless(quirks_ctx_ref);
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-switch.c 1.30.0-1/test/test-switch.c
--- 1.28.1-1/test/test-switch.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-switch.c	2025-11-25 03:40:43.000000000 +0000
@@ -24,6 +24,7 @@
 #include <config.h>
 
 #include <fcntl.h>
+#include <libevdev/libevdev.h>
 #include <libinput.h>
 
 #include "libinput-util.h"
@@ -50,14 +51,14 @@ START_TEST(switch_has_cap)
 	/* Need to check for this specific device here because the
 	 * unreliable tablet mode switch removes the capability too */
 	if (dev->which == LITEST_TABLET_MODE_UNRELIABLE) {
-		litest_assert(!libinput_device_has_capability(dev->libinput_device,
-							  LIBINPUT_DEVICE_CAP_SWITCH));
+		litest_assert(
+			!libinput_device_has_capability(dev->libinput_device,
+							LIBINPUT_DEVICE_CAP_SWITCH));
 		return LITEST_NOT_APPLICABLE;
 	}
 
 	litest_assert(libinput_device_has_capability(dev->libinput_device,
-						 LIBINPUT_DEVICE_CAP_SWITCH));
-
+						     LIBINPUT_DEVICE_CAP_SWITCH));
 }
 END_TEST
 
@@ -69,8 +70,8 @@ START_TEST(switch_has_lid_switch)
 		return LITEST_NOT_APPLICABLE;
 
 	litest_assert_int_eq(libinput_device_switch_has_switch(dev->libinput_device,
-							   LIBINPUT_SWITCH_LID),
-			 1);
+							       LIBINPUT_SWITCH_LID),
+			     1);
 }
 END_TEST
 
@@ -79,9 +80,9 @@ tablet_mode_switch_is_reliable(struct li
 {
 	bool is_unreliable = false;
 
-       quirks_get_bool(dev->quirks,
-		QUIRK_MODEL_TABLET_MODE_SWITCH_UNRELIABLE,
-		&is_unreliable);
+	quirks_get_bool(dev->quirks,
+			QUIRK_MODEL_TABLET_MODE_SWITCH_UNRELIABLE,
+			&is_unreliable);
 
 	return !is_unreliable;
 }
@@ -175,9 +176,7 @@ lid_switch_is_reliable(struct litest_dev
 	char *prop;
 	bool is_reliable = false;
 
-	if (quirks_get_string(dev->quirks,
-			      QUIRK_ATTR_LID_SWITCH_RELIABILITY,
-			      &prop)) {
+	if (quirks_get_string(dev->quirks, QUIRK_ATTR_LID_SWITCH_RELIABILITY, &prop)) {
 		is_reliable = streq(prop, "reliable");
 	}
 
@@ -187,7 +186,6 @@ lid_switch_is_reliable(struct litest_dev
 START_TEST(switch_down_on_init)
 {
 	struct litest_device *dev = litest_current_device();
-	struct libinput *li;
 	struct libinput_event *event;
 	enum libinput_switch sw = litest_test_param_get_i32(test_env->params, "switch");
 
@@ -202,9 +200,8 @@ START_TEST(switch_down_on_init)
 	litest_ungrab_device(dev);
 
 	/* need separate context to test */
-	li = litest_create_context();
-	libinput_path_add_device(li,
-				 libevdev_uinput_get_devnode(dev->uinput));
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	libinput_path_add_device(li, libevdev_uinput_get_devnode(dev->uinput));
 	litest_dispatch(li);
 
 	litest_wait_for_event_of_type(li, LIBINPUT_EVENT_SWITCH_TOGGLE);
@@ -224,15 +221,12 @@ START_TEST(switch_down_on_init)
 	litest_is_switch_event(event, sw, LIBINPUT_SWITCH_STATE_OFF);
 	libinput_event_destroy(event);
 	litest_assert_empty_queue(li);
-
-	litest_destroy_context(li);
 }
 END_TEST
 
 START_TEST(switch_not_down_on_init)
 {
 	struct litest_device *dev = litest_current_device();
-	struct libinput *li;
 	struct libinput_event *event;
 	enum libinput_switch sw = LIBINPUT_SWITCH_LID;
 
@@ -247,9 +241,8 @@ START_TEST(switch_not_down_on_init)
 	litest_ungrab_device(dev);
 
 	/* need separate context to test */
-	li = litest_create_context();
-	libinput_path_add_device(li,
-				 libevdev_uinput_get_devnode(dev->uinput));
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	libinput_path_add_device(li, libevdev_uinput_get_devnode(dev->uinput));
 	litest_dispatch(li);
 
 	while ((event = libinput_get_event(li)) != NULL) {
@@ -260,7 +253,6 @@ START_TEST(switch_not_down_on_init)
 
 	litest_switch_action(dev, sw, LIBINPUT_SWITCH_STATE_OFF);
 	litest_assert_empty_queue(li);
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -277,7 +269,8 @@ START_TEST(switch_disable_touchpad)
 	struct litest_device *sw = litest_current_device();
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
-	enum libinput_switch which = litest_test_param_get_i32(test_env->params, "switch");
+	enum libinput_switch which =
+		litest_test_param_get_i32(test_env->params, "switch");
 	if (libinput_device_switch_has_switch(sw->libinput_device, which) <= 0)
 		return LITEST_NOT_APPLICABLE;
 
@@ -306,7 +299,7 @@ START_TEST(switch_disable_touchpad)
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(touchpad);
+	litest_device_destroy(touchpad);
 	litest_ungrab_device(sw);
 }
 END_TEST
@@ -316,7 +309,8 @@ START_TEST(switch_disable_touchpad_durin
 	struct litest_device *sw = litest_current_device();
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
-	enum libinput_switch which = litest_test_param_get_i32(test_env->params, "switch");
+	enum libinput_switch which =
+		litest_test_param_get_i32(test_env->params, "switch");
 	if (libinput_device_switch_has_switch(sw->libinput_device, which) <= 0)
 		return LITEST_NOT_APPLICABLE;
 
@@ -338,7 +332,7 @@ START_TEST(switch_disable_touchpad_durin
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(touchpad);
+	litest_device_destroy(touchpad);
 }
 END_TEST
 
@@ -347,7 +341,8 @@ START_TEST(switch_disable_touchpad_edge_
 	struct litest_device *sw = litest_current_device();
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
-	enum libinput_switch which = litest_test_param_get_i32(test_env->params, "switch");
+	enum libinput_switch which =
+		litest_test_param_get_i32(test_env->params, "switch");
 	if (libinput_device_switch_has_switch(sw->libinput_device, which) <= 0)
 		return LITEST_NOT_APPLICABLE;
 
@@ -363,7 +358,7 @@ START_TEST(switch_disable_touchpad_edge_
 
 	litest_touch_down(touchpad, 0, 99, 20);
 	litest_dispatch(li);
-	litest_timeout_edgescroll();
+	litest_timeout_edgescroll(li);
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
@@ -376,7 +371,7 @@ START_TEST(switch_disable_touchpad_edge_
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(touchpad);
+	litest_device_destroy(touchpad);
 }
 END_TEST
 
@@ -386,7 +381,8 @@ START_TEST(switch_disable_touchpad_edge_
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
 	struct libinput_event *event;
-	enum libinput_switch which = litest_test_param_get_i32(test_env->params, "switch");
+	enum libinput_switch which =
+		litest_test_param_get_i32(test_env->params, "switch");
 	if (libinput_device_switch_has_switch(sw->libinput_device, which) <= 0)
 		return LITEST_NOT_APPLICABLE;
 
@@ -397,7 +393,7 @@ START_TEST(switch_disable_touchpad_edge_
 
 	litest_touch_down(touchpad, 0, 99, 20);
 	litest_dispatch(li);
-	litest_timeout_edgescroll();
+	litest_timeout_edgescroll(li);
 	litest_touch_move_to(touchpad, 0, 99, 20, 99, 30, 10);
 	litest_dispatch(li);
 	litest_assert_only_axis_events(li, LIBINPUT_EVENT_POINTER_SCROLL_FINGER);
@@ -416,7 +412,7 @@ START_TEST(switch_disable_touchpad_edge_
 	litest_is_switch_event(event, which, LIBINPUT_SWITCH_STATE_ON);
 	libinput_event_destroy(event);
 
-	litest_delete_device(touchpad);
+	litest_device_destroy(touchpad);
 }
 END_TEST
 
@@ -425,7 +421,8 @@ START_TEST(switch_disable_touchpad_alrea
 	struct litest_device *sw = litest_current_device();
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
-	enum libinput_switch which = litest_test_param_get_i32(test_env->params, "switch");
+	enum libinput_switch which =
+		litest_test_param_get_i32(test_env->params, "switch");
 	if (libinput_device_switch_has_switch(sw->libinput_device, which) <= 0)
 		return LITEST_NOT_APPLICABLE;
 
@@ -450,7 +447,7 @@ START_TEST(switch_disable_touchpad_alrea
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(touchpad);
+	litest_device_destroy(touchpad);
 }
 END_TEST
 
@@ -459,15 +456,17 @@ START_TEST(switch_dont_resume_disabled_t
 	struct litest_device *sw = litest_current_device();
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
-	enum libinput_switch which = litest_test_param_get_i32(test_env->params, "switch");
+	enum libinput_switch which =
+		litest_test_param_get_i32(test_env->params, "switch");
 	if (libinput_device_switch_has_switch(sw->libinput_device, which) <= 0)
 		return LITEST_NOT_APPLICABLE;
 
 	touchpad = switch_init_paired_touchpad(li);
 	litest_disable_tap(touchpad->libinput_device);
 	litest_disable_hold_gestures(touchpad->libinput_device);
-	libinput_device_config_send_events_set_mode(touchpad->libinput_device,
-						    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	libinput_device_config_send_events_set_mode(
+		touchpad->libinput_device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_drain_events(li);
 
 	/* switch is on - no events */
@@ -490,7 +489,7 @@ START_TEST(switch_dont_resume_disabled_t
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(touchpad);
+	litest_device_destroy(touchpad);
 }
 END_TEST
 
@@ -499,7 +498,8 @@ START_TEST(switch_dont_resume_disabled_t
 	struct litest_device *sw = litest_current_device();
 	struct litest_device *touchpad, *mouse;
 	struct libinput *li = sw->libinput;
-	enum libinput_switch which = litest_test_param_get_i32(test_env->params, "switch");
+	enum libinput_switch which =
+		litest_test_param_get_i32(test_env->params, "switch");
 	if (libinput_device_switch_has_switch(sw->libinput_device, which) <= 0)
 		return LITEST_NOT_APPLICABLE;
 
@@ -507,8 +507,9 @@ START_TEST(switch_dont_resume_disabled_t
 	mouse = litest_add_device(li, LITEST_MOUSE);
 	litest_disable_tap(touchpad->libinput_device);
 	litest_disable_hold_gestures(touchpad->libinput_device);
-	libinput_device_config_send_events_set_mode(touchpad->libinput_device,
-						    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE);
+	libinput_device_config_send_events_set_mode(
+		touchpad->libinput_device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE);
 	litest_drain_events(li);
 
 	litest_touch_down(touchpad, 0, 50, 50);
@@ -536,8 +537,8 @@ START_TEST(switch_dont_resume_disabled_t
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(touchpad);
-	litest_delete_device(mouse);
+	litest_device_destroy(touchpad);
+	litest_device_destroy(mouse);
 }
 END_TEST
 
@@ -555,9 +556,7 @@ START_TEST(lid_open_on_key)
 
 	litest_grab_device(sw);
 	for (int i = 0; i < 3; i++) {
-		litest_switch_action(sw,
-				     LIBINPUT_SWITCH_LID,
-				     LIBINPUT_SWITCH_STATE_ON);
+		litest_switch_action(sw, LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_ON);
 		litest_drain_events(li);
 
 		litest_event(keyboard, EV_KEY, KEY_A, 1);
@@ -581,7 +580,7 @@ START_TEST(lid_open_on_key)
 	}
 	litest_ungrab_device(sw);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -600,9 +599,7 @@ START_TEST(lid_open_on_key_touchpad_enab
 	litest_disable_hold_gestures(touchpad->libinput_device);
 
 	litest_grab_device(sw);
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_LID,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_ON);
 	litest_ungrab_device(sw);
 	litest_drain_events(li);
 
@@ -611,7 +608,7 @@ START_TEST(lid_open_on_key_touchpad_enab
 	litest_event(keyboard, EV_KEY, KEY_A, 0);
 	litest_event(keyboard, EV_SYN, SYN_REPORT, 0);
 	litest_drain_events(li);
-	litest_timeout_dwt_long();
+	litest_timeout_dwt_long(li);
 
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 70, 10);
@@ -620,20 +617,21 @@ START_TEST(lid_open_on_key_touchpad_enab
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
-	litest_delete_device(touchpad);
+	litest_device_destroy(keyboard);
+	litest_device_destroy(touchpad);
 }
 END_TEST
 
 START_TEST(switch_suspend_with_keyboard)
 {
-	struct libinput *li;
 	struct litest_device *keyboard;
 	struct litest_device *sw;
-	enum libinput_switch which = litest_test_param_get_i32(test_env->params, "switch");
-	li = litest_create_context();
+	enum libinput_switch which =
+		litest_test_param_get_i32(test_env->params, "switch");
 
-	switch(which) {
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+
+	switch (which) {
 	case LIBINPUT_SWITCH_LID:
 		sw = litest_add_device(li, LITEST_LID_SWITCH);
 		break;
@@ -656,24 +654,23 @@ START_TEST(switch_suspend_with_keyboard)
 	litest_drain_events(li);
 	litest_ungrab_device(sw);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 	litest_drain_events(li);
 
-	litest_delete_device(sw);
+	litest_device_destroy(sw);
 	litest_dispatch(li);
-
-	litest_destroy_context(li);
 }
 END_TEST
 
 START_TEST(switch_suspend_with_touchpad)
 {
-	struct libinput *li;
 	struct litest_device *touchpad, *sw;
-	enum libinput_switch which = litest_test_param_get_i32(test_env->params, "switch");
-	li = litest_create_context();
+	enum libinput_switch which =
+		litest_test_param_get_i32(test_env->params, "switch");
+
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 
-	switch(which) {
+	switch (which) {
 	case LIBINPUT_SWITCH_LID:
 		sw = litest_add_device(li, LITEST_LID_SWITCH);
 		break;
@@ -696,12 +693,10 @@ START_TEST(switch_suspend_with_touchpad)
 	litest_drain_events(li);
 	litest_ungrab_device(sw);
 
-	litest_delete_device(sw);
+	litest_device_destroy(sw);
 	litest_drain_events(li);
-	litest_delete_device(touchpad);
+	litest_device_destroy(touchpad);
 	litest_drain_events(li);
-
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -710,9 +705,8 @@ START_TEST(lid_update_hw_on_key)
 	struct litest_device *sw = litest_current_device();
 	struct libinput *li = sw->libinput;
 	struct litest_device *keyboard;
-	struct libevdev *evdev;
+	_free_(libevdev) *evdev = NULL;
 	struct input_event event;
-	int fd;
 	int rc;
 
 	if (!switch_has_lid(sw))
@@ -721,15 +715,14 @@ START_TEST(lid_update_hw_on_key)
 	keyboard = litest_add_device(li, LITEST_KEYBOARD);
 
 	litest_grab_device(sw);
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_LID,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_ON);
 	litest_drain_events(li);
 	litest_ungrab_device(sw);
 
 	/* Separate direct libevdev context to check if the HW event goes
 	 * through */
-	fd = open(libevdev_uinput_get_devnode(sw->uinput), O_RDONLY|O_NONBLOCK);
+	_autoclose_ int fd =
+		open(libevdev_uinput_get_devnode(sw->uinput), O_RDONLY | O_NONBLOCK);
 	litest_assert_int_ge(fd, 0);
 	litest_assert_int_eq(libevdev_new_from_fd(fd, &evdev), 0);
 	litest_assert_int_eq(libevdev_get_event_value(evdev, EV_SW, SW_LID), 1);
@@ -754,31 +747,26 @@ START_TEST(lid_update_hw_on_key)
 	rc = libevdev_next_event(evdev, LIBEVDEV_READ_FLAG_NORMAL, &event);
 	litest_assert_int_eq(rc, -EAGAIN);
 
-	litest_delete_device(keyboard);
-	close(fd);
-	libevdev_free(evdev);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
 START_TEST(lid_update_hw_on_key_closed_on_init)
 {
 	struct litest_device *sw = litest_current_device();
-	struct libinput *li;
 	struct litest_device *keyboard;
-	struct libevdev *evdev = sw->evdev;
+	_free_(libevdev) * evdev;
 	struct input_event event;
-	int fd;
 	int rc;
 
 	litest_grab_device(sw);
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_LID,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_ON);
 	litest_ungrab_device(sw);
 
 	/* Separate direct libevdev context to check if the HW event goes
 	 * through */
-	fd = open(libevdev_uinput_get_devnode(sw->uinput), O_RDONLY|O_NONBLOCK);
+	_autoclose_ int fd =
+		open(libevdev_uinput_get_devnode(sw->uinput), O_RDONLY | O_NONBLOCK);
 	litest_assert_int_ge(fd, 0);
 	litest_assert_int_eq(libevdev_new_from_fd(fd, &evdev), 0);
 	litest_assert_int_eq(libevdev_get_event_value(evdev, EV_SW, SW_LID), 1);
@@ -786,11 +774,9 @@ START_TEST(lid_update_hw_on_key_closed_o
 	keyboard = litest_add_device(sw->libinput, LITEST_KEYBOARD);
 
 	/* separate context for the right state on init */
-	li = litest_create_context();
-	libinput_path_add_device(li,
-				 libevdev_uinput_get_devnode(sw->uinput));
-	libinput_path_add_device(li,
-				 libevdev_uinput_get_devnode(keyboard->uinput));
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	libinput_path_add_device(li, libevdev_uinput_get_devnode(sw->uinput));
+	libinput_path_add_device(li, libevdev_uinput_get_devnode(keyboard->uinput));
 
 	/* don't expect a switch waiting for us, this is run for an
 	 * unreliable device */
@@ -821,10 +807,7 @@ START_TEST(lid_update_hw_on_key_closed_o
 	rc = libevdev_next_event(evdev, LIBEVDEV_READ_FLAG_NORMAL, &event);
 	litest_assert_int_eq(rc, -EAGAIN);
 
-	litest_destroy_context(li);
-	litest_delete_device(keyboard);
-	close(fd);
-	libevdev_free(evdev);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -833,31 +816,28 @@ START_TEST(lid_update_hw_on_key_multiple
 	struct litest_device *sw = litest_current_device();
 	struct libinput *li = sw->libinput;
 	struct litest_device *keyboard1, *keyboard2;
-	struct libevdev *evdev = sw->evdev;
+	_free_(libevdev) * evdev;
 	struct input_event event;
-	int fd;
 	int rc;
 
 	if (!switch_has_lid(sw))
 		return LITEST_NOT_APPLICABLE;
 
-	keyboard1 = litest_add_device(li,
-				LITEST_KEYBOARD_BLADE_STEALTH_VIDEOSWITCH);
+	keyboard1 = litest_add_device(li, LITEST_KEYBOARD_BLADE_STEALTH_VIDEOSWITCH);
 	litest_dispatch(li);
 
 	keyboard2 = litest_add_device(li, LITEST_KEYBOARD_BLADE_STEALTH);
 	litest_dispatch(li);
 
 	litest_grab_device(sw);
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_LID,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_ON);
 	litest_drain_events(li);
 	litest_ungrab_device(sw);
 
 	/* Separate direct libevdev context to check if the HW event goes
 	 * through */
-	fd = open(libevdev_uinput_get_devnode(sw->uinput), O_RDONLY|O_NONBLOCK);
+	_autoclose_ int fd =
+		open(libevdev_uinput_get_devnode(sw->uinput), O_RDONLY | O_NONBLOCK);
 	litest_assert_int_ge(fd, 0);
 	litest_assert_int_eq(libevdev_new_from_fd(fd, &evdev), 0);
 	litest_assert_int_eq(libevdev_get_event_value(evdev, EV_SW, SW_LID), 1);
@@ -882,10 +862,8 @@ START_TEST(lid_update_hw_on_key_multiple
 	rc = libevdev_next_event(evdev, LIBEVDEV_READ_FLAG_NORMAL, &event);
 	litest_assert_int_eq(rc, -EAGAIN);
 
-	litest_delete_device(keyboard1);
-	litest_delete_device(keyboard2);
-	close(fd);
-	libevdev_free(evdev);
+	litest_device_destroy(keyboard1);
+	litest_device_destroy(keyboard2);
 }
 END_TEST
 
@@ -915,9 +893,7 @@ START_TEST(tablet_mode_disable_touchpad_
 		return LITEST_NOT_APPLICABLE;
 
 	litest_grab_device(sw);
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_TABLET_MODE,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON);
 	litest_drain_events(li);
 
 	/* touchpad comes with switch already on - no events */
@@ -942,7 +918,7 @@ START_TEST(tablet_mode_disable_touchpad_
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 	litest_ungrab_device(sw);
 
-	litest_delete_device(touchpad);
+	litest_device_destroy(touchpad);
 }
 END_TEST
 
@@ -963,9 +939,7 @@ START_TEST(tablet_mode_disable_touchpad_
 	litest_drain_events(li);
 
 	libinput_suspend(li);
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_TABLET_MODE,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON);
 	litest_drain_events(li);
 	libinput_resume(li);
 	litest_dispatch(li);
@@ -1009,9 +983,13 @@ START_TEST(tablet_mode_disable_touchpad_
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10);
 	litest_touch_up(touchpad, 0);
+	litest_dispatch(li);
+	litest_drain_events_of_type(li,
+				    LIBINPUT_EVENT_GESTURE_HOLD_BEGIN,
+				    LIBINPUT_EVENT_GESTURE_HOLD_END);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(touchpad);
+	litest_device_destroy(touchpad);
 }
 END_TEST
 
@@ -1029,9 +1007,7 @@ START_TEST(tablet_mode_enable_touchpad_o
 	litest_disable_tap(touchpad->libinput_device);
 	litest_drain_events(li);
 
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_TABLET_MODE,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON);
 	libinput_suspend(li);
 	litest_drain_events(li);
 
@@ -1052,9 +1028,7 @@ START_TEST(tablet_mode_enable_touchpad_o
 				    LIBINPUT_EVENT_GESTURE_HOLD_END);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_TABLET_MODE,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
 	litest_is_switch_event(event,
@@ -1067,7 +1041,7 @@ START_TEST(tablet_mode_enable_touchpad_o
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(touchpad);
+	litest_device_destroy(touchpad);
 }
 END_TEST
 
@@ -1090,15 +1064,17 @@ START_TEST(tablet_mode_disable_keyboard)
 
 	litest_assert_key_event(li, KEY_A, LIBINPUT_KEY_STATE_PRESSED);
 	litest_assert_key_event(li, KEY_A, LIBINPUT_KEY_STATE_RELEASED);
-	litest_assert_key_event(li, KEY_B, LIBINPUT_KEY_STATE_PRESSED); /* KEY_B down but not up */
+	litest_assert_key_event(li,
+				KEY_B,
+				LIBINPUT_KEY_STATE_PRESSED); /* KEY_B down but not up */
 
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_TABLET_MODE,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON);
 
 	/* The key currently down must be released */
 	litest_assert_key_event(li, KEY_B, LIBINPUT_KEY_STATE_RELEASED);
-	litest_assert_switch_event(li, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON);
+	litest_assert_switch_event(li,
+				   LIBINPUT_SWITCH_TABLET_MODE,
+				   LIBINPUT_SWITCH_STATE_ON);
 	litest_assert_empty_queue(li);
 
 	litest_keyboard_key(keyboard, KEY_B, false); /* release the kernel device */
@@ -1109,7 +1085,9 @@ START_TEST(tablet_mode_disable_keyboard)
 	litest_switch_action(sw,
 			     LIBINPUT_SWITCH_TABLET_MODE,
 			     LIBINPUT_SWITCH_STATE_OFF);
-	litest_assert_switch_event(li, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_OFF);
+	litest_assert_switch_event(li,
+				   LIBINPUT_SWITCH_TABLET_MODE,
+				   LIBINPUT_SWITCH_STATE_OFF);
 
 	litest_keyboard_key(keyboard, KEY_A, true);
 	litest_keyboard_key(keyboard, KEY_A, false);
@@ -1120,7 +1098,7 @@ START_TEST(tablet_mode_disable_keyboard)
 	litest_assert_key_event(li, KEY_B, LIBINPUT_KEY_STATE_PRESSED);
 	litest_assert_key_event(li, KEY_B, LIBINPUT_KEY_STATE_RELEASED);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -1133,9 +1111,7 @@ START_TEST(tablet_mode_disable_keyboard_
 	if (!switch_has_tablet_mode(sw))
 		return LITEST_NOT_APPLICABLE;
 
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_TABLET_MODE,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON);
 	litest_drain_events(li);
 
 	/* keyboard comes with switch already on - no events */
@@ -1155,7 +1131,7 @@ START_TEST(tablet_mode_disable_keyboard_
 	litest_keyboard_key(keyboard, KEY_A, false);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -1178,9 +1154,7 @@ START_TEST(tablet_mode_disable_keyboard_
 	 * fd to this device, we need an independent grab.
 	 */
 	libevdev_grab(sw->evdev, LIBEVDEV_GRAB);
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_TABLET_MODE,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON);
 	libevdev_grab(sw->evdev, LIBEVDEV_UNGRAB);
 	litest_drain_events(li);
 
@@ -1223,7 +1197,7 @@ START_TEST(tablet_mode_disable_keyboard_
 	litest_keyboard_key(keyboard, KEY_A, false);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -1238,9 +1212,7 @@ START_TEST(tablet_mode_enable_keyboard_o
 
 	keyboard = litest_add_device(li, LITEST_KEYBOARD);
 	litest_grab_device(sw);
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_TABLET_MODE,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON);
 	litest_drain_events(li);
 	litest_ungrab_device(sw);
 	libinput_suspend(li);
@@ -1258,16 +1230,14 @@ START_TEST(tablet_mode_enable_keyboard_o
 	litest_keyboard_key(keyboard, KEY_A, false);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
 
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_TABLET_MODE,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_SWITCH_TOGGLE);
 
 	litest_keyboard_key(keyboard, KEY_A, true);
 	litest_keyboard_key(keyboard, KEY_A, false);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -1290,9 +1260,7 @@ START_TEST(tablet_mode_disable_trackpoin
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_grab_device(sw);
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_TABLET_MODE,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON);
 	litest_drain_events(li);
 
 	litest_event(trackpoint, EV_REL, REL_Y, -1);
@@ -1313,7 +1281,7 @@ START_TEST(tablet_mode_disable_trackpoin
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 	litest_ungrab_device(sw);
 
-	litest_delete_device(trackpoint);
+	litest_device_destroy(trackpoint);
 }
 END_TEST
 
@@ -1327,9 +1295,7 @@ START_TEST(tablet_mode_disable_trackpoin
 		return LITEST_NOT_APPLICABLE;
 
 	litest_grab_device(sw);
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_TABLET_MODE,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON);
 	litest_drain_events(li);
 
 	/* trackpoint comes with switch already on - no events */
@@ -1354,7 +1320,7 @@ START_TEST(tablet_mode_disable_trackpoin
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 	litest_ungrab_device(sw);
 
-	litest_delete_device(trackpoint);
+	litest_device_destroy(trackpoint);
 }
 END_TEST
 
@@ -1382,6 +1348,7 @@ END_TEST
 
 TEST_COLLECTION(switch)
 {
+	/* clang-format off */
 	litest_add(switch_has_cap, LITEST_SWITCH, LITEST_ANY);
 	litest_add(switch_has_lid_switch, LITEST_SWITCH, LITEST_ANY);
 	litest_add(switch_has_tablet_mode_switch, LITEST_SWITCH, LITEST_ANY);
@@ -1422,4 +1389,5 @@ TEST_COLLECTION(switch)
 	litest_add(tablet_mode_disable_trackpoint_on_init, LITEST_SWITCH, LITEST_ANY);
 
 	litest_add(dock_toggle, LITEST_SWITCH, LITEST_ANY);
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-tablet.c 1.30.0-1/test/test-tablet.c
--- 1.28.1-1/test/test-tablet.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-tablet.c	2025-11-25 03:40:43.000000000 +0000
@@ -27,18 +27,19 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <libinput.h>
-#include <unistd.h>
-#include <stdbool.h>
 #include <stdarg.h>
+#include <stdbool.h>
+#include <unistd.h>
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 #include <libwacom/libwacom.h>
 #endif
 
-#include "libinput-util.h"
+#include "util-input-event.h"
+
 #include "evdev-tablet.h"
+#include "libinput-util.h"
 #include "litest.h"
-#include "util-input-event.h"
 
 enum {
 	TILT_MINIMUM,
@@ -55,7 +56,7 @@ pick_stylus_or_btn0(struct litest_device
 	if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_0))
 		return BTN_0; /* totem */
 
-	abort();
+	litest_abort_msg("Device has neither BTN_STYLUS nor BTN_0");
 }
 
 START_TEST(button_down_up)
@@ -67,7 +68,7 @@ START_TEST(button_down_up)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	unsigned int button = pick_stylus_or_btn0(dev);
 
@@ -79,10 +80,9 @@ START_TEST(button_down_up)
 
 	event = libinput_get_event(li);
 	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
-	litest_assert_int_eq(libinput_event_tablet_tool_get_button(tev),
-			 button);
+	litest_assert_int_eq(libinput_event_tablet_tool_get_button(tev), button);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_button_state(tev),
-			 LIBINPUT_BUTTON_STATE_PRESSED);
+			      LIBINPUT_BUTTON_STATE_PRESSED);
 	libinput_event_destroy(event);
 	litest_assert_empty_queue(li);
 
@@ -91,10 +91,9 @@ START_TEST(button_down_up)
 
 	event = libinput_get_event(li);
 	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
-	litest_assert_int_eq(libinput_event_tablet_tool_get_button(tev),
-			 button);
+	litest_assert_int_eq(libinput_event_tablet_tool_get_button(tev), button);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_button_state(tev),
-			 LIBINPUT_BUTTON_STATE_RELEASED);
+			      LIBINPUT_BUTTON_STATE_RELEASED);
 	libinput_event_destroy(event);
 	litest_assert_empty_queue(li);
 }
@@ -110,7 +109,7 @@ START_TEST(button_seat_count)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	unsigned int button = pick_stylus_or_btn0(dev);
 
@@ -137,7 +136,7 @@ START_TEST(button_seat_count)
 	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 	litest_assert_int_eq(libinput_event_tablet_tool_get_button(tev), button);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_button_state(tev),
-			 LIBINPUT_BUTTON_STATE_PRESSED);
+			      LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_int_eq(libinput_event_tablet_tool_get_seat_button_count(tev), 1U);
 	libinput_event_destroy(event);
 
@@ -145,7 +144,7 @@ START_TEST(button_seat_count)
 	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 	litest_assert_int_eq(libinput_event_tablet_tool_get_button(tev), button);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_button_state(tev),
-			 LIBINPUT_BUTTON_STATE_PRESSED);
+			      LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_int_eq(libinput_event_tablet_tool_get_seat_button_count(tev), 2U);
 	libinput_event_destroy(event);
 
@@ -158,7 +157,7 @@ START_TEST(button_seat_count)
 	event = libinput_get_event(li);
 	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_button_state(tev),
-			 LIBINPUT_BUTTON_STATE_RELEASED);
+			      LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_int_eq(libinput_event_tablet_tool_get_button(tev), button);
 	litest_assert_int_eq(libinput_event_tablet_tool_get_seat_button_count(tev), 1U);
 	libinput_event_destroy(event);
@@ -166,19 +165,19 @@ START_TEST(button_seat_count)
 	event = libinput_get_event(li);
 	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_button_state(tev),
-			 LIBINPUT_BUTTON_STATE_RELEASED);
+			      LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_int_eq(libinput_event_tablet_tool_get_button(tev), button);
 	litest_assert_int_eq(libinput_event_tablet_tool_get_seat_button_count(tev), 0U);
 	libinput_event_destroy(event);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(dev2);
+	litest_device_destroy(dev2);
 }
 END_TEST
 
 START_TEST(button_up_on_delete)
 {
-	struct libinput *li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	struct litest_device *dev = litest_add_device(li, LITEST_WACOM_INTUOS5_PEN);
 	struct libevdev *evdev = libevdev_new();
 	unsigned int code;
@@ -197,7 +196,7 @@ START_TEST(button_up_on_delete)
 	}
 
 	litest_drain_events(li);
-	litest_delete_device(dev);
+	litest_device_destroy(dev);
 	litest_dispatch(li);
 
 	for (code = BTN_LEFT; code <= BTN_TASK; code++) {
@@ -205,14 +204,13 @@ START_TEST(button_up_on_delete)
 			continue;
 
 		litest_assert_tablet_button_event(li,
-					  code,
-					  LIBINPUT_BUTTON_STATE_RELEASED);
+						  code,
+						  LIBINPUT_BUTTON_STATE_RELEASED);
 	}
 
 	litest_assert_tablet_proximity_event(li,
 					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	libevdev_free(evdev);
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -225,7 +223,7 @@ START_TEST(tip_down_up)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_tablet_proximity_in(dev, 10, 10, axes);
@@ -238,10 +236,9 @@ START_TEST(tip_down_up)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_DOWN);
+			      LIBINPUT_TABLET_TOOL_TIP_DOWN);
 	libinput_event_destroy(event);
 	litest_assert_empty_queue(li);
 
@@ -251,14 +248,12 @@ START_TEST(tip_down_up)
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
-
 }
 END_TEST
 
@@ -272,7 +267,7 @@ START_TEST(tip_down_up_eraser)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	if (!libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_RUBBER))
@@ -290,12 +285,12 @@ START_TEST(tip_down_up_eraser)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_DOWN);
+			      LIBINPUT_TABLET_TOOL_TIP_DOWN);
 	tool = libinput_event_tablet_tool_get_tool(tablet_event);
-	litest_assert_enum_eq(libinput_tablet_tool_get_type(tool), LIBINPUT_TABLET_TOOL_TYPE_ERASER);
+	litest_assert_enum_eq(libinput_tablet_tool_get_type(tool),
+			      LIBINPUT_TABLET_TOOL_TYPE_ERASER);
 	libinput_event_destroy(event);
 	litest_assert_empty_queue(li);
 
@@ -305,16 +300,15 @@ START_TEST(tip_down_up_eraser)
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	tool = libinput_event_tablet_tool_get_tool(tablet_event);
-	litest_assert_enum_eq(libinput_tablet_tool_get_type(tool), LIBINPUT_TABLET_TOOL_TYPE_ERASER);
+	litest_assert_enum_eq(libinput_tablet_tool_get_type(tool),
+			      LIBINPUT_TABLET_TOOL_TYPE_ERASER);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
-
 }
 END_TEST
 
@@ -327,33 +321,32 @@ START_TEST(tip_down_prox_in)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 0 },
 		{ ABS_PRESSURE, 30 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_drain_events(li);
 
-	litest_push_event_frame(dev);
-	litest_tablet_proximity_in(dev, 10, 10, axes);
-	litest_tablet_tip_down(dev, 10, 10, axes);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_tablet_proximity_in(dev, 10, 10, axes);
+		litest_tablet_tip_down(dev, 10, 10, axes);
+	}
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
-	litest_assert_enum_eq(libinput_event_tablet_tool_get_proximity_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	litest_assert_enum_eq(
+		libinput_event_tablet_tool_get_proximity_state(tablet_event),
+		LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
 	libinput_event_destroy(event);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_DOWN);
+			      LIBINPUT_TABLET_TOOL_TIP_DOWN);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
-
 }
 END_TEST
 
@@ -366,7 +359,7 @@ START_TEST(tip_up_prox_out)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 0 },
 		{ ABS_PRESSURE, 30 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_tablet_proximity_in(dev, 10, 10, axes);
@@ -374,31 +367,30 @@ START_TEST(tip_up_prox_out)
 	litest_drain_events(li);
 
 	litest_axis_set_value(axes, ABS_DISTANCE, 30);
+
 	litest_axis_set_value(axes, ABS_PRESSURE, 0);
-	litest_push_event_frame(dev);
-	litest_tablet_tip_up(dev, 10, 10, axes);
-	litest_tablet_proximity_out(dev);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_tablet_tip_up(dev, 10, 10, axes);
+		litest_tablet_proximity_out(dev);
+	}
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	libinput_event_destroy(event);
 
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
-	litest_assert_enum_eq(libinput_event_tablet_tool_get_proximity_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	litest_assert_enum_eq(
+		libinput_event_tablet_tool_get_proximity_state(tablet_event),
+		LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
-
 }
 END_TEST
 
@@ -411,7 +403,7 @@ START_TEST(tip_up_btn_change)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 0 },
 		{ ABS_PRESSURE, 30 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_tablet_proximity_in(dev, 10, 10, axes);
@@ -420,27 +412,25 @@ START_TEST(tip_up_btn_change)
 
 	litest_axis_set_value(axes, ABS_DISTANCE, 30);
 	litest_axis_set_value(axes, ABS_PRESSURE, 0);
-	litest_push_event_frame(dev);
-	litest_tablet_tip_up(dev, 10, 20, axes);
-	litest_event(dev, EV_KEY, BTN_STYLUS, 1);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_tablet_tip_up(dev, 10, 20, axes);
+		litest_event(dev, EV_KEY, BTN_STYLUS, 1);
+	}
 
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	libinput_event_destroy(event);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 	litest_assert_int_eq(libinput_event_tablet_tool_get_button(tablet_event),
-			 (unsigned int)BTN_STYLUS);
+			     (unsigned int)BTN_STYLUS);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_button_state(tablet_event),
-			 LIBINPUT_BUTTON_STATE_PRESSED);
+			      LIBINPUT_BUTTON_STATE_PRESSED);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
@@ -453,27 +443,25 @@ START_TEST(tip_up_btn_change)
 	/* same thing with a release at tip-up */
 	litest_axis_set_value(axes, ABS_DISTANCE, 30);
 	litest_axis_set_value(axes, ABS_PRESSURE, 0);
-	litest_push_event_frame(dev);
-	litest_tablet_tip_up(dev, 10, 10, axes);
-	litest_event(dev, EV_KEY, BTN_STYLUS, 0);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_tablet_tip_up(dev, 10, 10, axes);
+		litest_event(dev, EV_KEY, BTN_STYLUS, 0);
+	}
 
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	libinput_event_destroy(event);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 	litest_assert_int_eq(libinput_event_tablet_tool_get_button(tablet_event),
-			 (unsigned int)BTN_STYLUS);
+			     (unsigned int)BTN_STYLUS);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_button_state(tablet_event),
-			 LIBINPUT_BUTTON_STATE_RELEASED);
+			      LIBINPUT_BUTTON_STATE_RELEASED);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
@@ -489,7 +477,7 @@ START_TEST(tip_down_btn_change)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_tablet_proximity_in(dev, 10, 10, axes);
@@ -497,28 +485,26 @@ START_TEST(tip_down_btn_change)
 
 	litest_axis_set_value(axes, ABS_DISTANCE, 0);
 	litest_axis_set_value(axes, ABS_PRESSURE, 30);
-	litest_push_event_frame(dev);
-	litest_tablet_tip_down(dev, 10, 20, axes);
-	litest_event(dev, EV_KEY, BTN_STYLUS, 1);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_tablet_tip_down(dev, 10, 20, axes);
+		litest_event(dev, EV_KEY, BTN_STYLUS, 1);
+	}
 
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_DOWN);
+			      LIBINPUT_TABLET_TOOL_TIP_DOWN);
 	libinput_event_destroy(event);
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 	litest_assert_int_eq(libinput_event_tablet_tool_get_button(tablet_event),
 			     (unsigned int)BTN_STYLUS);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_button_state(tablet_event),
-			 LIBINPUT_BUTTON_STATE_PRESSED);
+			      LIBINPUT_BUTTON_STATE_PRESSED);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
@@ -531,28 +517,26 @@ START_TEST(tip_down_btn_change)
 	/* same thing with a release at tip-down */
 	litest_axis_set_value(axes, ABS_DISTANCE, 0);
 	litest_axis_set_value(axes, ABS_PRESSURE, 30);
-	litest_push_event_frame(dev);
-	litest_tablet_tip_down(dev, 10, 20, axes);
-	litest_event(dev, EV_KEY, BTN_STYLUS, 0);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_tablet_tip_down(dev, 10, 20, axes);
+		litest_event(dev, EV_KEY, BTN_STYLUS, 0);
+	}
 
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_DOWN);
+			      LIBINPUT_TABLET_TOOL_TIP_DOWN);
 	libinput_event_destroy(event);
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 	litest_assert_int_eq(libinput_event_tablet_tool_get_button(tablet_event),
 			     (unsigned int)BTN_STYLUS);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_button_state(tablet_event),
-			 LIBINPUT_BUTTON_STATE_RELEASED);
+			      LIBINPUT_BUTTON_STATE_RELEASED);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
@@ -568,7 +552,7 @@ START_TEST(tip_down_motion)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	double x, y, last_x, last_y;
 
@@ -577,8 +561,8 @@ START_TEST(tip_down_motion)
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	last_x = libinput_event_tablet_tool_get_x(tablet_event);
 	last_y = libinput_event_tablet_tool_get_y(tablet_event);
 	libinput_event_destroy(event);
@@ -590,10 +574,9 @@ START_TEST(tip_down_motion)
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_DOWN);
+			      LIBINPUT_TABLET_TOOL_TIP_DOWN);
 	litest_assert(libinput_event_tablet_tool_x_has_changed(tablet_event));
 	litest_assert(libinput_event_tablet_tool_y_has_changed(tablet_event));
 	x = libinput_event_tablet_tool_get_x(tablet_event);
@@ -615,7 +598,7 @@ START_TEST(tip_up_motion)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 0 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	double x, y, last_x, last_y;
 
@@ -627,8 +610,7 @@ START_TEST(tip_up_motion)
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	last_x = libinput_event_tablet_tool_get_x(tablet_event);
 	last_y = libinput_event_tablet_tool_get_y(tablet_event);
 	libinput_event_destroy(event);
@@ -639,10 +621,9 @@ START_TEST(tip_up_motion)
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	litest_assert(libinput_event_tablet_tool_x_has_changed(tablet_event));
 	litest_assert(libinput_event_tablet_tool_y_has_changed(tablet_event));
 	x = libinput_event_tablet_tool_get_x(tablet_event);
@@ -664,11 +645,10 @@ START_TEST(tip_up_motion_one_axis)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 0 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	double x, y, last_x, last_y;
-	double start_x = 20,
-	       start_y = 20;
+	double start_x = 20, start_y = 20;
 	int axis = litest_test_param_get_i32(test_env->params, "axis");
 
 	switch (axis) {
@@ -681,7 +661,7 @@ START_TEST(tip_up_motion_one_axis)
 		start_y = 15;
 		break;
 	default:
-		abort();
+		litest_assert_not_reached();
 	}
 
 	/* generate enough events to fill the history and move alonge the
@@ -699,15 +679,13 @@ START_TEST(tip_up_motion_one_axis)
 			start_y++;
 			break;
 		}
-
 	}
 	litest_drain_events(li);
 
 	litest_tablet_motion(dev, 20, 20, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	last_x = libinput_event_tablet_tool_get_x(tablet_event);
 	last_y = libinput_event_tablet_tool_get_y(tablet_event);
 	libinput_event_destroy(event);
@@ -725,14 +703,13 @@ START_TEST(tip_up_motion_one_axis)
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	x = libinput_event_tablet_tool_get_x(tablet_event);
 	y = libinput_event_tablet_tool_get_y(tablet_event);
 
-	switch(axis) {
+	switch (axis) {
 	case ABS_X:
 		litest_assert(libinput_event_tablet_tool_x_has_changed(tablet_event));
 		litest_assert(!libinput_event_tablet_tool_y_has_changed(tablet_event));
@@ -762,7 +739,7 @@ START_TEST(tip_state_proximity)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_drain_events(li);
@@ -771,10 +748,10 @@ START_TEST(tip_state_proximity)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	libinput_event_destroy(event);
 
 	litest_axis_set_value(axes, ABS_PRESSURE, 30);
@@ -788,16 +765,13 @@ START_TEST(tip_state_proximity)
 	litest_drain_events(li);
 
 	litest_tablet_proximity_out(dev);
-	litest_dispatch(li);
-
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	libinput_event_destroy(event);
 }
 END_TEST
@@ -811,7 +785,7 @@ START_TEST(tip_state_axis)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_tablet_proximity_in(dev, 10, 10, axes);
@@ -821,10 +795,9 @@ START_TEST(tip_state_axis)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	libinput_event_destroy(event);
 
 	litest_axis_set_value(axes, ABS_PRESSURE, 30);
@@ -836,10 +809,9 @@ START_TEST(tip_state_axis)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_DOWN);
+			      LIBINPUT_TABLET_TOOL_TIP_DOWN);
 	libinput_event_destroy(event);
 
 	litest_axis_set_value(axes, ABS_PRESSURE, 0);
@@ -851,10 +823,9 @@ START_TEST(tip_state_axis)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
@@ -870,7 +841,7 @@ START_TEST(tip_state_button)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	unsigned int button = pick_stylus_or_btn0(dev);
 
@@ -881,10 +852,9 @@ START_TEST(tip_state_button)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	libinput_event_destroy(event);
 
 	litest_axis_set_value(axes, ABS_PRESSURE, 30);
@@ -896,10 +866,9 @@ START_TEST(tip_state_button)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_DOWN);
+			      LIBINPUT_TABLET_TOOL_TIP_DOWN);
 	libinput_event_destroy(event);
 
 	litest_axis_set_value(axes, ABS_PRESSURE, 0);
@@ -911,20 +880,18 @@ START_TEST(tip_state_button)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	libinput_event_destroy(event);
 
 	litest_button_click(dev, button, false);
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
@@ -933,14 +900,14 @@ END_TEST
 
 START_TEST(tip_up_on_delete)
 {
-	struct libinput *li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	struct litest_device *dev = litest_add_device(li, LITEST_WACOM_INTUOS5_PEN);
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tablet_event;
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_tablet_proximity_in(dev, 10, 10, axes);
@@ -951,17 +918,14 @@ START_TEST(tip_up_on_delete)
 	litest_tablet_tip_down(dev, 10, 10, axes);
 
 	litest_drain_events(li);
-	litest_delete_device(dev);
+	litest_device_destroy(dev);
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(tablet_event),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	libinput_event_destroy(event);
-
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -972,13 +936,12 @@ START_TEST(proximity_in_out)
 	struct libinput_event_tablet_tool *tablet_event;
 	struct libinput_event *event;
 	enum libinput_tablet_tool_type type;
-	bool have_tool_update = false,
-	     have_proximity_out = false;
+	bool have_tool_update = false, have_proximity_out = false;
 
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_drain_events(li);
@@ -998,23 +961,21 @@ START_TEST(proximity_in_out)
 	while ((event = libinput_get_event(li))) {
 		if (libinput_event_get_type(event) ==
 		    LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY) {
-			struct libinput_tablet_tool * tool;
+			struct libinput_tablet_tool *tool;
 
 			litest_assert(!have_tool_update);
 			have_tool_update = true;
 			tablet_event = libinput_event_get_tablet_tool_event(event);
 			tool = libinput_event_tablet_tool_get_tool(tablet_event);
-			litest_assert_enum_eq(libinput_tablet_tool_get_type(tool), type);
+			litest_assert_enum_eq(libinput_tablet_tool_get_type(tool),
+					      type);
 		}
 		libinput_event_destroy(event);
 	}
 	litest_assert(have_tool_update);
 
 	litest_tablet_proximity_out(dev);
-	litest_dispatch(li);
-
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	while ((event = libinput_get_event(li))) {
 		if (libinput_event_get_type(event) ==
@@ -1043,24 +1004,22 @@ START_TEST(proximity_in_button_down)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	unsigned int button = pick_stylus_or_btn0(dev);
 
 	litest_drain_events(li);
 
-	litest_push_event_frame(dev);
-	litest_tablet_proximity_in(dev, 10, 10, axes);
-	litest_event(dev, EV_KEY, button, 1);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_tablet_proximity_in(dev, 10, 10, axes);
+		litest_event(dev, EV_KEY, button, 1);
+	}
 	litest_dispatch(li);
 
 	litest_assert_tablet_proximity_event(li,
 					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
 	litest_drain_events_of_type(li, LIBINPUT_EVENT_TABLET_TOOL_TIP);
-	litest_assert_tablet_button_event(li,
-					  button,
-					  LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_tablet_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -1072,7 +1031,7 @@ START_TEST(proximity_out_button_up)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	unsigned int button = pick_stylus_or_btn0(dev);
 
@@ -1081,18 +1040,13 @@ START_TEST(proximity_out_button_up)
 	litest_button_click(dev, button, true);
 	litest_drain_events(li);
 
-	litest_push_event_frame(dev);
-	litest_tablet_proximity_out(dev);
-	litest_event(dev, EV_KEY, button, 0);
-	litest_pop_event_frame(dev);
-	litest_dispatch(li);
-
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_with_event_frame(dev) {
+		litest_tablet_proximity_out(dev);
+		litest_event(dev, EV_KEY, button, 0);
+	}
+	litest_timeout_tablet_proxout(li);
 
-	litest_assert_tablet_button_event(li,
-					  button,
-					  LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_tablet_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_drain_events_of_type(li, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_tablet_proximity_event(li,
 					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
@@ -1109,9 +1063,9 @@ START_TEST(proximity_out_clear_buttons)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
-	uint32_t stylus_buttons[] = {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3};
+	uint32_t stylus_buttons[] = { BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3 };
 	bool have_proximity = false;
 	double x = 50, y = 50;
 
@@ -1134,7 +1088,7 @@ START_TEST(proximity_out_clear_buttons)
 		litest_event(dev, EV_KEY, *button, 1);
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
 		litest_tablet_proximity_out(dev);
-		litest_dispatch(li);
+		litest_timeout_tablet_proxout(li);
 
 		event = libinput_get_event(li);
 		litest_assert_notnull(event);
@@ -1151,8 +1105,10 @@ START_TEST(proximity_out_clear_buttons)
 			if (libinput_event_get_type(event) ==
 			    LIBINPUT_EVENT_TABLET_TOOL_BUTTON) {
 
-				event_button = libinput_event_tablet_tool_get_button(tablet_event);
-				state = libinput_event_tablet_tool_get_button_state(tablet_event);
+				event_button = libinput_event_tablet_tool_get_button(
+					tablet_event);
+				state = libinput_event_tablet_tool_get_button_state(
+					tablet_event);
 
 				if (event_button == *button &&
 				    state == LIBINPUT_BUTTON_STATE_RELEASED)
@@ -1163,9 +1119,9 @@ START_TEST(proximity_out_clear_buttons)
 		} while ((event = libinput_get_event(li)));
 
 		litest_assert_msg(button_released,
-			      "Button %s (%d) was not released.",
-			      libevdev_event_code_get_name(EV_KEY, *button),
-			      event_button);
+				  "Button %s (%d) was not released.",
+				  libevdev_event_code_get_name(EV_KEY, *button),
+				  event_button);
 		litest_assert(have_proximity);
 		litest_assert_empty_queue(li);
 	}
@@ -1179,19 +1135,14 @@ START_TEST(proximity_has_axes)
 	struct libinput_event_tablet_tool *tablet_event;
 	struct libinput_event *event;
 	struct libinput_tablet_tool *tool;
-	double x, y,
-	       distance;
-	double last_x, last_y,
-	       last_distance = 0.0,
-	       last_tx = 0.0, last_ty = 0.0;
+	double x, y, distance;
+	double last_x, last_y, last_distance = 0.0, last_tx = 0.0, last_ty = 0.0;
 
-	struct axis_replacement axes[] = {
-		{ ABS_DISTANCE, 10 },
-		{ ABS_PRESSURE, 0 },
-		{ ABS_TILT_X, 10 },
-		{ ABS_TILT_Y, 10 },
-		{ -1, -1}
-	};
+	struct axis_replacement axes[] = { { ABS_DISTANCE, 10 },
+					   { ABS_PRESSURE, 0 },
+					   { ABS_TILT_X, 10 },
+					   { ABS_TILT_Y, 10 },
+					   { -1, -1 } };
 
 	litest_drain_events(li);
 
@@ -1199,8 +1150,8 @@ START_TEST(proximity_has_axes)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tablet_event);
 
 	litest_assert(libinput_event_tablet_tool_x_has_changed(tablet_event));
@@ -1213,18 +1164,18 @@ START_TEST(proximity_has_axes)
 	litest_assert_double_ne(y, 0);
 
 	if (libinput_tablet_tool_has_distance(tool)) {
-		litest_assert(libinput_event_tablet_tool_distance_has_changed(
-				tablet_event));
+		litest_assert(
+			libinput_event_tablet_tool_distance_has_changed(tablet_event));
 
 		distance = libinput_event_tablet_tool_get_distance(tablet_event);
 		litest_assert_double_ne(distance, 0);
 	}
 
 	if (libinput_tablet_tool_has_tilt(tool)) {
-		litest_assert(libinput_event_tablet_tool_tilt_x_has_changed(
-				tablet_event));
-		litest_assert(libinput_event_tablet_tool_tilt_y_has_changed(
-				tablet_event));
+		litest_assert(
+			libinput_event_tablet_tool_tilt_x_has_changed(tablet_event));
+		litest_assert(
+			libinput_event_tablet_tool_tilt_y_has_changed(tablet_event));
 
 		x = libinput_event_tablet_tool_get_tilt_x(tablet_event);
 		y = libinput_event_tablet_tool_get_tilt_y(tablet_event);
@@ -1256,8 +1207,7 @@ START_TEST(proximity_has_axes)
 	last_x = libinput_event_tablet_tool_get_x(tablet_event);
 	last_y = libinput_event_tablet_tool_get_y(tablet_event);
 	if (libinput_tablet_tool_has_distance(tool))
-		last_distance = libinput_event_tablet_tool_get_distance(
-					     tablet_event);
+		last_distance = libinput_event_tablet_tool_get_distance(tablet_event);
 	if (libinput_tablet_tool_has_tilt(tool)) {
 		last_tx = libinput_event_tablet_tool_get_tilt_x(tablet_event);
 		last_ty = libinput_event_tablet_tool_get_tilt_y(tablet_event);
@@ -1267,16 +1217,13 @@ START_TEST(proximity_has_axes)
 
 	/* Make sure that the axes are still present on proximity out */
 	litest_tablet_proximity_out(dev);
-	litest_dispatch(li);
-
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	litest_drain_events_of_type(li, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tablet_event);
 
 	litest_assert(!libinput_event_tablet_tool_x_has_changed(tablet_event));
@@ -1290,19 +1237,18 @@ START_TEST(proximity_has_axes)
 	litest_assert_double_le(y, last_y + 1);
 
 	if (libinput_tablet_tool_has_distance(tool)) {
-		litest_assert(!libinput_event_tablet_tool_distance_has_changed(
-				tablet_event));
+		litest_assert(
+			!libinput_event_tablet_tool_distance_has_changed(tablet_event));
 
-		distance = libinput_event_tablet_tool_get_distance(
-						tablet_event);
+		distance = libinput_event_tablet_tool_get_distance(tablet_event);
 		litest_assert_double_eq(distance, last_distance);
 	}
 
 	if (libinput_tablet_tool_has_tilt(tool)) {
-		litest_assert(!libinput_event_tablet_tool_tilt_x_has_changed(
-				tablet_event));
-		litest_assert(!libinput_event_tablet_tool_tilt_y_has_changed(
-				tablet_event));
+		litest_assert(
+			!libinput_event_tablet_tool_tilt_x_has_changed(tablet_event));
+		litest_assert(
+			!libinput_event_tablet_tool_tilt_y_has_changed(tablet_event));
 
 		x = libinput_event_tablet_tool_get_tilt_x(tablet_event);
 		y = libinput_event_tablet_tool_get_tilt_y(tablet_event);
@@ -1322,17 +1268,17 @@ START_TEST(proximity_range_enter)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 90 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_drain_events(li);
 
-	litest_push_event_frame(dev);
-	litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_tablet_proximity_in(dev, 10, 10, axes);
-	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
-	litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
+		litest_tablet_proximity_in(dev, 10, 10, axes);
+		litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
+		litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
+	}
 	litest_assert_empty_queue(li);
 
 	litest_axis_set_value(axes, ABS_DISTANCE, 20);
@@ -1348,12 +1294,12 @@ START_TEST(proximity_range_enter)
 	litest_assert_tablet_proximity_event(li,
 					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 
-	litest_push_event_frame(dev);
-	litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_tablet_proximity_out(dev);
-	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 0);
-	litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
+		litest_tablet_proximity_out(dev);
+		litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 0);
+		litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
+	}
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -1364,17 +1310,17 @@ START_TEST(proximity_range_in_out)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 20 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_drain_events(li);
 
-	litest_push_event_frame(dev);
-	litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_tablet_proximity_in(dev, 10, 10, axes);
-	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
-	litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
+		litest_tablet_proximity_in(dev, 10, 10, axes);
+		litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
+		litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
+	}
 	litest_dispatch(li);
 	litest_assert_tablet_proximity_event(li,
 					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
@@ -1394,12 +1340,13 @@ START_TEST(proximity_range_in_out)
 	litest_assert_tablet_proximity_event(li,
 					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
 
-	litest_push_event_frame(dev);
-	litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_tablet_proximity_out(dev);
-	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 0);
-	litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
+		litest_tablet_proximity_out(dev);
+		litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 0);
+		litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
+	}
+	litest_timeout_tablet_proxout(li);
 	litest_assert_tablet_proximity_event(li,
 					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	litest_assert_empty_queue(li);
@@ -1412,17 +1359,17 @@ START_TEST(proximity_range_button_click)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 90 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_drain_events(li);
 
-	litest_push_event_frame(dev);
-	litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_tablet_proximity_in(dev, 10, 10, axes);
-	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
-	litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
+		litest_tablet_proximity_in(dev, 10, 10, axes);
+		litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
+		litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
+	}
 	litest_drain_events(li);
 
 	litest_event(dev, EV_KEY, BTN_STYLUS, 1);
@@ -1432,12 +1379,13 @@ START_TEST(proximity_range_button_click)
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 
-	litest_push_event_frame(dev);
-	litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_tablet_proximity_out(dev);
-	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 0);
-	litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
+		litest_tablet_proximity_out(dev);
+		litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 0);
+		litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
+	}
+	litest_timeout_tablet_proxout(li);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -1448,15 +1396,15 @@ START_TEST(proximity_range_button_press)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 20 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
-	litest_push_event_frame(dev);
-	litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_tablet_proximity_in(dev, 10, 10, axes);
-	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
-	litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
+		litest_tablet_proximity_in(dev, 10, 10, axes);
+		litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
+		litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
+	}
 	litest_drain_events(li);
 
 	litest_event(dev, EV_KEY, BTN_STYLUS, 1);
@@ -1482,12 +1430,13 @@ START_TEST(proximity_range_button_press)
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 
-	litest_push_event_frame(dev);
-	litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_tablet_proximity_out(dev);
-	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 0);
-	litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
+		litest_tablet_proximity_out(dev);
+		litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 0);
+		litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
+	}
+	litest_timeout_tablet_proxout(li);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -1498,15 +1447,15 @@ START_TEST(proximity_range_button_releas
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 90 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
-	litest_push_event_frame(dev);
-	litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_tablet_proximity_in(dev, 10, 10, axes);
-	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
-	litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
+		litest_tablet_proximity_in(dev, 10, 10, axes);
+		litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
+		litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
+	}
 	litest_drain_events(li);
 
 	litest_event(dev, EV_KEY, BTN_STYLUS, 1);
@@ -1532,12 +1481,13 @@ START_TEST(proximity_range_button_releas
 					  BTN_STYLUS,
 					  LIBINPUT_BUTTON_STATE_RELEASED);
 
-	litest_push_event_frame(dev);
-	litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_tablet_proximity_out(dev);
-	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 0);
-	litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
+		litest_tablet_proximity_out(dev);
+		litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 0);
+		litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
+	}
+	litest_timeout_tablet_proxout(li);
 	litest_assert_tablet_proximity_event(li,
 					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 }
@@ -1549,15 +1499,14 @@ START_TEST(proximity_out_slow_event)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 90 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_tablet_motion(dev, 12, 12, axes);
 	litest_drain_events(li);
 
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	/* The forced prox out */
 	litest_assert_tablet_proximity_event(li,
@@ -1576,25 +1525,25 @@ START_TEST(proximity_out_not_during_cont
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 0 },
 		{ ABS_PRESSURE, 10 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_tablet_proximity_in(dev, 10, 10, axes);
+	litest_tablet_tip_down(dev, 10, 10, axes);
 	litest_tablet_motion(dev, 12, 12, axes);
 	litest_drain_events(li);
 
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	/* No forced proxout yet */
 	litest_assert_empty_queue(li);
 
 	litest_axis_set_value(axes, ABS_PRESSURE, 0);
 	litest_tablet_motion(dev, 14, 14, axes);
+	litest_tablet_tip_up(dev, 14, 14, axes);
 	litest_drain_events(li);
 
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	/* The forced prox out */
 	litest_assert_tablet_proximity_event(li,
@@ -1612,7 +1561,7 @@ START_TEST(proximity_out_not_during_butt
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_tablet_proximity_in(dev, 10, 10, axes);
@@ -1625,8 +1574,7 @@ START_TEST(proximity_out_not_during_butt
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	/* No forced proxout yet */
 	litest_assert_empty_queue(li);
@@ -1637,8 +1585,7 @@ START_TEST(proximity_out_not_during_butt
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	/* The forced prox out */
 	litest_assert_tablet_proximity_event(li,
@@ -1656,7 +1603,7 @@ START_TEST(proximity_out_disables_forced
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	/* A correct proximity out sequence from the device should disable
@@ -1669,8 +1616,7 @@ START_TEST(proximity_out_disables_forced
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_drain_events(li);
 
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	litest_assert_empty_queue(li);
 	litest_tablet_proximity_out(dev);
@@ -1687,7 +1633,7 @@ START_TEST(proximity_out_disables_forced
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	/* A correct proximity out sequence from the device should disable
@@ -1696,8 +1642,7 @@ START_TEST(proximity_out_disables_forced
 	litest_drain_events(li);
 
 	/* timeout-based forced prox out */
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 	litest_assert_tablet_proximity_event(li,
 					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	litest_assert_empty_queue(li);
@@ -1712,8 +1657,7 @@ START_TEST(proximity_out_disables_forced
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_drain_events(li);
 
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	litest_assert_empty_queue(li);
 	litest_tablet_proximity_out(dev);
@@ -1723,20 +1667,104 @@ START_TEST(proximity_out_disables_forced
 }
 END_TEST
 
+START_TEST(proximity_forced_in_with_eraser_swap)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_DISTANCE, 10 },
+		{ ABS_PRESSURE, 0 },
+		{ -1, -1 },
+	};
+
+	/* Test doesn't work with a double-tool device */
+	if (dev->which == LITEST_ELAN_TABLET)
+		return LITEST_NOT_APPLICABLE;
+
+	if (!libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_RUBBER))
+		return LITEST_NOT_APPLICABLE;
+
+	/* https://gitlab.freedesktop.org/libinput/libinput/-/issues/1171:
+	 * Device sends correct proximity for pen and eraser so both prox timer
+	 * and double-tool are unregistered. Then it sends a single ABS_X event
+	 * before an eraser proximity in - ensure that our forced proximity
+	 * out is immediately removed.
+	 */
+
+	/* A correct proximity out sequence from the device should disable
+	   the forced proximity out, even when we had a forced prox-out */
+	litest_tablet_proximity_in(dev, 10, 10, axes);
+	litest_tablet_proximity_out(dev);
+	litest_drain_events(li);
+	litest_timeout_tablet_proxout(li);
+
+	/* correct eraser prox out disables forced tool plugin */
+	litest_tablet_set_tool_type(dev, BTN_TOOL_RUBBER);
+	litest_tablet_proximity_in(dev, 12, 12, axes);
+	litest_tablet_proximity_out(dev);
+	litest_drain_events(li);
+	litest_timeout_tablet_proxout(li);
+
+	litest_dispatch(li);
+
+	litest_log_group("Expecting ABS_X to trigger a proximity event") {
+		/* All our tablets have this within their range so hardcoding 100 should
+		 * be fine here. If this breaks, scale it to absinfo */
+		auto abs = libevdev_get_abs_info(dev->evdev, ABS_X);
+		int x = absinfo_range(abs) / 3 + abs->minimum;
+		litest_event(dev, EV_ABS, ABS_X, x);
+		litest_event(dev, EV_SYN, SYN_REPORT, 0);
+
+		litest_dispatch(li);
+
+		_destroy_(libinput_event) *pen_in = libinput_get_event(li);
+		auto tev = litest_is_proximity_event(
+			pen_in,
+			LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+		auto tool = libinput_event_tablet_tool_get_tool(tev);
+		litest_assert_enum_eq(libinput_tablet_tool_get_type(tool),
+				      LIBINPUT_TABLET_TOOL_TYPE_PEN);
+	}
+
+	litest_log_group(
+		"Expecting eraser prox in to trigger prox out for pen and prox in for eraser") {
+		litest_tablet_proximity_in(dev, 15, 15, axes);
+		litest_dispatch(li);
+
+		_destroy_(libinput_event) *pen_out = libinput_get_event(li);
+		auto tev = litest_is_proximity_event(
+			pen_out,
+			LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+		auto tool = libinput_event_tablet_tool_get_tool(tev);
+		litest_assert_enum_eq(libinput_tablet_tool_get_type(tool),
+				      LIBINPUT_TABLET_TOOL_TYPE_PEN);
+
+		_destroy_(libinput_event) *eraser_in = libinput_get_event(li);
+		tev = litest_is_proximity_event(
+			eraser_in,
+			LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+		tool = libinput_event_tablet_tool_get_tool(tev);
+		litest_assert_enum_eq(libinput_tablet_tool_get_type(tool),
+				      LIBINPUT_TABLET_TOOL_TYPE_ERASER);
+	}
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
 START_TEST(proximity_out_on_delete)
 {
-	struct libinput *li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	struct litest_device *dev = litest_add_device(li, LITEST_WACOM_INTUOS5_PEN);
 
 	litest_tablet_proximity_in(dev, 10, 10, NULL);
 	litest_drain_events(li);
 
-	litest_delete_device(dev);
+	litest_device_destroy(dev);
 	litest_dispatch(li);
 
 	litest_assert_tablet_proximity_event(li,
 					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -1752,7 +1780,7 @@ START_TEST(motion)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	bool x_changed, y_changed;
 	double reported_x, reported_y;
@@ -1763,8 +1791,8 @@ START_TEST(motion)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-				      LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	x_changed = libinput_event_tablet_tool_x_has_changed(tablet_event);
 	y_changed = libinput_event_tablet_tool_y_has_changed(tablet_event);
 	litest_assert(x_changed);
@@ -1780,9 +1808,7 @@ START_TEST(motion)
 
 	libinput_event_destroy(event);
 
-	for (test_x = 10, test_y = 90;
-	     test_x <= 100;
-	     test_x += 10, test_y -= 10) {
+	for (test_x = 10, test_y = 90; test_x <= 100; test_x += 10, test_y -= 10) {
 		bool x_changed, y_changed;
 		double reported_x, reported_y;
 
@@ -1795,22 +1821,20 @@ START_TEST(motion)
 
 			if (type == LIBINPUT_EVENT_TABLET_TOOL_AXIS) {
 				x_changed = libinput_event_tablet_tool_x_has_changed(
-							    tablet_event);
+					tablet_event);
 				y_changed = libinput_event_tablet_tool_y_has_changed(
-							    tablet_event);
+					tablet_event);
 
 				litest_assert(x_changed);
 				litest_assert(y_changed);
 
-				reported_x = libinput_event_tablet_tool_get_x(
-								tablet_event);
-				reported_y = libinput_event_tablet_tool_get_y(
-								tablet_event);
-
-				litest_assert_double_gt(reported_x,
-							last_reported_x);
-				litest_assert_double_lt(reported_y,
-							last_reported_y);
+				reported_x =
+					libinput_event_tablet_tool_get_x(tablet_event);
+				reported_y =
+					libinput_event_tablet_tool_get_y(tablet_event);
+
+				litest_assert_double_gt(reported_x, last_reported_x);
+				litest_assert_double_lt(reported_y, last_reported_y);
 
 				last_reported_x = reported_x;
 				last_reported_y = reported_y;
@@ -1824,7 +1848,7 @@ END_TEST
 
 START_TEST(left_handed)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
@@ -1835,16 +1859,17 @@ START_TEST(left_handed)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_drain_events(li);
 
-	litest_assert(libinput_device_config_left_handed_is_available(dev->libinput_device));
+	litest_assert(
+		libinput_device_config_left_handed_is_available(dev->libinput_device));
 
-	libinput_device_get_size (dev->libinput_device,
-				  &libinput_max_x,
-				  &libinput_max_y);
+	libinput_device_get_size(dev->libinput_device,
+				 &libinput_max_x,
+				 &libinput_max_y);
 
 	/* Test that left-handed mode doesn't go into effect until the tool has
 	 * left proximity of the tablet. In order to test this, we have to bring
@@ -1856,8 +1881,8 @@ START_TEST(left_handed)
 	libinput_device_config_left_handed_set(dev->libinput_device, 1);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-				LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
 	last_x = libinput_event_tablet_tool_get_x(tablet_event);
 	last_y = libinput_event_tablet_tool_get_y(tablet_event);
@@ -1881,8 +1906,7 @@ START_TEST(left_handed)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 
 	x = libinput_event_tablet_tool_get_x(tablet_event);
 	y = libinput_event_tablet_tool_get_y(tablet_event);
@@ -1897,6 +1921,7 @@ START_TEST(left_handed)
 
 	litest_tablet_proximity_out(dev);
 	litest_drain_events(li);
+	litest_timeout_tablet_proxout(li);
 
 	/* Since we've drained the events and libinput's aware the tool is out
 	 * of proximity, it should have finally transitioned into left-handed
@@ -1906,8 +1931,8 @@ START_TEST(left_handed)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-				LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
 	last_x = libinput_event_tablet_tool_get_x(tablet_event);
 	last_y = libinput_event_tablet_tool_get_y(tablet_event);
@@ -1931,8 +1956,7 @@ START_TEST(left_handed)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-				LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 
 	x = libinput_event_tablet_tool_get_x(tablet_event);
 	y = libinput_event_tablet_tool_get_y(tablet_event);
@@ -1953,28 +1977,27 @@ START_TEST(no_left_handed)
 	struct litest_device *dev = litest_current_device();
 
 	/* Without libwacom we default to left-handed being available */
-#if HAVE_LIBWACOM
-	litest_assert(!libinput_device_config_left_handed_is_available(dev->libinput_device));
+#ifdef HAVE_LIBWACOM
+	litest_assert(
+		!libinput_device_config_left_handed_is_available(dev->libinput_device));
 #else
-	litest_assert(libinput_device_config_left_handed_is_available(dev->libinput_device));
+	litest_assert(
+		libinput_device_config_left_handed_is_available(dev->libinput_device));
 #endif
 }
 END_TEST
 
 START_TEST(left_handed_tilt)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tev;
 	enum libinput_config_status status;
 	struct axis_replacement axes[] = {
-		{ ABS_DISTANCE, 10 },
-		{ ABS_PRESSURE, 0 },
-		{ ABS_TILT_X, 90 },
-		{ ABS_TILT_Y, 10 },
-		{ -1, -1 }
+		{ ABS_DISTANCE, 10 }, { ABS_PRESSURE, 0 }, { ABS_TILT_X, 90 },
+		{ ABS_TILT_Y, 10 },   { -1, -1 },
 	};
 	double tx, ty;
 
@@ -1986,8 +2009,7 @@ START_TEST(left_handed_tilt)
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tx = libinput_event_tablet_tool_get_tilt_x(tev);
 	ty = libinput_event_tablet_tool_get_tilt_y(tev);
 
@@ -2006,7 +2028,7 @@ rotate_event(struct litest_device *dev,
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tev;
 	const struct input_absinfo *abs;
-	double a = (angle_degrees - 90 - 175)/180.0 * M_PI;
+	double a = (angle_degrees - 90 - 175) / 180.0 * M_PI;
 	double val;
 	int x, y;
 	int tilt_center_x, tilt_center_y;
@@ -2028,8 +2050,7 @@ rotate_event(struct litest_device *dev,
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	litest_assert(libinput_event_tablet_tool_rotation_has_changed(tev));
 	val = libinput_event_tablet_tool_get_rotation(tev);
 
@@ -2041,18 +2062,15 @@ rotate_event(struct litest_device *dev,
 
 START_TEST(left_handed_mouse_rotation)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	enum libinput_config_status status;
 	int angle;
 	double val, old_val = 0;
 	struct axis_replacement axes[] = {
-		{ ABS_DISTANCE, 10 },
-		{ ABS_PRESSURE, 0 },
-		{ ABS_TILT_X, 0 },
-		{ ABS_TILT_Y, 0 },
-		{ -1, -1 }
+		{ ABS_DISTANCE, 10 }, { ABS_PRESSURE, 0 }, { ABS_TILT_X, 0 },
+		{ ABS_TILT_Y, 0 },    { -1, -1 },
 	};
 
 	status = libinput_device_config_left_handed_set(dev->libinput_device, 1);
@@ -2060,12 +2078,12 @@ START_TEST(left_handed_mouse_rotation)
 
 	litest_drain_events(li);
 
-	litest_push_event_frame(dev);
-	litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_tablet_proximity_in(dev, 10, 10, axes);
-	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
-	litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
+		litest_tablet_proximity_in(dev, 10, 10, axes);
+		litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
+		litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
+	}
 
 	litest_drain_events(li);
 
@@ -2090,7 +2108,7 @@ END_TEST
 
 START_TEST(left_handed_artpen_rotation)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
@@ -2101,9 +2119,7 @@ START_TEST(left_handed_artpen_rotation)
 	double scale;
 	int angle;
 
-	if (!libevdev_has_event_code(dev->evdev,
-				    EV_ABS,
-				    ABS_Z))
+	if (!libevdev_has_event_code(dev->evdev, EV_ABS, ABS_Z))
 		return LITEST_NOT_APPLICABLE;
 
 	status = libinput_device_config_left_handed_set(dev->libinput_device, 1);
@@ -2113,7 +2129,7 @@ START_TEST(left_handed_artpen_rotation)
 
 	abs = libevdev_get_abs_info(dev->evdev, ABS_Z);
 	litest_assert_notnull(abs);
-	scale = absinfo_range(abs)/360.0;
+	scale = absinfo_range(abs) / 360.0;
 
 	litest_event(dev, EV_KEY, BTN_TOOL_BRUSH, 1);
 	litest_event(dev, EV_ABS, ABS_MISC, 0x804); /* Art Pen */
@@ -2133,8 +2149,7 @@ START_TEST(left_handed_artpen_rotation)
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
 		litest_dispatch(li);
 		event = libinput_get_event(li);
-		tev = litest_is_tablet_event(event,
-					     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+		tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 		litest_assert(libinput_event_tablet_tool_rotation_has_changed(tev));
 		val = libinput_event_tablet_tool_get_rotation(tev);
 
@@ -2143,7 +2158,6 @@ START_TEST(left_handed_artpen_rotation)
 
 		libinput_event_destroy(event);
 		litest_assert_empty_queue(li);
-
 	}
 #endif
 }
@@ -2160,7 +2174,7 @@ START_TEST(motion_event_state)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	unsigned int button = pick_stylus_or_btn0(dev);
 
@@ -2189,7 +2203,7 @@ START_TEST(motion_event_state)
 	libinput_event_destroy(event);
 	litest_dispatch(li);
 	litest_assert_enum_eq(libinput_next_event_type(li),
-			 LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+			      LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 
 	/* we expect all events up to the button event to go from
 	   bottom/left to top/right */
@@ -2214,7 +2228,7 @@ START_TEST(motion_event_state)
 	}
 
 	litest_assert_enum_eq(libinput_event_get_type(event),
-			 LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
+			      LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 	libinput_event_destroy(event);
 }
 END_TEST
@@ -2231,7 +2245,7 @@ START_TEST(motion_outside_bounds)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_tablet_proximity_in(dev, 50, 50, axes);
@@ -2253,8 +2267,7 @@ START_TEST(motion_outside_bounds)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	val = libinput_event_tablet_tool_get_x(tablet_event);
 	litest_assert_double_lt(val, 0.0);
 	val = libinput_event_tablet_tool_get_y(tablet_event);
@@ -2281,8 +2294,7 @@ START_TEST(motion_outside_bounds)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	val = libinput_event_tablet_tool_get_x(tablet_event);
 	litest_assert_double_gt(val, 0.0);
 	val = libinput_event_tablet_tool_get_y(tablet_event);
@@ -2306,6 +2318,7 @@ START_TEST(bad_distance_events)
 
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
 	litest_drain_events(li);
 
 	absinfo = libevdev_get_abs_info(dev->evdev, ABS_DISTANCE);
@@ -2336,8 +2349,8 @@ START_TEST(tool_unique)
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-				LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tablet_event);
 	litest_assert(libinput_tablet_tool_is_unique(tool));
 	libinput_event_destroy(event);
@@ -2360,8 +2373,8 @@ START_TEST(tool_serial)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-				LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tablet_event);
 	litest_assert_int_eq(libinput_tablet_tool_get_serial(tool), (uint64_t)1000);
 	libinput_event_destroy(event);
@@ -2383,8 +2396,8 @@ START_TEST(tool_id)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-				LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tablet_event);
 
 	litest_assert_int_eq(libinput_device_get_id_vendor(dev->libinput_device),
@@ -2394,8 +2407,8 @@ START_TEST(tool_id)
 	case 0x27: /* Intuos 5 */
 		tool_id = 1050626;
 		break;
-	case 0xc6: /* Cintiq 12WX */
-	case 0xf4: /* Cintiq 24HD */
+	case 0xc6:  /* Cintiq 12WX */
+	case 0xf4:  /* Cintiq 24HD */
 	case 0x333: /* Cintiq 13HD */
 	case 0x350: /* Cintiq Pro 16 */
 		tool_id = 2083;
@@ -2432,8 +2445,8 @@ START_TEST(serial_changes_tool)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-				LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tablet_event);
 
 	litest_assert_int_eq(libinput_tablet_tool_get_serial(tool), (uint64_t)2000);
@@ -2469,7 +2482,8 @@ START_TEST(invalid_serials)
 			tablet_event = libinput_event_get_tablet_tool_event(event);
 			tool = libinput_event_tablet_tool_get_tool(tablet_event);
 
-			litest_assert_int_eq(libinput_tablet_tool_get_serial(tool), (uint64_t)1000);
+			litest_assert_int_eq(libinput_tablet_tool_get_serial(tool),
+					     (uint64_t)1000);
 		}
 
 		libinput_event_destroy(event);
@@ -2493,8 +2507,8 @@ START_TEST(tool_ref)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-				LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tablet_event);
 
 	litest_assert_notnull(tool);
@@ -2521,8 +2535,8 @@ START_TEST(tool_user_data)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-				LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tablet_event);
 	litest_assert_notnull(tool);
 
@@ -2543,7 +2557,7 @@ START_TEST(pad_buttons_ignored)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	int button;
 
@@ -2581,9 +2595,9 @@ END_TEST
 
 START_TEST(tools_with_serials)
 {
-	struct libinput *li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	struct litest_device *dev[2];
-	struct libinput_tablet_tool *tool[2] = {0};
+	struct libinput_tablet_tool *tool[2] = { 0 };
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tev;
 	int i;
@@ -2597,14 +2611,15 @@ START_TEST(tools_with_serials)
 		 * Put a sleep(1) here and that usually fixes it.
 		 */
 
-		litest_push_event_frame(dev[i]);
-		litest_tablet_proximity_in(dev[i], 10, 10, NULL);
-		litest_event(dev[i], EV_MSC, MSC_SERIAL, 100);
-		litest_pop_event_frame(dev[i]);
+		litest_with_event_frame(dev[i]) {
+			litest_tablet_proximity_in(dev[i], 10, 10, NULL);
+			litest_event(dev[i], EV_MSC, MSC_SERIAL, 100);
+		}
 
 		litest_dispatch(li);
 		event = libinput_get_event(li);
-		tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+		tev = litest_is_tablet_event(event,
+					     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 		tool[i] = libinput_event_tablet_tool_get_tool(tev);
 		libinput_event_destroy(event);
 	}
@@ -2614,17 +2629,16 @@ START_TEST(tools_with_serials)
 	litest_assert_notnull(tool[1]);
 	litest_assert_ptr_eq(tool[0], tool[1]);
 
-	litest_delete_device(dev[0]);
-	litest_delete_device(dev[1]);
-	litest_destroy_context(li);
+	litest_device_destroy(dev[0]);
+	litest_device_destroy(dev[1]);
 }
 END_TEST
 
 START_TEST(tools_without_serials)
 {
-	struct libinput *li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	struct litest_device *dev[2];
-	struct libinput_tablet_tool *tool[2] = {0};
+	struct libinput_tablet_tool *tool[2] = { 0 };
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tev;
 	int i;
@@ -2648,7 +2662,8 @@ START_TEST(tools_without_serials)
 
 		litest_dispatch(li);
 		event = libinput_get_event(li);
-		tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+		tev = litest_is_tablet_event(event,
+					     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 		tool[i] = libinput_event_tablet_tool_get_tool(tev);
 		libinput_event_destroy(event);
 	}
@@ -2658,9 +2673,8 @@ START_TEST(tools_without_serials)
 	litest_assert_notnull(tool[1]);
 	litest_assert_ptr_ne(tool[0], tool[1]);
 
-	litest_delete_device(dev[0]);
-	litest_delete_device(dev[1]);
-	litest_destroy_context(li);
+	litest_device_destroy(dev[0]);
+	litest_device_destroy(dev[1]);
 }
 END_TEST
 
@@ -2683,8 +2697,7 @@ START_TEST(tool_delayed_serial)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tev);
 	serial = libinput_tablet_tool_get_serial(tool);
 	litest_assert_int_eq(serial, (uint64_t)0);
@@ -2707,8 +2720,7 @@ START_TEST(tool_delayed_serial)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	tool = libinput_event_tablet_tool_get_tool(tev);
 	serial = libinput_tablet_tool_get_serial(tool);
 	litest_assert_int_eq(serial, (uint64_t)0);
@@ -2724,8 +2736,7 @@ START_TEST(tool_delayed_serial)
 
 	event = libinput_get_event(li);
 	do {
-		tev = litest_is_tablet_event(event,
-					     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+		tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 		tool = libinput_event_tablet_tool_get_tool(tev);
 		serial = libinput_tablet_tool_get_serial(tool);
 		litest_assert_int_eq(serial, (uint64_t)0);
@@ -2742,8 +2753,7 @@ START_TEST(tool_delayed_serial)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tev);
 	serial = libinput_tablet_tool_get_serial(tool);
 	litest_assert_int_eq(serial, (uint64_t)0);
@@ -2757,13 +2767,13 @@ START_TEST(tool_capability)
 	struct libinput_device *device = dev->libinput_device;
 
 	litest_assert(libinput_device_has_capability(device,
-						 LIBINPUT_DEVICE_CAP_TABLET_TOOL));
+						     LIBINPUT_DEVICE_CAP_TABLET_TOOL));
 }
 END_TEST
 
 START_TEST(tool_capabilities)
 {
-	struct libinput *li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	struct litest_device *intuos;
 	struct litest_device *bamboo;
 	struct libinput_event *event;
@@ -2807,9 +2817,8 @@ START_TEST(tool_capabilities)
 	libinput_event_destroy(event);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(bamboo);
-	litest_delete_device(intuos);
-	litest_destroy_context(li);
+	litest_device_destroy(bamboo);
+	litest_device_destroy(intuos);
 }
 END_TEST
 
@@ -2828,11 +2837,8 @@ START_TEST(tool_type)
 	struct libinput_event_tablet_tool *t;
 	struct libinput_tablet_tool *tool;
 	struct axis_replacement axes[] = {
-		{ ABS_DISTANCE, 10 },
-		{ ABS_PRESSURE, 0 },
-		{ ABS_TILT_X, 0 },
-		{ ABS_TILT_Y, 0 },
-		{ -1, -1 }
+		{ ABS_DISTANCE, 10 }, { ABS_PRESSURE, 0 }, { ABS_TILT_X, 0 },
+		{ ABS_TILT_Y, 0 },    { -1, -1 },
 	};
 	struct tool_type_match {
 		int code;
@@ -2845,7 +2851,7 @@ START_TEST(tool_type)
 		{ BTN_TOOL_AIRBRUSH, LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH },
 		{ BTN_TOOL_MOUSE, LIBINPUT_TABLET_TOOL_TYPE_MOUSE },
 		{ BTN_TOOL_LENS, LIBINPUT_TABLET_TOOL_TYPE_LENS },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	struct tool_type_match *tt;
 	double x = 50, y = 50;
@@ -2855,9 +2861,7 @@ START_TEST(tool_type)
 	for (tt = types; tt->code != -1; tt++) {
 		enum libinput_tablet_tool_type type;
 
-		if (!libevdev_has_event_code(dev->evdev,
-					     EV_KEY,
-					     tt->code))
+		if (!libevdev_has_event_code(dev->evdev, EV_KEY, tt->code))
 			continue;
 
 		if ((tt->code == BTN_TOOL_MOUSE || tt->code == BTN_TOOL_LENS) &&
@@ -2869,8 +2873,7 @@ START_TEST(tool_type)
 		litest_dispatch(li);
 
 		event = libinput_get_event(li);
-		t = litest_is_tablet_event(event,
-				   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+		t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 		tool = libinput_event_tablet_tool_get_tool(t);
 		type = libinput_tablet_tool_get_type(tool);
 
@@ -2886,8 +2889,9 @@ START_TEST(tool_type)
 					       LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 			libinput_event_destroy(event);
 			event = libinput_get_event(li);
-			t = litest_is_tablet_event(event,
-					   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+			t = litest_is_tablet_event(
+				event,
+				LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 			tool = libinput_event_tablet_tool_get_tool(t);
 			type = libinput_tablet_tool_get_type(tool);
 		}
@@ -2898,6 +2902,7 @@ START_TEST(tool_type)
 		litest_assert_empty_queue(li);
 
 		litest_tablet_proximity_out(dev);
+		litest_timeout_tablet_proxout(li);
 		litest_drain_events(li);
 
 		x++;
@@ -2908,17 +2913,13 @@ END_TEST
 
 START_TEST(tool_in_prox_before_start)
 {
-	struct libinput *li;
 	struct litest_device *dev = litest_current_device();
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tev;
 	struct libinput_tablet_tool *tool;
 	struct axis_replacement axes[] = {
-		{ ABS_DISTANCE, 10 },
-		{ ABS_PRESSURE, 0 },
-		{ ABS_TILT_X, 0 },
-		{ ABS_TILT_Y, 0 },
-		{ -1, -1 }
+		{ ABS_DISTANCE, 10 }, { ABS_PRESSURE, 0 }, { ABS_TILT_X, 0 },
+		{ ABS_TILT_Y, 0 },    { -1, -1 },
 	};
 	const char *devnode;
 	uint64_t serial;
@@ -2927,7 +2928,7 @@ START_TEST(tool_in_prox_before_start)
 
 	/* for simplicity, we create a new litest context */
 	devnode = libevdev_uinput_get_devnode(dev->uinput);
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	libinput_path_add_device(li, devnode);
 
 	litest_drain_events_of_type(li, LIBINPUT_EVENT_DEVICE_ADDED);
@@ -2937,8 +2938,7 @@ START_TEST(tool_in_prox_before_start)
 	litest_tablet_motion(dev, 10, 20, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tev);
 	serial = libinput_tablet_tool_get_serial(tool);
 	libinput_event_destroy(event);
@@ -2948,11 +2948,9 @@ START_TEST(tool_in_prox_before_start)
 	litest_tablet_motion(dev, 30, 40, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	tool = libinput_event_tablet_tool_get_tool(tev);
-	litest_assert_int_eq(serial,
-			 libinput_tablet_tool_get_serial(tool));
+	litest_assert_int_eq(serial, libinput_tablet_tool_get_serial(tool));
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
@@ -2960,16 +2958,11 @@ START_TEST(tool_in_prox_before_start)
 	litest_button_click(dev, BTN_STYLUS, false);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
 	litest_tablet_proximity_out(dev);
-	litest_dispatch(li);
-
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	event = libinput_get_event(li);
 	litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	libinput_event_destroy(event);
-
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -2979,11 +2972,11 @@ START_TEST(tool_direct_switch_skip_tool_
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tev;
-	struct libinput_tablet_tool *tool;
+	struct libinput_tablet_tool *pen = NULL, *eraser = NULL;
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	if (!libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_RUBBER))
@@ -2995,103 +2988,87 @@ START_TEST(tool_direct_switch_skip_tool_
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
-	tool = libinput_event_tablet_tool_get_tool(tev);
-	libinput_tablet_tool_ref(tool);
+	tev = litest_is_proximity_event(event, LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+	pen = libinput_event_tablet_tool_get_tool(tev);
+	libinput_tablet_tool_ref(pen);
 	libinput_event_destroy(event);
 
+	litest_checkpoint("Switching directly to eraser");
 	litest_event(dev, EV_KEY, BTN_TOOL_RUBBER, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 
+	litest_checkpoint("Expecting pen prox out followed by eraser prox in ");
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
-	litest_assert_enum_eq(libinput_event_tablet_tool_get_proximity_state(tev),
-			 LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
-	litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev), tool);
+	tev = litest_is_proximity_event(event,
+					LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+	litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev), pen);
 	libinput_event_destroy(event);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
-	litest_assert_enum_eq(libinput_event_tablet_tool_get_proximity_state(tev),
-			 LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
-	litest_assert_ptr_ne(libinput_event_tablet_tool_get_tool(tev), tool);
-	libinput_tablet_tool_unref(tool);
-	tool = libinput_event_tablet_tool_get_tool(tev);
-	libinput_tablet_tool_ref(tool);
+	tev = litest_is_proximity_event(event, LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+	litest_assert_ptr_ne(libinput_event_tablet_tool_get_tool(tev), pen);
+	eraser = libinput_event_tablet_tool_get_tool(tev);
+	libinput_tablet_tool_ref(eraser);
 	libinput_event_destroy(event);
 
 	litest_tablet_motion(dev, 20, 30, axes);
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
-	litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev),
-			 tool);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev), eraser);
 	libinput_event_destroy(event);
 
+	litest_checkpoint("Switching directly to pen, expecting eraser prox out");
 	litest_event(dev, EV_KEY, BTN_TOOL_RUBBER, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
-	litest_assert_enum_eq(libinput_event_tablet_tool_get_proximity_state(tev),
-			 LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
-	litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev),
-			 tool);
+	tev = litest_is_proximity_event(event,
+					LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+	litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev), eraser);
 	libinput_event_destroy(event);
 
-	litest_push_event_frame(dev);
-	litest_event(dev, EV_KEY, BTN_TOOL_RUBBER, 1);
-	litest_tablet_motion(dev, 30, 40, axes);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_checkpoint("Prox in for eraser (pen still in prox)");
+		litest_event(dev, EV_KEY, BTN_TOOL_RUBBER, 1);
+		litest_tablet_motion(dev, 30, 40, axes);
+	}
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
-	litest_assert_enum_eq(libinput_event_tablet_tool_get_proximity_state(tev),
-			 LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
-	litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev),
-			 tool);
+	tev = litest_is_proximity_event(event, LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+	litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev), eraser);
 	libinput_event_destroy(event);
 
 	litest_tablet_motion(dev, 40, 30, axes);
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
-	litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev),
-			 tool);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev), eraser);
 	libinput_event_destroy(event);
 
-	litest_push_event_frame(dev);
-	litest_event(dev, EV_KEY, BTN_TOOL_RUBBER, 0);
-	litest_tablet_proximity_out(dev);
-	litest_pop_event_frame(dev);
-	litest_dispatch(li);
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_with_event_frame(dev) {
+		litest_event(dev, EV_KEY, BTN_TOOL_RUBBER, 0);
+		litest_tablet_proximity_out(dev);
+	}
+	litest_timeout_tablet_proxout(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
-	litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev),
-			 tool);
+	tev = litest_is_proximity_event(event,
+					LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+	litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev), eraser);
 	libinput_event_destroy(event);
 
 	litest_event(dev, EV_KEY, BTN_TOOL_RUBBER, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_assert_empty_queue(li);
 
-	libinput_tablet_tool_unref(tool);
+	libinput_tablet_tool_unref(eraser);
+	libinput_tablet_tool_unref(pen);
 }
 END_TEST
 
@@ -3099,11 +3076,10 @@ START_TEST(tool_direct_switch_with_force
 {
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
-	struct libinput_event *event;
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	if (!libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_RUBBER))
@@ -3121,53 +3097,42 @@ START_TEST(tool_direct_switch_with_force
 	/* pen prox in */
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_dispatch(li);
-	event = libinput_get_event(li);
-	litest_is_proximity_event(event,
-				  LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
-	libinput_event_destroy(event);
+	litest_assert_tablet_proximity_event(li,
+					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
 
 	/* pen motion */
 	litest_tablet_motion(dev, 20, 30, axes);
 	litest_dispatch(li);
 
-	event = libinput_get_event(li);
-	litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
-	libinput_event_destroy(event);
+	litest_assert_tablet_axis_event(li);
 
+	litest_checkpoint("Forcing a timeout prox-out");
 	/* pen forced prox out */
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
+	litest_checkpoint("Actual prox-out");
 	/* actual prox out for tablets that don't do forced prox out */
 	litest_tablet_proximity_out(dev);
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
-	event = libinput_get_event(li);
-	litest_is_proximity_event(event,
-				  LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
-	libinput_event_destroy(event);
+	litest_assert_tablet_proximity_event(li,
+					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+	litest_assert_empty_queue(li);
 
 	/* eraser prox in without axes */
 	litest_event(dev, EV_KEY, BTN_TOOL_RUBBER, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
-	event = libinput_get_event(li);
-	litest_is_proximity_event(event,
-				  LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
-	libinput_event_destroy(event);
+	litest_assert_tablet_proximity_event(li,
+					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
 
 	/* eraser motion */
 	litest_tablet_motion(dev, 30, 40, axes);
 	litest_tablet_motion(dev, 40, 50, axes);
 	litest_dispatch(li);
 
-	event = libinput_get_event(li);
-	litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
-	libinput_event_destroy(event);
-
-	event = libinput_get_event(li);
-	litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
-	libinput_event_destroy(event);
+	litest_assert_tablet_axis_event(li);
+	litest_assert_tablet_axis_event(li);
 
 	litest_assert_empty_queue(li);
 }
@@ -3180,7 +3145,7 @@ START_TEST(stylus_buttons)
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tev;
 	struct libinput_tablet_tool *tool;
-	uint32_t stylus_buttons[] = {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3};
+	uint32_t stylus_buttons[] = { BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3 };
 
 	litest_drain_events(li);
 
@@ -3191,8 +3156,7 @@ START_TEST(stylus_buttons)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tev);
 	litest_assert_notnull(tool);
 	libinput_tablet_tool_ref(tool);
@@ -3208,11 +3172,11 @@ START_TEST(stylus_buttons)
 		litest_dispatch(li);
 
 		litest_assert_tablet_button_event(li,
-					  *code,
-					  LIBINPUT_BUTTON_STATE_PRESSED);
+						  *code,
+						  LIBINPUT_BUTTON_STATE_PRESSED);
 		litest_assert_tablet_button_event(li,
-					  *code,
-					  LIBINPUT_BUTTON_STATE_RELEASED);
+						  *code,
+						  LIBINPUT_BUTTON_STATE_RELEASED);
 	}
 
 	libinput_tablet_tool_unref(tool);
@@ -3235,12 +3199,11 @@ START_TEST(mouse_tool)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tev);
 	litest_assert_notnull(tool);
 	litest_assert_enum_eq(libinput_tablet_tool_get_type(tool),
-			 LIBINPUT_TABLET_TOOL_TYPE_MOUSE);
+			      LIBINPUT_TABLET_TOOL_TYPE_MOUSE);
 
 	libinput_event_destroy(event);
 }
@@ -3264,8 +3227,7 @@ START_TEST(mouse_buttons)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tev);
 	litest_assert_notnull(tool);
 	libinput_tablet_tool_ref(tool);
@@ -3273,11 +3235,9 @@ START_TEST(mouse_buttons)
 	libinput_event_destroy(event);
 
 	for (code = BTN_LEFT; code <= BTN_TASK; code++) {
-		bool has_button = libevdev_has_event_code(dev->evdev,
-							  EV_KEY,
-							  code);
+		bool has_button = libevdev_has_event_code(dev->evdev, EV_KEY, code);
 		litest_assert_int_eq(!!has_button,
-				 !!libinput_tablet_tool_has_button(tool, code));
+				     !!libinput_tablet_tool_has_button(tool, code));
 
 		if (!has_button)
 			continue;
@@ -3290,11 +3250,11 @@ START_TEST(mouse_buttons)
 		litest_dispatch(li);
 
 		litest_assert_tablet_button_event(li,
-					  code,
-					  LIBINPUT_BUTTON_STATE_PRESSED);
+						  code,
+						  LIBINPUT_BUTTON_STATE_PRESSED);
 		litest_assert_tablet_button_event(li,
-					  code,
-					  LIBINPUT_BUTTON_STATE_RELEASED);
+						  code,
+						  LIBINPUT_BUTTON_STATE_RELEASED);
 	}
 
 	libinput_tablet_tool_unref(tool);
@@ -3309,21 +3269,18 @@ START_TEST(mouse_rotation)
 	double val, old_val = 0;
 
 	struct axis_replacement axes[] = {
-		{ ABS_DISTANCE, 10 },
-		{ ABS_PRESSURE, 0 },
-		{ ABS_TILT_X, 0 },
-		{ ABS_TILT_Y, 0 },
-		{ -1, -1 }
+		{ ABS_DISTANCE, 10 }, { ABS_PRESSURE, 0 }, { ABS_TILT_X, 0 },
+		{ ABS_TILT_Y, 0 },    { -1, -1 },
 	};
 
 	litest_drain_events(li);
 
-	litest_push_event_frame(dev);
-	litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_tablet_proximity_in(dev, 10, 10, axes);
-	litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
-	litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_filter_event(dev, EV_KEY, BTN_TOOL_PEN);
+		litest_tablet_proximity_in(dev, 10, 10, axes);
+		litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1);
+		litest_unfilter_event(dev, EV_KEY, BTN_TOOL_PEN);
+	}
 
 	litest_drain_events(li);
 
@@ -3354,9 +3311,7 @@ START_TEST(mouse_wheel)
 	double val;
 	int i;
 
-	if (!libevdev_has_event_code(dev->evdev,
-				     EV_REL,
-				     REL_WHEEL))
+	if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_drain_events(li);
@@ -3368,8 +3323,7 @@ START_TEST(mouse_wheel)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tev);
 	litest_assert_notnull(tool);
 	libinput_tablet_tool_ref(tool);
@@ -3384,8 +3338,7 @@ START_TEST(mouse_wheel)
 		litest_dispatch(li);
 
 		event = libinput_get_event(li);
-		tev = litest_is_tablet_event(event,
-					     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+		tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 		litest_assert(libinput_event_tablet_tool_wheel_has_changed(tev));
 
 		val = libinput_event_tablet_tool_get_wheel_delta(tev);
@@ -3402,15 +3355,14 @@ START_TEST(mouse_wheel)
 	for (i = 2; i < 5; i++) {
 		/* send  x/y events to make sure we reset the wheel */
 		abs = libevdev_get_abs_info(dev->evdev, ABS_X);
-		litest_event(dev, EV_ABS, ABS_X, absinfo_range(abs)/i);
+		litest_event(dev, EV_ABS, ABS_X, absinfo_range(abs) / i);
 		abs = libevdev_get_abs_info(dev->evdev, ABS_Y);
-		litest_event(dev, EV_ABS, ABS_Y, absinfo_range(abs)/i);
+		litest_event(dev, EV_ABS, ABS_Y, absinfo_range(abs) / i);
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
 		litest_dispatch(li);
 
 		event = libinput_get_event(li);
-		tev = litest_is_tablet_event(event,
-					     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+		tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 		litest_assert(!libinput_event_tablet_tool_wheel_has_changed(tev));
 
 		val = libinput_event_tablet_tool_get_wheel_delta(tev);
@@ -3436,9 +3388,7 @@ START_TEST(airbrush_tool)
 	struct libinput_event_tablet_tool *tev;
 	struct libinput_tablet_tool *tool;
 
-	if (!libevdev_has_event_code(dev->evdev,
-				    EV_KEY,
-				    BTN_TOOL_AIRBRUSH))
+	if (!libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_AIRBRUSH))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_drain_events(li);
@@ -3449,13 +3399,12 @@ START_TEST(airbrush_tool)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tev);
 
 	litest_assert_notnull(tool);
 	litest_assert_enum_eq(libinput_tablet_tool_get_type(tool),
-			 LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH);
+			      LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH);
 
 	litest_assert(libinput_tablet_tool_has_slider(tool));
 
@@ -3473,9 +3422,7 @@ START_TEST(airbrush_slider)
 	double val;
 	int v;
 
-	if (!libevdev_has_event_code(dev->evdev,
-				    EV_KEY,
-				    BTN_TOOL_AIRBRUSH))
+	if (!libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_AIRBRUSH))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_drain_events(li);
@@ -3499,8 +3446,7 @@ START_TEST(airbrush_slider)
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
 		litest_dispatch(li);
 		event = libinput_get_event(li);
-		tev = litest_is_tablet_event(event,
-					     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+		tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 		litest_assert(libinput_event_tablet_tool_slider_has_changed(tev));
 		val = libinput_event_tablet_tool_get_slider_position(tev);
 
@@ -3521,9 +3467,7 @@ START_TEST(artpen_tool)
 	struct libinput_event_tablet_tool *tev;
 	struct libinput_tablet_tool *tool;
 
-	if (!libevdev_has_event_code(dev->evdev,
-				    EV_ABS,
-				    ABS_Z))
+	if (!libevdev_has_event_code(dev->evdev, EV_ABS, ABS_Z))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_drain_events(li);
@@ -3534,12 +3478,11 @@ START_TEST(artpen_tool)
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(tev);
 	litest_assert_notnull(tool);
 	litest_assert_enum_eq(libinput_tablet_tool_get_type(tool),
-			 LIBINPUT_TABLET_TOOL_TYPE_PEN);
+			      LIBINPUT_TABLET_TOOL_TYPE_PEN);
 	litest_assert(libinput_tablet_tool_has_rotation(tool));
 
 	libinput_event_destroy(event);
@@ -3557,16 +3500,14 @@ START_TEST(artpen_rotation)
 	double scale;
 	int angle;
 
-	if (!libevdev_has_event_code(dev->evdev,
-				    EV_ABS,
-				    ABS_Z))
+	if (!libevdev_has_event_code(dev->evdev, EV_ABS, ABS_Z))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_drain_events(li);
 
 	abs = libevdev_get_abs_info(dev->evdev, ABS_Z);
 	litest_assert_notnull(abs);
-	scale = absinfo_range(abs)/360.0;
+	scale = absinfo_range(abs) / 360.0;
 
 	litest_event(dev, EV_KEY, BTN_TOOL_BRUSH, 1);
 	litest_event(dev, EV_ABS, ABS_MISC, 0x804); /* Art Pen */
@@ -3585,8 +3526,7 @@ START_TEST(artpen_rotation)
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
 		litest_dispatch(li);
 		event = libinput_get_event(li);
-		tev = litest_is_tablet_event(event,
-					     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+		tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 		litest_assert(libinput_event_tablet_tool_rotation_has_changed(tev));
 		val = libinput_event_tablet_tool_get_rotation(tev);
 
@@ -3595,7 +3535,6 @@ START_TEST(artpen_rotation)
 
 		libinput_event_destroy(event);
 		litest_assert_empty_queue(li);
-
 	}
 }
 END_TEST
@@ -3609,7 +3548,7 @@ START_TEST(tablet_time_usec)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	uint64_t time_usec;
 
@@ -3619,11 +3558,10 @@ START_TEST(tablet_time_usec)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	time_usec = libinput_event_tablet_tool_get_time_usec(tev);
 	litest_assert_int_eq(libinput_event_tablet_tool_get_time(tev),
-			 (uint32_t) (time_usec / 1000));
+			     (uint32_t)(time_usec / 1000));
 	libinput_event_destroy(event);
 }
 END_TEST
@@ -3689,12 +3627,18 @@ device_has_calibration(struct litest_dev
 		libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_PEN) ||
 		libevdev_has_event_code(dev->evdev, EV_KEY, BTN_STYLUS);
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	WacomDeviceDatabase *db = libwacom_database_new();
 	if (db) {
-		WacomDevice *d = libwacom_new_from_path(db, libevdev_uinput_get_devnode(dev->uinput), WFALLBACK_NONE, NULL);
+		WacomDevice *d =
+			libwacom_new_from_path(db,
+					       libevdev_uinput_get_devnode(dev->uinput),
+					       WFALLBACK_NONE,
+					       NULL);
 		if (d) {
-			has_calibration = !!(libwacom_get_integration_flags(d) & (WACOM_DEVICE_INTEGRATED_SYSTEM|WACOM_DEVICE_INTEGRATED_DISPLAY));
+			has_calibration = !!(libwacom_get_integration_flags(d) &
+					     (WACOM_DEVICE_INTEGRATED_SYSTEM |
+					      WACOM_DEVICE_INTEGRATED_DISPLAY));
 			libwacom_destroy(d);
 		}
 		libwacom_database_destroy(db);
@@ -3710,7 +3654,7 @@ START_TEST(tablet_calibration_has_matrix
 	struct libinput_device *d = dev->libinput_device;
 	enum libinput_config_status status;
 	int rc;
-	float calibration[6] = {1, 0, 0, 0, 1, 0};
+	float calibration[6] = { 1, 0, 0, 0, 1, 0 };
 	int has_calibration;
 
 	has_calibration = device_has_calibration(dev);
@@ -3719,12 +3663,10 @@ START_TEST(tablet_calibration_has_matrix
 	litest_assert_int_eq(rc, has_calibration);
 	rc = libinput_device_config_calibration_get_matrix(d, calibration);
 	litest_assert_int_eq(rc, 0);
-	rc = libinput_device_config_calibration_get_default_matrix(d,
-								   calibration);
+	rc = libinput_device_config_calibration_get_default_matrix(d, calibration);
 	litest_assert_int_eq(rc, 0);
 
-	status = libinput_device_config_calibration_set_matrix(d,
-							       calibration);
+	status = libinput_device_config_calibration_set_matrix(d, calibration);
 	if (has_calibration)
 		litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	else
@@ -3738,13 +3680,13 @@ START_TEST(tablet_calibration_set_matrix
 	struct libinput *li = dev->libinput;
 	struct libinput_device *d = dev->libinput_device;
 	enum libinput_config_status status;
-	float calibration[6] = {0.5, 0, 0, 0, 0.5, 0};
+	float calibration[6] = { 0.5, 0, 0, 0, 0.5, 0 };
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tablet_event;
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 0 },
 		{ ABS_PRESSURE, 10 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	double x, y, dx, dy, mdx, mdy;
 
@@ -3754,10 +3696,11 @@ START_TEST(tablet_calibration_set_matrix
 	litest_drain_events(li);
 
 	litest_tablet_proximity_in(dev, 100, 100, axes);
+	litest_tablet_tip_down(dev, 100, 100, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	x = libinput_event_tablet_tool_get_x(tablet_event);
 	y = libinput_event_tablet_tool_get_y(tablet_event);
 	libinput_event_destroy(event);
@@ -3770,25 +3713,26 @@ START_TEST(tablet_calibration_set_matrix
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 
 	dx = libinput_event_tablet_tool_get_x(tablet_event) - x;
 	dy = libinput_event_tablet_tool_get_y(tablet_event) - y;
 	libinput_event_destroy(event);
+	litest_tablet_tip_up(dev, 80, 80, axes);
 	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
 	litest_wait_for_event_of_type(li, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	litest_drain_events(li);
 
-	status = libinput_device_config_calibration_set_matrix(d,
-							       calibration);
+	status = libinput_device_config_calibration_set_matrix(d, calibration);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_tablet_proximity_in(dev, 100, 100, axes);
+	litest_tablet_tip_down(dev, 100, 100, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	x = libinput_event_tablet_tool_get_x(tablet_event);
 	y = libinput_event_tablet_tool_get_y(tablet_event);
 	libinput_event_destroy(event);
@@ -3801,8 +3745,7 @@ START_TEST(tablet_calibration_set_matrix
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tablet_event = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 
 	mdx = libinput_event_tablet_tool_get_x(tablet_event) - x;
 	mdy = libinput_event_tablet_tool_get_y(tablet_event) - y;
@@ -3822,13 +3765,13 @@ START_TEST(tablet_calibration_set_matrix
 	struct libinput *li = dev->libinput;
 	struct libinput_device *d = dev->libinput_device;
 	enum libinput_config_status status;
-	float calibration[6] = {0.5, 0, 0, 0, 1, 0};
+	float calibration[6] = { 0.5, 0, 0, 0, 1, 0 };
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tablet_event;
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	double x, y;
 
@@ -3837,16 +3780,15 @@ START_TEST(tablet_calibration_set_matrix
 
 	litest_drain_events(li);
 
-	status = libinput_device_config_calibration_set_matrix(d,
-							       calibration);
+	status = libinput_device_config_calibration_set_matrix(d, calibration);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_tablet_proximity_in(dev, 100, 100, axes);
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	x = libinput_event_tablet_tool_get_x_transformed(tablet_event, 100);
 	y = libinput_event_tablet_tool_get_y_transformed(tablet_event, 100);
 	libinput_event_destroy(event);
@@ -3857,25 +3799,25 @@ START_TEST(tablet_calibration_set_matrix
 	litest_assert_double_lt(y, 100.0);
 
 	litest_tablet_proximity_out(dev);
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 	litest_tablet_proximity_in(dev, 50, 50, axes);
 	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
 	litest_wait_for_event_of_type(li, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	litest_wait_for_event_of_type(li, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	litest_drain_events(li);
 
 	calibration[0] = 1;
 	calibration[4] = 0.5;
-	status = libinput_device_config_calibration_set_matrix(d,
-							       calibration);
+	status = libinput_device_config_calibration_set_matrix(d, calibration);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_tablet_proximity_in(dev, 100, 100, axes);
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tablet_event = litest_is_tablet_event(event,
-					      LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tablet_event =
+		litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	x = libinput_event_tablet_tool_get_x_transformed(tablet_event, 100);
 	y = libinput_event_tablet_tool_get_y_transformed(tablet_event, 100);
 	libinput_event_destroy(event);
@@ -3886,6 +3828,7 @@ START_TEST(tablet_calibration_set_matrix
 	litest_assert(y < 51.0);
 
 	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
 }
 END_TEST
 
@@ -3979,7 +3922,6 @@ START_TEST(tablet_area_set_rectangle_inv
 
 	rc = libinput_device_config_area_set_rectangle(d, &rect);
 	litest_assert_enum_eq(rc, LIBINPUT_CONFIG_STATUS_INVALID);
-
 }
 END_TEST
 
@@ -3996,10 +3938,11 @@ get_tool_xy(struct libinput *li, double
 		tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 		break;
 	case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
-		tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+		tev = litest_is_tablet_event(event,
+					     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 		break;
 	default:
-		abort();
+		litest_assert_not_reached();
 	}
 
 	*x = libinput_event_tablet_tool_get_x_transformed(tev, 100);
@@ -4015,13 +3958,14 @@ START_TEST(tablet_area_set_rectangle)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	double x, y;
 	double *scaled, *unscaled;
 
 	const char *param_axis = litest_test_param_get_string(test_env->params, "axis");
-	const char *param_direction = litest_test_param_get_string(test_env->params, "direction");
+	const char *param_direction =
+		litest_test_param_get_string(test_env->params, "direction");
 
 	bool use_vertical = streq(param_axis, "vertical");
 	int direction = streq(param_direction, "down") ? 1 : -1;
@@ -4032,26 +3976,33 @@ START_TEST(tablet_area_set_rectangle)
 	struct libinput_config_area_rectangle rect;
 	if (use_vertical) {
 		rect = (struct libinput_config_area_rectangle){
-			0.25, 0.0, 0.75, 1.0,
+			0.25,
+			0.0,
+			0.75,
+			1.0,
 		};
 		scaled = &x;
 		unscaled = &y;
 	} else {
 		rect = (struct libinput_config_area_rectangle){
-			0.0, 0.25, 1.0, 0.75,
+			0.0,
+			0.25,
+			1.0,
+			0.75,
 		};
 		scaled = &y;
 		unscaled = &x;
 	}
 
-	enum libinput_config_status status = libinput_device_config_area_set_rectangle(d, &rect);
+	enum libinput_config_status status =
+		libinput_device_config_area_set_rectangle(d, &rect);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_drain_events(li);
 
 	/* move from the center out */
 	litest_tablet_proximity_in(dev, 50, 50, axes);
-	libinput_dispatch(li);
+	litest_dispatch(li);
 	get_tool_xy(li, &x, &y);
 	litest_assert_double_eq_epsilon(*scaled, 50.0, 2);
 	litest_assert_double_eq_epsilon(*unscaled, 50.0, 2);
@@ -4065,7 +4016,7 @@ START_TEST(tablet_area_set_rectangle)
 		litest_drain_events(li);
 
 		litest_tablet_motion(dev, i, i, axes);
-		libinput_dispatch(li);
+		litest_dispatch(li);
 		get_tool_xy(li, &x, &y);
 		if (i <= 25)
 			litest_assert_double_eq(*scaled, 0.0);
@@ -4080,16 +4031,14 @@ START_TEST(tablet_area_set_rectangle)
 	/* Push through any smoothing */
 	litest_tablet_motion(dev, final_stop, final_stop, axes);
 	litest_tablet_motion(dev, final_stop, final_stop, axes);
-	libinput_dispatch(li);
+	litest_dispatch(li);
 	litest_drain_events(li);
 
 	litest_tablet_proximity_out(dev);
-	litest_timeout_tablet_proxout();
-	libinput_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 	get_tool_xy(li, &x, &y);
 	litest_assert_double_eq_epsilon(x, final_stop, 1);
 	litest_assert_double_eq_epsilon(y, final_stop, 1);
-
 }
 END_TEST
 
@@ -4101,7 +4050,7 @@ START_TEST(tablet_area_set_rectangle_mov
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	double x, y;
 
@@ -4109,10 +4058,14 @@ START_TEST(tablet_area_set_rectangle_mov
 		return LITEST_NOT_APPLICABLE;
 
 	struct libinput_config_area_rectangle rect = {
-		0.25, 0.25, 0.75, 0.75,
+		0.25,
+		0.25,
+		0.75,
+		0.75,
 	};
 
-	enum libinput_config_status status = libinput_device_config_area_set_rectangle(d, &rect);
+	enum libinput_config_status status =
+		libinput_device_config_area_set_rectangle(d, &rect);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_drain_events(li);
@@ -4120,33 +4073,31 @@ START_TEST(tablet_area_set_rectangle_mov
 	/* move in/out of prox outside the area */
 	litest_tablet_proximity_in(dev, 5, 5, axes);
 	litest_tablet_proximity_out(dev);
-	libinput_dispatch(li);
-	litest_timeout_tablet_proxout();
-	libinput_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 	litest_assert_empty_queue(li);
 
 	x = 5;
 	y = 5;
 	/* Move around the area - since we stay outside the area expect no events */
 	litest_tablet_proximity_in(dev, x, y, axes);
-	libinput_dispatch(li);
+	litest_dispatch(li);
 	for (; x < 90; x += 5) {
 		litest_tablet_motion(dev, x, y, axes);
-		libinput_dispatch(li);
+		litest_dispatch(li);
 		litest_assert_empty_queue(li);
 	}
 	litest_axis_set_value(axes, ABS_PRESSURE, 30);
 	litest_tablet_tip_down(dev, x, y, axes);
 	for (; y < 90; y += 5) {
 		litest_tablet_motion(dev, x, y, axes);
-		libinput_dispatch(li);
+		litest_dispatch(li);
 		litest_assert_empty_queue(li);
 	}
 	litest_axis_set_value(axes, ABS_PRESSURE, 0);
 	litest_tablet_tip_up(dev, x, y, axes);
 	for (; x > 5; x -= 5) {
 		litest_tablet_motion(dev, x, y, axes);
-		libinput_dispatch(li);
+		litest_dispatch(li);
 		litest_assert_empty_queue(li);
 	}
 	litest_button_click(dev, BTN_STYLUS, LIBINPUT_BUTTON_STATE_PRESSED);
@@ -4155,15 +4106,14 @@ START_TEST(tablet_area_set_rectangle_mov
 	litest_tablet_tip_down(dev, x, y, axes);
 	for (; y > 5; y -= 5) {
 		litest_tablet_motion(dev, x, y, axes);
-		libinput_dispatch(li);
+		litest_dispatch(li);
 		litest_assert_empty_queue(li);
 	}
 	litest_axis_set_value(axes, ABS_PRESSURE, 0);
 	litest_tablet_tip_up(dev, x, y, axes);
 
 	litest_tablet_proximity_out(dev);
-	litest_timeout_tablet_proxout();
-	libinput_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -4176,7 +4126,7 @@ START_TEST(tablet_area_set_rectangle_mov
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	double x, y;
 
@@ -4184,23 +4134,27 @@ START_TEST(tablet_area_set_rectangle_mov
 		return LITEST_NOT_APPLICABLE;
 
 	struct libinput_config_area_rectangle rect = {
-		0.25, 0.25, 0.75, 0.75,
+		0.25,
+		0.25,
+		0.75,
+		0.75,
 	};
 
-	enum libinput_config_status status = libinput_device_config_area_set_rectangle(d, &rect);
+	enum libinput_config_status status =
+		libinput_device_config_area_set_rectangle(d, &rect);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_drain_events(li);
 
 	x = 5;
 	y = 50;
-        /* Move into the center of the area - since we started outside the area
-         * expect no events */
-        litest_tablet_proximity_in(dev, x, y, axes);
-	libinput_dispatch(li);
+	/* Move into the center of the area - since we started outside the area
+	 * expect no events */
+	litest_tablet_proximity_in(dev, x, y, axes);
+	litest_dispatch(li);
 	for (; x < 50; x += 5) {
 		litest_tablet_motion(dev, x, y, axes);
-		libinput_dispatch(li);
+		litest_dispatch(li);
 		litest_assert_empty_queue(li);
 	}
 	litest_button_click(dev, BTN_STYLUS, LIBINPUT_BUTTON_STATE_PRESSED);
@@ -4210,16 +4164,15 @@ START_TEST(tablet_area_set_rectangle_mov
 	litest_axis_set_value(axes, ABS_PRESSURE, 0);
 	litest_tablet_tip_up(dev, x, y, axes);
 	litest_tablet_proximity_out(dev);
-	litest_timeout_tablet_proxout();
-	libinput_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 	litest_assert_empty_queue(li);
 
 	y = 5;
 	x = 50;
-        litest_tablet_proximity_in(dev, x, y, axes);
+	litest_tablet_proximity_in(dev, x, y, axes);
 	for (; y < 50; y += 5) {
 		litest_tablet_motion(dev, x, y, axes);
-		libinput_dispatch(li);
+		litest_dispatch(li);
 		litest_assert_empty_queue(li);
 	}
 	litest_button_click(dev, BTN_STYLUS, LIBINPUT_BUTTON_STATE_PRESSED);
@@ -4229,9 +4182,7 @@ START_TEST(tablet_area_set_rectangle_mov
 	litest_axis_set_value(axes, ABS_PRESSURE, 0);
 	litest_tablet_tip_up(dev, x, y, axes);
 	litest_tablet_proximity_out(dev);
-
-	litest_timeout_tablet_proxout();
-	libinput_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -4246,7 +4197,7 @@ START_TEST(tablet_area_set_rectangle_mov
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	double x, y;
 
@@ -4254,10 +4205,14 @@ START_TEST(tablet_area_set_rectangle_mov
 		return LITEST_NOT_APPLICABLE;
 
 	struct libinput_config_area_rectangle rect = {
-		0.25, 0.25, 0.75, 0.75,
+		0.25,
+		0.25,
+		0.75,
+		0.75,
 	};
 
-	enum libinput_config_status status = libinput_device_config_area_set_rectangle(d, &rect);
+	enum libinput_config_status status =
+		libinput_device_config_area_set_rectangle(d, &rect);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_drain_events(li);
@@ -4265,9 +4220,7 @@ START_TEST(tablet_area_set_rectangle_mov
 	/* move in/out of prox outside the area but within the margin */
 	litest_tablet_proximity_in(dev, 24, 24, axes);
 	litest_tablet_proximity_out(dev);
-	libinput_dispatch(li);
-	litest_timeout_tablet_proxout();
-	libinput_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	ev = libinput_get_event(li);
 	tev = litest_is_proximity_event(ev, LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
@@ -4296,7 +4249,7 @@ START_TEST(tablet_area_set_rectangle_whi
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	double x, y;
 
@@ -4305,10 +4258,14 @@ START_TEST(tablet_area_set_rectangle_whi
 
 	litest_checkpoint("Set tablet area");
 	struct libinput_config_area_rectangle rect = {
-		0.25, 0.25, 0.75, 0.75,
+		0.25,
+		0.25,
+		0.75,
+		0.75,
 	};
 
-	enum libinput_config_status status = libinput_device_config_area_set_rectangle(d, &rect);
+	enum libinput_config_status status =
+		libinput_device_config_area_set_rectangle(d, &rect);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_drain_events(li);
@@ -4316,15 +4273,16 @@ START_TEST(tablet_area_set_rectangle_whi
 	litest_checkpoint("Proximity in + out outside tablet area");
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_tablet_proximity_out(dev);
-	litest_dispatch(li);
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 	litest_assert_empty_queue(li);
 	litest_dispatch(li);
 
 	litest_checkpoint("Update tablet area");
-	rect = (struct libinput_config_area_rectangle) {
-		0.05, 0.05, 0.95, 0.95,
+	rect = (struct libinput_config_area_rectangle){
+		0.05,
+		0.05,
+		0.95,
+		0.95,
 	};
 
 	status = libinput_device_config_area_set_rectangle(d, &rect);
@@ -4334,15 +4292,15 @@ START_TEST(tablet_area_set_rectangle_whi
 	litest_tablet_proximity_in(dev, 11, 11, axes);
 	litest_tablet_motion(dev, 12, 12, axes);
 	litest_tablet_proximity_out(dev);
-	litest_dispatch(li);
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	ev = libinput_get_event(li);
 	tev = litest_is_proximity_event(ev, LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
 	x = libinput_event_tablet_tool_get_x_transformed(tev, 100.0);
 	y = libinput_event_tablet_tool_get_y_transformed(tev, 100.0);
-	litest_assert_double_gt(x, 6); /* somewhere around 6%, precise number doesn't matter */
+	litest_assert_double_gt(
+		x,
+		6); /* somewhere around 6%, precise number doesn't matter */
 	litest_assert_double_gt(y, 6);
 	libinput_event_destroy(ev);
 
@@ -4365,7 +4323,9 @@ START_TEST(tablet_area_set_rectangle_whi
 END_TEST
 
 static void
-assert_pressure(struct libinput *li, enum libinput_event_type type, double expected_pressure)
+assert_pressure(struct libinput *li,
+		enum libinput_event_type type,
+		double expected_pressure)
 {
 	struct libinput_event *event = libinput_get_event(li);
 	struct libinput_event_tablet_tool *tev = litest_is_tablet_event(event, type);
@@ -4397,6 +4357,7 @@ START_TEST(tablet_pressure_offset_set)
 			assert_pressure(li, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY, 0.20);
 			assert_pressure(li, LIBINPUT_EVENT_TABLET_TOOL_TIP, 0.20);
 			litest_tablet_proximity_out(dev);
+			litest_timeout_tablet_proxout(li);
 			litest_drain_events(li);
 		}
 	}
@@ -4452,7 +4413,6 @@ START_TEST(tablet_pressure_offset_set)
 	event = libinput_get_event(li);
 	litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	libinput_event_destroy(event);
-
 }
 END_TEST
 
@@ -4469,6 +4429,7 @@ START_TEST(tablet_pressure_offset_decrea
 	/* offset 20 on prox in */
 	litest_tablet_proximity_in(dev, 5, 100, axes);
 	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
 	litest_drain_events(li);
 
 	/* offset 15 on prox in - this one is so we trigger on the next prox
@@ -4476,12 +4437,14 @@ START_TEST(tablet_pressure_offset_decrea
 	litest_axis_set_value(axes, ABS_PRESSURE, 15);
 	litest_tablet_proximity_in(dev, 5, 100, axes);
 	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
 	litest_drain_events(li);
 
 	/* a reduced pressure value must reduce the offset */
 	litest_axis_set_value(axes, ABS_PRESSURE, 10);
 	litest_tablet_proximity_in(dev, 5, 100, axes);
 	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
 	litest_drain_events(li);
 
 	litest_tablet_proximity_in(dev, 5, 100, axes);
@@ -4514,7 +4477,7 @@ START_TEST(tablet_pressure_offset_decrea
 
 	/* back to 10% should now give us 5% pressure because we reduced the
 	 * offset */
-        litest_axis_set_value(axes, ABS_PRESSURE, 10);
+	litest_axis_set_value(axes, ABS_PRESSURE, 10);
 	litest_tablet_motion(dev, 75, 75, axes);
 	litest_dispatch(li);
 	assert_pressure(li, LIBINPUT_EVENT_TABLET_TOOL_TIP, 0.05);
@@ -4535,12 +4498,15 @@ START_TEST(tablet_pressure_offset_increa
 	/* offset 20 on first prox in */
 	litest_tablet_proximity_in(dev, 5, 100, axes);
 	litest_tablet_proximity_out(dev);
+	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 	litest_drain_events(li);
 
 	/* offset 25 on second prox in - must not change the offset */
 	litest_axis_set_value(axes, ABS_PRESSURE, 25);
 	litest_tablet_proximity_in(dev, 5, 100, axes);
 	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
 	litest_drain_events(li);
 
 	/* offset 30 on third prox in - must not change the offset */
@@ -4654,13 +4620,13 @@ START_TEST(tablet_pressure_config)
 {
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
-	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tev;
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 0 },
 		{ ABS_PRESSURE, 10 },
 		{ -1, -1 },
 	};
+	bool has_pressure = libevdev_has_event_code(dev->evdev, EV_ABS, ABS_PRESSURE);
 
 	litest_tablet_proximity_in(dev, 5, 100, axes);
 	litest_drain_events(li);
@@ -4669,36 +4635,65 @@ START_TEST(tablet_pressure_config)
 	litest_tablet_motion(dev, 70, 70, axes);
 	litest_dispatch(li);
 
-	event = libinput_get_event(li);
+	_destroy_(libinput_event) *event = libinput_get_event(li);
 	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	struct libinput_tablet_tool *tool = libinput_event_tablet_tool_get_tool(tev);
 
-	litest_assert(libinput_tablet_tool_config_pressure_range_is_available(tool));
-	litest_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_minimum(tool), 0.0);
-	litest_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_maximum(tool), 1.0);
-	litest_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_default_minimum(tool), 0.0);
-	litest_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_default_maximum(tool), 1.0);
-
-	litest_assert_enum_eq(libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 1.0),
-			 LIBINPUT_CONFIG_STATUS_SUCCESS);
-	litest_assert_enum_eq(libinput_tablet_tool_config_pressure_range_set(tool, 0.2, 0.5),
-			 LIBINPUT_CONFIG_STATUS_SUCCESS);
-	litest_assert_enum_eq(libinput_tablet_tool_config_pressure_range_set(tool, -0.1, 1.0),
-			 LIBINPUT_CONFIG_STATUS_INVALID);
-	litest_assert_enum_eq(libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 0.0),
-			 LIBINPUT_CONFIG_STATUS_INVALID);
-	litest_assert_enum_eq(libinput_tablet_tool_config_pressure_range_set(tool, 1.0, 1.0),
-			 LIBINPUT_CONFIG_STATUS_INVALID);
-	litest_assert_enum_eq(libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 1.1),
-			 LIBINPUT_CONFIG_STATUS_INVALID);
+	litest_assert_int_eq(
+		has_pressure,
+		libinput_tablet_tool_config_pressure_range_is_available(tool));
+	litest_assert_double_eq(
+		libinput_tablet_tool_config_pressure_range_get_minimum(tool),
+		0.0);
+	litest_assert_double_eq(
+		libinput_tablet_tool_config_pressure_range_get_maximum(tool),
+		1.0);
+	litest_assert_double_eq(
+		libinput_tablet_tool_config_pressure_range_get_default_minimum(tool),
+		0.0);
+	litest_assert_double_eq(
+		libinput_tablet_tool_config_pressure_range_get_default_maximum(tool),
+		1.0);
+
+	if (!has_pressure) {
+		litest_assert_enum_eq(
+			libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 1.0),
+			LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+		return LITEST_PASS;
+	}
+
+	litest_assert_enum_eq(
+		libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 1.0),
+		LIBINPUT_CONFIG_STATUS_SUCCESS);
+	litest_assert_enum_eq(
+		libinput_tablet_tool_config_pressure_range_set(tool, 0.2, 0.5),
+		LIBINPUT_CONFIG_STATUS_SUCCESS);
+	litest_assert_enum_eq(
+		libinput_tablet_tool_config_pressure_range_set(tool, -0.1, 1.0),
+		LIBINPUT_CONFIG_STATUS_INVALID);
+	litest_assert_enum_eq(
+		libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 0.0),
+		LIBINPUT_CONFIG_STATUS_INVALID);
+	litest_assert_enum_eq(
+		libinput_tablet_tool_config_pressure_range_set(tool, 1.0, 1.0),
+		LIBINPUT_CONFIG_STATUS_INVALID);
+	litest_assert_enum_eq(
+		libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 1.1),
+		LIBINPUT_CONFIG_STATUS_INVALID);
 
 	/* The last successful one */
-	litest_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_minimum(tool), 0.2);
-	litest_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_maximum(tool), 0.5);
-	litest_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_default_minimum(tool), 0.0);
-	litest_assert_double_eq(libinput_tablet_tool_config_pressure_range_get_default_maximum(tool), 1.0);
-
-	libinput_event_destroy(event);
+	litest_assert_double_eq(
+		libinput_tablet_tool_config_pressure_range_get_minimum(tool),
+		0.2);
+	litest_assert_double_eq(
+		libinput_tablet_tool_config_pressure_range_get_maximum(tool),
+		0.5);
+	litest_assert_double_eq(
+		libinput_tablet_tool_config_pressure_range_get_default_minimum(tool),
+		0.0);
+	litest_assert_double_eq(
+		libinput_tablet_tool_config_pressure_range_get_default_maximum(tool),
+		1.0);
 }
 END_TEST
 
@@ -4716,6 +4711,9 @@ START_TEST(tablet_pressure_config_set_mi
 	};
 	double p, old_pressure;
 
+	if (!libevdev_has_event_code(dev->evdev, EV_ABS, ABS_PRESSURE))
+		return LITEST_NOT_APPLICABLE;
+
 	litest_tablet_proximity_in(dev, 5, 100, axes);
 	litest_drain_events(li);
 	litest_dispatch(li);
@@ -4731,8 +4729,9 @@ START_TEST(tablet_pressure_config_set_mi
 	old_pressure = p;
 
 	litest_assert(libinput_tablet_tool_config_pressure_range_is_available(tool));
-	litest_assert_enum_eq(libinput_tablet_tool_config_pressure_range_set(tool, 0.4, 1.0),
-			 LIBINPUT_CONFIG_STATUS_SUCCESS);
+	litest_assert_enum_eq(
+		libinput_tablet_tool_config_pressure_range_set(tool, 0.4, 1.0),
+		LIBINPUT_CONFIG_STATUS_SUCCESS);
 	libinput_event_destroy(event);
 
 	/* config doesn't take effect until we're out of prox */
@@ -4747,7 +4746,7 @@ START_TEST(tablet_pressure_config_set_mi
 	}
 
 	litest_tablet_proximity_out(dev);
-	litest_timeout_tablet_proxout();
+	litest_timeout_tablet_proxout(li);
 	litest_drain_events(li);
 
 	/* 10% hw value is below our thresholds, so logical zero */
@@ -4804,6 +4803,9 @@ START_TEST(tablet_pressure_config_set_ma
 	};
 	double p, old_pressure;
 
+	if (!libevdev_has_event_code(dev->evdev, EV_ABS, ABS_PRESSURE))
+		return LITEST_NOT_APPLICABLE;
+
 	litest_tablet_proximity_in(dev, 5, 100, axes);
 	litest_drain_events(li);
 	litest_dispatch(li);
@@ -4819,8 +4821,9 @@ START_TEST(tablet_pressure_config_set_ma
 	old_pressure = p;
 
 	litest_assert(libinput_tablet_tool_config_pressure_range_is_available(tool));
-	litest_assert_enum_eq(libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 0.6),
-			      LIBINPUT_CONFIG_STATUS_SUCCESS);
+	litest_assert_enum_eq(
+		libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 0.6),
+		LIBINPUT_CONFIG_STATUS_SUCCESS);
 	libinput_event_destroy(event);
 
 	/* config doesn't take effect until we're out of prox */
@@ -4835,8 +4838,7 @@ START_TEST(tablet_pressure_config_set_ma
 	}
 
 	litest_tablet_proximity_out(dev);
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	litest_axis_set_value(axes, ABS_PRESSURE, 10);
 	litest_tablet_proximity_in(dev, 70, 70, axes);
@@ -4878,7 +4880,8 @@ START_TEST(tablet_pressure_config_set_ma
 			litest_tablet_motion(dev, pos, pos, axes);
 			litest_dispatch(li);
 			event = libinput_get_event(li);
-			tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+			tev = litest_is_tablet_event(event,
+						     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 			p = libinput_event_tablet_tool_get_pressure(tev);
 			litest_assert_double_eq(p, 1.0);
 			libinput_event_destroy(event);
@@ -4901,6 +4904,9 @@ START_TEST(tablet_pressure_config_set_ra
 	};
 	double p, old_pressure;
 
+	if (!libevdev_has_event_code(dev->evdev, EV_ABS, ABS_PRESSURE))
+		return LITEST_NOT_APPLICABLE;
+
 	litest_tablet_proximity_in(dev, 5, 100, axes);
 	litest_drain_events(li);
 	litest_dispatch(li);
@@ -4916,8 +4922,9 @@ START_TEST(tablet_pressure_config_set_ra
 	old_pressure = p;
 
 	litest_assert(libinput_tablet_tool_config_pressure_range_is_available(tool));
-	litest_assert_enum_eq(libinput_tablet_tool_config_pressure_range_set(tool, 0.4, 0.6),
-			 LIBINPUT_CONFIG_STATUS_SUCCESS);
+	litest_assert_enum_eq(
+		libinput_tablet_tool_config_pressure_range_set(tool, 0.4, 0.6),
+		LIBINPUT_CONFIG_STATUS_SUCCESS);
 	libinput_event_destroy(event);
 
 	/* config doesn't take effect until we're out of prox */
@@ -4932,7 +4939,7 @@ START_TEST(tablet_pressure_config_set_ra
 	}
 
 	litest_tablet_proximity_out(dev);
-	litest_timeout_tablet_proxout();
+	litest_timeout_tablet_proxout(li);
 	litest_drain_events(li);
 
 	litest_tablet_proximity_in(dev, 70, 70, axes);
@@ -4944,23 +4951,212 @@ START_TEST(tablet_pressure_config_set_ra
 		litest_dispatch(li);
 		event = libinput_get_event(li);
 		if (libinput_event_get_type(event) == LIBINPUT_EVENT_TABLET_TOOL_AXIS)
-			tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+			tev = litest_is_tablet_event(event,
+						     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 		else
-			tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
+			tev = litest_is_tablet_event(event,
+						     LIBINPUT_EVENT_TABLET_TOOL_TIP);
 		p = libinput_event_tablet_tool_get_pressure(tev);
 		if (pressure <= 40) {
 			litest_assert_double_eq(p, 0.0);
 		} else if (pressure >= 60) {
 			litest_assert_double_eq(p, 1.0);
 		} else {
-			litest_assert_double_ge(p, (pressure - 1 - 40)/20.0);
-			litest_assert_double_le(p, (pressure - 40)/20.0);
+			litest_assert_double_ge(p, (pressure - 1 - 40) / 20.0);
+			litest_assert_double_le(p, (pressure - 40) / 20.0);
 		}
 		libinput_event_destroy(event);
 	}
 }
 END_TEST
 
+START_TEST(tablet_pressure_config_resets_offset)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_DISTANCE, 70 },
+		{ ABS_PRESSURE, 70 }, /* high pressure offset */
+		{ -1, -1 },
+	};
+
+	if (!libevdev_has_event_code(dev->evdev, EV_ABS, ABS_PRESSURE))
+		return LITEST_NOT_APPLICABLE;
+
+	litest_tablet_proximity_in(dev, 5, 100, axes);
+	litest_drain_events(li);
+	litest_dispatch(li);
+
+	litest_tablet_motion(dev, 70, 70, axes);
+	litest_dispatch(li);
+
+	{
+		_destroy_(libinput_event) *event = libinput_get_event(li);
+		auto tev =
+			litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+		auto tool = libinput_event_tablet_tool_get_tool(tev);
+
+		litest_assert(
+			libinput_tablet_tool_config_pressure_range_is_available(tool));
+		litest_assert_enum_eq(
+			libinput_tablet_tool_config_pressure_range_set(tool,
+								       0.01,
+								       0.99),
+			LIBINPUT_CONFIG_STATUS_SUCCESS);
+	}
+
+	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
+	litest_drain_events(li);
+
+	/* Config should be applied on prox in */
+	litest_axis_set_value(axes, ABS_PRESSURE, 71);
+	litest_tablet_proximity_in(dev, 72, 72, axes);
+	litest_drain_events(li);
+	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
+	litest_drain_events(li);
+
+	/* Two prox in/out cycles to get past the heuristics on devices without
+	 * ABS_DISTANCE
+	 */
+	litest_axis_set_value(axes, ABS_PRESSURE, 72);
+	litest_tablet_proximity_in(dev, 75, 75, axes);
+	litest_drain_events(li);
+	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
+	litest_drain_events(li);
+
+	for (double pressure = 10.0, i = 71; pressure <= 25; pressure += 5, i += 0.2) {
+		litest_log_group("Prox in/out with pressure %.f", pressure) {
+			litest_axis_set_value(axes, ABS_PRESSURE, pressure);
+			litest_tablet_proximity_in(dev, i, i, axes);
+			litest_dispatch(li);
+
+			_destroy_(libinput_event) *event = libinput_get_event(li);
+			auto tev = litest_is_tablet_event(
+				event,
+				LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+			double p = libinput_event_tablet_tool_get_pressure(tev);
+			/* checking if >5% is good enough here, it'd be zero if the
+			 * 70% threshold were locked in */
+			litest_assert_double_gt(p, 0.05);
+
+			litest_tablet_proximity_out(dev);
+			litest_timeout_tablet_proxout(li);
+			litest_drain_events(li);
+		}
+	}
+}
+END_TEST
+
+START_TEST(tablet_pressure_config_01_does_not_reset_offset)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_PRESSURE, 20 }, /* high pressure offset */
+		{ -1, -1 },
+	};
+
+	/* test is only run for specific devices because some of our test devices
+	 * write ABS_PRESSURE 0 events on prox out and that changes the test outcomes.
+	 */
+	if (!libevdev_has_event_code(dev->evdev, EV_ABS, ABS_PRESSURE))
+		return LITEST_NOT_APPLICABLE;
+
+	if (libevdev_has_event_code(dev->evdev, EV_ABS, ABS_DISTANCE))
+		return LITEST_NOT_APPLICABLE;
+
+	litest_drain_events(li); /* Drain potential tip event */
+
+	litest_tablet_proximity_in(dev, 68, 68, axes);
+	litest_dispatch(li);
+	{
+		_destroy_(libinput_event) *event = libinput_get_event(li);
+		auto tev = litest_is_tablet_event(event,
+						  LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+		auto tool = libinput_event_tablet_tool_get_tool(tev);
+
+		litest_assert(
+			libinput_tablet_tool_config_pressure_range_is_available(tool));
+		litest_assert_enum_eq(
+			libinput_tablet_tool_config_pressure_range_set(tool, 0.0, 1.0),
+			LIBINPUT_CONFIG_STATUS_SUCCESS);
+	}
+
+	/* Drain potential tip event. */
+	litest_drain_events(li);
+
+	/* No motion events here, that might change the offset! */
+
+	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
+	litest_drain_events(li);
+
+	litest_checkpoint("second prox in");
+	/* Config should be applied on prox in */
+	litest_axis_set_value(axes, ABS_PRESSURE, 31);
+	litest_tablet_proximity_in(dev, 72, 72, axes);
+	litest_drain_events(li);
+	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
+	litest_drain_events(li);
+
+	/* Two prox in/out cycles to finalize heuristics */
+	litest_with_logcapture(li, capture) {
+		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_INFO);
+		litest_axis_set_value(axes, ABS_PRESSURE, 32);
+		litest_tablet_proximity_in(dev, 75, 75, axes);
+		litest_drain_events(li);
+		litest_tablet_proximity_out(dev);
+		litest_timeout_tablet_proxout(li);
+		litest_drain_events(li);
+
+		/* Full message is "Pressure offset detected of n% detected on tool pen"
+		 */
+		litest_assert_strv_substring(capture->infos, "detected on tool pen");
+	}
+
+	litest_log_group("Expecting pressure with offset factored in") {
+		litest_axis_set_value(axes, ABS_PRESSURE, 25);
+		litest_tablet_proximity_in(dev, 50, 50, axes);
+		litest_dispatch(li);
+
+		_destroy_(libinput_event) *event = libinput_get_event(li);
+		auto tev = litest_is_tablet_event(event,
+						  LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+		double p = libinput_event_tablet_tool_get_pressure(tev);
+		/* Pressure 25%, base offset of 20% so we should get 5% plus the 1% we
+		 * use anyway */
+		litest_assert_double_ge(p, 0.05);
+		litest_assert_double_le(p, 0.07);
+
+		litest_tablet_proximity_out(dev);
+		litest_timeout_tablet_proxout(li);
+		litest_drain_events(li);
+	}
+
+	litest_log_group("Expecting low pressure to reduce offset") {
+		/* Because we didn't configure the range, we expect lower pressure to
+		 * reduce the offset */
+		litest_axis_set_value(axes, ABS_PRESSURE, 5);
+		litest_tablet_proximity_in(dev, 50, 50, axes);
+		litest_dispatch(li);
+
+		_destroy_(libinput_event) *event = libinput_get_event(li);
+		auto tev = litest_is_tablet_event(event,
+						  LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+		double p = libinput_event_tablet_tool_get_pressure(tev);
+		litest_assert_double_eq(p, 0.00);
+
+		litest_tablet_proximity_out(dev);
+		litest_timeout_tablet_proxout(li);
+		litest_drain_events(li);
+	}
+}
+END_TEST
+
 static void
 pressure_threshold_warning(struct libinput *libinput,
 			   enum libinput_log_priority priority,
@@ -4992,7 +5188,7 @@ START_TEST(tablet_pressure_offset_exceed
 		for (int i = 0; i < 2; i++) {
 			litest_tablet_proximity_in(dev, 5, 100, axes);
 			litest_tablet_proximity_out(dev);
-			litest_dispatch(li);
+			litest_timeout_tablet_proxout(li);
 		}
 	}
 
@@ -5024,10 +5220,10 @@ START_TEST(tablet_pressure_offset_none_f
 
 	/* we're going straight to touch on proximity, make sure we don't
 	 * offset the pressure here */
-	litest_push_event_frame(dev);
-	litest_tablet_proximity_in(dev, 5, 100, axes);
-	litest_tablet_tip_down(dev, 5, 100, axes);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_tablet_proximity_in(dev, 5, 100, axes);
+		litest_tablet_tip_down(dev, 5, 100, axes);
+	}
 	litest_dispatch(li);
 
 	assert_pressure(li, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY, 0.20);
@@ -5068,7 +5264,8 @@ START_TEST(tablet_pressure_across_multip
 	struct litest_device *cintiq12wx = litest_current_device();
 	struct libinput *li = cintiq12wx->libinput;
 
-	struct litest_device *mobilestudio = litest_add_device(li, LITEST_WACOM_CINTIQ_PRO16_PEN);
+	struct litest_device *mobilestudio =
+		litest_add_device(li, LITEST_WACOM_CINTIQ_PRO16_PEN);
 
 	bool direction = litest_test_param_get_bool(test_env->params, "8k-to-1k");
 	struct litest_device *first = direction ? mobilestudio : cintiq12wx;
@@ -5083,7 +5280,7 @@ START_TEST(tablet_pressure_across_multip
 	bool have_cintiq12wx = false;
 	bool have_mobilestudio = false;
 
-	libinput_dispatch(li);
+	litest_dispatch(li);
 
 	while (!have_cintiq12wx || !have_mobilestudio) {
 		litest_wait_for_event_of_type(li, LIBINPUT_EVENT_DEVICE_ADDED);
@@ -5093,9 +5290,11 @@ START_TEST(tablet_pressure_across_multip
 			have_cintiq12wx = true;
 		if (libinput_event_get_device(ev) == mobilestudio->libinput_device)
 			have_mobilestudio = true;
-		litest_checkpoint("Have Cintiq 12WX: %s,  MobileStudio: %s", yesno(have_cintiq12wx), yesno(have_mobilestudio));
+		litest_checkpoint("Have Cintiq 12WX: %s,  MobileStudio: %s",
+				  yesno(have_cintiq12wx),
+				  yesno(have_mobilestudio));
 		libinput_event_destroy(ev);
-		libinput_dispatch(li);
+		litest_dispatch(li);
 	}
 
 	litest_drain_events(li);
@@ -5106,7 +5305,8 @@ START_TEST(tablet_pressure_across_multip
 	 * proportionate range */
 	struct litest_device *dev = first;
 	for (int i = 0; i < 2; i++, dev = second) {
-		litest_checkpoint("Putting pen into proximity on %s", libinput_device_get_name(dev->libinput_device));
+		litest_checkpoint("Putting pen into proximity on %s",
+				  libinput_device_get_name(dev->libinput_device));
 		litest_tablet_proximity_in(dev, 50, 50, axes);
 
 		litest_axis_set_value(axes, ABS_DISTANCE, 0);
@@ -5120,35 +5320,43 @@ START_TEST(tablet_pressure_across_multip
 			litest_dispatch(li);
 		}
 		litest_tablet_proximity_out(dev);
-		litest_timeout_tablet_proxout();
-		libinput_dispatch(li);
+		litest_timeout_tablet_proxout(li);
 
-		litest_assert_tablet_proximity_event(li, LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+		litest_assert_tablet_proximity_event(
+			li,
+			LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
 		litest_assert_tablet_tip_event(li, LIBINPUT_TABLET_TOOL_TIP_DOWN);
 		do {
 			struct libinput_event *ev = libinput_get_event(li);
-			struct libinput_event_tablet_tool *tev = litest_is_tablet_event(ev, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+			struct libinput_event_tablet_tool *tev =
+				litest_is_tablet_event(ev,
+						       LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 
 			double pressure = libinput_event_tablet_tool_get_pressure(tev);
-			/* We start at device range 10% but we always have a small threshold */
+			/* We start at device range 10% but we always have a small
+			 * threshold */
 			litest_assert_double_gt_epsilon(pressure, 0.09, 0);
 			litest_assert_double_le(pressure, 0.7);
 
 			libinput_event_destroy(ev);
-		} while (libinput_next_event_type(li) == LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+		} while (libinput_next_event_type(li) ==
+			 LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 
 		litest_assert_tablet_tip_event(li, LIBINPUT_TABLET_TOOL_TIP_UP);
-		litest_assert_tablet_proximity_event(li, LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+		litest_assert_tablet_proximity_event(
+			li,
+			LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	}
 
-	litest_delete_device(mobilestudio);
+	litest_device_destroy(mobilestudio);
 }
 END_TEST
 
 START_TEST(tablet_pressure_after_unplug)
 {
-	struct libinput *li = litest_create_context();
-	struct litest_device *dev = litest_add_device(li, LITEST_WACOM_CINTIQ_PRO16_PEN);
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	struct litest_device *dev =
+		litest_add_device(li, LITEST_WACOM_CINTIQ_PRO16_PEN);
 
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 20 },
@@ -5161,7 +5369,7 @@ START_TEST(tablet_pressure_after_unplug)
 
 	/* Unplug 10 times because that's more than however many tablets
 	 * we keep track of internally */
-	for (int iteration = 0; iteration < 10; iteration++)  {
+	for (int iteration = 0; iteration < 10; iteration++) {
 		litest_checkpoint("Putting pen into proximity");
 		litest_tablet_proximity_in(dev, 50, 50, axes);
 		litest_tablet_motion(dev, 51, 51, axes);
@@ -5170,7 +5378,7 @@ START_TEST(tablet_pressure_after_unplug)
 		litest_tablet_proximity_out(dev);
 
 		litest_checkpoint("Unplugging/replugging device");
-		litest_delete_device(dev);
+		litest_device_destroy(dev);
 		litest_dispatch(li);
 		litest_drain_events(li);
 		dev = litest_add_device(li, LITEST_WACOM_CINTIQ_PRO16_PEN);
@@ -5182,7 +5390,8 @@ START_TEST(tablet_pressure_after_unplug)
 
 	litest_tablet_proximity_in(dev, 49, 49, axes);
 	litest_dispatch(li);
-	litest_assert_tablet_proximity_event(li, LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+	litest_assert_tablet_proximity_event(li,
+					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
 
 	for (int i = 1; i < 5; i++) {
 		litest_tablet_motion(dev, 50 + i, 50 + i, axes);
@@ -5204,8 +5413,8 @@ START_TEST(tablet_pressure_after_unplug)
 		litest_tablet_motion(dev, 50 + i, 50 + i, axes);
 		litest_dispatch(li);
 		struct libinput_event *event = libinput_get_event(li);
-		struct libinput_event_tablet_tool *tev = litest_is_tablet_event(event,
-										LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+		struct libinput_event_tablet_tool *tev =
+			litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 		double pressure = libinput_event_tablet_tool_get_pressure(tev);
 		litest_assert_double_gt(pressure, old_pressure);
 		libinput_event_destroy(event);
@@ -5221,13 +5430,11 @@ START_TEST(tablet_pressure_after_unplug)
 
 	litest_checkpoint("Putting out of proximity");
 	litest_tablet_proximity_out(dev);
-	litest_dispatch(li);
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
-	litest_assert_tablet_proximity_event(li, LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+	litest_timeout_tablet_proxout(li);
+	litest_assert_tablet_proximity_event(li,
+					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 
-	litest_delete_device(dev);
-	litest_destroy_context(li);
+	litest_device_destroy(dev);
 }
 END_TEST
 
@@ -5272,11 +5479,8 @@ START_TEST(tilt_available)
 	struct libinput_event_tablet_tool *tev;
 	struct libinput_tablet_tool *tool;
 	struct axis_replacement axes[] = {
-		{ ABS_DISTANCE, 10 },
-		{ ABS_PRESSURE, 0 },
-		{ ABS_TILT_X, 80 },
-		{ ABS_TILT_Y, 20 },
-		{ -1, -1 }
+		{ ABS_DISTANCE, 10 }, { ABS_PRESSURE, 0 }, { ABS_TILT_X, 80 },
+		{ ABS_TILT_Y, 20 },   { -1, -1 },
 	};
 
 	litest_drain_events(li);
@@ -5284,8 +5488,7 @@ START_TEST(tilt_available)
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
 	tool = libinput_event_tablet_tool_get_tool(tev);
 	litest_assert(libinput_tablet_tool_has_tilt(tool));
@@ -5302,11 +5505,8 @@ START_TEST(tilt_not_available)
 	struct libinput_event_tablet_tool *tev;
 	struct libinput_tablet_tool *tool;
 	struct axis_replacement axes[] = {
-		{ ABS_DISTANCE, 10 },
-		{ ABS_PRESSURE, 0 },
-		{ ABS_TILT_X, 80 },
-		{ ABS_TILT_Y, 20 },
-		{ -1, -1 }
+		{ ABS_DISTANCE, 10 }, { ABS_PRESSURE, 0 }, { ABS_TILT_X, 80 },
+		{ ABS_TILT_Y, 20 },   { -1, -1 },
 	};
 
 	litest_drain_events(li);
@@ -5314,8 +5514,7 @@ START_TEST(tilt_not_available)
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
 	tool = libinput_event_tablet_tool_get_tool(tev);
 	litest_assert(!libinput_tablet_tool_has_tilt(tool));
@@ -5331,11 +5530,8 @@ START_TEST(tilt_x)
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tev;
 	struct axis_replacement axes[] = {
-		{ ABS_DISTANCE, 10 },
-		{ ABS_PRESSURE, 0 },
-		{ ABS_TILT_X, 10 },
-		{ ABS_TILT_Y, 0 },
-		{ -1, -1 }
+		{ ABS_DISTANCE, 10 }, { ABS_PRESSURE, 0 }, { ABS_TILT_X, 10 },
+		{ ABS_TILT_Y, 0 },    { -1, -1 },
 	};
 	double tx, ty;
 	int tilt;
@@ -5346,8 +5542,7 @@ START_TEST(tilt_x)
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
 	/* 90% of the actual axis but mapped into a [-64, 64] tilt range, so
 	 * we expect 51 degrees ± rounding errors */
@@ -5376,8 +5571,7 @@ START_TEST(tilt_x)
 		litest_tablet_motion(dev, 10, 11, axes);
 		litest_dispatch(li);
 		event = libinput_get_event(li);
-		tev = litest_is_tablet_event(event,
-					     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+		tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 
 		tx = libinput_event_tablet_tool_get_tilt_x(tev);
 		litest_assert_double_ge(tx, expected_tx - 2);
@@ -5405,11 +5599,8 @@ START_TEST(tilt_y)
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *tev;
 	struct axis_replacement axes[] = {
-		{ ABS_DISTANCE, 10 },
-		{ ABS_PRESSURE, 0 },
-		{ ABS_TILT_X, 0 },
-		{ ABS_TILT_Y, 10 },
-		{ -1, -1 }
+		{ ABS_DISTANCE, 10 }, { ABS_PRESSURE, 0 }, { ABS_TILT_X, 0 },
+		{ ABS_TILT_Y, 10 },   { -1, -1 },
 	};
 	double tx, ty;
 	int tilt;
@@ -5420,8 +5611,7 @@ START_TEST(tilt_y)
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
 	/* 90% of the actual axis but mapped into a [-64, 64] tilt range, so
 	 * we expect 50 degrees ± rounding errors */
@@ -5450,8 +5640,7 @@ START_TEST(tilt_y)
 		litest_tablet_motion(dev, 10, 10, axes);
 		litest_dispatch(li);
 		event = libinput_get_event(li);
-		tev = litest_is_tablet_event(event,
-					     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+		tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 
 		ty = libinput_event_tablet_tool_get_tilt_y(tev);
 		litest_assert_double_ge(ty, expected_ty - 2);
@@ -5481,12 +5670,12 @@ START_TEST(tilt_fixed_points)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
-	/* On devices with a range of [-N, M], make sure we calculate the hw zero position
-	 * as zero and that the respective min/max resolve to our (hardcoded) min/max degree
-	 * values
+	/* On devices with a range of [-N, M], make sure we calculate the hw zero
+	 * position as zero and that the respective min/max resolve to our (hardcoded)
+	 * min/max degree values
 	 */
 	const struct input_absinfo *abs = libevdev_get_abs_info(dev->evdev, ABS_TILT_X);
 	if (abs->minimum >= 0)
@@ -5521,16 +5710,15 @@ START_TEST(tilt_fixed_points)
 
 	litest_drain_events(li);
 
-	litest_push_event_frame(dev);
-	litest_tablet_proximity_in(dev, 10, 10, axes);
-	litest_event(dev, EV_ABS, ABS_TILT_X, axis_value);
-	litest_event(dev, EV_ABS, ABS_TILT_Y, axis_value);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_tablet_proximity_in(dev, 10, 10, axes);
+		litest_event(dev, EV_ABS, ABS_TILT_X, axis_value);
+		litest_event(dev, EV_ABS, ABS_TILT_Y, axis_value);
+	}
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
 	double tx = libinput_event_tablet_tool_get_tilt_x(tev);
 	double ty = libinput_event_tablet_tool_get_tilt_y(tev);
@@ -5566,14 +5754,16 @@ START_TEST(relative_no_profile)
 	litest_assert_enum_eq(profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, 0);
 	litest_assert_enum_eq(profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, 0);
 
-	status = libinput_device_config_accel_set_profile(device,
-							  LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
+	status = libinput_device_config_accel_set_profile(
+		device,
+		LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 	profile = libinput_device_config_accel_get_profile(device);
 	litest_assert_enum_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_NONE);
 
-	status = libinput_device_config_accel_set_profile(device,
-							  LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE);
+	status = libinput_device_config_accel_set_profile(
+		device,
+		LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 	profile = libinput_device_config_accel_get_profile(device);
 	litest_assert_enum_eq(profile, LIBINPUT_CONFIG_ACCEL_PROFILE_NONE);
@@ -5589,7 +5779,7 @@ START_TEST(relative_no_delta_prox_in)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	double dx, dy;
 
@@ -5598,8 +5788,7 @@ START_TEST(relative_no_delta_prox_in)
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	dx = libinput_event_tablet_tool_get_dx(tev);
 	dy = libinput_event_tablet_tool_get_dy(tev);
 	litest_assert(dx == 0.0);
@@ -5618,7 +5807,7 @@ START_TEST(relative_delta)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	double dx, dy;
 
@@ -5626,7 +5815,7 @@ START_TEST(relative_delta)
 	litest_drain_events(li);
 
 	/* flush the motion history */
-	for (int i = 0; i < 5; i ++)
+	for (int i = 0; i < 5; i++)
 		litest_tablet_motion(dev, 10 + i, 10, axes);
 	litest_drain_events(li);
 
@@ -5634,8 +5823,7 @@ START_TEST(relative_delta)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	dx = libinput_event_tablet_tool_get_dx(tev);
 	dy = libinput_event_tablet_tool_get_dy(tev);
 	litest_assert(dx > 0.0);
@@ -5643,15 +5831,14 @@ START_TEST(relative_delta)
 	libinput_event_destroy(event);
 
 	/* flush the motion history */
-	for (int i = 0; i < 5; i ++)
+	for (int i = 0; i < 5; i++)
 		litest_tablet_motion(dev, 20 - i, 10, axes);
 	litest_drain_events(li);
 
 	litest_tablet_motion(dev, 5, 10, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	dx = libinput_event_tablet_tool_get_dx(tev);
 	dy = libinput_event_tablet_tool_get_dy(tev);
 	litest_assert(dx < 0.0);
@@ -5659,15 +5846,14 @@ START_TEST(relative_delta)
 	libinput_event_destroy(event);
 
 	/* flush the motion history */
-	for (int i = 0; i < 5; i ++)
+	for (int i = 0; i < 5; i++)
 		litest_tablet_motion(dev, 5, 10 + i, axes);
 	litest_drain_events(li);
 
 	litest_tablet_motion(dev, 5, 20, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	dx = libinput_event_tablet_tool_get_dx(tev);
 	dy = libinput_event_tablet_tool_get_dy(tev);
 	litest_assert(dx == 0.0);
@@ -5675,15 +5861,14 @@ START_TEST(relative_delta)
 	libinput_event_destroy(event);
 
 	/* flush the motion history */
-	for (int i = 0; i < 5; i ++)
+	for (int i = 0; i < 5; i++)
 		litest_tablet_motion(dev, 5, 20 - i, axes);
 	litest_drain_events(li);
 
 	litest_tablet_motion(dev, 5, 10, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	dx = libinput_event_tablet_tool_get_dx(tev);
 	dy = libinput_event_tablet_tool_get_dy(tev);
 	litest_assert(dx == 0.0);
@@ -5701,7 +5886,7 @@ START_TEST(relative_no_delta_on_tip)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	double dx, dy;
 
@@ -5718,8 +5903,7 @@ START_TEST(relative_no_delta_on_tip)
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert(libinput_event_tablet_tool_x_has_changed(tev));
 	litest_assert(libinput_event_tablet_tool_y_has_changed(tev));
 	dx = libinput_event_tablet_tool_get_dx(tev);
@@ -5732,8 +5916,7 @@ START_TEST(relative_no_delta_on_tip)
 	litest_tablet_motion(dev, 40, 30, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	dx = libinput_event_tablet_tool_get_dx(tev);
 	dy = libinput_event_tablet_tool_get_dy(tev);
 	litest_assert(dx > 0.0);
@@ -5746,8 +5929,7 @@ START_TEST(relative_no_delta_on_tip)
 	litest_tablet_tip_up(dev, 50, 40, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert(libinput_event_tablet_tool_x_has_changed(tev));
 	litest_assert(libinput_event_tablet_tool_y_has_changed(tev));
 	dx = libinput_event_tablet_tool_get_dx(tev);
@@ -5767,7 +5949,7 @@ START_TEST(relative_calibration)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	double dx, dy;
 	float calibration[] = { -1, 0, 1, 0, -1, 1 };
@@ -5776,9 +5958,8 @@ START_TEST(relative_calibration)
 	if (!libinput_device_config_calibration_has_matrix(dev->libinput_device))
 		return LITEST_NOT_APPLICABLE;
 
-	status = libinput_device_config_calibration_set_matrix(
-							dev->libinput_device,
-							calibration);
+	status = libinput_device_config_calibration_set_matrix(dev->libinput_device,
+							       calibration);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_tablet_proximity_in(dev, 10, 10, axes);
@@ -5788,8 +5969,7 @@ START_TEST(relative_calibration)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	dx = libinput_event_tablet_tool_get_dx(tev);
 	dy = libinput_event_tablet_tool_get_dy(tev);
 	litest_assert(dx < 0.0);
@@ -5805,8 +5985,7 @@ START_TEST(relative_calibration)
 	litest_tablet_motion(dev, 5, 10, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	dx = libinput_event_tablet_tool_get_dx(tev);
 	dy = libinput_event_tablet_tool_get_dy(tev);
 	litest_assert(dx > 0.0);
@@ -5822,8 +6001,7 @@ START_TEST(relative_calibration)
 	litest_tablet_motion(dev, 5, 20, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	dx = libinput_event_tablet_tool_get_dx(tev);
 	dy = libinput_event_tablet_tool_get_dy(tev);
 	litest_assert(dx == 0.0);
@@ -5839,8 +6017,7 @@ START_TEST(relative_calibration)
 	litest_tablet_motion(dev, 5, 5, axes);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	tev = litest_is_tablet_event(event,
-				     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	dx = libinput_event_tablet_tool_get_dx(tev);
 	dy = libinput_event_tablet_tool_get_dy(tev);
 	litest_assert(dx == 0.0);
@@ -5852,7 +6029,7 @@ END_TEST
 static enum litest_device_type
 paired_device(struct litest_device *dev)
 {
-	switch(dev->which) {
+	switch (dev->which) {
 	case LITEST_WACOM_INTUOS5_PEN:
 		return LITEST_WACOM_INTUOS5_FINGER;
 	case LITEST_WACOM_INTUOS5_FINGER:
@@ -5872,11 +6049,8 @@ assert_touch_is_arbitrated(struct litest
 	struct libinput *li = dev->libinput;
 	bool is_touchpad = !libevdev_has_property(finger->evdev, INPUT_PROP_DIRECT);
 	struct axis_replacement axes[] = {
-		{ ABS_TILT_X, 80 },
-		{ ABS_TILT_Y, 80 },
-		{ ABS_DISTANCE, 10 },
-		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ ABS_TILT_X, 80 },  { ABS_TILT_Y, 80 }, { ABS_DISTANCE, 10 },
+		{ ABS_PRESSURE, 0 }, { -1, -1 },
 	};
 
 	litest_tablet_proximity_in(dev, 10, 10, axes);
@@ -5896,22 +6070,19 @@ assert_touch_is_arbitrated(struct litest
 		litest_touch_move(finger, 0, x + i, y + i);
 		litest_tablet_motion(dev, tx + 0.1 * i, ty + 0.1 * i, axes);
 	}
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	litest_tablet_proximity_out(dev);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	litest_timeout_tablet_proxout(li);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 
-	litest_timeout_touch_arbitration();
-	litest_dispatch(li);
+	litest_timeout_touch_arbitration(li);
 
 	/* finger still down */
 	litest_touch_move_to(finger, 0, 80, 80, 30, 30, 10);
 	litest_touch_up(finger, 0);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_touch_arbitration();
-	litest_dispatch(li);
+	litest_timeout_touch_arbitration(li);
 
 	/* lift finger, expect expect events */
 	litest_touch_down(finger, 0, 30, 30);
@@ -5920,8 +6091,7 @@ assert_touch_is_arbitrated(struct litest
 	litest_dispatch(li);
 
 	if (is_touchpad)
-		litest_assert_only_typed_events(li,
-						LIBINPUT_EVENT_POINTER_MOTION);
+		litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 	else
 		litest_assert_touch_sequence(li);
 }
@@ -5945,7 +6115,7 @@ START_TEST(touch_arbitration)
 
 	assert_touch_is_arbitrated(dev, finger);
 
-	litest_delete_device(finger);
+	litest_device_destroy(finger);
 }
 END_TEST
 
@@ -5956,11 +6126,8 @@ START_TEST(touch_arbitration_outside_rec
 	struct litest_device *finger;
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
-		{ ABS_TILT_X, 80 },
-		{ ABS_TILT_Y, 80 },
-		{ ABS_DISTANCE, 10 },
-		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ ABS_TILT_X, 80 },  { ABS_TILT_Y, 80 }, { ABS_DISTANCE, 10 },
+		{ ABS_PRESSURE, 0 }, { -1, -1 },
 	};
 	double x, y;
 	bool is_touchpad;
@@ -5982,6 +6149,7 @@ START_TEST(touch_arbitration_outside_rec
 	/* disable prox-out timer quirk */
 	litest_tablet_proximity_in(dev, x, y - 1, axes);
 	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
 
 	litest_tablet_proximity_in(dev, x, y - 1, axes);
 	litest_drain_events(li);
@@ -6013,6 +6181,7 @@ START_TEST(touch_arbitration_outside_rec
 	x = 20;
 	y = 10;
 	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
 	litest_tablet_motion(dev, x, y, axes);
 	litest_tablet_proximity_in(dev, x, y - 1, axes);
 	litest_drain_events(li);
@@ -6023,7 +6192,7 @@ START_TEST(touch_arbitration_outside_rec
 	litest_assert_touch_sequence(li);
 #endif
 
-	litest_delete_device(finger);
+	litest_device_destroy(finger);
 }
 END_TEST
 
@@ -6034,11 +6203,8 @@ START_TEST(touch_arbitration_remove_afte
 	struct litest_device *finger;
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
-		{ ABS_TILT_X, 80 },
-		{ ABS_TILT_Y, 80 },
-		{ ABS_DISTANCE, 10 },
-		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ ABS_TILT_X, 80 },  { ABS_TILT_Y, 80 }, { ABS_DISTANCE, 10 },
+		{ ABS_PRESSURE, 0 }, { -1, -1 },
 	};
 	bool is_touchpad;
 
@@ -6059,11 +6225,11 @@ START_TEST(touch_arbitration_remove_afte
 	litest_touch_down(finger, 0, 70, 70);
 	litest_drain_events(li);
 	litest_tablet_proximity_out(dev);
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 
 	/* Delete the device immediately after the tablet goes out of prox.
 	 * This merely tests that the arbitration timer gets cleaned up */
-	litest_delete_device(finger);
+	litest_device_destroy(finger);
 }
 END_TEST
 
@@ -6076,7 +6242,7 @@ START_TEST(touch_arbitration_stop_touch)
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	bool is_touchpad;
 
@@ -6094,6 +6260,7 @@ START_TEST(touch_arbitration_stop_touch)
 	/* disable prox-out timer quirk */
 	litest_tablet_proximity_in(dev, 30, 30, axes);
 	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
 	litest_drain_events(li);
 
 	litest_touch_down(finger, 0, 30, 30);
@@ -6119,12 +6286,11 @@ START_TEST(touch_arbitration_stop_touch)
 
 	litest_tablet_motion(dev, 10, 10, axes);
 	litest_tablet_motion(dev, 20, 40, axes);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	litest_tablet_proximity_out(dev);
 	litest_drain_events(li);
 
-	litest_timeout_tablet_proxout();
+	litest_timeout_tablet_proxout(li);
 	litest_drain_events(li);
 
 	/* Finger needs to be lifted for events to happen*/
@@ -6144,12 +6310,11 @@ START_TEST(touch_arbitration_stop_touch)
 	litest_dispatch(li);
 
 	if (is_touchpad)
-		litest_assert_only_typed_events(li,
-						LIBINPUT_EVENT_POINTER_MOTION);
+		litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 	else
 		litest_assert_touch_sequence(li);
 
-	litest_delete_device(finger);
+	litest_device_destroy(finger);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
 }
 END_TEST
@@ -6164,7 +6329,7 @@ START_TEST(touch_arbitration_suspend_tou
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	bool is_touchpad;
 
@@ -6182,12 +6347,18 @@ START_TEST(touch_arbitration_suspend_tou
 	/* we can't force a device suspend, but we can at least make sure
 	   the device doesn't send events */
 	status = libinput_device_config_send_events_set_mode(
-			     dev->libinput_device,
-			     LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+		dev->libinput_device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_drain_events(li);
 
+	/* Disable the proximity timer */
+	litest_tablet_proximity_in(tablet, 12, 12, axes);
+	litest_tablet_proximity_out(tablet);
+	litest_timeout_tablet_proxout(li);
+	litest_drain_events(li);
+
 	litest_tablet_proximity_in(tablet, 10, 10, axes);
 	litest_tablet_motion(tablet, 10, 10, axes);
 	litest_tablet_motion(tablet, 20, 40, axes);
@@ -6199,13 +6370,12 @@ START_TEST(touch_arbitration_suspend_tou
 	litest_assert_empty_queue(li);
 
 	/* Remove tablet device to unpair, still disabled though */
-	litest_delete_device(tablet);
+	litest_device_destroy(tablet);
 	litest_assert_tablet_proximity_event(li,
 					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
 
-	litest_timeout_touch_arbitration();
-	litest_dispatch(li);
+	litest_timeout_touch_arbitration(li);
 
 	litest_touch_down(dev, 0, 30, 30);
 	litest_touch_move_to(dev, 0, 30, 30, 80, 80, 10);
@@ -6219,8 +6389,8 @@ START_TEST(touch_arbitration_suspend_tou
 	litest_assert_empty_queue(li);
 
 	status = libinput_device_config_send_events_set_mode(
-			     dev->libinput_device,
-			     LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
+		dev->libinput_device,
+		LIBINPUT_CONFIG_SEND_EVENTS_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_touch_down(dev, 0, 30, 30);
@@ -6229,8 +6399,7 @@ START_TEST(touch_arbitration_suspend_tou
 	litest_dispatch(li);
 
 	if (is_touchpad)
-		litest_assert_only_typed_events(li,
-						LIBINPUT_EVENT_POINTER_MOTION);
+		litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 	else
 		litest_assert_touch_sequence(li);
 }
@@ -6245,13 +6414,19 @@ START_TEST(touch_arbitration_remove_touc
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	other = paired_device(dev);
 	if (other == LITEST_NO_DEVICE)
 		return LITEST_NOT_APPLICABLE;
 
+	/* Disable the proximity timer */
+	litest_tablet_proximity_in(dev, 12, 12, axes);
+	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
+	litest_drain_events(li);
+
 	finger = litest_add_device(li, other);
 	litest_touch_down(finger, 0, 30, 30);
 	litest_touch_move_to(finger, 0, 30, 30, 80, 80, 10);
@@ -6259,15 +6434,14 @@ START_TEST(touch_arbitration_remove_touc
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_drain_events(li);
 
-	litest_delete_device(finger);
+	litest_device_destroy(finger);
 	litest_dispatch(li);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
 	litest_assert_empty_queue(li);
 
 	litest_tablet_motion(dev, 10, 10, axes);
 	litest_tablet_motion(dev, 20, 40, axes);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 }
 END_TEST
 
@@ -6280,7 +6454,7 @@ START_TEST(touch_arbitration_remove_tabl
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	bool is_touchpad;
 
@@ -6295,6 +6469,12 @@ START_TEST(touch_arbitration_remove_tabl
 	if (is_touchpad)
 		litest_disable_hold_gestures(dev->libinput_device);
 
+	/* Disable the proximity timer */
+	litest_tablet_proximity_in(tablet, 12, 12, axes);
+	litest_tablet_proximity_out(tablet);
+	litest_timeout_tablet_proxout(li);
+	litest_drain_events(li);
+
 	litest_dispatch(li);
 	litest_tablet_proximity_in(tablet, 10, 10, axes);
 	litest_tablet_motion(tablet, 10, 10, axes);
@@ -6305,13 +6485,12 @@ START_TEST(touch_arbitration_remove_tabl
 	litest_touch_move_to(dev, 0, 30, 30, 80, 80, 10);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(tablet);
+	litest_device_destroy(tablet);
 	litest_assert_tablet_proximity_event(li,
-			     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
 
-	litest_timeout_touch_arbitration();
-	litest_dispatch(li);
+	litest_timeout_touch_arbitration(li);
 
 	/* Touch is still down, don't enable */
 	litest_touch_move_to(dev, 0, 80, 80, 30, 30, 10);
@@ -6339,13 +6518,19 @@ START_TEST(touch_arbitration_keep_ignori
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	other = paired_device(tablet);
 	if (other == LITEST_NO_DEVICE)
 		return LITEST_NOT_APPLICABLE;
 
+	/* Disable the proximity timer */
+	litest_tablet_proximity_in(tablet, 12, 12, axes);
+	litest_tablet_proximity_out(tablet);
+	litest_timeout_tablet_proxout(li);
+	litest_drain_events(li);
+
 	finger = litest_add_device(li, other);
 	litest_tablet_proximity_in(tablet, 10, 10, axes);
 	litest_tablet_motion(tablet, 10, 10, axes);
@@ -6355,6 +6540,8 @@ START_TEST(touch_arbitration_keep_ignori
 	litest_drain_events(li);
 
 	litest_tablet_proximity_out(tablet);
+	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 	litest_drain_events(li);
 
 	/* a touch during pen interaction stays a palm after the pen lifts.
@@ -6365,7 +6552,7 @@ START_TEST(touch_arbitration_keep_ignori
 
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(finger);
+	litest_device_destroy(finger);
 }
 END_TEST
 
@@ -6378,7 +6565,7 @@ START_TEST(touch_arbitration_late_touch_
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 	bool is_touchpad;
 
@@ -6386,6 +6573,12 @@ START_TEST(touch_arbitration_late_touch_
 	if (other == LITEST_NO_DEVICE)
 		return LITEST_NOT_APPLICABLE;
 
+	/* Disable the proximity timer */
+	litest_tablet_proximity_in(tablet, 12, 12, axes);
+	litest_tablet_proximity_out(tablet);
+	litest_timeout_tablet_proxout(li);
+	litest_drain_events(li);
+
 	finger = litest_add_device(li, other);
 	is_touchpad = !libevdev_has_property(finger->evdev, INPUT_PROP_DIRECT);
 	if (is_touchpad) {
@@ -6410,13 +6603,11 @@ START_TEST(touch_arbitration_late_touch_
 	 */
 	litest_touch_down(finger, 0, 30, 30);
 	litest_touch_up(finger, 0);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(finger);
+	litest_device_destroy(finger);
 }
 END_TEST
 
@@ -6424,13 +6615,25 @@ START_TEST(touch_arbitration_swap_device
 {
 	struct litest_device *tablet = litest_current_device();
 	struct libinput *li = tablet->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_DISTANCE, 10 },
+		{ ABS_PRESSURE, 0 },
+		{ -1, -1 },
+	};
 
 	enum litest_device_type paired = paired_device(tablet);
 	if (paired == LITEST_NO_DEVICE)
 		return LITEST_NOT_APPLICABLE;
 
+	/* Disable the proximity timer */
+	litest_tablet_proximity_in(tablet, 12, 12, axes);
+	litest_tablet_proximity_out(tablet);
+	litest_timeout_tablet_proxout(li);
+	litest_drain_events(li);
+
 	/* First, add a normal touchscreen */
-	struct litest_device *touchscreen = litest_add_device(li, LITEST_GENERIC_MULTITOUCH_SCREEN);
+	struct litest_device *touchscreen =
+		litest_add_device(li, LITEST_GENERIC_MULTITOUCH_SCREEN);
 	libinput_device_config_gesture_set_hold_enabled(touchscreen->libinput_device,
 							LIBINPUT_CONFIG_HOLD_DISABLED);
 	litest_drain_events(li);
@@ -6443,16 +6646,17 @@ START_TEST(touch_arbitration_swap_device
 	litest_drain_events(li);
 	assert_touch_is_arbitrated(tablet, finger);
 
-	litest_delete_device(touchscreen);
-	litest_delete_device(finger);
+	litest_device_destroy(touchscreen);
+	litest_device_destroy(finger);
 }
 END_TEST
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 static void
 verify_left_handed_tablet_motion(struct litest_device *tablet,
 				 struct libinput *li,
-				 double x, double y,
+				 double x,
+				 double y,
 				 bool left_handed)
 {
 	struct libinput_event *event;
@@ -6466,8 +6670,7 @@ verify_left_handed_tablet_motion(struct
 	}
 
 	event = libinput_get_event(li);
-	t = litest_is_tablet_event(event,
-				   LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 	x = libinput_event_tablet_tool_get_x(t);
 	y = libinput_event_tablet_tool_get_y(t);
 	libinput_event_destroy(event);
@@ -6478,8 +6681,7 @@ verify_left_handed_tablet_motion(struct
 	while (event) {
 		double last_x = x, last_y = y;
 
-		t = litest_is_tablet_event(event,
-					   LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+		t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 		x = libinput_event_tablet_tool_get_x(t);
 		y = libinput_event_tablet_tool_get_y(t);
 
@@ -6511,15 +6713,15 @@ verify_left_handed_tablet_sequence(struc
 	litest_drain_events(li);
 	verify_left_handed_tablet_motion(tablet, li, x, y, left_handed);
 	litest_tablet_proximity_out(tablet);
-	litest_dispatch(li);
-	litest_timeout_tablet_proxout();
+	litest_timeout_tablet_proxout(li);
 	litest_drain_events(li);
 }
 
 static void
 verify_left_handed_touch_motion(struct litest_device *finger,
 				struct libinput *li,
-				double x, double y,
+				double x,
+				double y,
 				bool left_handed)
 {
 	struct libinput_event *event;
@@ -6572,13 +6774,11 @@ verify_left_handed_touch_sequence(struct
 
 START_TEST(tablet_rotation_left_handed)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *tablet = litest_current_device();
 	enum litest_device_type other;
 	struct litest_device *finger;
 	struct libinput *li = tablet->libinput;
-	bool tablet_from, touch_from, tablet_to, touch_to;
-	bool enabled_from, enabled_to;
 
 	other = paired_device(tablet);
 	if (other == LITEST_NO_DEVICE)
@@ -6590,44 +6790,38 @@ START_TEST(tablet_rotation_left_handed)
 	if (libevdev_has_property(finger->evdev, INPUT_PROP_DIRECT))
 		goto out;
 
-	litest_test_param_fetch(test_env->params,
-				"tablet_from", 'b', &tablet_from,
-				"touch_from", 'b', &touch_from,
-				"tablet_to", 'b', &tablet_to,
-				"touch_to", 'b', &touch_to);
+	bool tablet_from = litest_test_param_get_bool(test_env->params, "tablet_from");
+	bool touch_from = litest_test_param_get_bool(test_env->params, "touch_from");
+	bool tablet_to = litest_test_param_get_bool(test_env->params, "tablet_to");
+	bool touch_to = litest_test_param_get_bool(test_env->params, "touch_to");
 
-	enabled_from = tablet_from || touch_from;
-	enabled_to   = tablet_to   || touch_to;
+	bool enabled_from = tablet_from || touch_from;
+	bool enabled_to = tablet_to || touch_to;
 
 	litest_disable_hold_gestures(finger->libinput_device);
-	libinput_device_config_left_handed_set(tablet->libinput_device,
-					       tablet_from);
-	libinput_device_config_left_handed_set(finger->libinput_device,
-					       touch_from);
+	libinput_device_config_left_handed_set(tablet->libinput_device, tablet_from);
+	libinput_device_config_left_handed_set(finger->libinput_device, touch_from);
 	verify_left_handed_tablet_sequence(tablet, li, enabled_from);
 	verify_left_handed_touch_sequence(finger, li, enabled_from);
 
-	libinput_device_config_left_handed_set(tablet->libinput_device,
-					       tablet_to);
-	libinput_device_config_left_handed_set(finger->libinput_device,
-					       touch_to);
+	libinput_device_config_left_handed_set(tablet->libinput_device, tablet_to);
+	libinput_device_config_left_handed_set(finger->libinput_device, touch_to);
 	verify_left_handed_tablet_sequence(tablet, li, enabled_to);
 	verify_left_handed_touch_sequence(finger, li, enabled_to);
 
 out:
-	litest_delete_device(finger);
+	litest_device_destroy(finger);
 #endif
 }
 END_TEST
 
 START_TEST(tablet_rotation_left_handed_configuration)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *tablet = litest_current_device();
 	enum litest_device_type other;
 	struct litest_device *finger;
 	struct libinput *li = tablet->libinput;
-	bool tablet_from, touch_from, tablet_to, touch_to;
 	bool tablet_enabled, touch_enabled;
 	struct libinput_device *tablet_dev, *touch_dev;
 
@@ -6641,11 +6835,10 @@ START_TEST(tablet_rotation_left_handed_c
 	if (libevdev_has_property(finger->evdev, INPUT_PROP_DIRECT))
 		goto out;
 
-	litest_test_param_fetch(test_env->params,
-				"tablet_from", 'b', &tablet_from,
-				"touch_from", 'b', &touch_from,
-				"tablet_to", 'b', &tablet_to,
-				"touch_to", 'b', &touch_to);
+	bool tablet_from = litest_test_param_get_bool(test_env->params, "tablet_from");
+	bool touch_from = litest_test_param_get_bool(test_env->params, "touch_from");
+	bool tablet_to = litest_test_param_get_bool(test_env->params, "tablet_to");
+	bool touch_to = litest_test_param_get_bool(test_env->params, "touch_to");
 
 	tablet_dev = tablet->libinput_device;
 	touch_dev = finger->libinput_device;
@@ -6669,20 +6862,18 @@ START_TEST(tablet_rotation_left_handed_c
 	litest_assert_int_eq(touch_enabled, touch_to);
 
 out:
-	litest_delete_device(finger);
+	litest_device_destroy(finger);
 #endif
 }
 END_TEST
 
 START_TEST(tablet_rotation_left_handed_while_in_prox)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *tablet = litest_current_device();
 	enum litest_device_type other;
 	struct litest_device *finger;
 	struct libinput *li = tablet->libinput;
-	bool tablet_from, touch_from, tablet_to, touch_to;
-	bool enabled_from, enabled_to;
 	double x, y;
 	double tx, ty;
 
@@ -6696,20 +6887,17 @@ START_TEST(tablet_rotation_left_handed_w
 	if (libevdev_has_property(finger->evdev, INPUT_PROP_DIRECT))
 		goto out;
 
-	litest_test_param_fetch(test_env->params,
-				"tablet_from", 'b', &tablet_from,
-				"touch_from", 'b', &touch_from,
-				"tablet_to", 'b', &tablet_to,
-				"touch_to", 'b', &touch_to);
+	bool tablet_from = litest_test_param_get_bool(test_env->params, "tablet_from");
+	bool touch_from = litest_test_param_get_bool(test_env->params, "touch_from");
+	bool tablet_to = litest_test_param_get_bool(test_env->params, "tablet_to");
+	bool touch_to = litest_test_param_get_bool(test_env->params, "touch_to");
 
-	enabled_from = tablet_from || touch_from;
-	enabled_to   = tablet_to   || touch_to;
+	bool enabled_from = tablet_from || touch_from;
+	bool enabled_to = tablet_to || touch_to;
 
 	litest_disable_hold_gestures(finger->libinput_device);
-	libinput_device_config_left_handed_set(finger->libinput_device,
-					       touch_from);
-	libinput_device_config_left_handed_set(tablet->libinput_device,
-					       tablet_from);
+	libinput_device_config_left_handed_set(finger->libinput_device, touch_from);
+	libinput_device_config_left_handed_set(tablet->libinput_device, tablet_from);
 
 	litest_checkpoint("Moving into proximity");
 	tx = 60;
@@ -6719,14 +6907,13 @@ START_TEST(tablet_rotation_left_handed_w
 	litest_drain_events(li);
 
 	litest_checkpoint("Changing tablet to left-handed: %s", truefalse(tablet_to));
-	libinput_device_config_left_handed_set(tablet->libinput_device,
-					       tablet_to);
+	libinput_device_config_left_handed_set(tablet->libinput_device, tablet_to);
 	litest_checkpoint("Changing touch to left-handed: %s", truefalse(touch_to));
-	libinput_device_config_left_handed_set(finger->libinput_device,
-					       touch_to);
+	libinput_device_config_left_handed_set(finger->libinput_device, touch_to);
 
 	/* not yet neutral, so still whatever the original was */
-	litest_checkpoint("Expecting tablet motion with left-handed: %s", truefalse(enabled_from));
+	litest_checkpoint("Expecting tablet motion with left-handed: %s",
+			  truefalse(enabled_from));
 	verify_left_handed_tablet_motion(tablet, li, tx, ty, enabled_from);
 	litest_drain_events(li);
 
@@ -6744,9 +6931,7 @@ START_TEST(tablet_rotation_left_handed_w
 	   trigger the tablet proximity timeout. */
 	for (int i = 0; i < 10; i++) {
 		litest_touch_move(finger, 0, x + i, y - i);
-		litest_tablet_motion(tablet,
-				     tx + 0.1 * i, ty + 0.1 * i,
-				     NULL);
+		litest_tablet_motion(tablet, tx + 0.1 * i, ty + 0.1 * i, NULL);
 	}
 
 	litest_touch_up(finger, 0);
@@ -6757,34 +6942,32 @@ START_TEST(tablet_rotation_left_handed_w
 #endif
 	litest_checkpoint("Moving out of proximity");
 	litest_tablet_proximity_out(tablet);
-	litest_dispatch(li);
-	litest_timeout_tablet_proxout();
+	litest_timeout_tablet_proxout(li);
 	litest_drain_events(li);
 
 	litest_assert_empty_queue(li);
 
 	/* now both should've switched */
-	litest_checkpoint("Expecting tablet motion with left-handed: %s", truefalse(enabled_to));
+	litest_checkpoint("Expecting tablet motion with left-handed: %s",
+			  truefalse(enabled_to));
 	verify_left_handed_tablet_sequence(tablet, li, enabled_to);
-	litest_checkpoint("Expecting touch motion with left-handed: %s", truefalse(enabled_to));
+	litest_checkpoint("Expecting touch motion with left-handed: %s",
+			  truefalse(enabled_to));
 	verify_left_handed_touch_sequence(finger, li, enabled_to);
 
 out:
-	litest_delete_device(finger);
+	litest_device_destroy(finger);
 #endif
 }
 END_TEST
 
 START_TEST(tablet_rotation_left_handed_while_touch_down)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *tablet = litest_current_device();
 	enum litest_device_type other;
 	struct litest_device *finger;
 	struct libinput *li = tablet->libinput;
-	bool tablet_from, touch_from, tablet_to, touch_to;
-	bool enabled_from, enabled_to;
-
 	double x, y;
 
 	other = paired_device(tablet);
@@ -6797,20 +6980,17 @@ START_TEST(tablet_rotation_left_handed_w
 	if (libevdev_has_property(finger->evdev, INPUT_PROP_DIRECT))
 		goto out;
 
-	litest_test_param_fetch(test_env->params,
-				"tablet_from", 'b', &tablet_from,
-				"touch_from", 'b', &touch_from,
-				"tablet_to", 'b', &tablet_to,
-				"touch_to", 'b', &touch_to);
+	bool tablet_from = litest_test_param_get_bool(test_env->params, "tablet_from");
+	bool touch_from = litest_test_param_get_bool(test_env->params, "touch_from");
+	bool tablet_to = litest_test_param_get_bool(test_env->params, "tablet_to");
+	bool touch_to = litest_test_param_get_bool(test_env->params, "touch_to");
 
-	enabled_from = tablet_from || touch_from;
-	enabled_to   = tablet_to   || touch_to;
+	bool enabled_from = tablet_from || touch_from;
+	bool enabled_to = tablet_to || touch_to;
 
 	litest_disable_hold_gestures(finger->libinput_device);
-	libinput_device_config_left_handed_set(finger->libinput_device,
-					       touch_from);
-	libinput_device_config_left_handed_set(tablet->libinput_device,
-					       tablet_from);
+	libinput_device_config_left_handed_set(finger->libinput_device, touch_from);
+	libinput_device_config_left_handed_set(tablet->libinput_device, tablet_from);
 
 	/* Touch down when setting to left-handed */
 	x = 10;
@@ -6819,10 +6999,8 @@ START_TEST(tablet_rotation_left_handed_w
 	litest_dispatch(li);
 	litest_drain_events(li);
 
-	libinput_device_config_left_handed_set(tablet->libinput_device,
-					       tablet_to);
-	libinput_device_config_left_handed_set(finger->libinput_device,
-					       touch_to);
+	libinput_device_config_left_handed_set(tablet->libinput_device, tablet_to);
+	libinput_device_config_left_handed_set(finger->libinput_device, touch_to);
 
 	/* not yet neutral, so still whatever the original was */
 	verify_left_handed_touch_motion(finger, li, x, y, enabled_from);
@@ -6839,38 +7017,34 @@ START_TEST(tablet_rotation_left_handed_w
 	verify_left_handed_touch_sequence(finger, li, enabled_to);
 
 out:
-	litest_delete_device(finger);
+	litest_device_destroy(finger);
 #endif
 }
 END_TEST
 
 START_TEST(tablet_rotation_left_handed_add_touchpad)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *tablet = litest_current_device();
 	enum litest_device_type other;
 	struct litest_device *finger;
 	struct libinput *li = tablet->libinput;
-	bool tablet_from, touch_from, tablet_to, touch_to;
-	bool enabled_from, enabled_to;
 
 	other = paired_device(tablet);
 	if (other == LITEST_NO_DEVICE)
 		return LITEST_NOT_APPLICABLE;
 
-	litest_test_param_fetch(test_env->params,
-				"tablet_from", 'b', &tablet_from,
-				"touch_from", 'b', &touch_from,
-				"tablet_to", 'b', &tablet_to,
-				"touch_to", 'b', &touch_to);
+	bool tablet_from = litest_test_param_get_bool(test_env->params, "tablet_from");
+	bool touch_from = litest_test_param_get_bool(test_env->params, "touch_from");
+	bool tablet_to = litest_test_param_get_bool(test_env->params, "tablet_to");
+	bool touch_to = litest_test_param_get_bool(test_env->params, "touch_to");
 
-	enabled_from = tablet_from || touch_from;
-	enabled_to   = tablet_to   || touch_to;
+	bool enabled_from = tablet_from || touch_from;
+	bool enabled_to = tablet_to || touch_to;
 
 	/* change left-handed before touchpad appears */
 
-	libinput_device_config_left_handed_set(tablet->libinput_device,
-					       tablet_from);
+	libinput_device_config_left_handed_set(tablet->libinput_device, tablet_from);
 
 	finger = litest_add_device(li, other);
 	litest_drain_events(li);
@@ -6878,30 +7052,27 @@ START_TEST(tablet_rotation_left_handed_a
 	if (libevdev_has_property(finger->evdev, INPUT_PROP_DIRECT))
 		goto out;
 
-	libinput_device_config_left_handed_set(finger->libinput_device,
-					       touch_from);
+	libinput_device_config_left_handed_set(finger->libinput_device, touch_from);
 	litest_disable_hold_gestures(finger->libinput_device);
 
 	verify_left_handed_touch_sequence(finger, li, enabled_from);
 	verify_left_handed_tablet_sequence(tablet, li, enabled_from);
 
-	libinput_device_config_left_handed_set(tablet->libinput_device,
-					       tablet_to);
-	libinput_device_config_left_handed_set(finger->libinput_device,
-					       touch_to);
+	libinput_device_config_left_handed_set(tablet->libinput_device, tablet_to);
+	libinput_device_config_left_handed_set(finger->libinput_device, touch_to);
 
 	verify_left_handed_touch_sequence(finger, li, enabled_to);
 	verify_left_handed_tablet_sequence(tablet, li, enabled_to);
 
 out:
-	litest_delete_device(finger);
+	litest_device_destroy(finger);
 #endif
 }
 END_TEST
 
 START_TEST(tablet_rotation_left_handed_add_tablet)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *finger = litest_current_device();
 	enum litest_device_type other;
 	struct litest_device *tablet;
@@ -6918,36 +7089,32 @@ START_TEST(tablet_rotation_left_handed_a
 		return LITEST_NOT_APPLICABLE;
 
 	tablet_from = !!(transition & bit(0));
-	touch_from  = !!(transition & bit(1));
-	tablet_to   = !!(transition & bit(2));
-	touch_to    = !!(transition & bit(3));
+	touch_from = !!(transition & bit(1));
+	tablet_to = !!(transition & bit(2));
+	touch_to = !!(transition & bit(3));
 
 	enabled_from = tablet_from || touch_from;
-	enabled_to   = tablet_to   || touch_to;
+	enabled_to = tablet_to || touch_to;
 
 	/* change left-handed before tablet appears */
-	libinput_device_config_left_handed_set(finger->libinput_device,
-					       touch_from);
+	libinput_device_config_left_handed_set(finger->libinput_device, touch_from);
 	litest_disable_hold_gestures(finger->libinput_device);
 
 	tablet = litest_add_device(li, other);
 	litest_drain_events(li);
 
-	libinput_device_config_left_handed_set(tablet->libinput_device,
-					       tablet_from);
+	libinput_device_config_left_handed_set(tablet->libinput_device, tablet_from);
 
 	verify_left_handed_touch_sequence(finger, li, enabled_from);
 	verify_left_handed_tablet_sequence(tablet, li, enabled_from);
 
-	libinput_device_config_left_handed_set(tablet->libinput_device,
-					       tablet_to);
-	libinput_device_config_left_handed_set(finger->libinput_device,
-					       touch_to);
+	libinput_device_config_left_handed_set(tablet->libinput_device, tablet_to);
+	libinput_device_config_left_handed_set(finger->libinput_device, touch_to);
 
 	verify_left_handed_touch_sequence(finger, li, enabled_to);
 	verify_left_handed_tablet_sequence(tablet, li, enabled_to);
 
-	litest_delete_device(tablet);
+	litest_device_destroy(tablet);
 #endif
 }
 END_TEST
@@ -6957,7 +7124,8 @@ START_TEST(huion_static_btn_tool_pen)
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	int i;
-	bool send_btn_tool = litest_test_param_get_bool(test_env->params, "send-btn-tool");
+	bool send_btn_tool =
+		litest_test_param_get_bool(test_env->params, "send-btn-tool");
 
 	litest_drain_events(li);
 
@@ -6975,14 +7143,12 @@ START_TEST(huion_static_btn_tool_pen)
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
 		litest_dispatch(li);
 	}
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 
 	/* Wait past the timeout to expect a proximity out */
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_timeout_tablet_proxout(li);
 	litest_assert_tablet_proximity_event(li,
-			     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	litest_dispatch(li);
 
 	/* New events should fake a proximity in again */
@@ -6991,7 +7157,7 @@ START_TEST(huion_static_btn_tool_pen)
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 	litest_assert_tablet_proximity_event(li,
-			     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
 	litest_dispatch(li);
 
 	for (i = 0; i < 10; i++) {
@@ -7000,12 +7166,10 @@ START_TEST(huion_static_btn_tool_pen)
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
 		litest_dispatch(li);
 	}
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_TABLET_TOOL_AXIS);
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	litest_timeout_tablet_proxout(li);
 	litest_assert_tablet_proximity_event(li,
-			     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	litest_dispatch(li);
 
 	/* New events, just to ensure cleanup paths are correct */
@@ -7021,7 +7185,8 @@ START_TEST(huion_static_btn_tool_pen_no_
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	int i;
-	bool send_btn_tool = litest_test_param_get_bool(test_env->params, "send-btn-tool");
+	bool send_btn_tool =
+		litest_test_param_get_bool(test_env->params, "send-btn-tool");
 
 	litest_drain_events(li);
 
@@ -7041,12 +7206,11 @@ START_TEST(huion_static_btn_tool_pen_no_
 		litest_dispatch(li);
 		msleep(5);
 	}
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_TABLET_TOOL_AXIS);
-	litest_timeout_tablet_proxout();
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	litest_timeout_tablet_proxout(li);
 	litest_dispatch(li);
 	litest_assert_tablet_proximity_event(li,
-			     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	litest_dispatch(li);
 }
 END_TEST
@@ -7061,7 +7225,8 @@ START_TEST(huion_static_btn_tool_pen_dis
 	 * during proximity out, one where the real BTN_TOOL_PEN is
 	 * triggered after we already triggered the quirk timeout
 	 */
-	bool with_timeout = litest_test_param_get_bool(test_env->params, "btn_tool_pen_timeout");
+	bool with_timeout =
+		litest_test_param_get_bool(test_env->params, "btn_tool_pen_timeout");
 
 	litest_drain_events(li);
 
@@ -7072,23 +7237,23 @@ START_TEST(huion_static_btn_tool_pen_dis
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_drain_events(li);
 
-	for (i = 0; i < 10; i++) {
+	for (i = 0; i < 3; i++) {
 		litest_event(dev, EV_ABS, ABS_X, 20000 + 10 * i);
 		litest_event(dev, EV_ABS, ABS_Y, 20000 - 10 * i);
 		litest_event(dev, EV_SYN, SYN_REPORT, 0);
 		litest_dispatch(li);
 	}
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 
 	/* Wait past the timeout to expect a proximity out */
 	if (with_timeout) {
-		litest_timeout_tablet_proxout();
-		litest_dispatch(li);
-		litest_assert_tablet_proximity_event(li,
-						     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+		litest_timeout_tablet_proxout(li);
+		litest_assert_tablet_proximity_event(
+			li,
+			LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	}
 
+	litest_checkpoint("Sending BTN_TOOL_PEN 0 ");
 	/* Send a real prox out, expect quirk to be disabled */
 	litest_event(dev, EV_KEY, BTN_TOOL_PEN, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
@@ -7098,57 +7263,56 @@ START_TEST(huion_static_btn_tool_pen_dis
 		/* we got the proximity event above already */
 		litest_assert_empty_queue(li);
 	} else {
-		litest_assert_tablet_proximity_event(li,
-						     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+		litest_assert_tablet_proximity_event(
+			li,
+			LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	}
 
 	litest_tablet_proximity_in(dev, 50, 50, NULL);
 	litest_dispatch(li);
 	litest_assert_tablet_proximity_event(li,
-			     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
 
-	for (i = 0; i < 10; i++) {
+	for (i = 0; i < 3; i++) {
 		litest_tablet_motion(dev, 50 + i, 50 + i, NULL);
 		litest_dispatch(li);
 	}
 
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 
-	litest_dispatch(li);
-	litest_timeout_tablet_proxout();
-	litest_dispatch(li);
+	/* Expect the timeout quirk to be disabled */
+	litest_timeout_tablet_proxout(li);
 
 	litest_assert_empty_queue(li);
 
-	litest_push_event_frame(dev);
-	litest_tablet_proximity_out(dev);
-	litest_event(dev, EV_KEY, BTN_TOOL_PEN, 0);
-	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_pop_event_frame(dev);
+	litest_with_event_frame(dev) {
+		litest_tablet_proximity_out(dev);
+		litest_event(dev, EV_KEY, BTN_TOOL_PEN, 0);
+		litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	}
 	litest_dispatch(li);
 
 	litest_assert_tablet_proximity_event(li,
-			     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	litest_assert_empty_queue(li);
 }
 END_TEST
 
 START_TEST(tablet_smoothing)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	double x, y;
 	struct point {
 		double x, y;
-	} coordinates[100] = {0};
+	} coordinates[100] = { 0 };
 	size_t npoints = 0;
 	size_t idx = 0;
 	struct axis_replacement axes[] = {
 		{ ABS_DISTANCE, 10 },
 		{ ABS_PRESSURE, 0 },
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_drain_events(li);
@@ -7169,8 +7333,7 @@ START_TEST(tablet_smoothing)
 		litest_dispatch(li);
 
 		event = libinput_get_event(li);
-		tev = litest_is_tablet_event(event,
-					     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+		tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 		p->x = libinput_event_tablet_tool_get_x(tev);
 		p->y = libinput_event_tablet_tool_get_y(tev);
 
@@ -7178,6 +7341,7 @@ START_TEST(tablet_smoothing)
 	}
 
 	litest_tablet_proximity_out(dev);
+	litest_timeout_tablet_proxout(li);
 	litest_tablet_proximity_in(dev, 10, 10, axes);
 	litest_dispatch(li);
 	litest_drain_events(li);
@@ -7202,8 +7366,7 @@ START_TEST(tablet_smoothing)
 		litest_tablet_motion(dev, x, y, axes);
 		litest_dispatch(li);
 		event = libinput_get_event(li);
-		tev = litest_is_tablet_event(event,
-					     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+		tev = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
 		ex = libinput_event_tablet_tool_get_x(tev);
 		ey = libinput_event_tablet_tool_get_y(tev);
 
@@ -7216,8 +7379,352 @@ START_TEST(tablet_smoothing)
 }
 END_TEST
 
+START_TEST(tablet_eraser_button_disabled)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_DISTANCE, 10 },
+		{ ABS_PRESSURE, 0 },
+		{ -1, -1 },
+	};
+	struct axis_replacement tip_down_axes[] = {
+		{ ABS_DISTANCE, 0 },
+		{ ABS_PRESSURE, 30 },
+		{ -1, -1 },
+	};
+	struct libinput_event *ev;
+	struct libinput_event_tablet_tool *tev;
+	struct libinput_tablet_tool *tool, *pen;
+	bool with_tip_down =
+		litest_test_param_get_bool(test_env->params, "with-tip-down");
+	bool configure_while_out_of_prox =
+		litest_test_param_get_bool(test_env->params,
+					   "configure-while-out-of-prox");
+	bool down_when_in_prox =
+		litest_test_param_get_bool(test_env->params, "down-when-in-prox");
+	bool down_when_out_of_prox =
+		litest_test_param_get_bool(test_env->params, "down-when-out-of-prox");
+	bool with_motion_events =
+		litest_test_param_get_bool(test_env->params, "with-motion-events");
+
+	if (!libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_RUBBER))
+		return LITEST_NOT_APPLICABLE;
+
+	/* Device forces BTN_TOOL_PEN on tip */
+	if (with_tip_down && dev->which == LITEST_WACOM_ISDV4_524C_PEN)
+		return LITEST_NOT_APPLICABLE;
+
+	litest_log_group("Prox in/out to disable proximity timer") {
+		litest_tablet_proximity_in(dev, 25, 25, axes);
+		litest_tablet_proximity_out(dev);
+		litest_timeout_tablet_proxout(li);
+
+		litest_checkpoint(
+			"Eraser prox in/out to force-disable config on broken tablets");
+		litest_tablet_set_tool_type(dev, BTN_TOOL_RUBBER);
+		litest_tablet_proximity_in(dev, 25, 25, axes);
+		litest_tablet_proximity_out(dev);
+		litest_timeout_tablet_proxout(li);
+	}
+
+	litest_drain_events(li);
+
+	litest_log_group("Proximity in for pen") {
+		litest_tablet_set_tool_type(dev, BTN_TOOL_PEN);
+		litest_tablet_proximity_in(dev, 20, 20, axes);
+		litest_dispatch(li);
+		_destroy_(libinput_event) *ev = libinput_get_event(li);
+		tev = litest_is_proximity_event(
+			ev,
+			LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+		tool = libinput_event_tablet_tool_get_tool(tev);
+		litest_assert_enum_eq(libinput_tablet_tool_get_type(tool),
+				      LIBINPUT_TABLET_TOOL_TYPE_PEN);
+		pen = libinput_tablet_tool_ref(tool);
+	}
+
+	if (!libinput_tablet_tool_config_eraser_button_get_modes(tool)) {
+		libinput_tablet_tool_unref(tool);
+		return LITEST_NOT_APPLICABLE;
+	}
+
+	unsigned int expected_button = BTN_STYLUS3;
+	if (!libinput_tablet_tool_has_button(pen, BTN_STYLUS))
+		expected_button = BTN_STYLUS;
+	else if (!libinput_tablet_tool_has_button(pen, BTN_STYLUS2))
+		expected_button = BTN_STYLUS2;
+	else if (!libinput_tablet_tool_has_button(pen, BTN_STYLUS3))
+		expected_button = BTN_STYLUS3;
+	litest_checkpoint("expected button from now on: %s (%d)",
+			  libevdev_event_code_get_name(EV_KEY, expected_button),
+			  expected_button);
+
+	if (!configure_while_out_of_prox) {
+		litest_log_group("Configuring eraser button while in of proximity") {
+			auto status =
+				libinput_tablet_tool_config_eraser_button_set_mode(
+					tool,
+					LIBINPUT_CONFIG_ERASER_BUTTON_BUTTON);
+			if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
+				return LITEST_NOT_APPLICABLE;
+			litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+		}
+	}
+
+	litest_log_group("Prox out to apply changed settings") {
+		litest_tablet_proximity_out(dev);
+		litest_timeout_tablet_proxout(li);
+		litest_drain_events(li);
+	}
+
+	if (configure_while_out_of_prox) {
+		litest_log_group("Configuring eraser button while out of proximity") {
+			auto status =
+				libinput_tablet_tool_config_eraser_button_set_mode(
+					tool,
+					LIBINPUT_CONFIG_ERASER_BUTTON_BUTTON);
+			if (status == LIBINPUT_CONFIG_STATUS_UNSUPPORTED)
+				return LITEST_NOT_APPLICABLE;
+			litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
+		}
+	}
+
+	litest_mark_test_start();
+
+	if (down_when_in_prox) {
+		litest_log_group("Prox in with eraser, expecting eraser button event") {
+			litest_tablet_set_tool_type(dev, BTN_TOOL_RUBBER);
+			litest_tablet_proximity_in(dev, 10, 10, axes);
+			litest_wait_for_event(li);
+			ev = libinput_get_event(li);
+			tev = litest_is_proximity_event(
+				ev,
+				LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+			litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev),
+					     pen);
+			libinput_event_destroy(ev);
+			litest_drain_events_of_type(li,
+						    LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+			ev = libinput_get_event(li);
+			tev = litest_is_tablet_event(ev,
+						     LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
+			litest_assert_enum_eq(
+				libinput_event_tablet_tool_get_button_state(tev),
+				LIBINPUT_BUTTON_STATE_PRESSED);
+			litest_assert_int_eq(libinput_event_tablet_tool_get_button(tev),
+					     expected_button);
+			litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev),
+					     pen);
+			libinput_event_destroy(ev);
+		}
+	} else {
+		litest_tablet_proximity_in(dev, 10, 10, axes);
+	}
+
+	litest_drain_events(li);
+
+	if (with_motion_events) {
+		for (int i = 0; i < 3; i++) {
+			litest_tablet_motion(dev, 11 + i, 11 + i, axes);
+			litest_dispatch(li);
+		}
+		ev = libinput_get_event(li);
+		do {
+			tev = litest_is_tablet_event(ev,
+						     LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+			litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev),
+					     pen);
+			libinput_event_destroy(ev);
+			ev = libinput_get_event(li);
+		} while (ev);
+	}
+
+	if (with_tip_down) {
+		litest_tablet_tip_down(dev, 11, 11, tip_down_axes);
+		litest_dispatch(li);
+		litest_assert_tablet_tip_event(li, LIBINPUT_TABLET_TOOL_TIP_DOWN);
+
+		if (with_motion_events) {
+			for (int i = 0; i < 3; i++) {
+				litest_tablet_motion(dev,
+						     11 + i,
+						     11 + i,
+						     tip_down_axes);
+				litest_dispatch(li);
+			}
+			ev = libinput_get_event(li);
+			do {
+				tev = litest_is_tablet_event(
+					ev,
+					LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+				litest_assert_ptr_eq(
+					libinput_event_tablet_tool_get_tool(tev),
+					pen);
+				libinput_event_destroy(ev);
+				ev = libinput_get_event(li);
+			} while (ev);
+		}
+	}
+
+	/* Make sure the button still works as-is */
+	if (libinput_tablet_tool_has_button(pen, BTN_STYLUS)) {
+		litest_log_group("Testing BTN_STYLUS") {
+			litest_event(dev, EV_KEY, BTN_STYLUS, 1);
+			litest_event(dev, EV_SYN, SYN_REPORT, 0);
+			litest_dispatch(li);
+			litest_event(dev, EV_KEY, BTN_STYLUS, 0);
+			litest_event(dev, EV_SYN, SYN_REPORT, 0);
+			litest_dispatch(li);
+			litest_assert_tablet_button_event(
+				li,
+				BTN_STYLUS,
+				LIBINPUT_BUTTON_STATE_PRESSED);
+			litest_assert_tablet_button_event(
+				li,
+				BTN_STYLUS,
+				LIBINPUT_BUTTON_STATE_RELEASED);
+		}
+	}
+
+	litest_dispatch(li);
+
+	if (!down_when_in_prox) {
+		litest_log_group("Prox out for the pen ...") {
+			litest_with_event_frame(dev) {
+				litest_tablet_set_tool_type(dev, BTN_TOOL_PEN);
+				if (with_tip_down)
+					litest_tablet_tip_up(dev, 11, 11, axes);
+				litest_tablet_proximity_out(dev);
+			}
+			litest_dispatch(li);
+		}
+
+		litest_log_group("...and prox in for the eraser") {
+			litest_with_event_frame(dev) {
+				litest_tablet_set_tool_type(dev, BTN_TOOL_RUBBER);
+				if (with_tip_down) {
+					litest_tablet_tip_down(dev,
+							       12,
+							       12,
+							       tip_down_axes);
+					litest_tablet_proximity_in(dev,
+								   12,
+								   12,
+								   tip_down_axes);
+				} else {
+					litest_tablet_proximity_in(dev, 12, 12, axes);
+				}
+			}
+			litest_dispatch(li);
+		}
+
+		litest_drain_events_of_type(li, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+
+		litest_log_group("Expect button event") {
+			ev = libinput_get_event(li);
+			tev = litest_is_tablet_event(ev,
+						     LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
+			litest_assert_enum_eq(
+				libinput_event_tablet_tool_get_button_state(tev),
+				LIBINPUT_BUTTON_STATE_PRESSED);
+			litest_assert_int_eq(libinput_event_tablet_tool_get_button(tev),
+					     expected_button);
+			litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev),
+					     pen);
+			libinput_event_destroy(ev);
+		}
+	}
+
+	if (!down_when_out_of_prox) {
+		litest_log_group("Prox out for the eraser...") {
+			litest_with_event_frame(dev) {
+				if (with_tip_down)
+					litest_tablet_tip_up(dev, 11, 11, axes);
+				litest_tablet_proximity_out(dev);
+			}
+			litest_dispatch(li);
+		}
+
+		litest_log_group("...and prox in for the pen") {
+			litest_with_event_frame(dev) {
+				litest_tablet_set_tool_type(dev, BTN_TOOL_PEN);
+				if (with_tip_down) {
+					litest_tablet_tip_down(dev,
+							       12,
+							       12,
+							       tip_down_axes);
+					litest_tablet_proximity_in(dev,
+								   12,
+								   12,
+								   tip_down_axes);
+				} else {
+					litest_tablet_proximity_in(dev, 12, 12, axes);
+				}
+			}
+			litest_dispatch(li);
+		}
+
+		litest_drain_events_of_type(li, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+
+		litest_log_group("Expect button event") {
+			ev = libinput_get_event(li);
+			tev = litest_is_tablet_event(ev,
+						     LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
+			litest_assert_int_eq(libinput_event_tablet_tool_get_button(tev),
+					     expected_button);
+			litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev),
+					     pen);
+			libinput_event_destroy(ev);
+		}
+	}
+
+	litest_log_group("Real prox out for the %s",
+			 down_when_out_of_prox ? "eraser" : "pen") {
+		litest_with_event_frame(dev) {
+			if (with_tip_down)
+				litest_tablet_tip_up(dev, 12, 12, axes);
+			litest_tablet_proximity_out(dev);
+		}
+		litest_dispatch(li);
+		litest_timeout_tablet_proxout(li);
+	}
+
+	litest_drain_events_of_type(li, LIBINPUT_EVENT_TABLET_TOOL_AXIS);
+
+	if (down_when_out_of_prox) {
+		litest_log_group("Expect button release") {
+			ev = libinput_get_event(li);
+			tev = litest_is_tablet_event(ev,
+						     LIBINPUT_EVENT_TABLET_TOOL_BUTTON);
+			litest_assert_int_eq(libinput_event_tablet_tool_get_button(tev),
+					     expected_button);
+			litest_assert_ptr_eq(libinput_event_tablet_tool_get_tool(tev),
+					     pen);
+			libinput_event_destroy(ev);
+		}
+	}
+
+	if (with_tip_down)
+		litest_assert_tablet_tip_event(li, LIBINPUT_TABLET_TOOL_TIP_UP);
+
+	litest_log_group("Expect final prox out for the pen") {
+		ev = libinput_get_event(li);
+		tev = litest_is_proximity_event(
+			ev,
+			LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+		tool = libinput_event_tablet_tool_get_tool(tev);
+		litest_assert_ptr_eq(pen, tool);
+		libinput_event_destroy(ev);
+	}
+
+	libinput_tablet_tool_unref(pen);
+}
+END_TEST
+
 TEST_COLLECTION(tablet)
 {
+	/* clang-format off */
 	litest_add(tool_ref, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY);
 	litest_add(tool_user_data, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY);
 	litest_add(tool_capability, LITEST_TABLET, LITEST_ANY);
@@ -7238,41 +7745,10 @@ TEST_COLLECTION(tablet)
 	litest_add_no_device(tools_with_serials);
 	litest_add_no_device(tools_without_serials);
 	litest_add_for_device(tool_delayed_serial, LITEST_WACOM_HID4800_PEN);
-	litest_add(proximity_out_clear_buttons, LITEST_TABLET, LITEST_FORCED_PROXOUT);
-	litest_add(proximity_in_out, LITEST_TABLET, LITEST_ANY);
-	litest_add(proximity_in_button_down, LITEST_TABLET, LITEST_ANY);
-	litest_add(proximity_out_button_up, LITEST_TABLET, LITEST_ANY);
-	litest_add(proximity_has_axes, LITEST_TABLET, LITEST_ANY);
-	litest_add(bad_distance_events, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
-	litest_add(proximity_range_enter, LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_MOUSE, LITEST_ANY);
-	litest_add(proximity_range_in_out, LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_MOUSE, LITEST_ANY);
-	litest_add(proximity_range_button_click, LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_MOUSE, LITEST_ANY);
-	litest_add(proximity_range_button_press, LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_MOUSE, LITEST_ANY);
-	litest_add(proximity_range_button_release, LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_MOUSE, LITEST_ANY);
-	litest_add(proximity_out_slow_event, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
-	litest_add(proximity_out_not_during_contact, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
-	litest_add(proximity_out_not_during_buttonpress, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
-	litest_add(proximity_out_disables_forced, LITEST_TABLET, LITEST_FORCED_PROXOUT|LITEST_TOTEM);
-	litest_add(proximity_out_disables_forced_after_forced, LITEST_TABLET, LITEST_FORCED_PROXOUT|LITEST_TOTEM);
 	litest_add_no_device(proximity_out_on_delete);
 	litest_add(button_down_up, LITEST_TABLET, LITEST_ANY);
 	litest_add(button_seat_count, LITEST_TABLET, LITEST_ANY);
 	litest_add_no_device(button_up_on_delete);
-	litest_add(tip_down_up, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
-	litest_add(tip_down_prox_in, LITEST_TABLET, LITEST_ANY);
-	litest_add(tip_up_prox_out, LITEST_TABLET, LITEST_ANY);
-	litest_add(tip_down_btn_change, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
-	litest_add(tip_up_btn_change, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
-	litest_add(tip_down_motion, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
-	litest_add(tip_up_motion, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
-	litest_add(tip_down_up_eraser, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
-	litest_with_parameters(params, "axis", 'I', 2, litest_named_i32(ABS_X), litest_named_i32(ABS_Y)) {
-		litest_add_parametrized(tip_up_motion_one_axis, LITEST_TABLET|LITEST_HOVER, LITEST_ANY, params);
-	}
-	litest_add(tip_state_proximity, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
-	litest_add(tip_state_axis, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
-	litest_add(tip_state_button, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
-	litest_add_no_device(tip_up_on_delete);
 	litest_add(motion, LITEST_TABLET, LITEST_ANY);
 	litest_add(motion_event_state, LITEST_TABLET, LITEST_ANY);
 	litest_add_for_device(motion_outside_bounds, LITEST_WACOM_CINTIQ_24HD_PEN);
@@ -7291,6 +7767,7 @@ TEST_COLLECTION(tablet)
 	litest_add(mouse_buttons, LITEST_TABLET | LITEST_TOOL_MOUSE, LITEST_ANY);
 	litest_add(mouse_rotation, LITEST_TABLET | LITEST_TOOL_MOUSE, LITEST_ANY);
 	litest_add(mouse_wheel, LITEST_TABLET | LITEST_TOOL_MOUSE, LITEST_WHEEL);
+
 	litest_add(airbrush_tool, LITEST_TABLET, LITEST_ANY);
 	litest_add(airbrush_slider, LITEST_TABLET, LITEST_ANY);
 	litest_add(artpen_tool, LITEST_TABLET, LITEST_ANY);
@@ -7339,6 +7816,8 @@ TEST_COLLECTION(tablet)
 	litest_add(tablet_pressure_config_set_minimum, LITEST_TABLET, LITEST_TOTEM);
 	litest_add(tablet_pressure_config_set_maximum, LITEST_TABLET, LITEST_TOTEM);
 	litest_add(tablet_pressure_config_set_range, LITEST_TABLET, LITEST_TOTEM);
+	litest_add(tablet_pressure_config_resets_offset, LITEST_TABLET, LITEST_TOTEM);
+	litest_add_for_device(tablet_pressure_config_01_does_not_reset_offset, LITEST_WACOM_HID4800_PEN);
 
 	litest_add_for_device(tablet_distance_range, LITEST_WACOM_INTUOS5_PEN);
 
@@ -7369,10 +7848,12 @@ TEST_COLLECTION(tablet)
 	}
 
 	litest_add_for_device(tablet_smoothing, LITEST_WACOM_HID4800_PEN);
+	/* clang-format on */
 }
 
 TEST_COLLECTION(tablet_left_handed)
 {
+	/* clang-format off */
 	litest_add_for_device(left_handed, LITEST_WACOM_INTUOS5_PEN);
 	litest_add_for_device(left_handed_tilt, LITEST_WACOM_INTUOS5_PEN);
 	litest_add_for_device(left_handed_mouse_rotation, LITEST_WACOM_INTUOS5_PEN);
@@ -7391,4 +7872,63 @@ TEST_COLLECTION(tablet_left_handed)
 		litest_add_parametrized(tablet_rotation_left_handed_add_touchpad, LITEST_TABLET, LITEST_ANY, params);
 		litest_add_parametrized(tablet_rotation_left_handed_add_tablet, LITEST_TOUCHPAD, LITEST_ANY, params);
 	}
+	/* clang-format on */
+}
+
+TEST_COLLECTION(tablet_eraser)
+{
+	/* clang-format off */
+	litest_with_parameters(params,
+			       "with-tip-down", 'b',
+			       "configure-while-out-of-prox", 'b',
+			       "down-when-in-prox", 'b',
+			       "down-when-out-of-prox", 'b',
+			       "with-motion-events", 'b') {
+		litest_add_parametrized(tablet_eraser_button_disabled, LITEST_TABLET, LITEST_TOTEM|LITEST_FORCED_PROXOUT, params);
+	}
+	/* clang-format on */
+}
+
+TEST_COLLECTION(tablet_proximity)
+{
+	/* clang-format off */
+	litest_add(proximity_out_clear_buttons, LITEST_TABLET, LITEST_FORCED_PROXOUT);
+	litest_add(proximity_in_out, LITEST_TABLET, LITEST_ANY);
+	litest_add(proximity_in_button_down, LITEST_TABLET, LITEST_ANY);
+	litest_add(proximity_out_button_up, LITEST_TABLET, LITEST_ANY);
+	litest_add(proximity_has_axes, LITEST_TABLET, LITEST_ANY);
+	litest_add(bad_distance_events, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
+	litest_add(proximity_range_enter, LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_MOUSE, LITEST_ANY);
+	litest_add(proximity_range_in_out, LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_MOUSE, LITEST_ANY);
+	litest_add(proximity_range_button_click, LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_MOUSE, LITEST_ANY);
+	litest_add(proximity_range_button_press, LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_MOUSE, LITEST_ANY);
+	litest_add(proximity_range_button_release, LITEST_TABLET | LITEST_DISTANCE | LITEST_TOOL_MOUSE, LITEST_ANY);
+	litest_add(proximity_out_slow_event, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
+	litest_add(proximity_out_not_during_contact, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
+	litest_add(proximity_out_not_during_buttonpress, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY);
+	litest_add(proximity_out_disables_forced, LITEST_TABLET, LITEST_FORCED_PROXOUT|LITEST_TOTEM);
+	litest_add(proximity_out_disables_forced_after_forced, LITEST_TABLET, LITEST_FORCED_PROXOUT|LITEST_TOTEM);
+	litest_add(proximity_forced_in_with_eraser_swap, LITEST_TABLET, LITEST_FORCED_PROXOUT|LITEST_TOTEM);
+	/* clang-format on */
+}
+
+TEST_COLLECTION(tablet_tip)
+{
+	/* clang-format off */
+	litest_add(tip_down_up, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
+	litest_add(tip_down_prox_in, LITEST_TABLET, LITEST_ANY);
+	litest_add(tip_up_prox_out, LITEST_TABLET, LITEST_ANY);
+	litest_add(tip_down_btn_change, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
+	litest_add(tip_up_btn_change, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
+	litest_add(tip_down_motion, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
+	litest_add(tip_up_motion, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
+	litest_add(tip_down_up_eraser, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
+	litest_with_parameters(params, "axis", 'I', 2, litest_named_i32(ABS_X), litest_named_i32(ABS_Y)) {
+		litest_add_parametrized(tip_up_motion_one_axis, LITEST_TABLET|LITEST_HOVER, LITEST_ANY, params);
+	}
+	litest_add(tip_state_proximity, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
+	litest_add(tip_state_axis, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
+	litest_add(tip_state_button, LITEST_TABLET|LITEST_HOVER, LITEST_ANY);
+	litest_add_no_device(tip_up_on_delete);
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-totem.c 1.30.0-1/test/test-totem.c
--- 1.28.1-1/test/test-totem.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-totem.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,13 +26,14 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <libinput.h>
-#include <unistd.h>
-#include <stdbool.h>
 #include <stdarg.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include "util-input-event.h"
 
 #include "libinput-util.h"
 #include "litest.h"
-#include "util-input-event.h"
 
 START_TEST(totem_type)
 {
@@ -48,12 +49,11 @@ START_TEST(totem_type)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	t = litest_is_tablet_event(event,
-				   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(t);
 
 	litest_assert_enum_eq(libinput_tablet_tool_get_type(tool),
-			 LIBINPUT_TABLET_TOOL_TYPE_TOTEM);
+			      LIBINPUT_TABLET_TOOL_TYPE_TOTEM);
 	libinput_event_destroy(event);
 }
 END_TEST
@@ -72,8 +72,7 @@ START_TEST(totem_axes)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	t = litest_is_tablet_event(event,
-				   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	tool = libinput_event_tablet_tool_get_tool(t);
 
 	litest_assert(libinput_tablet_tool_has_rotation(tool));
@@ -97,17 +96,15 @@ START_TEST(totem_proximity_in_out)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	t = litest_is_tablet_event(event,
-				   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_proximity_state(t),
-			 LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+			      LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
 	libinput_event_destroy(event);
 
 	event = libinput_get_event(li);
-	t = litest_is_tablet_event(event,
-				   LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(t),
-			 LIBINPUT_TABLET_TOOL_TIP_DOWN);
+			      LIBINPUT_TABLET_TOOL_TIP_DOWN);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
@@ -115,17 +112,15 @@ START_TEST(totem_proximity_in_out)
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	t = litest_is_tablet_event(event,
-				   LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(t),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	libinput_event_destroy(event);
 
 	event = libinput_get_event(li);
-	t = litest_is_tablet_event(event,
-				   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_proximity_state(t),
-			 LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+			      LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	libinput_event_destroy(event);
 }
 END_TEST
@@ -133,7 +128,6 @@ END_TEST
 START_TEST(totem_proximity_in_on_init)
 {
 	struct litest_device *dev = litest_current_device();
-	struct libinput *li;
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *t;
 	const char *devnode;
@@ -142,15 +136,15 @@ START_TEST(totem_proximity_in_on_init)
 	const struct input_absinfo *abs;
 
 	abs = libevdev_get_abs_info(dev->evdev, ABS_MT_POSITION_X);
-	w = absinfo_range(abs)/abs->resolution;
+	w = absinfo_range(abs) / abs->resolution;
 	abs = libevdev_get_abs_info(dev->evdev, ABS_MT_POSITION_Y);
-	h = absinfo_range(abs)/abs->resolution;
+	h = absinfo_range(abs) / abs->resolution;
 
 	litest_tablet_proximity_in(dev, 50, 50, NULL);
 
 	/* for simplicity, we create a new litest context */
 	devnode = libevdev_uinput_get_devnode(dev->uinput);
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	libinput_path_add_device(li, devnode);
 	litest_dispatch(li);
 
@@ -159,52 +153,47 @@ START_TEST(totem_proximity_in_on_init)
 	libinput_event_destroy(event);
 
 	event = libinput_get_event(li);
-	t = litest_is_tablet_event(event,
-				   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_proximity_state(t),
-			 LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+			      LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
 	x = libinput_event_tablet_tool_get_x(t);
 	y = libinput_event_tablet_tool_get_y(t);
 
-	litest_assert_double_gt(x, w/2 - 1);
-	litest_assert_double_lt(x, w/2 + 1);
-	litest_assert_double_gt(y, h/2 - 1);
-	litest_assert_double_lt(y, h/2 + 1);
+	litest_assert_double_gt(x, w / 2 - 1);
+	litest_assert_double_lt(x, w / 2 + 1);
+	litest_assert_double_gt(y, h / 2 - 1);
+	litest_assert_double_lt(y, h / 2 + 1);
 
 	libinput_event_destroy(event);
 
 	event = libinput_get_event(li);
-	t = litest_is_tablet_event(event,
-				   LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(t),
-			 LIBINPUT_TABLET_TOOL_TIP_DOWN);
+			      LIBINPUT_TABLET_TOOL_TIP_DOWN);
 	x = libinput_event_tablet_tool_get_x(t);
 	y = libinput_event_tablet_tool_get_y(t);
 
-	litest_assert_double_gt(x, w/2 - 1);
-	litest_assert_double_lt(x, w/2 + 1);
-	litest_assert_double_gt(y, h/2 - 1);
-	litest_assert_double_lt(y, h/2 + 1);
+	litest_assert_double_gt(x, w / 2 - 1);
+	litest_assert_double_lt(x, w / 2 + 1);
+	litest_assert_double_gt(y, h / 2 - 1);
+	litest_assert_double_lt(y, h / 2 + 1);
 
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
-
-	litest_destroy_context(li);
 }
 END_TEST
 
 START_TEST(totem_proximity_out_on_suspend)
 {
 	struct litest_device *dev = litest_current_device();
-	struct libinput *li;
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *t;
 	const char *devnode;
 
 	/* for simplicity, we create a new litest context */
 	devnode = libevdev_uinput_get_devnode(dev->uinput);
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	libinput_path_add_device(li, devnode);
 
 	litest_tablet_proximity_in(dev, 50, 50, NULL);
@@ -214,21 +203,18 @@ START_TEST(totem_proximity_out_on_suspen
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	t = litest_is_tablet_event(event,
-				   LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(t),
-			 LIBINPUT_TABLET_TOOL_TIP_UP);
+			      LIBINPUT_TABLET_TOOL_TIP_UP);
 	libinput_event_destroy(event);
 
 	event = libinput_get_event(li);
-	t = litest_is_tablet_event(event,
-				   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_proximity_state(t),
-			 LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
+			      LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
 	libinput_event_destroy(event);
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -277,7 +263,7 @@ START_TEST(totem_rotation)
 	double r, old_r;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_ORIENTATION, 50 }, /* mid-point is 0 */
-		{ -1, -1 }
+		{ -1, -1 },
 	};
 
 	litest_tablet_proximity_in(dev, 50, 50, axes);
@@ -376,9 +362,9 @@ START_TEST(totem_button)
 	litest_assert_int_eq(libinput_event_tablet_tool_get_button(t),
 			     (unsigned int)BTN_0);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_button_state(t),
-			 LIBINPUT_BUTTON_STATE_PRESSED);
+			      LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(t),
-			 LIBINPUT_TABLET_TOOL_TIP_DOWN);
+			      LIBINPUT_TABLET_TOOL_TIP_DOWN);
 	libinput_event_destroy(event);
 
 	litest_button_click(dev, BTN_0, false);
@@ -389,9 +375,9 @@ START_TEST(totem_button)
 	litest_assert_int_eq(libinput_event_tablet_tool_get_button(t),
 			     (unsigned int)BTN_0);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_button_state(t),
-			 LIBINPUT_BUTTON_STATE_RELEASED);
+			      LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(t),
-			 LIBINPUT_TABLET_TOOL_TIP_DOWN);
+			      LIBINPUT_TABLET_TOOL_TIP_DOWN);
 	libinput_event_destroy(event);
 }
 END_TEST
@@ -399,7 +385,6 @@ END_TEST
 START_TEST(totem_button_down_on_init)
 {
 	struct litest_device *dev = litest_current_device();
-	struct libinput *li;
 	struct libinput_event *event;
 	struct libinput_event_tablet_tool *t;
 	const char *devnode;
@@ -409,7 +394,7 @@ START_TEST(totem_button_down_on_init)
 
 	/* for simplicity, we create a new litest context */
 	devnode = libevdev_uinput_get_devnode(dev->uinput);
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	libinput_path_add_device(li, devnode);
 	litest_dispatch(li);
 
@@ -418,18 +403,16 @@ START_TEST(totem_button_down_on_init)
 	libinput_event_destroy(event);
 
 	event = libinput_get_event(li);
-	t = litest_is_tablet_event(event,
-				   LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
+	t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_proximity_state(t),
-			 LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+			      LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
 
 	libinput_event_destroy(event);
 
 	event = libinput_get_event(li);
-	t = litest_is_tablet_event(event,
-				   LIBINPUT_EVENT_TABLET_TOOL_TIP);
+	t = litest_is_tablet_event(event, LIBINPUT_EVENT_TABLET_TOOL_TIP);
 	litest_assert_enum_eq(libinput_event_tablet_tool_get_tip_state(t),
-			 LIBINPUT_TABLET_TOOL_TIP_DOWN);
+			      LIBINPUT_TABLET_TOOL_TIP_DOWN);
 
 	libinput_event_destroy(event);
 
@@ -447,16 +430,13 @@ START_TEST(totem_button_down_on_init)
 	litest_button_click(dev, BTN_0, false);
 	litest_dispatch(li);
 	litest_assert_tablet_button_event(li, BTN_0, LIBINPUT_BUTTON_STATE_RELEASED);
-
-	litest_destroy_context(li);
 }
 END_TEST
 
 START_TEST(totem_button_up_on_delete)
 {
-	struct libinput *li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	struct litest_device *dev = litest_add_device(li, LITEST_DELL_CANVAS_TOTEM);
-	struct libevdev *evdev = libevdev_new();
 
 	litest_tablet_proximity_in(dev, 10, 10, NULL);
 	litest_drain_events(li);
@@ -464,18 +444,14 @@ START_TEST(totem_button_up_on_delete)
 	litest_button_click(dev, BTN_0, true);
 	litest_drain_events(li);
 
-	litest_delete_device(dev);
+	litest_device_destroy(dev);
 	litest_dispatch(li);
 
-	litest_assert_tablet_button_event(li,
-					  BTN_0,
-					  LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_tablet_button_event(li, BTN_0, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_tablet_tip_event(li, LIBINPUT_TABLET_TOOL_TIP_UP);
 	litest_assert_tablet_proximity_event(li,
 					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT);
-	libevdev_free(evdev);
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -501,7 +477,8 @@ START_TEST(totem_arbitration_below)
 	litest_tablet_proximity_in(totem, 50, 70, NULL);
 	litest_dispatch(li);
 
-	litest_assert_tablet_proximity_event(li, LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+	litest_assert_tablet_proximity_event(li,
+					     LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
 	litest_assert_tablet_tip_event(li, LIBINPUT_TABLET_TOOL_TIP_DOWN);
 	litest_assert_touch_cancel(li);
 
@@ -514,7 +491,7 @@ START_TEST(totem_arbitration_below)
 	litest_touch_up(touch, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(touch);
+	litest_device_destroy(touch);
 }
 END_TEST
 
@@ -540,7 +517,7 @@ START_TEST(totem_arbitration_during)
 		litest_assert_empty_queue(li);
 	}
 
-	litest_delete_device(touch);
+	litest_device_destroy(touch);
 }
 END_TEST
 
@@ -575,12 +552,13 @@ START_TEST(totem_arbitration_outside_rec
 
 	litest_assert_touch_sequence(li);
 
-	litest_delete_device(touch);
+	litest_device_destroy(touch);
 }
 END_TEST
 
 TEST_COLLECTION(totem)
 {
+	/* clang-format off */
 	litest_add(totem_type, LITEST_TOTEM, LITEST_ANY);
 	litest_add(totem_axes, LITEST_TOTEM, LITEST_ANY);
 	litest_add(totem_proximity_in_out, LITEST_TOTEM, LITEST_ANY);
@@ -597,4 +575,5 @@ TEST_COLLECTION(totem)
 	litest_add(totem_arbitration_below, LITEST_TOTEM, LITEST_ANY);
 	litest_add(totem_arbitration_during, LITEST_TOTEM, LITEST_ANY);
 	litest_add(totem_arbitration_outside_rect, LITEST_TOTEM, LITEST_ANY);
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-touch.c 1.30.0-1/test/test-touch.c
--- 1.28.1-1/test/test-touch.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-touch.c	2025-11-25 03:40:43.000000000 +0000
@@ -25,8 +25,8 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <libinput.h>
 #include <libevdev/libevdev.h>
+#include <libinput.h>
 #include <unistd.h>
 
 #include "libinput-util.h"
@@ -101,7 +101,9 @@ START_TEST(touch_abs_transform)
 
 	dev = litest_create_device_with_overrides(LITEST_WACOM_ISDV4_E6_FINGER,
 						  "litest Highres touch device",
-						  NULL, abs, NULL);
+						  NULL,
+						  abs,
+						  NULL);
 
 	libinput = dev->libinput;
 
@@ -130,7 +132,7 @@ START_TEST(touch_abs_transform)
 
 	litest_assert(tested);
 
-	litest_delete_device(dev);
+	litest_device_destroy(dev);
 }
 END_TEST
 
@@ -199,7 +201,7 @@ START_TEST(touch_seat_slot)
 	litest_touch_up(dev1, 1);
 	touch_assert_seat_slot(li, LIBINPUT_EVENT_TOUCH_UP, 1, 3);
 
-	litest_delete_device(dev2);
+	litest_device_destroy(dev2);
 }
 END_TEST
 
@@ -220,7 +222,9 @@ START_TEST(touch_many_slots)
 
 	dev = litest_create_device_with_overrides(LITEST_WACOM_ISDV4_E6_FINGER,
 						  "litest Multi-touch device",
-						  NULL, abs, NULL);
+						  NULL,
+						  abs,
+						  NULL);
 	libinput = dev->libinput;
 
 	for (slot = 0; slot < num_tps; ++slot)
@@ -257,7 +261,7 @@ START_TEST(touch_many_slots)
 
 	litest_assert_int_eq(slot_count, 0);
 
-	litest_delete_device(dev);
+	litest_device_destroy(dev);
 }
 END_TEST
 
@@ -312,10 +316,7 @@ START_TEST(touch_calibration_scale)
 	struct litest_device *dev;
 	struct libinput_event *ev;
 	struct libinput_event_touch *tev;
-	float matrix[6] = {
-		1, 0, 0,
-		0, 1, 0
-	};
+	float matrix[6] = { 1, 0, 0, 0, 1, 0 };
 
 	float calibration;
 	double x, y;
@@ -366,7 +367,7 @@ START_TEST(touch_calibration_rotation)
 	li = dev->libinput;
 
 	for (i = 0; i < 4; i++) {
-		float angle = i * M_PI/2;
+		float angle = i * M_PI / 2;
 
 		/* [ cos -sin  tx ]
 		   [ sin  cos  ty ]
@@ -376,7 +377,7 @@ START_TEST(touch_calibration_rotation)
 		matrix[3] = sin(angle);
 		matrix[4] = cos(angle);
 
-		switch(i) {
+		switch (i) {
 		case 0: /* 0 deg */
 			matrix[2] = 0;
 			matrix[5] = 0;
@@ -409,7 +410,7 @@ START_TEST(touch_calibration_rotation)
 		y = libinput_event_touch_get_y_transformed(tev, height);
 
 		/* rounding errors... */
-		switch(i) {
+		switch (i) {
 		case 0: /* 0 deg */
 			litest_assert_double_eq_epsilon(x, width * 0.8, 1.0);
 			litest_assert_double_eq_epsilon(y, height * 0.2, 1.0);
@@ -440,10 +441,7 @@ START_TEST(touch_calibration_translation
 	struct litest_device *dev;
 	struct libinput_event *ev;
 	struct libinput_event_touch *tev;
-	float matrix[6] = {
-		1, 0, 0,
-		0, 1, 0
-	};
+	float matrix[6] = { 1, 0, 0, 0, 1, 0 };
 
 	float translate;
 	double x, y;
@@ -508,8 +506,8 @@ END_TEST
 START_TEST(touch_calibration_config)
 {
 	struct litest_device *dev = litest_current_device();
-	float identity[6] = {1, 0, 0, 0, 1, 0};
-	float nonidentity[6] = {1, 2, 3, 4, 5, 6};
+	float identity[6] = { 1, 0, 0, 0, 1, 0 };
+	float nonidentity[6] = { 1, 2, 3, 4, 5, 6 };
 	float matrix[6];
 	enum libinput_config_status status;
 	int rc;
@@ -519,27 +517,34 @@ START_TEST(touch_calibration_config)
 
 	/* Twice so we have every to-fro combination */
 	for (int i = 0; i < 2; i++) {
-		status = libinput_device_config_calibration_set_matrix(dev->libinput_device, identity);
+		status = libinput_device_config_calibration_set_matrix(
+			dev->libinput_device,
+			identity);
 		litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
-		libinput_device_config_calibration_get_matrix(dev->libinput_device, matrix);
+		libinput_device_config_calibration_get_matrix(dev->libinput_device,
+							      matrix);
 		litest_assert_int_eq(memcmp(matrix, identity, sizeof(matrix)), 0);
 
-		status = libinput_device_config_calibration_set_matrix(dev->libinput_device, nonidentity);
+		status = libinput_device_config_calibration_set_matrix(
+			dev->libinput_device,
+			nonidentity);
 		litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
-		libinput_device_config_calibration_get_matrix(dev->libinput_device, matrix);
+		libinput_device_config_calibration_get_matrix(dev->libinput_device,
+							      matrix);
 		litest_assert_int_eq(memcmp(matrix, nonidentity, sizeof(matrix)), 0);
 	}
-
 }
 END_TEST
 
-static int open_restricted(const char *path, int flags, void *data)
+static int
+open_restricted(const char *path, int flags, void *data)
 {
 	int fd;
 	fd = open(path, flags);
 	return fd < 0 ? -errno : fd;
 }
-static void close_restricted(int fd, void *data)
+static void
+close_restricted(int fd, void *data)
 {
 	close(fd);
 }
@@ -570,8 +575,7 @@ START_TEST(touch_calibrated_screen_udev)
 	while ((ev = libinput_get_event(li))) {
 		struct libinput_device *d;
 
-		if (libinput_event_get_type(ev) !=
-		    LIBINPUT_EVENT_DEVICE_ADDED) {
+		if (libinput_event_get_type(ev) != LIBINPUT_EVENT_DEVICE_ADDED) {
 			libinput_event_destroy(ev);
 			continue;
 		}
@@ -606,7 +610,6 @@ START_TEST(touch_calibrated_screen_udev)
 
 	libinput_unref(li);
 	udev_unref(udev);
-
 }
 END_TEST
 
@@ -643,13 +646,13 @@ START_TEST(fake_mt_exists)
 	event = libinput_get_event(li);
 	device = libinput_event_get_device(event);
 
-	litest_assert(!libinput_device_has_capability(device,
-						  LIBINPUT_DEVICE_CAP_TOUCH));
+	litest_assert(
+		!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TOUCH));
 
 	/* This test may need fixing if we add other fake-mt devices that
 	 * have different capabilities */
-	litest_assert(libinput_device_has_capability(device,
-						 LIBINPUT_DEVICE_CAP_POINTER));
+	litest_assert(
+		libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER));
 
 	libinput_event_destroy(event);
 }
@@ -673,8 +676,7 @@ START_TEST(fake_mt_no_touch_events)
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
 
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE);
 }
 END_TEST
 
@@ -686,8 +688,8 @@ START_TEST(touch_protocol_a_init)
 
 	litest_wait_for_event(li);
 
-	litest_assert(libinput_device_has_capability(device,
-						 LIBINPUT_DEVICE_CAP_TOUCH));
+	litest_assert(
+		libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TOUCH));
 }
 END_TEST
 
@@ -720,8 +722,7 @@ START_TEST(touch_protocol_a_touch)
 	litest_dispatch(li);
 
 	while ((ev = libinput_get_event(li))) {
-		if (libinput_event_get_type(ev) ==
-		    LIBINPUT_EVENT_TOUCH_FRAME) {
+		if (libinput_event_get_type(ev) == LIBINPUT_EVENT_TOUCH_FRAME) {
 			libinput_event_destroy(ev);
 			continue;
 		}
@@ -810,7 +811,7 @@ END_TEST
 START_TEST(touch_initial_state)
 {
 	struct litest_device *dev;
-	struct libinput *libinput1, *libinput2;
+	struct libinput *libinput1;
 	struct libinput_event *ev1 = NULL;
 	struct libinput_event *ev2 = NULL;
 	struct libinput_event_touch *t1, *t2;
@@ -819,8 +820,7 @@ START_TEST(touch_initial_state)
 
 	dev = litest_current_device();
 	device1 = dev->libinput_device;
-	libinput_device_config_tap_set_enabled(device1,
-					       LIBINPUT_CONFIG_TAP_DISABLED);
+	libinput_device_config_tap_set_enabled(device1, LIBINPUT_CONFIG_TAP_DISABLED);
 
 	libinput1 = dev->libinput;
 	litest_touch_down(dev, 0, 40, 60);
@@ -829,12 +829,10 @@ START_TEST(touch_initial_state)
 	/* device is now on some x/y value */
 	litest_drain_events(libinput1);
 
-	libinput2 = litest_create_context();
+	_litest_context_destroy_ struct libinput *libinput2 = litest_create_context();
 	device2 = libinput_path_add_device(libinput2,
-					   libevdev_uinput_get_devnode(
-							       dev->uinput));
-	libinput_device_config_tap_set_enabled(device2,
-					       LIBINPUT_CONFIG_TAP_DISABLED);
+					   libevdev_uinput_get_devnode(dev->uinput));
+	libinput_device_config_tap_set_enabled(device2, LIBINPUT_CONFIG_TAP_DISABLED);
 	litest_drain_events(libinput2);
 
 	if (axis == ABS_X)
@@ -854,7 +852,7 @@ START_TEST(touch_initial_state)
 		t2 = litest_is_touch_event(ev2, 0);
 
 		litest_assert_int_eq(libinput_event_get_type(ev1),
-				 libinput_event_get_type(ev2));
+				     libinput_event_get_type(ev2));
 
 		if (libinput_event_get_type(ev1) == LIBINPUT_EVENT_TOUCH_UP ||
 		    libinput_event_get_type(ev1) == LIBINPUT_EVENT_TOUCH_FRAME)
@@ -873,8 +871,6 @@ START_TEST(touch_initial_state)
 
 	libinput_event_destroy(ev1);
 	libinput_event_destroy(ev2);
-
-	litest_destroy_context(libinput2);
 }
 END_TEST
 
@@ -895,7 +891,7 @@ START_TEST(touch_time_usec)
 	tev = litest_is_touch_event(event, LIBINPUT_EVENT_TOUCH_DOWN);
 	time_usec = libinput_event_touch_get_time_usec(tev);
 	litest_assert_int_eq(libinput_event_touch_get_time(tev),
-			 (uint32_t) (time_usec / 1000));
+			     (uint32_t)(time_usec / 1000));
 	libinput_event_destroy(event);
 }
 END_TEST
@@ -972,10 +968,9 @@ END_TEST
 START_TEST(touch_release_on_unplug)
 {
 	struct litest_device *dev;
-	struct libinput *li;
 	struct libinput_event *ev;
 
-	li = litest_create_context();
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
 	dev = litest_add_device(li, LITEST_GENERIC_MULTITOUCH_SCREEN);
 	litest_drain_events(li);
 
@@ -984,7 +979,7 @@ START_TEST(touch_release_on_unplug)
 	litest_drain_events(li);
 
 	/* Touch is still down when device is removed, expect a release */
-	litest_delete_device(dev);
+	litest_device_destroy(dev);
 	litest_dispatch(li);
 
 	ev = libinput_get_event(li);
@@ -998,8 +993,6 @@ START_TEST(touch_release_on_unplug)
 	ev = libinput_get_event(li);
 	litest_assert_event_type(ev, LIBINPUT_EVENT_DEVICE_REMOVED);
 	libinput_event_destroy(ev);
-
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -1097,16 +1090,17 @@ START_TEST(touch_count_mt)
 	struct libevdev *evdev = dev->evdev;
 
 	litest_assert_int_eq(libinput_device_touch_get_touch_count(device),
-			 libevdev_get_num_slots(evdev));
+			     libevdev_get_num_slots(evdev));
 }
 END_TEST
 
-START_TEST(touch_count_unknown)
+START_TEST(touch_count_mtdev)
 {
 	struct litest_device *dev = litest_current_device();
 	struct libinput_device *device = dev->libinput_device;
 
-	litest_assert_int_eq(libinput_device_touch_get_touch_count(device), 0);
+	/* We hardcode this to 10 */
+	litest_assert_int_eq(libinput_device_touch_get_touch_count(device), 10);
 }
 END_TEST
 
@@ -1131,7 +1125,7 @@ START_TEST(touch_palm_detect_tool_palm)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_TOOL_TYPE, MT_TOOL_PALM },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touch_has_tool_palm(dev))
@@ -1158,7 +1152,7 @@ START_TEST(touch_palm_detect_tool_palm_o
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_TOOL_TYPE, MT_TOOL_PALM },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touch_has_tool_palm(dev))
@@ -1189,7 +1183,7 @@ START_TEST(touch_palm_detect_tool_palm_k
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_TOOL_TYPE, MT_TOOL_PALM },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touch_has_tool_palm(dev))
@@ -1220,7 +1214,7 @@ START_TEST(touch_palm_detect_tool_palm_2
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_TOOL_TYPE, MT_TOOL_PALM },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touch_has_tool_palm(dev))
@@ -1256,7 +1250,7 @@ START_TEST(touch_palm_detect_tool_palm_o
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_TOOL_TYPE, MT_TOOL_PALM },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touch_has_tool_palm(dev))
@@ -1300,7 +1294,7 @@ START_TEST(touch_palm_detect_tool_palm_k
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_TOOL_TYPE, MT_TOOL_PALM },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touch_has_tool_palm(dev))
@@ -1336,6 +1330,7 @@ END_TEST
 
 TEST_COLLECTION(touch)
 {
+	/* clang-format off */
 	litest_add(touch_frame_events, LITEST_TOUCH, LITEST_ANY);
 	litest_add(touch_downup_no_motion, LITEST_TOUCH, LITEST_ANY);
 	litest_add(touch_downup_no_motion, LITEST_SINGLE_TOUCH, LITEST_TOUCHPAD);
@@ -1358,9 +1353,11 @@ TEST_COLLECTION(touch)
 	litest_add(fake_mt_exists, LITEST_FAKE_MT, LITEST_ANY);
 	litest_add(fake_mt_no_touch_events, LITEST_FAKE_MT, LITEST_ANY);
 
+#ifdef HAVE_MTDEV
 	litest_add(touch_protocol_a_init, LITEST_PROTOCOL_A, LITEST_ANY);
 	litest_add(touch_protocol_a_touch, LITEST_PROTOCOL_A, LITEST_ANY);
 	litest_add(touch_protocol_a_2fg_touch, LITEST_PROTOCOL_A, LITEST_ANY);
+#endif
 
 	litest_with_parameters(params, "axis", 'I', 2, litest_named_i32(ABS_X), litest_named_i32(ABS_Y)) {
 		litest_add_parametrized(touch_initial_state, LITEST_TOUCH, LITEST_PROTOCOL_A, params);
@@ -1378,8 +1375,10 @@ TEST_COLLECTION(touch)
 
 	litest_add(touch_count_st, LITEST_SINGLE_TOUCH, LITEST_TOUCHPAD);
 	litest_add(touch_count_mt, LITEST_TOUCH, LITEST_SINGLE_TOUCH|LITEST_PROTOCOL_A);
-	litest_add(touch_count_unknown, LITEST_PROTOCOL_A, LITEST_ANY);
+#ifdef HAVE_MTDEV
+	litest_add(touch_count_mtdev, LITEST_PROTOCOL_A, LITEST_ANY);
 	litest_add(touch_count_invalid, LITEST_ANY, LITEST_TOUCH|LITEST_SINGLE_TOUCH|LITEST_PROTOCOL_A);
+#endif
 
 	litest_add(touch_palm_detect_tool_palm, LITEST_TOUCH, LITEST_ANY);
 	litest_add(touch_palm_detect_tool_palm_on_off, LITEST_TOUCH, LITEST_ANY);
@@ -1387,4 +1386,5 @@ TEST_COLLECTION(touch)
 	litest_add(touch_palm_detect_tool_palm_2fg, LITEST_TOUCH, LITEST_SINGLE_TOUCH);
 	litest_add(touch_palm_detect_tool_palm_on_off_2fg, LITEST_TOUCH, LITEST_SINGLE_TOUCH);
 	litest_add(touch_palm_detect_tool_palm_keep_type_2fg, LITEST_TOUCH, LITEST_ANY);
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-touchpad-buttons.c 1.30.0-1/test/test-touchpad-buttons.c
--- 1.28.1-1/test/test-touchpad-buttons.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-touchpad-buttons.c	2025-11-25 03:40:43.000000000 +0000
@@ -43,14 +43,10 @@ START_TEST(touchpad_button)
 
 	litest_button_click(dev, BTN_LEFT, true);
 	litest_dispatch(li);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_button_click(dev, BTN_LEFT, false);
 	litest_dispatch(li);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -72,11 +68,13 @@ START_TEST(touchpad_click_defaults_click
 	method = libinput_device_config_click_get_default_method(device);
 	litest_assert_enum_eq(method, LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
 
-	status = libinput_device_config_click_set_method(device,
-							 LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS);
+	status = libinput_device_config_click_set_method(
+		device,
+		LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
-	status = libinput_device_config_click_set_method(device,
-							 LIBINPUT_CONFIG_CLICK_METHOD_NONE);
+	status = libinput_device_config_click_set_method(
+		device,
+		LIBINPUT_CONFIG_CLICK_METHOD_NONE);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 }
 END_TEST
@@ -104,13 +102,15 @@ START_TEST(touchpad_click_set_clickfinge
 	map = LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM;
 	status = libinput_device_config_click_set_clickfinger_button_map(device, map);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
-	map = libinput_device_config_click_get_clickfinger_button_map(dev->libinput_device);
+	map = libinput_device_config_click_get_clickfinger_button_map(
+		dev->libinput_device);
 	litest_assert_enum_eq(map, LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM);
 
 	map = LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR;
 	status = libinput_device_config_click_set_clickfinger_button_map(device, map);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
-	map = libinput_device_config_click_get_clickfinger_button_map(dev->libinput_device);
+	map = libinput_device_config_click_get_clickfinger_button_map(
+		dev->libinput_device);
 	litest_assert_enum_eq(map, LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR);
 
 	map = LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM - 1;
@@ -141,11 +141,13 @@ START_TEST(touchpad_click_defaults_btnar
 	method = libinput_device_config_click_get_default_method(device);
 	litest_assert_enum_eq(method, LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS);
 
-	status = libinput_device_config_click_set_method(device,
-							 LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
+	status = libinput_device_config_click_set_method(
+		device,
+		LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
-	status = libinput_device_config_click_set_method(device,
-							 LIBINPUT_CONFIG_CLICK_METHOD_NONE);
+	status = libinput_device_config_click_set_method(
+		device,
+		LIBINPUT_CONFIG_CLICK_METHOD_NONE);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 }
 END_TEST
@@ -171,11 +173,13 @@ START_TEST(touchpad_click_defaults_none)
 	method = libinput_device_config_click_get_default_method(device);
 	litest_assert_enum_eq(method, LIBINPUT_CONFIG_CLICK_METHOD_NONE);
 
-	status = libinput_device_config_click_set_method(device,
-							 LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
+	status = libinput_device_config_click_set_method(
+		device,
+		LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
-	status = libinput_device_config_click_set_method(device,
-							 LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS);
+	status = libinput_device_config_click_set_method(
+		device,
+		LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 }
 END_TEST
@@ -198,10 +202,8 @@ START_TEST(touchpad_1fg_clickfinger)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -227,10 +229,8 @@ START_TEST(touchpad_1fg_clickfinger_no_t
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -259,7 +259,8 @@ START_TEST(touchpad_2fg_clickfinger)
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	unsigned int button = 0;
-	enum libinput_config_clickfinger_button_map map = litest_test_param_get_i32(test_env->params, "map");
+	enum libinput_config_clickfinger_button_map map =
+		litest_test_param_get_i32(test_env->params, "map");
 
 	litest_enable_clickfinger(dev);
 	litest_set_clickfinger_map(dev, map);
@@ -288,10 +289,8 @@ START_TEST(touchpad_2fg_clickfinger)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -301,7 +300,8 @@ START_TEST(touchpad_3fg_clickfinger)
 	struct libinput *li = dev->libinput;
 	unsigned int button = 0;
 
-	enum libinput_config_clickfinger_button_map map = litest_test_param_get_i32(test_env->params, "map");
+	enum libinput_config_clickfinger_button_map map =
+		litest_test_param_get_i32(test_env->params, "map");
 
 	if (litest_slot_count(dev) < 3)
 		return LITEST_NOT_APPLICABLE;
@@ -335,12 +335,8 @@ START_TEST(touchpad_3fg_clickfinger)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -349,7 +345,8 @@ START_TEST(touchpad_3fg_clickfinger_btnt
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	unsigned int button = 0;
-	enum libinput_config_clickfinger_button_map map = litest_test_param_get_i32(test_env->params, "map");
+	enum libinput_config_clickfinger_button_map map =
+		litest_test_param_get_i32(test_env->params, "map");
 
 	if (litest_slot_count(dev) >= 3 ||
 	    !libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_TRIPLETAP))
@@ -388,12 +385,8 @@ START_TEST(touchpad_3fg_clickfinger_btnt
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -503,10 +496,10 @@ START_TEST(touchpad_2fg_clickfinger_dist
 	double w, h;
 	bool small_touchpad = false;
 	unsigned int expected_button = 0;
-	enum libinput_config_clickfinger_button_map map = litest_test_param_get_i32(test_env->params, "map");
+	enum libinput_config_clickfinger_button_map map =
+		litest_test_param_get_i32(test_env->params, "map");
 
-	if (libinput_device_get_size(dev->libinput_device, &w, &h) == 0 &&
-	    h < 50.0)
+	if (libinput_device_get_size(dev->libinput_device, &w, &h) == 0 && h < 50.0)
 		small_touchpad = true;
 
 	litest_enable_clickfinger(dev);
@@ -523,12 +516,8 @@ START_TEST(touchpad_2fg_clickfinger_dist
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 
@@ -555,12 +544,8 @@ START_TEST(touchpad_2fg_clickfinger_dist
 	else
 		expected_button = BTN_LEFT;
 
-	litest_assert_button_event(li,
-				   expected_button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   expected_button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, expected_button, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, expected_button, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -569,7 +554,8 @@ START_TEST(touchpad_3fg_clickfinger_dist
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	unsigned int button = 0;
-	enum libinput_config_clickfinger_button_map map = litest_test_param_get_i32(test_env->params, "map");
+	enum libinput_config_clickfinger_button_map map =
+		litest_test_param_get_i32(test_env->params, "map");
 
 	if (litest_slot_count(dev) < 3)
 		return LITEST_NOT_APPLICABLE;
@@ -602,12 +588,8 @@ START_TEST(touchpad_3fg_clickfinger_dist
 	litest_touch_up(dev, 1);
 	litest_touch_up(dev, 2);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -616,7 +598,8 @@ START_TEST(touchpad_3fg_clickfinger_dist
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	unsigned int button = 0;
-	enum libinput_config_clickfinger_button_map map = litest_test_param_get_i32(test_env->params, "map");
+	enum libinput_config_clickfinger_button_map map =
+		litest_test_param_get_i32(test_env->params, "map");
 
 	if (litest_slot_count(dev) > 2)
 		return LITEST_NOT_APPLICABLE;
@@ -654,12 +637,8 @@ START_TEST(touchpad_3fg_clickfinger_dist
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -671,8 +650,9 @@ START_TEST(touchpad_2fg_clickfinger_bott
 	/* this test is run for the T440s touchpad only, makes getting the
 	 * mm correct easier */
 
-	libinput_device_config_click_set_method(dev->libinput_device,
-						LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
+	libinput_device_config_click_set_method(
+		dev->libinput_device,
+		LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
 	litest_drain_events(li);
 
 	/* one above, one below the magic line, vert spread ca 27mm */
@@ -685,12 +665,8 @@ START_TEST(touchpad_2fg_clickfinger_bott
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 
@@ -704,12 +680,8 @@ START_TEST(touchpad_2fg_clickfinger_bott
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	/* one above, one below the magic line, vert spread 17mm */
 	litest_touch_down(dev, 0, 50, 75);
@@ -721,12 +693,8 @@ START_TEST(touchpad_2fg_clickfinger_bott
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -747,10 +715,8 @@ START_TEST(touchpad_clickfinger_to_area_
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_enable_clickfinger(dev);
 
@@ -765,11 +731,8 @@ START_TEST(touchpad_clickfinger_to_area_
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -786,8 +749,7 @@ START_TEST(touchpad_clickfinger_to_area_
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_enable_clickfinger(dev);
 
@@ -796,8 +758,7 @@ START_TEST(touchpad_clickfinger_to_area_
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_drain_events(li);
 
@@ -810,11 +771,8 @@ START_TEST(touchpad_clickfinger_to_area_
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -836,10 +794,8 @@ START_TEST(touchpad_area_to_clickfinger_
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_enable_buttonareas(dev);
 
@@ -851,11 +807,8 @@ START_TEST(touchpad_area_to_clickfinger_
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -872,8 +825,7 @@ START_TEST(touchpad_area_to_clickfinger_
 	litest_touch_down(dev, 0, 95, 95);
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_enable_buttonareas(dev);
 
@@ -882,8 +834,7 @@ START_TEST(touchpad_area_to_clickfinger_
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_touch_down(dev, 0, 95, 95);
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
@@ -893,11 +844,8 @@ START_TEST(touchpad_area_to_clickfinger_
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -924,10 +872,8 @@ START_TEST(touchpad_clickfinger_3fg_tool
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -952,12 +898,8 @@ START_TEST(touchpad_clickfinger_4fg_tool
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -975,11 +917,13 @@ START_TEST(touchpad_clickfinger_appletou
 	method = libinput_device_config_click_get_method(device);
 	litest_assert_enum_eq(method, LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
 
-	status = libinput_device_config_click_set_method(device,
-							 LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS);
+	status = libinput_device_config_click_set_method(
+		device,
+		LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
-	status = libinput_device_config_click_set_method(device,
-							 LIBINPUT_CONFIG_CLICK_METHOD_NONE);
+	status = libinput_device_config_click_set_method(
+		device,
+		LIBINPUT_CONFIG_CLICK_METHOD_NONE);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 }
 END_TEST
@@ -1002,10 +946,8 @@ START_TEST(touchpad_clickfinger_appletou
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -1029,10 +971,8 @@ START_TEST(touchpad_clickfinger_appletou
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -1058,10 +998,8 @@ START_TEST(touchpad_clickfinger_appletou
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -1101,8 +1039,7 @@ START_TEST(touchpad_clickfinger_click_dr
 	litest_button_click(dev, BTN_LEFT, true);
 
 	litest_dispatch(li);
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	for (int i = 0; i < 20; i++) {
 		litest_push_event_frame(dev);
@@ -1126,8 +1063,7 @@ START_TEST(touchpad_clickfinger_click_dr
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_button_click(dev, BTN_LEFT, false);
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	if (nfingers > 3) {
 		if (nslots > 3) {
@@ -1167,10 +1103,8 @@ START_TEST(touchpad_btn_left)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -1206,8 +1140,7 @@ START_TEST(clickpad_click_n_drag)
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
 	litest_dispatch(li);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_assert_empty_queue(li);
 
@@ -1222,8 +1155,7 @@ START_TEST(clickpad_click_n_drag)
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -1244,7 +1176,7 @@ START_TEST(clickpad_finger_pin)
 	if (libinput_device_get_size(dev->libinput_device, &w, &h) != 0)
 		return LITEST_NOT_APPLICABLE;
 
-	dist = 100.0/max(w, h);
+	dist = 100.0 / max(w, h);
 
 	litest_drain_events(li);
 
@@ -1292,17 +1224,13 @@ START_TEST(clickpad_softbutton_left)
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_dispatch(li);
 
@@ -1321,17 +1249,13 @@ START_TEST(clickpad_softbutton_middle)
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_dispatch(li);
 
@@ -1350,17 +1274,13 @@ START_TEST(clickpad_softbutton_right)
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-			    LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-			    LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_dispatch(li);
 
@@ -1388,24 +1308,16 @@ START_TEST(clickpad_softbutton_left_tap_
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-			    LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-			    LIBINPUT_BUTTON_STATE_RELEASED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-			    LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_empty_queue(li);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-			    LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -1430,24 +1342,16 @@ START_TEST(clickpad_softbutton_right_tap
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_empty_queue(li);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -1475,16 +1379,12 @@ START_TEST(clickpad_softbutton_left_1st_
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_empty_queue(li);
 
 	/* move out of the area, then wait for softbutton timer */
 	litest_touch_move_to(dev, 0, 20, 90, 50, 50, 20);
-	litest_dispatch(li);
-	litest_timeout_softbuttons();
-	litest_dispatch(li);
+	litest_timeout_softbuttons(li);
 	litest_drain_events(li);
 
 	/* move down left, expect motion */
@@ -1512,16 +1412,14 @@ START_TEST(clickpad_softbutton_left_1st_
 		event = libinput_get_event(li);
 	}
 
-	litest_assert(x/nevents < 0);
-	litest_assert(y/nevents > 0);
+	litest_assert(x / nevents < 0);
+	litest_assert(y / nevents > 0);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1547,9 +1445,7 @@ START_TEST(clickpad_softbutton_left_2nd_
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_empty_queue(li);
 
 	litest_touch_down(dev, 1, 20, 20);
@@ -1612,9 +1508,7 @@ START_TEST(clickpad_softbutton_left_2nd_
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1639,18 +1533,14 @@ START_TEST(clickpad_softbutton_left_to_r
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_empty_queue(li);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1675,18 +1565,14 @@ START_TEST(clickpad_softbutton_right_to_
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_empty_queue(li);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1709,17 +1595,13 @@ START_TEST(clickpad_softbutton_hover_int
 	litest_button_click(dev, BTN_LEFT, true);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_empty_queue(li);
 
 	litest_button_click(dev, BTN_LEFT, false);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -1734,18 +1616,14 @@ START_TEST(clickpad_topsoftbuttons_left)
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_empty_queue(li);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1762,18 +1640,14 @@ START_TEST(clickpad_topsoftbuttons_right
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_empty_queue(li);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1790,18 +1664,14 @@ START_TEST(clickpad_topsoftbuttons_middl
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_empty_queue(li);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1821,9 +1691,7 @@ START_TEST(clickpad_topsoftbuttons_move_
 	litest_drain_events(li);
 
 	litest_touch_down(dev, 0, 80, 5);
-	litest_dispatch(li);
-	litest_timeout_softbuttons();
-	litest_dispatch(li);
+	litest_timeout_softbuttons(li);
 	litest_assert_empty_queue(li);
 
 	litest_touch_move_to(dev, 0, 80, 5, 80, 90, 20);
@@ -1855,15 +1723,11 @@ START_TEST(clickpad_topsoftbuttons_move_
 	litest_drain_events(li);
 
 	litest_touch_down(dev, 0, 80, 5);
-	litest_dispatch(li);
-	litest_timeout_softbuttons();
-	litest_dispatch(li);
+	litest_timeout_softbuttons(li);
 	litest_assert_empty_queue(li);
 
 	litest_touch_move_to(dev, 0, 80, 5, 80, 90, 20);
-	litest_dispatch(li);
-	litest_timeout_softbuttons();
-	litest_dispatch(li);
+	litest_timeout_softbuttons(li);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
@@ -1893,12 +1757,8 @@ START_TEST(clickpad_topsoftbuttons_click
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 
@@ -1911,12 +1771,8 @@ START_TEST(clickpad_topsoftbuttons_click
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -1924,11 +1780,11 @@ START_TEST(clickpad_topsoftbuttons_click
 {
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
-	struct litest_device *trackpoint = litest_add_device(li,
-							     LITEST_TRACKPOINT);
+	struct litest_device *trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
 
-	libinput_device_config_send_events_set_mode(dev->libinput_device,
-						    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+	libinput_device_config_send_events_set_mode(
+		dev->libinput_device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_enable_clickfinger(dev);
 	litest_drain_events(li);
 
@@ -1939,12 +1795,8 @@ START_TEST(clickpad_topsoftbuttons_click
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 
@@ -1957,14 +1809,10 @@ START_TEST(clickpad_topsoftbuttons_click
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 
-	litest_delete_device(trackpoint);
+	litest_device_destroy(trackpoint);
 }
 END_TEST
 
@@ -1985,15 +1833,17 @@ START_TEST(clickpad_middleemulation_conf
 	litest_dispatch(li);
 
 	/* actual config is delayed, but status is immediate */
-	status = libinput_device_config_middle_emulation_set_enabled(device,
-				LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
+	status = libinput_device_config_middle_emulation_set_enabled(
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	enabled = libinput_device_config_middle_emulation_get_enabled(device);
 	litest_assert(enabled);
 
-	status = libinput_device_config_middle_emulation_set_enabled(device,
-				LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
+	status = libinput_device_config_middle_emulation_set_enabled(
+		device,
+		LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	enabled = libinput_device_config_middle_emulation_get_enabled(device);
 	litest_assert(!enabled);
@@ -2014,18 +1864,14 @@ START_TEST(clickpad_middleemulation_clic
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_dispatch(li);
 
@@ -2046,17 +1892,13 @@ START_TEST(clickpad_middleemulation_clic
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_dispatch(li);
 
@@ -2077,17 +1919,13 @@ START_TEST(clickpad_middleemulation_clic
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_dispatch(li);
 
@@ -2107,9 +1945,7 @@ START_TEST(clickpad_middleemulation_clic
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_enable_middleemu(dev);
 
@@ -2117,9 +1953,7 @@ START_TEST(clickpad_middleemulation_clic
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_dispatch(li);
 
@@ -2132,12 +1966,8 @@ START_TEST(clickpad_middleemulation_clic
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_dispatch(li);
 }
@@ -2158,9 +1988,7 @@ START_TEST(clickpad_middleemulation_clic
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_disable_middleemu(dev);
 
@@ -2169,9 +1997,7 @@ START_TEST(clickpad_middleemulation_clic
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_dispatch(li);
 
 	litest_assert_empty_queue(li);
@@ -2183,12 +2009,8 @@ START_TEST(clickpad_middleemulation_clic
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_dispatch(li);
 }
@@ -2196,7 +2018,6 @@ END_TEST
 
 START_TEST(touchpad_non_clickpad_detection)
 {
-	struct libinput *li;
 	struct libinput_device *device;
 	struct libevdev_uinput *uinput;
 	static struct input_absinfo absinfo[] = {
@@ -2216,6 +2037,7 @@ START_TEST(touchpad_non_clickpad_detecti
 	/* Create a touchpad with only a left button but missing
 	 * INPUT_PROP_BUTTONPAD. We should treat this as clickpad.
 	 */
+	/* clang-format off */
 	uinput = litest_create_uinput_abs_device("litest NonClickpad",
 						 NULL,
 						 absinfo,
@@ -2223,10 +2045,10 @@ START_TEST(touchpad_non_clickpad_detecti
 						 EV_KEY, BTN_TOOL_FINGER,
 						 EV_KEY, BTN_TOUCH,
 						 -1);
+	/* clang-format on */
 
-	li = litest_create_context();
-	device = libinput_path_add_device(li,
-					  libevdev_uinput_get_devnode(uinput));
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	device = libinput_path_add_device(li, libevdev_uinput_get_devnode(uinput));
 
 	methods = libinput_device_config_click_get_methods(device);
 	litest_assert(methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS);
@@ -2234,12 +2056,12 @@ START_TEST(touchpad_non_clickpad_detecti
 
 	libinput_path_remove_device(device);
 	libevdev_uinput_destroy(uinput);
-	litest_destroy_context(li);
 }
 END_TEST
 
 TEST_COLLECTION(touchpad_buttons)
 {
+	/* clang-format off */
 	litest_add(touchpad_button, LITEST_TOUCHPAD, LITEST_CLICKPAD);
 
 	litest_add(touchpad_1fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY);
@@ -2318,4 +2140,5 @@ TEST_COLLECTION(touchpad_buttons)
 	litest_add(clickpad_middleemulation_click_disable_while_down, LITEST_CLICKPAD, LITEST_ANY);
 
 	litest_add_no_device(touchpad_non_clickpad_detection);
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-touchpad-tap.c 1.30.0-1/test/test-touchpad-tap.c
--- 1.28.1-1/test/test-touchpad-tap.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-touchpad-tap.c	2025-11-25 03:40:43.000000000 +0000
@@ -45,11 +45,9 @@ START_TEST(touchpad_1fg_tap)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_timeout_tap();
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap(li);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -64,8 +62,7 @@ START_TEST(touchpad_doubletap)
 	uint32_t oldtime, curtime;
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers_1st"),
 	    nfingers2 = litest_test_param_get_i32(test_env->params, "fingers_2nd");
-	unsigned int button = 0,
-		     button2 = 0;
+	unsigned int button = 0, button2 = 0;
 
 	if (nfingers > litest_slot_count(dev))
 		return LITEST_NOT_APPLICABLE;
@@ -154,39 +151,29 @@ START_TEST(touchpad_doubletap)
 		litest_touch_up(dev, 0);
 		break;
 	}
-	litest_dispatch(li);
 
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 
-	litest_dispatch(li);
 	event = libinput_get_event(li);
-	ptrev = litest_is_button_event(event,
-				       button,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
+	ptrev = litest_is_button_event(event, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	oldtime = libinput_event_pointer_get_time(ptrev);
 	libinput_event_destroy(event);
 
 	event = libinput_get_event(li);
-	ptrev = litest_is_button_event(event,
-				       button,
-				       LIBINPUT_BUTTON_STATE_RELEASED);
+	ptrev = litest_is_button_event(event, button, LIBINPUT_BUTTON_STATE_RELEASED);
 	curtime = libinput_event_pointer_get_time(ptrev);
 	libinput_event_destroy(event);
 	litest_assert_int_lt(oldtime, curtime);
 
 	event = libinput_get_event(li);
-	ptrev = litest_is_button_event(event,
-				       button2,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
+	ptrev = litest_is_button_event(event, button2, LIBINPUT_BUTTON_STATE_PRESSED);
 	curtime = libinput_event_pointer_get_time(ptrev);
 	libinput_event_destroy(event);
 	litest_assert_int_lt(oldtime, curtime);
 	oldtime = curtime;
 
 	event = libinput_get_event(li);
-	ptrev = litest_is_button_event(event,
-				       button2,
-				       LIBINPUT_BUTTON_STATE_RELEASED);
+	ptrev = litest_is_button_event(event, button2, LIBINPUT_BUTTON_STATE_RELEASED);
 	curtime = libinput_event_pointer_get_time(ptrev);
 	libinput_event_destroy(event);
 	litest_assert_int_lt(oldtime, curtime);
@@ -201,11 +188,9 @@ START_TEST(touchpad_multitap)
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
 	struct libinput_event_pointer *ptrev;
-	uint32_t oldtime = 0,
-		 curtime;
+	uint32_t oldtime = 0, curtime;
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers"),
-	    range = litest_test_param_get_i32(test_env->params, "taps"),
-	    ntaps;
+	    range = litest_test_param_get_i32(test_env->params, "taps"), ntaps;
 	unsigned int button = 0;
 
 	if (nfingers > litest_slot_count(dev))
@@ -259,8 +244,7 @@ START_TEST(touchpad_multitap)
 		msleep(10);
 	}
 
-	litest_timeout_tapndrag();
-	litest_dispatch(li);
+	litest_timeout_tapndrag(li);
 
 	for (ntaps = 0; ntaps <= range; ntaps++) {
 		event = libinput_get_event(li);
@@ -280,7 +264,7 @@ START_TEST(touchpad_multitap)
 		litest_assert_int_ge(curtime, oldtime);
 		oldtime = curtime;
 	}
-	litest_timeout_tapndrag();
+	litest_timeout_tapndrag(li);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -291,11 +275,9 @@ START_TEST(touchpad_multitap_n_drag_move
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
 	struct libinput_event_pointer *ptrev;
-	uint32_t oldtime = 0,
-		 curtime;
+	uint32_t oldtime = 0, curtime;
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers"),
-	    range = litest_test_param_get_i32(test_env->params, "taps"),
-	    ntaps;
+	    range = litest_test_param_get_i32(test_env->params, "taps"), ntaps;
 	unsigned int button = 0;
 
 	if (nfingers > litest_slot_count(dev))
@@ -373,22 +355,16 @@ START_TEST(touchpad_multitap_n_drag_move
 	}
 
 	event = libinput_get_event(li);
-	ptrev = litest_is_button_event(event,
-				       button,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
+	ptrev = litest_is_button_event(event, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	curtime = libinput_event_pointer_get_time(ptrev);
 	libinput_event_destroy(event);
 	litest_assert_int_gt(curtime, oldtime);
 
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-	litest_timeout_tapndrag();
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_timeout_tapndrag(li);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -400,11 +376,9 @@ START_TEST(touchpad_multitap_n_drag_2fg)
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
 	struct libinput_event_pointer *ptrev;
-	uint32_t oldtime = 0,
-		 curtime;
+	uint32_t oldtime = 0, curtime;
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers"),
-	    range = litest_test_param_get_i32(test_env->params, "taps"),
-	    ntaps;
+	    range = litest_test_param_get_i32(test_env->params, "taps"), ntaps;
 	unsigned int button = 0;
 
 	if (libevdev_has_property(dev->evdev, INPUT_PROP_SEMI_MT))
@@ -487,25 +461,19 @@ START_TEST(touchpad_multitap_n_drag_2fg)
 	}
 
 	event = libinput_get_event(li);
-	ptrev = litest_is_button_event(event,
-				       button,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
+	ptrev = litest_is_button_event(event, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	curtime = libinput_event_pointer_get_time(ptrev);
 	libinput_event_destroy(event);
 	litest_assert_int_gt(curtime, oldtime);
 
 	litest_touch_move_to(dev, 1, 70, 50, 90, 50, 10);
 
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_touch_up(dev, 1);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-	litest_timeout_tapndrag();
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_timeout_tapndrag(li);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -517,11 +485,9 @@ START_TEST(touchpad_multitap_n_drag_clic
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
 	struct libinput_event_pointer *ptrev;
-	uint32_t oldtime = 0,
-		 curtime;
+	uint32_t oldtime = 0, curtime;
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers"),
-	    range = litest_test_param_get_i32(test_env->params, "taps"),
-	    ntaps;
+	    range = litest_test_param_get_i32(test_env->params, "taps"), ntaps;
 	unsigned int button = 0;
 
 	if (nfingers > litest_slot_count(dev))
@@ -600,14 +566,10 @@ START_TEST(touchpad_multitap_n_drag_clic
 		oldtime = curtime;
 	}
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_touch_up(dev, 0);
-	litest_timeout_tapndrag();
+	litest_timeout_tapndrag(li);
 
 	litest_assert_empty_queue(li);
 }
@@ -621,8 +583,7 @@ START_TEST(touchpad_multitap_timeout)
 	struct libinput_event_pointer *ptrev;
 	uint32_t ptime, rtime;
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers"),
-	    range = litest_test_param_get_i32(test_env->params, "taps"),
-	    ntaps;
+	    range = litest_test_param_get_i32(test_env->params, "taps"), ntaps;
 	unsigned int button = 0;
 
 	if (nfingers > litest_slot_count(dev))
@@ -677,9 +638,7 @@ START_TEST(touchpad_multitap_timeout)
 		msleep(10);
 	}
 
-	litest_dispatch(li);
-	litest_timeout_tapndrag();
-	litest_dispatch(li);
+	litest_timeout_tapndrag(li);
 
 	for (ntaps = 0; ntaps <= range; ntaps++) {
 		event = libinput_get_event(li);
@@ -708,11 +667,9 @@ START_TEST(touchpad_multitap_n_drag_time
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
 	struct libinput_event_pointer *ptrev;
-	uint32_t oldtime = 0,
-		 curtime;
+	uint32_t oldtime = 0, curtime;
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers"),
-	    range = litest_test_param_get_i32(test_env->params, "taps"),
-	    ntaps;
+	    range = litest_test_param_get_i32(test_env->params, "taps"), ntaps;
 	unsigned int button = 0;
 
 	if (nfingers > litest_slot_count(dev))
@@ -769,10 +726,8 @@ START_TEST(touchpad_multitap_n_drag_time
 
 	litest_dispatch(li);
 	litest_touch_down(dev, 0, 50, 50);
-	litest_dispatch(li);
 
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	for (ntaps = 0; ntaps < range; ntaps++) {
 		event = libinput_get_event(li);
@@ -794,24 +749,18 @@ START_TEST(touchpad_multitap_n_drag_time
 	}
 
 	event = libinput_get_event(li);
-	ptrev = litest_is_button_event(event,
-				       button,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
+	ptrev = litest_is_button_event(event, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	curtime = libinput_event_pointer_get_time(ptrev);
 	libinput_event_destroy(event);
 	litest_assert_int_gt(curtime, oldtime);
 
 	litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10);
 
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-	litest_timeout_tapndrag();
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_timeout_tapndrag(li);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -822,8 +771,7 @@ START_TEST(touchpad_multitap_n_drag_high
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers"),
-	    range = litest_test_param_get_i32(test_env->params, "taps"),
-	    ntaps;
+	    range = litest_test_param_get_i32(test_env->params, "taps"), ntaps;
 	unsigned int button = 0;
 
 	if (nfingers > litest_slot_count(dev))
@@ -851,8 +799,8 @@ START_TEST(touchpad_multitap_n_drag_high
 
 	for (ntaps = 0; ntaps <= range; ntaps++) {
 		/* Tap timeout is 180ms after a touch or release. Make sure we
-		* go over 180ms for touch+release, but stay under 180ms for
-		* each single event. */
+		 * go over 180ms for touch+release, but stay under 180ms for
+		 * each single event. */
 		switch (nfingers) {
 		case 3:
 			litest_touch_down(dev, 2, 60, 30);
@@ -888,22 +836,16 @@ START_TEST(touchpad_multitap_n_drag_high
 	litest_dispatch(li);
 
 	for (ntaps = 0; ntaps < range; ntaps++) {
-		litest_assert_button_event(li, button,
-					   LIBINPUT_BUTTON_STATE_PRESSED);
-		litest_assert_button_event(li, button,
-					   LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 	}
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-	litest_timeout_tapndrag();
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_timeout_tapndrag(li);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -915,11 +857,9 @@ START_TEST(touchpad_multitap_n_drag_tap)
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
 	struct libinput_event_pointer *ptrev;
-	uint32_t oldtime = 0,
-		 curtime;
+	uint32_t oldtime = 0, curtime;
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers"),
-	    range = litest_test_param_get_i32(test_env->params, "taps"),
-	    ntaps;
+	    range = litest_test_param_get_i32(test_env->params, "taps"), ntaps;
 	unsigned int button = 0;
 
 	if (nfingers > litest_slot_count(dev))
@@ -976,10 +916,7 @@ START_TEST(touchpad_multitap_n_drag_tap)
 
 	litest_dispatch(li);
 	litest_touch_down(dev, 0, 50, 50);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	for (ntaps = 0; ntaps < range; ntaps++) {
 		event = libinput_get_event(li);
@@ -1001,24 +938,19 @@ START_TEST(touchpad_multitap_n_drag_tap)
 	}
 
 	event = libinput_get_event(li);
-	ptrev = litest_is_button_event(event,
-				       button,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
+	ptrev = litest_is_button_event(event, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	curtime = libinput_event_pointer_get_time(ptrev);
 	libinput_event_destroy(event);
 	litest_assert_int_gt(curtime, oldtime);
 
 	litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10);
 
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_touch_up(dev, 0);
 	litest_touch_down(dev, 0, 70, 50);
 	litest_touch_up(dev, 0);
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1030,11 +962,9 @@ START_TEST(touchpad_multitap_n_drag_tap_
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
 	struct libinput_event_pointer *ptrev;
-	uint32_t oldtime = 0,
-		 curtime;
+	uint32_t oldtime = 0, curtime;
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers"),
-	    range = litest_test_param_get_i32(test_env->params, "taps"),
-	    ntaps;
+	    range = litest_test_param_get_i32(test_env->params, "taps"), ntaps;
 	unsigned int button = 0;
 
 	if (nfingers > litest_slot_count(dev))
@@ -1091,10 +1021,7 @@ START_TEST(touchpad_multitap_n_drag_tap_
 
 	litest_dispatch(li);
 	litest_touch_down(dev, 0, 50, 50);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	for (ntaps = 0; ntaps < range; ntaps++) {
 		event = libinput_get_event(li);
@@ -1116,17 +1043,14 @@ START_TEST(touchpad_multitap_n_drag_tap_
 	}
 
 	event = libinput_get_event(li);
-	ptrev = litest_is_button_event(event,
-				       button,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
+	ptrev = litest_is_button_event(event, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	curtime = libinput_event_pointer_get_time(ptrev);
 	libinput_event_destroy(event);
 	litest_assert_int_gt(curtime, oldtime);
 
 	litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10);
 
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_touch_up(dev, 0);
 	litest_touch_down(dev, 0, 70, 50);
@@ -1134,17 +1058,11 @@ START_TEST(touchpad_multitap_n_drag_tap_
 	litest_button_click(dev, BTN_LEFT, false);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	/* the physical click */
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_touch_up(dev, 0);
 
 	litest_assert_empty_queue(li);
@@ -1209,8 +1127,7 @@ START_TEST(touchpad_tap_n_drag)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_dispatch(li);
 
@@ -1225,9 +1142,7 @@ START_TEST(touchpad_tap_n_drag)
 	litest_dispatch(li);
 	event = libinput_get_event(li);
 	litest_assert_notnull(event);
-	litest_is_button_event(event,
-			       button,
-			       LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_is_button_event(event, button, LIBINPUT_BUTTON_STATE_RELEASED);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
@@ -1292,8 +1207,7 @@ START_TEST(touchpad_tap_n_drag_draglock)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_dispatch(li);
 
@@ -1306,10 +1220,9 @@ START_TEST(touchpad_tap_n_drag_draglock)
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1377,8 +1290,7 @@ START_TEST(touchpad_tap_n_drag_draglock_
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_dispatch(li);
 
@@ -1415,8 +1327,7 @@ START_TEST(touchpad_tap_n_drag_draglock_
 		break;
 	}
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1478,8 +1389,7 @@ START_TEST(touchpad_tap_n_drag_draglock_
 	litest_touch_move_to(dev, 0, 50, 50, 80, 80, 20);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_dispatch(li);
 
@@ -1491,16 +1401,11 @@ START_TEST(touchpad_tap_n_drag_draglock_
 	litest_button_click(dev, BTN_LEFT, false);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	/* the physical click */
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_touch_up(dev, 0);
 
 	litest_assert_empty_queue(li);
@@ -1560,19 +1465,14 @@ START_TEST(touchpad_tap_n_drag_draglock_
 		break;
 	}
 	litest_touch_down(dev, 0, 50, 50);
-	litest_dispatch(li);
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_assert_empty_queue(li);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tapndrag();
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_timeout_tapndrag(li);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1631,23 +1531,19 @@ START_TEST(touchpad_tap_n_drag_draglock_
 		break;
 	}
 	litest_touch_down(dev, 0, 50, 50);
-	litest_dispatch(li);
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_assert_empty_queue(li);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
 
-	litest_timeout_tapndrag();
+	litest_timeout_tapndrag(li);
 	litest_assert_empty_queue(li);
 
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_up(dev, 0);
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1714,23 +1610,19 @@ START_TEST(touchpad_tap_n_drag_2fg)
 		break;
 	}
 	litest_touch_down(dev, 0, 30, 70);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 	litest_touch_down(dev, 1, 80, 70);
 	litest_touch_move_to(dev, 0, 30, 70, 30, 30, 10);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -1804,10 +1696,8 @@ START_TEST(touchpad_tap_n_drag_2fg_scrol
 	litest_touch_up(dev, 1);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_only_axis_events(li, LIBINPUT_EVENT_POINTER_SCROLL_FINGER);
 
@@ -1877,8 +1767,7 @@ START_TEST(touchpad_tap_n_drag_draglock_
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_move_to(dev, 0, 50, 50, 50, 70, 10);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	/* Release finger to trigger drag-lock */
@@ -1895,8 +1784,7 @@ START_TEST(touchpad_tap_n_drag_draglock_
 	litest_touch_up(dev, 1);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_only_axis_events(li, LIBINPUT_EVENT_POINTER_SCROLL_FINGER);
 
@@ -1963,15 +1851,12 @@ START_TEST(touchpad_tap_n_drag_3fg_btnto
 		break;
 	}
 	litest_touch_down(dev, 0, 30, 70);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 	litest_touch_down(dev, 1, 80, 90);
 	litest_touch_move_to(dev, 0, 30, 70, 30, 30, 5);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
@@ -1981,8 +1866,7 @@ START_TEST(touchpad_tap_n_drag_3fg_btnto
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	/* Releasing the fingers should not cause any events */
 	litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 0);
@@ -2056,16 +1940,13 @@ START_TEST(touchpad_tap_n_drag_3fg)
 	}
 	/* 1fg down triggers the drag */
 	litest_touch_down(dev, 0, 30, 70);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 	/* 2fg is allowed now without cancelling the drag */
 	litest_touch_down(dev, 1, 80, 90);
 	litest_touch_move_to(dev, 0, 30, 70, 30, 30, 10);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
@@ -2074,8 +1955,7 @@ START_TEST(touchpad_tap_n_drag_3fg)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	/* Releasing the fingers should not cause any events */
 	litest_touch_up(dev, 2);
@@ -2145,31 +2025,19 @@ START_TEST(touchpad_tap_n_drag_3fg_swipe
 	litest_touch_down(dev, 1, 50, 50);
 	litest_touch_down(dev, 2, 80, 50);
 	litest_dispatch(li);
-	litest_touch_move_three_touches(dev,
-					30, 50,
-					50, 50,
-					80, 50,
-					0, 20,
-					10);
-	litest_dispatch(li);
-
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-
-	litest_assert_gesture_event(li,
-				    LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
-				    3);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE);
+	litest_touch_move_three_touches(dev, 30, 50, 50, 50, 80, 50, 0, 20, 10);
+	litest_dispatch(li);
+
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
+
+	litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, 3);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE);
 
 	litest_touch_up(dev, 2);
 	litest_touch_up(dev, 1);
 	litest_touch_up(dev, 0);
-	litest_assert_gesture_event(li,
-				    LIBINPUT_EVENT_GESTURE_SWIPE_END,
-				    3);
+	litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_SWIPE_END, 3);
 
 	litest_assert_empty_queue(li);
 }
@@ -2236,8 +2104,7 @@ START_TEST(touchpad_tap_n_drag_draglock_
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_move_to(dev, 0, 50, 50, 50, 70, 10);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	/* Release finger to trigger drag-lock */
@@ -2247,29 +2114,18 @@ START_TEST(touchpad_tap_n_drag_draglock_
 	litest_touch_down(dev, 1, 50, 50);
 	litest_touch_down(dev, 2, 80, 50);
 	litest_dispatch(li);
-	litest_touch_move_three_touches(dev,
-					30, 50,
-					50, 50,
-					80, 50,
-					0, 20,
-					10);
-	litest_dispatch(li);
-
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-
-	litest_assert_gesture_event(li,
-				    LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN,
-				    3);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE);
+	litest_touch_move_three_touches(dev, 30, 50, 50, 50, 80, 50, 0, 20, 10);
+	litest_dispatch(li);
+
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
+
+	litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN, 3);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE);
 
 	litest_touch_up(dev, 2);
 	litest_touch_up(dev, 1);
 	litest_touch_up(dev, 0);
-	litest_assert_gesture_event(li,
-				    LIBINPUT_EVENT_GESTURE_SWIPE_END,
-				    3);
+	litest_assert_gesture_event(li, LIBINPUT_EVENT_GESTURE_SWIPE_END, 3);
 
 	litest_assert_empty_queue(li);
 }
@@ -2284,7 +2140,8 @@ START_TEST(touchpad_2fg_tap)
 	struct libinput_event_pointer *ptrev;
 	uint64_t ptime, rtime;
 
-	enum libinput_config_tap_button_map map = litest_test_param_get_i32(test_env->params, "map");
+	enum libinput_config_tap_button_map map =
+		litest_test_param_get_i32(test_env->params, "map");
 
 	litest_enable_tap(dev->libinput_device);
 	litest_set_tap_map(dev->libinput_device, map);
@@ -2308,20 +2165,14 @@ START_TEST(touchpad_2fg_tap)
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
 
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	ev = libinput_get_event(li);
-	ptrev = litest_is_button_event(ev,
-				       button,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
+	ptrev = litest_is_button_event(ev, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	ptime = libinput_event_pointer_get_time_usec(ptrev);
 	libinput_event_destroy(ev);
 	ev = libinput_get_event(li);
-	ptrev = litest_is_button_event(ev,
-				       button,
-				       LIBINPUT_BUTTON_STATE_RELEASED);
+	ptrev = litest_is_button_event(ev, button, LIBINPUT_BUTTON_STATE_RELEASED);
 	rtime = libinput_event_pointer_get_time_usec(ptrev);
 	libinput_event_destroy(ev);
 
@@ -2340,7 +2191,8 @@ START_TEST(touchpad_2fg_tap_inverted)
 	struct libinput_event_pointer *ptrev;
 	uint64_t ptime, rtime;
 
-	enum libinput_config_tap_button_map map = litest_test_param_get_i32(test_env->params, "map");
+	enum libinput_config_tap_button_map map =
+		litest_test_param_get_i32(test_env->params, "map");
 
 	litest_enable_tap(dev->libinput_device);
 	litest_set_tap_map(dev->libinput_device, map);
@@ -2364,20 +2216,14 @@ START_TEST(touchpad_2fg_tap_inverted)
 	litest_touch_up(dev, 1);
 	litest_touch_up(dev, 0);
 
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	ev = libinput_get_event(li);
-	ptrev = litest_is_button_event(ev,
-				       button,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
+	ptrev = litest_is_button_event(ev, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	ptime = libinput_event_pointer_get_time_usec(ptrev);
 	libinput_event_destroy(ev);
 	ev = libinput_get_event(li);
-	ptrev = litest_is_button_event(ev,
-				       button,
-				       LIBINPUT_BUTTON_STATE_RELEASED);
+	ptrev = litest_is_button_event(ev, button, LIBINPUT_BUTTON_STATE_RELEASED);
 	rtime = libinput_event_pointer_get_time_usec(ptrev);
 	libinput_event_destroy(ev);
 
@@ -2408,12 +2254,8 @@ START_TEST(touchpad_2fg_tap_move_on_rele
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -2435,7 +2277,7 @@ START_TEST(touchpad_2fg_tap_n_hold_first
 	litest_dispatch(li);
 
 	litest_assert_empty_queue(li);
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 
 	litest_assert_empty_queue(li);
 }
@@ -2457,7 +2299,7 @@ START_TEST(touchpad_2fg_tap_n_hold_secon
 	litest_dispatch(li);
 
 	litest_assert_empty_queue(li);
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 
 	litest_assert_empty_queue(li);
 }
@@ -2484,11 +2326,9 @@ START_TEST(touchpad_2fg_tap_quickrelease
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_timeout_tap();
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap(li);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -2512,15 +2352,10 @@ START_TEST(touchpad_1fg_tap_click)
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
-	litest_timeout_tap();
-
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -2548,14 +2383,10 @@ START_TEST(touchpad_2fg_tap_click)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -2583,10 +2414,8 @@ START_TEST(clickpad_2fg_tap_click)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -2615,10 +2444,8 @@ START_TEST(touchpad_2fg_tap_click_apple)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -2662,9 +2489,7 @@ START_TEST(touchpad_no_2fg_tap_after_tim
 	   -> no event
 	 */
 	litest_touch_down(dev, 0, 50, 50);
-	litest_dispatch(dev->libinput);
-	litest_timeout_tap();
-	litest_dispatch(dev->libinput);
+	litest_timeout_tap(li);
 	litest_drain_events(dev->libinput);
 
 	litest_touch_down(dev, 1, 70, 50);
@@ -2699,7 +2524,8 @@ START_TEST(touchpad_no_first_fg_tap_afte
 	litest_dispatch(dev->libinput);
 
 	while ((event = libinput_get_event(li))) {
-		litest_assert_event_type_not_one_of(event, LIBINPUT_EVENT_POINTER_BUTTON);
+		litest_assert_event_type_not_one_of(event,
+						    LIBINPUT_EVENT_POINTER_BUTTON);
 		libinput_event_destroy(event);
 	}
 }
@@ -2767,14 +2593,10 @@ START_TEST(touchpad_double_tap_click)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -2837,18 +2659,15 @@ START_TEST(touchpad_tap_n_drag_click)
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_move_to(dev, 0, 50, 50, 80, 50, 10);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
@@ -2856,8 +2675,7 @@ START_TEST(touchpad_tap_n_drag_click)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -2870,7 +2688,8 @@ START_TEST(touchpad_3fg_tap)
 	unsigned int button = 0;
 	int i;
 
-	enum libinput_config_tap_button_map map = litest_test_param_get_i32(test_env->params, "map");
+	enum libinput_config_tap_button_map map =
+		litest_test_param_get_i32(test_env->params, "map");
 
 	if (litest_slot_count(dev) < 3)
 		return LITEST_NOT_APPLICABLE;
@@ -2908,9 +2727,7 @@ START_TEST(touchpad_3fg_tap)
 		litest_touch_up(dev, (i + 1) % 3);
 		litest_touch_up(dev, (i + 0) % 3);
 
-		litest_dispatch(li);
-		litest_timeout_tap();
-		litest_dispatch(li);
+		litest_timeout_tap(li);
 
 		ev = libinput_get_event(li);
 		ptrev = litest_is_button_event(ev,
@@ -2926,7 +2743,6 @@ START_TEST(touchpad_3fg_tap)
 		libinput_event_destroy(ev);
 
 		litest_assert_int_lt(ptime, rtime);
-
 	}
 }
 END_TEST
@@ -2963,9 +2779,7 @@ START_TEST(touchpad_3fg_tap_tap_again)
 	litest_touch_up(dev, 1);
 	litest_touch_up(dev, 2);
 
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	for (i = 0; i < 2; i++) {
 		ev = libinput_get_event(li);
@@ -3013,11 +2827,9 @@ START_TEST(touchpad_3fg_tap_quickrelease
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_timeout_tap();
-	litest_assert_button_event(li, BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap(li);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
@@ -3047,10 +2859,8 @@ START_TEST(touchpad_3fg_tap_pressure_btn
 
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_down(dev, 1, 70, 50);
-	litest_dispatch(li);
 
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 	litest_drain_events(li);
 
 	/* drop below the pressure threshold in the same frame as starting a
@@ -3072,16 +2882,10 @@ START_TEST(touchpad_3fg_tap_pressure_btn
 
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -3143,7 +2947,8 @@ START_TEST(touchpad_3fg_tap_btntool)
 	struct libinput *li = dev->libinput;
 	unsigned int button = 0;
 
-	enum libinput_config_tap_button_map map = litest_test_param_get_i32(test_env->params, "map");
+	enum libinput_config_tap_button_map map =
+		litest_test_param_get_i32(test_env->params, "map");
 
 	if (litest_slot_count(dev) >= 3 ||
 	    !libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_TRIPLETAP))
@@ -3179,11 +2984,9 @@ START_TEST(touchpad_3fg_tap_btntool)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_timeout_tap();
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap(li);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -3195,7 +2998,8 @@ START_TEST(touchpad_3fg_tap_btntool_inve
 	struct libinput *li = dev->libinput;
 	unsigned int button = 0;
 
-	enum libinput_config_tap_button_map map = litest_test_param_get_i32(test_env->params, "map");
+	enum libinput_config_tap_button_map map =
+		litest_test_param_get_i32(test_env->params, "map");
 
 	if (litest_slot_count(dev) > 3 ||
 	    !libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_TRIPLETAP))
@@ -3231,11 +3035,9 @@ START_TEST(touchpad_3fg_tap_btntool_inve
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_timeout_tap();
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap(li);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -3247,7 +3049,8 @@ START_TEST(touchpad_3fg_tap_btntool_poin
 	struct libinput *li = dev->libinput;
 	unsigned int button = 0;
 
-	enum libinput_config_tap_button_map map = litest_test_param_get_i32(test_env->params, "map");
+	enum libinput_config_tap_button_map map =
+		litest_test_param_get_i32(test_env->params, "map");
 
 	if (litest_slot_count(dev) > 3 ||
 	    !libevdev_has_event_code(dev->evdev, EV_KEY, BTN_TOOL_TRIPLETAP))
@@ -3286,11 +3089,9 @@ START_TEST(touchpad_3fg_tap_btntool_poin
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_timeout_tap();
-	litest_assert_button_event(li, button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap(li);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -3377,13 +3178,10 @@ START_TEST(touchpad_3fg_tap_slot_release
 	litest_event(dev, EV_KEY, BTN_TOUCH, 0);
 	litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li, BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -3409,8 +3207,7 @@ START_TEST(touchpad_3fg_tap_after_scroll
 	litest_touch_move_two_touches(dev, 40, 20, 50, 20, 0, 20, 10);
 	litest_drain_events(li);
 
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	/* third finger tap without the other two fingers moving */
 	litest_touch_down(dev, 2, 60, 40);
@@ -3418,8 +3215,7 @@ START_TEST(touchpad_3fg_tap_after_scroll
 	litest_touch_up(dev, 2);
 	litest_dispatch(li);
 
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	litest_assert_empty_queue(li);
 }
@@ -3452,7 +3248,7 @@ START_TEST(touchpad_4fg_tap)
 
 		litest_dispatch(li);
 		litest_assert_empty_queue(li);
-		litest_timeout_tap();
+		litest_timeout_tap(li);
 		litest_assert_empty_queue(li);
 	}
 }
@@ -3489,7 +3285,7 @@ START_TEST(touchpad_4fg_tap_quickrelease
 
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -3508,7 +3304,7 @@ START_TEST(touchpad_move_after_touch)
 	litest_drain_events(li);
 
 	/* respective number of fingers down */
-	switch(nfingers) {
+	switch (nfingers) {
 	case 5:
 		litest_touch_down(dev, 4, 80, 30);
 		_fallthrough_;
@@ -3540,7 +3336,7 @@ START_TEST(touchpad_move_after_touch)
 	litest_dispatch(li);
 
 	/* lift fingers up */
-	switch(nfingers) {
+	switch (nfingers) {
 	case 5:
 		litest_touch_up(dev, 4);
 		_fallthrough_;
@@ -3557,9 +3353,7 @@ START_TEST(touchpad_move_after_touch)
 		litest_touch_up(dev, 0);
 		break;
 	}
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	litest_assert_no_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON);
 }
@@ -3594,7 +3388,7 @@ START_TEST(touchpad_5fg_tap)
 
 		litest_dispatch(li);
 		litest_assert_empty_queue(li);
-		litest_timeout_tap();
+		litest_timeout_tap(li);
 		litest_assert_empty_queue(li);
 	}
 }
@@ -3634,7 +3428,7 @@ START_TEST(touchpad_5fg_tap_quickrelease
 
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -3656,15 +3450,10 @@ START_TEST(clickpad_1fg_tap_click)
 	litest_event(dev, EV_KEY, BTN_LEFT, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-	litest_timeout_tap();
-
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -3674,7 +3463,9 @@ START_TEST(touchpad_tap_is_available)
 {
 	struct litest_device *dev = litest_current_device();
 
-	litest_assert_int_ge(libinput_device_config_tap_get_finger_count(dev->libinput_device), 1);
+	litest_assert_int_ge(
+		libinput_device_config_tap_get_finger_count(dev->libinput_device),
+		1);
 }
 END_TEST
 
@@ -3682,15 +3473,20 @@ START_TEST(touchpad_tap_is_not_available
 {
 	struct litest_device *dev = litest_current_device();
 
-	litest_assert_int_eq(libinput_device_config_tap_get_finger_count(dev->libinput_device), 0);
-	litest_assert_enum_eq(libinput_device_config_tap_get_enabled(dev->libinput_device),
-			 LIBINPUT_CONFIG_TAP_DISABLED);
-	litest_assert_enum_eq(libinput_device_config_tap_set_enabled(dev->libinput_device,
-								LIBINPUT_CONFIG_TAP_ENABLED),
-			 LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
-	litest_assert_enum_eq(libinput_device_config_tap_set_enabled(dev->libinput_device,
-								LIBINPUT_CONFIG_TAP_DISABLED),
-			 LIBINPUT_CONFIG_STATUS_SUCCESS);
+	litest_assert_int_eq(
+		libinput_device_config_tap_get_finger_count(dev->libinput_device),
+		0);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_get_enabled(dev->libinput_device),
+		LIBINPUT_CONFIG_TAP_DISABLED);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_set_enabled(dev->libinput_device,
+						       LIBINPUT_CONFIG_TAP_ENABLED),
+		LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_set_enabled(dev->libinput_device,
+						       LIBINPUT_CONFIG_TAP_DISABLED),
+		LIBINPUT_CONFIG_STATUS_SUCCESS);
 }
 END_TEST
 
@@ -3700,10 +3496,12 @@ START_TEST(touchpad_tap_default_disabled
 
 	/* this test is only run on specific devices */
 
-	litest_assert_enum_eq(libinput_device_config_tap_get_enabled(dev->libinput_device),
-			 LIBINPUT_CONFIG_TAP_DISABLED);
-	litest_assert_enum_eq(libinput_device_config_tap_get_default_enabled(dev->libinput_device),
-			 LIBINPUT_CONFIG_TAP_DISABLED);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_get_enabled(dev->libinput_device),
+		LIBINPUT_CONFIG_TAP_DISABLED);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_get_default_enabled(dev->libinput_device),
+		LIBINPUT_CONFIG_TAP_DISABLED);
 }
 END_TEST
 
@@ -3713,10 +3511,12 @@ START_TEST(touchpad_tap_default_enabled)
 
 	/* this test is only run on specific devices */
 
-	litest_assert_enum_eq(libinput_device_config_tap_get_enabled(dev->libinput_device),
-			 LIBINPUT_CONFIG_TAP_ENABLED);
-	litest_assert_enum_eq(libinput_device_config_tap_get_default_enabled(dev->libinput_device),
-			 LIBINPUT_CONFIG_TAP_ENABLED);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_get_enabled(dev->libinput_device),
+		LIBINPUT_CONFIG_TAP_ENABLED);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_get_default_enabled(dev->libinput_device),
+		LIBINPUT_CONFIG_TAP_ENABLED);
 }
 END_TEST
 
@@ -3724,10 +3524,12 @@ START_TEST(touchpad_tap_invalid)
 {
 	struct litest_device *dev = litest_current_device();
 
-	litest_assert_enum_eq(libinput_device_config_tap_set_enabled(dev->libinput_device, 2),
-			 LIBINPUT_CONFIG_STATUS_INVALID);
-	litest_assert_enum_eq(libinput_device_config_tap_set_enabled(dev->libinput_device, -1),
-			 LIBINPUT_CONFIG_STATUS_INVALID);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_set_enabled(dev->libinput_device, 2),
+		LIBINPUT_CONFIG_STATUS_INVALID);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_set_enabled(dev->libinput_device, -1),
+		LIBINPUT_CONFIG_STATUS_INVALID);
 }
 END_TEST
 
@@ -3825,10 +3627,10 @@ START_TEST(touchpad_tap_get_map_no_tappi
 	enum libinput_config_tap_button_map map;
 
 	map = libinput_device_config_tap_get_button_map(device);
-	litest_assert_enum_eq(map,  LIBINPUT_CONFIG_TAP_MAP_LRM);
+	litest_assert_enum_eq(map, LIBINPUT_CONFIG_TAP_MAP_LRM);
 
 	map = libinput_device_config_tap_get_default_button_map(device);
-	litest_assert_enum_eq(map,  LIBINPUT_CONFIG_TAP_MAP_LRM);
+	litest_assert_enum_eq(map, LIBINPUT_CONFIG_TAP_MAP_LRM);
 }
 END_TEST
 
@@ -3839,8 +3641,7 @@ START_TEST(touchpad_tap_map_delayed)
 	enum libinput_config_tap_button_map map;
 
 	litest_enable_tap(dev->libinput_device);
-	litest_set_tap_map(dev->libinput_device,
-			   LIBINPUT_CONFIG_TAP_MAP_LRM);
+	litest_set_tap_map(dev->libinput_device, LIBINPUT_CONFIG_TAP_MAP_LRM);
 	litest_disable_hold_gestures(dev->libinput_device);
 	litest_drain_events(dev->libinput);
 
@@ -3848,8 +3649,7 @@ START_TEST(touchpad_tap_map_delayed)
 	litest_touch_down(dev, 1, 70, 70);
 	litest_dispatch(li);
 
-	litest_set_tap_map(dev->libinput_device,
-			   LIBINPUT_CONFIG_TAP_MAP_LMR);
+	litest_set_tap_map(dev->libinput_device, LIBINPUT_CONFIG_TAP_MAP_LMR);
 	map = libinput_device_config_tap_get_button_map(dev->libinput_device);
 	litest_assert_enum_eq(map, LIBINPUT_CONFIG_TAP_MAP_LMR);
 
@@ -3858,12 +3658,9 @@ START_TEST(touchpad_tap_map_delayed)
 
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_timeout_tap();
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap(li);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -3875,10 +3672,12 @@ START_TEST(touchpad_drag_default_disable
 
 	/* this test is only run on specific devices */
 
-	litest_assert_enum_eq(libinput_device_config_tap_get_default_drag_enabled(dev->libinput_device),
-			 LIBINPUT_CONFIG_DRAG_DISABLED);
-	litest_assert_enum_eq(libinput_device_config_tap_get_drag_enabled(dev->libinput_device),
-			 LIBINPUT_CONFIG_DRAG_DISABLED);
+	litest_assert_enum_eq(libinput_device_config_tap_get_default_drag_enabled(
+				      dev->libinput_device),
+			      LIBINPUT_CONFIG_DRAG_DISABLED);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_get_drag_enabled(dev->libinput_device),
+		LIBINPUT_CONFIG_DRAG_DISABLED);
 }
 END_TEST
 
@@ -3888,10 +3687,12 @@ START_TEST(touchpad_drag_default_enabled
 
 	/* this test is only run on specific devices */
 
-	litest_assert_enum_eq(libinput_device_config_tap_get_default_drag_enabled(dev->libinput_device),
-			 LIBINPUT_CONFIG_DRAG_ENABLED);
-	litest_assert_enum_eq(libinput_device_config_tap_get_drag_enabled(dev->libinput_device),
-			 LIBINPUT_CONFIG_DRAG_ENABLED);
+	litest_assert_enum_eq(libinput_device_config_tap_get_default_drag_enabled(
+				      dev->libinput_device),
+			      LIBINPUT_CONFIG_DRAG_ENABLED);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_get_drag_enabled(dev->libinput_device),
+		LIBINPUT_CONFIG_DRAG_ENABLED);
 }
 END_TEST
 
@@ -3899,10 +3700,12 @@ START_TEST(touchpad_drag_config_invalid)
 {
 	struct litest_device *dev = litest_current_device();
 
-	litest_assert_enum_eq(libinput_device_config_tap_set_drag_enabled(dev->libinput_device, 2),
-			 LIBINPUT_CONFIG_STATUS_INVALID);
-	litest_assert_enum_eq(libinput_device_config_tap_set_drag_enabled(dev->libinput_device, -1),
-			 LIBINPUT_CONFIG_STATUS_INVALID);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_set_drag_enabled(dev->libinput_device, 2),
+		LIBINPUT_CONFIG_STATUS_INVALID);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_set_drag_enabled(dev->libinput_device, -1),
+		LIBINPUT_CONFIG_STATUS_INVALID);
 }
 END_TEST
 
@@ -3911,15 +3714,19 @@ START_TEST(touchpad_drag_config_unsuppor
 	struct litest_device *dev = litest_current_device();
 	enum libinput_config_status status;
 
-	litest_assert_enum_eq(libinput_device_config_tap_get_default_drag_enabled(dev->libinput_device),
-			 LIBINPUT_CONFIG_DRAG_DISABLED);
-	litest_assert_enum_eq(libinput_device_config_tap_get_drag_enabled(dev->libinput_device),
-			 LIBINPUT_CONFIG_DRAG_DISABLED);
-	status = libinput_device_config_tap_set_drag_enabled(dev->libinput_device,
-							     LIBINPUT_CONFIG_DRAG_ENABLED);
+	litest_assert_enum_eq(libinput_device_config_tap_get_default_drag_enabled(
+				      dev->libinput_device),
+			      LIBINPUT_CONFIG_DRAG_DISABLED);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_get_drag_enabled(dev->libinput_device),
+		LIBINPUT_CONFIG_DRAG_DISABLED);
+	status = libinput_device_config_tap_set_drag_enabled(
+		dev->libinput_device,
+		LIBINPUT_CONFIG_DRAG_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
-	status = libinput_device_config_tap_set_drag_enabled(dev->libinput_device,
-							     LIBINPUT_CONFIG_DRAG_DISABLED);
+	status = libinput_device_config_tap_set_drag_enabled(
+		dev->libinput_device,
+		LIBINPUT_CONFIG_DRAG_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 }
 END_TEST
@@ -4014,15 +3821,9 @@ START_TEST(touchpad_drag_disabled)
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
-
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 }
 END_TEST
 
@@ -4086,16 +3887,12 @@ START_TEST(touchpad_drag_disabled_immedi
 	litest_dispatch(li);
 
 	ev = libinput_get_event(li);
-	ptrev = litest_is_button_event(ev,
-				       button,
-				       LIBINPUT_BUTTON_STATE_PRESSED);
+	ptrev = litest_is_button_event(ev, button, LIBINPUT_BUTTON_STATE_PRESSED);
 	press_time = libinput_event_pointer_get_time(ptrev);
 	libinput_event_destroy(ev);
 
 	ev = libinput_get_event(li);
-	ptrev = litest_is_button_event(ev,
-				       button,
-				       LIBINPUT_BUTTON_STATE_RELEASED);
+	ptrev = litest_is_button_event(ev, button, LIBINPUT_BUTTON_STATE_RELEASED);
 	release_time = libinput_event_pointer_get_time(ptrev);
 	libinput_event_destroy(ev);
 
@@ -4109,11 +3906,9 @@ START_TEST(touchpad_drag_disabled_multit
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
 	struct libinput_event_pointer *ptrev;
-	uint32_t oldtime = 0,
-		 curtime;
+	uint32_t oldtime = 0, curtime;
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers"),
-	    range = litest_test_param_get_i32(test_env->params, "taps"),
-	    ntaps;
+	    range = litest_test_param_get_i32(test_env->params, "taps"), ntaps;
 	unsigned int button = 0;
 
 	if (nfingers > litest_slot_count(dev))
@@ -4191,8 +3986,7 @@ START_TEST(touchpad_drag_disabled_multit
 		oldtime = curtime;
 	}
 
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -4204,37 +3998,41 @@ START_TEST(touchpad_drag_lock_default_di
 	enum libinput_config_status status;
 
 	litest_assert_enum_eq(libinput_device_config_tap_get_drag_lock_enabled(device),
-			 LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
-	litest_assert_enum_eq(libinput_device_config_tap_get_default_drag_lock_enabled(device),
-			 LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
-
-	status = libinput_device_config_tap_set_drag_lock_enabled(device,
-								  LIBINPUT_CONFIG_DRAG_LOCK_ENABLED);
+			      LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_get_default_drag_lock_enabled(device),
+		LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
+
+	status = libinput_device_config_tap_set_drag_lock_enabled(
+		device,
+		LIBINPUT_CONFIG_DRAG_LOCK_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	/* ENABLED is a legacy spelling for ENABLED_TIMEOUT */
 	litest_assert_enum_eq(libinput_device_config_tap_get_drag_lock_enabled(device),
-			 LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
+			      LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
 
-	status = libinput_device_config_tap_set_drag_lock_enabled(device,
-								  LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
+	status = libinput_device_config_tap_set_drag_lock_enabled(
+		device,
+		LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	litest_assert_enum_eq(libinput_device_config_tap_get_drag_lock_enabled(device),
-			 LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
+			      LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
 
-	status = libinput_device_config_tap_set_drag_lock_enabled(device,
-								  LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
+	status = libinput_device_config_tap_set_drag_lock_enabled(
+		device,
+		LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	litest_assert_enum_eq(libinput_device_config_tap_get_drag_lock_enabled(device),
-			 LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
+			      LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
 
-	status = libinput_device_config_tap_set_drag_lock_enabled(device,
-								  LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY);
+	status = libinput_device_config_tap_set_drag_lock_enabled(
+		device,
+		LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	litest_assert_enum_eq(libinput_device_config_tap_get_drag_lock_enabled(device),
-			 LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY);
+			      LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY);
 
-	status = libinput_device_config_tap_set_drag_lock_enabled(device,
-								  3);
+	status = libinput_device_config_tap_set_drag_lock_enabled(device, 3);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
 }
 END_TEST
@@ -4246,28 +4044,32 @@ START_TEST(touchpad_drag_lock_default_un
 	enum libinput_config_status status;
 
 	litest_assert_enum_eq(libinput_device_config_tap_get_drag_lock_enabled(device),
-			 LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
-	litest_assert_enum_eq(libinput_device_config_tap_get_default_drag_lock_enabled(device),
-			 LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
-
-	status = libinput_device_config_tap_set_drag_lock_enabled(device,
-								  LIBINPUT_CONFIG_DRAG_LOCK_ENABLED);
+			      LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
+	litest_assert_enum_eq(
+		libinput_device_config_tap_get_default_drag_lock_enabled(device),
+		LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
+
+	status = libinput_device_config_tap_set_drag_lock_enabled(
+		device,
+		LIBINPUT_CONFIG_DRAG_LOCK_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 
-	status = libinput_device_config_tap_set_drag_lock_enabled(device,
-								  LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
+	status = libinput_device_config_tap_set_drag_lock_enabled(
+		device,
+		LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 
-	status = libinput_device_config_tap_set_drag_lock_enabled(device,
-								  LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY);
+	status = libinput_device_config_tap_set_drag_lock_enabled(
+		device,
+		LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 
-	status = libinput_device_config_tap_set_drag_lock_enabled(device,
-								  LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
+	status = libinput_device_config_tap_set_drag_lock_enabled(
+		device,
+		LIBINPUT_CONFIG_DRAG_LOCK_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
-	status = libinput_device_config_tap_set_drag_lock_enabled(device,
-								  3);
+	status = libinput_device_config_tap_set_drag_lock_enabled(device, 3);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_INVALID);
 }
 END_TEST
@@ -4281,8 +4083,7 @@ touchpad_has_palm_pressure(struct litest
 		return false;
 
 	if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_PRESSURE))
-		return libevdev_get_abs_resolution(evdev,
-						   ABS_MT_PRESSURE) == 0;
+		return libevdev_get_abs_resolution(evdev, ABS_MT_PRESSURE) == 0;
 
 	return false;
 }
@@ -4293,7 +4094,7 @@ START_TEST(touchpad_tap_palm_on_idle)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -4320,7 +4121,7 @@ START_TEST(touchpad_tap_palm_on_touch)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -4348,7 +4149,7 @@ START_TEST(touchpad_tap_palm_on_touch_ho
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -4361,9 +4162,7 @@ START_TEST(touchpad_tap_palm_on_touch_ho
 	/* Finger down is palm after tap timeout */
 
 	litest_touch_down(dev, 0, 50, 50);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1);
 	litest_touch_up(dev, 0);
 
@@ -4379,7 +4178,7 @@ START_TEST(touchpad_tap_palm_on_touch_ho
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -4410,7 +4209,7 @@ START_TEST(touchpad_tap_palm_on_tapped)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers");
 	unsigned int button = 0;
@@ -4466,21 +4265,15 @@ START_TEST(touchpad_tap_palm_on_tapped)
 	}
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1);
 	litest_touch_up(dev, 0);
 
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -4492,7 +4285,7 @@ START_TEST(touchpad_tap_palm_on_tapped_p
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers");
 	unsigned int button = 0;
@@ -4548,21 +4341,15 @@ START_TEST(touchpad_tap_palm_on_tapped_p
 	}
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_touch_down_extended(dev, 0, 50, 50, axes);
 	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1);
 	litest_touch_up(dev, 0);
 
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -4574,12 +4361,11 @@ START_TEST(touchpad_tap_palm_on_tapped_d
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers_1st"),
 	    nfingers2 = litest_test_param_get_i32(test_env->params, "fingers_2nd");
-	unsigned int button = 0,
-		     button2 = 0;
+	unsigned int button = 0, button2 = 0;
 
 	if (!touchpad_has_palm_pressure(dev))
 		return LITEST_NOT_APPLICABLE;
@@ -4648,9 +4434,7 @@ START_TEST(touchpad_tap_palm_on_tapped_d
 	}
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1);
@@ -4679,20 +4463,12 @@ START_TEST(touchpad_tap_palm_on_tapped_d
 		litest_touch_up(dev, 1);
 		break;
 	}
-	litest_dispatch(li);
 
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-	litest_assert_button_event(li,
-				   button2,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   button2,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button2, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button2, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_touch_up(dev, 0);
 	litest_assert_empty_queue(li);
@@ -4705,7 +4481,7 @@ START_TEST(touchpad_tap_palm_on_drag)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers");
 	unsigned int button = 0;
@@ -4761,21 +4537,15 @@ START_TEST(touchpad_tap_palm_on_drag)
 	}
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_touch_down(dev, 0, 50, 50);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_touch_up(dev, 0);
 	litest_assert_empty_queue(li);
@@ -4788,11 +4558,10 @@ START_TEST(touchpad_tap_palm_on_drag_2fg
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int which = litest_test_param_get_i32(test_env->params, "which"),
-	    this = which % 2,
-	    other = (which + 1) % 2,
+	    this = which % 2, other = (which + 1) % 2,
 	    nfingers = litest_test_param_get_i32(test_env->params, "fingers");
 	unsigned int button = 0;
 
@@ -4847,14 +4616,10 @@ START_TEST(touchpad_tap_palm_on_drag_2fg
 	}
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_touch_down(dev, this, 50, 50);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 	litest_touch_down(dev, other, 60, 50);
 	litest_dispatch(li);
 
@@ -4862,13 +4627,10 @@ START_TEST(touchpad_tap_palm_on_drag_2fg
 	litest_dispatch(li);
 
 	litest_touch_move_to(dev, other, 60, 50, 65, 50, 10);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 	litest_touch_up(dev, other);
 
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_touch_up(dev, this);
 	litest_assert_empty_queue(li);
@@ -4881,11 +4643,10 @@ START_TEST(touchpad_tap_palm_on_touch_2)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int which = litest_test_param_get_i32(test_env->params, "which"),
-	    this = which % 2,
-	    other = (which + 1) % 2;
+	    this = which % 2, other = (which + 1) % 2;
 
 	if (!touchpad_has_palm_pressure(dev))
 		return LITEST_NOT_APPLICABLE;
@@ -4904,13 +4665,9 @@ START_TEST(touchpad_tap_palm_on_touch_2)
 	litest_touch_up(dev, other);
 
 	litest_dispatch(li);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_timeout_tap();
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap(li);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -4922,11 +4679,10 @@ START_TEST(touchpad_tap_palm_on_touch_2_
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int which = litest_test_param_get_i32(test_env->params, "which"),
-	    this = which % 2,
-	    other = (which + 1) % 2;
+	    this = which % 2, other = (which + 1) % 2;
 
 	if (!touchpad_has_palm_pressure(dev))
 		return LITEST_NOT_APPLICABLE;
@@ -4949,13 +4705,9 @@ START_TEST(touchpad_tap_palm_on_touch_2_
 	litest_touch_up(dev, other);
 
 	litest_dispatch(li);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_timeout_tap();
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap(li);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -4967,7 +4719,7 @@ START_TEST(touchpad_tap_palm_on_touch_3)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int which = litest_test_param_get_i32(test_env->params, "which"),
 	    this = which % 3;
@@ -4996,13 +4748,9 @@ START_TEST(touchpad_tap_palm_on_touch_3)
 	litest_touch_up(dev, (this + 2) % 3);
 
 	litest_dispatch(li);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_timeout_tap();
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap(li);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -5014,7 +4762,7 @@ START_TEST(touchpad_tap_palm_on_touch_3_
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int which = litest_test_param_get_i32(test_env->params, "which"),
 	    this = which % 3;
@@ -5035,8 +4783,7 @@ START_TEST(touchpad_tap_palm_on_touch_3_
 	litest_touch_down(dev, (this + 1) % 3, 60, 50);
 	litest_touch_down(dev, (this + 2) % 3, 70, 50);
 	litest_drain_events(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	litest_touch_move_to_extended(dev, this, 50, 50, 50, 50, axes, 1);
 	litest_touch_up(dev, this);
@@ -5048,13 +4795,9 @@ START_TEST(touchpad_tap_palm_on_touch_3_
 	litest_touch_up(dev, (this + 2) % 3);
 
 	litest_dispatch(li);
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_timeout_tap();
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap(li);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -5066,7 +4809,7 @@ START_TEST(touchpad_tap_palm_on_touch_4)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int which = litest_test_param_get_i32(test_env->params, "which"),
 	    this = which % 4;
@@ -5106,7 +4849,7 @@ START_TEST(touchpad_tap_palm_after_tap)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers");
 	unsigned int button = 0;
@@ -5161,19 +4904,14 @@ START_TEST(touchpad_tap_palm_after_tap)
 	litest_dispatch(li);
 
 	litest_dispatch(li);
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
 
-	litest_timeout_tap();
-	litest_assert_button_event(li,
-				   button,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_timeout_tap(li);
+	litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -5185,11 +4923,10 @@ START_TEST(touchpad_tap_palm_multitap)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers"),
-	    range = litest_test_param_get_i32(test_env->params, "taps"),
-	    ntaps;
+	    range = litest_test_param_get_i32(test_env->params, "taps"), ntaps;
 	unsigned int button = 0;
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -5248,17 +4985,11 @@ START_TEST(touchpad_tap_palm_multitap)
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	for (ntaps = 0; ntaps <= range; ntaps++) {
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_PRESSED);
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 	}
 
 	litest_assert_empty_queue(li);
@@ -5271,11 +5002,10 @@ START_TEST(touchpad_tap_palm_multitap_ti
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers"),
-	    range = litest_test_param_get_i32(test_env->params, "taps"),
-	    ntaps;
+	    range = litest_test_param_get_i32(test_env->params, "taps"), ntaps;
 	unsigned int button = 0;
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -5333,17 +5063,11 @@ START_TEST(touchpad_tap_palm_multitap_ti
 
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	for (ntaps = 0; ntaps <= range; ntaps++) {
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_PRESSED);
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 	}
 
 	litest_assert_empty_queue(li);
@@ -5356,11 +5080,10 @@ START_TEST(touchpad_tap_palm_multitap_do
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers"),
-	    range = litest_test_param_get_i32(test_env->params, "taps"),
-	    ntaps;
+	    range = litest_test_param_get_i32(test_env->params, "taps"), ntaps;
 	unsigned int button = 0;
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -5449,16 +5172,11 @@ START_TEST(touchpad_tap_palm_multitap_do
 		msleep(10);
 	}
 
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	for (ntaps = 0; ntaps <= 2 * range + 1; ntaps++) {
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_PRESSED);
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 	}
 
 	litest_touch_up(dev, 0);
@@ -5472,11 +5190,10 @@ START_TEST(touchpad_tap_palm_multitap_cl
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	int nfingers = litest_test_param_get_i32(test_env->params, "fingers"),
-	    range = litest_test_param_get_i32(test_env->params, "taps"),
-	    ntaps;
+	    range = litest_test_param_get_i32(test_env->params, "taps"), ntaps;
 	unsigned int button = 0;
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -5542,21 +5259,13 @@ START_TEST(touchpad_tap_palm_multitap_cl
 	litest_dispatch(li);
 
 	for (ntaps = 0; ntaps <= range; ntaps++) {
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_PRESSED);
-		litest_assert_button_event(li,
-					   button,
-					   LIBINPUT_BUTTON_STATE_RELEASED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li, button, LIBINPUT_BUTTON_STATE_RELEASED);
 	}
 
 	/* the click */
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_touch_up(dev, 0);
 	litest_assert_empty_queue(li);
 }
@@ -5568,7 +5277,7 @@ START_TEST(touchpad_tap_palm_click_then_
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -5585,28 +5294,18 @@ START_TEST(touchpad_tap_palm_click_then_
 	litest_button_click(dev, BTN_LEFT, false);
 	litest_dispatch(li);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_touch_up(dev, 0);
 	litest_assert_empty_queue(li);
 
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -5619,7 +5318,7 @@ START_TEST(touchpad_tap_palm_dwt_tap)
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -5641,8 +5340,7 @@ START_TEST(touchpad_tap_palm_dwt_tap)
 
 	litest_keyboard_key(keyboard, KEY_B, false);
 	litest_drain_events(li);
-	litest_timeout_dwt_long();
-	litest_dispatch(li);
+	litest_timeout_dwt_long(li);
 
 	/* Changes to palm after dwt timeout */
 	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1);
@@ -5653,7 +5351,7 @@ START_TEST(touchpad_tap_palm_dwt_tap)
 
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -5662,8 +5360,7 @@ START_TEST(touchpad_tap_palm_3fg_start)
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	if (litest_slot_count(dev) < 3 ||
-	    !litest_has_palm_detect_size(dev))
+	if (litest_slot_count(dev) < 3 || !litest_has_palm_detect_size(dev))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_tap(dev->libinput_device);
@@ -5684,16 +5381,15 @@ START_TEST(touchpad_tap_palm_3fg_start)
 	litest_touch_up(dev, 1);
 
 	litest_dispatch(li);
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li, BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 }
 END_TEST
 
 TEST_COLLECTION(touchpad_tap)
 {
+	/* clang-format off */
 	litest_add(touchpad_1fg_tap, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_with_parameters(params, "fingers_1st", 'i', 3, 1, 2, 3,
 				       "fingers_2nd", 'i', 3, 1, 2, 3) {
@@ -5763,10 +5459,12 @@ TEST_COLLECTION(touchpad_tap)
 
 	litest_add(clickpad_1fg_tap_click, LITEST_CLICKPAD, LITEST_ANY);
 	litest_add(clickpad_2fg_tap_click, LITEST_CLICKPAD, LITEST_SINGLE_TOUCH|LITEST_APPLE_CLICKPAD);
+	/* clang-format on */
 }
 
 TEST_COLLECTION(touchpad_tap_drag)
 {
+	/* clang-format off */
 	litest_add(touchpad_drag_lock_default_disabled, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add(touchpad_drag_lock_default_unavailable, LITEST_ANY, LITEST_TOUCHPAD);
 
@@ -5818,10 +5516,12 @@ TEST_COLLECTION(touchpad_tap_drag)
 	litest_add(touchpad_drag_config_invalid, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add(touchpad_drag_config_unsupported, LITEST_ANY, LITEST_TOUCHPAD);
 	litest_add(touchpad_drag_config_enabledisable, LITEST_TOUCHPAD, LITEST_ANY);
+	/* clang-format on */
 }
 
 TEST_COLLECTION(touchpad_tap_palm)
 {
+	/* clang-format off */
 	litest_add(touchpad_tap_palm_on_idle, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add(touchpad_tap_palm_on_touch, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add(touchpad_tap_palm_on_touch_hold_timeout, LITEST_TOUCHPAD, LITEST_ANY);
@@ -5871,4 +5571,5 @@ TEST_COLLECTION(touchpad_tap_palm)
 	litest_add(touchpad_tap_palm_click_then_tap, LITEST_CLICKPAD, LITEST_ANY);
 	litest_add(touchpad_tap_palm_dwt_tap, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add(touchpad_tap_palm_3fg_start, LITEST_TOUCHPAD, LITEST_ANY);
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-touchpad.c 1.30.0-1/test/test-touchpad.c
--- 1.28.1-1/test/test-touchpad.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-touchpad.c	2025-11-25 03:40:43.000000000 +0000
@@ -38,8 +38,7 @@ has_disable_while_typing(struct litest_d
 }
 
 static inline struct litest_device *
-dwt_init_paired_keyboard(struct libinput *li,
-			 struct litest_device *touchpad)
+dwt_init_paired_keyboard(struct libinput *li, struct litest_device *touchpad)
 {
 	enum litest_device_type which = LITEST_KEYBOARD;
 
@@ -124,9 +123,7 @@ test_2fg_scroll(struct litest_device *de
 
 	/* Avoid a small scroll being seen as a tap */
 	if (want_sleep) {
-		litest_dispatch(li);
-		litest_timeout_tap();
-		litest_dispatch(li);
+		litest_timeout_tap(li);
 	}
 
 	litest_touch_up(dev, 1);
@@ -189,7 +186,7 @@ START_TEST(touchpad_2fg_scroll_initially
 		return LITEST_NOT_APPLICABLE;
 
 	litest_assert_int_eq(libinput_device_get_size(dev->libinput_device, &w, &h), 0);
-	ratio = w/h;
+	ratio = w / h;
 	litest_enable_2fg_scroll(dev);
 	litest_drain_events(li);
 
@@ -225,11 +222,12 @@ START_TEST(touchpad_2fg_scroll_initially
 		struct libinput_event_pointer *ptrev;
 
 		ptrev = litest_is_axis_event(event,
-				LIBINPUT_EVENT_POINTER_SCROLL_FINGER,
-				LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
-				LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
-		litest_assert(!libinput_event_pointer_has_axis(ptrev,
-				LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL));
+					     LIBINPUT_EVENT_POINTER_SCROLL_FINGER,
+					     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
+					     LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
+		litest_assert(!libinput_event_pointer_has_axis(
+			ptrev,
+			LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL));
 		libinput_event_destroy(event);
 		event = libinput_get_event(li);
 		expected_nevents--;
@@ -244,26 +242,25 @@ START_TEST(touchpad_2fg_scroll_initially
 END_TEST
 
 static bool
-is_single_axis_2fg_scroll(struct litest_device *dev,
-			   enum libinput_pointer_axis axis)
+is_single_axis_2fg_scroll(struct litest_device *dev, enum libinput_pointer_axis axis)
 {
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
 	struct libinput_event_pointer *ptrev;
 	enum libinput_pointer_axis on_axis = axis;
 	enum libinput_pointer_axis off_axis =
-		(axis == LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL) ?
-		LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL :
-		LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
+		(axis == LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)
+			? LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL
+			: LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
 	bool has_on_axis, has_off_axis;
 	bool val = true;
 
 	event = libinput_get_event(li);
 	while (event) {
 		ptrev = litest_is_axis_event(event,
-				LIBINPUT_EVENT_POINTER_SCROLL_FINGER,
-				on_axis,
-				LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
+					     LIBINPUT_EVENT_POINTER_SCROLL_FINGER,
+					     on_axis,
+					     LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
 
 		has_on_axis = libinput_event_pointer_has_axis(ptrev, on_axis);
 		has_off_axis = libinput_event_pointer_has_axis(ptrev, off_axis);
@@ -276,11 +273,14 @@ is_single_axis_2fg_scroll(struct litest_
 			libinput_event_destroy(event);
 			event = libinput_get_event(li);
 			litest_assert_notnull(event);
-			ptrev = litest_is_axis_event(event,
-					     LIBINPUT_EVENT_POINTER_SCROLL_FINGER,
-					     on_axis,
-					     LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
-			litest_assert(val == (litest_event_pointer_get_value(ptrev, off_axis) == 0.0));
+			ptrev = litest_is_axis_event(
+				event,
+				LIBINPUT_EVENT_POINTER_SCROLL_FINGER,
+				on_axis,
+				LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
+			litest_assert(val == (litest_event_pointer_get_value(
+						      ptrev,
+						      off_axis) == 0.0));
 			break;
 		}
 
@@ -301,10 +301,10 @@ START_TEST(touchpad_2fg_scroll_axis_lock
 	struct libinput *li = dev->libinput;
 	enum libinput_pointer_axis axis;
 	double delta[4][2] = {
-		{ 7,  40},
-		{ 7, -40},
-		{-7,  40},
-		{-7, -40}
+		{ 7, 40 },
+		{ 7, -40 },
+		{ -7, 40 },
+		{ -7, -40 },
 	};
 	/* 10 degrees off from horiz/vert should count as straight */
 
@@ -406,10 +406,10 @@ START_TEST(touchpad_2fg_scroll_slow_dist
 	last_low_res_event_found = false;
 
 	/* We want to move > 5 mm. */
-	litest_assert_int_eq(libinput_device_get_size(dev->libinput_device,
-						  &width,
-						  &height), 0);
-	y_move = 100.0/height * 7;
+	litest_assert_int_eq(
+		libinput_device_get_size(dev->libinput_device, &width, &height),
+		0);
+	y_move = 100.0 / height * 7;
 
 	litest_enable_2fg_scroll(dev);
 	litest_disable_hold_gestures(dev->libinput_device);
@@ -433,8 +433,9 @@ START_TEST(touchpad_2fg_scroll_slow_dist
 					     LIBINPUT_EVENT_POINTER_SCROLL_FINGER,
 					     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
 					     0);
-		axisval = litest_event_pointer_get_value(ptrev,
-				LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+		axisval = litest_event_pointer_get_value(
+			ptrev,
+			LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
 
 		if (litest_is_high_res_axis_event(event)) {
 			litest_assert(!last_hi_res_event_found);
@@ -503,11 +504,7 @@ START_TEST(touchpad_2fg_scroll_semi_mt)
 	litest_touch_down(dev, 0, 20, 20);
 	litest_touch_down(dev, 1, 30, 20);
 	litest_dispatch(li);
-	litest_touch_move_two_touches(dev,
-				      20, 20,
-				      30, 20,
-				      30, 40,
-				      10);
+	litest_touch_move_two_touches(dev, 20, 20, 30, 20, 30, 40, 10);
 
 	litest_assert_only_axis_events(li, LIBINPUT_EVENT_POINTER_SCROLL_FINGER);
 }
@@ -534,7 +531,7 @@ START_TEST(touchpad_2fg_scroll_return_to
 	litest_touch_move_two_touches(dev, 49, 50, 51, 50, 0, 20, 5);
 	litest_touch_up(dev, 1);
 	litest_dispatch(li);
-	litest_timeout_finger_switch();
+	litest_timeout_finger_switch(li);
 	litest_dispatch(li);
 	litest_assert_only_axis_events(li, LIBINPUT_EVENT_POINTER_SCROLL_FINGER);
 
@@ -546,7 +543,7 @@ START_TEST(touchpad_2fg_scroll_return_to
 	litest_touch_move_two_touches(dev, 49, 50, 51, 50, 0, 20, 5);
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
-	litest_timeout_finger_switch();
+	litest_timeout_finger_switch(li);
 	litest_dispatch(li);
 	litest_assert_only_axis_events(li, LIBINPUT_EVENT_POINTER_SCROLL_FINGER);
 
@@ -564,8 +561,7 @@ START_TEST(touchpad_2fg_scroll_from_btna
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	if (!litest_has_2fg_scroll(dev) ||
-	    !litest_has_btnareas(dev))
+	if (!litest_has_2fg_scroll(dev) || !litest_has_btnareas(dev))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_2fg_scroll(dev);
@@ -602,9 +598,16 @@ START_TEST(touchpad_scroll_natural_defau
 
 	int enabled = libevdev_get_id_vendor(dev->evdev) == VENDOR_ID_APPLE;
 
-	litest_assert_int_ge(libinput_device_config_scroll_has_natural_scroll(dev->libinput_device), 1);
-	litest_assert_int_eq(libinput_device_config_scroll_get_natural_scroll_enabled(dev->libinput_device), enabled);
-	litest_assert_int_eq(libinput_device_config_scroll_get_default_natural_scroll_enabled(dev->libinput_device), enabled);
+	litest_assert_int_ge(
+		libinput_device_config_scroll_has_natural_scroll(dev->libinput_device),
+		1);
+	litest_assert_int_eq(libinput_device_config_scroll_get_natural_scroll_enabled(
+				     dev->libinput_device),
+			     enabled);
+	litest_assert_int_eq(
+		libinput_device_config_scroll_get_default_natural_scroll_enabled(
+			dev->libinput_device),
+		enabled);
 }
 END_TEST
 
@@ -613,13 +616,21 @@ START_TEST(touchpad_scroll_natural_enabl
 	struct litest_device *dev = litest_current_device();
 	enum libinput_config_status status;
 
-	status = libinput_device_config_scroll_set_natural_scroll_enabled(dev->libinput_device, 1);
+	status = libinput_device_config_scroll_set_natural_scroll_enabled(
+		dev->libinput_device,
+		1);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
-	litest_assert_int_eq(libinput_device_config_scroll_get_natural_scroll_enabled(dev->libinput_device), 1);
-
-	status = libinput_device_config_scroll_set_natural_scroll_enabled(dev->libinput_device, 0);
+	litest_assert_int_eq(libinput_device_config_scroll_get_natural_scroll_enabled(
+				     dev->libinput_device),
+			     1);
+
+	status = libinput_device_config_scroll_set_natural_scroll_enabled(
+		dev->libinput_device,
+		0);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
-	litest_assert_int_eq(libinput_device_config_scroll_get_natural_scroll_enabled(dev->libinput_device), 0);
+	litest_assert_int_eq(libinput_device_config_scroll_get_natural_scroll_enabled(
+				     dev->libinput_device),
+			     0);
 }
 END_TEST
 
@@ -634,7 +645,8 @@ START_TEST(touchpad_scroll_natural_2fg)
 	litest_enable_2fg_scroll(dev);
 	litest_drain_events(li);
 
-	libinput_device_config_scroll_set_natural_scroll_enabled(dev->libinput_device, 1);
+	libinput_device_config_scroll_set_natural_scroll_enabled(dev->libinput_device,
+								 1);
 
 	test_2fg_scroll(dev, 0.1, 40, false);
 	litest_assert_scroll(li,
@@ -656,7 +668,6 @@ START_TEST(touchpad_scroll_natural_2fg)
 			     LIBINPUT_EVENT_POINTER_SCROLL_FINGER,
 			     LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
 			     9);
-
 }
 END_TEST
 
@@ -668,7 +679,8 @@ START_TEST(touchpad_scroll_natural_edge)
 	litest_enable_edge_scroll(dev);
 	litest_drain_events(li);
 
-	libinput_device_config_scroll_set_natural_scroll_enabled(dev->libinput_device, 1);
+	libinput_device_config_scroll_set_natural_scroll_enabled(dev->libinput_device,
+								 1);
 
 	litest_touch_down(dev, 0, 99, 20);
 	litest_touch_move_to(dev, 0, 99, 20, 99, 80, 10);
@@ -691,7 +703,6 @@ START_TEST(touchpad_scroll_natural_edge)
 			     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
 			     4);
 	litest_assert_empty_queue(li);
-
 }
 END_TEST
 
@@ -870,10 +881,10 @@ START_TEST(touchpad_scroll_defaults)
 	litest_assert_int_eq(method, expected);
 
 	status = libinput_device_config_scroll_set_method(device,
-					  LIBINPUT_CONFIG_SCROLL_EDGE);
+							  LIBINPUT_CONFIG_SCROLL_EDGE);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	status = libinput_device_config_scroll_set_method(device,
-					  LIBINPUT_CONFIG_SCROLL_2FG);
+							  LIBINPUT_CONFIG_SCROLL_2FG);
 
 	if (should_have_2fg)
 		litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
@@ -891,10 +902,10 @@ START_TEST(touchpad_edge_scroll_timeout)
 	int nevents = 0;
 	double mm; /* one mm in percent of the device */
 
-	litest_assert_int_eq(libinput_device_get_size(dev->libinput_device,
-						  &width,
-						  &height), 0);
-	mm = 100.0/height;
+	litest_assert_int_eq(
+		libinput_device_get_size(dev->libinput_device, &width, &height),
+		0);
+	mm = 100.0 / height;
 
 	/* timeout-based scrolling is disabled when software buttons are
 	 * active, so switch to clickfinger. Not all test devices support
@@ -910,20 +921,20 @@ START_TEST(touchpad_edge_scroll_timeout)
 	 * the scroll threshold of 2mm */
 	litest_touch_down(dev, 0, 99, 20);
 	litest_dispatch(li);
-	litest_timeout_hysteresis();
+	litest_timeout_hysteresis(li);
 	litest_dispatch(li);
 
-	litest_touch_move_to(dev, 0, 99, 20, 99, 20 + mm/2, 8);
+	litest_touch_move_to(dev, 0, 99, 20, 99, 20 + mm / 2, 8);
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_edgescroll();
+	litest_timeout_edgescroll(li);
 	litest_dispatch(li);
 
 	litest_assert_empty_queue(li);
 
 	/* now move slowly up to the 2mm scroll threshold. we expect events */
-	litest_touch_move_to(dev, 0, 99, 20 + mm/2, 99, 20 + mm * 2, 20);
+	litest_touch_move_to(dev, 0, 99, 20 + mm / 2, 99, 20 + mm * 2, 20);
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
@@ -937,8 +948,9 @@ START_TEST(touchpad_edge_scroll_timeout)
 					     LIBINPUT_EVENT_POINTER_SCROLL_FINGER,
 					     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
 					     0);
-		value = litest_event_pointer_get_value(ptrev,
-						       LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+		value = litest_event_pointer_get_value(
+			ptrev,
+			LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
 		litest_assert_double_lt(value, 5.0);
 		libinput_event_destroy(event);
 		nevents++;
@@ -1126,9 +1138,7 @@ START_TEST(touchpad_edge_scroll_buttonar
 					LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
 
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_RIGHT,
-			       LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_is_button_event(event, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	libinput_event_destroy(event);
 
@@ -1176,9 +1186,7 @@ START_TEST(touchpad_edge_scroll_clickfin
 					LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
 
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_LEFT,
-			       LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_is_button_event(event, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 
 	libinput_event_destroy(event);
 
@@ -1238,8 +1246,7 @@ START_TEST(touchpad_palm_detect_at_edge)
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	if (!litest_has_palm_detect_size(dev) ||
-	    !litest_has_2fg_scroll(dev))
+	if (!litest_has_palm_detect_size(dev) || !litest_has_2fg_scroll(dev))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_2fg_scroll(dev);
@@ -1307,8 +1314,7 @@ START_TEST(touchpad_palm_detect_at_botto
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	if (!litest_has_palm_detect_size(dev) ||
-	    !litest_has_2fg_scroll(dev))
+	if (!litest_has_palm_detect_size(dev) || !litest_has_2fg_scroll(dev))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_2fg_scroll(dev);
@@ -1337,8 +1343,7 @@ START_TEST(touchpad_palm_detect_at_top_c
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	if (!litest_has_palm_detect_size(dev) ||
-	    !litest_has_2fg_scroll(dev))
+	if (!litest_has_palm_detect_size(dev) || !litest_has_2fg_scroll(dev))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_2fg_scroll(dev);
@@ -1369,8 +1374,7 @@ START_TEST(touchpad_palm_detect_palm_sta
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	if (!litest_has_palm_detect_size(dev) ||
-	    !litest_has_2fg_scroll(dev))
+	if (!litest_has_palm_detect_size(dev) || !litest_has_2fg_scroll(dev))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_2fg_scroll(dev);
@@ -1411,8 +1415,7 @@ START_TEST(touchpad_palm_detect_palm_bec
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	if (!litest_has_palm_detect_size(dev) ||
-	    !litest_has_2fg_scroll(dev))
+	if (!litest_has_palm_detect_size(dev) || !litest_has_2fg_scroll(dev))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_2fg_scroll(dev);
@@ -1531,9 +1534,7 @@ START_TEST(touchpad_palm_detect_no_tap_t
 
 	litest_touch_down(dev, 0, 50, 1);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -1552,30 +1553,22 @@ START_TEST(touchpad_palm_detect_tap_hard
 
 	litest_touch_down(dev, 0, 95, 5);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 
 	litest_touch_down(dev, 0, 5, 5);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 
 	litest_touch_down(dev, 0, 5, 99);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 
 	litest_touch_down(dev, 0, 95, 99);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -1597,44 +1590,28 @@ START_TEST(touchpad_palm_detect_tap_soft
 	 * the palm detection edge zone -> expect palm detection */
 	litest_touch_down(dev, 0, 99, 99);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 
 	litest_touch_down(dev, 0, 1, 99);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 
 	/* Two touches in the software button area, but
 	 * not in the palm detection edge zone -> expect taps */
 	litest_touch_down(dev, 0, 10, 99);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_timeout_tap(li);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 
 	litest_touch_down(dev, 0, 90, 99);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_timeout_tap(li);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -1656,30 +1633,22 @@ START_TEST(touchpad_palm_detect_tap_clic
 	 * inside the palm detection edge zone*/
 	litest_touch_down(dev, 0, 95, 5);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 
 	litest_touch_down(dev, 0, 5, 5);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 
 	litest_touch_down(dev, 0, 5, 99);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 
 	litest_touch_down(dev, 0, 95, 99);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -1689,8 +1658,7 @@ START_TEST(touchpad_no_palm_detect_2fg_s
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	if (!litest_has_palm_detect_size(dev) ||
-	    !litest_has_2fg_scroll(dev))
+	if (!litest_has_palm_detect_size(dev) || !litest_has_2fg_scroll(dev))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_2fg_scroll(dev);
@@ -1716,8 +1684,7 @@ START_TEST(touchpad_palm_detect_both_edg
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 
-	if (!litest_has_palm_detect_size(dev) ||
-	    !litest_has_2fg_scroll(dev))
+	if (!litest_has_palm_detect_size(dev) || !litest_has_2fg_scroll(dev))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_2fg_scroll(dev);
@@ -1822,15 +1789,13 @@ START_TEST(touchpad_palm_detect_tool_pal
 	litest_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
 	litest_touch_up(dev, 0);
 	litest_pop_event_frame(dev);
-	litest_dispatch(li);
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 
 	litest_touch_down(dev, 0, 50, 50);
 	litest_dispatch(li);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 
 	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
@@ -1858,8 +1823,7 @@ START_TEST(touchpad_palm_detect_tool_pal
 	litest_assert_empty_queue(li);
 
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 
 	litest_assert_empty_queue(li);
 }
@@ -1874,8 +1838,7 @@ touchpad_has_palm_pressure(struct litest
 		return false;
 
 	if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_PRESSURE))
-		return libevdev_get_abs_resolution(evdev,
-						   ABS_MT_PRESSURE) == 0;
+		return libevdev_get_abs_resolution(evdev, ABS_MT_PRESSURE) == 0;
 
 	return false;
 }
@@ -1886,7 +1849,7 @@ START_TEST(touchpad_palm_detect_pressure
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -1910,7 +1873,7 @@ START_TEST(touchpad_palm_detect_pressure
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -1925,15 +1888,13 @@ START_TEST(touchpad_palm_detect_pressure
 	litest_touch_down(dev, 0, 50, 80);
 	litest_touch_move_extended(dev, 0, 51, 99, axes);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_empty_queue(li);
 
 	/* make sure normal tap still works */
 	litest_touch_down(dev, 0, 50, 99);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON);
 }
 END_TEST
@@ -1944,7 +1905,7 @@ START_TEST(touchpad_palm_detect_pressure
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -1957,9 +1918,7 @@ START_TEST(touchpad_palm_detect_pressure
 
 	/* event in state HOLD is thumb */
 	litest_touch_down(dev, 0, 50, 99);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 	litest_touch_move_extended(dev, 0, 51, 99, axes);
 	litest_touch_up(dev, 0);
 	litest_assert_empty_queue(li);
@@ -1967,8 +1926,7 @@ START_TEST(touchpad_palm_detect_pressure
 	/* make sure normal tap still works */
 	litest_touch_down(dev, 0, 50, 99);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON);
 }
 END_TEST
@@ -1979,7 +1937,7 @@ START_TEST(touchpad_palm_detect_pressure
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -1992,9 +1950,7 @@ START_TEST(touchpad_palm_detect_pressure
 
 	/* event in state HOLD is thumb */
 	litest_touch_down(dev, 0, 50, 99);
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 	litest_touch_move_extended(dev, 0, 51, 99, axes);
 
 	litest_assert_empty_queue(li);
@@ -2007,17 +1963,14 @@ START_TEST(touchpad_palm_detect_pressure
 	litest_assert_empty_queue(li);
 
 	/* timeout -> into HOLD, no event on release */
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 	litest_touch_up(dev, 1);
 	litest_assert_empty_queue(li);
 
 	/* make sure normal tap still works */
 	litest_touch_down(dev, 0, 50, 99);
 	litest_touch_up(dev, 0);
-	litest_dispatch(li);
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON);
 }
 END_TEST
@@ -2028,7 +1981,7 @@ START_TEST(touchpad_palm_detect_move_and
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -2049,15 +2002,10 @@ START_TEST(touchpad_palm_detect_move_and
 	/* thumb is resting, check if tapping still works */
 	litest_touch_down(dev, 1, 50, 50);
 	litest_touch_up(dev, 1);
-	litest_dispatch(li);
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	litest_assert_empty_queue(li);
 }
 END_TEST
@@ -2068,7 +2016,7 @@ START_TEST(touchpad_palm_detect_pressure
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -2095,7 +2043,7 @@ START_TEST(touchpad_palm_detect_pressure
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -2120,11 +2068,10 @@ START_TEST(touchpad_palm_detect_pressure
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
-	if (!touchpad_has_palm_pressure(dev) ||
-	    !litest_has_palm_detect_size(dev) ||
+	if (!touchpad_has_palm_pressure(dev) || !litest_has_palm_detect_size(dev) ||
 	    !litest_has_2fg_scroll(dev))
 		return LITEST_NOT_APPLICABLE;
 
@@ -2149,7 +2096,7 @@ START_TEST(touchpad_palm_detect_pressure
 	struct libinput *li = touchpad->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(touchpad))
@@ -2169,8 +2116,7 @@ START_TEST(touchpad_palm_detect_pressure
 	litest_touch_move_to_extended(touchpad, 0, 50, 50, 20, 50, axes, 20);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_dwt_short();
-	litest_dispatch(li);
+	litest_timeout_dwt_short(li);
 	litest_assert_empty_queue(li);
 
 	/* after dwt timeout, pressure blocks events */
@@ -2179,7 +2125,7 @@ START_TEST(touchpad_palm_detect_pressure
 
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -2189,7 +2135,7 @@ START_TEST(touchpad_palm_ignore_threshol
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	litest_disable_tap(dev->libinput_device);
@@ -2211,7 +2157,7 @@ START_TEST(touchpad_palm_clickfinger_pre
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -2242,7 +2188,7 @@ START_TEST(touchpad_palm_clickfinger_pre
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 75 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_palm_pressure(dev))
@@ -2294,7 +2240,7 @@ START_TEST(touchpad_palm_clickfinger_siz
 		{ ABS_MT_TOUCH_MAJOR, 0 },
 		{ ABS_MT_TOUCH_MINOR, 0 },
 		{ ABS_MT_ORIENTATION, 0 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_touch_size(dev))
@@ -2327,7 +2273,7 @@ START_TEST(touchpad_palm_clickfinger_siz
 		{ ABS_MT_TOUCH_MAJOR, 0 },
 		{ ABS_MT_TOUCH_MINOR, 0 },
 		{ ABS_MT_ORIENTATION, 0 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_touch_size(dev))
@@ -2375,25 +2321,15 @@ START_TEST(touchpad_left_handed)
 	litest_button_click(dev, BTN_LEFT, 1);
 	litest_button_click(dev, BTN_LEFT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_button_click(dev, BTN_RIGHT, 1);
 	litest_button_click(dev, BTN_RIGHT, 0);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-
-	if (libevdev_has_event_code(dev->evdev,
-				    EV_KEY,
-				    BTN_MIDDLE)) {
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
+
+	if (libevdev_has_event_code(dev->evdev, EV_KEY, BTN_MIDDLE)) {
 		litest_button_click(dev, BTN_MIDDLE, 1);
 		litest_button_click(dev, BTN_MIDDLE, 0);
 		litest_assert_button_event(li,
@@ -2438,12 +2374,8 @@ START_TEST(touchpad_left_handed_clickpad
 	litest_button_click(dev, BTN_LEFT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_drain_events(li);
 	litest_touch_down(dev, 0, 90, 90);
@@ -2451,12 +2383,8 @@ START_TEST(touchpad_left_handed_clickpad
 	litest_button_click(dev, BTN_LEFT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_drain_events(li);
 	litest_touch_down(dev, 0, 50, 50);
@@ -2464,12 +2392,8 @@ START_TEST(touchpad_left_handed_clickpad
 	litest_button_click(dev, BTN_LEFT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -2493,12 +2417,8 @@ START_TEST(touchpad_left_handed_clickfin
 	litest_touch_up(dev, 0);
 
 	/* Clickfinger is unaffected by left-handed setting */
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_drain_events(li);
 	litest_touch_down(dev, 0, 10, 90);
@@ -2508,12 +2428,8 @@ START_TEST(touchpad_left_handed_clickfin
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
 
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -2537,18 +2453,11 @@ START_TEST(touchpad_left_handed_tapping)
 
 	litest_touch_down(dev, 0, 50, 50);
 	litest_touch_up(dev, 0);
-
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	/* Tapping is unaffected by left-handed setting */
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -2574,18 +2483,11 @@ START_TEST(touchpad_left_handed_tapping_
 	litest_touch_down(dev, 1, 70, 50);
 	litest_touch_up(dev, 1);
 	litest_touch_up(dev, 0);
-
-	litest_dispatch(li);
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	/* Tapping is unaffected by left-handed setting */
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -2608,18 +2510,12 @@ START_TEST(touchpad_left_handed_delayed)
 
 	litest_button_click(dev, BTN_LEFT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	/* left-handed takes effect now */
 	litest_button_click(dev, BTN_RIGHT, 1);
-	litest_dispatch(li);
-	litest_timeout_middlebutton();
-	litest_dispatch(li);
+	litest_timeout_middlebutton(li);
 	litest_button_click(dev, BTN_LEFT, 1);
 	litest_dispatch(li);
 
@@ -2629,18 +2525,10 @@ START_TEST(touchpad_left_handed_delayed)
 	litest_button_click(dev, BTN_RIGHT, 0);
 	litest_button_click(dev, BTN_LEFT, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
-	litest_assert_button_event(li,
-				   BTN_RIGHT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -2665,12 +2553,8 @@ START_TEST(touchpad_left_handed_clickpad
 	litest_button_click(dev, BTN_LEFT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	/* left-handed takes effect now */
 	litest_drain_events(li);
@@ -2684,16 +2568,12 @@ START_TEST(touchpad_left_handed_clickpad
 	litest_button_click(dev, BTN_LEFT, 0);
 	litest_touch_up(dev, 0);
 
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_LEFT,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 static inline bool
 touchpad_has_rotation(struct libevdev *evdev)
 {
@@ -2703,7 +2583,7 @@ touchpad_has_rotation(struct libevdev *e
 
 START_TEST(touchpad_left_handed_rotation)
 {
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 	struct litest_device *dev = litest_current_device();
 	struct libinput_device *d = dev->libinput_device;
 	struct libinput *li = dev->libinput;
@@ -2756,8 +2636,7 @@ START_TEST(touchpad_left_handed_rotation
 END_TEST
 
 static void
-hover_continue(struct litest_device *dev, unsigned int slot,
-	       int x, int y)
+hover_continue(struct litest_device *dev, unsigned int slot, int x, int y)
 {
 	litest_event(dev, EV_ABS, ABS_MT_SLOT, slot);
 	litest_event(dev, EV_ABS, ABS_MT_POSITION_X, x);
@@ -2770,8 +2649,7 @@ hover_continue(struct litest_device *dev
 }
 
 static void
-hover_start(struct litest_device *dev, unsigned int slot,
-	    int x, int y)
+hover_start(struct litest_device *dev, unsigned int slot, int x, int y)
 {
 	static unsigned int tracking_id;
 
@@ -2786,8 +2664,7 @@ START_TEST(touchpad_semi_mt_hover_noeven
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	int i;
-	int x = 2400,
-	    y = 2400;
+	int x = 2400, y = 2400;
 
 	litest_drain_events(li);
 
@@ -2818,8 +2695,7 @@ START_TEST(touchpad_semi_mt_hover_down)
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
 	int i;
-	int x = 2400,
-	    y = 2400;
+	int x = 2400, y = 2400;
 
 	litest_drain_events(li);
 
@@ -2892,8 +2768,7 @@ START_TEST(touchpad_semi_mt_hover_down_h
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	int i, j;
-	int x = 1400,
-	    y = 1400;
+	int x = 1400, y = 1400;
 
 	litest_drain_events(li);
 
@@ -2924,8 +2799,7 @@ START_TEST(touchpad_semi_mt_hover_down_h
 
 		litest_dispatch(li);
 
-		litest_assert_only_typed_events(li,
-						LIBINPUT_EVENT_POINTER_MOTION);
+		litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 		/* go back to hover */
 		hover_continue(dev, 0, x, y);
@@ -2961,8 +2835,7 @@ START_TEST(touchpad_semi_mt_hover_down_h
 	litest_touch_up(dev, 0);
 
 	litest_dispatch(li);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 }
 END_TEST
 
@@ -2971,8 +2844,7 @@ START_TEST(touchpad_semi_mt_hover_down_u
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	int i;
-	int x = 1400,
-	    y = 1400;
+	int x = 1400, y = 1400;
 
 	litest_drain_events(li);
 
@@ -3038,8 +2910,7 @@ START_TEST(touchpad_semi_mt_hover_2fg_no
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	int i;
-	int x = 2400,
-	    y = 2400;
+	int x = 2400, y = 2400;
 
 	litest_drain_events(li);
 
@@ -3083,8 +2954,7 @@ START_TEST(touchpad_semi_mt_hover_2fg_1f
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	int i;
-	int x = 2400,
-	    y = 2400;
+	int x = 2400, y = 2400;
 
 	litest_drain_events(li);
 
@@ -3213,8 +3083,7 @@ START_TEST(touchpad_hover_down_hover_dow
 
 		litest_dispatch(li);
 
-		litest_assert_only_typed_events(li,
-						LIBINPUT_EVENT_POINTER_MOTION);
+		litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 	}
 
 	litest_hover_end(dev, 0);
@@ -3330,7 +3199,6 @@ START_TEST(touchpad_hover_1fg_tap)
 
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
-
 }
 END_TEST
 
@@ -3365,10 +3233,9 @@ START_TEST(touchpad_trackpoint_buttons)
 		{ BTN_2, BTN_MIDDLE },
 	};
 
-	trackpoint = litest_add_device(li,
-				       LITEST_TRACKPOINT);
+	trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
 	libinput_device_config_scroll_set_method(trackpoint->libinput_device,
-					 LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
+						 LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
 
 	litest_drain_events(li);
 
@@ -3385,7 +3252,7 @@ START_TEST(touchpad_trackpoint_buttons)
 					    LIBINPUT_BUTTON_STATE_RELEASED);
 	}
 
-	litest_delete_device(trackpoint);
+	litest_device_destroy(trackpoint);
 }
 END_TEST
 
@@ -3395,14 +3262,11 @@ START_TEST(touchpad_trackpoint_mb_scroll
 	struct litest_device *trackpoint;
 	struct libinput *li = touchpad->libinput;
 
-	trackpoint = litest_add_device(li,
-				       LITEST_TRACKPOINT);
+	trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
 
 	litest_drain_events(li);
 	litest_button_click(touchpad, BTN_2, true); /* middle */
-	litest_dispatch(li);
-	litest_timeout_buttonscroll();
-	litest_dispatch(li);
+	litest_timeout_buttonscroll(li);
 	litest_event(trackpoint, EV_REL, REL_Y, -2);
 	litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
 	litest_event(trackpoint, EV_REL, REL_Y, -2);
@@ -3413,10 +3277,9 @@ START_TEST(touchpad_trackpoint_mb_scroll
 	litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
 	litest_button_click(touchpad, BTN_2, false);
 
-	litest_assert_only_axis_events(li,
-				       LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS);
+	litest_assert_only_axis_events(li, LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS);
 
-	litest_delete_device(trackpoint);
+	litest_device_destroy(trackpoint);
 }
 END_TEST
 
@@ -3427,11 +3290,10 @@ START_TEST(touchpad_trackpoint_mb_click)
 	struct libinput *li = touchpad->libinput;
 	enum libinput_config_status status;
 
-	trackpoint = litest_add_device(li,
-				       LITEST_TRACKPOINT);
+	trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
 	status = libinput_device_config_scroll_set_method(
-				  trackpoint->libinput_device,
-				  LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
+		trackpoint->libinput_device,
+		LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_drain_events(li);
@@ -3444,7 +3306,7 @@ START_TEST(touchpad_trackpoint_mb_click)
 	assert_btnevent_from_device(trackpoint,
 				    BTN_MIDDLE,
 				    LIBINPUT_BUTTON_STATE_RELEASED);
-	litest_delete_device(trackpoint);
+	litest_device_destroy(trackpoint);
 }
 END_TEST
 
@@ -3454,8 +3316,7 @@ START_TEST(touchpad_trackpoint_buttons_s
 	struct litest_device *trackpoint;
 	struct libinput *li = touchpad->libinput;
 
-	trackpoint = litest_add_device(li,
-				       LITEST_TRACKPOINT);
+	trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
 
 	litest_drain_events(li);
 
@@ -3466,9 +3327,7 @@ START_TEST(touchpad_trackpoint_buttons_s
 	litest_touch_up(touchpad, 0);
 	litest_button_click_debounced(touchpad, li, BTN_1, false);
 
-	assert_btnevent_from_device(touchpad,
-				    BTN_RIGHT,
-				    LIBINPUT_BUTTON_STATE_PRESSED);
+	assert_btnevent_from_device(touchpad, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 	assert_btnevent_from_device(trackpoint,
 				    BTN_RIGHT,
 				    LIBINPUT_BUTTON_STATE_PRESSED);
@@ -3486,9 +3345,7 @@ START_TEST(touchpad_trackpoint_buttons_s
 	litest_button_click_debounced(touchpad, li, BTN_LEFT, false);
 	litest_touch_up(touchpad, 0);
 
-	assert_btnevent_from_device(touchpad,
-				    BTN_RIGHT,
-				    LIBINPUT_BUTTON_STATE_PRESSED);
+	assert_btnevent_from_device(touchpad, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 	assert_btnevent_from_device(trackpoint,
 				    BTN_RIGHT,
 				    LIBINPUT_BUTTON_STATE_PRESSED);
@@ -3499,7 +3356,7 @@ START_TEST(touchpad_trackpoint_buttons_s
 				    BTN_RIGHT,
 				    LIBINPUT_BUTTON_STATE_RELEASED);
 
-	litest_delete_device(trackpoint);
+	litest_device_destroy(trackpoint);
 }
 END_TEST
 
@@ -3511,8 +3368,7 @@ START_TEST(touchpad_trackpoint_buttons_2
 	struct libinput_event *e;
 	double val;
 
-	trackpoint = litest_add_device(li,
-				       LITEST_TRACKPOINT);
+	trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
 
 	litest_drain_events(li);
 
@@ -3531,8 +3387,9 @@ START_TEST(touchpad_trackpoint_buttons_2
 					   LIBINPUT_EVENT_POINTER_SCROLL_FINGER,
 					   LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
 					   0);
-		val = litest_event_pointer_get_value(pev,
-				LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+		val = litest_event_pointer_get_value(
+			pev,
+			LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
 		litest_assert(val != 0.0);
 		libinput_event_destroy(e);
 	}
@@ -3554,8 +3411,9 @@ START_TEST(touchpad_trackpoint_buttons_2
 					   LIBINPUT_EVENT_POINTER_SCROLL_FINGER,
 					   LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
 					   0);
-		val = litest_event_pointer_get_value(pev,
-				LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+		val = litest_event_pointer_get_value(
+			pev,
+			LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
 		litest_assert(val != 0.0);
 		libinput_event_destroy(e);
 	}
@@ -3587,7 +3445,7 @@ START_TEST(touchpad_trackpoint_buttons_2
 			     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
 			     -1);
 
-	litest_delete_device(trackpoint);
+	litest_device_destroy(trackpoint);
 }
 END_TEST
 
@@ -3614,7 +3472,7 @@ END_TEST
 START_TEST(touchpad_initial_state)
 {
 	struct litest_device *dev;
-	struct libinput *libinput1, *libinput2;
+	struct libinput *libinput1;
 	int x = 40, y = 60;
 	int axis = litest_test_param_get_i32(test_env->params, "axis");
 
@@ -3630,17 +3488,21 @@ START_TEST(touchpad_initial_state)
 	/* device is now on some x/y value */
 	litest_drain_events(libinput1);
 
-	libinput2 = litest_create_context();
-	libinput_path_add_device(libinput2,
-				 libevdev_uinput_get_devnode(dev->uinput));
+	_litest_context_destroy_ struct libinput *libinput2 = litest_create_context();
+	libinput_path_add_device(libinput2, libevdev_uinput_get_devnode(dev->uinput));
 	litest_drain_events(libinput2);
 
 	if (axis == ABS_X)
 		x = 30;
 	else
 		y = 30;
+
 	litest_touch_down(dev, 0, x, y);
+	litest_dispatch(libinput1);
+	litest_dispatch(libinput2);
 	litest_touch_move_to(dev, 0, x, y, 70, 70, 10);
+	litest_dispatch(libinput1);
+	litest_dispatch(libinput2);
 	litest_touch_up(dev, 0);
 	litest_dispatch(libinput1);
 	litest_dispatch(libinput2);
@@ -3659,7 +3521,7 @@ START_TEST(touchpad_initial_state)
 		p2 = litest_is_motion_event(ev2);
 
 		litest_assert_int_eq(libinput_event_get_type(ev1),
-				 libinput_event_get_type(ev2));
+				     libinput_event_get_type(ev2));
 
 		litest_assert_double_eq(libinput_event_pointer_get_dx(p1),
 					libinput_event_pointer_get_dx(p2));
@@ -3668,20 +3530,20 @@ START_TEST(touchpad_initial_state)
 		libinput_event_destroy(ev1);
 		libinput_event_destroy(ev2);
 	}
-
-	litest_destroy_context(libinput2);
 }
 END_TEST
 
 START_TEST(touchpad_fingers_down_before_init)
 {
 	struct litest_device *dev = litest_current_device();
-	struct libinput *li;
 
 	int finger_count = litest_test_param_get_i32(test_env->params, "fingers");
-	unsigned int map[] = {0, BTN_TOOL_PEN, BTN_TOOL_DOUBLETAP,
-			      BTN_TOOL_TRIPLETAP, BTN_TOOL_QUADTAP,
-			      BTN_TOOL_QUINTTAP};
+	unsigned int map[] = { 0,
+			       BTN_TOOL_PEN,
+			       BTN_TOOL_DOUBLETAP,
+			       BTN_TOOL_TRIPLETAP,
+			       BTN_TOOL_QUADTAP,
+			       BTN_TOOL_QUINTTAP };
 
 	if (!libevdev_has_event_code(dev->evdev, EV_KEY, map[finger_count]))
 		return LITEST_NOT_APPLICABLE;
@@ -3698,9 +3560,8 @@ START_TEST(touchpad_fingers_down_before_
 	litest_drain_events(dev->libinput);
 
 	/* create anew context that already has the fingers down */
-	li = litest_create_context();
-	libinput_path_add_device(li,
-				 libevdev_uinput_get_devnode(dev->uinput));
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	libinput_path_add_device(li, libevdev_uinput_get_devnode(dev->uinput));
 	litest_drain_events(li);
 
 	for (int x = 0; x < 10; x++) {
@@ -3708,6 +3569,7 @@ START_TEST(touchpad_fingers_down_before_
 			if (litest_slot_count(dev) < finger_count)
 				break;
 			litest_touch_move(dev, i, 20 + 10 * i + x, 30);
+			litest_dispatch(li);
 		}
 		litest_dispatch(li);
 	}
@@ -3724,8 +3586,6 @@ START_TEST(touchpad_fingers_down_before_
 	}
 
 	litest_assert_empty_queue(li);
-
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -3814,8 +3674,7 @@ START_TEST(touchpad_dwt)
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
 
-	litest_timeout_dwt_short();
-	litest_dispatch(li);
+	litest_timeout_dwt_short(li);
 
 	/* after timeout  - motion events*/
 	litest_touch_down(touchpad, 0, 50, 50);
@@ -3824,7 +3683,7 @@ START_TEST(touchpad_dwt)
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -3856,8 +3715,7 @@ START_TEST(touchpad_dwt_ext_and_int_keyb
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
 
-	litest_timeout_dwt_short();
-	litest_dispatch(li);
+	litest_timeout_dwt_short(li);
 
 	/* after timeout  - motion events*/
 	litest_touch_down(touchpad, 0, 50, 50);
@@ -3866,8 +3724,8 @@ START_TEST(touchpad_dwt_ext_and_int_keyb
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
-	litest_delete_device(yubikey);
+	litest_device_destroy(keyboard);
+	litest_device_destroy(yubikey);
 }
 END_TEST
 
@@ -3897,8 +3755,7 @@ START_TEST(touchpad_dwt_enable_touch)
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_dwt_short();
-	litest_dispatch(li);
+	litest_timeout_dwt_short(li);
 
 	/* same touch after timeout  - motion events*/
 	litest_touch_move_to(touchpad, 0, 70, 50, 50, 50, 10);
@@ -3906,7 +3763,7 @@ START_TEST(touchpad_dwt_enable_touch)
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -3938,13 +3795,12 @@ START_TEST(touchpad_dwt_touch_hold)
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
 
 	/* touch still down - no events */
-	litest_timeout_dwt_short();
-	litest_dispatch(li);
+	litest_timeout_dwt_short(li);
 	litest_touch_move_to(touchpad, 0, 30, 50, 50, 50, 5);
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -3972,7 +3828,7 @@ START_TEST(touchpad_dwt_key_hold)
 	litest_keyboard_key(keyboard, KEY_A, false);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -3993,8 +3849,7 @@ START_TEST(touchpad_dwt_key_hold_timeout
 	litest_keyboard_key(keyboard, KEY_A, true);
 	litest_dispatch(li);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
-	litest_timeout_dwt_long();
-	litest_dispatch(li);
+	litest_timeout_dwt_long(li);
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5);
 	litest_touch_up(touchpad, 0);
@@ -4010,14 +3865,13 @@ START_TEST(touchpad_dwt_key_hold_timeout
 	litest_assert_empty_queue(li);
 
 	/* expire timeout */
-	litest_timeout_dwt_long();
-	litest_dispatch(li);
+	litest_timeout_dwt_long(li);
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5);
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4043,8 +3897,7 @@ START_TEST(touchpad_dwt_key_hold_timeout
 	litest_keyboard_key(keyboard, KEY_A, true);
 	litest_dispatch(li);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
-	litest_timeout_dwt_long();
-	litest_dispatch(li);
+	litest_timeout_dwt_long(li);
 
 	/* Touch starting after re-issuing the dwt timeout */
 	litest_touch_down(touchpad, 0, 50, 50);
@@ -4065,7 +3918,7 @@ START_TEST(touchpad_dwt_key_hold_timeout
 	 * This is buggy behavior and not what a user would typically
 	 * expect. But it's hard to trigger in real life too.
 	 */
-	litest_timeout_dwt_long();
+	litest_timeout_dwt_long(li);
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5);
 	litest_touch_up(touchpad, 0);
 	/* If the below check for motion event fails because no events are
@@ -4073,7 +3926,7 @@ START_TEST(touchpad_dwt_key_hold_timeout
 	 * can be removed */
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4096,9 +3949,7 @@ START_TEST(touchpad_dwt_key_hold_timeout
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5);
-	litest_dispatch(li);
-	litest_timeout_dwt_long();
-	litest_dispatch(li);
+	litest_timeout_dwt_long(li);
 
 	litest_assert_empty_queue(li);
 
@@ -4109,12 +3960,12 @@ START_TEST(touchpad_dwt_key_hold_timeout
 	litest_assert_empty_queue(li);
 
 	/* expire timeout, but touch started before release */
-	litest_timeout_dwt_long();
+	litest_timeout_dwt_long(li);
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5);
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4146,14 +3997,13 @@ START_TEST(touchpad_dwt_type)
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_dwt_long();
-	litest_dispatch(li);
+	litest_timeout_dwt_long(li);
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5);
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4185,14 +4035,13 @@ START_TEST(touchpad_dwt_type_short_timeo
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_dwt_short();
-	litest_dispatch(li);
+	litest_timeout_dwt_short(li);
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5);
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4202,21 +4051,10 @@ START_TEST(touchpad_dwt_modifier_no_dwt)
 	struct litest_device *keyboard;
 	struct libinput *li = touchpad->libinput;
 	unsigned int modifiers[] = {
-		KEY_LEFTCTRL,
-		KEY_RIGHTCTRL,
-		KEY_LEFTALT,
-		KEY_RIGHTALT,
-		KEY_LEFTSHIFT,
-		KEY_RIGHTSHIFT,
-		KEY_FN,
-		KEY_CAPSLOCK,
-		KEY_TAB,
-		KEY_COMPOSE,
-		KEY_RIGHTMETA,
-		KEY_LEFTMETA,
-		KEY_ESC,
-		KEY_KPASTERISK,
-		KEY_F1,
+		KEY_LEFTCTRL,  KEY_RIGHTCTRL,  KEY_LEFTALT,   KEY_RIGHTALT,
+		KEY_LEFTSHIFT, KEY_RIGHTSHIFT, KEY_FN,        KEY_CAPSLOCK,
+		KEY_TAB,       KEY_COMPOSE,    KEY_RIGHTMETA, KEY_LEFTMETA,
+		KEY_ESC,       KEY_KPASTERISK, KEY_F1,
 	};
 
 	if (!has_disable_while_typing(touchpad))
@@ -4240,7 +4078,7 @@ START_TEST(touchpad_dwt_modifier_no_dwt)
 		litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 	}
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4277,14 +4115,13 @@ START_TEST(touchpad_dwt_shift_combo_trig
 		litest_assert_empty_queue(li);
 	}
 
-	litest_timeout_dwt_long();
-	litest_dispatch(li);
+	litest_timeout_dwt_long(li);
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5);
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4294,16 +4131,8 @@ START_TEST(touchpad_dwt_modifier_combo_n
 	struct litest_device *keyboard;
 	struct libinput *li = touchpad->libinput;
 	unsigned int modifiers[] = {
-		KEY_LEFTCTRL,
-		KEY_RIGHTCTRL,
-		KEY_LEFTALT,
-		KEY_RIGHTALT,
-		KEY_FN,
-		KEY_CAPSLOCK,
-		KEY_TAB,
-		KEY_COMPOSE,
-		KEY_RIGHTMETA,
-		KEY_LEFTMETA,
+		KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTALT, KEY_RIGHTALT,  KEY_FN,
+		KEY_CAPSLOCK, KEY_TAB,       KEY_COMPOSE, KEY_RIGHTMETA, KEY_LEFTMETA,
 	};
 
 	if (!has_disable_while_typing(touchpad))
@@ -4331,7 +4160,7 @@ START_TEST(touchpad_dwt_modifier_combo_n
 		litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 	}
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4341,16 +4170,8 @@ START_TEST(touchpad_dwt_modifier_combo_d
 	struct litest_device *keyboard;
 	struct libinput *li = touchpad->libinput;
 	unsigned int modifiers[] = {
-		KEY_LEFTCTRL,
-		KEY_RIGHTCTRL,
-		KEY_LEFTALT,
-		KEY_RIGHTALT,
-		KEY_FN,
-		KEY_CAPSLOCK,
-		KEY_TAB,
-		KEY_COMPOSE,
-		KEY_RIGHTMETA,
-		KEY_LEFTMETA,
+		KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTALT, KEY_RIGHTALT,  KEY_FN,
+		KEY_CAPSLOCK, KEY_TAB,       KEY_COMPOSE, KEY_RIGHTMETA, KEY_LEFTMETA,
 	};
 
 	if (!has_disable_while_typing(touchpad))
@@ -4378,11 +4199,10 @@ START_TEST(touchpad_dwt_modifier_combo_d
 		litest_touch_up(touchpad, 0);
 		litest_assert_empty_queue(li);
 
-		litest_timeout_dwt_long();
-		litest_dispatch(li);
+		litest_timeout_dwt_long(li);
 	}
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4392,16 +4212,8 @@ START_TEST(touchpad_dwt_modifier_combo_d
 	struct litest_device *keyboard;
 	struct libinput *li = touchpad->libinput;
 	unsigned int modifiers[] = {
-		KEY_LEFTCTRL,
-		KEY_RIGHTCTRL,
-		KEY_LEFTALT,
-		KEY_RIGHTALT,
-		KEY_FN,
-		KEY_CAPSLOCK,
-		KEY_TAB,
-		KEY_COMPOSE,
-		KEY_RIGHTMETA,
-		KEY_LEFTMETA,
+		KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTALT, KEY_RIGHTALT,  KEY_FN,
+		KEY_CAPSLOCK, KEY_TAB,       KEY_COMPOSE, KEY_RIGHTMETA, KEY_LEFTMETA,
 	};
 
 	if (!has_disable_while_typing(touchpad))
@@ -4437,11 +4249,10 @@ START_TEST(touchpad_dwt_modifier_combo_d
 		litest_touch_up(touchpad, 0);
 		litest_assert_empty_queue(li);
 
-		litest_timeout_dwt_long();
-		litest_dispatch(li);
+		litest_timeout_dwt_long(li);
 	}
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4461,8 +4272,7 @@ START_TEST(touchpad_dwt_fkeys_no_dwt)
 	litest_drain_events(li);
 
 	for (key = KEY_F1; key < KEY_CNT; key++) {
-		if (!libinput_device_keyboard_has_key(keyboard->libinput_device,
-						      key))
+		if (!libinput_device_keyboard_has_key(keyboard->libinput_device, key))
 			continue;
 
 		litest_keyboard_key(keyboard, key, true);
@@ -4477,7 +4287,7 @@ START_TEST(touchpad_dwt_fkeys_no_dwt)
 		litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 	}
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4503,12 +4313,12 @@ START_TEST(touchpad_dwt_tap)
 	litest_keyboard_key(keyboard, KEY_A, false);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
 
-	litest_timeout_dwt_short();
+	litest_timeout_dwt_short(li);
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4537,13 +4347,12 @@ START_TEST(touchpad_dwt_tap_drag)
 	litest_keyboard_key(keyboard, KEY_A, false);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
 
-	litest_timeout_dwt_short();
-	litest_dispatch(li);
+	litest_timeout_dwt_short(li);
 	litest_touch_move_to(touchpad, 0, 70, 50, 50, 50, 5);
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4575,7 +4384,7 @@ START_TEST(touchpad_dwt_click)
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4600,9 +4409,7 @@ START_TEST(touchpad_dwt_edge_scroll)
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
 
 	litest_touch_down(touchpad, 0, 99, 20);
-	litest_dispatch(li);
-	litest_timeout_edgescroll();
-	litest_dispatch(li);
+	litest_timeout_edgescroll(li);
 	litest_assert_empty_queue(li);
 
 	/* edge scroll timeout is 300ms atm, make sure we don't accidentally
@@ -4621,7 +4428,7 @@ START_TEST(touchpad_dwt_edge_scroll)
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4640,8 +4447,7 @@ START_TEST(touchpad_dwt_edge_scroll_inte
 	litest_drain_events(li);
 
 	litest_touch_down(touchpad, 0, 99, 20);
-	litest_dispatch(li);
-	litest_timeout_edgescroll();
+	litest_timeout_edgescroll(li);
 	litest_touch_move_to(touchpad, 0, 99, 20, 99, 30, 10);
 	litest_dispatch(li);
 	litest_assert_only_axis_events(li, LIBINPUT_EVENT_POINTER_SCROLL_FINGER);
@@ -4660,7 +4466,7 @@ START_TEST(touchpad_dwt_edge_scroll_inte
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
 
-	litest_timeout_dwt_long();
+	litest_timeout_dwt_long(li);
 
 	/* Known bad behavior: a touch starting to edge-scroll before dwt
 	 * kicks in will stop to scroll but be recognized as normal
@@ -4670,7 +4476,7 @@ START_TEST(touchpad_dwt_edge_scroll_inte
 	litest_touch_move_to(touchpad, 0, 99, 30, 99, 80, 10);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4694,10 +4500,10 @@ START_TEST(touchpad_dwt_config_default_o
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_DWT_ENABLED);
 
 	status = libinput_device_config_dwt_set_enabled(device,
-					LIBINPUT_CONFIG_DWT_ENABLED);
+							LIBINPUT_CONFIG_DWT_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	status = libinput_device_config_dwt_set_enabled(device,
-					LIBINPUT_CONFIG_DWT_DISABLED);
+							LIBINPUT_CONFIG_DWT_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	status = libinput_device_config_dwt_set_enabled(device, 3);
@@ -4724,10 +4530,10 @@ START_TEST(touchpad_dwtp_config_default_
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_DWTP_ENABLED);
 
 	status = libinput_device_config_dwtp_set_enabled(device,
-					LIBINPUT_CONFIG_DWTP_ENABLED);
+							 LIBINPUT_CONFIG_DWTP_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	status = libinput_device_config_dwtp_set_enabled(device,
-					LIBINPUT_CONFIG_DWTP_DISABLED);
+							 LIBINPUT_CONFIG_DWTP_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	status = libinput_device_config_dwtp_set_enabled(device, 3);
@@ -4749,10 +4555,10 @@ START_TEST(touchpad_dwt_config_default_o
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_DWT_DISABLED);
 
 	status = libinput_device_config_dwt_set_enabled(device,
-					LIBINPUT_CONFIG_DWT_ENABLED);
+							LIBINPUT_CONFIG_DWT_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 	status = libinput_device_config_dwt_set_enabled(device,
-					LIBINPUT_CONFIG_DWT_DISABLED);
+							LIBINPUT_CONFIG_DWT_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	status = libinput_device_config_dwt_set_enabled(device, 3);
@@ -4774,10 +4580,10 @@ START_TEST(touchpad_dwtp_config_default_
 	litest_assert_enum_eq(state, LIBINPUT_CONFIG_DWTP_DISABLED);
 
 	status = libinput_device_config_dwtp_set_enabled(device,
-					LIBINPUT_CONFIG_DWTP_ENABLED);
+							 LIBINPUT_CONFIG_DWTP_ENABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 	status = libinput_device_config_dwtp_set_enabled(device,
-					LIBINPUT_CONFIG_DWTP_DISABLED);
+							 LIBINPUT_CONFIG_DWTP_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	status = libinput_device_config_dwtp_set_enabled(device, 3);
@@ -4788,20 +4594,18 @@ END_TEST
 static inline void
 disable_dwt(struct litest_device *dev)
 {
-	enum libinput_config_status status,
-				    expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
+	enum libinput_config_status status, expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
 	status = libinput_device_config_dwt_set_enabled(dev->libinput_device,
-						LIBINPUT_CONFIG_DWT_DISABLED);
+							LIBINPUT_CONFIG_DWT_DISABLED);
 	litest_assert_int_eq(status, expected);
 }
 
 static inline void
 enable_dwt(struct litest_device *dev)
 {
-	enum libinput_config_status status,
-				    expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
+	enum libinput_config_status status, expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
 	status = libinput_device_config_dwt_set_enabled(dev->libinput_device,
-						LIBINPUT_CONFIG_DWT_ENABLED);
+							LIBINPUT_CONFIG_DWT_ENABLED);
 	litest_assert_int_eq(status, expected);
 }
 
@@ -4831,7 +4635,7 @@ START_TEST(touchpad_dwt_disabled)
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4863,8 +4667,7 @@ START_TEST(touchpad_dwt_disable_during_t
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_dwt_long();
-	litest_dispatch(li);
+	litest_timeout_dwt_long(li);
 
 	disable_dwt(touchpad);
 
@@ -4874,7 +4677,7 @@ START_TEST(touchpad_dwt_disable_during_t
 
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4906,7 +4709,7 @@ START_TEST(touchpad_dwt_disable_before_t
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4936,12 +4739,11 @@ START_TEST(touchpad_dwt_disable_during_k
 
 	/* touch down during timeout, wait, should generate events */
 	litest_touch_down(touchpad, 0, 50, 50);
-	litest_dispatch(li);
-	litest_timeout_dwt_long();
+	litest_timeout_dwt_long(li);
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -4969,12 +4771,11 @@ START_TEST(touchpad_dwt_disable_during_k
 
 	/* touch down during timeout, wait, should generate events */
 	litest_touch_down(touchpad, 0, 50, 50);
-	litest_dispatch(li);
-	litest_timeout_dwt_long();
+	litest_timeout_dwt_long(li);
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -5009,7 +4810,7 @@ START_TEST(touchpad_dwt_enable_during_to
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -5040,7 +4841,7 @@ START_TEST(touchpad_dwt_enable_before_to
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -5069,9 +4870,7 @@ START_TEST(touchpad_dwt_enable_during_ta
 	enable_dwt(touchpad);
 	litest_dispatch(li);
 	litest_touch_up(touchpad, 0);
-	litest_dispatch(li);
-
-	litest_timeout_tap();
+	litest_timeout_tap(li);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_BUTTON);
 
 	litest_touch_down(touchpad, 0, 50, 50);
@@ -5079,7 +4878,7 @@ START_TEST(touchpad_dwt_enable_during_ta
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -5110,7 +4909,7 @@ START_TEST(touchpad_dwt_remove_kbd_while
 	litest_keyboard_key(keyboard, KEY_A, false);
 	litest_drain_events(li);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 	litest_drain_events(li);
 
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10);
@@ -5121,7 +4920,6 @@ START_TEST(touchpad_dwt_remove_kbd_while
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10);
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
-
 }
 END_TEST
 
@@ -5146,7 +4944,7 @@ START_TEST(touchpad_dwt_apple)
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(apple_keyboard);
+	litest_device_destroy(apple_keyboard);
 }
 END_TEST
 
@@ -5184,8 +4982,8 @@ START_TEST(touchpad_dwt_acer_hawaii)
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(keyboard);
-	litest_delete_device(hawaii_keyboard);
+	litest_device_destroy(keyboard);
+	litest_device_destroy(hawaii_keyboard);
 }
 END_TEST
 
@@ -5211,7 +5009,7 @@ START_TEST(touchpad_dwt_multiple_keyboar
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_dwt_short();
+	litest_timeout_dwt_short(li);
 
 	litest_keyboard_key(k2, KEY_A, true);
 	litest_keyboard_key(k2, KEY_A, false);
@@ -5222,10 +5020,10 @@ START_TEST(touchpad_dwt_multiple_keyboar
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_dwt_short();
+	litest_timeout_dwt_short(li);
 
-	litest_delete_device(k1);
-	litest_delete_device(k2);
+	litest_device_destroy(k1);
+	litest_device_destroy(k2);
 }
 END_TEST
 
@@ -5242,7 +5040,7 @@ START_TEST(touchpad_dwt_remove_before_ke
 
 	/* remove the touchpad before the keyboard.
 	 * this test can fail in valgrind only */
-	litest_delete_device(touchpad);
+	litest_device_destroy(touchpad);
 }
 END_TEST
 
@@ -5270,8 +5068,8 @@ START_TEST(touchpad_dwt_multiple_keyboar
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(k1);
-	litest_delete_device(k2);
+	litest_device_destroy(k1);
+	litest_device_destroy(k2);
 }
 END_TEST
 
@@ -5305,8 +5103,8 @@ START_TEST(touchpad_dwt_multiple_keyboar
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(k1);
-	litest_delete_device(k2);
+	litest_device_destroy(k1);
+	litest_device_destroy(k2);
 }
 END_TEST
 
@@ -5333,12 +5131,12 @@ START_TEST(touchpad_dwt_multiple_keyboar
 	litest_keyboard_key(keyboards[1], KEY_B, false);
 	litest_drain_events(li);
 
-	litest_timeout_dwt_short();
+	litest_timeout_dwt_short(li);
 
 	removed = keyboards[which % 2];
 	remained = keyboards[(which + 1) % 2];
 
-	litest_delete_device(removed);
+	litest_device_destroy(removed);
 	litest_keyboard_key(remained, KEY_C, true);
 	litest_keyboard_key(remained, KEY_C, false);
 	litest_drain_events(li);
@@ -5348,7 +5146,7 @@ START_TEST(touchpad_dwt_multiple_keyboar
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(remained);
+	litest_device_destroy(remained);
 }
 END_TEST
 
@@ -5452,7 +5250,6 @@ START_TEST(touchpad_thumb_speed_empty_sl
 			     LIBINPUT_EVENT_POINTER_SCROLL_FINGER,
 			     LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
 			     2);
-
 }
 END_TEST
 
@@ -5468,8 +5265,9 @@ START_TEST(touchpad_thumb_area_clickfing
 	litest_disable_tap(dev->libinput_device);
 	litest_disable_hold_gestures(dev->libinput_device);
 
-	libinput_device_config_click_set_method(dev->libinput_device,
-						LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
+	libinput_device_config_click_set_method(
+		dev->libinput_device,
+		LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
 
 	litest_drain_events(li);
 
@@ -5481,9 +5279,7 @@ START_TEST(touchpad_thumb_area_clickfing
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_LEFT,
-			       LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_is_button_event(event, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
@@ -5502,9 +5298,7 @@ START_TEST(touchpad_thumb_area_clickfing
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_LEFT,
-			       LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_is_button_event(event, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
@@ -5523,8 +5317,9 @@ START_TEST(touchpad_thumb_area_btnarea)
 	litest_disable_tap(dev->libinput_device);
 	litest_disable_hold_gestures(dev->libinput_device);
 
-	libinput_device_config_click_set_method(dev->libinput_device,
-						LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS);
+	libinput_device_config_click_set_method(
+		dev->libinput_device,
+		LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS);
 
 	litest_drain_events(li);
 
@@ -5536,9 +5331,7 @@ START_TEST(touchpad_thumb_area_btnarea)
 
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_RIGHT,
-			       LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_is_button_event(event, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 	libinput_event_destroy(event);
 
 	litest_assert_empty_queue(li);
@@ -5663,14 +5456,10 @@ START_TEST(touchpad_tool_tripletap_touch
 
 	litest_wait_for_event(li);
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_MIDDLE,
-			       LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_is_button_event(event, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
 	libinput_event_destroy(event);
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_MIDDLE,
-			       LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_is_button_event(event, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 	libinput_event_destroy(event);
 
 	/* release everything */
@@ -5769,14 +5558,10 @@ START_TEST(touchpad_tool_tripletap_touch
 
 	litest_wait_for_event(li);
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_MIDDLE,
-			       LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_is_button_event(event, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
 	libinput_event_destroy(event);
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_MIDDLE,
-			       LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_is_button_event(event, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 	libinput_event_destroy(event);
 
 	/* release everything */
@@ -5807,7 +5592,7 @@ START_TEST(touchpad_slot_swap)
 	litest_drain_events(li);
 
 	for (first = 0; first <= 1; first++) {
-		const double start[2][2] = {{50, 50}, {60, 60}};
+		const double start[2][2] = { { 50, 50 }, { 60, 60 } };
 		second = 1 - first;
 
 		litest_touch_down(dev, 0, start[0][0], start[0][1]);
@@ -5820,13 +5605,16 @@ START_TEST(touchpad_slot_swap)
 					      start[first][1],
 					      start[second][0],
 					      start[second][1],
-					      30, 30, 10);
+					      30,
+					      30,
+					      10);
 		litest_drain_events(li);
 
 		/* release touch 0, continue other slot with 0's coords */
 		litest_push_event_frame(dev);
 		litest_touch_up(dev, first);
-		litest_touch_move(dev, second,
+		litest_touch_move(dev,
+				  second,
 				  start[second][0] + 30,
 				  start[second][1] + 30.1);
 		litest_pop_event_frame(dev);
@@ -5835,12 +5623,15 @@ START_TEST(touchpad_slot_swap)
 		 * timeout to trigger events. So let's move a bit first to
 		 * make sure it looks continuous, then wait, then move again
 		 * to make sure we trigger events */
-		litest_touch_move_to(dev, second,
+		litest_touch_move_to(dev,
+				     second,
 				     start[first][0] + 30,
 				     start[first][1] + 30,
-				     50, 21, 10);
+				     50,
+				     21,
+				     10);
 		litest_dispatch(li);
-		litest_timeout_gesture();
+		litest_timeout_gesture(li);
 		litest_dispatch(li);
 		/* drain a potential scroll stop */
 		litest_drain_events(li);
@@ -5851,8 +5642,10 @@ START_TEST(touchpad_slot_swap)
 			struct libinput_event_pointer *ptrev;
 
 			ptrev = litest_is_motion_event(event);
-			litest_assert_double_eq(libinput_event_pointer_get_dx(ptrev), 0.0);
-			litest_assert_double_lt(libinput_event_pointer_get_dy(ptrev), 1.0);
+			litest_assert_double_eq(libinput_event_pointer_get_dx(ptrev),
+						0.0);
+			litest_assert_double_lt(libinput_event_pointer_get_dy(ptrev),
+						1.0);
 
 			libinput_event_destroy(event);
 			event = libinput_get_event(li);
@@ -5867,23 +5660,20 @@ END_TEST
 START_TEST(touchpad_finger_always_down)
 {
 	struct litest_device *dev = litest_current_device();
-	struct libinput *li;
 
 	/* Set BTN_TOOL_FINGER before a new context is initialized */
 	litest_event(dev, EV_KEY, BTN_TOOL_FINGER, 1);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
 
-	li = litest_create_context();
-	libinput_path_add_device(li,
-				 libevdev_uinput_get_devnode(dev->uinput));
+	_litest_context_destroy_ struct libinput *li = litest_create_context();
+	libinput_path_add_device(li, libevdev_uinput_get_devnode(dev->uinput));
 	litest_drain_events(li);
 
 	litest_touch_down(dev, 0, 50, 50);
+	litest_dispatch(li);
 	litest_touch_move_to(dev, 0, 50, 50, 70, 50, 10);
 
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
-
-	litest_destroy_context(li);
 }
 END_TEST
 
@@ -5914,7 +5704,7 @@ START_TEST(touchpad_time_usec)
 		utime = libinput_event_pointer_get_time_usec(ptrev);
 
 		litest_assert_int_eq(libinput_event_pointer_get_time(ptrev),
-				 (uint32_t) (utime / 1000));
+				     (uint32_t)(utime / 1000));
 		libinput_event_destroy(event);
 		event = libinput_get_event(li);
 	}
@@ -6010,8 +5800,8 @@ START_TEST(touchpad_disabled_on_mouse)
 	litest_drain_events(li);
 
 	status = libinput_device_config_send_events_set_mode(
-			     dev->libinput_device,
-			     LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE);
+		dev->libinput_device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_touch_down(dev, 0, 20, 30);
@@ -6027,7 +5817,7 @@ START_TEST(touchpad_disabled_on_mouse)
 	litest_touch_up(dev, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(mouse);
+	litest_device_destroy(mouse);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
 
 	litest_touch_down(dev, 0, 20, 30);
@@ -6047,8 +5837,8 @@ START_TEST(touchpad_disabled_on_mouse_su
 	litest_drain_events(li);
 
 	status = libinput_device_config_send_events_set_mode(
-			     dev->libinput_device,
-			     LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE);
+		dev->libinput_device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_touch_down(dev, 0, 20, 30);
@@ -6061,8 +5851,8 @@ START_TEST(touchpad_disabled_on_mouse_su
 
 	/* Disable external mouse -> expect touchpad events */
 	status = libinput_device_config_send_events_set_mode(
-			     mouse->libinput_device,
-			     LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+		mouse->libinput_device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_touch_down(dev, 0, 20, 30);
@@ -6070,7 +5860,7 @@ START_TEST(touchpad_disabled_on_mouse_su
 	litest_touch_up(dev, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_delete_device(mouse);
+	litest_device_destroy(mouse);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
 
 	litest_touch_down(dev, 0, 20, 30);
@@ -6090,8 +5880,8 @@ START_TEST(touchpad_disabled_double_mous
 	litest_drain_events(li);
 
 	status = libinput_device_config_send_events_set_mode(
-			     dev->libinput_device,
-			     LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE);
+		dev->libinput_device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_touch_down(dev, 0, 20, 30);
@@ -6108,7 +5898,7 @@ START_TEST(touchpad_disabled_double_mous
 	litest_touch_up(dev, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(mouse1);
+	litest_device_destroy(mouse1);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
 
 	litest_touch_down(dev, 0, 20, 30);
@@ -6116,7 +5906,7 @@ START_TEST(touchpad_disabled_double_mous
 	litest_touch_up(dev, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(mouse2);
+	litest_device_destroy(mouse2);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
 
 	litest_touch_down(dev, 0, 20, 30);
@@ -6136,8 +5926,8 @@ START_TEST(touchpad_disabled_double_mous
 	litest_drain_events(li);
 
 	status = libinput_device_config_send_events_set_mode(
-			     dev->libinput_device,
-			     LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE);
+		dev->libinput_device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_touch_down(dev, 0, 20, 30);
@@ -6151,8 +5941,8 @@ START_TEST(touchpad_disabled_double_mous
 
 	/* Disable one external mouse -> don't expect touchpad events */
 	status = libinput_device_config_send_events_set_mode(
-			     mouse1->libinput_device,
-			     LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+		mouse1->libinput_device,
+		LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_touch_down(dev, 0, 20, 30);
@@ -6160,7 +5950,7 @@ START_TEST(touchpad_disabled_double_mous
 	litest_touch_up(dev, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(mouse1);
+	litest_device_destroy(mouse1);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
 
 	litest_touch_down(dev, 0, 20, 30);
@@ -6168,7 +5958,7 @@ START_TEST(touchpad_disabled_double_mous
 	litest_touch_up(dev, 0);
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(mouse2);
+	litest_device_destroy(mouse2);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_DEVICE_REMOVED);
 
 	litest_touch_down(dev, 0, 20, 30);
@@ -6187,8 +5977,7 @@ touchpad_has_pressure(struct litest_devi
 		return false;
 
 	if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_PRESSURE))
-		return libevdev_get_abs_resolution(evdev,
-						   ABS_MT_PRESSURE) == 0;
+		return libevdev_get_abs_resolution(evdev, ABS_MT_PRESSURE) == 0;
 
 	if (libevdev_has_event_code(evdev, EV_ABS, ABS_PRESSURE) &&
 	    !libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT))
@@ -6204,7 +5993,7 @@ START_TEST(touchpad_pressure)
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 1 },
 		{ ABS_PRESSURE, 1 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 	double pressure; /* in percent */
 	double threshold = 12.0;
@@ -6218,15 +6007,13 @@ START_TEST(touchpad_pressure)
 		litest_axis_set_value(axes, ABS_MT_PRESSURE, pressure);
 		litest_axis_set_value(axes, ABS_PRESSURE, pressure);
 		litest_touch_down_extended(dev, 0, 50, 50, axes);
-		litest_touch_move_to_extended(dev, 0, 50, 50, 80, 80, axes,
-					      10);
+		litest_touch_move_to_extended(dev, 0, 50, 50, 80, 80, axes, 10);
 		litest_touch_up(dev, 0);
 		if (pressure < threshold)
 			litest_assert_empty_queue(li);
 		else
 			litest_assert_only_typed_events(li,
 							LIBINPUT_EVENT_POINTER_MOTION);
-
 	}
 }
 END_TEST
@@ -6238,7 +6025,7 @@ START_TEST(touchpad_pressure_2fg)
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 5 },
 		{ ABS_PRESSURE, 5 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_pressure(dev))
@@ -6251,14 +6038,12 @@ START_TEST(touchpad_pressure_2fg)
 	litest_dispatch(li);
 	litest_touch_move_to(dev, 0, 30, 50, 80, 80, 10);
 	litest_dispatch(li);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 	litest_touch_move_to_extended(dev, 1, 50, 50, 80, 80, axes, 10);
 	litest_assert_empty_queue(li);
 	litest_touch_move_to(dev, 0, 80, 80, 20, 50, 10);
 	litest_touch_move_to_extended(dev, 1, 80, 80, 50, 50, axes, 10);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 }
 END_TEST
 
@@ -6269,7 +6054,7 @@ START_TEST(touchpad_pressure_2fg_st)
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 5 },
 		{ ABS_PRESSURE, 5 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_pressure(dev))
@@ -6303,7 +6088,7 @@ START_TEST(touchpad_pressure_tap)
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 5 },
 		{ ABS_PRESSURE, 5 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_pressure(dev))
@@ -6327,7 +6112,7 @@ START_TEST(touchpad_pressure_tap_2fg)
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 5 },
 		{ ABS_PRESSURE, 5 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_pressure(dev))
@@ -6355,7 +6140,7 @@ START_TEST(touchpad_pressure_tap_2fg_1fg
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 5 },
 		{ ABS_PRESSURE, 5 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_pressure(dev))
@@ -6374,18 +6159,13 @@ START_TEST(touchpad_pressure_tap_2fg_1fg
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_LEFT,
-			       LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_is_button_event(event, BTN_LEFT, LIBINPUT_BUTTON_STATE_PRESSED);
 	libinput_event_destroy(event);
 
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_LEFT,
-			       LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_is_button_event(event, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	libinput_event_destroy(event);
 }
 END_TEST
@@ -6397,7 +6177,7 @@ START_TEST(touchpad_pressure_btntool)
 	struct axis_replacement axes[] = {
 		{ ABS_MT_PRESSURE, 5 },
 		{ ABS_PRESSURE, 5 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	/* we only have tripletap, can't test 4 slots because nothing will
@@ -6443,17 +6223,11 @@ START_TEST(touchpad_pressure_btntool)
 	litest_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
 	litest_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, 0);
 	litest_event(dev, EV_SYN, SYN_REPORT, 0);
-	litest_dispatch(li);
 
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -6463,7 +6237,7 @@ START_TEST(touchpad_pressure_semi_mt_2fg
 	struct libinput *li = dev->libinput;
 	struct axis_replacement axes[] = {
 		{ ABS_PRESSURE, 2 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	litest_enable_2fg_scroll(dev);
@@ -6507,7 +6281,7 @@ START_TEST(touchpad_touch_size)
 		{ ABS_MT_TOUCH_MAJOR, 0 },
 		{ ABS_MT_TOUCH_MINOR, 0 },
 		{ ABS_MT_ORIENTATION, 0 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_touch_size(dev))
@@ -6527,8 +6301,7 @@ START_TEST(touchpad_touch_size)
 	litest_touch_down_extended(dev, 0, 50, 50, axes);
 	litest_touch_move_to_extended(dev, 0, 50, 50, 80, 80, axes, 10);
 	litest_touch_up(dev, 0);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 }
 END_TEST
 
@@ -6540,7 +6313,7 @@ START_TEST(touchpad_touch_size_2fg)
 		{ ABS_MT_TOUCH_MAJOR, 0 },
 		{ ABS_MT_TOUCH_MINOR, 0 },
 		{ ABS_MT_ORIENTATION, 0 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!touchpad_has_touch_size(dev))
@@ -6552,8 +6325,7 @@ START_TEST(touchpad_touch_size_2fg)
 	litest_touch_down_extended(dev, 0, 50, 50, axes);
 	litest_touch_move_to_extended(dev, 0, 50, 50, 80, 80, axes, 10);
 
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_axis_set_value(axes, ABS_MT_TOUCH_MAJOR, 1);
 	litest_axis_set_value(axes, ABS_MT_TOUCH_MINOR, 1);
@@ -6565,8 +6337,7 @@ START_TEST(touchpad_touch_size_2fg)
 	litest_axis_set_value(axes, ABS_MT_TOUCH_MINOR, 15);
 	litest_touch_move_to_extended(dev, 0, 80, 80, 50, 50, axes, 10);
 
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	litest_touch_up(dev, 1);
 	litest_touch_up(dev, 0);
@@ -6580,11 +6351,10 @@ START_TEST(touchpad_palm_detect_touch_si
 	struct axis_replacement axes[] = {
 		{ ABS_MT_TOUCH_MAJOR, 0 },
 		{ ABS_MT_TOUCH_MINOR, 0 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
-	if (!touchpad_has_touch_size(dev) ||
-	    litest_touchpad_is_external(dev))
+	if (!touchpad_has_touch_size(dev) || litest_touchpad_is_external(dev))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_drain_events(li);
@@ -6594,8 +6364,7 @@ START_TEST(touchpad_palm_detect_touch_si
 	litest_axis_set_value(axes, ABS_MT_TOUCH_MINOR, 30);
 	litest_touch_down_extended(dev, 0, 50, 50, axes);
 	litest_touch_move_to_extended(dev, 0, 50, 50, 80, 80, axes, 10);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	/* apply sufficient pressure */
 	litest_axis_set_value_unchecked(axes, ABS_MT_TOUCH_MAJOR, 90);
@@ -6612,11 +6381,10 @@ START_TEST(touchpad_palm_detect_touch_si
 	struct axis_replacement axes[] = {
 		{ ABS_MT_TOUCH_MAJOR, 0 },
 		{ ABS_MT_TOUCH_MINOR, 0 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
-	if (!touchpad_has_touch_size(dev) ||
-	    litest_touchpad_is_external(dev))
+	if (!touchpad_has_touch_size(dev) || litest_touchpad_is_external(dev))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_drain_events(li);
@@ -6630,8 +6398,7 @@ START_TEST(touchpad_palm_detect_touch_si
 	litest_dispatch(li);
 	litest_touch_move_to_extended(dev, 0, 80, 90, 50, 20, axes, 10);
 	litest_touch_up(dev, 0);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	/* apply sufficient pressure */
 	litest_axis_set_value_unchecked(axes, ABS_MT_TOUCH_MAJOR, 90);
@@ -6653,11 +6420,10 @@ START_TEST(touchpad_palm_detect_touch_si
 	struct axis_replacement axes[] = {
 		{ ABS_MT_TOUCH_MAJOR, 0 },
 		{ ABS_MT_TOUCH_MINOR, 0 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
-	if (!touchpad_has_touch_size(dev) ||
-	    litest_touchpad_is_external(dev))
+	if (!touchpad_has_touch_size(dev) || litest_touchpad_is_external(dev))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_drain_events(li);
@@ -6669,8 +6435,7 @@ START_TEST(touchpad_palm_detect_touch_si
 	litest_touch_move_to_extended(dev, 0, 80, 90, 50, 20, axes, 10);
 	litest_touch_move_to(dev, 0, 50, 20, 80, 90, 10);
 	litest_touch_up(dev, 0);
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
 	/* apply sufficient pressure */
 	litest_axis_set_value_unchecked(axes, ABS_MT_TOUCH_MAJOR, 90);
@@ -6690,13 +6455,11 @@ START_TEST(touchpad_palm_detect_touch_si
 	struct axis_replacement axes[] = {
 		{ ABS_MT_TOUCH_MAJOR, 0 },
 		{ ABS_MT_TOUCH_MINOR, 0 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
-	if (!touchpad_has_touch_size(dev) ||
-	    litest_touchpad_is_external(dev) ||
-	    !litest_has_palm_detect_size(dev) ||
-	    !litest_has_2fg_scroll(dev))
+	if (!touchpad_has_touch_size(dev) || litest_touchpad_is_external(dev) ||
+	    !litest_has_palm_detect_size(dev) || !litest_has_2fg_scroll(dev))
 		return LITEST_NOT_APPLICABLE;
 
 	litest_enable_2fg_scroll(dev);
@@ -6710,8 +6473,7 @@ START_TEST(touchpad_palm_detect_touch_si
 	litest_touch_up(dev, 0);
 	litest_dispatch(li);
 
-	litest_assert_only_typed_events(li,
-					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 }
 END_TEST
 
@@ -6723,11 +6485,10 @@ START_TEST(touchpad_palm_detect_touch_si
 	struct axis_replacement axes[] = {
 		{ ABS_MT_TOUCH_MAJOR, 0 },
 		{ ABS_MT_TOUCH_MINOR, 0 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
-	if (!touchpad_has_touch_size(touchpad) ||
-	    litest_touchpad_is_external(touchpad))
+	if (!touchpad_has_touch_size(touchpad) || litest_touchpad_is_external(touchpad))
 		return LITEST_NOT_APPLICABLE;
 
 	keyboard = dwt_init_paired_keyboard(li, touchpad);
@@ -6746,7 +6507,7 @@ START_TEST(touchpad_palm_detect_touch_si
 	litest_touch_move_to_extended(touchpad, 0, 50, 50, 20, 50, axes, 20);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_dwt_short();
+	litest_timeout_dwt_short(li);
 	litest_dispatch(li);
 	litest_assert_empty_queue(li);
 
@@ -6756,7 +6517,7 @@ START_TEST(touchpad_palm_detect_touch_si
 
 	litest_assert_empty_queue(li);
 
-	litest_delete_device(keyboard);
+	litest_device_destroy(keyboard);
 }
 END_TEST
 
@@ -6854,7 +6615,7 @@ START_TEST(touchpad_speed_ignore_hoverin
 	struct axis_replacement axes[] = {
 		{ ABS_MT_TOUCH_MAJOR, 1 },
 		{ ABS_MT_TOUCH_MINOR, 1 },
-		{ -1, 0 }
+		{ -1, 0 },
 	};
 
 	if (!has_thumb_detect(dev))
@@ -6960,20 +6721,20 @@ START_TEST(touchpad_suspend_abba)
 
 		/* First reason for suspend: on */
 		switch (first) {
-		case  SUSPEND_EXT_MOUSE:
+		case SUSPEND_EXT_MOUSE:
 			litest_sendevents_ext_mouse(tp);
 			break;
-		case  SUSPEND_TABLETMODE:
+		case SUSPEND_TABLETMODE:
 			litest_switch_action(tabletmode,
 					     LIBINPUT_SWITCH_TABLET_MODE,
 					     LIBINPUT_SWITCH_STATE_ON);
 			break;
-		case  SUSPEND_LID:
+		case SUSPEND_LID:
 			litest_switch_action(lid,
 					     LIBINPUT_SWITCH_LID,
 					     LIBINPUT_SWITCH_STATE_ON);
 			break;
-		case  SUSPEND_SENDEVENTS:
+		case SUSPEND_SENDEVENTS:
 			litest_sendevents_off(tp);
 			break;
 		default:
@@ -7022,20 +6783,20 @@ START_TEST(touchpad_suspend_abba)
 
 		/* First reason for suspend: off */
 		switch (first) {
-		case  SUSPEND_EXT_MOUSE:
+		case SUSPEND_EXT_MOUSE:
 			litest_sendevents_on(tp);
 			break;
-		case  SUSPEND_TABLETMODE:
+		case SUSPEND_TABLETMODE:
 			litest_switch_action(tabletmode,
 					     LIBINPUT_SWITCH_TABLET_MODE,
 					     LIBINPUT_SWITCH_STATE_OFF);
 			break;
-		case  SUSPEND_LID:
+		case SUSPEND_LID:
 			litest_switch_action(lid,
 					     LIBINPUT_SWITCH_LID,
 					     LIBINPUT_SWITCH_STATE_OFF);
 			break;
-		case  SUSPEND_SENDEVENTS:
+		case SUSPEND_SENDEVENTS:
 			litest_sendevents_on(tp);
 			break;
 		default:
@@ -7049,9 +6810,9 @@ START_TEST(touchpad_suspend_abba)
 out:
 	litest_ungrab_device(lid);
 	litest_ungrab_device(tabletmode);
-	litest_delete_device(lid);
-	litest_delete_device(tabletmode);
-	litest_delete_device(extmouse);
+	litest_device_destroy(lid);
+	litest_device_destroy(tabletmode);
+	litest_device_destroy(extmouse);
 }
 END_TEST
 
@@ -7098,20 +6859,20 @@ START_TEST(touchpad_suspend_abab)
 
 		/* First reason for suspend: on */
 		switch (first) {
-		case  SUSPEND_EXT_MOUSE:
+		case SUSPEND_EXT_MOUSE:
 			litest_sendevents_ext_mouse(tp);
 			break;
-		case  SUSPEND_TABLETMODE:
+		case SUSPEND_TABLETMODE:
 			litest_switch_action(tabletmode,
 					     LIBINPUT_SWITCH_TABLET_MODE,
 					     LIBINPUT_SWITCH_STATE_ON);
 			break;
-		case  SUSPEND_LID:
+		case SUSPEND_LID:
 			litest_switch_action(lid,
 					     LIBINPUT_SWITCH_LID,
 					     LIBINPUT_SWITCH_STATE_ON);
 			break;
-		case  SUSPEND_SENDEVENTS:
+		case SUSPEND_SENDEVENTS:
 			litest_sendevents_off(tp);
 			break;
 		default:
@@ -7150,20 +6911,20 @@ START_TEST(touchpad_suspend_abab)
 
 		/* First reason for suspend: off */
 		switch (first) {
-		case  SUSPEND_EXT_MOUSE:
+		case SUSPEND_EXT_MOUSE:
 			litest_sendevents_on(tp);
 			break;
-		case  SUSPEND_TABLETMODE:
+		case SUSPEND_TABLETMODE:
 			litest_switch_action(tabletmode,
 					     LIBINPUT_SWITCH_TABLET_MODE,
 					     LIBINPUT_SWITCH_STATE_OFF);
 			break;
-		case  SUSPEND_LID:
+		case SUSPEND_LID:
 			litest_switch_action(lid,
 					     LIBINPUT_SWITCH_LID,
 					     LIBINPUT_SWITCH_STATE_OFF);
 			break;
-		case  SUSPEND_SENDEVENTS:
+		case SUSPEND_SENDEVENTS:
 			litest_sendevents_on(tp);
 			break;
 		default:
@@ -7204,9 +6965,9 @@ START_TEST(touchpad_suspend_abab)
 out:
 	litest_ungrab_device(lid);
 	litest_ungrab_device(tabletmode);
-	litest_delete_device(lid);
-	litest_delete_device(tabletmode);
-	litest_delete_device(extmouse);
+	litest_device_destroy(lid);
+	litest_device_destroy(tabletmode);
+	litest_device_destroy(extmouse);
 }
 END_TEST
 
@@ -7234,8 +6995,7 @@ START_TEST(touchpad_end_start_touch)
 
 	litest_assert_empty_queue(li);
 
-	litest_timeout_tap();
-	litest_dispatch(li);
+	litest_timeout_tap(li);
 
 	litest_touch_move_to(dev, 0, 50.2, 50.2, 50, 70, 10);
 	litest_touch_up(dev, 0);
@@ -7254,14 +7014,17 @@ START_TEST(touchpad_fuzz)
 	litest_assert_int_eq(libevdev_get_abs_fuzz(evdev, ABS_Y), 0);
 
 	if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X))
-		litest_assert_int_eq(libevdev_get_abs_fuzz(evdev, ABS_MT_POSITION_X), 0);
+		litest_assert_int_eq(libevdev_get_abs_fuzz(evdev, ABS_MT_POSITION_X),
+				     0);
 	if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y))
-		litest_assert_int_eq(libevdev_get_abs_fuzz(evdev, ABS_MT_POSITION_Y), 0);
+		litest_assert_int_eq(libevdev_get_abs_fuzz(evdev, ABS_MT_POSITION_Y),
+				     0);
 }
 END_TEST
 
 TEST_COLLECTION(touchpad)
 {
+	/* clang-format off */
 	litest_add(touchpad_1fg_motion, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add(touchpad_2fg_no_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
 
@@ -7391,10 +7154,12 @@ TEST_COLLECTION(touchpad)
 	litest_add_for_device(touchpad_end_start_touch, LITEST_WACOM_INTUOS5_FINGER);
 
 	litest_add(touchpad_fuzz, LITEST_TOUCHPAD, LITEST_ANY);
+	/* clang-format on */
 }
 
 TEST_COLLECTION(touchpad_dwt)
 {
+	/* clang-format off */
 	litest_add(touchpad_dwt, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add_for_device(touchpad_dwt_ext_and_int_keyboard, LITEST_SYNAPTICS_I2C);
 	litest_add(touchpad_dwt_enable_touch, LITEST_TOUCHPAD, LITEST_ANY);
@@ -7438,10 +7203,12 @@ TEST_COLLECTION(touchpad_dwt)
 		litest_add_parametrized_for_device(touchpad_dwt_multiple_keyboards_remove, LITEST_SYNAPTICS_I2C, params);
 	}
 	litest_add_for_device(touchpad_dwt_remove_before_keyboard, LITEST_KEYBOARD);
+	/* clang-format on */
 }
 
 TEST_COLLECTION(touchpad_palm)
 {
+	/* clang-format off */
 	litest_add(touchpad_palm_detect_at_edge, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add(touchpad_palm_detect_at_top, LITEST_TOUCHPAD, LITEST_TOPBUTTONPAD);
 	litest_add(touchpad_palm_detect_at_bottom_corners, LITEST_TOUCHPAD, LITEST_CLICKPAD);
@@ -7485,5 +7252,5 @@ TEST_COLLECTION(touchpad_palm)
 	litest_add(touchpad_palm_clickfinger_pressure_2fg, LITEST_CLICKPAD, LITEST_ANY);
 	litest_add(touchpad_palm_clickfinger_size, LITEST_CLICKPAD, LITEST_ANY);
 	litest_add(touchpad_palm_clickfinger_size_2fg, LITEST_CLICKPAD, LITEST_ANY);
-
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-trackball.c 1.30.0-1/test/test-trackball.c
--- 1.28.1-1/test/test-trackball.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-trackball.c	2025-11-25 03:40:43.000000000 +0000
@@ -80,8 +80,7 @@ START_TEST(trackball_rotation_config_no_
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	for (angle = 1; angle < 360; angle++) {
-		status = libinput_device_config_rotation_set_angle(device,
-								   angle);
+		status = libinput_device_config_rotation_set_angle(device, angle);
 		litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
 	}
 }
@@ -97,8 +96,7 @@ START_TEST(trackball_rotation_config_rig
 	litest_assert(libinput_device_config_rotation_is_available(device));
 
 	for (angle = 0; angle < 360; angle += 90) {
-		status = libinput_device_config_rotation_set_angle(device,
-								   angle);
+		status = libinput_device_config_rotation_set_angle(device, angle);
 		litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	}
 }
@@ -114,8 +112,7 @@ START_TEST(trackball_rotation_config_odd
 	litest_assert(libinput_device_config_rotation_is_available(device));
 
 	for (angle = 0; angle < 360; angle++) {
-		status = libinput_device_config_rotation_set_angle(device,
-								   angle);
+		status = libinput_device_config_rotation_set_angle(device, angle);
 		litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	}
 }
@@ -256,6 +253,7 @@ END_TEST
 
 TEST_COLLECTION(trackball)
 {
+	/* clang-format off */
 	litest_add(trackball_rotation_config_defaults, LITEST_TRACKBALL, LITEST_ANY);
 	litest_add(trackball_rotation_config_invalid_range, LITEST_TRACKBALL, LITEST_ANY);
 	litest_add(trackball_rotation_config_no_rotation, LITEST_POINTINGSTICK, LITEST_ANY);
@@ -264,4 +262,5 @@ TEST_COLLECTION(trackball)
 	litest_add(trackball_rotation_x, LITEST_TRACKBALL, LITEST_ANY);
 	litest_add(trackball_rotation_y, LITEST_TRACKBALL, LITEST_ANY);
 	litest_add(trackball_rotation_accel, LITEST_TRACKBALL, LITEST_ANY);
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-trackpoint.c 1.30.0-1/test/test-trackpoint.c
--- 1.28.1-1/test/test-trackpoint.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-trackpoint.c	2025-11-25 03:40:43.000000000 +0000
@@ -106,11 +106,8 @@ START_TEST(trackpoint_scroll)
 	litest_button_scroll(dev, BTN_MIDDLE, 1, 1);
 
 	litest_button_scroll(dev, BTN_MIDDLE, 0, 0);
-	litest_assert_button_event(li, BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_PRESSED);
-	litest_assert_button_event(li,
-				   BTN_MIDDLE,
-				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li, BTN_MIDDLE, LIBINPUT_BUTTON_STATE_RELEASED);
 
 	litest_assert_empty_queue(li);
 }
@@ -124,7 +121,7 @@ START_TEST(trackpoint_middlebutton_noscr
 
 	/* Disable middle button scrolling */
 	libinput_device_config_scroll_set_method(dev->libinput_device,
-					LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
+						 LIBINPUT_CONFIG_SCROLL_NO_SCROLL);
 
 	litest_drain_events(li);
 
@@ -143,9 +140,9 @@ START_TEST(trackpoint_middlebutton_noscr
 	litest_assert_empty_queue(li);
 
 	/* Restore default scroll behavior */
-	libinput_device_config_scroll_set_method(dev->libinput_device,
-		libinput_device_config_scroll_get_default_method(
-			dev->libinput_device));
+	libinput_device_config_scroll_set_method(
+		dev->libinput_device,
+		libinput_device_config_scroll_get_default_method(dev->libinput_device));
 }
 END_TEST
 
@@ -165,7 +162,7 @@ START_TEST(trackpoint_scroll_source)
 		ptrev = libinput_event_get_pointer_event(event);
 
 		litest_assert_enum_eq(litest_event_pointer_get_axis_source(ptrev),
-				 LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
+				      LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
 
 		libinput_event_destroy(event);
 	}
@@ -175,7 +172,6 @@ END_TEST
 START_TEST(trackpoint_topsoftbuttons_left_handed_trackpoint)
 {
 	struct litest_device *touchpad = litest_current_device();
-	struct litest_device *trackpoint;
 	struct libinput *li = touchpad->libinput;
 	enum libinput_config_status status;
 	struct libinput_event *event;
@@ -183,11 +179,10 @@ START_TEST(trackpoint_topsoftbuttons_lef
 
 	litest_disable_hold_gestures(touchpad->libinput_device);
 
-	trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
+	_destroy_(litest_device) *trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
 	litest_drain_events(li);
 	/* touchpad right-handed, trackpoint left-handed */
-	status = libinput_device_config_left_handed_set(
-					trackpoint->libinput_device, 1);
+	status = libinput_device_config_left_handed_set(trackpoint->libinput_device, 1);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_touch_down(touchpad, 0, 5, 5);
@@ -196,9 +191,7 @@ START_TEST(trackpoint_topsoftbuttons_lef
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_RIGHT,
-			       LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_is_button_event(event, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 	device = libinput_event_get_device(event);
 	litest_assert(device == trackpoint->libinput_device);
 	libinput_event_destroy(event);
@@ -206,21 +199,16 @@ START_TEST(trackpoint_topsoftbuttons_lef
 	litest_button_click_debounced(touchpad, li, BTN_LEFT, false);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_RIGHT,
-			       LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_is_button_event(event, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 	device = libinput_event_get_device(event);
 	litest_assert(device == trackpoint->libinput_device);
 	libinput_event_destroy(event);
-
-	litest_delete_device(trackpoint);
 }
 END_TEST
 
 START_TEST(trackpoint_topsoftbuttons_left_handed_touchpad)
 {
 	struct litest_device *touchpad = litest_current_device();
-	struct litest_device *trackpoint;
 	struct libinput *li = touchpad->libinput;
 	enum libinput_config_status status;
 	struct libinput_event *event;
@@ -228,11 +216,10 @@ START_TEST(trackpoint_topsoftbuttons_lef
 
 	litest_disable_hold_gestures(touchpad->libinput_device);
 
-	trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
+	_destroy_(litest_device) *trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
 	litest_drain_events(li);
 	/* touchpad left-handed, trackpoint right-handed */
-	status = libinput_device_config_left_handed_set(
-					touchpad->libinput_device, 1);
+	status = libinput_device_config_left_handed_set(touchpad->libinput_device, 1);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_touch_down(touchpad, 0, 5, 5);
@@ -249,21 +236,16 @@ START_TEST(trackpoint_topsoftbuttons_lef
 	litest_button_click_debounced(touchpad, li, BTN_LEFT, false);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_LEFT,
-			       LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_is_button_event(event, BTN_LEFT, LIBINPUT_BUTTON_STATE_RELEASED);
 	device = libinput_event_get_device(event);
 	litest_assert(device == trackpoint->libinput_device);
 	libinput_event_destroy(event);
-
-	litest_delete_device(trackpoint);
 }
 END_TEST
 
 START_TEST(trackpoint_topsoftbuttons_left_handed_both)
 {
 	struct litest_device *touchpad = litest_current_device();
-	struct litest_device *trackpoint;
 	struct libinput *li = touchpad->libinput;
 	enum libinput_config_status status;
 	struct libinput_event *event;
@@ -271,14 +253,12 @@ START_TEST(trackpoint_topsoftbuttons_lef
 
 	litest_disable_hold_gestures(touchpad->libinput_device);
 
-	trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
+	_destroy_(litest_device) *trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
 	litest_drain_events(li);
 	/* touchpad left-handed, trackpoint left-handed */
-	status = libinput_device_config_left_handed_set(
-					touchpad->libinput_device, 1);
+	status = libinput_device_config_left_handed_set(touchpad->libinput_device, 1);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
-	status = libinput_device_config_left_handed_set(
-					trackpoint->libinput_device, 1);
+	status = libinput_device_config_left_handed_set(trackpoint->libinput_device, 1);
 	litest_assert_enum_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 
 	litest_touch_down(touchpad, 0, 5, 5);
@@ -287,9 +267,7 @@ START_TEST(trackpoint_topsoftbuttons_lef
 	litest_dispatch(li);
 
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_RIGHT,
-			       LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_is_button_event(event, BTN_RIGHT, LIBINPUT_BUTTON_STATE_PRESSED);
 	device = libinput_event_get_device(event);
 	litest_assert(device == trackpoint->libinput_device);
 	libinput_event_destroy(event);
@@ -297,45 +275,39 @@ START_TEST(trackpoint_topsoftbuttons_lef
 	litest_button_click_debounced(touchpad, li, BTN_LEFT, false);
 	litest_dispatch(li);
 	event = libinput_get_event(li);
-	litest_is_button_event(event,
-			       BTN_RIGHT,
-			       LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_is_button_event(event, BTN_RIGHT, LIBINPUT_BUTTON_STATE_RELEASED);
 	device = libinput_event_get_device(event);
 	litest_assert(device == trackpoint->libinput_device);
 	libinput_event_destroy(event);
-
-	litest_delete_device(trackpoint);
 }
 END_TEST
 
 static inline void
 enable_dwtp(struct litest_device *dev)
 {
-	enum libinput_config_status status,
-				    expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
+	enum libinput_config_status status, expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
 	status = libinput_device_config_dwtp_set_enabled(dev->libinput_device,
-						LIBINPUT_CONFIG_DWTP_ENABLED);
+							 LIBINPUT_CONFIG_DWTP_ENABLED);
 	litest_assert_enum_eq(status, expected);
 }
 
 static inline void
 disable_dwtp(struct litest_device *dev)
 {
-	enum libinput_config_status status,
-				    expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
+	enum libinput_config_status status, expected = LIBINPUT_CONFIG_STATUS_SUCCESS;
 	status = libinput_device_config_dwtp_set_enabled(dev->libinput_device,
-						LIBINPUT_CONFIG_DWTP_DISABLED);
+							 LIBINPUT_CONFIG_DWTP_DISABLED);
 	litest_assert_enum_eq(status, expected);
 }
 
 START_TEST(trackpoint_palmdetect)
 {
 	struct litest_device *trackpoint = litest_current_device();
-	struct litest_device *touchpad;
 	struct libinput *li = trackpoint->libinput;
 	int i;
 
-	touchpad = litest_add_device(li, LITEST_SYNAPTICS_I2C);
+	_destroy_(litest_device) *touchpad =
+		litest_add_device(li, LITEST_SYNAPTICS_I2C);
 	if (has_disable_while_trackpointing(touchpad))
 		enable_dwtp(touchpad);
 
@@ -355,26 +327,23 @@ START_TEST(trackpoint_palmdetect)
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_trackpoint();
-	litest_dispatch(li);
+	litest_timeout_trackpoint(li);
 
 	litest_touch_down(touchpad, 0, 30, 30);
 	litest_touch_move_to(touchpad, 0, 30, 30, 80, 80, 10);
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
-
-	litest_delete_device(touchpad);
 }
 END_TEST
 
 START_TEST(trackpoint_palmdetect_dwtp_disabled)
 {
 	struct litest_device *trackpoint = litest_current_device();
-	struct litest_device *touchpad;
 	struct libinput *li = trackpoint->libinput;
 	int i;
 
-	touchpad = litest_add_device(li, LITEST_SYNAPTICS_I2C);
+	_destroy_(litest_device) *touchpad =
+		litest_add_device(li, LITEST_SYNAPTICS_I2C);
 	if (has_disable_while_trackpointing(touchpad))
 		disable_dwtp(touchpad);
 
@@ -393,19 +362,17 @@ START_TEST(trackpoint_palmdetect_dwtp_di
 	litest_touch_move_to(touchpad, 0, 30, 30, 80, 80, 10);
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
-
-	litest_delete_device(touchpad);
 }
 END_TEST
 
 START_TEST(trackpoint_palmdetect_resume_touch)
 {
 	struct litest_device *trackpoint = litest_current_device();
-	struct litest_device *touchpad;
 	struct libinput *li = trackpoint->libinput;
 	int i;
 
-	touchpad = litest_add_device(li, LITEST_SYNAPTICS_I2C);
+	_destroy_(litest_device) *touchpad =
+		litest_add_device(li, LITEST_SYNAPTICS_I2C);
 
 	if (has_disable_while_trackpointing(touchpad))
 		enable_dwtp(touchpad);
@@ -425,25 +392,22 @@ START_TEST(trackpoint_palmdetect_resume_
 	litest_touch_move_to(touchpad, 0, 30, 30, 80, 80, 10);
 	litest_assert_empty_queue(li);
 
-	litest_timeout_trackpoint();
-	litest_dispatch(li);
+	litest_timeout_trackpoint(li);
 
 	/* touch started after last tp event, expect resume */
 	litest_touch_move_to(touchpad, 0, 80, 80, 30, 30, 10);
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
-
-	litest_delete_device(touchpad);
 }
 END_TEST
 
 START_TEST(trackpoint_palmdetect_require_min_events)
 {
 	struct litest_device *trackpoint = litest_current_device();
-	struct litest_device *touchpad;
 	struct libinput *li = trackpoint->libinput;
 
-	touchpad = litest_add_device(li, LITEST_SYNAPTICS_I2C);
+	_destroy_(litest_device) *touchpad =
+		litest_add_device(li, LITEST_SYNAPTICS_I2C);
 
 	if (has_disable_while_trackpointing(touchpad))
 		enable_dwtp(touchpad);
@@ -462,18 +426,16 @@ START_TEST(trackpoint_palmdetect_require
 	litest_touch_move_to(touchpad, 0, 30, 30, 80, 80, 10);
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
-
-	litest_delete_device(touchpad);
 }
 END_TEST
 
 START_TEST(trackpoint_palmdetect_require_min_events_timeout)
 {
 	struct litest_device *trackpoint = litest_current_device();
-	struct litest_device *touchpad;
 	struct libinput *li = trackpoint->libinput;
 
-	touchpad = litest_add_device(li, LITEST_SYNAPTICS_I2C);
+	_destroy_(litest_device) *touchpad =
+		litest_add_device(li, LITEST_SYNAPTICS_I2C);
 
 	if (has_disable_while_trackpointing(touchpad))
 		enable_dwtp(touchpad);
@@ -494,15 +456,14 @@ START_TEST(trackpoint_palmdetect_require
 		litest_touch_up(touchpad, 0);
 		litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-		litest_timeout_trackpoint();
+		litest_timeout_trackpoint(li);
 	}
-
-	litest_delete_device(touchpad);
 }
 END_TEST
 
 TEST_COLLECTION(trackpoint)
 {
+	/* clang-format off */
 	litest_add(trackpoint_middlebutton, LITEST_POINTINGSTICK, LITEST_ANY);
 	litest_add(trackpoint_middlebutton_noscroll, LITEST_POINTINGSTICK, LITEST_ANY);
 	litest_add(trackpoint_scroll, LITEST_POINTINGSTICK, LITEST_ANY);
@@ -516,4 +477,5 @@ TEST_COLLECTION(trackpoint)
 	litest_add(trackpoint_palmdetect_resume_touch, LITEST_POINTINGSTICK, LITEST_ANY);
 	litest_add(trackpoint_palmdetect_require_min_events, LITEST_POINTINGSTICK, LITEST_ANY);
 	litest_add(trackpoint_palmdetect_require_min_events_timeout, LITEST_POINTINGSTICK, LITEST_ANY);
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-udev.c 1.30.0-1/test/test-udev.c
--- 1.28.1-1/test/test-udev.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-udev.c	2025-11-25 03:40:43.000000000 +0000
@@ -25,20 +25,22 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <libinput.h>
 #include <libinput-util.h>
+#include <libinput.h>
 #include <libudev.h>
 #include <unistd.h>
 
 #include "litest.h"
 
-static int open_restricted(const char *path, int flags, void *data)
+static int
+open_restricted(const char *path, int flags, void *data)
 {
 	int fd;
 	fd = open(path, flags);
 	return fd < 0 ? -errno : fd;
 }
-static void close_restricted(int fd, void *data)
+static void
+close_restricted(int fd, void *data)
 {
 	close(fd);
 }
@@ -50,12 +52,9 @@ static const struct libinput_interface s
 
 START_TEST(udev_create_NULL)
 {
-	struct libinput *li;
-	struct udev *udev;
-
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 
-	li = libinput_udev_create_context(NULL, NULL, NULL);
+	_unref_(libinput) *li = libinput_udev_create_context(NULL, NULL, NULL);
 	litest_assert(li == NULL);
 
 	li = libinput_udev_create_context(&simple_interface, NULL, NULL);
@@ -67,23 +66,19 @@ START_TEST(udev_create_NULL)
 	li = libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 	litest_assert_int_eq(libinput_udev_assign_seat(li, NULL), -1);
-
-	libinput_unref(li);
-	udev_unref(udev);
 }
 END_TEST
 
 START_TEST(udev_create_seat0)
 {
-	struct libinput *li;
 	struct libinput_event *event;
-	struct udev *udev;
 	int fd;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 	litest_assert_int_eq(libinput_udev_assign_seat(li, "seat0"), 0);
 
@@ -96,23 +91,20 @@ START_TEST(udev_create_seat0)
 	litest_assert_notnull(event);
 
 	libinput_event_destroy(event);
-	libinput_unref(li);
-	udev_unref(udev);
 }
 END_TEST
 
 START_TEST(udev_create_empty_seat)
 {
-	struct libinput *li;
 	struct libinput_event *event;
-	struct udev *udev;
 	int fd;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
 	/* expect a libinput reference, but no events */
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 	litest_assert_int_eq(libinput_udev_assign_seat(li, "seatdoesntexist"), 0);
 
@@ -124,70 +116,58 @@ START_TEST(udev_create_empty_seat)
 	litest_assert(event == NULL);
 
 	libinput_event_destroy(event);
-	libinput_unref(li);
-	udev_unref(udev);
 }
 END_TEST
 
 START_TEST(udev_create_seat_too_long)
 {
-	struct libinput *li;
-	struct udev *udev;
 	char seatname[258];
 
 	memset(seatname, 'a', sizeof(seatname) - 1);
 	seatname[sizeof(seatname) - 1] = '\0';
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 	litest_set_log_handler_bug(li);
 
 	litest_assert_int_eq(libinput_udev_assign_seat(li, seatname), -1);
 
 	litest_assert_empty_queue(li);
-
-	libinput_unref(li);
-	udev_unref(udev);
 }
 END_TEST
 
 START_TEST(udev_set_user_data)
 {
-	struct libinput *li;
-	struct udev *udev;
 	int data1, data2;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, &data1, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, &data1, udev);
 	litest_assert_notnull(li);
 	litest_assert(libinput_get_user_data(li) == &data1);
 	libinput_set_user_data(li, &data2);
 	litest_assert(libinput_get_user_data(li) == &data2);
-
-	libinput_unref(li);
-	udev_unref(udev);
 }
 END_TEST
 
 START_TEST(udev_added_seat_default)
 {
-	struct libinput *li;
 	struct libinput_event *event;
-	struct udev *udev;
 	struct libinput_device *device;
 	struct libinput_seat *seat;
 	const char *seat_name;
-	struct litest_device *dev;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 	litest_assert_int_eq(libinput_udev_assign_seat(li, "seat0"), 0);
 	litest_dispatch(li);
@@ -198,7 +178,8 @@ START_TEST(udev_added_seat_default)
 	/* Now create our own device, it should be in the "default"
 	 * logical seat. This test may fail if there is a local rule changing
 	 * that, but it'll be fine for the 99% case. */
-	dev = litest_create(LITEST_MOUSE, NULL, NULL, NULL, NULL);
+	_unused_ _destroy_(litest_device) *dev =
+		litest_create(LITEST_MOUSE, NULL, NULL, NULL, NULL);
 	litest_wait_for_event_of_type(li, LIBINPUT_EVENT_DEVICE_ADDED);
 	event = libinput_get_event(li);
 	device = libinput_event_get_device(event);
@@ -208,30 +189,18 @@ START_TEST(udev_added_seat_default)
 	seat_name = libinput_seat_get_logical_name(seat);
 	litest_assert_str_eq(seat_name, "default");
 	libinput_event_destroy(event);
-
-	libinput_unref(li);
-	udev_unref(udev);
-
-	litest_delete_device(dev);
 }
 END_TEST
 
 START_TEST(udev_change_seat)
 {
-	struct libinput *li;
-	struct udev *udev;
-	struct libinput_event *event;
-	struct libinput_device *device;
-	struct libinput_seat *seat1, *seat2;
-	const char *seat1_name;
 	const char *seat2_name = "new seat";
-	int rc;
-	struct litest_device *dev;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 	litest_assert_int_eq(libinput_udev_assign_seat(li, "seat0"), 0);
 	litest_dispatch(li);
@@ -239,72 +208,73 @@ START_TEST(udev_change_seat)
 	/* Drop any events from other devices */
 	litest_drain_events(li);
 
+	const char *devname = "udev-change-seat device";
+
 	/* Now create our own device, it should be in the "default"
 	 * logical seat. This test may fail if there is a local rule changing
 	 * that, but it'll be fine for the 99% case. */
-	dev = litest_create(LITEST_MOUSE, NULL, NULL, NULL, NULL);
-	litest_wait_for_event_of_type(li, LIBINPUT_EVENT_DEVICE_ADDED);
-	event = libinput_get_event(li);
-	device = libinput_event_get_device(event);
-	libinput_device_ref(device);
-
-	seat1 = libinput_device_get_seat(device);
-	libinput_seat_ref(seat1);
-
-	seat1_name = libinput_seat_get_logical_name(seat1);
-	libinput_event_destroy(event);
+	_unused_ _destroy_(litest_device) *dev =
+		litest_create(LITEST_MOUSE, devname, NULL, NULL, NULL);
 
+	_unref_(libinput_device) *device = NULL;
+	_autofree_ char *seat1_name = NULL;
+	while (true) {
+		litest_wait_for_event_of_type(li, LIBINPUT_EVENT_DEVICE_ADDED);
+
+		_destroy_(libinput_event) *event = libinput_get_event(li);
+		struct libinput_device *d = libinput_event_get_device(event);
+		const char *name = libinput_device_get_name(d);
+		if (strendswith(name, devname)) {
+			device = libinput_device_ref(d);
+			struct libinput_seat *seat = libinput_device_get_seat(device);
+			seat1_name = safe_strdup(libinput_seat_get_logical_name(seat));
+			break;
+		}
+	}
 	litest_drain_events(li);
 
 	/* Changing the logical seat name will remove and re-add the device */
-	rc = libinput_device_set_seat_logical_name(device,
-						   seat2_name);
+	int rc = libinput_device_set_seat_logical_name(device, seat2_name);
 	litest_assert_int_eq(rc, 0);
 
-	litest_dispatch(li);
-
-	event = libinput_get_event(li);
-	litest_assert_enum_eq(libinput_event_get_type(event),
-			 LIBINPUT_EVENT_DEVICE_REMOVED);
-
-	litest_assert(libinput_event_get_device(event) == device);
-	libinput_event_destroy(event);
-
-	event = libinput_get_event(li);
-	litest_assert_enum_eq(libinput_event_get_type(event),
-			 LIBINPUT_EVENT_DEVICE_ADDED);
-	litest_assert(libinput_event_get_device(event) != device);
-	libinput_device_unref(device);
-
-	device = libinput_event_get_device(event);
-	seat2 = libinput_device_get_seat(device);
-
-	litest_assert_str_ne(libinput_seat_get_logical_name(seat2),
-			 seat1_name);
-	litest_assert_str_eq(libinput_seat_get_logical_name(seat2),
-			 seat2_name);
-	libinput_event_destroy(event);
-
-	libinput_seat_unref(seat1);
+	while (true) {
+		litest_wait_for_event_of_type(li, LIBINPUT_EVENT_DEVICE_REMOVED);
+		_destroy_(libinput_event) *event = libinput_get_event(li);
+		litest_assert_event_type(event, LIBINPUT_EVENT_DEVICE_REMOVED);
+		if (libinput_event_get_device(event) == device)
+			break;
+	}
 
-	libinput_unref(li);
-	udev_unref(udev);
+	_unref_(libinput_seat) *seat2 = NULL;
+	while (true) {
+		litest_wait_for_event_of_type(li, LIBINPUT_EVENT_DEVICE_ADDED);
+
+		_destroy_(libinput_event) *event = libinput_get_event(li);
+		litest_assert_event_type(event, LIBINPUT_EVENT_DEVICE_ADDED);
+		struct libinput_device *d = libinput_event_get_device(event);
+		const char *name = libinput_device_get_name(d);
+		if (strendswith(name, devname)) {
+			seat2 = libinput_device_get_seat(d);
+			libinput_seat_ref(seat2);
+			break;
+		}
+	}
 
-	litest_delete_device(dev);
+	litest_assert_str_ne(libinput_seat_get_logical_name(seat2), seat1_name);
+	litest_assert_str_eq(libinput_seat_get_logical_name(seat2), seat2_name);
 }
 END_TEST
 
 START_TEST(udev_double_suspend)
 {
-	struct libinput *li;
 	struct libinput_event *event;
-	struct udev *udev;
 	int fd;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 	litest_assert_int_eq(libinput_udev_assign_seat(li, "seat0"), 0);
 
@@ -321,22 +291,19 @@ START_TEST(udev_double_suspend)
 	libinput_resume(li);
 
 	libinput_event_destroy(event);
-	libinput_unref(li);
-	udev_unref(udev);
 }
 END_TEST
 
 START_TEST(udev_double_resume)
 {
-	struct libinput *li;
 	struct libinput_event *event;
-	struct udev *udev;
 	int fd;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 	litest_assert_int_eq(libinput_udev_assign_seat(li, "seat0"), 0);
 
@@ -353,8 +320,6 @@ START_TEST(udev_double_resume)
 	libinput_resume(li);
 
 	libinput_event_destroy(event);
-	libinput_unref(li);
-	udev_unref(udev);
 }
 END_TEST
 
@@ -380,15 +345,14 @@ process_events_count_devices(struct libi
 
 START_TEST(udev_suspend_resume)
 {
-	struct libinput *li;
-	struct udev *udev;
 	int fd;
 	int num_devices = 0;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 	litest_assert_int_eq(libinput_udev_assign_seat(li, "seat0"), 0);
 
@@ -411,73 +375,56 @@ START_TEST(udev_suspend_resume)
 	litest_assert_int_ge(litest_dispatch(li), 0);
 	process_events_count_devices(li, &num_devices);
 	litest_assert_int_gt(num_devices, 0);
-
-	libinput_unref(li);
-	udev_unref(udev);
 }
 END_TEST
 
 START_TEST(udev_resume_before_seat)
 {
-	struct libinput *li;
-	struct udev *udev;
-	int rc;
-
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 
-	rc = libinput_resume(li);
+	int rc = libinput_resume(li);
 	litest_assert_int_eq(rc, 0);
-
-	libinput_unref(li);
-	udev_unref(udev);
 }
 END_TEST
 
 START_TEST(udev_suspend_resume_before_seat)
 {
-	struct libinput *li;
-	struct udev *udev;
-	int rc;
-
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 
 	libinput_suspend(li);
-	rc = libinput_resume(li);
+	int rc = libinput_resume(li);
 	litest_assert_int_eq(rc, 0);
-
-	libinput_unref(li);
-	udev_unref(udev);
 }
 END_TEST
 
 START_TEST(udev_device_sysname)
 {
-	struct libinput *li;
 	struct libinput_event *ev;
 	struct libinput_device *device;
 	const char *sysname;
-	struct udev *udev;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 	litest_assert_int_eq(libinput_udev_assign_seat(li, "seat0"), 0);
 
 	litest_dispatch(li);
 
 	while ((ev = libinput_get_event(li))) {
-		if (libinput_event_get_type(ev) !=
-		    LIBINPUT_EVENT_DEVICE_ADDED) {
+		if (libinput_event_get_type(ev) != LIBINPUT_EVENT_DEVICE_ADDED) {
 			libinput_event_destroy(ev);
 			continue;
 		}
@@ -490,16 +437,11 @@ START_TEST(udev_device_sysname)
 		litest_assert(strstartswith(sysname, "event"));
 		libinput_event_destroy(ev);
 	}
-
-	libinput_unref(li);
-	udev_unref(udev);
 }
 END_TEST
 
 START_TEST(udev_seat_recycle)
 {
-	struct udev *udev;
-	struct libinput *li;
 	struct libinput_event *ev;
 	struct libinput_device *device;
 	struct libinput_seat *saved_seat = NULL;
@@ -508,10 +450,11 @@ START_TEST(udev_seat_recycle)
 	int found = 0;
 	void *user_data;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 	litest_assert_int_eq(libinput_udev_assign_seat(li, "seat0"), 0);
 
@@ -565,22 +508,18 @@ START_TEST(udev_seat_recycle)
 	}
 
 	litest_assert(found == 1);
-
-	libinput_unref(li);
-	udev_unref(udev);
 }
 END_TEST
 
 START_TEST(udev_path_add_device)
 {
-	struct udev *udev;
-	struct libinput *li;
 	struct libinput_device *device;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 	litest_assert_int_eq(libinput_udev_assign_seat(li, "seat0"), 0);
 
@@ -588,23 +527,19 @@ START_TEST(udev_path_add_device)
 	device = libinput_path_add_device(li, "/dev/input/event0");
 	litest_assert(device == NULL);
 	litest_restore_log_handler(li);
-
-	libinput_unref(li);
-	udev_unref(udev);
 }
 END_TEST
 
 START_TEST(udev_path_remove_device)
 {
-	struct udev *udev;
-	struct libinput *li;
 	struct libinput_device *device;
 	struct libinput_event *event;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 	litest_assert_int_eq(libinput_udev_assign_seat(li, "seat0"), 0);
 	litest_dispatch(li);
@@ -620,27 +555,24 @@ START_TEST(udev_path_remove_device)
 	litest_restore_log_handler(li);
 
 	libinput_event_destroy(event);
-	libinput_unref(li);
-	udev_unref(udev);
 }
 END_TEST
 
 START_TEST(udev_ignore_device)
 {
-	struct udev *udev;
-	struct libinput *li;
 	struct libinput_device *device;
 	struct libinput_event *event;
-	struct litest_device *dev;
 	const char *devname;
 
-	dev = litest_create(LITEST_IGNORED_MOUSE, NULL, NULL, NULL, NULL);
+	_destroy_(litest_device) *dev =
+		litest_create(LITEST_IGNORED_MOUSE, NULL, NULL, NULL, NULL);
 	devname = libevdev_get_name(dev->evdev);
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	litest_assert_notnull(udev);
 
-	li = libinput_udev_create_context(&simple_interface, NULL, udev);
+	_unref_(libinput) *li =
+		libinput_udev_create_context(&simple_interface, NULL, udev);
 	litest_assert_notnull(li);
 	litest_restore_log_handler(li);
 
@@ -650,8 +582,7 @@ START_TEST(udev_ignore_device)
 	event = libinput_get_event(li);
 	litest_assert_notnull(event);
 	while (event) {
-		if (libinput_event_get_type(event) ==
-		    LIBINPUT_EVENT_DEVICE_ADDED) {
+		if (libinput_event_get_type(event) == LIBINPUT_EVENT_DEVICE_ADDED) {
 			const char *name;
 
 			device = libinput_event_get_device(event);
@@ -662,16 +593,12 @@ START_TEST(udev_ignore_device)
 		litest_dispatch(li);
 		event = libinput_get_event(li);
 	}
-
-	libinput_unref(li);
-	udev_unref(udev);
-
-	litest_delete_device(dev);
 }
 END_TEST
 
 TEST_COLLECTION(udev)
 {
+	/* clang-format off */
 	litest_add_no_device(udev_create_NULL);
 	litest_add_no_device(udev_create_seat0);
 	litest_add_no_device(udev_create_empty_seat);
@@ -693,4 +620,5 @@ TEST_COLLECTION(udev)
 	litest_add_for_device(udev_path_remove_device, LITEST_SYNAPTICS_CLICKPAD_X220);
 
 	litest_add_no_device(udev_ignore_device);
+	/* clang-format on */
 }
diff -pruN 1.28.1-1/test/test-util-includes.c 1.30.0-1/test/test-util-includes.c
--- 1.28.1-1/test/test-util-includes.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-util-includes.c	1970-01-01 00:00:00.000000000 +0000
@@ -1,6 +0,0 @@
-/* compile test for the util files */
-#include @FILE@
-
-int main(void) {
-	return 0;
-}
diff -pruN 1.28.1-1/test/test-util-includes.c.in 1.30.0-1/test/test-util-includes.c.in
--- 1.28.1-1/test/test-util-includes.c.in	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/test/test-util-includes.c.in	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,6 @@
+/* compile test for the util files */
+#include @FILE@
+
+int main(void) {
+	return 0;
+}
diff -pruN 1.28.1-1/test/test-utils.c 1.30.0-1/test/test-utils.c
--- 1.28.1-1/test/test-utils.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test-utils.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,30 +23,221 @@
 
 #include <config.h>
 
-#include <valgrind/valgrind.h>
-
+#include <errno.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <valgrind/valgrind.h>
 
-#include "litest.h"
-#include "litest-runner.h"
+#include "util-bits.h"
+#include "util-files.h"
+#include "util-input-event.h"
 #include "util-list.h"
-#include "util-strings.h"
-#include "util-time.h"
-#include "util-prop-parsers.h"
 #include "util-macros.h"
-#include "util-bits.h"
+#include "util-matrix.h"
+#include "util-mem.h"
+#include "util-newtype.h"
+#include "util-prop-parsers.h"
 #include "util-range.h"
 #include "util-ratelimit.h"
 #include "util-stringbuf.h"
-#include "util-matrix.h"
-#include "util-input-event.h"
+#include "util-strings.h"
+#include "util-time.h"
 
+#include "evdev-frame.h"
+#include "litest-runner.h"
 #include "litest.h"
 
-#define  TEST_VERSIONSORT
+#define TEST_VERSIONSORT
 #include "libinput-versionsort.h"
 
+START_TEST(auto_test)
+{
+	/* This one is just a compile test */
+	auto tv = us2tv(0);
+	tv.tv_sec = 0;
+	litest_assert_int_eq(tv.tv_sec, 0);
+}
+END_TEST
+
+START_TEST(mkdir_p_test)
+{
+	const char *testdir = "/tmp/litest_mkdir_test";
+	litest_assert_neg_errno_success(mkdir_p("/"));
+
+	rmdir(testdir);
+	litest_assert_neg_errno_success(mkdir_p(testdir));
+	/* EEXIST is not an error */
+	litest_assert_neg_errno_success(mkdir_p(testdir));
+	rmdir(testdir);
+
+	int ret = mkdir_p("/proc/foo");
+	litest_assert_msg(ret == -ENOENT || ret == -EACCES,
+			  "mkdir_p(\"/proc/foo\") returned %d\n",
+			  ret);
+}
+END_TEST
+
+START_TEST(rmdir_r_test)
+{
+	const char *testdir = "/tmp/litest_rmdir_test";
+	_autofree_ char *path = strdup_printf("%s/foo/bar/baz", testdir);
+	mkdir_p(path);
+
+	_autofree_ char *f1 = strdup_printf("%s/remain", testdir);
+	_autofree_ char *f2 = strdup_printf("%s/foo/remove", testdir);
+	_autofree_ char *f3 = strdup_printf("%s/foo/bar/to-remove", testdir);
+	_autofree_ char *f4 = strdup_printf("%s/foo/bar/baz/wipeme", testdir);
+
+	litest_assert_errno_success(close(open(f1, O_WRONLY | O_CREAT, 0644)));
+	litest_assert_errno_success(close(open(f2, O_WRONLY | O_CREAT, 0644)));
+	litest_assert_errno_success(close(open(f3, O_WRONLY | O_CREAT, 0644)));
+	litest_assert_errno_success(close(open(f4, O_WRONLY | O_CREAT, 0644)));
+
+	struct stat st;
+	litest_assert_errno_success(stat(f1, &st));
+	litest_assert_errno_success(stat(f2, &st));
+	litest_assert_errno_success(stat(f3, &st));
+	litest_assert_errno_success(stat(f4, &st));
+
+	_autofree_ char *rmpath = strdup_printf("%s/foo/", testdir);
+	int rc = rmdir_r(rmpath);
+	litest_assert_neg_errno_success(rc);
+
+	litest_assert_errno_success(stat(f1, &st));
+	litest_assert_errno_success(stat(testdir, &st));
+
+	rc = stat(f2, &st) < 0 ? -errno : 0;
+	litest_assert_int_eq(rc, -ENOENT);
+	rc = stat(f3, &st) < 0 ? -errno : 0;
+	litest_assert_int_eq(rc, -ENOENT);
+	rc = stat(f4, &st) < 0 ? -errno : 0;
+	litest_assert_int_eq(rc, -ENOENT);
+}
+END_TEST
+
+START_TEST(tmpdir_test)
+{
+	_autofree_ char *tmpdir_path = NULL;
+	{
+		_destroy_(tmpdir) *tmpdir = tmpdir_create(NULL);
+
+		tmpdir_path = safe_strdup(tmpdir->path);
+
+		_autofree_ char *f1 = strdup_printf("%s/wipeme", tmpdir_path);
+		litest_assert_errno_success(close(open(f1, O_WRONLY | O_CREAT, 0644)));
+	}
+	struct stat st;
+	int rc = stat(tmpdir_path, &st) < 0 ? -errno : 0;
+	litest_assert_int_eq(rc, -ENOENT);
+}
+END_TEST
+
+START_TEST(find_files_test)
+{
+	_autofree_ char *dirname = strdup("/tmp/litest_find_files_test.XXXXXX");
+	mkdtemp(dirname);
+
+	_autofree_ char *d1 = strdup_printf("%s/d1", dirname);
+	_autofree_ char *d2 = strdup_printf("%s/d2", dirname);
+	_autofree_ char *d3 = strdup_printf("%s/d3", dirname);
+
+	litest_assert_neg_errno_success(mkdir_p(d1));
+	litest_assert_neg_errno_success(mkdir_p(d2));
+	litest_assert_neg_errno_success(mkdir_p(d3));
+
+	/* clang-format off */
+	struct f {
+		const char *name;
+		const char *dir1;
+		const char *dir2;
+		const char *dir3;
+		char *expected;
+	} files[] = {
+		{ "10-abc.suf", d1, d2, d3 },
+		{ "20-def.suf", d1, NULL, d3 },
+		{ "30-ghi.suf", d1, d2, NULL },
+		{ "40-jkl.suf", NULL, d2, NULL },
+		{ "50-mno.suf", NULL, d2, d3 },
+		{ "60-pgr.suf", NULL, NULL, d3 },
+		{ "70-abc.suf", NULL, NULL, d3 },
+		{ "21-xyz.fix", NULL, NULL, d3 },
+		{ "35-uvw.fix", NULL, d2, d3 },
+		{ "70-rst.fix", d1, NULL, d3 },
+		{ NULL },
+	};
+	/* clang-format on */
+	for (struct f *f = files; f->name; f++) {
+		if (f->dir1) {
+			_autofree_ char *path =
+				strdup_printf("%s/%s", f->dir1, f->name);
+			close(open(path, O_WRONLY | O_CREAT, 0644));
+			f->expected = steal(&path);
+		}
+		if (f->dir2) {
+			_autofree_ char *path =
+				strdup_printf("%s/%s", f->dir2, f->name);
+			close(open(path, O_WRONLY | O_CREAT, 0644));
+			if (!f->expected)
+				f->expected = steal(&path);
+		}
+		if (f->dir3) {
+			_autofree_ char *path =
+				strdup_printf("%s/%s", f->dir3, f->name);
+			close(open(path, O_WRONLY | O_CREAT, 0644));
+			if (!f->expected)
+				f->expected = steal(&path);
+		}
+	}
+
+	const char *dirs[] = { d1, d2, d3, NULL };
+	size_t nfiles;
+	_autostrvfree_ char **paths = list_files(dirs, "suf", &nfiles);
+	litest_assert_int_eq(nfiles, (size_t)7);
+	litest_assert_str_eq(paths[0], files[0].expected);
+	litest_assert_str_eq(paths[1], files[1].expected);
+	litest_assert_str_eq(paths[2], files[2].expected);
+	litest_assert_str_eq(paths[3], files[3].expected);
+	litest_assert_str_eq(paths[4], files[4].expected);
+	litest_assert_str_eq(paths[5], files[5].expected);
+	litest_assert_str_eq(paths[6], files[6].expected);
+	litest_assert_str_eq(paths[7], NULL);
+
+	for (struct f *f = files; f->name; f++) {
+		if (f->dir1) {
+			_autofree_ char *path =
+				strdup_printf("%s/%s", f->dir1, f->name);
+			unlink(path);
+		}
+		if (f->dir2) {
+			_autofree_ char *path =
+				strdup_printf("%s/%s", f->dir2, f->name);
+			unlink(path);
+		}
+		if (f->dir3) {
+			_autofree_ char *path =
+				strdup_printf("%s/%s", f->dir3, f->name);
+			unlink(path);
+		}
+		free(f->expected);
+	}
+	rmdir(d1);
+	rmdir(d2);
+	rmdir(d3);
+	rmdir(dirname);
+
+	const char *empty[] = { NULL };
+	_autostrvfree_ char **empty_path = list_files(empty, "suf", &nfiles);
+	litest_assert_int_eq(nfiles, (size_t)0);
+	litest_assert_ptr_notnull(empty_path);
+	litest_assert_ptr_null(empty_path[0]);
+
+	_autostrvfree_ char **also_empty_path = list_files(NULL, "suf", &nfiles);
+	litest_assert_int_eq(nfiles, (size_t)0);
+	litest_assert_ptr_notnull(also_empty_path);
+	litest_assert_ptr_null(also_empty_path[0]);
+}
+END_TEST
+
 START_TEST(array_for_each)
 {
 	int ai[6];
@@ -64,7 +255,7 @@ START_TEST(array_for_each)
 	for (size_t i = 0; i < 32; i++) {
 		as[i].a = 10 + i;
 		as[i].b = 20 + i;
-		as[i].ptr = (int*)0xab + i;
+		as[i].ptr = (int *)0xab + i;
 	}
 
 	int iexpected = 20;
@@ -84,7 +275,7 @@ START_TEST(array_for_each)
 	struct as sexpected = {
 		.a = 10,
 		.b = 20,
-		.ptr = (int*)0xab,
+		.ptr = (int *)0xab,
 	};
 	ARRAY_FOR_EACH(as, entry) {
 		litest_assert_int_eq(entry->a, sexpected.a);
@@ -104,7 +295,7 @@ START_TEST(bitfield_helpers)
 	 * test: 0, 1, 7, 8, 31, 32, and 33
 	 */
 	unsigned char read_bitfield[] = { 0x83, 0x1, 0x0, 0x80, 0x3 };
-	unsigned char write_bitfield[ARRAY_LENGTH(read_bitfield)] = {0};
+	unsigned char write_bitfield[ARRAY_LENGTH(read_bitfield)] = { 0 };
 	size_t i;
 
 	/* Now check that the bitfield we wrote to came out to be the same as
@@ -128,10 +319,207 @@ START_TEST(bitfield_helpers)
 		}
 	}
 
-	litest_assert_int_eq(memcmp(read_bitfield,
-				write_bitfield,
-				sizeof(read_bitfield)),
-			 0);
+	litest_assert_int_eq(
+		memcmp(read_bitfield, write_bitfield, sizeof(read_bitfield)),
+		0);
+}
+END_TEST
+
+START_TEST(bitmask_test)
+{
+	{
+		bitmask_t mask1 = bitmask_from_u32(0x12345678U);
+		litest_assert(bitmask_as_u32(mask1) == 0x12345678U);
+
+		bitmask_t mask2 = bitmask_from_u32(0);
+		litest_assert_int_eq(bitmask_as_u32(mask2), 0U);
+
+		bitmask_t mask3 = bitmask_from_u32(0xFFFFFFFFU);
+		litest_assert_int_eq(bitmask_as_u32(mask3), 0xFFFFFFFFU);
+	}
+	{
+		bitmask_t mask1 = bitmask_new();
+		litest_assert(bitmask_is_empty(mask1));
+
+		bitmask_t mask2 = bitmask_from_u32(0x00000001U);
+		litest_assert(!bitmask_is_empty(mask2));
+
+		bitmask_t mask3 = bitmask_from_u32(0xFFFFFFFFU);
+		litest_assert(!bitmask_is_empty(mask3));
+	}
+	{
+		bitmask_t mask1 = bitmask_from_u32(0x0000000FU);
+		bitmask_t bits1 = bitmask_from_u32(0x00000003U);
+		litest_assert(bitmask_any(mask1, bits1));
+
+		bitmask_t mask2 = bitmask_from_u32(0x0000000FU);
+		bitmask_t bits2 = bitmask_from_u32(0x000000F0U);
+		litest_assert(!bitmask_any(mask2, bits2));
+
+		bitmask_t mask3 = bitmask_from_u32(0x00000000U);
+		bitmask_t bits3 = bitmask_from_u32(0x00000001U);
+		litest_assert(!bitmask_any(mask3, bits3));
+
+		bitmask_t mask4 = bitmask_from_u32(0xFFFFFFFFU);
+		bitmask_t bits4 = bitmask_from_u32(0xFFFFFFFFU);
+		litest_assert(bitmask_any(mask4, bits4));
+
+		bitmask_t mask5 = bitmask_from_u32(0x10000000U);
+		bitmask_t bits5 = bitmask_from_u32(0x10000000U);
+		litest_assert(bitmask_any(mask5, bits5));
+	}
+	{
+		bitmask_t mask1 = bitmask_from_u32(0x0000000FU);
+		bitmask_t bits1 = bitmask_from_u32(0x00000003U);
+		litest_assert(bitmask_all(mask1, bits1));
+		litest_assert(!bitmask_all(bits1, mask1));
+
+		bitmask_t mask2 = bitmask_from_u32(0x0000000FU);
+		bitmask_t bits2 = bitmask_from_u32(0x0000000FU);
+		litest_assert(bitmask_all(mask2, bits2));
+		litest_assert(bitmask_all(bits2, mask2));
+
+		bitmask_t mask3 = bitmask_from_u32(0x00000000U);
+		bitmask_t bits3 = bitmask_from_u32(0x00000000U);
+		litest_assert(!bitmask_all(mask3, bits3)); /* zero is special */
+
+		bitmask_t mask4 = bitmask_from_u32(0xFFFFFFFFU);
+		bitmask_t bits4 = bitmask_from_u32(0xFFFFFFFFU);
+		litest_assert(bitmask_all(mask4, bits4));
+
+		bitmask_t mask5 = bitmask_from_u32(0x10000000U);
+		bitmask_t bits5 = bitmask_from_u32(0x10000000U);
+		litest_assert(bitmask_all(mask5, bits5));
+	}
+	{
+
+		bitmask_t mask1 = bitmask_from_u32(0x0000000FU);
+		bitmask_t bits1 = bitmask_from_u32(0x000000F0U);
+		litest_assert(!bitmask_merge(&mask1, bits1));
+		litest_assert_int_eq(mask1.mask, 0x000000FFU);
+
+		bitmask_t mask2 = bitmask_from_u32(0x0000000FU);
+		bitmask_t bits2 = bitmask_from_u32(0x0000000FU);
+		litest_assert(bitmask_merge(&mask2, bits2));
+		litest_assert_int_eq(mask2.mask, 0x0000000FU);
+
+		bitmask_t mask3 = bitmask_new();
+		bitmask_t bits3 = bitmask_from_u32(0xFFFFFFFFU);
+		litest_assert(!bitmask_merge(&mask3, bits3));
+		litest_assert_int_eq(mask3.mask, 0xFFFFFFFFU);
+
+		bitmask_t mask4 = bitmask_from_u32(0x80000000U);
+		bitmask_t bits4 = bitmask_from_u32(0x00000001U);
+		litest_assert(!bitmask_merge(&mask4, bits4));
+		litest_assert_int_eq(mask4.mask, 0x80000001U);
+	}
+	{
+		bitmask_t mask1 = bitmask_from_u32(0x000000FFU);
+		bitmask_t bits1 = bitmask_from_u32(0x0000000FU);
+		litest_assert(bitmask_clear(&mask1, bits1));
+		litest_assert_int_eq(mask1.mask, 0x000000F0U);
+
+		bitmask_t mask2 = bitmask_from_u32(0x0000000FU);
+		bitmask_t bits2 = bitmask_from_u32(0x0000000FU);
+		litest_assert(bitmask_clear(&mask2, bits2));
+		litest_assert_int_eq(mask2.mask, 0x00000000U);
+
+		bitmask_t mask3 = bitmask_from_u32(0xFFFFFFFFU);
+		bitmask_t bits3 = bitmask_from_u32(0x00000000U);
+		litest_assert(!bitmask_clear(&mask3, bits3)); /* zero is special */
+		litest_assert_int_eq(mask3.mask, 0xFFFFFFFFU);
+
+		bitmask_t mask4 = bitmask_from_u32(0xFFFFFFFFU);
+		bitmask_t bits4 = bitmask_from_u32(0xFFFFFFFFU);
+		litest_assert(bitmask_clear(&mask4, bits4));
+		litest_assert_int_eq(mask4.mask, 0x0U);
+	}
+	{
+		bitmask_t mask1 = bitmask_from_u32(0x00000001U);
+		litest_assert(bitmask_bit_is_set(mask1, 0));
+		litest_assert(!bitmask_bit_is_set(mask1, 1));
+
+		bitmask_t mask2 = bitmask_from_u32(0x80000000U);
+		litest_assert(bitmask_bit_is_set(mask2, 31));
+		litest_assert(!bitmask_bit_is_set(mask2, 0));
+
+		bitmask_t mask3 = bitmask_from_u32(0xFFFFFFFFU);
+		litest_assert(bitmask_bit_is_set(mask3, 0));
+		litest_assert(bitmask_bit_is_set(mask3, 31));
+		litest_assert(bitmask_bit_is_set(mask3, 16));
+
+		bitmask_t mask4 = bitmask_new();
+		litest_assert(!bitmask_bit_is_set(mask4, 0));
+		litest_assert(!bitmask_bit_is_set(mask4, 1));
+	}
+	{
+		bitmask_t mask1 = bitmask_new();
+		litest_assert(!bitmask_set_bit(&mask1, 0));
+		litest_assert_int_eq(mask1.mask, 0x00000001U);
+
+		litest_assert(bitmask_set_bit(&mask1, 0));
+		litest_assert_int_eq(mask1.mask, 0x00000001U);
+
+		litest_assert(!bitmask_set_bit(&mask1, 31));
+		litest_assert_int_eq(mask1.mask, 0x80000001U);
+
+		bitmask_t mask2 = bitmask_from_u32(0x0000000FU);
+		litest_assert(!bitmask_set_bit(&mask2, 4));
+		litest_assert_int_eq(mask2.mask, 0x0000001FU);
+		litest_assert(bitmask_set_bit(&mask2, 4));
+		litest_assert_int_eq(mask2.mask, 0x0000001FU);
+	}
+	{
+		bitmask_t mask1 = bitmask_from_u32(0xFFFFFFFFU);
+		litest_assert(bitmask_clear_bit(&mask1, 0));
+		litest_assert_int_eq(mask1.mask, 0xFFFFFFFEU);
+
+		litest_assert(!bitmask_clear_bit(&mask1, 0));
+		litest_assert_int_eq(mask1.mask, 0xFFFFFFFEU);
+
+		litest_assert(bitmask_clear_bit(&mask1, 31));
+		litest_assert_int_eq(mask1.mask, 0x7FFFFFFEU);
+
+		bitmask_t mask2 = bitmask_from_u32(0x0000001FU);
+		litest_assert(bitmask_clear_bit(&mask2, 4));
+		litest_assert_int_eq(mask2.mask, 0x0000000FU);
+		litest_assert(!bitmask_clear_bit(&mask2, 4));
+		litest_assert_int_eq(mask2.mask, 0x0000000FU);
+	}
+	{
+		bitmask_t mask1 = bitmask_from_bit(0);
+		litest_assert_int_eq(mask1.mask, 0x00000001U);
+
+		bitmask_t mask2 = bitmask_from_bit(31);
+		litest_assert_int_eq(mask2.mask, 0x80000000U);
+
+		bitmask_t mask3 = bitmask_from_bit(16);
+		litest_assert_int_eq(mask3.mask, 0x00010000U);
+	}
+	{
+		bitmask_t mask1 = bitmask_from_u32(0x12345678U);
+		litest_assert_int_eq(mask1.mask, 0x12345678U);
+
+		bitmask_t mask2 = bitmask_from_u32(0);
+		litest_assert_int_eq(mask2.mask, 0U);
+
+		bitmask_t mask3 = bitmask_from_u32(0xFFFFFFFFU);
+		litest_assert_int_eq(mask3.mask, 0xFFFFFFFFU);
+	}
+	{
+		bitmask_t mask1 = bitmask_from_bits(1, 2, 5);
+		litest_assert_int_eq(mask1.mask, bit(1) | bit(2) | bit(5));
+
+		bitmask_t mask2 = bitmask_from_bits(0);
+		litest_assert_int_eq(mask2.mask, bit(0));
+	}
+	{
+		bitmask_t mask1 = bitmask_from_masks(0x1, 0x2, 0x8);
+		litest_assert_int_eq(mask1.mask, 0x0000000BU);
+
+		bitmask_t mask2 = bitmask_from_masks(0x0);
+		litest_assert_int_eq(mask2.mask, 0x00000000U);
+	}
 }
 END_TEST
 
@@ -146,8 +534,7 @@ START_TEST(matrix_helpers)
 
 	for (row = 0; row < 3; row++) {
 		for (col = 0; col < 3; col++) {
-			litest_assert_int_eq(m1.val[row][col],
-					 (row == col) ? 1 : 0);
+			litest_assert_int_eq(m1.val[row][col], (row == col) ? 1 : 0);
 		}
 	}
 	litest_assert(matrix_is_identity(&m1));
@@ -223,8 +610,7 @@ START_TEST(ratelimit_helpers)
 	for (j = 0; j < 3; ++j) {
 		/* a burst of 9 attempts must succeed */
 		for (i = 0; i < 9; ++i) {
-			litest_assert_enum_eq(ratelimit_test(&rl),
-					 RATELIMIT_PASS);
+			litest_assert_enum_eq(ratelimit_test(&rl), RATELIMIT_PASS);
 		}
 
 		/* the 10th attempt reaches the threshold */
@@ -235,15 +621,13 @@ START_TEST(ratelimit_helpers)
 
 		/* ..regardless of how often we try. */
 		for (i = 0; i < 100; ++i) {
-			litest_assert_enum_eq(ratelimit_test(&rl),
-					 RATELIMIT_EXCEEDED);
+			litest_assert_enum_eq(ratelimit_test(&rl), RATELIMIT_EXCEEDED);
 		}
 
 		/* ..even after waiting 20ms */
 		msleep(100);
 		for (i = 0; i < 100; ++i) {
-			litest_assert_enum_eq(ratelimit_test(&rl),
-					 RATELIMIT_EXCEEDED);
+			litest_assert_enum_eq(ratelimit_test(&rl), RATELIMIT_EXCEEDED);
 		}
 
 		/* but after 1000ms the counter is reset */
@@ -259,6 +643,7 @@ struct parser_test {
 
 START_TEST(dpi_parser)
 {
+	/* clang-format off */
 	struct parser_test tests[] = {
 		{ "450 *1800 3200", 1800 },
 		{ "*450 1800 3200", 450 },
@@ -282,8 +667,9 @@ START_TEST(dpi_parser)
 		{ "", 0 },
 		{ "   ", 0 },
 		{ "* ", 0 },
-		{ NULL, 0 }
+		{ NULL, 0 },
 	};
+	/* clang-format on */
 	int i, dpi;
 
 	for (i = 0; tests[i].tag != NULL; i++) {
@@ -298,6 +684,7 @@ END_TEST
 
 START_TEST(wheel_click_parser)
 {
+	/* clang-format off */
 	struct parser_test tests[] = {
 		{ "1", 1 },
 		{ "10", 10 },
@@ -311,8 +698,9 @@ START_TEST(wheel_click_parser)
 		{ "10-", 0 },
 		{ "sadfasfd", 0 },
 		{ "361", 0 },
-		{ NULL, 0 }
+		{ NULL, 0 },
 	};
+	/* clang-format on */
 
 	int i, angle;
 
@@ -325,6 +713,7 @@ END_TEST
 
 START_TEST(wheel_click_count_parser)
 {
+	/* clang-format off */
 	struct parser_test tests[] = {
 		{ "1", 1 },
 		{ "10", 10 },
@@ -340,6 +729,7 @@ START_TEST(wheel_click_count_parser)
 		{ "361", 0 },
 		{ NULL, 0 }
 	};
+	/* clang-format on */
 
 	int i, angle;
 
@@ -355,6 +745,7 @@ END_TEST
 
 START_TEST(dimension_prop_parser)
 {
+	/* clang-format off */
 	struct parser_test_dimension {
 		char *tag;
 		bool success;
@@ -376,8 +767,9 @@ START_TEST(dimension_prop_parser)
 		{ "0xaf", false, 0, 0 },
 		{ "0x0x", false, 0, 0 },
 		{ "x10", false, 0, 0 },
-		{ NULL, false, 0, 0 }
+		{ NULL, false, 0, 0 },
 	};
+	/* clang-format on */
 	int i;
 	size_t x, y;
 	bool success;
@@ -402,6 +794,7 @@ END_TEST
 
 START_TEST(reliability_prop_parser)
 {
+	/* clang-format off */
 	struct parser_test_reliability {
 		char *tag;
 		bool success;
@@ -413,8 +806,9 @@ START_TEST(reliability_prop_parser)
 		{ "", false, 0 },
 		{ "0", false, 0 },
 		{ "1", false, 0 },
-		{ NULL, false, 0, }
+		{ NULL, false, 0, },
 	};
+	/* clang-format on */
 	enum switch_reliability r;
 	bool success;
 	int i;
@@ -442,6 +836,7 @@ START_TEST(calibration_prop_parser)
 {
 #define DEFAULT_VALUES { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }
 	const float untouched[6] = DEFAULT_VALUES;
+	/* clang-format off */
 	struct parser_test_calibration {
 		char *prop;
 		bool success;
@@ -456,8 +851,9 @@ START_TEST(calibration_prop_parser)
 		{ "6.00012 3.244 4.238 5.2421 6.0134 8.860", true,
 			{ 6.00012, 3.244, 4.238, 5.2421, 6.0134, 8.860 }},
 		{ "0xff 2 3 4 5 6", false, DEFAULT_VALUES },
-		{ NULL, false, DEFAULT_VALUES }
+		{ NULL, false, DEFAULT_VALUES },
 	};
+	/* clang-format on */
 	bool success;
 	float calibration[6];
 	int rc;
@@ -466,17 +862,12 @@ START_TEST(calibration_prop_parser)
 	for (i = 0; tests[i].prop != NULL; i++) {
 		memcpy(calibration, untouched, sizeof(calibration));
 
-		success = parse_calibration_property(tests[i].prop,
-						     calibration);
+		success = parse_calibration_property(tests[i].prop, calibration);
 		litest_assert_int_eq(success, tests[i].success);
 		if (success)
-			rc = memcmp(tests[i].values,
-				    calibration,
-				    sizeof(calibration));
+			rc = memcmp(tests[i].values, calibration, sizeof(calibration));
 		else
-			rc = memcmp(untouched,
-				    calibration,
-				    sizeof(calibration));
+			rc = memcmp(untouched, calibration, sizeof(calibration));
 		litest_assert_int_eq(rc, 0);
 	}
 
@@ -491,6 +882,7 @@ END_TEST
 
 START_TEST(range_prop_parser)
 {
+	/* clang-format off */
 	struct parser_test_range {
 		char *tag;
 		bool success;
@@ -505,8 +897,9 @@ START_TEST(range_prop_parser)
 		{ "", false, 0, 0 },
 		{ "abcd", false, 0, 0 },
 		{ "10:30:10", false, 0, 0 },
-		{ NULL, false, 0, 0 }
+		{ NULL, false, 0, 0 },
 	};
+	/* clang-format on */
 	int i;
 	int hi, lo;
 	bool success;
@@ -531,6 +924,7 @@ END_TEST
 
 START_TEST(boolean_prop_parser)
 {
+	/* clang-format off */
 	struct parser_test_range {
 		char *tag;
 		bool success;
@@ -541,8 +935,9 @@ START_TEST(boolean_prop_parser)
 		{ "-1", false, false },
 		{ "2", false, false },
 		{ "abcd", false, false },
-		{ NULL, false, false }
+		{ NULL, false, false },
 	};
+	/* clang-format on */
 	int i;
 	bool success, b;
 
@@ -563,6 +958,7 @@ END_TEST
 
 START_TEST(evcode_prop_parser)
 {
+	/* clang-format off */
 	struct parser_test_tuple {
 		const char *prop;
 		bool success;
@@ -613,6 +1009,7 @@ START_TEST(evcode_prop_parser)
 		{ .prop = "none", .success = false },
 		{ .prop = NULL },
 	};
+	/* clang-format on */
 	struct parser_test_tuple *t;
 
 	for (int i = 0; tests[i].prop; i++) {
@@ -641,6 +1038,7 @@ END_TEST
 
 START_TEST(input_prop_parser)
 {
+	/* clang-format off */
 	struct parser_test_val {
 		const char *prop;
 		bool success;
@@ -664,6 +1062,7 @@ START_TEST(input_prop_parser)
 		{ .prop = "none", .success = false },
 		{ .prop = NULL },
 	};
+	/* clang-format on */
 	struct parser_test_val *t;
 
 	for (int i = 0; tests[i].prop; i++) {
@@ -688,6 +1087,7 @@ END_TEST
 
 START_TEST(evdev_abs_parser)
 {
+	/* clang-format off */
 	struct test {
 		uint32_t which;
 		const char *prop;
@@ -740,6 +1140,7 @@ START_TEST(evdev_abs_parser)
 		{ .which = 0, .prop = ":asb::::" },
 		{ .which = 0, .prop = "foo" },
 	};
+	/* clang-format on */
 
 	ARRAY_FOR_EACH(tests, t) {
 		struct input_absinfo abs;
@@ -775,6 +1176,7 @@ END_TEST
 
 START_TEST(human_time)
 {
+	/* clang-format off */
 	struct ht_tests {
 		uint64_t interval;
 		unsigned int value;
@@ -794,6 +1196,7 @@ START_TEST(human_time)
 		{ 1000 * 24 * 60 * s2us(60), 1000, "d" },
 		{ 0, 0, NULL },
 	};
+	/* clang-format on */
 	for (int i = 0; tests[i].unit != NULL; i++) {
 		struct human_time ht;
 
@@ -812,6 +1215,7 @@ struct atoi_test {
 
 START_TEST(safe_atoi_test)
 {
+	/* clang-format off */
 	struct atoi_test tests[] = {
 		{ "10", true, 10 },
 		{ "20", true, 20 },
@@ -828,8 +1232,9 @@ START_TEST(safe_atoi_test)
 		{ "0xaf", false, 0 },
 		{ "0x0x", false, 0 },
 		{ "x10", false, 0 },
-		{ NULL, false, 0 }
+		{ NULL, false, 0 },
 	};
+	/* clang-format on */
 	int v;
 	bool success;
 
@@ -847,6 +1252,7 @@ END_TEST
 
 START_TEST(safe_atoi_base_16_test)
 {
+	/* clang-format off */
 	struct atoi_test tests[] = {
 		{ "10", true, 0x10 },
 		{ "20", true, 0x20 },
@@ -861,8 +1267,9 @@ START_TEST(safe_atoi_base_16_test)
 		{ "0xak", false, 0 },
 		{ "0x", false, 0 },
 		{ "x10", false, 0 },
-		{ NULL, false, 0 }
+		{ NULL, false, 0 },
 	};
+	/* clang-format on */
 
 	int v;
 	bool success;
@@ -881,6 +1288,7 @@ END_TEST
 
 START_TEST(safe_atoi_base_8_test)
 {
+	/* clang-format off */
 	struct atoi_test tests[] = {
 		{ "7", true, 07 },
 		{ "10", true, 010 },
@@ -898,8 +1306,9 @@ START_TEST(safe_atoi_base_8_test)
 		{ "0xak", false, 0 },
 		{ "0x", false, 0 },
 		{ "x10", false, 0 },
-		{ NULL, false, 0 }
+		{ NULL, false, 0 },
 	};
+	/* clang-format on */
 
 	int v;
 	bool success;
@@ -924,6 +1333,7 @@ struct atou_test {
 
 START_TEST(safe_atou_test)
 {
+	/* clang-format off */
 	struct atou_test tests[] = {
 		{ "10", true, 10 },
 		{ "20", true, 20 },
@@ -939,8 +1349,9 @@ START_TEST(safe_atou_test)
 		{ "0xaf", false, 0 },
 		{ "0x0x", false, 0 },
 		{ "x10", false, 0 },
-		{ NULL, false, 0 }
+		{ NULL, false, 0 },
 	};
+	/* clang-format on */
 	unsigned int v;
 	bool success;
 
@@ -958,6 +1369,7 @@ END_TEST
 
 START_TEST(safe_atou_base_16_test)
 {
+	/* clang-format off */
 	struct atou_test tests[] = {
 		{ "10", true, 0x10 },
 		{ "20", true, 0x20 },
@@ -972,8 +1384,9 @@ START_TEST(safe_atou_base_16_test)
 		{ "0xak", false, 0 },
 		{ "0x", false, 0 },
 		{ "x10", false, 0 },
-		{ NULL, false, 0 }
+		{ NULL, false, 0 },
 	};
+	/* clang-format on */
 
 	unsigned int v;
 	bool success;
@@ -992,6 +1405,7 @@ END_TEST
 
 START_TEST(safe_atou_base_8_test)
 {
+	/* clang-format off */
 	struct atou_test tests[] = {
 		{ "7", true, 07 },
 		{ "10", true, 010 },
@@ -1009,8 +1423,9 @@ START_TEST(safe_atou_base_8_test)
 		{ "0xak", false, 0 },
 		{ "0x", false, 0 },
 		{ "x10", false, 0 },
-		{ NULL, false, 0 }
+		{ NULL, false, 0 },
 	};
+	/* clang-format on */
 
 	unsigned int v;
 	bool success;
@@ -1027,8 +1442,52 @@ START_TEST(safe_atou_base_8_test)
 }
 END_TEST
 
+struct atou64_test {
+	char *str;
+	bool success;
+	uint64_t val;
+};
+
+START_TEST(safe_atou64_test)
+{
+	/* clang-format off */
+	struct atou64_test tests[] = {
+		{ "10", true, 10 },
+		{ "20", true, 20 },
+		{ "-1", false, 0 },
+		{ "9999999999", true, 9999999999 },
+		{ "2147483647", true, 2147483647 },
+		{ "-2147483648", false, 0},
+		{ "0x0", false, 0 },
+		{ "-10x10", false, 0 },
+		{ "1x-99", false, 0 },
+		{ "", false, 0 },
+		{ "abd", false, 0 },
+		{ "xabd", false, 0 },
+		{ "0xaf", false, 0 },
+		{ "0x0x", false, 0 },
+		{ "x10", false, 0 },
+		{ NULL, false, 0 },
+	};
+	/* clang-format on */
+	uint64_t v;
+	bool success;
+
+	for (int i = 0; tests[i].str != NULL; i++) {
+		v = 0xad;
+		success = safe_atou64(tests[i].str, &v);
+		litest_assert(success == tests[i].success);
+		if (success)
+			litest_assert_int_eq(v, tests[i].val);
+		else
+			litest_assert_int_eq(v, 0xadU);
+	}
+}
+END_TEST
+
 START_TEST(safe_atod_test)
 {
+	/* clang-format off */
 	struct atod_test {
 		char *str;
 		bool success;
@@ -1057,8 +1516,9 @@ START_TEST(safe_atod_test)
 		{ "abd", false, 0 },
 		{ "xabd", false, 0 },
 		{ "0x0x", false, 0 },
-		{ NULL, false, 0 }
+		{ NULL, false, 0 },
 	};
+	/* clang-format on */
 	double v;
 	bool success;
 
@@ -1076,6 +1536,7 @@ END_TEST
 
 START_TEST(strsplit_test)
 {
+	/* clang-format off */
 	struct strsplit_test {
 		const char *string;
 		const char *delim;
@@ -1099,8 +1560,9 @@ START_TEST(strsplit_test)
 		{ " ", " ", { NULL }, 0 },
 		{ "     ", " ", { NULL }, 0 },
 		{ "oneoneone", "one", { NULL} , 0 },
-		{ NULL, NULL, { NULL }, 0}
+		{ NULL, NULL, { NULL }, 0},
 	};
+	/* clang-format on */
 	struct strsplit_test *t = tests;
 
 	while (t->string) {
@@ -1114,7 +1576,7 @@ START_TEST(strsplit_test)
 
 		/* When there are no elements validate return value is Null,
 		   otherwise validate result array is Null terminated. */
-		if(t->nresults == 0)
+		if (t->nresults == 0)
 			litest_assert_ptr_eq(strv, NULL);
 		else
 			litest_assert_ptr_eq(strv[t->nresults], NULL);
@@ -1130,7 +1592,8 @@ struct strv_test_data {
 	unsigned char bitmask[1];
 };
 
-static int strv_test_set_bitmask(const char *str, size_t index, void *data)
+static int
+strv_test_set_bitmask(const char *str, size_t index, void *data)
 {
 	struct strv_test_data *td = data;
 
@@ -1144,6 +1607,7 @@ static int strv_test_set_bitmask(const c
 
 START_TEST(strv_for_each_test)
 {
+	/* clang-format off */
 	struct test_data {
 		const char *terminator;
 		int index;
@@ -1158,6 +1622,7 @@ START_TEST(strv_for_each_test)
 		{ NULL, 0, 0x1f },
 		{ NULL, 0 },
 	};
+	/* clang-format on */
 	const char *array[] = { "one", "two", "three", "four", "five", NULL };
 	struct test_data *t = test_data;
 
@@ -1189,8 +1654,163 @@ START_TEST(strv_for_each_test)
 }
 END_TEST
 
+__attribute__((format(printf, 1, 0))) static char **
+test_strv_appendv(char *format, ...)
+{
+	va_list args;
+	va_start(args, format);
+	char **strv = NULL;
+	strv = strv_append_vprintf(strv, "%s %d", args);
+	va_end(args);
+	return strv;
+}
+
+START_TEST(strv_append_test)
+{
+	{
+		char *test_strv1[] = { "a", "b", "c", NULL };
+		char **test_strv2 = NULL;
+
+		litest_assert_int_eq(strv_len(test_strv1), 4U);
+		litest_assert_int_eq(strv_len(test_strv2), 0U);
+	}
+	{
+		char **strv = NULL;
+		char *dup = safe_strdup("test");
+		strv = strv_append_take(strv, &dup);
+		litest_assert_ptr_null(dup);
+		litest_assert_ptr_notnull(strv);
+		litest_assert_str_eq(strv[0], "test");
+		litest_assert_ptr_eq(strv[1], NULL);
+		litest_assert_int_eq(strv_len(strv), 2U);
+
+		char *dup2 = safe_strdup("test2");
+		strv = strv_append_take(strv, &dup2);
+		litest_assert_ptr_null(dup2);
+		litest_assert_str_eq(strv[1], "test2");
+		litest_assert_ptr_eq(strv[2], NULL);
+		litest_assert_int_eq(strv_len(strv), 3U);
+
+		strv = strv_append_take(strv, NULL);
+		litest_assert_int_eq(strv_len(strv), 3U);
+		strv_free(strv);
+	}
+	{
+		char **strv = NULL;
+		strv = strv_append_strdup(strv, "banana");
+		litest_assert(strv != NULL);
+		litest_assert_str_eq(strv[0], "banana");
+		litest_assert_ptr_null(strv[1]);
+		litest_assert_int_eq(strv_len(strv), 2U);
+		strv_free(strv);
+	}
+	{
+		char **strv = test_strv_appendv("%s %d", "apple", 2);
+		litest_assert_ptr_notnull(strv);
+		litest_assert_str_eq(strv[0], "apple 2");
+		litest_assert_ptr_null(strv[1]);
+		litest_assert_int_eq(strv_len(strv), 2U);
+		strv_free(strv);
+	}
+	{
+		char **strv = NULL;
+		strv = strv_append_printf(strv, "coco%s", "nut");
+		litest_assert_ptr_notnull(strv);
+		litest_assert_str_eq(strv[0], "coconut");
+		litest_assert_ptr_null(strv[1]);
+		litest_assert_int_eq(strv_len(strv), 2U);
+		strv_free(strv);
+	}
+}
+END_TEST
+
+START_TEST(strv_find_test)
+{
+	char *strv[] = { "a", "b", "c", NULL };
+
+	bool rc;
+	size_t index;
+
+	rc = strv_find(strv, "a", &index);
+	litest_assert(rc);
+	litest_assert_int_eq(index, 0U);
+
+	rc = strv_find(strv, "b", &index);
+	litest_assert(rc);
+	litest_assert_int_eq(index, 1U);
+
+	rc = strv_find(strv, "a", NULL);
+	litest_assert(rc);
+
+	index = 0xffff;
+	rc = strv_find(strv, "d", &index);
+	litest_assert(!rc);
+	litest_assert_int_eq(index, 0xffffU);
+
+	rc = strv_find(strv, "d", NULL);
+	litest_assert(!rc);
+
+	rc = strv_find(NULL, "a", &index);
+	litest_assert(!rc);
+	litest_assert_int_eq(index, 0xffffU);
+
+	rc = strv_find(NULL, NULL, &index);
+	litest_assert(!rc);
+	litest_assert_int_eq(index, 0xffffU);
+
+	rc = strv_find(strv, NULL, &index);
+	litest_assert(!rc);
+	litest_assert_int_eq(index, 0xffffU);
+}
+END_TEST
+
+START_TEST(strv_find_substring_test)
+{
+	char *strv[] = { "a", "bc", "cccc", NULL };
+
+	bool rc;
+	size_t index;
+
+	rc = strv_find_substring(strv, "a", &index);
+	litest_assert(rc);
+	litest_assert_int_eq(index, 0U);
+
+	rc = strv_find_substring(strv, "b", &index);
+	litest_assert(rc);
+	litest_assert_int_eq(index, 1U);
+
+	rc = strv_find_substring(strv, "c", &index);
+	litest_assert(rc);
+	litest_assert_int_eq(index, 1U);
+
+	rc = strv_find_substring(strv, "a", NULL);
+	litest_assert(rc);
+
+	index = 0xffff;
+	rc = strv_find_substring(strv, "d", &index);
+	litest_assert(!rc);
+	litest_assert_int_eq(index, 0xffffU);
+
+	rc = strv_find_substring(strv, "d", NULL);
+	litest_assert(!rc);
+
+	rc = strv_find_substring(NULL, "a", &index);
+	litest_assert(!rc);
+	litest_assert_int_eq(index, 0xffffU);
+
+	rc = strv_find_substring(NULL, NULL, &index);
+	litest_assert(!rc);
+	litest_assert_int_eq(index, 0xffffU);
+
+	rc = strv_find_substring(strv, NULL, &index);
+	litest_assert(!rc);
+	litest_assert_int_eq(index, 0xffffU);
+}
+END_TEST
+
 START_TEST(double_array_from_string_test)
 {
+	/* clang-format off */
 	struct double_array_from_string_test {
 		const char *string;
 		const char *delim;
@@ -1213,15 +1833,14 @@ START_TEST(double_array_from_string_test
 		{ "    ", " ", { 0 }, 0 },
 		{ "", " ", { 0 }, 0 },
 		{ "oneoneone", "one", { 0 }, 0 },
-		{ NULL, NULL, { 0 }, 0 }
+		{ NULL, NULL, { 0 }, 0 },
 	};
+	/* clang-format on */
 	struct double_array_from_string_test *t = tests;
 
 	while (t->string) {
 		size_t len;
-		double *array = double_array_from_string(t->string,
-							 t->delim,
-							 &len);
+		double *array = double_array_from_string(t->string, t->delim, &len);
 		litest_assert_int_eq(len, t->len);
 
 		for (size_t idx = 0; idx < len; idx++) {
@@ -1237,6 +1856,7 @@ END_TEST
 
 START_TEST(strargv_test)
 {
+	/* clang-format off */
 	struct argv_test {
 		int argc;
 		char *argv[10];
@@ -1251,6 +1871,7 @@ START_TEST(strargv_test)
 		{ 1, {NULL, NULL}, 0 },
 		{ 3, {"hello", NULL, "World"}, 0 },
 	};
+	/* clang-format on */
 
 	ARRAY_FOR_EACH(tests, t) {
 		char **strv = strv_from_argv(t->argc, t->argv);
@@ -1274,6 +1895,7 @@ END_TEST
 
 START_TEST(kvsplit_double_test)
 {
+	/* clang-format off */
 	struct kvsplit_dbl_test {
 		const char *string;
 		const char *psep;
@@ -1296,18 +1918,16 @@ START_TEST(kvsplit_double_test)
 		{ "a:b", "x", ":", -1, {}},
 		{ "", " ", "x", -1, {}},
 		{ "1.2.3.4.5", ".", "", -1, {}},
-		{ NULL }
+		{ NULL },
 	};
+	/* clang-format on */
 	struct kvsplit_dbl_test *t = tests;
 
 	while (t->string) {
 		struct key_value_double *result = NULL;
 		ssize_t npairs;
 
-		npairs = kv_double_from_string(t->string,
-					       t->psep,
-					       t->kvsep,
-					       &result);
+		npairs = kv_double_from_string(t->string, t->psep, t->kvsep, &result);
 		litest_assert_int_eq(npairs, t->nresults);
 
 		for (ssize_t i = 0; i < npairs; i++) {
@@ -1323,6 +1943,7 @@ END_TEST
 
 START_TEST(strjoin_test)
 {
+	/* clang-format off */
 	struct strjoin_test {
 		char *strv[10];
 		const char *joiner;
@@ -1339,10 +1960,11 @@ START_TEST(strjoin_test)
 		{ { "a", "b", "c", NULL }, "", "abc" },
 		{ { "", "b", "c", NULL }, "x", "xbxc" },
 		{ { "", "", "", NULL }, "", "" },
-		{ { NULL }, NULL, NULL }
+		{ { NULL }, NULL, NULL },
 	};
+	/* clang-format on */
 	struct strjoin_test *t = tests;
-	struct strjoin_test nulltest = { {NULL}, "x", NULL };
+	struct strjoin_test nulltest = { { NULL }, "x", NULL };
 
 	while (t->strv[0]) {
 		char *str;
@@ -1361,6 +1983,7 @@ END_TEST
 
 START_TEST(strstrip_test)
 {
+	/* clang-format off */
 	struct strstrip_test {
 		const char *string;
 		const char *expected;
@@ -1383,8 +2006,9 @@ START_TEST(strstrip_test)
 		{ " foo\n",		"foo",		" \n" },
 		{ "",			"",		"abc" },
 		{ "",			"",		"" },
-		{ NULL , NULL, NULL }
+		{ NULL , NULL, NULL },
 	};
+	/* clang-format on */
 	struct strstrip_test *t = tests;
 
 	while (t->string) {
@@ -1399,6 +2023,7 @@ END_TEST
 
 START_TEST(strendswith_test)
 {
+	/* clang-format off */
 	struct strendswith_test {
 		const char *string;
 		const char *suffix;
@@ -1413,16 +2038,17 @@ START_TEST(strendswith_test)
 		{ "", "foo", false },
 		{ NULL, NULL, false },
 	};
+	/* clang-format on */
 
 	for (struct strendswith_test *t = tests; t->string; t++) {
-		litest_assert_int_eq(strendswith(t->string, t->suffix),
-				 t->expected);
+		litest_assert_int_eq(strendswith(t->string, t->suffix), t->expected);
 	}
 }
 END_TEST
 
 START_TEST(strstartswith_test)
 {
+	/* clang-format off */
 	struct strstartswith_test {
 		const char *string;
 		const char *suffix;
@@ -1437,16 +2063,17 @@ START_TEST(strstartswith_test)
 		{ "foo", "", false },
 		{ NULL, NULL, false },
 	};
+	/* clang-format on */
 
 	for (struct strstartswith_test *t = tests; t->string; t++) {
-		litest_assert_int_eq(strstartswith(t->string, t->suffix),
-				 t->expected);
+		litest_assert_int_eq(strstartswith(t->string, t->suffix), t->expected);
 	}
 }
 END_TEST
 
 START_TEST(strsanitize_test)
 {
+	/* clang-format off */
 	struct strsanitize_test {
 		const char *string;
 		const char *expected;
@@ -1461,6 +2088,7 @@ START_TEST(strsanitize_test)
 		{ "%s%s", "%%s%%s" },
 		{ NULL, NULL },
 	};
+	/* clang-format on */
 
 	for (struct strsanitize_test *t = tests; t->string; t++) {
 		char *sanitized = str_sanitize(t->string);
@@ -1476,10 +2104,10 @@ START_TEST(list_test_insert)
 		int val;
 		struct list node;
 	} tests[] = {
-		{ .val  = 1 },
-		{ .val  = 2 },
-		{ .val  = 3 },
-		{ .val  = 4 },
+		{ .val = 1 },
+		{ .val = 2 },
+		{ .val = 3 },
+		{ .val = 4 },
 	};
 	struct list_test *t;
 	struct list head;
@@ -1507,10 +2135,10 @@ START_TEST(list_test_append)
 		int val;
 		struct list node;
 	} tests[] = {
-		{ .val  = 1 },
-		{ .val  = 2 },
-		{ .val  = 3 },
-		{ .val  = 4 },
+		{ .val = 1 },
+		{ .val = 2 },
+		{ .val = 3 },
+		{ .val = 4 },
 	};
 	struct list_test *t;
 	struct list head;
@@ -1531,16 +2159,67 @@ START_TEST(list_test_append)
 }
 END_TEST
 
+START_TEST(list_test_chain)
+{
+	struct list_test {
+		int val;
+		struct list node;
+	} tests[] = {
+		{ .val = 1 },
+		{ .val = 2 },
+		{ .val = 3 },
+		{ .val = 4 },
+	};
+	struct list l1, l2;
+	struct list_test *t;
+	int val;
+
+	list_init(&l1);
+	list_init(&l2);
+
+	list_chain(&l1, &l2);
+	litest_assert(list_empty(&l2));
+
+	list_append(&l2, &tests[0].node);
+	list_append(&l2, &tests[1].node);
+	list_chain(&l1, &l2);
+	litest_assert(list_empty(&l2));
+
+	val = 1;
+	list_for_each_safe(t, &l1, node) {
+		litest_assert_int_eq(t->val, val);
+		val++;
+		list_remove(&t->node);
+	}
+	litest_assert_int_eq(val, 3);
+
+	list_append(&l1, &tests[0].node);
+	list_append(&l1, &tests[1].node);
+	list_append(&l2, &tests[2].node);
+	list_append(&l2, &tests[3].node);
+
+	list_chain(&l1, &l2);
+	litest_assert(list_empty(&l2));
+
+	val = 1;
+	list_for_each(t, &l1, node) {
+		litest_assert_int_eq(t->val, val);
+		val++;
+	}
+	litest_assert_int_eq(val, 5);
+}
+END_TEST
+
 START_TEST(list_test_foreach)
 {
 	struct list_test {
 		int val;
 		struct list node;
 	} tests[] = {
-		{ .val  = 1 },
-		{ .val  = 2 },
-		{ .val  = 3 },
-		{ .val  = 4 },
+		{ .val = 1 },
+		{ .val = 2 },
+		{ .val = 3 },
+		{ .val = 4 },
 	};
 	struct list_test *t;
 	struct list head;
@@ -1564,6 +2243,64 @@ START_TEST(list_test_foreach)
 }
 END_TEST
 
+START_TEST(list_test_first_last)
+{
+	struct list_test {
+		int val;
+		struct list node;
+	} tests[] = {
+		{ .val = 1 },
+		{ .val = 2 },
+		{ .val = 3 },
+		{ .val = 4 },
+	};
+	struct list head;
+
+	list_init(&head);
+
+	ARRAY_FOR_EACH(tests, t) {
+		list_append(&head, &t->node);
+	}
+
+	struct list_test *first;
+	struct list_test *last;
+
+	first = list_first_entry(&head, first, node);
+	last = list_last_entry(&head, last, node);
+	litest_assert_ptr_eq(first, &tests[0]);
+	litest_assert_ptr_eq(last, &tests[3]);
+
+	struct list_test *second;
+	struct list_test *penultimate;
+
+	second = list_first_entry(&first->node, first, node);
+	penultimate = list_last_entry(&last->node, last, node);
+	litest_assert_ptr_eq(second, &tests[1]);
+	litest_assert_ptr_eq(penultimate, &tests[2]);
+
+	/* Now remove nodes */
+
+	/* No change expected */
+	list_remove(&tests[2].node);
+	first = list_first_entry(&head, first, node);
+	last = list_last_entry(&head, last, node);
+	litest_assert_ptr_eq(first, &tests[0]);
+	litest_assert_ptr_eq(last, &tests[3]);
+
+	list_remove(&tests[3].node);
+	first = list_first_entry(&head, first, node);
+	last = list_last_entry(&head, last, node);
+	litest_assert_ptr_eq(first, &tests[0]);
+	litest_assert_ptr_eq(last, &tests[1]);
+
+	list_remove(&tests[0].node);
+	first = list_first_entry(&head, first, node);
+	last = list_last_entry(&head, last, node);
+	litest_assert_ptr_eq(first, &tests[1]);
+	litest_assert_ptr_eq(last, &tests[1]);
+}
+END_TEST
+
 START_TEST(strverscmp_test)
 {
 	litest_assert_int_eq(libinput_strverscmp("", ""), 0);
@@ -1720,7 +2457,6 @@ START_TEST(range_test)
 		expected++;
 	}
 	litest_assert_int_eq(r, 101);
-
 }
 END_TEST
 
@@ -1782,9 +2518,8 @@ START_TEST(stringbuf_test)
 		rc = stringbuf_append_from_fd(b, pipefd[0], 64);
 		litest_assert_neg_errno_success(rc);
 
-		char *expected;
-		rc = xasprintf(&expected, "%s%s", compare ? compare : "", str);
-		litest_assert_neg_errno_success(rc);
+		char *expected = strdup_printf("%s%s", compare ? compare : "", str);
+		litest_assert_ptr_notnull(expected);
 		litest_assert_str_eq(b->data, expected);
 
 		free(compare);
@@ -1803,7 +2538,7 @@ START_TEST(stringbuf_test)
 
 	for (size_t i = 0; i < maxsize; i += stride) {
 		char buf[stride];
-		memset(buf, i/stride, sizeof(buf));
+		memset(buf, i / stride, sizeof(buf));
 		rc = write(pipefd[1], buf, sizeof(buf));
 		litest_assert_neg_errno_success(rc);
 	}
@@ -1812,9 +2547,9 @@ START_TEST(stringbuf_test)
 	litest_assert_int_eq(b->len, maxsize);
 	litest_assert_int_ge(b->sz, maxsize);
 
-	for (size_t i = 0; i < maxsize; i+= stride) {
+	for (size_t i = 0; i < maxsize; i += stride) {
 		char buf[stride];
-		memset(buf, i/stride, sizeof(buf));
+		memset(buf, i / stride, sizeof(buf));
 		litest_assert_int_eq(memcmp(buf, b->data + i, sizeof(buf)), 0);
 	}
 
@@ -1834,10 +2569,10 @@ START_TEST(multivalue_test)
 		const char *s;
 		multivalue_extract_typed(&v, 's', &s);
 		litest_assert_str_eq(s, "test");
-		litest_assert_ptr_eq(s, (const char*)v.value.s);
+		litest_assert_ptr_eq(s, (const char *)v.value.s);
 		multivalue_extract(&v, &s);
 		litest_assert_str_eq(s, "test");
-		litest_assert_ptr_eq(s, (const char*)v.value.s);
+		litest_assert_ptr_eq(s, (const char *)v.value.s);
 
 		struct multivalue copy = multivalue_copy(&v);
 		litest_assert_int_eq(copy.type, v.type);
@@ -1950,11 +2685,521 @@ START_TEST(multivalue_test)
 		litest_assert_str_eq(str, "0.123400");
 		free(str);
 	}
+}
+END_TEST
+
+DECLARE_NEWTYPE(newint, int);
+DECLARE_NEWTYPE(newdouble, double);
+
+START_TEST(newtype_test)
+{
+	{
+		newint_t n1 = newint_from_int(1);
+		newint_t n2 = newint_from_int(2);
+
+		litest_assert_int_eq(newint(n1), 1);
+		litest_assert_int_eq(newint_as_int(n1), 1);
+		litest_assert_int_eq(newint(n2), 2);
+		litest_assert_int_eq(newint_as_int(n2), 2);
+
+		litest_assert_int_eq(newint_cmp(n1, n2), -1);
+		litest_assert_int_eq(newint_cmp(n1, n1), 0);
+		litest_assert_int_eq(newint_cmp(n2, n1), 1);
+
+		newint_t copy = newint_copy(n1);
+		litest_assert_int_eq(newint_cmp(n1, copy), 0);
+
+		newint_t min = newint_min(n1, n2);
+		newint_t max = newint_max(n1, n2);
+		litest_assert_int_eq(newint_cmp(min, n1), 0);
+		litest_assert_int_eq(newint_cmp(max, n2), 0);
+
+		litest_assert(newint_gt(n1, 0));
+		litest_assert(newint_eq(n1, 1));
+		litest_assert(newint_ge(n1, 1));
+		litest_assert(newint_le(n1, 1));
+		litest_assert(newint_ne(n1, 2));
+		litest_assert(newint_lt(n1, 2));
+
+		litest_assert(!newint_gt(n1, 1));
+		litest_assert(!newint_eq(n1, 0));
+		litest_assert(!newint_ge(n1, 2));
+		litest_assert(!newint_le(n1, 0));
+		litest_assert(!newint_ne(n1, 1));
+		litest_assert(!newint_lt(n1, 1));
+	}
+	{
+		newdouble_t n1 = newdouble_from_double(1.2);
+		newdouble_t n2 = newdouble_from_double(2.3);
+
+		litest_assert_double_eq(newdouble(n1), 1.2);
+		litest_assert_double_eq(newdouble_as_double(n1), 1.2);
+		litest_assert_double_eq(newdouble(n2), 2.3);
+		litest_assert_double_eq(newdouble_as_double(n2), 2.3);
+
+		litest_assert_int_eq(newdouble_cmp(n1, n2), -1);
+		litest_assert_int_eq(newdouble_cmp(n1, n1), 0);
+		litest_assert_int_eq(newdouble_cmp(n2, n1), 1);
+
+		newdouble_t copy = newdouble_copy(n1);
+		litest_assert_int_eq(newdouble_cmp(n1, copy), 0);
+
+		newdouble_t min = newdouble_min(n1, n2);
+		newdouble_t max = newdouble_max(n1, n2);
+		litest_assert_int_eq(newdouble_cmp(min, n1), 0);
+		litest_assert_int_eq(newdouble_cmp(max, n2), 0);
+
+		litest_assert(newdouble_gt(n1, 0.0));
+		litest_assert(newdouble_eq(n1, 1.2));
+		litest_assert(newdouble_ge(n1, 1.2));
+		litest_assert(newdouble_le(n1, 1.2));
+		litest_assert(newdouble_ne(n1, 2.3));
+		litest_assert(newdouble_lt(n1, 2.3));
+
+		litest_assert(!newdouble_gt(n1, 1.2));
+		litest_assert(!newdouble_eq(n1, 0.0));
+		litest_assert(!newdouble_ge(n1, 2.3));
+		litest_assert(!newdouble_le(n1, 0.0));
+		litest_assert(!newdouble_ne(n1, 1.2));
+		litest_assert(!newdouble_lt(n1, 1.2));
+	}
+}
+END_TEST
+
+struct sunref {};
+struct sdestroy {};
+struct sfree {};
+
+static void
+sunref_unref(struct sunref *s)
+{
+	free(s);
+}
+static void
+sdestroy_destroy(struct sdestroy *s)
+{
+	free(s);
+}
+static void
+sfree_free(struct sfree *s)
+{
+	free(s);
+}
+
+DEFINE_UNREF_CLEANUP_FUNC(sunref);
+DEFINE_DESTROY_CLEANUP_FUNC(sdestroy);
+DEFINE_FREE_CLEANUP_FUNC(sfree);
+
+START_TEST(attribute_cleanup)
+{
+	/* These tests will likely only show up in valgrind,
+	 * the various asserts are just to shut up the compiler
+	 * about unused variables
+	 */
+	{
+		_autofree_ char *autofree = zalloc(64);
+		litest_assert(autofree);
+	}
+	{
+		_autofree_ char *stolen = zalloc(64);
+		free(steal(&stolen));
+	}
+	{
+		_autoclose_ int fd = open("/proc/self/cmdline", O_RDONLY);
+		litest_assert_int_ge(fd, 0);
+
+		_autoclose_ int badfd = -1;
+		litest_assert_int_eq(badfd, -1);
+
+		_autoclose_ int stealfd = open("/proc/self/cmdline", O_RDONLY);
+		steal_fd(&stealfd);
+		litest_assert_int_eq(stealfd, -1);
+	}
+	{
+		_autostrvfree_ char **strv = zalloc(3 * sizeof(*strv));
+		for (int i = 0; i < 2; i++) {
+			strv[i] = strdup_printf("element %d", i);
+		}
+
+		_autostrvfree_ char **badstrv = NULL;
+		litest_assert_ptr_null(badstrv);
+	}
+	{
+		_autofclose_ FILE *fp = fopen("/proc/self/cmdline", "r");
+		litest_assert_ptr_notnull(fp);
+
+		_autofclose_ FILE *badfd = NULL;
+		litest_assert_ptr_null(badfd);
+	}
+	{
+		_unref_(sunref) *s = zalloc(sizeof(*s));
+	}
+	{
+		_destroy_(sdestroy) *s = zalloc(sizeof(*s));
+	}
+	{
+		_free_(sfree) *s = zalloc(sizeof(*s));
+	}
+}
+END_TEST
+
+START_TEST(macros_expand)
+{
+#define _A1(_1) _1, #_1
+#define _A2(_1, _2) _1, _2
+#define A(...) _VARIABLE_MACRO(_A, __VA_ARGS__)
+	char buf[64];
+	snprintf(buf, sizeof(buf), "%d:%s", A(0));
+	litest_assert_str_eq(buf, "0:0");
+	snprintf(buf, sizeof(buf), "%d:%s", A(100));
+	litest_assert_str_eq(buf, "100:100");
+	snprintf(buf, sizeof(buf), "%d:%s", A(100, "hundred"));
+	litest_assert_str_eq(buf, "100:hundred");
+#undef _A1
+#undef _A2
+#undef A
+}
+END_TEST
+
+START_TEST(evdev_frames)
+{
+#define U(u_) evdev_usage_from_uint32_t(u_)
+	{
+		evdev_frame_unref(NULL); /* unref on NULL is permitted */
+	}
+	{
+		_unref_(evdev_frame) *frame = evdev_frame_new(3);
+		litest_assert_int_eq(evdev_frame_get_count(frame), 1U); /* SYN_REPORT */
+
+		litest_assert_ptr_eq(evdev_frame_ref(frame), frame);
+		litest_assert_ptr_eq(evdev_frame_unref(frame), NULL);
+	}
+	{
+		_unref_(evdev_frame) *frame = evdev_frame_new(3);
+		struct evdev_event toobig[] = {
+			{
+				.usage = U(EVDEV_ABS_X),
+				.value = 1,
+			},
+			{
+				.usage = U(EVDEV_ABS_Y),
+				.value = 2,
+			},
+			{
+				.usage = U(EVDEV_ABS_Z),
+				.value = 3,
+			},
+			{
+				.usage = U(EVDEV_SYN_REPORT),
+				.value = 0,
+			},
+		};
+
+		int rc = evdev_frame_set(frame, toobig, ARRAY_LENGTH(toobig));
+		litest_assert_int_eq(rc, -ENOMEM);
+	}
+	{
+		struct evdev_event events[] = {
+			{
+				.usage = U(EVDEV_ABS_X),
+				.value = 1,
+			},
+			{
+				.usage = U(EVDEV_ABS_Y),
+				.value = 2,
+			},
+			{
+				.usage = U(EVDEV_SYN_REPORT),
+				.value = 0,
+			},
+		};
+
+		_unref_(evdev_frame) *frame = evdev_frame_new(3);
+		int rc = evdev_frame_set(frame, events, ARRAY_LENGTH(events));
+		litest_assert_neg_errno_success(rc);
+		litest_assert_int_eq(evdev_frame_get_count(frame),
+				     ARRAY_LENGTH(events));
+		litest_assert_int_eq(frame->max_size, ARRAY_LENGTH(events));
+
+		size_t nevents;
+		rc = memcmp(evdev_frame_get_events(frame, &nevents),
+			    events,
+			    sizeof(events));
+		litest_assert_int_eq(rc, 0);
+		litest_assert_int_eq(nevents, ARRAY_LENGTH(events));
+
+		/* Already full, can't append */
+		rc = evdev_frame_append(frame, events, 1);
+		litest_assert_int_eq(rc, -ENOMEM);
+	}
+	{
+		struct evdev_event events[] = {
+			{
+				.usage = U(EVDEV_ABS_X),
+				.value = 1,
+			},
+			{
+				.usage = U(EVDEV_ABS_Y),
+				.value = 2,
+			},
+			{
+				.usage = U(EVDEV_SYN_REPORT),
+				.value = 0,
+			},
+		};
+
+		_unref_(evdev_frame) *frame = evdev_frame_new(3);
+		int rc = evdev_frame_set(frame, events, 1);
+		litest_assert_neg_errno_success(rc);
+		litest_assert_int_eq(evdev_frame_get_count(frame),
+				     2U); /* we appended SYN_REPORT */
+		rc = evdev_frame_append(frame, events + 1, 1);
+		litest_assert_neg_errno_success(rc);
+		litest_assert_int_eq(evdev_frame_get_count(frame),
+				     3U); /* we appended SYN_REPORT */
+		rc = evdev_frame_append(frame, events + 2, 1);
+		litest_assert_neg_errno_success(rc);
+		litest_assert_int_eq(evdev_frame_get_count(frame),
+				     3U); /* SYN_REPORT already there */
+	}
+	{
+		struct evdev_event interrupted[] = {
+			{
+				.usage = U(EVDEV_ABS_X),
+				.value = 1,
+			},
+			{
+				.usage = U(EVDEV_ABS_Y),
+				.value = 2,
+			},
+			{
+				.usage = U(EVDEV_SYN_REPORT),
+				.value = 0,
+			},
+			{
+				.usage = U(EVDEV_ABS_RX),
+				.value = 1,
+			},
+			{
+				.usage = U(EVDEV_ABS_RY),
+				.value = 2,
+			},
+			{
+				.usage = U(EVDEV_SYN_REPORT),
+				.value = 0,
+			},
+		};
+
+		_unref_(evdev_frame) *frame = evdev_frame_new(5);
+		int rc = evdev_frame_set(frame, interrupted, ARRAY_LENGTH(interrupted));
+		litest_assert_neg_errno_success(rc);
+		litest_assert_int_eq(evdev_frame_get_count(frame), 3U);
+
+		rc = evdev_frame_set(frame, &interrupted[2], 1);
+		litest_assert_neg_errno_success(rc);
+		litest_assert_int_eq(evdev_frame_get_count(frame), 1U);
+
+		rc = evdev_frame_set(frame,
+				     &interrupted[1],
+				     ARRAY_LENGTH(interrupted) - 1);
+		litest_assert_neg_errno_success(rc);
+		litest_assert_int_eq(evdev_frame_get_count(frame), 2U);
+
+		/* We never appended a timestamp */
+		litest_assert_int_eq(evdev_frame_get_time(frame), 0U);
+	}
+	{
+		struct evdev_event events[] = {
+			{
+				.usage = U(EVDEV_ABS_X),
+				.value = 1,
+			},
+			{
+				.usage = U(EVDEV_ABS_Y),
+				.value = 2,
+			},
+			{
+				.usage = U(EVDEV_SYN_REPORT),
+				.value = 0,
+			},
+		};
+
+		_unref_(evdev_frame) *frame = evdev_frame_new(3);
+		int rc = evdev_frame_append_one(frame, U(EVDEV_ABS_X), 1);
+		litest_assert_neg_errno_success(rc);
+		rc = evdev_frame_append_one(frame, U(EVDEV_ABS_Y), 2);
+		litest_assert_neg_errno_success(rc);
+		rc = evdev_frame_append_one(frame, U(EV_SYN), 0);
+		litest_assert_neg_errno_success(rc);
 
+		litest_assert_int_eq(evdev_frame_get_count(frame),
+				     ARRAY_LENGTH(events));
+		litest_assert_int_eq(frame->max_size, ARRAY_LENGTH(events));
+
+		size_t nevents;
+		rc = memcmp(evdev_frame_get_events(frame, &nevents),
+			    events,
+			    sizeof(events));
+		litest_assert_int_eq(rc, 0);
+		litest_assert_int_eq(nevents, ARRAY_LENGTH(events));
+
+		/* Already full, can't append */
+		rc = evdev_frame_append_one(frame, U(EVDEV_ABS_Z), 1);
+		litest_assert_int_eq(rc, -ENOMEM);
+
+		/* Appending SYN_REPORT is a noop */
+		rc = evdev_frame_append_one(frame, U(EVDEV_SYN_REPORT), 0);
+		litest_assert_neg_errno_success(rc);
+		litest_assert_int_eq(evdev_frame_get_count(frame),
+				     ARRAY_LENGTH(events));
+		litest_assert_int_eq(frame->max_size, ARRAY_LENGTH(events));
+	}
+}
+END_TEST
+
+START_TEST(infmask_test)
+{
+	/* Test empty mask */
+	infmask_t empty = infmask_new();
+	litest_assert(infmask_is_empty(&empty));
+	litest_assert(!infmask_bit_is_set(&empty, 0));
+	litest_assert(!infmask_bit_is_set(&empty, 100));
+	infmask_reset(&empty);
+
+	/* Test single bit operations */
+	infmask_t single = infmask_new();
+	litest_assert(!infmask_set_bit(&single, 5));
+	litest_assert(infmask_bit_is_set(&single, 5));
+	litest_assert(!infmask_bit_is_set(&single, 4));
+	litest_assert(!infmask_bit_is_set(&single, 6));
+	litest_assert(!infmask_is_empty(&single));
+	litest_assert(infmask_clear_bit(&single, 5));
+	litest_assert(!infmask_bit_is_set(&single, 5));
+	litest_assert(infmask_is_empty(&single));
+	infmask_reset(&single);
+
+	/* Test from_bit constructor */
+	infmask_t from_bit = infmask_from_bit(7);
+	litest_assert(infmask_bit_is_set(&from_bit, 7));
+	litest_assert(!infmask_bit_is_set(&from_bit, 6));
+	litest_assert(!infmask_bit_is_set(&from_bit, 8));
+	infmask_reset(&from_bit);
+
+	/* Test from_bits constructor */
+	infmask_t from_bits = infmask_from_bits(1, 3, 5);
+	litest_assert(infmask_bit_is_set(&from_bits, 1));
+	litest_assert(!infmask_bit_is_set(&from_bits, 2));
+	litest_assert(infmask_bit_is_set(&from_bits, 3));
+	litest_assert(!infmask_bit_is_set(&from_bits, 4));
+	litest_assert(infmask_bit_is_set(&from_bits, 5));
+	infmask_reset(&from_bits);
+
+	/* Test high bit operations */
+	infmask_t high = infmask_new();
+	litest_assert(!infmask_set_bit(&high, 100));
+	litest_assert(infmask_bit_is_set(&high, 100));
+	litest_assert(!infmask_bit_is_set(&high, 99));
+	litest_assert(!infmask_bit_is_set(&high, 101));
+	litest_assert(infmask_clear_bit(&high, 100));
+	litest_assert(!infmask_bit_is_set(&high, 100));
+	infmask_reset(&high);
+
+	/* Test any/all operations */
+	infmask_t mask1 = infmask_from_bits(1, 2, 3);
+	infmask_t mask2 = infmask_from_bits(2, 3, 4);
+	infmask_t mask3 = infmask_from_bits(2, 3);
+
+	litest_assert(infmask_any(&mask1, &mask2));
+	litest_assert(!infmask_all(&mask1, &mask2));
+	litest_assert(infmask_all(&mask1, &mask3));
+	litest_assert(infmask_any(&mask1, &mask3));
+
+	infmask_reset(&mask1);
+	infmask_reset(&mask2);
+	infmask_reset(&mask3);
+
+	/* Test merge operation */
+	infmask_t merge1 = infmask_from_bits(1, 2);
+	infmask_t merge2 = infmask_from_bits(2, 3);
+	litest_assert(!infmask_merge(&merge1, &merge2));
+	litest_assert(infmask_bit_is_set(&merge1, 1));
+	litest_assert(infmask_bit_is_set(&merge1, 2));
+	litest_assert(infmask_bit_is_set(&merge1, 3));
+	infmask_reset(&merge1);
+	infmask_reset(&merge2);
+
+	/* Test clear operation */
+	infmask_t clear1 = infmask_from_bits(1, 2, 3);
+	infmask_t clear2 = infmask_from_bits(2, 3);
+	litest_assert(infmask_clear(&clear1, &clear2));
+	litest_assert(infmask_bit_is_set(&clear1, 1));
+	litest_assert(!infmask_bit_is_set(&clear1, 2));
+	litest_assert(!infmask_bit_is_set(&clear1, 3));
+	infmask_reset(&clear1);
+	infmask_reset(&clear2);
+
+	/* Test growing behavior */
+	infmask_t grow = infmask_new();
+	litest_assert(!infmask_set_bit(&grow, 5));
+	litest_assert(grow.nmasks == 1);
+	litest_assert(!infmask_set_bit(&grow, 35));
+	litest_assert(grow.nmasks == 2);
+	litest_assert(!infmask_set_bit(&grow, 65));
+	litest_assert(grow.nmasks == 3);
+	litest_assert(infmask_bit_is_set(&grow, 5));
+	litest_assert(infmask_bit_is_set(&grow, 35));
+	litest_assert(infmask_bit_is_set(&grow, 65));
+	infmask_reset(&grow);
+}
+END_TEST
+
+START_TEST(evdev_mask_test)
+{
+	_destroy_(evdev_mask) *mask = evdev_mask_new();
+
+	evdev_mask_reset(mask);
+
+	litest_assert(bitmask_is_empty(mask->ev));
+	litest_assert(bitmask_is_empty(mask->rel));
+	litest_assert(bitmask_is_empty(mask->sw));
+	litest_assert(infmask_is_empty(&mask->key));
+	litest_assert(infmask_is_empty(&mask->btn));
+	litest_assert(infmask_is_empty(&mask->abs));
+
+	evdev_mask_set_enum(mask, EVDEV_BTN_TOOL_PEN);
+	evdev_mask_set_enum(mask, EVDEV_BTN_TOOL_AIRBRUSH);
+
+	litest_assert(bitmask_bit_is_set(mask->ev, EV_KEY));
+
+	/* Verify these are in btn, not key */
+	litest_assert(!infmask_is_empty(&mask->btn));
+	litest_assert(infmask_is_empty(&mask->key));
+
+	litest_assert(evdev_mask_is_set(mask, evdev_usage_from(EVDEV_BTN_TOOL_PEN)));
+	litest_assert(
+		!evdev_mask_is_set(mask, evdev_usage_from(EVDEV_BTN_TOOL_RUBBER)));
+	litest_assert(
+		evdev_mask_is_set(mask, evdev_usage_from(EVDEV_BTN_TOOL_AIRBRUSH)));
+
+	/* Test regular key (should go into key field) */
+	evdev_mask_set_enum(mask, EVDEV_KEY_ESC);
+	litest_assert(!infmask_is_empty(&mask->key));
+	litest_assert(evdev_mask_is_set(mask, evdev_usage_from(EVDEV_KEY_ESC)));
+
+	evdev_mask_set_enum(mask, EVDEV_REL_X);
+	litest_assert(bitmask_bit_is_set(mask->ev, EV_REL));
+	litest_assert(bitmask_bit_is_set(mask->rel, REL_X));
+	litest_assert(evdev_mask_is_set(mask, evdev_usage_from(EVDEV_REL_X)));
+
+	evdev_mask_set_enum(mask, EVDEV_ABS_X);
+	litest_assert(bitmask_bit_is_set(mask->ev, EV_ABS));
+	litest_assert(!infmask_is_empty(&mask->abs));
+	litest_assert(evdev_mask_is_set(mask, evdev_usage_from(EVDEV_ABS_X)));
 }
 END_TEST
 
-int main(void)
+int
+main(void)
 {
 	struct litest_runner *runner = litest_runner_new();
 
@@ -1969,9 +3214,16 @@ int main(void)
 	litest_runner_add_test(runner, &tdesc); \
 } while(0)
 
+	ADD_TEST(auto_test);
+	ADD_TEST(mkdir_p_test);
+	ADD_TEST(rmdir_r_test);
+	ADD_TEST(tmpdir_test);
+	ADD_TEST(find_files_test);
+
 	ADD_TEST(array_for_each);
 
 	ADD_TEST(bitfield_helpers);
+	ADD_TEST(bitmask_test);
 	ADD_TEST(matrix_helpers);
 	ADD_TEST(ratelimit_helpers);
 	ADD_TEST(dpi_parser);
@@ -1991,9 +3243,13 @@ int main(void)
 	ADD_TEST(safe_atou_test);
 	ADD_TEST(safe_atou_base_16_test);
 	ADD_TEST(safe_atou_base_8_test);
+	ADD_TEST(safe_atou64_test);
 	ADD_TEST(safe_atod_test);
 	ADD_TEST(strsplit_test);
 	ADD_TEST(strv_for_each_test);
+	ADD_TEST(strv_append_test);
+	ADD_TEST(strv_find_test);
+	ADD_TEST(strv_find_substring_test);
 	ADD_TEST(double_array_from_string_test);
 	ADD_TEST(strargv_test);
 	ADD_TEST(kvsplit_double_test);
@@ -2008,6 +3264,8 @@ int main(void)
 	ADD_TEST(list_test_insert);
 	ADD_TEST(list_test_append);
 	ADD_TEST(list_test_foreach);
+	ADD_TEST(list_test_first_last);
+	ADD_TEST(list_test_chain);
 	ADD_TEST(strverscmp_test);
 	ADD_TEST(streq_test);
 	ADD_TEST(strneq_test);
@@ -2020,6 +3278,16 @@ int main(void)
 	ADD_TEST(stringbuf_test);
 	ADD_TEST(multivalue_test);
 
+	ADD_TEST(newtype_test);
+	ADD_TEST(attribute_cleanup);
+	ADD_TEST(macros_expand);
+
+	ADD_TEST(evdev_frames);
+
+	ADD_TEST(infmask_test);
+
+	ADD_TEST(evdev_mask_test);
+
 	enum litest_runner_result result = litest_runner_run_tests(runner);
 	litest_runner_destroy(runner);
 
diff -pruN 1.28.1-1/test/test_quirks_files.py 1.30.0-1/test/test_quirks_files.py
--- 1.28.1-1/test/test_quirks_files.py	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/test/test_quirks_files.py	2025-11-25 03:40:43.000000000 +0000
@@ -57,15 +57,15 @@ def test_matches_are_valid(quirksfile):
 
         vid = section.get("MatchVendor")
         if vid is not None:
-            assert re.match(
-                "0x[0-9A-F]{4}", vid
-            ), f"{quirksfile}: {name}: {vid} must be uppercase hex (0xAB12)"
+            assert re.match("0x[0-9A-F]{4}", vid), (
+                f"{quirksfile}: {name}: {vid} must be uppercase hex (0xAB12)"
+            )
 
         pid = section.get("MatchProduct")
         if pid is not None:
-            assert re.match(
-                "0x[0-9A-F]{4}", pid
-            ), f"{quirksfile}: {name}: {pid} must be uppercase hex (0xAB12)"
+            assert re.match("0x[0-9A-F]{4}", pid), (
+                f"{quirksfile}: {name}: {pid} must be uppercase hex (0xAB12)"
+            )
 
 
 def test_match_product_is_not_a_logitech_receiver(quirksfile):
@@ -78,9 +78,9 @@ def test_match_product_is_not_a_logitech
         vid = int(section.get("MatchVendor", "0x0"), 16)
         if vid == 0x046D:
             pid = int(section.get("MatchProduct", "0x0"), 16)
-            assert (
-                pid not in logitech_receivers
-            ), f"{quirksfile}: {name}: applies to a Logitech Receiver"
+            assert pid not in logitech_receivers, (
+                f"{quirksfile}: {name}: applies to a Logitech Receiver"
+            )
 
 
 def main():
diff -pruN 1.28.1-1/tools/libinput-analyze-per-slot-delta.py 1.30.0-1/tools/libinput-analyze-per-slot-delta.py
--- 1.28.1-1/tools/libinput-analyze-per-slot-delta.py	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-analyze-per-slot-delta.py	2025-11-25 03:40:43.000000000 +0000
@@ -41,6 +41,8 @@ import libevdev
 
 COLOR_RESET = "\x1b[0m"
 COLOR_RED = "\x1b[6;31m"
+COLOR_BLUE = "\x1b[6;34m"
+COLOR_GREEN = "\x1b[6;32m"
 
 
 class SlotFormatter:
@@ -51,12 +53,14 @@ class SlotFormatter:
         threshold=None,
         ignore_below=None,
         show_distance=False,
+        pressure_thresholds=(0, 0),
     ):
         self.threshold = threshold
         self.ignore_below = ignore_below
         self.resolution = resolution
         self.is_absolute = is_absolute
         self.show_distance = show_distance
+        self.pressure_thresholds = pressure_thresholds
         self.slots = []
         self.have_data = False
         self.filtered = False
@@ -118,9 +122,22 @@ class SlotFormatter:
                 else:
                     direction = "↓↓"
 
-            color = ""
-            reset = ""
+            color = COLOR_RESET
+            reset = COLOR_RESET
             if not self.is_absolute:
+                if (
+                    self.pressure_thresholds[1] > 0
+                    and slot.pressure > self.pressure_thresholds[1]
+                ):
+                    color = COLOR_GREEN
+                    reset = COLOR_RESET
+                elif (
+                    self.pressure_thresholds[0] > 0
+                    and slot.pressure > self.pressure_thresholds[0]
+                ):
+                    color = COLOR_BLUE
+                    reset = COLOR_RESET
+
                 if self.ignore_below is not None or self.threshold is not None:
                     dist = math.hypot(delta.x, delta.y)
                     if self.ignore_below is not None and dist < self.ignore_below:
@@ -180,6 +197,7 @@ class Slot:
     position: Point = field(default_factory=Point)
     delta: Point = field(default_factory=Point)
     origin: Point = field(default_factory=Point)
+    pressure: int = 0
     used: bool = False
     dirty: bool = False
 
@@ -187,6 +205,8 @@ class Slot:
 def main(argv):
     global COLOR_RESET
     global COLOR_RED
+    global COLOR_BLUE
+    global COLOR_GREEN
 
     slots = []
     xres, yres = 1, 1
@@ -227,11 +247,25 @@ def main(argv):
         default=None,
         help="Ignore any delta below this threshold",
     )
+    parser.add_argument(
+        "--pressure-min",
+        type=int,
+        default=0,
+        help="Highlight touches above this pressure minimum",
+    )
+    parser.add_argument(
+        "--pressure-max",
+        type=int,
+        default=0,
+        help="Highlight touches below this pressure maximum",
+    )
     args = parser.parse_args()
 
     if not sys.stdout.isatty():
         COLOR_RESET = ""
         COLOR_RED = ""
+        COLOR_GREEN = ""
+        COLOR_BLUE = ""
 
     yml = yaml.safe_load(open(args.path[0]))
     device = yml["devices"][0]
@@ -314,6 +348,8 @@ def main(argv):
                         s.state = SlotState.BEGIN
                     else:
                         s.state = SlotState.END
+                elif e.code == libevdev.EV_ABS.ABS_PRESSURE:
+                    s.pressure = e.value
             else:
                 if e.code == libevdev.EV_ABS.ABS_MT_SLOT:
                     slot = e.value
@@ -330,6 +366,8 @@ def main(argv):
                         s.state = SlotState.BEGIN
                         s.delta.x, s.delta.y = 0, 0
                     s.dirty = True
+                elif e.code == libevdev.EV_ABS.ABS_MT_PRESSURE:
+                    s.pressure = e.value
 
             if args.use_st:
                 axes = [libevdev.EV_ABS.ABS_X, libevdev.EV_ABS.ABS_Y]
@@ -405,6 +443,7 @@ def main(argv):
                     threshold=args.threshold,
                     ignore_below=args.ignore_below,
                     show_distance=args.show_distance,
+                    pressure_thresholds=(args.pressure_min, args.pressure_max),
                 )
                 for sl in [s for s in slots if s.used]:
                     fmt.format_slot(sl)
diff -pruN 1.28.1-1/tools/libinput-analyze-recording.py 1.30.0-1/tools/libinput-analyze-recording.py
--- 1.28.1-1/tools/libinput-analyze-recording.py	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-analyze-recording.py	2025-11-25 03:40:43.000000000 +0000
@@ -192,7 +192,7 @@ def main(argv):
                     print("")
                     continuation_count = 0
 
-                fields.insert(0, f"{e.sec: 3d}.{e.usec//1000:03d}")
+                fields.insert(0, f"{e.sec: 3d}.{e.usec // 1000:03d}")
                 keys_down = [k.name for k, v in keystate.items() if v]
                 fields.append(", ".join(keys_down))
                 print(" | ".join(fields))
diff -pruN 1.28.1-1/tools/libinput-analyze.c 1.30.0-1/tools/libinput-analyze.c
--- 1.28.1-1/tools/libinput-analyze.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-analyze.c	2025-11-25 03:40:43.000000000 +0000
@@ -41,16 +41,14 @@ main(int argc, char **argv)
 
 	while (1) {
 		int c;
-		static struct option opts[] = {
-			{ "help",	no_argument,	0, 'h' },
-			{ 0, 0, 0, 0}
-		};
+		static struct option opts[] = { { "help", no_argument, 0, 'h' },
+						{ 0, 0, 0, 0 } };
 
 		c = getopt_long(argc, argv, "+h", opts, &option_index);
 		if (c == -1)
 			break;
 
-		switch(c) {
+		switch (c) {
 		case 'h':
 			usage();
 			return EXIT_SUCCESS;
diff -pruN 1.28.1-1/tools/libinput-debug-events.c 1.30.0-1/tools/libinput-debug-events.c
--- 1.28.1-1/tools/libinput-debug-events.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-debug-events.c	2025-11-25 03:40:43.000000000 +0000
@@ -24,24 +24,24 @@
 #include "config.h"
 
 #include <errno.h>
-#include <inttypes.h>
 #include <getopt.h>
+#include <inttypes.h>
+#include <libevdev/libevdev.h>
+#include <libinput.h>
 #include <poll.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <signal.h>
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
-#include "linux/input.h"
 
-#include <libinput.h>
-#include <libevdev/libevdev.h>
+#include "util-libinput.h"
+#include "util-macros.h"
+#include "util-strings.h"
 
 #include "libinput-version.h"
-#include "util-strings.h"
-#include "util-macros.h"
-#include "util-libinput.h"
+#include "linux/input.h"
 #include "shared.h"
 
 static struct tools_options options;
@@ -85,7 +85,8 @@ handle_and_print_events(struct libinput
 		case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
 		case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
 		case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
-			is_repeat = last_event_type == type && last_device == device && log_serial == last_log_serial;
+			is_repeat = last_event_type == type && last_device == device &&
+				    log_serial == last_log_serial;
 			break;
 		default:
 			break;
@@ -100,7 +101,8 @@ handle_and_print_events(struct libinput
 		}
 
 		if (type != LIBINPUT_EVENT_TOUCH_FRAME || !compress_motion_events) {
-			char *event_str = libinput_event_to_str(ev, event_repeat_count + 1, opts);
+			_autofree_ char *event_str =
+				libinput_event_to_str(ev, event_repeat_count + 1, opts);
 
 			switch (type) {
 			case LIBINPUT_EVENT_DEVICE_ADDED:
@@ -120,7 +122,6 @@ handle_and_print_events(struct libinput
 			}
 
 			printq("%s\n", event_str);
-			free(event_str);
 		}
 
 		last_device = device;
@@ -161,8 +162,9 @@ mainloop(struct libinput *li)
 
 	/* Handle already-pending device added events */
 	if (handle_and_print_events(li, &opts))
-		fprintf(stderr, "Expected device added events on startup but got none. "
-				"Maybe you don't have the right permissions?\n");
+		fprintf(stderr,
+			"Expected device added events on startup but got none. "
+			"Maybe you don't have the right permissions?\n");
 
 	/* time offset starts with our first received event */
 	if (poll(&fds, 1, -1) > -1) {
@@ -179,7 +181,8 @@ mainloop(struct libinput *li)
 }
 
 static void
-usage(struct option *opts) {
+usage(struct option *opts)
+{
 	printf("Usage: libinput debug-events [options] [--udev <seat>|--device /dev/input/event0 ...]\n");
 
 	if (opts)
@@ -191,7 +194,7 @@ main(int argc, char **argv)
 {
 	struct libinput *li;
 	enum tools_backend backend = BACKEND_NONE;
-	const char *seat_or_devices[60] = {NULL};
+	const char *seat_or_devices[60] = { NULL };
 	size_t ndevices = 0;
 	bool grab = false;
 	bool verbose = false;
@@ -213,6 +216,7 @@ main(int argc, char **argv)
 			OPT_QUIET,
 			OPT_COMPRESS_MOTION_EVENTS,
 		};
+		/* clang-format off */
 		static struct option opts[] = {
 			CONFIGURATION_OPTIONS,
 			{ "help",                      no_argument,       0, 'h' },
@@ -223,14 +227,15 @@ main(int argc, char **argv)
 			{ "verbose",                   no_argument,       0, OPT_VERBOSE },
 			{ "quiet",                     no_argument,       0, OPT_QUIET },
 			{ "compress-motion-events",    no_argument,       0, OPT_COMPRESS_MOTION_EVENTS },
-			{ 0, 0, 0, 0}
+			{ 0, 0, 0, 0},
 		};
+		/* clang-format on */
 
 		c = getopt_long(argc, argv, "h", opts, &option_index);
 		if (c == -1)
 			break;
 
-		switch(c) {
+		switch (c) {
 		case '?':
 			exit(EXIT_INVALID_USAGE);
 			break;
@@ -249,7 +254,6 @@ main(int argc, char **argv)
 			    ndevices >= ARRAY_LENGTH(seat_or_devices)) {
 				usage(NULL);
 				return EXIT_INVALID_USAGE;
-
 			}
 			backend = BACKEND_DEVICE;
 			seat_or_devices[ndevices++] = optarg;
@@ -259,7 +263,6 @@ main(int argc, char **argv)
 			    ndevices >= ARRAY_LENGTH(seat_or_devices)) {
 				usage(NULL);
 				return EXIT_INVALID_USAGE;
-
 			}
 			backend = BACKEND_UDEV;
 			seat_or_devices[0] = optarg;
@@ -282,7 +285,6 @@ main(int argc, char **argv)
 			}
 			break;
 		}
-
 	}
 
 	if (optind < argc) {
@@ -297,7 +299,7 @@ main(int argc, char **argv)
 				return EXIT_INVALID_USAGE;
 			}
 			seat_or_devices[ndevices++] = argv[optind];
-		} while(++optind < argc);
+		} while (++optind < argc);
 	} else if (backend == BACKEND_NONE) {
 		backend = BACKEND_UDEV;
 		seat_or_devices[0] = "seat0";
@@ -308,15 +310,22 @@ main(int argc, char **argv)
 	act.sa_flags = SA_SIGINFO;
 
 	if (sigaction(SIGINT, &act, NULL) == -1) {
-		fprintf(stderr, "Failed to set up signal handling (%s)\n",
-				strerror(errno));
+		fprintf(stderr,
+			"Failed to set up signal handling (%s)\n",
+			strerror(errno));
 		return EXIT_FAILURE;
 	}
 
 	if (verbose)
 		printf("libinput version: %s\n", LIBINPUT_VERSION);
 
-	li = tools_open_backend(backend, seat_or_devices, verbose, &grab);
+	bool with_plugins = (options.plugins == 1);
+	li = tools_open_backend(backend,
+				seat_or_devices,
+				verbose,
+				&grab,
+				with_plugins,
+				steal(&options.plugin_paths));
 	if (!li)
 		return EXIT_FAILURE;
 
diff -pruN 1.28.1-1/tools/libinput-debug-events.man 1.30.0-1/tools/libinput-debug-events.man
--- 1.28.1-1/tools/libinput-debug-events.man	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-debug-events.man	2025-11-25 03:40:43.000000000 +0000
@@ -117,6 +117,14 @@ Sets the type of the custom acceleration
 Defaults to fallback.
 This only applies to the custom profile.
 .TP 8
+.B \-\-set\-eraser\-button\-button=[BTN_STYLUS|BTN_STYLUS2|BTN_STYLUS3]
+Sets the eraser button button to the given tablet tool button. Only
+takes effect if combined with
+.B \-\-set\-eraser\-button\-mode=on\-button\-down.
+.TP 8
+.B \-\-set\-eraser\-button\-mode=[default|on-button-down]
+Sets the eraser button mode to the given mode.
+.TP 8
 .B \-\-set\-pressure\-range=<min>:<max>
 Set the tablet tool pressure range to min:max. min and max must be in range [0.0, 1.0].
 .TP 8
diff -pruN 1.28.1-1/tools/libinput-debug-gui.c 1.30.0-1/tools/libinput-debug-gui.c
--- 1.28.1-1/tools/libinput-debug-gui.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-debug-gui.c	2025-11-25 03:40:43.000000000 +0000
@@ -22,50 +22,49 @@
  */
 #include <config.h>
 
-#include <linux/input.h>
-
 #include <assert.h>
 #include <cairo.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
+#include <glib-unix.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <libevdev/libevdev.h>
+#include <libinput.h>
+#include <linux/input.h>
 #include <math.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdarg.h>
 #include <string.h>
 #include <unistd.h>
 
-#include <gtk/gtk.h>
-#include <glib.h>
-#include <glib-unix.h>
-#include <libevdev/libevdev.h>
-
-#include <libinput.h>
-#include "util-strings.h"
-#include "util-macros.h"
 #include "util-list.h"
+#include "util-macros.h"
+#include "util-strings.h"
 
 #include "shared.h"
 
-#if HAVE_GTK_WAYLAND
-	#include <wayland-client.h>
-	#include "pointer-constraints-unstable-v1-client-protocol.h"
-	#if HAVE_GTK4
-		#include <gdk/wayland/gdkwayland.h>
-	#else
-		#include <gdk/gdkwayland.h>
-	#endif
+#ifdef HAVE_GTK_WAYLAND
+#include <wayland-client.h>
+
+#include "pointer-constraints-unstable-v1-client-protocol.h"
+#ifdef HAVE_GTK4
+#include <gdk/wayland/gdkwayland.h>
+#else
+#include <gdk/gdkwayland.h>
+#endif
 #endif
 
-#if HAVE_GTK_X11
-	#include <X11/X.h>
-	#include <X11/Xlib.h>
-	#if HAVE_GTK4
-		#include <gdk/x11/gdkx.h>
-	#else
-		#include <gdk/gdkx.h>
-	#endif
+#ifdef HAVE_GTK_X11
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#ifdef HAVE_GTK4
+#include <gdk/x11/gdkx.h>
+#else
+#include <gdk/gdkx.h>
+#endif
 #endif
 
 #define clip(val_, min_, max_) min((max_), max((min_), (val_)))
@@ -120,7 +119,7 @@ struct window {
 	struct {
 		bool locked;
 
-#if HAVE_GTK_WAYLAND
+#ifdef HAVE_GTK_WAYLAND
 		struct zwp_pointer_constraints_v1 *wayland_pointer_constraints;
 		struct zwp_locked_pointer_v1 *wayland_locked_pointer;
 #endif
@@ -198,7 +197,7 @@ struct window {
 
 	struct {
 		int rel_x, rel_y; /* REL_X/Y */
-		int x, y;	  /* ABS_X/Y */
+		int x, y;         /* ABS_X/Y */
 		struct {
 			int x, y; /* ABS_MT_POSITION_X/Y */
 			bool active;
@@ -211,7 +210,7 @@ struct window {
 	struct libinput_device *devices[50];
 };
 
-#if HAVE_GTK_WAYLAND
+#ifdef HAVE_GTK_WAYLAND
 static void
 wayland_registry_global(void *data,
 			struct wl_registry *registry,
@@ -227,7 +226,7 @@ wayland_registry_global(void *data,
 					 name,
 					 &zwp_pointer_constraints_v1_interface,
 					 1);
-        }
+	}
 }
 
 static void
@@ -235,7 +234,6 @@ wayland_registry_global_remove(void *dat
 			       struct wl_registry *wl_registry,
 			       uint32_t name)
 {
-
 }
 
 static struct wl_registry_listener registry_listener = {
@@ -270,7 +268,7 @@ wayland_lock_pointer(struct window *w)
 	if (!w->lock_pointer.wayland_pointer_constraints)
 		return false;
 
-#if HAVE_GTK4
+#ifdef HAVE_GTK4
 	GtkNative *window = gtk_widget_get_native(w->win);
 	GdkSurface *gdk_surface = gtk_native_get_surface(window);
 	surface = gdk_wayland_surface_get_wl_surface(gdk_surface);
@@ -280,11 +278,12 @@ wayland_lock_pointer(struct window *w)
 #endif
 
 	w->lock_pointer.wayland_locked_pointer =
-		zwp_pointer_constraints_v1_lock_pointer(w->lock_pointer.wayland_pointer_constraints,
-							surface,
-							wayland_pointer,
-							NULL,
-							ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
+		zwp_pointer_constraints_v1_lock_pointer(
+			w->lock_pointer.wayland_pointer_constraints,
+			surface,
+			wayland_pointer,
+			NULL,
+			ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
 
 	return true;
 }
@@ -303,7 +302,7 @@ backend_is_wayland(void)
 }
 #endif /* HAVE_GTK_WAYLAND */
 
-#if HAVE_GTK_X11
+#ifdef HAVE_GTK_X11
 static bool
 x_lock_pointer(struct window *w)
 {
@@ -311,20 +310,33 @@ x_lock_pointer(struct window *w)
 	Window x_win;
 	int result;
 
+	/* gdk_display_get_default() is deprecated but probably won't be removed
+	 * before GTK X11 is removed completely so this whole section will
+	 * be gone anyway. Meanwhile, disable the warning
+	 */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 	x_display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+#pragma GCC diagnostic pop
 
-#if HAVE_GTK4
+#ifdef HAVE_GTK4
 	GtkNative *window = gtk_widget_get_native(w->win);
 	GdkSurface *surface = gtk_native_get_surface(window);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 	x_win = GDK_SURFACE_XID(surface);
+#pragma GCC diagnostic pop
 #else
 	GdkWindow *window = gtk_widget_get_window(w->win);
 	x_win = GDK_WINDOW_XID(window);
 #endif
 
-	result = XGrabPointer(x_display, x_win,
-			      False, NoEventMask,
-			      GrabModeAsync, GrabModeAsync,
+	result = XGrabPointer(x_display,
+			      x_win,
+			      False,
+			      NoEventMask,
+			      GrabModeAsync,
+			      GrabModeAsync,
 			      x_win,
 			      None,
 			      CurrentTime);
@@ -336,7 +348,10 @@ x_unlock_pointer(struct window *w)
 {
 	Display *x_display;
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 	x_display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+#pragma GCC diagnostic pop
 
 	XUngrabPointer(x_display, CurrentTime);
 }
@@ -354,12 +369,12 @@ window_lock_pointer(struct window *w)
 	if (w->lock_pointer.locked)
 		return true;
 
-#if HAVE_GTK_WAYLAND
+#ifdef HAVE_GTK_WAYLAND
 	if (backend_is_wayland())
 		w->lock_pointer.locked = wayland_lock_pointer(w);
 #endif
 
-#if HAVE_GTK_X11
+#ifdef HAVE_GTK_X11
 	if (backend_is_x11())
 		w->lock_pointer.locked = x_lock_pointer(w);
 #endif
@@ -375,12 +390,12 @@ window_unlock_pointer(struct window *w)
 
 	w->lock_pointer.locked = false;
 
-#if HAVE_GTK_WAYLAND
+#ifdef HAVE_GTK_WAYLAND
 	if (backend_is_wayland())
 		wayland_unlock_pointer(w);
 #endif
 
-#if HAVE_GTK_X11
+#ifdef HAVE_GTK_X11
 	if (backend_is_x11())
 		x_unlock_pointer(w);
 #endif
@@ -405,8 +420,8 @@ draw_evdev_rel(struct window *w, cairo_t
 
 	cairo_save(cr);
 	cairo_set_source_rgb(cr, .2, .2, .8);
-	center_x = w->width/2 - 400;
-	center_y = w->height/2;
+	center_x = w->width / 2 - 400;
+	center_y = w->height / 2;
 
 	cairo_arc(cr, center_x, center_y, 10, 0, 2 * M_PI);
 	cairo_stroke(cr);
@@ -414,28 +429,24 @@ draw_evdev_rel(struct window *w, cairo_t
 	if (w->evdev.rel_x) {
 		int dir = w->evdev.rel_x > 0 ? 1 : -1;
 		for (int i = 0; i < abs(w->evdev.rel_x); i++) {
-			cairo_move_to(cr,
-				      center_x + (i + 1) * 20 * dir,
-				      center_y - 20);
+			cairo_move_to(cr, center_x + (i + 1) * 20 * dir, center_y - 20);
 			cairo_rel_line_to(cr, 0, 40);
 			cairo_rel_line_to(cr, 20 * dir, -20);
 			cairo_rel_line_to(cr, -20 * dir, -20);
 			cairo_fill(cr);
 		}
-        }
+	}
 
 	if (w->evdev.rel_y) {
 		int dir = w->evdev.rel_y > 0 ? 1 : -1;
 		for (int i = 0; i < abs(w->evdev.rel_y); i++) {
-			cairo_move_to(cr,
-				      center_x - 20,
-				      center_y + (i + 1) * 20 * dir);
+			cairo_move_to(cr, center_x - 20, center_y + (i + 1) * 20 * dir);
 			cairo_rel_line_to(cr, 40, 0);
 			cairo_rel_line_to(cr, -20, 20 * dir);
 			cairo_rel_line_to(cr, -20, -20 * dir);
 			cairo_fill(cr);
 		}
-        }
+	}
 
 	cairo_restore(cr);
 }
@@ -445,16 +456,15 @@ draw_evdev_abs(struct window *w, cairo_t
 {
 	static const struct input_absinfo *ax = NULL, *ay = NULL;
 	const int normalized_width = 200;
-	int outline_width = normalized_width,
-	    outline_height = normalized_width * 0.75;
+	int outline_width = normalized_width, outline_height = normalized_width * 0.75;
 	int center_x, center_y;
 	int width, height;
 	int x, y;
 
 	cairo_save(cr);
 
-	center_x = w->width/2 + 400;
-	center_y = w->height/2;
+	center_x = w->width / 2 + 400;
+	center_y = w->height / 2;
 
 	/* Always the outline even if we didn't get any abs events yet so it
 	 * doesn't just appear out of nowhere */
@@ -477,21 +487,20 @@ draw_evdev_abs(struct window *w, cairo_t
 			ay = libevdev_get_abs_info(d->evdev, ABS_Y);
 			w->evdev.last_device = w->evdev.device;
 		}
-
 	}
 	if (ax == NULL || ay == NULL)
 		goto draw_outline;
 
 	width = ax->maximum - ax->minimum;
 	height = ay->maximum - ay->minimum;
-	outline_height = 1.0 * height/width * normalized_width;
+	outline_height = 1.0 * height / width * normalized_width;
 	outline_width = normalized_width;
 
 	cairo_set_source_rgb(cr, .2, .2, .8);
-	x = 1.0 * (w->evdev.x - ax->minimum)/width * outline_width;
-	y = 1.0 * (w->evdev.y - ay->minimum)/height * outline_height;
-	x += center_x - outline_width/2;
-	y += center_y - outline_height/2;
+	x = 1.0 * (w->evdev.x - ax->minimum) / width * outline_width;
+	y = 1.0 * (w->evdev.y - ay->minimum) / height * outline_height;
+	x += center_x - outline_width / 2;
+	y += center_y - outline_height / 2;
 	cairo_arc(cr, x, y, 10, 0, 2 * M_PI);
 	cairo_fill(cr);
 
@@ -505,10 +514,10 @@ draw_evdev_abs(struct window *w, cairo_t
 				     .8 - .2 * (i % 5));
 		x = w->evdev.slots[i].x;
 		y = w->evdev.slots[i].y;
-		x = 1.0 * (x - ax->minimum)/width * outline_width;
-		y = 1.0 * (y - ay->minimum)/height * outline_height;
-		x += center_x - outline_width/2;
-		y += center_y - outline_height/2;
+		x = 1.0 * (x - ax->minimum) / width * outline_width;
+		y = 1.0 * (y - ay->minimum) / height * outline_height;
+		x += center_x - outline_width / 2;
+		y += center_y - outline_height / 2;
 		cairo_arc(cr, x, y, 10, 0, 2 * M_PI);
 		cairo_fill(cr);
 
@@ -518,8 +527,9 @@ draw_evdev_abs(struct window *w, cairo_t
 		cairo_set_source_rgb(cr, 1.f, 1.f, 1.f);
 		cairo_set_font_size(cr, 12.0);
 		cairo_text_extents(cr, finger_text, &finger_text_extents);
-		cairo_move_to(cr, x - finger_text_extents.width/2,
-				  y + finger_text_extents.height/2);
+		cairo_move_to(cr,
+			      x - finger_text_extents.width / 2,
+			      y + finger_text_extents.height / 2);
 		cairo_show_text(cr, finger_text);
 	}
 
@@ -527,8 +537,8 @@ draw_outline:
 	/* The touchpad outline */
 	cairo_set_source_rgb(cr, .2, .2, .8);
 	cairo_rectangle(cr,
-			center_x - outline_width/2,
-			center_y - outline_height/2,
+			center_x - outline_width / 2,
+			center_y - outline_height / 2,
 			outline_width,
 			outline_height);
 	cairo_stroke(cr);
@@ -560,7 +570,7 @@ draw_gestures(struct window *w, cairo_t
 	cairo_save(cr);
 	offset = w->pinch.scale * 100;
 	cairo_translate(cr, w->pinch.x, w->pinch.y);
-	cairo_rotate(cr, w->pinch.angle * M_PI/180.0);
+	cairo_rotate(cr, w->pinch.angle * M_PI / 180.0);
 	if (w->pinch.nfingers > 0) {
 		cairo_set_source_rgb(cr, .4, .4, .8);
 		cairo_arc(cr, offset, -offset, 20, 0, 2 * M_PI);
@@ -578,7 +588,7 @@ draw_gestures(struct window *w, cairo_t
 
 	/* hold */
 	cairo_save(cr);
-	cairo_translate(cr, w->width/2, w->height/2 + 100);
+	cairo_translate(cr, w->width / 2, w->height / 2 + 100);
 
 	for (int i = 4; i > 0; i--) { /* 4 fg max */
 		double r, g, b, hold_alpha;
@@ -613,8 +623,16 @@ draw_scrollbars(struct window *w, cairo_
 
 	/* discrete scrollbars */
 	cairo_set_source_rgb(cr, .8, .4, 0);
-	cairo_rectangle(cr, w->scroll.vx_discrete - 5, w->scroll.vy_discrete - 10, 10, 20);
-	cairo_rectangle(cr, w->scroll.hx_discrete - 10, w->scroll.hy_discrete - 5, 20, 10);
+	cairo_rectangle(cr,
+			w->scroll.vx_discrete - 5,
+			w->scroll.vy_discrete - 10,
+			10,
+			20);
+	cairo_rectangle(cr,
+			w->scroll.hx_discrete - 10,
+			w->scroll.hy_discrete - 5,
+			20,
+			10);
 	cairo_fill(cr);
 
 	cairo_restore(cr);
@@ -659,12 +677,12 @@ draw_text(cairo_t *cr, const char *text,
 	cairo_font_extents(cr, &fe);
 	/* center of the rectangle */
 	cairo_move_to(cr, x, y);
-	cairo_rel_move_to(cr, -te.width/2, -fe.descent + te.height/2);
+	cairo_rel_move_to(cr, -te.width / 2, -fe.descent + te.height / 2);
 	cairo_show_text(cr, text);
 }
 
 static inline void
-draw_other_button (struct window *w, cairo_t *cr)
+draw_other_button(struct window *w, cairo_t *cr)
 {
 	const char *name = w->buttons.other_name;
 
@@ -677,16 +695,16 @@ draw_other_button (struct window *w, cai
 		name = "undefined";
 
 	cairo_set_source_rgb(cr, .2, .8, .8);
-	cairo_rectangle(cr, w->width/2 - 40, w->height - 150, 80, 30);
+	cairo_rectangle(cr, w->width / 2 - 40, w->height - 150, 80, 30);
 	cairo_fill(cr);
 
 	cairo_set_source_rgb(cr, 0, 0, 0);
 
-	draw_text(cr, name, w->width/2, w->height - 150 + 15);
+	draw_text(cr, name, w->width / 2, w->height - 150 + 15);
 
 outline:
 	cairo_set_source_rgb(cr, 0, 0, 0);
-	cairo_rectangle(cr, w->width/2 - 40, w->height - 150, 80, 30);
+	cairo_rectangle(cr, w->width / 2 - 40, w->height - 150, 80, 30);
 	cairo_stroke(cr);
 	cairo_restore(cr);
 }
@@ -699,18 +717,22 @@ draw_buttons(struct window *w, cairo_t *
 	if (w->buttons.l || w->buttons.m || w->buttons.r) {
 		cairo_set_source_rgb(cr, .2, .8, .8);
 		if (w->buttons.l)
-			cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30);
+			cairo_rectangle(cr,
+					w->width / 2 - 100,
+					w->height - 200,
+					70,
+					30);
 		if (w->buttons.m)
-			cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30);
+			cairo_rectangle(cr, w->width / 2 - 20, w->height - 200, 40, 30);
 		if (w->buttons.r)
-			cairo_rectangle(cr, w->width/2 + 30, w->height - 200, 70, 30);
+			cairo_rectangle(cr, w->width / 2 + 30, w->height - 200, 70, 30);
 		cairo_fill(cr);
 	}
 
 	cairo_set_source_rgb(cr, 0, 0, 0);
-	cairo_rectangle(cr, w->width/2 - 100, w->height - 200, 70, 30);
-	cairo_rectangle(cr, w->width/2 - 20, w->height - 200, 40, 30);
-	cairo_rectangle(cr, w->width/2 + 30, w->height - 200, 70, 30);
+	cairo_rectangle(cr, w->width / 2 - 100, w->height - 200, 70, 30);
+	cairo_rectangle(cr, w->width / 2 - 20, w->height - 200, 40, 30);
+	cairo_rectangle(cr, w->width / 2 + 30, w->height - 200, 70, 30);
 	cairo_stroke(cr);
 	cairo_restore(cr);
 
@@ -724,8 +746,8 @@ draw_pad(struct window *w, cairo_t *cr)
 	double pos;
 	char number[3];
 
-	rx = w->width/2 - 200;
-	ry = w->height/2 + 100;
+	rx = w->width / 2 - 200;
+	ry = w->height / 2 + 100;
 
 	cairo_save(cr);
 	/* outer ring (for ring) */
@@ -742,10 +764,10 @@ draw_pad(struct window *w, cairo_t *cr)
 	/* libinput has degrees and 0 is north, cairo has radians and 0 is
 	 * east */
 	if (w->pad.ring.position != -1) {
-		pos = (w->pad.ring.position + 270) * M_PI/180.0;
+		pos = (w->pad.ring.position + 270) * M_PI / 180.0;
 		cairo_set_source_rgb(cr, .0, .0, .0);
 		cairo_set_line_width(cr, 20);
-		cairo_arc(cr, rx, ry, 40, pos - M_PI/8 , pos + M_PI/8);
+		cairo_arc(cr, rx, ry, 40, pos - M_PI / 8, pos + M_PI / 8);
 		cairo_stroke(cr);
 
 		snprintf(number, sizeof(number), "%d", w->pad.ring.number);
@@ -755,11 +777,12 @@ draw_pad(struct window *w, cairo_t *cr)
 
 	if (w->pad.dial.position != -1) {
 		const int degrees_per_click = 15.0;
-		double degrees = fmod(w->pad.dial.position/120 * degrees_per_click, 360);
-		pos = (degrees + 270) * M_PI/180.0;
+		double degrees =
+			fmod(w->pad.dial.position / 120 * degrees_per_click, 360);
+		pos = (degrees + 270) * M_PI / 180.0;
 		cairo_set_source_rgb(cr, .0, .0, .0);
 		cairo_set_line_width(cr, 20);
-		cairo_arc(cr, rx, ry, 20, pos - M_PI/12 , pos + M_PI/12);
+		cairo_arc(cr, rx, ry, 20, pos - M_PI / 12, pos + M_PI / 12);
 		cairo_stroke(cr);
 
 		snprintf(number, sizeof(number), "%d", w->pad.dial.number);
@@ -769,8 +792,8 @@ draw_pad(struct window *w, cairo_t *cr)
 
 	cairo_restore(cr);
 
-	rx = w->width/2 - 300;
-	ry = w->height/2 + 50;
+	rx = w->width / 2 - 300;
+	ry = w->height / 2 + 50;
 
 	cairo_save(cr);
 	cairo_set_source_rgb(cr, .7, .7, .0);
@@ -800,8 +823,8 @@ draw_tablet(struct window *w, cairo_t *c
 	int rx, ry;
 
 	/* pressure/distance bars */
-	rx = w->width/2 + 100;
-	ry = w->height/2 + 50;
+	rx = w->width / 2 + 100;
+	ry = w->height / 2 + 50;
 	cairo_save(cr);
 	cairo_set_source_rgb(cr, .2, .6, .6);
 	cairo_rectangle(cr, rx, ry, 20, 100);
@@ -845,17 +868,18 @@ draw_tablet(struct window *w, cairo_t *c
 	cairo_translate(cr, w->tool.x, w->tool.y);
 	/* scale of 2.5 is large enough to make the marker visible around the
 	   physical totem */
-	cairo_scale(cr,
-		    1.0 + w->tool.size_major * 2.5,
-		    1.0 + w->tool.size_minor * 2.5);
-	cairo_scale(cr, 1.0 + w->tool.tilt_x/30.0, 1.0 + w->tool.tilt_y/30.0);
+	cairo_scale(cr, 1.0 + w->tool.size_major * 2.5, 1.0 + w->tool.size_minor * 2.5);
+	cairo_scale(cr, 1.0 + w->tool.tilt_x / 30.0, 1.0 + w->tool.tilt_y / 30.0);
 	if (w->tool.rotation)
-		cairo_rotate(cr, w->tool.rotation * M_PI/180.0);
+		cairo_rotate(cr, w->tool.rotation * M_PI / 180.0);
 	if (w->tool.pressure)
 		cairo_set_source_rgb(cr, .8, .8, .2);
-	cairo_arc(cr, 0, 0,
+	cairo_arc(cr,
+		  0,
+		  0,
 		  1 + 10 * max(w->tool.pressure, w->tool.distance),
-		  0, 2 * M_PI);
+		  0,
+		  2 * M_PI);
 	cairo_fill(cr);
 	cairo_restore(cr);
 
@@ -865,7 +889,7 @@ draw_tablet(struct window *w, cairo_t *c
 		cairo_scale(cr, 1.0, 1.0);
 		cairo_translate(cr, w->tool.x, w->tool.y);
 		if (w->tool.rotation)
-			cairo_rotate(cr, w->tool.rotation * M_PI/180.0);
+			cairo_rotate(cr, w->tool.rotation * M_PI / 180.0);
 		cairo_set_source_rgb(cr, .0, .0, .0);
 		cairo_move_to(cr, 0, 0);
 		cairo_rel_line_to(cr, 0, -w->tool.size_major * 2.5);
@@ -950,10 +974,10 @@ draw_background(struct window *w, cairo_
 	/* 10px and 5px grids */
 	cairo_save(cr);
 	cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
-	x1 = w->width/2 - 200;
-	y1 = w->height/2 - 200;
-	x2 = w->width/2 + 200;
-	y2 = w->height/2 - 200;
+	x1 = w->width / 2 - 200;
+	y1 = w->height / 2 - 200;
+	x2 = w->width / 2 + 200;
+	y2 = w->height / 2 - 200;
 	for (cols = 1; cols < 10; cols++) {
 		cairo_move_to(cr, x1 + 10 * cols, y1);
 		cairo_rel_line_to(cr, 0, 100);
@@ -967,10 +991,10 @@ draw_background(struct window *w, cairo_
 	}
 
 	/* 3px horiz/vert bar codes */
-	x3 = w->width/2 - 200;
-	y3 = w->height/2 + 200;
-	x4 = w->width/2 + 200;
-	y4 = w->height/2 + 100;
+	x3 = w->width / 2 - 200;
+	y3 = w->height / 2 + 200;
+	x4 = w->width / 2 + 200;
+	y4 = w->height / 2 + 100;
 	for (cols = 0; cols < 50; cols++) {
 		cairo_move_to(cr, x3 + 3 * cols, y3);
 		cairo_rel_line_to(cr, 0, 20);
@@ -982,11 +1006,11 @@ draw_background(struct window *w, cairo_
 
 	/* round targets */
 	for (int i = 0; i <= 3; i++) {
-		x1 = w->width * i/4.0;
-		x2 = w->width * i/4.0;
+		x1 = w->width * i / 4.0;
+		x2 = w->width * i / 4.0;
 
-		y1 = w->height * 1.0/4.0;
-		y2 = w->height * 3.0/4.0;
+		y1 = w->height * 1.0 / 4.0;
+		y2 = w->height * 3.0 / 4.0;
 
 		cairo_arc(cr, x1, y1, 10, 0, 2 * M_PI);
 		cairo_stroke(cr);
@@ -1023,13 +1047,9 @@ draw(GtkWidget *widget, cairo_t *cr, gpo
 	return TRUE;
 }
 
-#if HAVE_GTK4
+#ifdef HAVE_GTK4
 static void
-draw_gtk4(GtkDrawingArea *widget,
-	  cairo_t *cr,
-	  int width,
-	  int height,
-	  gpointer data)
+draw_gtk4(GtkDrawingArea *widget, cairo_t *cr, int width, int height, gpointer data)
 {
 	draw(GTK_WIDGET(widget), cr, data);
 }
@@ -1038,38 +1058,38 @@ draw_gtk4(GtkDrawingArea *widget,
 static void
 window_place_ui_elements(GtkWidget *widget, struct window *w)
 {
-#if HAVE_GTK4
+#ifdef HAVE_GTK4
 	w->width = gtk_widget_get_width(w->area);
 	w->height = gtk_widget_get_height(w->area);
 #else
 	gtk_window_get_size(GTK_WINDOW(widget), &w->width, &w->height);
 #endif
 
-	w->pointer.x = w->width/2;
-	w->pointer.y = w->height/2;
-	w->unaccelerated.x = w->width/2;
-	w->unaccelerated.y = w->height/2;
+	w->pointer.x = w->width / 2;
+	w->pointer.y = w->height / 2;
+	w->unaccelerated.x = w->width / 2;
+	w->unaccelerated.y = w->height / 2;
 	w->deltas[0].x = w->pointer.x;
 	w->deltas[0].y = w->pointer.y;
 
-	w->scroll.vx = w->width/2;
-	w->scroll.vy = w->height/2;
-	w->scroll.hx = w->width/2;
-	w->scroll.hy = w->height/2;
-	w->scroll.vx_discrete = w->width/2;
-	w->scroll.vy_discrete = w->height/2;
-	w->scroll.hx_discrete = w->width/2;
-	w->scroll.hy_discrete = w->height/2;
+	w->scroll.vx = w->width / 2;
+	w->scroll.vy = w->height / 2;
+	w->scroll.hx = w->width / 2;
+	w->scroll.hy = w->height / 2;
+	w->scroll.vx_discrete = w->width / 2;
+	w->scroll.vy_discrete = w->height / 2;
+	w->scroll.hx_discrete = w->width / 2;
+	w->scroll.hy_discrete = w->height / 2;
 
-	w->swipe.x = w->width/2;
-	w->swipe.y = w->height/2;
+	w->swipe.x = w->width / 2;
+	w->swipe.y = w->height / 2;
 
 	w->pinch.scale = 1.0;
-	w->pinch.x = w->width/2;
-	w->pinch.y = w->height/2;
+	w->pinch.x = w->width / 2;
+	w->pinch.y = w->height / 2;
 }
 
-#if HAVE_GTK4
+#ifdef HAVE_GTK4
 static void
 map_event_cb(GtkDrawingArea *widget, int width, int height, gpointer data)
 {
@@ -1077,10 +1097,7 @@ map_event_cb(GtkDrawingArea *widget, int
 
 	window_place_ui_elements(GTK_WIDGET(widget), w);
 
-	gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(w->area),
-				       draw_gtk4,
-				       w,
-				       NULL);
+	gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(w->area), draw_gtk4, w, NULL);
 
 	gtk_widget_set_cursor_from_name(w->win, "none");
 
@@ -1102,8 +1119,7 @@ map_event_cb(GtkWidget *widget, GdkEvent
 	display = gdk_window_get_display(window);
 
 	gdk_window_set_cursor(gtk_widget_get_window(w->win),
-			      gdk_cursor_new_for_display(display,
-							 GDK_BLANK_CURSOR));
+			      gdk_cursor_new_for_display(display, GDK_BLANK_CURSOR));
 
 	window_lock_pointer(w);
 }
@@ -1115,7 +1131,7 @@ window_quit(struct window *w)
 	g_main_loop_quit(w->event_loop);
 }
 
-#if HAVE_GTK4
+#ifdef HAVE_GTK4
 static gboolean
 window_delete_event_cb(GtkWindow *window, gpointer data)
 {
@@ -1140,14 +1156,14 @@ window_init(struct window *w)
 {
 	list_init(&w->evdev_devices);
 
-#if HAVE_GTK4
+#ifdef HAVE_GTK4
 	w->win = gtk_window_new();
 #else
 	w->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 #endif
 
 	if (getenv("LIBINPUT_RUNNING_TEST_SUITE")) {
-#if HAVE_GTK4
+#ifdef HAVE_GTK4
 		gtk_window_minimize(GTK_WINDOW(w->win));
 #else
 		gtk_window_iconify(GTK_WINDOW(w->win));
@@ -1162,15 +1178,21 @@ window_init(struct window *w)
 
 	w->area = gtk_drawing_area_new();
 
-#if HAVE_GTK4
+#ifdef HAVE_GTK4
 	g_signal_connect(G_OBJECT(w->area), "resize", G_CALLBACK(map_event_cb), w);
-	g_signal_connect(G_OBJECT(w->win), "close-request", G_CALLBACK(window_delete_event_cb), w);
+	g_signal_connect(G_OBJECT(w->win),
+			 "close-request",
+			 G_CALLBACK(window_delete_event_cb),
+			 w);
 
 	gtk_window_set_child(GTK_WINDOW(w->win), w->area);
 	gtk_widget_set_visible(w->win, TRUE);
 #else
 	g_signal_connect(G_OBJECT(w->win), "map-event", G_CALLBACK(map_event_cb), w);
-	g_signal_connect(G_OBJECT(w->win), "delete-event", G_CALLBACK(window_delete_event_cb), w);
+	g_signal_connect(G_OBJECT(w->win),
+			 "delete-event",
+			 G_CALLBACK(window_delete_event_cb),
+			 w);
 
 	gtk_widget_set_events(w->win, 0);
 	gtk_widget_set_events(w->area, 0);
@@ -1221,7 +1243,6 @@ change_ptraccel(struct window *w, double
 			       libinput_device_get_name(*dev),
 			       speed);
 		}
-
 	}
 }
 
@@ -1231,8 +1252,7 @@ handle_event_evdev(GIOChannel *source, G
 	struct libinput_device *dev = data;
 	struct libinput *li = libinput_device_get_context(dev);
 	struct window *w = libinput_get_user_data(li);
-	struct evdev_device *d,
-			    *device = NULL;
+	struct evdev_device *d, *device = NULL;
 	struct input_event e;
 	int rc;
 
@@ -1249,9 +1269,7 @@ handle_event_evdev(GIOChannel *source, G
 	}
 
 	do {
-		rc = libevdev_next_event(device->evdev,
-					 LIBEVDEV_READ_FLAG_NORMAL,
-					 &e);
+		rc = libevdev_next_event(device->evdev, LIBEVDEV_READ_FLAG_NORMAL, &e);
 		if (rc == -EAGAIN) {
 			break;
 		} else if (rc == LIBEVDEV_READ_STATUS_SYNC) {
@@ -1272,7 +1290,7 @@ handle_event_evdev(GIOChannel *source, G
 			break;
 		case EVENT(EV_ABS, ABS_MT_SLOT):
 			w->evdev.slot = min((unsigned int)e.value,
-					  ARRAY_LENGTH(w->evdev.slots) - 1);
+					    ARRAY_LENGTH(w->evdev.slots) - 1);
 			w->evdev.device = (uintptr_t)dev;
 			break;
 		case EVENT(EV_ABS, ABS_MT_TRACKING_ID):
@@ -1316,7 +1334,7 @@ register_evdev_device(struct window *w,
 	ud = libinput_device_get_udev_device(dev);
 	device_node = udev_device_get_devnode(ud);
 
-	fd = open(device_node, O_RDONLY|O_NONBLOCK);
+	fd = open(device_node, O_RDONLY | O_NONBLOCK);
 	if (fd == -1) {
 		msg("failed to open %s, evdev events unavailable\n", device_node);
 		goto out;
@@ -1332,13 +1350,12 @@ register_evdev_device(struct window *w,
 	list_append(&w->evdev_devices, &d->node);
 	d->fd = fd;
 	d->evdev = evdev;
-	d->libinput_device =libinput_device_ref(dev);
+	d->libinput_device = libinput_device_ref(dev);
 
 	c = g_io_channel_unix_new(fd);
 	g_io_channel_set_encoding(c, NULL, NULL);
-	d->source_id = g_io_add_watch(c, G_IO_IN,
-				      handle_event_evdev,
-				      d->libinput_device);
+	d->source_id =
+		g_io_add_watch(c, G_IO_IN, handle_event_evdev, d->libinput_device);
 	fd = -1;
 out:
 	close(fd);
@@ -1380,8 +1397,7 @@ handle_event_device_notify(struct libinp
 	if (libinput_event_get_type(ev) == LIBINPUT_EVENT_DEVICE_ADDED) {
 		type = "added";
 		register_evdev_device(w, dev);
-		tools_device_apply_config(libinput_event_get_device(ev),
-					  &w->options);
+		tools_device_apply_config(libinput_event_get_device(ev), &w->options);
 	} else {
 		type = "removed";
 		unregister_evdev_device(w, dev);
@@ -1399,7 +1415,7 @@ handle_event_device_notify(struct libinp
 				break;
 			}
 		}
-	} else  {
+	} else {
 		ARRAY_FOR_EACH(w->devices, device) {
 			if (*device == dev) {
 				libinput_device_unref(*device);
@@ -1455,7 +1471,7 @@ handle_event_touch(struct libinput_event
 	struct touch *touch;
 	double x, y;
 
-	if (slot == -1 || slot >= (int) ARRAY_LENGTH(w->touches))
+	if (slot == -1 || slot >= (int)ARRAY_LENGTH(w->touches))
 		return;
 
 	touch = &w->touches[slot];
@@ -1497,7 +1513,8 @@ handle_event_axis(struct libinput_event
 
 		if (type == LIBINPUT_EVENT_POINTER_SCROLL_WHEEL) {
 			w->scroll.vy_discrete += value;
-			w->scroll.vy_discrete = clip(w->scroll.vy_discrete, 0, w->height);
+			w->scroll.vy_discrete =
+				clip(w->scroll.vy_discrete, 0, w->height);
 		}
 	}
 
@@ -1509,7 +1526,8 @@ handle_event_axis(struct libinput_event
 
 		if (type == LIBINPUT_EVENT_POINTER_SCROLL_WHEEL) {
 			w->scroll.hx_discrete += value;
-			w->scroll.hx_discrete = clip(w->scroll.hx_discrete, 0, w->width);
+			w->scroll.hx_discrete =
+				clip(w->scroll.hx_discrete, 0, w->width);
 		}
 	}
 }
@@ -1520,11 +1538,10 @@ handle_event_keyboard(struct libinput_ev
 	struct libinput_event_keyboard *k = libinput_event_get_keyboard_event(ev);
 	unsigned int key = libinput_event_keyboard_get_key(k);
 
-	if (libinput_event_keyboard_get_key_state(k) ==
-	    LIBINPUT_KEY_STATE_RELEASED)
+	if (libinput_event_keyboard_get_key_state(k) == LIBINPUT_KEY_STATE_RELEASED)
 		return 0;
 
-	switch(key) {
+	switch (key) {
 	case KEY_ESC:
 		return 1;
 	case KEY_UP:
@@ -1547,7 +1564,8 @@ handle_event_button(struct libinput_even
 	unsigned int button = libinput_event_pointer_get_button(p);
 	bool is_press;
 
-	is_press = libinput_event_pointer_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED;
+	is_press = libinput_event_pointer_get_button_state(p) ==
+		   LIBINPUT_BUTTON_STATE_PRESSED;
 
 	switch (button) {
 	case BTN_LEFT:
@@ -1561,10 +1579,8 @@ handle_event_button(struct libinput_even
 		break;
 	default:
 		w->buttons.other = is_press;
-		w->buttons.other_name = libevdev_event_code_get_name(EV_KEY,
-								     button);
+		w->buttons.other_name = libevdev_event_code_get_name(EV_KEY, button);
 	}
-
 }
 
 static void
@@ -1579,8 +1595,8 @@ handle_event_swipe(struct libinput_event
 	switch (libinput_event_get_type(ev)) {
 	case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
 		w->swipe.nfingers = nfingers;
-		w->swipe.x = w->width/2;
-		w->swipe.y = w->height/2;
+		w->swipe.x = w->width / 2;
+		w->swipe.y = w->height / 2;
 		break;
 	case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
 		dx = libinput_event_gesture_get_dx(g);
@@ -1590,8 +1606,8 @@ handle_event_swipe(struct libinput_event
 		break;
 	case LIBINPUT_EVENT_GESTURE_SWIPE_END:
 		w->swipe.nfingers = 0;
-		w->swipe.x = w->width/2;
-		w->swipe.y = w->height/2;
+		w->swipe.x = w->width / 2;
+		w->swipe.y = w->height / 2;
 		break;
 	default:
 		abort();
@@ -1610,8 +1626,8 @@ handle_event_pinch(struct libinput_event
 	switch (libinput_event_get_type(ev)) {
 	case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
 		w->pinch.nfingers = nfingers;
-		w->pinch.x = w->width/2;
-		w->pinch.y = w->height/2;
+		w->pinch.x = w->width / 2;
+		w->pinch.y = w->height / 2;
 		break;
 	case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
 		dx = libinput_event_gesture_get_dx(g);
@@ -1623,8 +1639,8 @@ handle_event_pinch(struct libinput_event
 		break;
 	case LIBINPUT_EVENT_GESTURE_PINCH_END:
 		w->pinch.nfingers = 0;
-		w->pinch.x = w->width/2;
-		w->pinch.y = w->height/2;
+		w->pinch.x = w->width / 2;
+		w->pinch.y = w->height / 2;
 		w->pinch.angle = 0.0;
 		w->pinch.scale = 1.0;
 		break;
@@ -1685,8 +1701,8 @@ handle_event_tablet(struct libinput_even
 			w->tool.x_in = x;
 			w->tool.y_in = y;
 			w->tool.ndeltas = 0;
-			w->tool.deltas[0].x = w->width/2;
-			w->tool.deltas[0].y = w->height/2;
+			w->tool.deltas[0].x = w->width / 2;
+			w->tool.deltas[0].y = w->height / 2;
 		}
 		break;
 	case LIBINPUT_EVENT_TABLET_TOOL_TIP:
@@ -1728,12 +1744,12 @@ handle_event_tablet(struct libinput_even
 		w->tool.ndeltas++;
 		break;
 	case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
-		is_press = libinput_event_tablet_tool_get_button_state(t) == LIBINPUT_BUTTON_STATE_PRESSED;
+		is_press = libinput_event_tablet_tool_get_button_state(t) ==
+			   LIBINPUT_BUTTON_STATE_PRESSED;
 		button = libinput_event_tablet_tool_get_button(t);
 
 		w->buttons.other = is_press;
-		w->buttons.other_name = libevdev_event_code_get_name(EV_KEY,
-								     button);
+		w->buttons.other_name = libevdev_event_code_get_name(EV_KEY, button);
 		break;
 	default:
 		abort();
@@ -1746,16 +1762,16 @@ handle_event_tablet_pad(struct libinput_
 	struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(ev);
 	bool is_press;
 	unsigned int button;
-	static const char *pad_buttons[] = {
-		"Pad 0", "Pad 1", "Pad 2", "Pad 3", "Pad 4", "Pad 5",
-		"Pad 6", "Pad 7", "Pad 8", "Pad 9", "Pad >= 10"
-	};
+	static const char *pad_buttons[] = { "Pad 0", "Pad 1", "Pad 2",    "Pad 3",
+					     "Pad 4", "Pad 5", "Pad 6",    "Pad 7",
+					     "Pad 8", "Pad 9", "Pad >= 10" };
 	double position, delta;
 	double number;
 
 	switch (libinput_event_get_type(ev)) {
 	case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
-		is_press = libinput_event_tablet_pad_get_button_state(p) == LIBINPUT_BUTTON_STATE_PRESSED;
+		is_press = libinput_event_tablet_pad_get_button_state(p) ==
+			   LIBINPUT_BUTTON_STATE_PRESSED;
 		button = libinput_event_tablet_pad_get_button_number(p);
 		w->buttons.other = is_press;
 		w->buttons.other_name = pad_buttons[min(button, 10)];
@@ -1883,7 +1899,8 @@ sockets_init(struct libinput *li)
 }
 
 static void
-usage(struct option *opts) {
+usage(struct option *opts)
+{
 	printf("Usage: libinput debug-gui [options] [--udev <seat>|[--device] /dev/input/event0]\n");
 
 	if (opts)
@@ -1904,15 +1921,15 @@ signal_handler(void *data)
 int
 main(int argc, char **argv)
 {
-	struct window w = {0};
+	struct window w = { 0 };
 	struct tools_options options;
 	struct libinput *li;
 	enum tools_backend backend = BACKEND_NONE;
-	const char *seat_or_device[2] = {"seat0", NULL};
+	const char *seat_or_device[2] = { "seat0", NULL };
 	bool verbose = false;
 	bool gtk_init = false;
 
-#if HAVE_GTK4
+#ifdef HAVE_GTK4
 	gtk_init = gtk_init_check();
 #else
 	gtk_init = gtk_init_check(&argc, &argv);
@@ -1932,6 +1949,7 @@ main(int argc, char **argv)
 			OPT_GRAB,
 			OPT_VERBOSE,
 		};
+		/* clang-format off */
 		static struct option opts[] = {
 			CONFIGURATION_OPTIONS,
 			{ "help",                      no_argument,       0, 'h' },
@@ -1939,14 +1957,15 @@ main(int argc, char **argv)
 			{ "udev",                      required_argument, 0, OPT_UDEV },
 			{ "grab",                      no_argument,       0, OPT_GRAB },
 			{ "verbose",                   no_argument,       0, OPT_VERBOSE },
-			{ 0, 0, 0, 0}
+			{ 0, 0, 0, 0},
 		};
+		/* clang-format on */
 
 		c = getopt_long(argc, argv, "h", opts, &option_index);
 		if (c == -1)
 			break;
 
-		switch(c) {
+		switch (c) {
 		case '?':
 			exit(EXIT_INVALID_USAGE);
 			break;
@@ -1975,7 +1994,6 @@ main(int argc, char **argv)
 			}
 			break;
 		}
-
 	}
 
 	if (optind < argc) {
@@ -1989,7 +2007,13 @@ main(int argc, char **argv)
 		backend = BACKEND_UDEV;
 	}
 
-	li = tools_open_backend(backend, seat_or_device, verbose, &w.grab);
+	bool with_plugins = (options.plugins == 1);
+	li = tools_open_backend(backend,
+				seat_or_device,
+				verbose,
+				&w.grab,
+				with_plugins,
+				steal(&options.plugin_paths));
 	if (!li)
 		return EXIT_FAILURE;
 
diff -pruN 1.28.1-1/tools/libinput-debug-tablet-pad.c 1.30.0-1/tools/libinput-debug-tablet-pad.c
--- 1.28.1-1/tools/libinput-debug-tablet-pad.c	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/tools/libinput-debug-tablet-pad.c	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,552 @@
+/*
+ * Copyright © 2025 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:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <libevdev/libevdev.h>
+#include <libinput.h>
+#include <libudev.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "util-files.h"
+#include "util-input-event.h"
+#include "util-macros.h"
+#include "util-mem.h"
+
+#include "shared.h"
+
+DEFINE_UNREF_CLEANUP_FUNC(udev_device);
+
+static volatile sig_atomic_t stop = 0;
+static struct tools_options options;
+static int termwidth = 78;
+
+struct context {
+	struct libinput *libinput;
+	struct libinput_device *device;
+	struct libevdev *evdev;
+
+	/* fd[0] ... libinput fd
+	   fd[1] ... libevdev fd */
+	struct pollfd fds[2];
+
+	/* libinput device state */
+	double ring[2];
+	double strip[2];
+	double dial[2];
+	unsigned int buttons_down[32];
+	unsigned int evdev_buttons_down[BTN_START - BTN_0 + 1];
+	/* keys[i] = keycode if a keycode is down, 8 keys simultaneously is enough */
+	uint32_t keys[8];
+
+	unsigned int nbuttons;
+
+	/* libevdev device state */
+	struct {
+		int wheel;
+		int throttle;
+		int rx;
+		int ry;
+	} abs;
+
+	struct {
+		int wheel[2];
+		int wheel_v120[2];
+	} rel;
+};
+
+LIBINPUT_ATTRIBUTE_PRINTF(2, 3)
+static void
+print_line(const char *label, const char *format, ...)
+{
+	va_list args;
+	va_start(args, format);
+	_autofree_ char *msg = strdup_vprintf(format, args);
+	va_end(args);
+
+	_autofree_ char *prefix = strdup_printf("%s:", label);
+	printf(ANSI_CLEAR_LINE "  %-19s %s\n", prefix, msg);
+}
+
+static void
+print_buttons(struct context *ctx, unsigned int *buttons, size_t nbuttons)
+{
+	_autostrvfree_ char **strv = NULL;
+	for (size_t i = 0; i < nbuttons; i++) {
+		strv = strv_append_printf(strv, "%2zd: %c", i, buttons[i] ? 'X' : ' ');
+	}
+	_autofree_ char *btnstr = strv_join(strv, " ");
+	print_line("buttons", "%s", btnstr ? btnstr : "");
+}
+
+static void
+print_dial(const char *prefix, double value)
+{
+	print_line(prefix, "% 8.2f", value);
+}
+
+static void
+print_buttons_evdev(struct context *ctx, unsigned int *buttons, size_t nbuttons)
+{
+	_autostrvfree_ char **strv = NULL;
+	for (size_t i = 0; i < nbuttons; i++) {
+		if (!buttons[i])
+			continue;
+
+		unsigned int button = BTN_0 + i;
+		strv = strv_append_printf(strv,
+					  "%s",
+					  libevdev_event_code_get_name(EV_KEY, button));
+	}
+
+	_autofree_ char *btnstr = strv_join(strv, ", ");
+	print_line("buttons", "%s", btnstr ? btnstr : "");
+}
+
+static void
+print_rel_wheel(struct context *ctx, unsigned int code, int value)
+{
+	print_line(libevdev_event_code_get_name(EV_REL, code), "% 5d", value);
+}
+
+static void
+print_bar(const char *header, double value, double normalized)
+{
+	char empty[termwidth];
+	bool oob = false;
+	/* the bar is minimum 10 chars, otherwise 78 or whatever fits.
+	   32 is the manually-added up length of the prefix + [|] */
+	const int width = clamp(termwidth - 32, 10, 78);
+	int left_pad, right_pad;
+
+	memset(empty, '-', sizeof empty);
+
+	if (normalized < 0.0 || normalized > 1.0) {
+		normalized = clamp(normalized, 0.0, 1.0);
+		oob = true;
+	}
+
+	left_pad = width * normalized + 0.5;
+	right_pad = width - left_pad;
+
+	print_line(header,
+		   "%s%8.2f [%.*s|%.*s]%s",
+		   oob ? ANSI_RED : "",
+		   value,
+		   left_pad,
+		   empty,
+		   right_pad,
+		   empty,
+		   oob ? ANSI_NORMAL : "");
+}
+
+static double
+normalize(struct libevdev *evdev, int code, int value)
+{
+	const struct input_absinfo *abs;
+
+	if (!evdev)
+		return 0.0;
+
+	abs = libevdev_get_abs_info(evdev, code);
+
+	if (!abs)
+		return 0.0;
+
+	return 1.0 * (value - abs->minimum) / absinfo_range(abs);
+}
+
+static int
+print_state(struct context *ctx)
+{
+	double w, h;
+	int lines_printed = 0;
+
+	if (!ctx->device) {
+		printf(ANSI_RED "No device connected" ANSI_NORMAL ANSI_CLEAR_EOL "\n");
+	} else {
+		libinput_device_get_size(ctx->device, &w, &h);
+		printf("Device: %s (%s)%s\n",
+		       libinput_device_get_name(ctx->device),
+		       libinput_device_get_sysname(ctx->device),
+		       ANSI_CLEAR_EOL);
+	}
+	lines_printed++;
+
+	printf("libinput:\n");
+	print_bar("ring 0", ctx->ring[0], ctx->ring[0] / 360.0);
+	print_bar("ring 1", ctx->ring[1], ctx->ring[1] / 360.0);
+	print_bar("strip 0", ctx->strip[0], ctx->strip[0]);
+	print_bar("strip 1", ctx->strip[1], ctx->strip[1]);
+	print_dial("dial 0", ctx->dial[0]);
+	print_dial("dial 1", ctx->dial[1]);
+	print_buttons(ctx,
+		      ctx->buttons_down,
+		      min(ARRAY_LENGTH(ctx->buttons_down), ctx->nbuttons));
+	lines_printed += 8;
+
+	printf("evdev:\n");
+	print_bar("ABS_WHEEL",
+		  ctx->abs.wheel,
+		  normalize(ctx->evdev, ABS_WHEEL, ctx->abs.wheel));
+	print_bar("ABS_THROTTLE",
+		  ctx->abs.throttle,
+		  normalize(ctx->evdev, ABS_THROTTLE, ctx->abs.throttle));
+	print_bar("ABS_RX", ctx->abs.rx, normalize(ctx->evdev, ABS_RX, ctx->abs.rx));
+	print_bar("ABS_RY", ctx->abs.ry, normalize(ctx->evdev, ABS_RY, ctx->abs.ry));
+	print_rel_wheel(ctx, REL_WHEEL, ctx->rel.wheel[0]);
+	print_rel_wheel(ctx, REL_WHEEL_HI_RES, ctx->rel.wheel_v120[0]);
+	print_rel_wheel(ctx, REL_HWHEEL, ctx->rel.wheel[1]);
+	print_rel_wheel(ctx, REL_HWHEEL_HI_RES, ctx->rel.wheel_v120[1]);
+	print_buttons_evdev(ctx,
+			    ctx->evdev_buttons_down,
+			    ARRAY_LENGTH(ctx->evdev_buttons_down));
+	lines_printed += 10;
+
+	return lines_printed;
+}
+
+static void
+handle_device_added(struct context *ctx, struct libinput_event *ev)
+{
+	struct libinput_device *device = libinput_event_get_device(ev);
+	_unref_(udev_device) *udev_device = NULL;
+	const char *devnode;
+
+	if (ctx->device)
+		return;
+
+	if (!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_PAD))
+		return;
+
+	ctx->device = libinput_device_ref(device);
+	ctx->nbuttons = libinput_device_tablet_pad_get_num_buttons(device);
+
+	udev_device = libinput_device_get_udev_device(device);
+	if (!udev_device)
+		return;
+
+	devnode = udev_device_get_devnode(udev_device);
+	if (devnode) {
+		int fd = open(devnode, O_RDONLY | O_NONBLOCK);
+		assert(fd != -1);
+		assert(libevdev_new_from_fd(fd, &ctx->evdev) == 0);
+	}
+}
+
+static void
+handle_device_removed(struct context *ctx, struct libinput_event *ev)
+{
+	struct libinput_device *device = libinput_event_get_device(ev);
+
+	if (ctx->device != device)
+		return;
+
+	libinput_device_unref(steal(&ctx->device));
+	libevdev_free(steal(&ctx->evdev));
+	xclose(&ctx->fds[1].fd);
+}
+
+static void
+handle_libinput_events(struct context *ctx)
+{
+	struct libinput *li = ctx->libinput;
+	struct libinput_event *ev;
+	struct libinput_event_tablet_pad *pev;
+	uint32_t number;
+	double value;
+	enum libinput_button_state state;
+
+	libinput_dispatch(li);
+	while ((ev = libinput_get_event(li))) {
+		switch (libinput_event_get_type(ev)) {
+		case LIBINPUT_EVENT_NONE:
+			abort();
+		case LIBINPUT_EVENT_DEVICE_ADDED:
+			handle_device_added(ctx, ev);
+			tools_device_apply_config(libinput_event_get_device(ev),
+						  &options);
+			break;
+		case LIBINPUT_EVENT_DEVICE_REMOVED:
+			handle_device_removed(ctx, ev);
+			break;
+		case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
+			pev = libinput_event_get_tablet_pad_event(ev);
+			number = libinput_event_tablet_pad_get_button_number(pev);
+			state = libinput_event_tablet_pad_get_button_state(pev);
+			ctx->buttons_down[number] =
+				state == LIBINPUT_BUTTON_STATE_PRESSED ? 1 : 0;
+			break;
+		case LIBINPUT_EVENT_TABLET_PAD_RING:
+			pev = libinput_event_get_tablet_pad_event(ev);
+			number = libinput_event_tablet_pad_get_ring_number(pev);
+			value = libinput_event_tablet_pad_get_ring_position(pev);
+			ctx->ring[number] = value;
+			break;
+		case LIBINPUT_EVENT_TABLET_PAD_STRIP:
+			pev = libinput_event_get_tablet_pad_event(ev);
+			number = libinput_event_tablet_pad_get_strip_number(pev);
+			value = libinput_event_tablet_pad_get_strip_position(pev);
+			ctx->strip[number] = value;
+			break;
+		case LIBINPUT_EVENT_TABLET_PAD_DIAL: {
+			pev = libinput_event_get_tablet_pad_event(ev);
+			number = libinput_event_tablet_pad_get_dial_number(pev);
+			value = libinput_event_tablet_pad_get_dial_delta_v120(pev);
+			ctx->dial[number] = value;
+			break;
+		}
+		case LIBINPUT_EVENT_TABLET_PAD_KEY: {
+			pev = libinput_event_get_tablet_pad_event(ev);
+			uint32_t key = libinput_event_tablet_pad_get_key(pev);
+			if (libinput_event_tablet_pad_get_key_state(pev) ==
+			    LIBINPUT_KEY_STATE_PRESSED) {
+				ARRAY_FOR_EACH(ctx->keys, k) {
+					if (*k == 0) {
+						*k = key;
+					}
+				}
+			} else {
+				ARRAY_FOR_EACH(ctx->keys, k) {
+					if (*k == key) {
+						*k = 0;
+					}
+				}
+			}
+			break;
+		}
+		default:
+			break;
+		}
+
+		libinput_event_destroy(ev);
+	}
+}
+
+static void
+handle_libevdev_events(struct context *ctx)
+{
+	struct libevdev *evdev = ctx->evdev;
+	struct input_event event;
+
+#define evbit(_t, _c) (((_t) << 16) | (_c))
+
+	if (!evdev)
+		return;
+
+	while (libevdev_next_event(evdev, LIBEVDEV_READ_FLAG_NORMAL, &event) ==
+	       LIBEVDEV_READ_STATUS_SUCCESS) {
+		switch (evbit(event.type, event.code)) {
+		case evbit(EV_KEY, BTN_0)... evbit(EV_KEY, BTN_START):
+			ctx->evdev_buttons_down[event.code - BTN_0] =
+				event.value ? event.code : 0;
+			break;
+		case evbit(EV_REL, REL_WHEEL):
+			ctx->rel.wheel[0] = event.value;
+			break;
+		case evbit(EV_REL, REL_HWHEEL):
+			ctx->rel.wheel[1] = event.value;
+			break;
+		case evbit(EV_REL, REL_WHEEL_HI_RES):
+			ctx->rel.wheel_v120[0] = event.value;
+			break;
+		case evbit(EV_REL, REL_HWHEEL_HI_RES):
+			ctx->rel.wheel_v120[1] = event.value;
+			break;
+		case evbit(EV_ABS, ABS_WHEEL):
+			ctx->abs.wheel = event.value;
+			break;
+		case evbit(EV_ABS, ABS_THROTTLE):
+			ctx->abs.throttle = event.value;
+			break;
+		case evbit(EV_ABS, ABS_RX):
+			ctx->abs.rx = event.value;
+			break;
+		case evbit(EV_ABS, ABS_RY):
+			ctx->abs.ry = event.value;
+			break;
+		}
+	}
+}
+
+static void
+sighandler(int signal, siginfo_t *siginfo, void *userdata)
+{
+	stop = 1;
+}
+
+static void
+mainloop(struct context *ctx)
+{
+	unsigned int lines_printed = 20;
+
+	ctx->fds[0].fd = libinput_get_fd(ctx->libinput);
+
+	/* pre-load the lines */
+	for (unsigned int i = 0; i < lines_printed; i++)
+		printf("\n");
+
+	do {
+		handle_libinput_events(ctx);
+		handle_libevdev_events(ctx);
+
+		printf(ANSI_LEFT, 1000);
+		printf(ANSI_UP, lines_printed);
+		lines_printed = print_state(ctx);
+	} while (!stop && poll(ctx->fds, 2, -1) > -1);
+
+	printf("\n");
+}
+
+static void
+usage(void)
+{
+	printf("Usage: libinput debug-tablet [options] [--udev <seat>|--device /dev/input/event0]\n");
+}
+
+static void
+init_context(struct context *ctx)
+{
+
+	memset(ctx, 0, sizeof *ctx);
+
+	ctx->fds[0].fd = -1; /* libinput fd */
+	ctx->fds[0].events = POLLIN;
+	ctx->fds[0].revents = 0;
+	ctx->fds[1].fd = -1; /* libevdev fd */
+	ctx->fds[1].events = POLLIN;
+	ctx->fds[1].revents = 0;
+}
+
+int
+main(int argc, char **argv)
+{
+	struct context ctx;
+	struct libinput *li;
+	enum tools_backend backend = BACKEND_NONE;
+	const char *seat_or_device[2] = { "seat0", NULL };
+	struct sigaction act;
+	bool grab = false;
+
+	init_context(&ctx);
+
+	tools_init_options(&options);
+
+	while (1) {
+		int c;
+		int option_index = 0;
+		enum {
+			OPT_DEVICE = 1,
+			OPT_UDEV,
+		};
+		static struct option opts[] = {
+			CONFIGURATION_OPTIONS,
+			{ "help", no_argument, 0, 'h' },
+			{ "device", required_argument, 0, OPT_DEVICE },
+			{ "udev", required_argument, 0, OPT_UDEV },
+			{ 0, 0, 0, 0 }
+		};
+
+		c = getopt_long(argc, argv, "h", opts, &option_index);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case '?':
+			exit(EXIT_INVALID_USAGE);
+			break;
+		case 'h':
+			usage();
+			exit(EXIT_SUCCESS);
+			break;
+		case OPT_DEVICE:
+			backend = BACKEND_DEVICE;
+			seat_or_device[0] = optarg;
+			break;
+		case OPT_UDEV:
+			backend = BACKEND_UDEV;
+			seat_or_device[0] = optarg;
+			break;
+		default:
+			if (tools_parse_option(c, optarg, &options) != 0) {
+				usage();
+				return EXIT_INVALID_USAGE;
+			}
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		if (optind < argc - 1 || backend != BACKEND_NONE) {
+			usage();
+			return EXIT_INVALID_USAGE;
+		}
+		backend = BACKEND_DEVICE;
+		seat_or_device[0] = argv[optind];
+	} else if (backend == BACKEND_NONE) {
+		backend = BACKEND_UDEV;
+	}
+
+	memset(&act, 0, sizeof(act));
+	act.sa_sigaction = sighandler;
+	act.sa_flags = SA_SIGINFO;
+
+	if (sigaction(SIGINT, &act, NULL) == -1) {
+		fprintf(stderr,
+			"Failed to set up signal handling (%s)\n",
+			strerror(errno));
+		return EXIT_FAILURE;
+	}
+
+	bool with_plugins = (options.plugins == 1);
+	li = tools_open_backend(backend,
+				seat_or_device,
+				false,
+				&grab,
+				with_plugins,
+				steal(&options.plugin_paths));
+
+	if (!li)
+		return EXIT_FAILURE;
+
+	struct winsize w;
+	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1)
+		termwidth = w.ws_col;
+
+	ctx.libinput = li;
+	mainloop(&ctx);
+
+	libinput_unref(li);
+
+	return EXIT_SUCCESS;
+}
diff -pruN 1.28.1-1/tools/libinput-debug-tablet-pad.man 1.30.0-1/tools/libinput-debug-tablet-pad.man
--- 1.28.1-1/tools/libinput-debug-tablet-pad.man	1970-01-01 00:00:00.000000000 +0000
+++ 1.30.0-1/tools/libinput-debug-tablet-pad.man	2025-11-25 03:40:43.000000000 +0000
@@ -0,0 +1,33 @@
+.TH libinput-debug-tablet-pad "1"
+.SH NAME
+libinput\-debug\-tablet-pad\ \- debug and visualize tablet pad events
+.SH SYNOPSIS
+.B libinput debug-tablet-pad [\-\-help] [options] [\fI/dev/input/event0\fI]
+.SH DESCRIPTION
+.PP
+The
+.B "libinput debug-tablet-pad"
+tool debugs the values of the various buttons and axes on a tablet pad.
+This is an interactive tool. When executed, the tool will prompt the user to
+interact with the tablet and display the current value on each available
+feature.
+.PP
+This is a debugging tool only, its output may change at any time. Do not
+rely on the output.
+.PP
+This tool usually needs to be run as root to have access to the
+/dev/input/eventX nodes.
+.SH OPTIONS
+If a device node is given, this tool opens that device node. Otherwise, this
+tool searches for the first node that looks like a tablet and uses that
+node.
+.TP 8
+.B \-\-help
+Print help
+.PP
+Events shown by this tool may not correspond to the events seen by a
+different user of libinput. This tool initializes a separate context.
+.SH LIBINPUT
+Part of the
+.B libinput(1)
+suite
diff -pruN 1.28.1-1/tools/libinput-debug-tablet.c 1.30.0-1/tools/libinput-debug-tablet.c
--- 1.28.1-1/tools/libinput-debug-tablet.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-debug-tablet.c	2025-11-25 03:40:43.000000000 +0000
@@ -26,20 +26,21 @@
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <inttypes.h>
 #include <getopt.h>
+#include <inttypes.h>
+#include <libevdev/libevdev.h>
+#include <libinput.h>
 #include <poll.h>
+#include <signal.h>
 #include <stdio.h>
 #include <string.h>
-#include <signal.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
-#include <libinput.h>
-#include <libevdev/libevdev.h>
 
-#include "shared.h"
-#include "util-macros.h"
 #include "util-input-event.h"
+#include "util-macros.h"
+
+#include "shared.h"
 
 static volatile sig_atomic_t stop = 0;
 static struct tools_options options;
@@ -77,23 +78,20 @@ LIBINPUT_ATTRIBUTE_PRINTF(1, 2)
 static void
 print_line(const char *format, ...)
 {
-	char empty[] = "                                                                                ";
-	const int width = 80;
-	int n;
-	va_list args;
-
-	printf("\r");
+	char buf[256];
 
+	va_list args;
 	va_start(args, format);
-	n = vprintf(format, args);
+	vsnprintf(buf, sizeof(buf), format, args);
 	va_end(args);
-	printf("%.*s\n", width - n, empty);
+
+	printf(ANSI_CLEAR_LINE "%s\n", buf);
 }
 
 static void
 print_buttons(struct context *ctx, unsigned int *buttons, size_t sz)
 {
-	char buf[256] = {0};
+	char buf[256] = { 0 };
 	size_t len = 0;
 
 	for (size_t i = 0; i < sz; i++) {
@@ -132,8 +130,10 @@ print_bar(const char *header, double val
 	       oob ? ANSI_RED : "",
 	       header,
 	       value,
-	       left_pad, empty,
-	       right_pad, empty,
+	       left_pad,
+	       empty,
+	       right_pad,
+	       empty,
 	       oob ? ANSI_NORMAL : "");
 }
 
@@ -150,7 +150,7 @@ normalize(struct libevdev *evdev, int co
 	if (!abs)
 		return 0.0;
 
-	return 1.0 * (value - abs->minimum)/absinfo_range(abs);
+	return 1.0 * (value - abs->minimum) / absinfo_range(abs);
 }
 
 static int
@@ -214,25 +214,31 @@ print_state(struct context *ctx)
 	print_line("tip: %s", ctx->tip_is_down ? "down" : "up");
 	print_bar("x:", ctx->x, ctx->x_norm);
 	print_bar("y:", ctx->y, ctx->y_norm);
-	print_bar("tilt x:", ctx->tx, (ctx->tx + 90)/180);
-	print_bar("tilt y:", ctx->ty, (ctx->ty + 90)/180);
+	print_bar("tilt x:", ctx->tx, (ctx->tx + 90) / 180);
+	print_bar("tilt y:", ctx->ty, (ctx->ty + 90) / 180);
 	print_bar("dist:", ctx->dist, ctx->dist);
 	print_bar("pressure:", ctx->pressure, ctx->pressure);
-	print_bar("rotation:", ctx->rotation, ctx->rotation/360.0);
-	print_bar("slider:", ctx->slider, (ctx->slider + 1.0)/2.0);
-	print_buttons(ctx,
-		      ctx->buttons_down,
-		      ARRAY_LENGTH(ctx->buttons_down));
+	print_bar("rotation:", ctx->rotation, ctx->rotation / 360.0);
+	print_bar("slider:", ctx->slider, (ctx->slider + 1.0) / 2.0);
+	print_buttons(ctx, ctx->buttons_down, ARRAY_LENGTH(ctx->buttons_down));
 	lines_printed += 11;
 
 	printf("evdev:\n");
 	print_bar("ABS_X:", ctx->abs.x, normalize(ctx->evdev, ABS_X, ctx->abs.x));
 	print_bar("ABS_Y:", ctx->abs.y, normalize(ctx->evdev, ABS_Y, ctx->abs.y));
 	print_bar("ABS_Z:", ctx->abs.z, normalize(ctx->evdev, ABS_Z, ctx->abs.z));
-	print_bar("ABS_TILT_X:", ctx->abs.tilt_x, normalize(ctx->evdev, ABS_TILT_X, ctx->abs.tilt_x));
-	print_bar("ABS_TILT_Y:", ctx->abs.tilt_y, normalize(ctx->evdev, ABS_TILT_Y, ctx->abs.tilt_y));
-	print_bar("ABS_DISTANCE:", ctx->abs.distance, normalize(ctx->evdev, ABS_DISTANCE, ctx->abs.distance));
-	print_bar("ABS_PRESSURE:", ctx->abs.pressure, normalize(ctx->evdev, ABS_PRESSURE, ctx->abs.pressure));
+	print_bar("ABS_TILT_X:",
+		  ctx->abs.tilt_x,
+		  normalize(ctx->evdev, ABS_TILT_X, ctx->abs.tilt_x));
+	print_bar("ABS_TILT_Y:",
+		  ctx->abs.tilt_y,
+		  normalize(ctx->evdev, ABS_TILT_Y, ctx->abs.tilt_y));
+	print_bar("ABS_DISTANCE:",
+		  ctx->abs.distance,
+		  normalize(ctx->evdev, ABS_DISTANCE, ctx->abs.distance));
+	print_bar("ABS_PRESSURE:",
+		  ctx->abs.pressure,
+		  normalize(ctx->evdev, ABS_PRESSURE, ctx->abs.pressure));
 	print_buttons(ctx,
 		      ctx->evdev_buttons_down,
 		      ARRAY_LENGTH(ctx->evdev_buttons_down));
@@ -262,7 +268,7 @@ handle_device_added(struct context *ctx,
 
 	devnode = udev_device_get_devnode(udev_device);
 	if (devnode) {
-		int fd = open(devnode, O_RDONLY|O_NONBLOCK);
+		int fd = open(devnode, O_RDONLY | O_NONBLOCK);
 		assert(fd != -1);
 		assert(libevdev_new_from_fd(fd, &ctx->evdev) == 0);
 	}
@@ -308,14 +314,15 @@ handle_tablet_button_event(struct contex
 {
 	struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(ev);
 	unsigned int button = libinput_event_tablet_tool_get_button(t);
-	enum libinput_button_state state = libinput_event_tablet_tool_get_button_state(t);
+	enum libinput_button_state state =
+		libinput_event_tablet_tool_get_button_state(t);
 
 	ARRAY_FOR_EACH(ctx->buttons_down, btn) {
 		if (state == LIBINPUT_BUTTON_STATE_PRESSED) {
-		    if (*btn == 0) {
+			if (*btn == 0) {
 				*btn = button;
 				break;
-		    }
+			}
 		} else {
 			if (*btn == button) {
 				*btn = 0;
@@ -344,7 +351,8 @@ handle_tablet_proximity_event(struct con
 		ctx->tool = NULL;
 	}
 
-	if (libinput_event_tablet_tool_get_proximity_state(t) == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN)
+	if (libinput_event_tablet_tool_get_proximity_state(t) ==
+	    LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN)
 		ctx->tool = libinput_tablet_tool_ref(tool);
 }
 
@@ -353,7 +361,8 @@ handle_tablet_tip_event(struct context *
 {
 	struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(ev);
 
-	ctx->tip_is_down = libinput_event_tablet_tool_get_tip_state(t) == LIBINPUT_TABLET_TOOL_TIP_DOWN;
+	ctx->tip_is_down = libinput_event_tablet_tool_get_tip_state(t) ==
+			   LIBINPUT_TABLET_TOOL_TIP_DOWN;
 }
 
 static void
@@ -412,10 +421,9 @@ handle_libevdev_events(struct context *c
 	if (!evdev)
 		return;
 
-	while (libevdev_next_event(evdev, LIBEVDEV_READ_FLAG_NORMAL, &event)
-	       == LIBEVDEV_READ_STATUS_SUCCESS)
-	{
-		switch(evbit(event.type, event.code)) {
+	while (libevdev_next_event(evdev, LIBEVDEV_READ_FLAG_NORMAL, &event) ==
+	       LIBEVDEV_READ_STATUS_SUCCESS) {
+		switch (evbit(event.type, event.code)) {
 		case evbit(EV_KEY, BTN_TOOL_PEN):
 		case evbit(EV_KEY, BTN_TOOL_RUBBER):
 		case evbit(EV_KEY, BTN_TOOL_BRUSH):
@@ -423,7 +431,8 @@ handle_libevdev_events(struct context *c
 		case evbit(EV_KEY, BTN_TOOL_AIRBRUSH):
 		case evbit(EV_KEY, BTN_TOOL_MOUSE):
 		case evbit(EV_KEY, BTN_TOOL_LENS):
-			ctx->evdev_buttons_down[event.code - BTN_TOOL_PEN] = event.value ?  event.code : 0;
+			ctx->evdev_buttons_down[event.code - BTN_TOOL_PEN] =
+				event.value ? event.code : 0;
 			break;
 		/* above tools should be mutually exclusive but let's leave
 		 * enough space */
@@ -494,7 +503,8 @@ mainloop(struct context *ctx)
 }
 
 static void
-usage(void) {
+usage(void)
+{
 	printf("Usage: libinput debug-tablet [options] [--udev <seat>|--device /dev/input/event0]\n");
 }
 
@@ -518,7 +528,7 @@ main(int argc, char **argv)
 	struct context ctx;
 	struct libinput *li;
 	enum tools_backend backend = BACKEND_NONE;
-	const char *seat_or_device[2] = {"seat0", NULL};
+	const char *seat_or_device[2] = { "seat0", NULL };
 	struct sigaction act;
 	bool grab = false;
 
@@ -535,17 +545,17 @@ main(int argc, char **argv)
 		};
 		static struct option opts[] = {
 			CONFIGURATION_OPTIONS,
-			{ "help",                      no_argument,       0, 'h' },
-			{ "device",                    required_argument, 0, OPT_DEVICE },
-			{ "udev",                      required_argument, 0, OPT_UDEV },
-			{ 0, 0, 0, 0}
+			{ "help", no_argument, 0, 'h' },
+			{ "device", required_argument, 0, OPT_DEVICE },
+			{ "udev", required_argument, 0, OPT_UDEV },
+			{ 0, 0, 0, 0 }
 		};
 
 		c = getopt_long(argc, argv, "h", opts, &option_index);
 		if (c == -1)
 			break;
 
-		switch(c) {
+		switch (c) {
 		case '?':
 			exit(EXIT_INVALID_USAGE);
 			break;
@@ -568,7 +578,6 @@ main(int argc, char **argv)
 			}
 			break;
 		}
-
 	}
 
 	if (optind < argc) {
@@ -587,12 +596,19 @@ main(int argc, char **argv)
 	act.sa_flags = SA_SIGINFO;
 
 	if (sigaction(SIGINT, &act, NULL) == -1) {
-		fprintf(stderr, "Failed to set up signal handling (%s)\n",
-				strerror(errno));
+		fprintf(stderr,
+			"Failed to set up signal handling (%s)\n",
+			strerror(errno));
 		return EXIT_FAILURE;
 	}
 
-	li = tools_open_backend(backend, seat_or_device, false, &grab);
+	bool with_plugins = (options.plugins == 1);
+	li = tools_open_backend(backend,
+				seat_or_device,
+				false,
+				&grab,
+				with_plugins,
+				steal(&options.plugin_paths));
 	if (!li)
 		return EXIT_FAILURE;
 
diff -pruN 1.28.1-1/tools/libinput-list-devices.c 1.30.0-1/tools/libinput-list-devices.c
--- 1.28.1-1/tools/libinput-list-devices.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-list-devices.c	2025-11-25 03:40:43.000000000 +0000
@@ -25,15 +25,15 @@
 
 #include <errno.h>
 #include <getopt.h>
+#include <libevdev/libevdev.h>
+#include <libinput-version.h>
+#include <libinput.h>
+#include <libudev.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
-#include <libudev.h>
-#include <libevdev/libevdev.h>
 
-#include <libinput.h>
-#include <libinput-version.h>
 #include "util-strings.h"
 
 #include "shared.h"
@@ -57,8 +57,10 @@ tap_button_map(struct libinput_device *d
 		return "n/a";
 
 	switch (libinput_device_config_tap_get_button_map(device)) {
-	case LIBINPUT_CONFIG_TAP_MAP_LRM: return "left/right/middle";
-	case LIBINPUT_CONFIG_TAP_MAP_LMR: return "left/middle/right";
+	case LIBINPUT_CONFIG_TAP_MAP_LRM:
+		return "left/right/middle";
+	case LIBINPUT_CONFIG_TAP_MAP_LMR:
+		return "left/middle/right";
 	}
 
 	return "<invalid value>";
@@ -88,7 +90,7 @@ draglock_default(struct libinput_device
 	return "disabled";
 }
 
-static const char*
+static const char *
 left_handed_default(struct libinput_device *device)
 {
 	if (!libinput_device_config_left_handed_is_available(device))
@@ -136,19 +138,19 @@ calibration_default(struct libinput_devi
 	}
 
 	if (libinput_device_config_calibration_get_default_matrix(device,
-						  calibration) == 0) {
+								  calibration) == 0) {
 		xasprintf(&str, "identity matrix");
 		return str;
 	}
 
 	xasprintf(&str,
-		 "%.2f %.2f %.2f %.2f %.2f %.2f",
-		 calibration[0],
-		 calibration[1],
-		 calibration[2],
-		 calibration[3],
-		 calibration[4],
-		 calibration[5]);
+		  "%.2f %.2f %.2f %.2f %.2f %.2f",
+		  calibration[0],
+		  calibration[1],
+		  calibration[2],
+		  calibration[3],
+		  calibration[4],
+		  calibration[5]);
 	return str;
 }
 
@@ -168,13 +170,14 @@ scroll_defaults(struct libinput_device *
 	method = libinput_device_config_scroll_get_default_method(device);
 
 	xasprintf(&str,
-		 "%s%s%s%s%s%s",
-		 (method == LIBINPUT_CONFIG_SCROLL_2FG) ? "*" : "",
-		 (scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG) ? "two-finger " : "",
-		 (method == LIBINPUT_CONFIG_SCROLL_EDGE) ? "*" : "",
-		 (scroll_methods & LIBINPUT_CONFIG_SCROLL_EDGE) ? "edge " : "",
-		 (method == LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) ? "*" : "",
-		 (scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) ? "button" : "");
+		  "%s%s%s%s%s%s",
+		  (method == LIBINPUT_CONFIG_SCROLL_2FG) ? "*" : "",
+		  (scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG) ? "two-finger " : "",
+		  (method == LIBINPUT_CONFIG_SCROLL_EDGE) ? "*" : "",
+		  (scroll_methods & LIBINPUT_CONFIG_SCROLL_EDGE) ? "edge " : "",
+		  (method == LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) ? "*" : "",
+		  (scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) ? "button"
+									   : "");
 	return str;
 }
 
@@ -183,7 +186,8 @@ scroll_button_default(struct libinput_de
 {
 	uint32_t scroll_methods = libinput_device_config_scroll_get_methods(device);
 	if (scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) {
-		uint32_t button = libinput_device_config_scroll_get_default_button(device);
+		uint32_t button =
+			libinput_device_config_scroll_get_default_button(device);
 		return libevdev_event_code_get_name(EV_KEY, button);
 	}
 
@@ -196,8 +200,10 @@ scroll_button_lock_default(struct libinp
 	uint32_t scroll_methods = libinput_device_config_scroll_get_methods(device);
 	if (scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) {
 		switch (libinput_device_config_scroll_get_default_button_lock(device)) {
-		case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED: return "enabled";
-		case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED: return "disabled";
+		case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED:
+			return "enabled";
+		case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED:
+			return "disabled";
 		}
 		return "<invalid value>";
 	}
@@ -205,7 +211,7 @@ scroll_button_lock_default(struct libinp
 	return "n/a";
 }
 
-static char*
+static char *
 click_defaults(struct libinput_device *device)
 {
 	uint32_t click_methods;
@@ -220,11 +226,15 @@ click_defaults(struct libinput_device *d
 
 	method = libinput_device_config_click_get_default_method(device);
 	xasprintf(&str,
-		 "%s%s%s%s",
-		 (method == LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "*" : "",
-		 (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "button-areas " : "",
-		 (method == LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) ? "*" : "",
-		 (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) ? "clickfinger " : "");
+		  "%s%s%s%s",
+		  (method == LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS) ? "*" : "",
+		  (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS)
+			  ? "button-areas "
+			  : "",
+		  (method == LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) ? "*" : "",
+		  (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER)
+			  ? "clickfinger "
+			  : "");
 	return str;
 }
 
@@ -233,9 +243,12 @@ clickfinger_button_map(struct libinput_d
 {
 	uint32_t click_methods = libinput_device_config_click_get_methods(device);
 	if (click_methods & LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER) {
-		switch (libinput_device_config_click_get_default_clickfinger_button_map(device)) {
-		case LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR: return "left/middle/right";
-		case LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM: return "left/right/middle";
+		switch (libinput_device_config_click_get_default_clickfinger_button_map(
+			device)) {
+		case LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR:
+			return "left/middle/right";
+		case LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM:
+			return "left/right/middle";
 		}
 		return "<invalid value>";
 	} else {
@@ -244,7 +257,7 @@ clickfinger_button_map(struct libinput_d
 	}
 }
 
-static char*
+static char *
 accel_profiles(struct libinput_device *device)
 {
 	uint32_t profiles;
@@ -323,8 +336,12 @@ area_rectangle(struct libinput_device *d
 			libinput_device_config_area_get_default_rectangle(device);
 
 		char *str;
-		xasprintf(&str, "(%.2f, %.2f) - (%.2f, %.2f)",
-			  rect.x1, rect.y1, rect.x2, rect.y2);
+		xasprintf(&str,
+			  "(%.2f, %.2f) - (%.2f, %.2f)",
+			  rect.x1,
+			  rect.y1,
+			  rect.x2,
+			  rect.y2);
 		return str;
 	}
 
@@ -358,10 +375,14 @@ print_pad_info(struct libinput_device *d
 			printf("            Buttons:");
 			for (int b = 0; b < nbuttons; b++) {
 				if (libinput_tablet_pad_mode_group_has_button(group, b))
-				    printf("%s%s%d",
-					   b == 0 ? " " : ", ",
-					   libinput_tablet_pad_mode_group_button_is_toggle(group, b) ? "*" : "",
-					   b);
+					printf("%s%s%d",
+					       b == 0 ? " " : ", ",
+					       libinput_tablet_pad_mode_group_button_is_toggle(
+						       group,
+						       b)
+						       ? "*"
+						       : "",
+					       b);
 			}
 			printf("\n");
 		}
@@ -369,7 +390,7 @@ print_pad_info(struct libinput_device *d
 			printf("            Rings:");
 			for (int r = 0; r < nrings; r++) {
 				if (libinput_tablet_pad_mode_group_has_ring(group, r))
-				    printf("%s%d", r == 0 ? " " : ", ", r);
+					printf("%s%d", r == 0 ? " " : ", ", r);
 			}
 			printf("\n");
 		}
@@ -377,7 +398,7 @@ print_pad_info(struct libinput_device *d
 			printf("            Strips:");
 			for (int s = 0; s < nstrips; s++) {
 				if (libinput_tablet_pad_mode_group_has_strip(group, s))
-				    printf("%s%d", s == 0 ? " " : ", ", s);
+					printf("%s%d", s == 0 ? " " : ", ", s);
 			}
 			printf("\n");
 		}
@@ -385,12 +406,11 @@ print_pad_info(struct libinput_device *d
 			printf("            Dials:");
 			for (int d = 0; d < ndials; d++) {
 				if (libinput_tablet_pad_mode_group_has_dial(group, d))
-				    printf("%s%d", d == 0 ? " " : ", ", d);
+					printf("%s%d", d == 0 ? " " : ", ", d);
 			}
 			printf("\n");
 		}
 	}
-
 }
 
 #define print_aligned(topic, fmt, ...) do {\
@@ -415,7 +435,7 @@ print_device_notify(struct libinput_even
 	group_id = (intptr_t)libinput_device_group_get_user_data(group);
 	if (!group_id) {
 		group_id = ++next_group_id;
-		libinput_device_group_set_user_data(group, (void*)group_id);
+		libinput_device_group_set_user_data(group, (void *)group_id);
 	}
 
 	udev_device = libinput_device_get_udev_device(dev);
@@ -425,19 +445,32 @@ print_device_notify(struct libinput_even
 	print_aligned("Kernel", "%s", devnode);
 
 	switch (libinput_device_get_id_bustype(dev)) {
-	case BUS_USB: bustype = "usb"; break;
-	case BUS_BLUETOOTH: bustype = "bluetooth"; break;
-	case BUS_VIRTUAL: bustype = "virtual"; break;
-	case BUS_I2C: bustype = "i2c"; break;
-	case BUS_HOST: bustype = "host"; break;
-	case BUS_I8042: bustype = "serial"; break;
+	case BUS_USB:
+		bustype = "usb";
+		break;
+	case BUS_BLUETOOTH:
+		bustype = "bluetooth";
+		break;
+	case BUS_VIRTUAL:
+		bustype = "virtual";
+		break;
+	case BUS_I2C:
+		bustype = "i2c";
+		break;
+	case BUS_HOST:
+		bustype = "host";
+		break;
+	case BUS_I8042:
+		bustype = "serial";
+		break;
 	}
-	print_aligned("Id", "%s:%04x:%04x",
+	print_aligned("Id",
+		      "%s:%04x:%04x",
 		      bustype,
 		      libinput_device_get_id_vendor(dev),
 		      libinput_device_get_id_product(dev));
 
-	print_aligned("Group" , "%d", (int)group_id);
+	print_aligned("Group", "%d", (int)group_id);
 	print_aligned("Seat",
 		      "%s, %s",
 		      libinput_seat_get_physical_name(seat),
@@ -448,14 +481,30 @@ print_device_notify(struct libinput_even
 	if (libinput_device_get_size(dev, &w, &h) == 0)
 		print_aligned("Size", "%.fx%.fmm", w, h);
 
-	print_aligned("Capabilities", "%s%s%s%s%s%s%s",
-		      libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_KEYBOARD) ? "keyboard " : "",
-		      libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_POINTER) ? "pointer " : "",
-		      libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TOUCH) ? "touch " : "",
-		      libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TABLET_TOOL) ? "tablet " : "",
-		      libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TABLET_PAD) ? "tablet-pad" : "",
-		      libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_GESTURE) ? "gesture" : "",
-		      libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_SWITCH) ? "switch" : "");
+	print_aligned(
+		"Capabilities",
+		"%s%s%s%s%s%s%s",
+		libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_KEYBOARD)
+			? "keyboard "
+			: "",
+		libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_POINTER)
+			? "pointer "
+			: "",
+		libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TOUCH)
+			? "touch "
+			: "",
+		libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TABLET_TOOL)
+			? "tablet "
+			: "",
+		libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TABLET_PAD)
+			? "tablet-pad"
+			: "",
+		libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_GESTURE)
+			? "gesture"
+			: "",
+		libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_SWITCH)
+			? "switch"
+			: "");
 
 	print_aligned("Tap-to-click", "%s", tap_default(dev));
 	print_aligned("Tap-and-drag", "%s", drag_default(dev));
@@ -493,11 +542,10 @@ print_device_notify(struct libinput_even
 	free(str);
 
 	str = area_rectangle(dev);
-	print_aligned("Area rectangle", "%s",  str);
+	print_aligned("Area rectangle", "%s", str);
 	free(str);
 
-	if (libinput_device_has_capability(dev,
-					   LIBINPUT_DEVICE_CAP_TABLET_PAD))
+	if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TABLET_PAD))
 		print_pad_info(dev);
 
 	printf("\n");
@@ -529,15 +577,15 @@ main(int argc, char **argv)
 		};
 		static struct option opts[] = {
 			CONFIGURATION_OPTIONS,
-			{ "help",                      no_argument,       0, 'h' },
-			{ "verbose",                   no_argument,       0, OPT_VERBOSE },
-			{ 0, 0, 0, 0}
+			{ "help", no_argument, 0, 'h' },
+			{ "verbose", no_argument, 0, OPT_VERBOSE },
+			{ 0, 0, 0, 0 }
 		};
 		c = getopt_long(argc, argv, "h", opts, &option_index);
 		if (c == -1)
 			break;
 
-		switch(c) {
+		switch (c) {
 		case '?':
 			return EXIT_INVALID_USAGE;
 		case 'h':
@@ -547,10 +595,9 @@ main(int argc, char **argv)
 		default:
 			return EXIT_INVALID_USAGE;
 		}
-
 	}
 	if (optind < argc) {
-		const char *devices[32] = {NULL};
+		const char *devices[32] = { NULL };
 		size_t ndevices = 0;
 		do {
 			if (ndevices >= ARRAY_LENGTH(devices) - 1) {
@@ -559,10 +606,15 @@ main(int argc, char **argv)
 			}
 			devices[ndevices++] = argv[optind];
 		} while (++optind < argc);
-		li = tools_open_backend(BACKEND_DEVICE, devices, false, &grab);
+		li = tools_open_backend(BACKEND_DEVICE,
+					devices,
+					false,
+					&grab,
+					false,
+					NULL);
 	} else {
-		const char *seat[2] = {"seat0", NULL};
-		li = tools_open_backend(BACKEND_UDEV, seat, false, &grab);
+		const char *seat[2] = { "seat0", NULL };
+		li = tools_open_backend(BACKEND_UDEV, seat, false, &grab, false, NULL);
 	}
 	if (!li)
 		return 1;
diff -pruN 1.28.1-1/tools/libinput-measure-touchpad-pressure.man 1.30.0-1/tools/libinput-measure-touchpad-pressure.man
--- 1.28.1-1/tools/libinput-measure-touchpad-pressure.man	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-measure-touchpad-pressure.man	2025-11-25 03:40:43.000000000 +0000
@@ -16,9 +16,9 @@ pressure\-related bug report.
 .PP
 For a full description on how libinput's pressure-to-click behavior works, see
 the online documentation here:
-.I https://wayland.freedesktop.org/libinput/doc/latest/touchpad_pressure.html
+.I https://wayland.freedesktop.org/libinput/doc/latest/touchpad-pressure.html
 and
-.I https://wayland.freedesktop.org/libinput/doc/latest/palm_detection.html
+.I https://wayland.freedesktop.org/libinput/doc/latest/palm-detection.html
 .PP
 This is a debugging tool only, its output may change at any time. Do not
 rely on the output.
diff -pruN 1.28.1-1/tools/libinput-measure-touchpad-tap.py 1.30.0-1/tools/libinput-measure-touchpad-tap.py
--- 1.28.1-1/tools/libinput-measure-touchpad-tap.py	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-measure-touchpad-tap.py	2025-11-25 03:40:43.000000000 +0000
@@ -145,8 +145,7 @@ class Device(libevdev.Device):
             if not self.warned:
                 self.warned = True
                 error(
-                    "\rThis tool cannot handle multiple fingers, "
-                    "output will be invalid"
+                    "\rThis tool cannot handle multiple fingers, output will be invalid"
                 )
                 return
 
diff -pruN 1.28.1-1/tools/libinput-measure.c 1.30.0-1/tools/libinput-measure.c
--- 1.28.1-1/tools/libinput-measure.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-measure.c	2025-11-25 03:40:43.000000000 +0000
@@ -42,15 +42,15 @@ main(int argc, char **argv)
 	while (1) {
 		int c;
 		static struct option opts[] = {
-			{ "help",	no_argument,	0, 'h' },
-			{ 0, 0, 0, 0}
+			{ "help", no_argument, 0, 'h' },
+			{ 0, 0, 0, 0 },
 		};
 
 		c = getopt_long(argc, argv, "+h", opts, &option_index);
 		if (c == -1)
 			break;
 
-		switch(c) {
+		switch (c) {
 		case 'h':
 			usage();
 			return EXIT_SUCCESS;
diff -pruN 1.28.1-1/tools/libinput-quirks.c 1.30.0-1/tools/libinput-quirks.c
--- 1.28.1-1/tools/libinput-quirks.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-quirks.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,15 +23,18 @@
 
 #include "config.h"
 
-#include <stdio.h>
-#include <stdlib.h>
 #include <errno.h>
 #include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <sys/stat.h>
 
+#include "util-mem.h"
+
+#include "builddir.h"
+#include "libinput-util.h"
 #include "quirks.h"
 #include "shared.h"
-#include "builddir.h"
 
 static bool verbose = false;
 
@@ -44,7 +47,7 @@ log_handler(struct libinput *this_is_nul
 {
 	FILE *out = stdout;
 	enum quirks_log_priorities p = (enum quirks_log_priorities)priority;
-	char buf[256] = {0};
+	char buf[256] = { 0 };
 	const char *prefix = NULL;
 
 	switch (p) {
@@ -94,13 +97,7 @@ simple_printf(void *userdata, const char
 int
 main(int argc, char **argv)
 {
-	struct udev *udev = NULL;
-	struct udev_device *device = NULL;
-	const char *path;
-	const char *data_path = NULL,
-	           *override_file = NULL;
-	int rc = 1;
-	struct quirks_context *quirks;
+	const char *data_path = NULL, *override_file = NULL;
 	bool validate = false;
 
 	while (1) {
@@ -111,17 +108,17 @@ main(int argc, char **argv)
 			OPT_DATADIR,
 		};
 		static struct option opts[] = {
-			{ "help",     no_argument,       0, 'h' },
-			{ "verbose",  no_argument,       0, OPT_VERBOSE },
+			{ "help", no_argument, 0, 'h' },
+			{ "verbose", no_argument, 0, OPT_VERBOSE },
 			{ "data-dir", required_argument, 0, OPT_DATADIR },
-			{ 0, 0, 0, 0}
+			{ 0, 0, 0, 0 }
 		};
 
 		c = getopt_long(argc, argv, "h", opts, &option_index);
 		if (c == -1)
 			break;
 
-		switch(c) {
+		switch (c) {
 		case '?':
 			exit(1);
 			break;
@@ -137,46 +134,45 @@ main(int argc, char **argv)
 			break;
 		default:
 			usage();
-			return 1;
+			return EXIT_FAILURE;
 		}
 	}
 
 	if (optind >= argc) {
 		usage();
-		return 1;
+		return EXIT_FAILURE;
 	}
 
 	if (streq(argv[optind], "list")) {
 		optind++;
 		if (optind >= argc) {
 			usage();
-			return 1;
+			return EXIT_FAILURE;
 		}
 	} else if (streq(argv[optind], "validate")) {
 		optind++;
 		if (optind < argc) {
 			usage();
-			return 1;
+			return EXIT_FAILURE;
 		}
 		validate = true;
 	} else {
 		fprintf(stderr, "Unnkown action '%s'\n", argv[optind]);
-		return 1;
+		return EXIT_FAILURE;
 	}
 
 	/* Overriding the data dir means no custom override file */
 	if (!data_path) {
-		char *builddir = builddir_lookup();
-		if (builddir) {
+		if (builddir_lookup(NULL)) {
 			data_path = LIBINPUT_QUIRKS_SRCDIR;
-			free(builddir);
 		} else {
 			data_path = LIBINPUT_QUIRKS_DIR;
 			override_file = LIBINPUT_QUIRKS_OVERRIDE_FILE;
 		}
 	}
 
-	quirks = quirks_init_subsystem(data_path,
+	_unref_(quirks_context) *quirks =
+		quirks_init_subsystem(data_path,
 				      override_file,
 				      log_handler,
 				      NULL,
@@ -186,43 +182,34 @@ main(int argc, char **argv)
 			"Failed to initialize the device quirks. "
 			"Please see the above errors "
 			"and/or re-run with --verbose for more details\n");
-		return 1;
+		return EXIT_FAILURE;
 	}
 
-	if (validate) {
-		rc = 0;
-		goto out;
-	}
+	if (validate)
+		return EXIT_SUCCESS;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	if (!udev)
-		goto out;
+		return EXIT_FAILURE;
 
-	path = argv[optind];
+	_unref_(udev_device) *device = NULL;
+	const char *path = argv[optind];
 	if (strstartswith(path, "/sys/")) {
 		device = udev_device_new_from_syspath(udev, path);
 	} else {
 		struct stat st;
 		if (stat(path, &st) < 0) {
 			fprintf(stderr, "Error: %s: %m\n", path);
-			goto out;
+			return EXIT_FAILURE;
 		}
 
 		device = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
 	}
 	if (device) {
 		tools_list_device_quirks(quirks, device, simple_printf, NULL);
-		rc = 0;
+		return EXIT_SUCCESS;
 	} else {
 		usage();
-		rc = 1;
+		return EXIT_FAILURE;
 	}
-
-	udev_device_unref(device);
-out:
-	udev_unref(udev);
-
-	quirks_context_unref(quirks);
-
-	return rc;
 }
diff -pruN 1.28.1-1/tools/libinput-record.c 1.30.0-1/tools/libinput-record.c
--- 1.28.1-1/tools/libinput-record.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-record.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,37 +23,42 @@
 
 #include "config.h"
 
+#include <dirent.h>
 #include <errno.h>
-#include <sys/epoll.h>
+#include <fcntl.h>
+#include <getopt.h>
 #include <inttypes.h>
-#include <linux/input.h>
 #include <libevdev/libevdev.h>
 #include <libudev.h>
-#include <sys/signalfd.h>
-#include <sys/timerfd.h>
-#include <sys/utsname.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <getopt.h>
+#include <linux/input.h>
 #include <poll.h>
-#include <unistd.h>
 #include <signal.h>
 #include <stdbool.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/signalfd.h>
+#include <sys/stat.h>
+#include <sys/timerfd.h>
+#include <sys/utsname.h>
 #include <time.h>
+#include <unistd.h>
 
-#include "libinput-versionsort.h"
-#include "libinput-version.h"
-#include "libinput-git-version.h"
-#include "shared.h"
-#include "builddir.h"
 #include "util-bits.h"
-#include "util-list.h"
-#include "util-time.h"
+#include "util-files.h"
 #include "util-input-event.h"
+#include "util-list.h"
 #include "util-macros.h"
+#include "util-mem.h"
 #include "util-strings.h"
+#include "util-time.h"
+#include "util-udev.h"
+
+#include "builddir.h"
+#include "libinput-git-version.h"
+#include "libinput-util.h"
+#include "libinput-version.h"
+#include "libinput-versionsort.h"
+#include "shared.h"
 
 static const int FILE_VERSION_NUMBER = 1;
 
@@ -61,25 +66,25 @@ static const int FILE_VERSION_NUMBER = 1
 enum indent {
 	I_NONE = 0,
 	I_TOPLEVEL = 0,
-	I_LIBINPUT = 2,			/* nodes inside libinput: */
-	I_SYSTEM = 2,			/* nodes inside system:   */
-	I_DEVICE = 2,			/* nodes inside devices:  */
-	I_EVDEV = 4,			/* nodes inside evdev:    */
-	I_EVDEV_DATA = 6,		/* nodes below evdev:	  */
-	I_UDEV = 4,			/* nodes inside udev:     */
-	I_UDEV_DATA = 6,		/* nodes below udev:      */
-	I_QUIRKS = 4,			/* nodes inside quirks:	  */
-	I_LIBINPUTDEV = 4,		/* nodes inside libinput: (the
-					   device description */
-	I_EVENTTYPE = 4,		/* event type (evdev:, libinput:,
-					   hidraw:) */
-	I_EVENT = 6,			/* event data */
+	I_LIBINPUT = 2,    /* nodes inside libinput: */
+	I_SYSTEM = 2,      /* nodes inside system:   */
+	I_DEVICE = 2,      /* nodes inside devices:  */
+	I_EVDEV = 4,       /* nodes inside evdev:    */
+	I_EVDEV_DATA = 6,  /* nodes below evdev:	  */
+	I_UDEV = 4,        /* nodes inside udev:     */
+	I_UDEV_DATA = 6,   /* nodes below udev:      */
+	I_QUIRKS = 4,      /* nodes inside quirks:	  */
+	I_LIBINPUTDEV = 4, /* nodes inside libinput: (the
+			      device description */
+	I_EVENTTYPE = 4,   /* event type (evdev:, libinput:,
+			      hidraw:) */
+	I_EVENT = 6,       /* event data */
 };
 
 struct record_device {
 	struct record_context *ctx;
 	struct list link;
-	char *devnode;		/* device node of the source device */
+	char *devnode; /* device node of the source device */
 	struct libevdev *evdev;
 	struct libevdev *evdev_prev; /* previous value, used for EV_ABS
 					deltas */
@@ -115,8 +120,8 @@ struct record_context {
 	int ndevices;
 
 	struct {
-		char *name;		 /* file name given on cmdline */
-		char *name_with_suffix;  /* full file name with suffix */
+		char *name;             /* file name given on cmdline */
+		char *name_with_suffix; /* full file name with suffix */
 	} output_file;
 
 	struct libinput *libinput;
@@ -142,9 +147,7 @@ struct record_context {
 	(sz_) = new_size; \
 }
 
-typedef void (*source_dispatch_t)(struct record_context *ctx,
-				  int fd,
-				  void *user_data);
+typedef void (*source_dispatch_t)(struct record_context *ctx, int fd, void *user_data);
 
 struct source {
 	source_dispatch_t dispatch;
@@ -188,9 +191,7 @@ obfuscate_keycode(struct input_event *ev
  */
 LIBINPUT_ATTRIBUTE_PRINTF(3, 4)
 static void
-iprintf(FILE *fp,
-	enum indent indent,
-	const char *format, ...)
+iprintf(FILE *fp, enum indent indent, const char *format, ...)
 {
 	va_list args;
 	char fmt[1024];
@@ -228,8 +229,7 @@ time_offset(struct record_context *ctx,
 }
 
 static void
-print_evdev_event(struct record_device *dev,
-		  struct input_event *ev)
+print_evdev_event(struct record_device *dev, struct input_event *ev)
 {
 	const char *tname, *cname;
 	bool was_modified = false;
@@ -261,10 +261,10 @@ print_evdev_event(struct record_device *
 
 		snprintf(desc,
 			 sizeof(desc),
-			"------------ %s (%d) ---------- %+ldms",
-			cname,
-			ev->value,
-			dt);
+			 "------------ %s (%d) ---------- %+ldms",
+			 cname,
+			 ev->value,
+			 dt);
 	} else if (ev->type == EV_ABS) {
 		int oldval = 0;
 		enum { DELTA, SLOT_DELTA, NO_DELTA } want = DELTA;
@@ -320,7 +320,6 @@ print_evdev_event(struct record_device *
 		}
 		case NO_DELTA:
 			break;
-
 		}
 
 		delta = ev->value - oldval;
@@ -373,7 +372,7 @@ handle_evdev_frame(struct record_device
 	struct input_event e;
 
 	if (libevdev_next_event(evdev, LIBEVDEV_READ_FLAG_NORMAL, &e) !=
-		LIBEVDEV_READ_STATUS_SUCCESS)
+	    LIBEVDEV_READ_STATUS_SUCCESS)
 		return false;
 
 	iprintf(d->fp, I_EVENTTYPE, "- evdev:\n");
@@ -386,8 +385,7 @@ handle_evdev_frame(struct record_device
 
 		print_evdev_event(d, &e);
 
-		if (d->touch.is_touch_device &&
-		    e.type == EV_ABS &&
+		if (d->touch.is_touch_device && e.type == EV_ABS &&
 		    e.code == ABS_MT_TRACKING_ID) {
 			unsigned int slot = libevdev_get_current_slot(evdev);
 			assert(slot < sizeof(d->touch.slot_state) * 8);
@@ -400,16 +398,15 @@ handle_evdev_frame(struct record_device
 
 		if (e.type == EV_SYN && e.code == SYN_REPORT)
 			break;
-	} while (libevdev_next_event(evdev,
-				     LIBEVDEV_READ_FLAG_NORMAL,
-				     &e) == LIBEVDEV_READ_STATUS_SUCCESS);
+	} while (libevdev_next_event(evdev, LIBEVDEV_READ_FLAG_NORMAL, &e) ==
+		 LIBEVDEV_READ_STATUS_SUCCESS);
 
 	if (d->touch.slot_state != d->touch.last_slot_state) {
 		d->touch.last_slot_state = d->touch.slot_state;
 		if (d->touch.slot_state == 0) {
 			iprintf(d->fp,
 				I_EVENT,
-				 "                                 # Touch device in neutral state\n");
+				"                                 # Touch device in neutral state\n");
 		}
 	}
 
@@ -423,7 +420,7 @@ print_device_notify(struct record_device
 	struct libinput_seat *seat = libinput_device_get_seat(d);
 	const char *type = NULL;
 
-	switch(libinput_event_get_type(e)) {
+	switch (libinput_event_get_type(e)) {
 	case LIBINPUT_EVENT_DEVICE_ADDED:
 		type = "DEVICE_ADDED";
 		break;
@@ -451,7 +448,7 @@ print_key_event(struct record_device *de
 	uint64_t time;
 	const char *type;
 
-	switch(libinput_event_get_type(e)) {
+	switch (libinput_event_get_type(e)) {
 	case LIBINPUT_EVENT_KEYBOARD_KEY:
 		type = "KEYBOARD_KEY";
 		break;
@@ -463,8 +460,7 @@ print_key_event(struct record_device *de
 	state = libinput_event_keyboard_get_key_state(k);
 
 	key = libinput_event_keyboard_get_key(k);
-	if (!dev->ctx->show_keycodes &&
-	    (key >= KEY_ESC && key < KEY_ZENKAKUHANKAKU))
+	if (!dev->ctx->show_keycodes && (key >= KEY_ESC && key < KEY_ZENKAKUHANKAKU))
 		key = -1;
 
 	iprintf(dev->fp,
@@ -488,7 +484,7 @@ print_motion_event(struct record_device
 	uint64_t time;
 	const char *type;
 
-	switch(libinput_event_get_type(e)) {
+	switch (libinput_event_get_type(e)) {
 	case LIBINPUT_EVENT_POINTER_MOTION:
 		type = "POINTER_MOTION";
 		break;
@@ -503,8 +499,10 @@ print_motion_event(struct record_device
 		(long)(time / (int)1e6),
 		(long)(time % (int)1e6),
 		type,
-		x, y,
-		uax, uay);
+		x,
+		y,
+		uax,
+		uay);
 }
 
 static void
@@ -518,7 +516,7 @@ print_absmotion_event(struct record_devi
 	uint64_t time;
 	const char *type;
 
-	switch(libinput_event_get_type(e)) {
+	switch (libinput_event_get_type(e)) {
 	case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
 		type = "POINTER_MOTION_ABSOLUTE";
 		break;
@@ -534,8 +532,10 @@ print_absmotion_event(struct record_devi
 		(long)(time / (int)1e6),
 		(long)(time % (int)1e6),
 		type,
-		x, y,
-		tx, ty);
+		x,
+		y,
+		tx,
+		ty);
 }
 
 static void
@@ -547,7 +547,7 @@ print_pointer_button_event(struct record
 	uint64_t time;
 	const char *type;
 
-	switch(libinput_event_get_type(e)) {
+	switch (libinput_event_get_type(e)) {
 	case LIBINPUT_EVENT_POINTER_BUTTON:
 		type = "POINTER_BUTTON";
 		break;
@@ -579,7 +579,7 @@ print_pointer_axis_event(struct record_d
 	double h = 0, v = 0;
 	int hd = 0, vd = 0;
 
-	switch(libinput_event_get_type(e)) {
+	switch (libinput_event_get_type(e)) {
 	case LIBINPUT_EVENT_POINTER_AXIS:
 		type = "POINTER_AXIS";
 		break;
@@ -589,24 +589,35 @@ print_pointer_axis_event(struct record_d
 
 	time = time_offset(dev->ctx, libinput_event_pointer_get_time_usec(p));
 	if (libinput_event_pointer_has_axis(p,
-				LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
-		h = libinput_event_pointer_get_axis_value(p,
-				LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
-		hd = libinput_event_pointer_get_axis_value_discrete(p,
-				LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
-	}
-	if (libinput_event_pointer_has_axis(p,
-				LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
-		v = libinput_event_pointer_get_axis_value(p,
-				LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
-		vd = libinput_event_pointer_get_axis_value_discrete(p,
-				LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
-	}
-	switch(libinput_event_pointer_get_axis_source(p)) {
-	case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: source = "wheel"; break;
-	case LIBINPUT_POINTER_AXIS_SOURCE_FINGER: source = "finger"; break;
-	case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: source = "continuous"; break;
-	case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT: source = "wheel-tilt"; break;
+					    LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
+		h = libinput_event_pointer_get_axis_value(
+			p,
+			LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
+		hd = libinput_event_pointer_get_axis_value_discrete(
+			p,
+			LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
+	}
+	if (libinput_event_pointer_has_axis(p, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
+		v = libinput_event_pointer_get_axis_value(
+			p,
+			LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+		vd = libinput_event_pointer_get_axis_value_discrete(
+			p,
+			LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+	}
+	switch (libinput_event_pointer_get_axis_source(p)) {
+	case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
+		source = "wheel";
+		break;
+	case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
+		source = "finger";
+		break;
+	case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
+		source = "continuous";
+		break;
+	case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT:
+		source = "wheel-tilt";
+		break;
 	default:
 		source = "unknown";
 		break;
@@ -618,8 +629,10 @@ print_pointer_axis_event(struct record_d
 		(long)(time / (int)1e6),
 		(long)(time % (int)1e6),
 		type,
-		h, v,
-		hd, vd,
+		h,
+		v,
+		hd,
+		vd,
 		source);
 }
 
@@ -634,7 +647,7 @@ print_touch_event(struct record_device *
 	uint64_t time;
 	int32_t slot, seat_slot;
 
-	switch(etype) {
+	switch (etype) {
 	case LIBINPUT_EVENT_TOUCH_DOWN:
 		type = "TOUCH_DOWN";
 		break;
@@ -685,8 +698,10 @@ print_touch_event(struct record_device *
 			type,
 			slot,
 			seat_slot,
-			x, y,
-			tx, ty);
+			x,
+			y,
+			tx,
+			ty);
 		break;
 	case LIBINPUT_EVENT_TOUCH_UP:
 	case LIBINPUT_EVENT_TOUCH_CANCEL:
@@ -712,7 +727,7 @@ print_gesture_event(struct record_device
 	const char *type;
 	uint64_t time;
 
-	switch(etype) {
+	switch (etype) {
 	case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
 		type = "GESTURE_PINCH_BEGIN";
 		break;
@@ -755,8 +770,7 @@ print_gesture_event(struct record_device
 			libinput_event_gesture_get_dx_unaccelerated(g),
 			libinput_event_gesture_get_dy_unaccelerated(g),
 			libinput_event_gesture_get_angle_delta(g),
-			libinput_event_gesture_get_scale(g)
-		       );
+			libinput_event_gesture_get_scale(g));
 		break;
 	case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
 	case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
@@ -772,8 +786,7 @@ print_gesture_event(struct record_device
 			libinput_event_gesture_get_dx(g),
 			libinput_event_gesture_get_dy(g),
 			libinput_event_gesture_get_dx_unaccelerated(g),
-			libinput_event_gesture_get_dy_unaccelerated(g)
-		       );
+			libinput_event_gesture_get_dy_unaccelerated(g));
 		break;
 	default:
 		abort();
@@ -785,28 +798,26 @@ buffer_tablet_axes(struct libinput_event
 {
 	const int MAX_AXES = 10;
 	struct libinput_tablet_tool *tool;
-	char *s = NULL;
 	int idx = 0;
 	int len;
 	double x, y;
-	char **strv;
 
 	tool = libinput_event_tablet_tool_get_tool(t);
 
-	strv = zalloc(MAX_AXES * sizeof *strv);
+	_autostrvfree_ char **strv = zalloc(MAX_AXES * sizeof *strv);
 
 	x = libinput_event_tablet_tool_get_x(t);
 	y = libinput_event_tablet_tool_get_y(t);
 	len = xasprintf(&strv[idx++], "point: [%.2f, %.2f]", x, y);
 	if (len <= 0)
-		goto out;
+		return NULL;
 
 	if (libinput_tablet_tool_has_tilt(tool)) {
 		x = libinput_event_tablet_tool_get_tilt_x(t);
 		y = libinput_event_tablet_tool_get_tilt_y(t);
 		len = xasprintf(&strv[idx++], "tilt: [%.2f, %.2f]", x, y);
 		if (len <= 0)
-			goto out;
+			return NULL;
 	}
 
 	if (libinput_tablet_tool_has_distance(tool) ||
@@ -820,7 +831,7 @@ buffer_tablet_axes(struct libinput_event
 		else
 			len = xasprintf(&strv[idx++], "pressure: %.2f", pressure);
 		if (len <= 0)
-			goto out;
+			return NULL;
 	}
 
 	if (libinput_tablet_tool_has_rotation(tool)) {
@@ -829,7 +840,7 @@ buffer_tablet_axes(struct libinput_event
 		rotation = libinput_event_tablet_tool_get_rotation(t);
 		len = xasprintf(&strv[idx++], "rotation: %.2f", rotation);
 		if (len <= 0)
-			goto out;
+			return NULL;
 	}
 
 	if (libinput_tablet_tool_has_slider(tool)) {
@@ -838,8 +849,7 @@ buffer_tablet_axes(struct libinput_event
 		slider = libinput_event_tablet_tool_get_slider_position(t);
 		len = xasprintf(&strv[idx++], "slider: %.2f", slider);
 		if (len <= 0)
-			goto out;
-
+			return NULL;
 	}
 
 	if (libinput_tablet_tool_has_wheel(tool)) {
@@ -849,33 +859,27 @@ buffer_tablet_axes(struct libinput_event
 		wheel = libinput_event_tablet_tool_get_wheel_delta(t);
 		len = xasprintf(&strv[idx++], "wheel: %.2f", wheel);
 		if (len <= 0)
-			goto out;
+			return NULL;
 
 		delta = libinput_event_tablet_tool_get_wheel_delta_discrete(t);
 		len = xasprintf(&strv[idx++], "wheel-discrete: %d", delta);
 		if (len <= 0)
-			goto out;
+			return NULL;
 	}
 
 	assert(idx < MAX_AXES);
 
-	s = strv_join(strv, ", ");
-out:
-	strv_free(strv);
-	return s;
+	return strv_join(strv, ", ");
 }
 
 static void
 print_tablet_tool_proximity_event(struct record_device *dev, struct libinput_event *e)
 {
-	struct libinput_event_tablet_tool *t =
-		libinput_event_get_tablet_tool_event(e);
-	struct libinput_tablet_tool *tool =
-		libinput_event_tablet_tool_get_tool(t);
+	struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(e);
+	struct libinput_tablet_tool *tool = libinput_event_tablet_tool_get_tool(t);
 	uint64_t time;
 	const char *type, *tool_type;
-	char *axes;
-	char caps[10] = {0};
+	char caps[10] = { 0 };
 	enum libinput_tablet_tool_proximity_state prox;
 	size_t idx;
 
@@ -916,7 +920,7 @@ print_tablet_tool_proximity_event(struct
 
 	prox = libinput_event_tablet_tool_get_proximity_state(t);
 	time = time_offset(dev->ctx, libinput_event_tablet_tool_get_time_usec(t));
-	axes = buffer_tablet_axes(t);
+	_autofree_ char *axes = buffer_tablet_axes(t);
 
 	idx = 0;
 	if (libinput_tablet_tool_has_pressure(tool))
@@ -935,7 +939,8 @@ print_tablet_tool_proximity_event(struct
 
 	iprintf(dev->fp,
 		I_EVENT,
-		"- {time: %ld.%06ld, type: %s, proximity: %s, tool-type: %s, serial: %" PRIu64 ", axes: %s, %s}\n",
+		"- {time: %ld.%06ld, type: %s, proximity: %s, tool-type: %s, serial: %" PRIu64
+		", axes: %s, %s}\n",
 		(long)(time / (int)1e6),
 		(long)(time % (int)1e6),
 		type,
@@ -944,21 +949,18 @@ print_tablet_tool_proximity_event(struct
 		libinput_tablet_tool_get_serial(tool),
 		caps,
 		axes);
-	free(axes);
 }
 
 static void
-print_tablet_tool_button_event(struct record_device *dev,
-			       struct libinput_event *e)
+print_tablet_tool_button_event(struct record_device *dev, struct libinput_event *e)
 {
-	struct libinput_event_tablet_tool *t =
-		libinput_event_get_tablet_tool_event(e);
+	struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(e);
 	uint64_t time;
 	const char *type;
 	uint32_t button;
 	enum libinput_button_state state;
 
-	switch(libinput_event_get_type(e)) {
+	switch (libinput_event_get_type(e)) {
 	case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
 		type = "TABLET_TOOL_BUTTON";
 		break;
@@ -983,15 +985,13 @@ print_tablet_tool_button_event(struct re
 static void
 print_tablet_tool_event(struct record_device *dev, struct libinput_event *e)
 {
-	struct libinput_event_tablet_tool *t =
-		libinput_event_get_tablet_tool_event(e);
+	struct libinput_event_tablet_tool *t = libinput_event_get_tablet_tool_event(e);
 	uint64_t time;
 	const char *type;
-	char *axes;
 	enum libinput_tablet_tool_tip_state tip;
-	char btn_buffer[30] = {0};
+	char btn_buffer[30] = { 0 };
 
-	switch(libinput_event_get_type(e)) {
+	switch (libinput_event_get_type(e)) {
 	case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
 		type = "TABLET_TOOL_AXIS";
 		break;
@@ -1011,7 +1011,8 @@ print_tablet_tool_event(struct record_de
 
 		button = libinput_event_tablet_tool_get_button(t);
 		state = libinput_event_tablet_tool_get_button_state(t);
-		snprintf(btn_buffer, sizeof(btn_buffer),
+		snprintf(btn_buffer,
+			 sizeof(btn_buffer),
 			 ", button: %d, state: %s\n",
 			 button,
 			 state ? "pressed" : "released");
@@ -1019,7 +1020,7 @@ print_tablet_tool_event(struct record_de
 
 	tip = libinput_event_tablet_tool_get_tip_state(t);
 	time = time_offset(dev->ctx, libinput_event_tablet_tool_get_time_usec(t));
-	axes = buffer_tablet_axes(t);
+	_autofree_ char *axes = buffer_tablet_axes(t);
 
 	iprintf(dev->fp,
 		I_EVENT,
@@ -1030,22 +1031,19 @@ print_tablet_tool_event(struct record_de
 		btn_buffer, /* may be empty string */
 		tip ? "down" : "up",
 		axes);
-	free(axes);
 }
 
 static void
-print_tablet_pad_button_event(struct record_device *dev,
-			      struct libinput_event *e)
+print_tablet_pad_button_event(struct record_device *dev, struct libinput_event *e)
 {
-	struct libinput_event_tablet_pad *p =
-		libinput_event_get_tablet_pad_event(e);
+	struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(e);
 	struct libinput_tablet_pad_mode_group *group;
 	enum libinput_button_state state;
 	unsigned int button, mode;
 	const char *type;
 	uint64_t time;
 
-	switch(libinput_event_get_type(e)) {
+	switch (libinput_event_get_type(e)) {
 	case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
 		type = "TABLET_PAD_BUTTON";
 		break;
@@ -1068,27 +1066,25 @@ print_tablet_pad_button_event(struct rec
 		button,
 		state == LIBINPUT_BUTTON_STATE_PRESSED ? "pressed" : "released",
 		mode,
-		truefalse(libinput_tablet_pad_mode_group_button_is_toggle(group, button))
-	       );
-
+		truefalse(libinput_tablet_pad_mode_group_button_is_toggle(group,
+									  button)));
 }
 
 static void
 print_tablet_pad_ringstrip_event(struct record_device *dev, struct libinput_event *e)
 {
-	struct libinput_event_tablet_pad *p =
-		libinput_event_get_tablet_pad_event(e);
+	struct libinput_event_tablet_pad *p = libinput_event_get_tablet_pad_event(e);
 	const char *source = NULL;
 	unsigned int mode, number;
 	const char *type;
 	uint64_t time;
 	double pos;
 
-	switch(libinput_event_get_type(e)) {
+	switch (libinput_event_get_type(e)) {
 	case LIBINPUT_EVENT_TABLET_PAD_RING:
 		type = "TABLET_PAD_RING";
 		number = libinput_event_tablet_pad_get_ring_number(p);
-	        pos = libinput_event_tablet_pad_get_ring_position(p);
+		pos = libinput_event_tablet_pad_get_ring_position(p);
 
 		switch (libinput_event_tablet_pad_get_ring_source(p)) {
 		case LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER:
@@ -1102,7 +1098,7 @@ print_tablet_pad_ringstrip_event(struct
 	case LIBINPUT_EVENT_TABLET_PAD_STRIP:
 		type = "TABLET_PAD_STRIP";
 		number = libinput_event_tablet_pad_get_strip_number(p);
-	        pos = libinput_event_tablet_pad_get_strip_position(p);
+		pos = libinput_event_tablet_pad_get_strip_position(p);
 
 		switch (libinput_event_tablet_pad_get_strip_source(p)) {
 		case LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER:
@@ -1141,7 +1137,7 @@ print_switch_event(struct record_device
 	const char *type;
 	uint64_t time;
 
-	switch(libinput_event_get_type(e)) {
+	switch (libinput_event_get_type(e)) {
 	case LIBINPUT_EVENT_SWITCH_TOGGLE:
 		type = "SWITCH_TOGGLE";
 		break;
@@ -1235,7 +1231,6 @@ handle_hidraw(struct hidraw *hidraw)
 	unsigned char report[4096];
 	const char *sep = "";
 	struct timespec ts;
-	struct timeval tv;
 	uint64_t time;
 
 	int rc = read(hidraw->fd, report, sizeof(report));
@@ -1255,10 +1250,12 @@ handle_hidraw(struct hidraw *hidraw)
 	else
 		time = time_offset(d->ctx, time);
 
-	tv = us2tv(time);
-
 	iprintf(d->fp, I_EVENTTYPE, "- hid:\n");
-	iprintf(d->fp, I_EVENT, "time: [%3lu, %6lu]\n", tv.tv_sec, tv.tv_usec);
+	iprintf(d->fp,
+		I_EVENT,
+		"time: [%3" PRIu64 ", %6" PRIu64 "]\n",
+		time / s2us(1),
+		time % s2us(1));
 	iprintf(d->fp, I_EVENT, "%s: [", hidraw->name);
 
 	for (int byte = 0; byte < rc; byte++) {
@@ -1326,9 +1323,7 @@ handle_events(struct record_context *ctx
 		has_events = handle_evdev_frame(d);
 
 		if (ctx->libinput)
-			has_events |= handle_libinput_events(ctx,
-							     d,
-							     !has_events);
+			has_events |= handle_libinput_events(ctx, d, !has_events);
 	}
 
 	fflush(d->fp);
@@ -1349,17 +1344,15 @@ print_system_header(FILE *fp)
 {
 	struct utsname u;
 	const char *kernel = "unknown";
-	FILE *dmi, *osrelease;
-	char dmistr[2048] = "unknown";
 
 	iprintf(fp, I_TOPLEVEL, "system:\n");
 
 	/* /etc/os-release version and distribution name */
-	osrelease = fopen("/etc/os-release", "r");
+	_autofclose_ FILE *osrelease = fopen("/etc/os-release", "r");
 	if (!osrelease)
 		osrelease = fopen("/usr/lib/os-release", "r");
 	if (osrelease) {
-		char *distro = NULL, *version = NULL;
+		_autofree_ char *distro = NULL, *version = NULL;
 		char osrstr[256] = "unknown";
 
 		while (fgets(osrstr, sizeof(osrstr), osrelease)) {
@@ -1370,18 +1363,13 @@ print_system_header(FILE *fp)
 			else if (!version && strstartswith(osrstr, "VERSION_ID="))
 				version = strstrip(&osrstr[11], "\"'");
 
-			if (distro && version) {
-				iprintf(fp,
-					I_SYSTEM,
-					"os: \"%s:%s\"\n",
-					distro,
-					version);
+			if (distro && version)
 				break;
-			}
 		}
-		free(distro);
-		free(version);
-		fclose(osrelease);
+
+		if (distro && version) { // NOLINT: unix.Stream
+			iprintf(fp, I_SYSTEM, "os: \"%s:%s\"\n", distro, version);
+		}
 	}
 
 	/* kernel version */
@@ -1390,14 +1378,15 @@ print_system_header(FILE *fp)
 	iprintf(fp, I_SYSTEM, "kernel: \"%s\"\n", kernel);
 
 	/* dmi modalias */
-	dmi = fopen("/sys/class/dmi/id/modalias", "r");
+	_autofclose_ FILE *dmi = fopen("/sys/class/dmi/id/modalias", "r");
+	_autofree_ char *dmistr = strdup("unknown"); //
 	if (dmi) {
-		if (fgets(dmistr, sizeof(dmistr), dmi)) {
-			dmistr[strlen(dmistr) - 1] = '\0'; /* linebreak */
-		} else {
-			sprintf(dmistr, "unknown");
+		char buf[2048] = "unknown";
+		size_t n = fread(buf, sizeof(buf), 1, dmi); // NOLINT: unix.Stream
+		if (n > 0) {
+			free(dmistr);
+			dmistr = strndup(buf, n - 1);
 		}
-		fclose(dmi);
 	}
 	iprintf(fp, I_SYSTEM, "dmi: \"%s\"\n", dmistr);
 }
@@ -1413,9 +1402,7 @@ print_header(FILE *fp, struct record_con
 }
 
 static void
-print_description_abs(FILE *fp,
-		      struct libevdev *dev,
-		      unsigned int code)
+print_description_abs(FILE *fp, struct libevdev *dev, unsigned int code)
 {
 	const struct input_absinfo *abs;
 
@@ -1441,9 +1428,7 @@ print_description_state(FILE *fp,
 }
 
 static void
-print_description_codes(FILE *fp,
-			struct libevdev *dev,
-			unsigned int type)
+print_description_codes(FILE *fp, struct libevdev *dev, unsigned int type)
 {
 	int max;
 
@@ -1468,8 +1453,7 @@ print_description_codes(FILE *fp,
 			I_EVDEV,
 			"#   Event code %d (%s)\n",
 			code,
-			libevdev_event_code_get_name(type,
-						     code));
+			libevdev_event_code_get_name(type, code));
 
 		switch (type) {
 		case EV_ABS:
@@ -1528,8 +1512,8 @@ print_description(FILE *fp, struct libev
 		if (x->resolution && y->resolution) {
 			int w, h;
 
-			w = (x->maximum - x->minimum)/x->resolution;
-			h = (y->maximum - y->minimum)/y->resolution;
+			w = (x->maximum - x->minimum) / x->resolution;
+			h = (y->maximum - y->minimum) / y->resolution;
 			iprintf(fp, I_EVDEV, "# Size in mm: %dx%d\n", w, h);
 		} else {
 			iprintf(fp,
@@ -1720,36 +1704,33 @@ print_hid_report_descriptor(struct recor
 static void
 print_udev_properties(struct record_device *dev)
 {
-	struct udev *udev = NULL;
-	struct udev_device *udev_device = NULL;
-	struct udev_list_entry *entry;
 	struct stat st;
 
 	if (stat(dev->devnode, &st) < 0)
 		return;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	if (!udev)
-		goto out;
+		return;
 
-	udev_device = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
+	_unref_(udev_device) *udev_device =
+		udev_device_new_from_devnum(udev, 'c', st.st_rdev);
 	if (!udev_device)
-		goto out;
+		return;
 
 	iprintf(dev->fp, I_DEVICE, "udev:\n");
 
 	iprintf(dev->fp, I_UDEV, "properties:\n");
 
-	entry = udev_device_get_properties_list_entry(udev_device);
+	struct udev_list_entry *entry =
+		udev_device_get_properties_list_entry(udev_device);
 	while (entry) {
 		const char *key, *value;
 
 		key = udev_list_entry_get_name(entry);
 
-		if (strstartswith(key, "ID_INPUT") ||
-		    strstartswith(key, "HID_BPF") ||
-		    strstartswith(key, "LIBINPUT") ||
-		    strstartswith(key, "EVDEV_ABS") ||
+		if (strstartswith(key, "ID_INPUT") || strstartswith(key, "HID_BPF") ||
+		    strstartswith(key, "LIBINPUT") || strstartswith(key, "EVDEV_ABS") ||
 		    strstartswith(key, "MOUSE_DPI") ||
 		    strstartswith(key, "POINTINGSTICK_")) {
 			value = udev_list_entry_get_value(entry);
@@ -1759,8 +1740,7 @@ print_udev_properties(struct record_devi
 		entry = udev_list_entry_get_next(entry);
 	}
 
-	for (struct udev_device *parent = udev_device;
-	     parent;
+	for (struct udev_device *parent = udev_device; parent;
 	     parent = udev_device_get_parent(parent)) {
 		const char *driver = udev_device_get_property_value(parent, "DRIVER");
 		if (driver) {
@@ -1769,9 +1749,10 @@ print_udev_properties(struct record_devi
 		}
 	}
 
-out:
-	udev_device_unref(udev_device);
-	udev_unref(udev);
+	iprintf(dev->fp,
+		I_UDEV,
+		"virtual: %s\n",
+		udev_device_is_virtual(udev_device) ? "true" : "false");
 }
 
 static void
@@ -1793,30 +1774,25 @@ list_print(void *userdata, const char *v
 static void
 print_device_quirks(struct record_device *dev)
 {
-	struct udev *udev = NULL;
-	struct udev_device *udev_device = NULL;
 	struct stat st;
-	struct quirks_context *quirks;
 	const char *data_path = LIBINPUT_QUIRKS_DIR;
 	const char *override_file = LIBINPUT_QUIRKS_OVERRIDE_FILE;
-	char *builddir = NULL;
 
 	if (stat(dev->devnode, &st) < 0)
 		return;
 
-	if ((builddir = builddir_lookup())) {
+	if (builddir_lookup(NULL)) {
 		setenv("LIBINPUT_QUIRKS_DIR", LIBINPUT_QUIRKS_SRCDIR, 0);
 		data_path = LIBINPUT_QUIRKS_SRCDIR;
 		override_file = NULL;
 	}
 
-	free(builddir);
-
-	quirks = quirks_init_subsystem(data_path,
-				       override_file,
-				       quirks_log_handler,
-				       NULL,
-				       QLOG_CUSTOM_LOG_PRIORITIES);
+	_unref_(quirks_context) *quirks =
+		quirks_init_subsystem(data_path,
+				      override_file,
+				      quirks_log_handler,
+				      NULL,
+				      QLOG_CUSTOM_LOG_PRIORITIES);
 	if (!quirks) {
 		fprintf(stderr,
 			"Failed to load the device quirks from %s%s%s. "
@@ -1829,20 +1805,16 @@ print_device_quirks(struct record_device
 		return;
 	}
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	if (!udev)
-		goto out;
-
-	udev_device = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
-	if (!udev_device)
-		goto out;
+		return;
 
-	iprintf(dev->fp, I_DEVICE, "quirks:\n");
-	tools_list_device_quirks(quirks, udev_device, list_print, dev->fp);
-out:
-	udev_device_unref(udev_device);
-	udev_unref(udev);
-	quirks_context_unref(quirks);
+	_unref_(udev_device) *udev_device =
+		udev_device_new_from_devnum(udev, 'c', st.st_rdev);
+	if (udev_device) {
+		iprintf(dev->fp, I_DEVICE, "quirks:\n");
+		tools_list_device_quirks(quirks, udev_device, list_print, dev->fp);
+	}
 }
 
 static void
@@ -1853,14 +1825,14 @@ print_libinput_description(struct record
 	struct cap {
 		enum libinput_device_capability cap;
 		const char *name;
-	} caps[] =  {
-		{LIBINPUT_DEVICE_CAP_KEYBOARD, "keyboard"},
-		{LIBINPUT_DEVICE_CAP_POINTER, "pointer"},
-		{LIBINPUT_DEVICE_CAP_TOUCH, "touch"},
-		{LIBINPUT_DEVICE_CAP_TABLET_TOOL, "tablet"},
-		{LIBINPUT_DEVICE_CAP_TABLET_PAD, "pad"},
-		{LIBINPUT_DEVICE_CAP_GESTURE, "gesture"},
-		{LIBINPUT_DEVICE_CAP_SWITCH, "switch"},
+	} caps[] = {
+		{ LIBINPUT_DEVICE_CAP_KEYBOARD, "keyboard" },
+		{ LIBINPUT_DEVICE_CAP_POINTER, "pointer" },
+		{ LIBINPUT_DEVICE_CAP_TOUCH, "touch" },
+		{ LIBINPUT_DEVICE_CAP_TABLET_TOOL, "tablet" },
+		{ LIBINPUT_DEVICE_CAP_TABLET_PAD, "pad" },
+		{ LIBINPUT_DEVICE_CAP_GESTURE, "gesture" },
+		{ LIBINPUT_DEVICE_CAP_SWITCH, "switch" },
 	};
 	const char *sep = "";
 
@@ -1899,14 +1871,16 @@ print_device_description(struct record_d
 	print_libinput_description(dev);
 }
 
-static int is_event_node(const struct dirent *dir) {
+static int
+is_event_node(const struct dirent *dir)
+{
 	return strstartswith(dir->d_name, "event");
 }
 
 static char *
 select_device(void)
 {
-	struct dirent **namelist;
+	_autofree_ struct dirent **namelist;
 	int ndev, selected_device;
 	int rc;
 	char *device_path;
@@ -1923,23 +1897,19 @@ select_device(void)
 
 	fprintf(stderr, "%sAvailable devices:\n", prefix);
 	for (int i = 0; i < ndev; i++) {
-		struct libevdev *device;
+		_autofree_ struct dirent *entry = namelist[i];
 		char path[PATH_MAX];
-		int fd = -1;
 
-		snprintf(path,
-			 sizeof(path),
-			 "/dev/input/%s",
-			 namelist[i]->d_name);
-		fd = open(path, O_RDONLY);
+		snprintf(path, sizeof(path), "/dev/input/%s", entry->d_name);
+		_cleanup_(xclose) int fd = open(path, O_RDONLY);
 		if (fd < 0) {
 			if (errno == EACCES)
 				has_eaccess = true;
 			continue;
 		}
 
+		struct libevdev *device;
 		rc = libevdev_new_from_fd(fd, &device);
-		close(fd);
 		if (rc != 0)
 			continue;
 
@@ -1948,10 +1918,6 @@ select_device(void)
 		available_devices++;
 	}
 
-	for (int i = 0; i < ndev; i++)
-		free(namelist[i]);
-	free(namelist);
-
 	if (available_devices == 0) {
 		fprintf(stderr,
 			"No devices available.%s\n",
@@ -1975,36 +1941,27 @@ select_device(void)
 static char **
 all_devices(void)
 {
-	struct dirent **namelist;
+	_autofree_ struct dirent **namelist;
 	int ndev;
-	char **devices = NULL;
+	_autostrvfree_ char **devices = NULL;
 
 	ndev = scandir("/dev/input", &namelist, is_event_node, versionsort);
 	if (ndev <= 0)
 		return NULL;
 
-	devices = zalloc((ndev + 1)* sizeof *devices); /* NULL-terminated */
+	devices = zalloc((ndev + 1) * sizeof *devices); /* NULL-terminated */
 	for (int i = 0; i < ndev; i++) {
+		_autofree_ struct dirent *entry = namelist[i];
 		char *device_path;
 
-		int rc = xasprintf(&device_path,
-				   "/dev/input/%s",
-				   namelist[i]->d_name);
+		int rc = xasprintf(&device_path, "/dev/input/%s", entry->d_name);
 		if (rc == -1)
-			goto error;
+			return NULL;
 
 		devices[i] = device_path;
 	}
 
-	return devices;
-
-error:
-	for (int i = 0; i < ndev; i++)
-		free(namelist[i]);
-	free(namelist);
-	if (devices)
-		strv_free(devices);
-	return NULL;
+	return steal(&devices);
 }
 
 static char *
@@ -2022,11 +1979,7 @@ init_output_file(const char *file, bool
 		t = time(NULL);
 		tm = localtime(&t);
 		strftime(suffix, sizeof(suffix), "%F-%T", tm);
-		snprintf(name,
-			 sizeof(name),
-			 "%s.%s",
-			 file,
-			 suffix);
+		snprintf(name, sizeof(name), "%s.%s", file, suffix);
 	} else {
 		snprintf(name, sizeof(name), "%s", file);
 	}
@@ -2088,7 +2041,9 @@ print_wall_time(struct record_context *c
 		iprintf(d->fp,
 			I_DEVICE,
 			"# Current time is %02d:%02d:%02d\n",
-			tm.tm_hour, tm.tm_min, tm.tm_sec);
+			tm.tm_hour,
+			tm.tm_min,
+			tm.tm_sec);
 		fflush(d->fp);
 	}
 }
@@ -2114,27 +2069,24 @@ add_source(struct record_context *ctx,
 	   source_dispatch_t dispatch,
 	   void *user_data)
 {
-	struct source *source;
-	struct epoll_event ep;
-
 	assert(fd != -1);
 
-	source = zalloc(sizeof *source);
+	_autofree_ struct source *source = zalloc(sizeof *source);
 	source->dispatch = dispatch;
 	source->user_data = user_data;
 	source->fd = fd;
-	list_append(&ctx->sources, &source->link);
 
+	struct epoll_event ep;
 	memset(&ep, 0, sizeof ep);
 	ep.events = EPOLLIN;
 	ep.data.ptr = source;
 
 	if (epoll_ctl(ctx->epoll_fd, EPOLL_CTL_ADD, fd, &ep) < 0) {
-		free(source);
 		return NULL;
 	}
 
-	return source;
+	list_append(&ctx->sources, &source->link);
+	return steal(&source);
 }
 
 static void
@@ -2252,7 +2204,7 @@ mainloop(struct record_context *ctx)
 	sigfd = signalfd(-1, &mask, SFD_NONBLOCK);
 	add_source(ctx, sigfd, signalfd_dispatch, NULL);
 
-	timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC|TFD_NONBLOCK);
+	timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
 	add_source(ctx, timerfd, timefd_dispatch, NULL);
 	arm_timer(timerfd);
 
@@ -2293,7 +2245,8 @@ mainloop(struct record_context *ctx)
 				ctx->output_file.name_with_suffix);
 			break;
 		}
-		fprintf(stderr, "%sRecording to '%s'.\n",
+		fprintf(stderr,
+			"%sRecording to '%s'.\n",
 			isatty(STDERR_FILENO) ? "" : "# ",
 			ctx->output_file.name_with_suffix);
 
@@ -2338,12 +2291,10 @@ mainloop(struct record_context *ctx)
 					" ... timeout%s\n",
 					ctx->had_events ? "" : " (file is empty)");
 				break;
-
 			}
 
 			if (ctx->first_device->fp != stdout)
 				print_progress_bar();
-
 		}
 
 		if (autorestart) {
@@ -2351,7 +2302,7 @@ mainloop(struct record_context *ctx)
 				iprintf(d->fp,
 					I_NONE,
 					"# Closing after %ds inactivity",
-					ctx->timeout/1000);
+					ctx->timeout / 1000);
 			}
 		}
 
@@ -2411,24 +2362,19 @@ mainloop(struct record_context *ctx)
 static bool
 init_device(struct record_context *ctx, const char *path, bool grab)
 {
-	struct record_device *d;
-	int fd, rc;
-
-	d = zalloc(sizeof(*d));
+	_autofree_ struct record_device *d = zalloc(sizeof(*d));
 	d->ctx = ctx;
 	d->devnode = safe_strdup(path);
 
 	list_init(&d->hidraw_devices);
 
-	fd = open(d->devnode, O_RDONLY|O_NONBLOCK);
+	_cleanup_(xclose) int fd = open(d->devnode, O_RDONLY | O_NONBLOCK);
 	if (fd < 0) {
-		fprintf(stderr,
-			"Failed to open device %s (%m)\n",
-			d->devnode);
-		goto error;
+		fprintf(stderr, "Failed to open device %s (%m)\n", d->devnode);
+		return false;
 	}
 
-	rc = libevdev_new_from_fd(fd, &d->evdev);
+	int rc = libevdev_new_from_fd(fd, &d->evdev);
 	if (rc == 0)
 		rc = libevdev_new_from_fd(fd, &d->evdev_prev);
 	if (rc != 0) {
@@ -2436,17 +2382,14 @@ init_device(struct record_context *ctx,
 			"Failed to create context for %s (%s)\n",
 			d->devnode,
 			strerror(-rc));
-		goto error;
+		return false;
 	}
 
 	if (grab) {
 		rc = libevdev_grab(d->evdev, LIBEVDEV_GRAB);
 		if (rc != 0) {
-			fprintf(stderr,
-				"Grab failed on %s: %s\n",
-				path,
-				strerror(-rc));
-			goto error;
+			fprintf(stderr, "Grab failed on %s: %s\n", path, strerror(-rc));
+			return false;
 		}
 	}
 
@@ -2455,18 +2398,17 @@ init_device(struct record_context *ctx,
 	if (libevdev_get_num_slots(d->evdev) > 0)
 		d->touch.is_touch_device = true;
 
-	list_append(&ctx->devices, &d->link);
 	if (!ctx->first_device)
 		ctx->first_device = d;
+	list_take_append(&ctx->devices, d, link);
 	ctx->ndevices++;
 
-	return true;
-error:
-	close(fd);
-	free(d);
-	return false;
+	// Shut up clang-tidy
+	steal_fd(&fd);
 
+	return true;
 }
+
 static int
 open_restricted(const char *path, int flags, void *user_data)
 {
@@ -2474,7 +2416,8 @@ open_restricted(const char *path, int fl
 	return fd == -1 ? -errno : fd;
 }
 
-static void close_restricted(int fd, void *user_data)
+static void
+close_restricted(int fd, void *user_data)
 {
 	close(fd);
 }
@@ -2492,8 +2435,7 @@ init_libinput(struct record_context *ctx
 
 	li = libinput_path_create_context(&interface, NULL);
 	if (li == NULL) {
-		fprintf(stderr,
-			"Failed to create libinput context\n");
+		fprintf(stderr, "Failed to create libinput context\n");
 		return false;
 	}
 
@@ -2504,15 +2446,12 @@ init_libinput(struct record_context *ctx
 
 		d = libinput_path_add_device(li, dev->devnode);
 		if (!d) {
-			fprintf(stderr,
-				"Failed to add device %s\n",
-				dev->devnode);
+			fprintf(stderr, "Failed to add device %s\n", dev->devnode);
 			continue;
 		}
 		dev->device = libinput_device_ref(d);
 		/* FIXME: this needs to be a commandline option */
-		libinput_device_config_tap_set_enabled(d,
-					       LIBINPUT_CONFIG_TAP_ENABLED);
+		libinput_device_config_tap_set_enabled(d, LIBINPUT_CONFIG_TAP_ENABLED);
 	}
 
 	return true;
@@ -2548,7 +2487,7 @@ init_hidraw(struct record_context *ctx)
 				 sizeof(hidraw_node),
 				 "/dev/%s",
 				 entry->d_name);
-			fd = open(hidraw_node, O_RDONLY|O_NONBLOCK);
+			fd = open(hidraw_node, O_RDONLY | O_NONBLOCK);
 			if (fd == -1)
 				continue;
 
@@ -2567,14 +2506,14 @@ init_hidraw(struct record_context *ctx)
 static void
 usage(void)
 {
-	printf("Usage: %s [--help] [--all] [--autorestart] [--output-file filename] [/dev/input/event0] [...]\n"
+	printf("Usage: %s [--help] [--all] [--autorestart=2] [--output-file filename] [/dev/input/event0] [...]\n"
 	       "Common use-cases:\n"
 	       "\n"
 	       " sudo %s -o recording.yml\n"
 	       "    Then select the device to record and it Ctrl+C to stop.\n"
 	       "    The recorded data is in recording.yml and can be attached to a bug report.\n"
 	       "\n"
-	       " sudo %s -o recording.yml --autorestart 2\n"
+	       " sudo %s -o recording.yml --autorestart=2\n"
 	       "    As above, but restarts after 2s of inactivity on the device.\n"
 	       "    Note, the output file is only the prefix.\n"
 	       "\n"
@@ -2653,18 +2592,18 @@ find_output_file(int argc, char *argv[],
 	 */
 #define _m(f, l) (((f) << 8) | (l))
 	switch (_m(ftype_first, ftype_last)) {
-	case _m(F_FILE,    F_DEVICE):
-	case _m(F_FILE,    F_NOEXIST):
+	case _m(F_FILE, F_DEVICE):
+	case _m(F_FILE, F_NOEXIST):
 	case _m(F_NOEXIST, F_DEVICE):
 		*output_file = first;
 		return FIRST;
-	case _m(F_DEVICE,  F_FILE):
-	case _m(F_DEVICE,  F_NOEXIST):
+	case _m(F_DEVICE, F_FILE):
+	case _m(F_DEVICE, F_NOEXIST):
 		*output_file = last;
 		return LAST;
-	case _m(F_DEVICE,  F_DEVICE):
+	case _m(F_DEVICE, F_DEVICE):
 		break;
-	case _m(F_FILE,    F_FILE):
+	case _m(F_FILE, F_FILE):
 	case _m(F_NOEXIST, F_FILE):
 	case _m(F_NOEXIST, F_NOEXIST):
 		return ERROR;
@@ -2706,13 +2645,10 @@ main(int argc, char **argv)
 	};
 	struct record_device *d;
 	const char *output_arg = NULL;
-	bool all = false,
-	     with_libinput = false,
-	     with_hidraw = false,
-	     grab = false;
+	bool all = false, with_libinput = false, with_hidraw = false, grab = false;
 	int ndevices;
 	int rc = EXIT_FAILURE;
-	char **paths = NULL;
+	_autostrvfree_ char **paths = NULL;
 
 	list_init(&ctx.devices);
 	list_init(&ctx.sources);
@@ -2732,8 +2668,7 @@ main(int argc, char **argv)
 			rc = EXIT_SUCCESS;
 			goto out;
 		case OPT_AUTORESTART:
-			if (!safe_atoi(optarg, &ctx.timeout) ||
-			    ctx.timeout <= 0) {
+			if (!safe_atoi(optarg, &ctx.timeout) || ctx.timeout <= 0) {
 				usage();
 				rc = EXIT_INVALID_USAGE;
 				goto out;
@@ -2757,7 +2692,8 @@ main(int argc, char **argv)
 			break;
 		case OPT_HIDRAW:
 			with_hidraw = true;
-			fprintf(stderr, "# WARNING: do not type passwords while recording HID reports\n");
+			fprintf(stderr,
+				"# WARNING: do not type passwords while recording HID reports\n");
 			break;
 		case OPT_GRAB:
 			grab = true;
@@ -2778,9 +2714,8 @@ main(int argc, char **argv)
 	 * because this will only backfire anyway.
 	 */
 	if (ndevices >= 1 && output_arg == NULL) {
-		enum fposition pos = find_output_file(argc - optind,
-						      &argv[optind],
-						      &output_arg);
+		enum fposition pos =
+			find_output_file(argc - optind, &argv[optind], &output_arg);
 		if (pos == ERROR) {
 			fprintf(stderr,
 				"Ambiguous device vs output file list. "
@@ -2795,8 +2730,7 @@ main(int argc, char **argv)
 	}
 
 	if (ctx.timeout > 0 && output_arg == NULL) {
-		fprintf(stderr,
-			"Option --autorestart requires --output-file\n");
+		fprintf(stderr, "Option --autorestart requires --output-file\n");
 		rc = EXIT_INVALID_USAGE;
 		goto out;
 	}
@@ -2804,8 +2738,7 @@ main(int argc, char **argv)
 	ctx.output_file.name = safe_strdup(output_arg);
 
 	if (output_arg == NULL && (all || ndevices > 1)) {
-		fprintf(stderr,
-			"Recording multiple devices requires an output file\n");
+		fprintf(stderr, "Recording multiple devices requires an output file\n");
 		rc = EXIT_INVALID_USAGE;
 		goto out;
 	}
@@ -2816,13 +2749,11 @@ main(int argc, char **argv)
 	} else if (ndevices >= 1) {
 		paths = strv_from_argv(ndevices, &argv[optind]);
 	} else {
-		char *path = select_device();
+		_autofree_ char *path = select_device();
 		if (path == NULL) {
 			goto out;
 		}
-
 		paths = strv_from_argv(1, &path);
-		free(path);
 	}
 
 	for (char **p = paths; *p; p++) {
@@ -2839,7 +2770,6 @@ main(int argc, char **argv)
 
 	rc = mainloop(&ctx);
 out:
-	strv_free(paths);
 	list_for_each_safe(d, &ctx.devices, link) {
 		struct hidraw *hidraw;
 
diff -pruN 1.28.1-1/tools/libinput-replay.py 1.30.0-1/tools/libinput-replay.py
--- 1.28.1-1/tools/libinput-replay.py	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-replay.py	2025-11-25 03:40:43.000000000 +0000
@@ -30,6 +30,7 @@ import math
 import multiprocessing
 import argparse
 from pathlib import Path
+from tempfile import NamedTemporaryFile
 
 try:
     import libevdev
@@ -154,20 +155,23 @@ def create(device):
 def print_events(devnode, indent, evs):
     devnode = os.path.basename(devnode)
     for e in evs:
-        print(
-            "{}: {}{:06d}.{:06d} {} / {:<20s} {:4d}".format(
-                devnode,
-                " " * (indent * 8),
-                e.sec,
-                e.usec,
-                e.type.name,
-                e.code.name,
-                e.value,
+        if e.type != libevdev.EV_SYN:
+            print(
+                "{}: {}{:-6d}.{:06d} {} / {:<20s} {:6d}".format(
+                    devnode,
+                    " " * (indent * 8),
+                    e.sec,
+                    e.usec,
+                    e.type.name,
+                    e.code.name,
+                    e.value,
+                )
             )
-        )
         if e.type == libevdev.EV_SYN:
             print(
-                "{}: ------------------------------------------------".format(devnode)
+                "{}: {}----------------- SYN_REPORT ({}) -----------------".format(
+                    devnode, " " * (indent * 8), e.value
+                )
             )
 
 
@@ -296,13 +300,7 @@ def loop(args, recording):
             print("Note that the device may not be in a neutral state now.")
 
 
-def create_device_quirk(device):
-    try:
-        quirks = fetch(device, "quirks")
-        if not quirks:
-            return None
-    except YamlException:
-        return None
+def create_device_quirk(device, quirks):
     # Where the device has a quirk, we match on name, vendor and product.
     # That's the best match we can assemble here from the info we have.
     evdev = fetch(device, "evdev")
@@ -318,33 +316,37 @@ def create_device_quirk(device):
     return quirk
 
 
-def setup_quirks(recording):
+def setup_quirks(recording) -> Path | None:
     devices = fetch(recording, "devices")
-    overrides = None
     quirks = []
     for d in devices:
-        if "quirks" in d:
-            quirk = create_device_quirk(d)
-            if quirk:
-                quirks.append(quirk)
+        qs = d.get("quirks") or []
+        if not any(q.startswith("AttrIsVirtual=") for q in qs):
+            try:
+                is_virtual = d["udev"]["virtual"]
+            except Exception:
+                is_virtual = False
+            qs.append(f"AttrIsVirtual={int(is_virtual)}")
+        quirks.append(create_device_quirk(d, qs))
     if not quirks:
         return None
 
-    overrides = Path("/etc/libinput/local-overrides.quirks")
-    if overrides.exists():
-        print(
-            "{} exists, please move it out of the way first".format(overrides),
-            file=sys.stderr,
-        )
-        sys.exit(1)
-
-    overrides.parent.mkdir(exist_ok=True)
-    with overrides.open("w+") as fd:
+    runtime_dir = (
+        Path(os.getenv("XDG_RUNTIME_DIR", f"/run/user/{os.geteuid()}")) / "libinput"
+    )
+    runtime_dir.mkdir(exist_ok=True, parents=True)
+    with NamedTemporaryFile(
+        mode="w+",
+        dir=runtime_dir,
+        suffix=".quirks",
+        prefix="libinput-replay",
+        delete=False,
+    ) as fd:
         fd.write("# This file was generated by libinput replay\n")
         fd.write("# Unless libinput replay is running right now, remove this file.\n")
         fd.write("\n\n".join(quirks))
 
-    return overrides
+        return Path(fd.name)
 
 
 def check_file(recording):
@@ -367,6 +369,8 @@ def check_file(recording):
 
 
 def main():
+    multiprocessing.set_start_method("fork")
+
     parser = argparse.ArgumentParser(description="Replay a device recording")
     parser.add_argument(
         "recording",
@@ -406,6 +410,13 @@ def main():
     finally:
         if quirks_file:
             quirks_file.unlink()
+            try:
+                quirks_file.parent.rmdir()
+            except OSError as e:
+                import errno
+
+                if e.errno != errno.ENOTEMPTY:
+                    raise e
 
 
 if __name__ == "__main__":
diff -pruN 1.28.1-1/tools/libinput-test.c 1.30.0-1/tools/libinput-test.c
--- 1.28.1-1/tools/libinput-test.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-test.c	2025-11-25 03:40:43.000000000 +0000
@@ -31,7 +31,7 @@
 static inline void
 usage(void)
 {
-#if HAVE_INSTALLED_TESTS
+#ifdef HAVE_INSTALLED_TESTS
 	printf("Usage: libinput test [--help] <feature>\n");
 #else
 	fprintf(stderr, "libinput test was disabled in the build configuration\n");
@@ -46,15 +46,15 @@ main(int argc, char **argv)
 	while (1) {
 		int c;
 		static struct option opts[] = {
-			{ "help",	no_argument,	0, 'h' },
-			{ 0, 0, 0, 0}
+			{ "help", no_argument, 0, 'h' },
+			{ 0, 0, 0, 0 },
 		};
 
 		c = getopt_long(argc, argv, "+h", opts, &option_index);
 		if (c == -1)
 			break;
 
-		switch(c) {
+		switch (c) {
 		case 'h':
 			usage();
 			return EXIT_SUCCESS;
diff -pruN 1.28.1-1/tools/libinput-tool.c 1.30.0-1/tools/libinput-tool.c
--- 1.28.1-1/tools/libinput-tool.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/libinput-tool.c	2025-11-25 03:40:43.000000000 +0000
@@ -24,9 +24,8 @@
 #include "config.h"
 
 #include <getopt.h>
-#include <stdio.h>
-
 #include <libinput-version.h>
+#include <stdio.h>
 
 #include "shared.h"
 
@@ -46,7 +45,7 @@ usage(void)
 	       "  debug-events\n"
 	       "	Print events to stdout\n"
 	       "\n"
-#if HAVE_DEBUG_GUI
+#ifdef HAVE_DEBUG_GUI
 	       "  debug-gui\n"
 	       "	Display a simple GUI to visualize libinput's events.\n"
 	       "\n"
@@ -78,16 +77,16 @@ main(int argc, char **argv)
 	while (1) {
 		int c;
 		static struct option opts[] = {
-			{ "help",	no_argument,	0, GOPT_HELP },
-			{ "version",	no_argument,	0, GOPT_VERSION },
-			{ 0, 0, 0, 0}
+			{ "help", no_argument, 0, GOPT_HELP },
+			{ "version", no_argument, 0, GOPT_VERSION },
+			{ 0, 0, 0, 0 }
 		};
 
 		c = getopt_long(argc, argv, "+h", opts, &option_index);
 		if (c == -1)
 			break;
 
-		switch(c) {
+		switch (c) {
 		case 'h':
 		case GOPT_HELP:
 			usage();
diff -pruN 1.28.1-1/tools/ptraccel-debug.c 1.30.0-1/tools/ptraccel-debug.c
--- 1.28.1-1/tools/ptraccel-debug.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/ptraccel-debug.c	2025-11-25 03:40:43.000000000 +0000
@@ -25,9 +25,9 @@
 
 #include <assert.h>
 #include <errno.h>
+#include <getopt.h>
 #include <stdbool.h>
 #include <stdio.h>
-#include <getopt.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -51,7 +51,7 @@ print_ptraccel_deltas(struct motion_filt
 	printf("#\n");
 
 	/* Accel flattens out after 15 and becomes linear */
-	for (i = 0.0; i < 15.0; i += step) {
+	for (i = 0.0; i < 15.0; i += step) { // NOLINT: security.FloatLoopCounter
 		motion.x = i;
 		motion.y = 0;
 		time += us(12500); /* pretend 80Hz data */
@@ -86,7 +86,7 @@ print_ptraccel_movement(struct motion_fi
 		if (step > 1.0)
 			nevents = max_dx;
 		else
-			nevents = 1.0 * max_dx/step + 0.5;
+			nevents = 1.0 * max_dx / step + 0.5;
 
 		/* Print more events than needed so we see the curve
 		 * flattening out */
@@ -110,9 +110,7 @@ print_ptraccel_movement(struct motion_fi
 }
 
 static void
-print_ptraccel_sequence(struct motion_filter *filter,
-			int nevents,
-			double *deltas)
+print_ptraccel_sequence(struct motion_filter *filter, int nevents, double *deltas)
 {
 	struct device_float_coords motion;
 	struct normalized_coords accel;
@@ -145,13 +143,11 @@ print_ptraccel_sequence(struct motion_fi
 static inline double
 mmps_to_upus(double mmps, int dpi)
 {
-	return mmps * (dpi/25.4) / 1e6;
+	return mmps * (dpi / 25.4) / 1e6;
 }
 
 static void
-print_accel_func(struct motion_filter *filter,
-		 accel_profile_func_t profile,
-		 int dpi)
+print_accel_func(struct motion_filter *filter, accel_profile_func_t profile, int dpi)
 {
 	double mmps;
 
@@ -162,18 +158,24 @@ print_accel_func(struct motion_filter *f
 	printf("# plot \"gnuplot.data\" using 1:2 title 'accel factor'\n");
 	printf("#\n");
 	printf("# data: velocity(mm/s) factor velocity(units/us) velocity(units/ms)\n");
-	for (mmps = 0.0; mmps < 1000.0; mmps += 1) {
+	for (mmps = 0.0; mmps < 1000.0; // NOLINT: security.FloatLoopCounter
+	     mmps += 1) {
 		double units_per_us = mmps_to_upus(mmps, dpi);
 		double units_per_ms = units_per_us * 1000.0;
 		double result = profile(filter, NULL, units_per_us, 0 /* time */);
-		printf("%.8f\t%.4f\t%.8f\t%.8f\n", mmps, result, units_per_us, units_per_ms);
+		printf("%.8f\t%.4f\t%.8f\t%.8f\n",
+		       mmps,
+		       result,
+		       units_per_us,
+		       units_per_ms);
 	}
 }
 
 static void
 usage(void)
 {
-	printf("Usage: %s [options] [dx1] [dx2] [...] > gnuplot.data\n", program_invocation_short_name);
+	printf("Usage: %s [options] [dx1] [dx2] [...] > gnuplot.data\n",
+	       program_invocation_short_name);
 	printf("\n"
 	       "Options:\n"
 	       "--mode=<accel|motion|delta|sequence> \n"
@@ -219,8 +221,7 @@ int
 main(int argc, char **argv)
 {
 	struct motion_filter *filter;
-	double step = 0.1,
-	       max_dx = 10;
+	double step = 0.1, max_dx = 10;
 	int nevents = 0;
 	enum mode mode = ACCEL;
 	double custom_deltas[1024];
@@ -233,7 +234,7 @@ main(int argc, char **argv)
 	struct libinput_config_accel_custom_func custom_func = {
 		.step = 1.0,
 		.npoints = 2,
-		.points = {0.0, 1.0},
+		.points = { 0.0, 1.0 },
 	};
 	struct libinput_config_accel *accel_config =
 		libinput_config_accel_create(LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
@@ -255,21 +256,20 @@ main(int argc, char **argv)
 		int c;
 		int option_index = 0;
 		static struct option long_options[] = {
-			{"help", 0, 0, OPT_HELP },
-			{"mode", 1, 0, OPT_MODE },
-			{"nevents", 1, 0, OPT_NEVENTS },
-			{"maxdx", 1, 0, OPT_MAXDX },
-			{"step", 1, 0, OPT_STEP },
-			{"speed", 1, 0, OPT_SPEED },
-			{"dpi", 1, 0, OPT_DPI },
-			{"filter", 1, 0, OPT_FILTER },
-			{"custom-points", 1, 0, OPT_CUSTOM_POINTS },
-			{"custom-step", 1, 0, OPT_CUSTOM_STEP },
-			{0, 0, 0, 0}
+			{ "help", 0, 0, OPT_HELP },
+			{ "mode", 1, 0, OPT_MODE },
+			{ "nevents", 1, 0, OPT_NEVENTS },
+			{ "maxdx", 1, 0, OPT_MAXDX },
+			{ "step", 1, 0, OPT_STEP },
+			{ "speed", 1, 0, OPT_SPEED },
+			{ "dpi", 1, 0, OPT_DPI },
+			{ "filter", 1, 0, OPT_FILTER },
+			{ "custom-points", 1, 0, OPT_CUSTOM_POINTS },
+			{ "custom-step", 1, 0, OPT_CUSTOM_STEP },
+			{ 0, 0, 0, 0 }
 		};
 
-		c = getopt_long(argc, argv, "",
-				long_options, &option_index);
+		c = getopt_long(argc, argv, "", long_options, &option_index);
 		if (c == -1)
 			break;
 
@@ -324,24 +324,18 @@ main(int argc, char **argv)
 			break;
 		case OPT_CUSTOM_POINTS: {
 			size_t npoints;
-			double *points = double_array_from_string(optarg,
-								  ";",
-								  &npoints);
-			if (!points ||
-			    npoints < LIBINPUT_ACCEL_NPOINTS_MIN ||
+			_autofree_ double *points =
+				double_array_from_string(optarg, ";", &npoints);
+			if (!points || npoints < LIBINPUT_ACCEL_NPOINTS_MIN ||
 			    npoints > LIBINPUT_ACCEL_NPOINTS_MAX) {
 				fprintf(stderr,
 					"Invalid --custom-points\n"
 					"Please provide at least 2 points separated by a semicolon\n"
 					" e.g. --custom-points=\"1.0;1.5\"\n");
-				free(points);
 				return 1;
 			}
 			custom_func.npoints = npoints;
-			memcpy(custom_func.points,
-			       points,
-			       sizeof(*points) * npoints);
-			free(points);
+			memcpy(custom_func.points, points, sizeof(*points) * npoints);
 			break;
 		}
 		case OPT_CUSTOM_STEP:
@@ -355,16 +349,17 @@ main(int argc, char **argv)
 	}
 
 	if (streq(filter_type, "linear")) {
-		filter = create_pointer_accelerator_filter_linear(dpi,
-								  use_averaging);
+		filter = create_pointer_accelerator_filter_linear(dpi, use_averaging);
 		profile = pointer_accel_profile_linear;
 	} else if (streq(filter_type, "low-dpi")) {
-		filter = create_pointer_accelerator_filter_linear_low_dpi(dpi,
-									  use_averaging);
+		filter =
+			create_pointer_accelerator_filter_linear_low_dpi(dpi,
+									 use_averaging);
 		profile = pointer_accel_profile_linear_low_dpi;
 	} else if (streq(filter_type, "touchpad")) {
 		filter = create_pointer_accelerator_filter_touchpad(dpi,
-								    0, 0,
+								    0,
+								    0,
 								    use_averaging);
 		profile = touchpad_accel_profile_linear;
 	} else if (streq(filter_type, "x230")) {
@@ -398,7 +393,7 @@ main(int argc, char **argv)
 		nevents = 0;
 		memset(custom_deltas, 0, sizeof(custom_deltas));
 
-		while(fgets(buf, sizeof(buf), stdin) && nevents < 1024) {
+		while (fgets(buf, sizeof(buf), stdin) && nevents < 1024) {
 			custom_deltas[nevents++] = strtod(buf, NULL);
 		}
 	} else if (optind < argc) {
diff -pruN 1.28.1-1/tools/shared.c 1.30.0-1/tools/shared.c
--- 1.28.1-1/tools/shared.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/shared.c	2025-11-25 03:40:43.000000000 +0000
@@ -28,21 +28,22 @@
 #include <fcntl.h>
 #include <fnmatch.h>
 #include <getopt.h>
+#include <libevdev/libevdev.h>
+#include <libudev.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
-#include <libudev.h>
 #include <unistd.h>
 
-#include <libevdev/libevdev.h>
+#include "util-macros.h"
+#include "util-strings.h"
 
 #include "builddir.h"
+#include "libinput-util.h"
 #include "libinput.h"
 #include "shared.h"
-#include "util-macros.h"
-#include "util-strings.h"
 
 static uint32_t dispatch_counter = 0;
 uint32_t log_serial = 0;
@@ -61,14 +62,20 @@ log_handler(struct libinput *li,
 	    const char *format,
 	    va_list args)
 {
-	static int is_tty = -1;
+	static int use_color = -1;
 	static uint32_t last_dispatch_no = 0;
 	static bool color_toggle = false;
 
-	if (is_tty == -1)
-		is_tty = isatty(STDOUT_FILENO);
+	if (use_color == -1) {
+		if (getenv("NO_COLOR"))
+			use_color = 0;
+		else if (getenv("FORCE_COLOR"))
+			use_color = 1;
+		else
+			use_color = isatty(STDOUT_FILENO);
+	}
 
-	if (is_tty) {
+	if (use_color) {
 		if (priority >= LIBINPUT_LOG_PRIORITY_ERROR) {
 			if (strstr(format, "client bug: ") ||
 			    strstr(format, "libinput bug: ") ||
@@ -77,13 +84,11 @@ log_handler(struct libinput *li,
 			else
 				printf(ANSI_RED);
 		} else if (priority >= LIBINPUT_LOG_PRIORITY_INFO) {
-			printf(ANSI_HIGHLIGHT);
+			printf(ANSI_BOLD);
 		} else if (priority == LIBINPUT_LOG_PRIORITY_DEBUG) {
 			if (dispatch_counter != last_dispatch_no)
 				color_toggle = !color_toggle;
-			uint8_t r = 0,
-				g = 135,
-				b = 95 + (color_toggle ? 80 :0);
+			uint8_t r = 0, g = 135, b = 95 + (color_toggle ? 80 : 0);
 			printf("\x1B[38;2;%u;%u;%um", r, g, b);
 		}
 	}
@@ -98,7 +103,7 @@ log_handler(struct libinput *li,
 	}
 	vprintf(format, args);
 
-	if (is_tty)
+	if (use_color)
 		printf(ANSI_NORMAL);
 
 	log_serial++;
@@ -108,6 +113,8 @@ void
 tools_init_options(struct tools_options *options)
 {
 	memset(options, 0, sizeof(*options));
+	options->plugins = -1;
+	options->plugin_paths = NULL;
 	options->tapping = -1;
 	options->tap_map = -1;
 	options->drag = -1;
@@ -124,7 +131,7 @@ tools_init_options(struct tools_options
 	options->speed = 0.0;
 	options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_NONE;
 	/* initialize accel args */
-	static double points[] = {0.0, 1.0};
+	static double points[] = { 0.0, 1.0 };
 	options->custom_points = points;
 	options->custom_npoints = ARRAY_LENGTH(points);
 	options->custom_type = LIBINPUT_ACCEL_TYPE_FALLBACK;
@@ -138,14 +145,24 @@ tools_init_options(struct tools_options
 	options->area.x2 = 1.0;
 	options->area.y2 = 1.0;
 	options->sendevents = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
+	options->eraser_button_mode = LIBINPUT_CONFIG_ERASER_BUTTON_DEFAULT;
+	options->eraser_button_button = BTN_STYLUS;
+	options->eraser_button_button = 0;
 }
 
 int
-tools_parse_option(int option,
-		   const char *optarg,
-		   struct tools_options *options)
+tools_parse_option(int option, const char *optarg, struct tools_options *options)
 {
-	switch(option) {
+	switch (option) {
+	case OPT_PLUGINS_ENABLE:
+		options->plugins = 1;
+		break;
+	case OPT_PLUGINS_DISABLE:
+		options->plugins = 0;
+		break;
+	case OPT_PLUGIN_PATH:
+		options->plugin_paths = strv_from_string(optarg, ":", NULL);
+		break;
 	case OPT_TAP_ENABLE:
 		options->tapping = 1;
 		break;
@@ -173,9 +190,11 @@ tools_parse_option(int option,
 	case OPT_DRAG_LOCK_ENABLE:
 		if (optarg) {
 			if (streq(optarg, "sticky")) {
-				options->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY;
+				options->drag_lock =
+					LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY;
 			} else if (streq(optarg, "timeout")) {
-				options->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT;
+				options->drag_lock =
+					LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT;
 			} else {
 				return 1;
 			}
@@ -221,14 +240,13 @@ tools_parse_option(int option,
 			return 1;
 
 		if (streq(optarg, "none")) {
-			options->click_method =
-			LIBINPUT_CONFIG_CLICK_METHOD_NONE;
+			options->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE;
 		} else if (streq(optarg, "clickfinger")) {
 			options->click_method =
-			LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
+				LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
 		} else if (streq(optarg, "buttonareas")) {
 			options->click_method =
-			LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
+				LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
 		} else {
 			return 1;
 		}
@@ -250,17 +268,13 @@ tools_parse_option(int option,
 			return 1;
 
 		if (streq(optarg, "none")) {
-			options->scroll_method =
-			LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
+			options->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
 		} else if (streq(optarg, "twofinger")) {
-			options->scroll_method =
-			LIBINPUT_CONFIG_SCROLL_2FG;
+			options->scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
 		} else if (streq(optarg, "edge")) {
-			options->scroll_method =
-			LIBINPUT_CONFIG_SCROLL_EDGE;
+			options->scroll_method = LIBINPUT_CONFIG_SCROLL_EDGE;
 		} else if (streq(optarg, "button")) {
-			options->scroll_method =
-			LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
+			options->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
 		} else {
 			return 1;
 		}
@@ -269,13 +283,9 @@ tools_parse_option(int option,
 		if (!optarg) {
 			return 1;
 		}
-		options->scroll_button =
-		libevdev_event_code_from_name(EV_KEY,
-					      optarg);
+		options->scroll_button = libevdev_event_code_from_name(EV_KEY, optarg);
 		if (options->scroll_button == -1) {
-			fprintf(stderr,
-				"Invalid button %s\n",
-				optarg);
+			fprintf(stderr, "Invalid button %s\n", optarg);
 			return 1;
 		}
 		break;
@@ -297,11 +307,11 @@ tools_parse_option(int option,
 		if (streq(optarg, "adaptive"))
 			options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
 		else if (streq(optarg, "flat"))
-		      options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
+			options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
 		else if (streq(optarg, "custom"))
-		      options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM;
+			options->profile = LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM;
 		else
-		      return 1;
+			return 1;
 		break;
 	case OPT_DISABLE_SENDEVENTS:
 		if (!optarg)
@@ -318,7 +328,8 @@ tools_parse_option(int option,
 		else if (streq(optarg, "enabled"))
 			options->sendevents = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
 		else if (streq(optarg, "disabled-on-external-mouse"))
-			options->sendevents = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
+			options->sendevents =
+				LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
 		else {
 			fprintf(stderr, "Invalid sendevents mode: %s\n", optarg);
 			return 1;
@@ -328,17 +339,13 @@ tools_parse_option(int option,
 		if (!optarg)
 			return 1;
 
-		snprintf(options->match,
-			 sizeof(options->match),
-			 "%s",
-			 optarg);
+		snprintf(options->match, sizeof(options->match), "%s", optarg);
 		break;
 	case OPT_CUSTOM_POINTS:
 		if (!optarg)
 			return 1;
-		options->custom_points = double_array_from_string(optarg,
-								  ";",
-								  &options->custom_npoints);
+		options->custom_points =
+			double_array_from_string(optarg, ";", &options->custom_npoints);
 		if (!options->custom_points || options->custom_npoints < 2) {
 			fprintf(stderr,
 				"Invalid --set-custom-points\n"
@@ -362,8 +369,9 @@ tools_parse_option(int option,
 		else if (streq(optarg, "scroll"))
 			options->custom_type = LIBINPUT_ACCEL_TYPE_SCROLL;
 		else {
-			fprintf(stderr, "Invalid --set-custom-type\n"
-			                "Valid custom types: fallback|motion|scroll\n");
+			fprintf(stderr,
+				"Invalid --set-custom-type\n"
+				"Valid custom types: fallback|motion|scroll\n");
 			return 1;
 		}
 		break;
@@ -381,31 +389,32 @@ tools_parse_option(int option,
 			return 1;
 
 		size_t npoints = 0;
-		double *range = double_array_from_string(optarg, ":", &npoints);
-		if (npoints != 2 || !range || range[0] < 0.0 || range[1] > 1.0 || range[0] >= range[1]) {
-			free(range);
-			fprintf(stderr, "Invalid pressure range, must be in format \"min:max\"\n");
+		_autofree_ double *range =
+			double_array_from_string(optarg, ":", &npoints);
+		if (npoints != 2 || !range || range[0] < 0.0 || range[1] > 1.0 ||
+		    range[0] >= range[1]) {
+			fprintf(stderr,
+				"Invalid pressure range, must be in format \"min:max\"\n");
 			return 1;
 		}
 		options->pressure_range[0] = range[0];
 		options->pressure_range[1] = range[1];
-		free(range);
 		break;
-		}
+	}
 	case OPT_CALIBRATION: {
 		if (!optarg)
 			return 1;
 
 		size_t npoints = 0;
-		double *matrix = double_array_from_string(optarg, " ", &npoints);
+		_autofree_ double *matrix =
+			double_array_from_string(optarg, " ", &npoints);
 		if (!matrix || npoints != 6) {
-			free(matrix);
-			fprintf(stderr, "Invalid calibration matrix, must be 6 space-separated values\n");
+			fprintf(stderr,
+				"Invalid calibration matrix, must be 6 space-separated values\n");
 			return 1;
 		}
 		for (size_t i = 0; i < 6; i++)
-			options->calibration[i] =  matrix[i];
-		free(matrix);
+			options->calibration[i] = matrix[i];
 		break;
 	}
 	case OPT_AREA: {
@@ -423,7 +432,7 @@ tools_parse_option(int option,
 		options->area.x2 = x2;
 		options->area.y2 = y2;
 		break;
-		}
+	}
 	case OPT_3FG_DRAG:
 		if (!optarg)
 			return 1;
@@ -434,8 +443,39 @@ tools_parse_option(int option,
 		else if (streq(optarg, "disabled"))
 			options->drag_3fg = LIBINPUT_CONFIG_3FG_DRAG_DISABLED;
 		else {
-			fprintf(stderr, "Invalid --enable-3fg-drag\n"
-			                "Valid options: 3fg|4fg|disabled\n");
+			fprintf(stderr,
+				"Invalid --enable-3fg-drag\n"
+				"Valid options: 3fg|4fg|disabled\n");
+			return 1;
+		}
+		break;
+	case OPT_ERASER_BUTTON_MODE:
+		if (!optarg)
+			return 1;
+		if (streq(optarg, "default"))
+			options->eraser_button_mode =
+				LIBINPUT_CONFIG_ERASER_BUTTON_DEFAULT;
+		else if (streq(optarg, "button"))
+			options->eraser_button_mode =
+				LIBINPUT_CONFIG_ERASER_BUTTON_BUTTON;
+		else {
+			fprintf(stderr,
+				"Invalid --set-eraser-button-mode\n"
+				"Valid options: default|button\n");
+			return 1;
+		}
+		break;
+	case OPT_ERASER_BUTTON_BUTTON:
+		if (!optarg)
+			return 1;
+		if (streq(optarg, "BTN_STYLUS"))
+			options->eraser_button_button = BTN_STYLUS;
+		else if (streq(optarg, "BTN_STYLUS2"))
+			options->eraser_button_button = BTN_STYLUS2;
+		else if (streq(optarg, "BTN_STYLUS3"))
+			options->eraser_button_button = BTN_STYLUS3;
+		else {
+			fprintf(stderr, "Unsupported eraser button %s\n", optarg);
 			return 1;
 		}
 		break;
@@ -450,11 +490,12 @@ open_restricted(const char *path, int fl
 	int fd = open(path, flags);
 
 	if (fd < 0)
-		fprintf(stderr, "Failed to open %s (%s)\n",
-			path, strerror(errno));
-	else if (grab && *grab && ioctl(fd, EVIOCGRAB, (void*)1) == -1)
-		fprintf(stderr, "Grab requested, but failed for %s (%s)\n",
-			path, strerror(errno));
+		fprintf(stderr, "Failed to open %s (%s)\n", path, strerror(errno));
+	else if (grab && *grab && ioctl(fd, EVIOCGRAB, (void *)1) == -1)
+		fprintf(stderr,
+			"Grab requested, but failed for %s (%s)\n",
+			path,
+			strerror(errno));
 
 	return fd < 0 ? -errno : fd;
 }
@@ -470,47 +511,82 @@ static const struct libinput_interface i
 	.close_restricted = close_restricted,
 };
 
-static struct libinput *
-tools_open_udev(const char *seat, bool verbose, bool *grab)
+static int
+add_path(const char *str, size_t index, void *data)
 {
-	struct libinput *li;
-	struct udev *udev = udev_new();
+	struct libinput *libinput = data;
+	libinput_plugin_system_append_default_paths(libinput);
+	return 0;
+}
+
+static void
+tools_load_plugins(struct libinput *libinput, char **plugin_paths)
+{
+	_autofree_ char *builddir = NULL;
+
+	if (plugin_paths) {
+		strv_for_each((const char **)plugin_paths, add_path, libinput);
+		strv_free(plugin_paths);
+	} else {
+		if (builddir_lookup(&builddir)) {
+			_autofree_ char *plugindir =
+				strdup_printf("%s/plugins", builddir);
+			libinput_plugin_system_append_path(libinput, plugindir);
+		}
+		libinput_plugin_system_append_default_paths(libinput);
+	}
+	switch (libinput_plugin_system_load_plugins(libinput,
+						    LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE)) {
+	case -ENOSYS:
+		fprintf(stderr, "Warning: plugins were disabled at compile time");
+		break;
+	case 0:
+		break;
+	}
+}
 
+static struct libinput *
+tools_open_udev(const char *seat,
+		bool verbose,
+		bool *grab,
+		bool with_plugins,
+		char **plugin_paths)
+{
+	_unref_(udev) *udev = udev_new();
 	if (!udev) {
 		fprintf(stderr, "Failed to initialize udev\n");
 		return NULL;
 	}
 
-	li = libinput_udev_create_context(&interface, grab, udev);
+	_unref_(libinput) *li = libinput_udev_create_context(&interface, grab, udev);
 	if (!li) {
 		fprintf(stderr, "Failed to initialize context from udev\n");
-		goto out;
+		return NULL;
 	}
 
 	libinput_log_set_handler(li, log_handler);
 	if (verbose)
 		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);
 
+	if (with_plugins)
+		tools_load_plugins(li, plugin_paths);
+
 	if (libinput_udev_assign_seat(li, seat)) {
 		fprintf(stderr, "Failed to set seat\n");
-		libinput_unref(li);
-		li = NULL;
-		goto out;
+		return NULL;
 	}
 
-out:
-	udev_unref(udev);
-	return li;
+	return steal(&li);
 }
 
 static struct libinput *
-tools_open_device(const char **paths, bool verbose, bool *grab)
+tools_open_device(const char **paths,
+		  bool verbose,
+		  bool *grab,
+		  bool with_plugins,
+		  char **plugin_paths)
 {
-	struct libinput_device *device;
-	struct libinput *li;
-	const char **p = paths;
-
-	li = libinput_path_create_context(&interface, grab);
+	_unref_(libinput) *li = libinput_path_create_context(&interface, grab);
 	if (!li) {
 		fprintf(stderr, "Failed to initialize path context\n");
 		return NULL;
@@ -521,35 +597,36 @@ tools_open_device(const char **paths, bo
 		libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG);
 	}
 
+	if (with_plugins)
+		tools_load_plugins(li, plugin_paths);
+
+	const char **p = paths;
 	while (*p) {
-		device = libinput_path_add_device(li, *p);
+		struct libinput_device *device = libinput_path_add_device(li, *p);
 		if (!device) {
 			fprintf(stderr, "Failed to initialize device %s\n", *p);
-			libinput_unref(li);
-			li = NULL;
-			break;
+			return NULL;
 		}
 		p++;
 	}
 
-	return li;
+	return steal(&li);
 }
 
 static void
 tools_setenv_quirks_dir(void)
 {
-	char *builddir = builddir_lookup();
-	if (builddir) {
+	if (builddir_lookup(NULL))
 		setenv("LIBINPUT_QUIRKS_DIR", LIBINPUT_QUIRKS_SRCDIR, 0);
-		free(builddir);
-	}
 }
 
 struct libinput *
 tools_open_backend(enum tools_backend which,
 		   const char **seat_or_device,
 		   bool verbose,
-		   bool *grab)
+		   bool *grab,
+		   bool with_plugins,
+		   char **plugin_paths)
 {
 	struct libinput *li;
 
@@ -557,10 +634,18 @@ tools_open_backend(enum tools_backend wh
 
 	switch (which) {
 	case BACKEND_UDEV:
-		li = tools_open_udev(seat_or_device[0], verbose, grab);
+		li = tools_open_udev(seat_or_device[0],
+				     verbose,
+				     grab,
+				     with_plugins,
+				     plugin_paths);
 		break;
 	case BACKEND_DEVICE:
-		li = tools_open_device(seat_or_device, verbose, grab);
+		li = tools_open_device(seat_or_device,
+				       verbose,
+				       grab,
+				       with_plugins,
+				       plugin_paths);
 		break;
 	default:
 		abort();
@@ -570,16 +655,16 @@ tools_open_backend(enum tools_backend wh
 }
 
 void
-tools_device_apply_config(struct libinput_device *device,
-			  struct tools_options *options)
+tools_device_apply_config(struct libinput_device *device, struct tools_options *options)
 {
 	const char *name = libinput_device_get_name(device);
 
 	if (libinput_device_config_send_events_get_modes(device) &
-	      LIBINPUT_CONFIG_SEND_EVENTS_DISABLED &&
+		    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED &&
 	    fnmatch(options->disable_pattern, name, 0) != FNM_NOMATCH) {
-		libinput_device_config_send_events_set_mode(device,
-					    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
+		libinput_device_config_send_events_set_mode(
+			device,
+			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
 	}
 
 	if (strlen(options->match) > 0 &&
@@ -590,23 +675,23 @@ tools_device_apply_config(struct libinpu
 
 	if (options->tapping != -1)
 		libinput_device_config_tap_set_enabled(device, options->tapping);
-	if (options->tap_map != (enum libinput_config_tap_button_map)-1)
-		libinput_device_config_tap_set_button_map(device,
-							  options->tap_map);
+	if (options->tap_map != (enum libinput_config_tap_button_map) - 1)
+		libinput_device_config_tap_set_button_map(device, options->tap_map);
 	if (options->drag != -1)
-		libinput_device_config_tap_set_drag_enabled(device,
-							    options->drag);
+		libinput_device_config_tap_set_drag_enabled(device, options->drag);
 	if (options->drag_lock != -1)
 		libinput_device_config_tap_set_drag_lock_enabled(device,
 								 options->drag_lock);
 	if (options->natural_scroll != -1)
-		libinput_device_config_scroll_set_natural_scroll_enabled(device,
-									 options->natural_scroll);
+		libinput_device_config_scroll_set_natural_scroll_enabled(
+			device,
+			options->natural_scroll);
 	if (options->left_handed != -1)
 		libinput_device_config_left_handed_set(device, options->left_handed);
 	if (options->middlebutton != -1)
-		libinput_device_config_middle_emulation_set_enabled(device,
-								    options->middlebutton);
+		libinput_device_config_middle_emulation_set_enabled(
+			device,
+			options->middlebutton);
 
 	if (options->dwt != -1)
 		libinput_device_config_dwt_set_enabled(device, options->dwt);
@@ -614,48 +699,50 @@ tools_device_apply_config(struct libinpu
 	if (options->dwtp != -1)
 		libinput_device_config_dwtp_set_enabled(device, options->dwtp);
 
-	if (options->click_method != (enum libinput_config_click_method)-1)
+	if (options->click_method != (enum libinput_config_click_method) - 1)
 		libinput_device_config_click_set_method(device, options->click_method);
 
-	if (options->clickfinger_map != (enum libinput_config_clickfinger_button_map)-1)
-		libinput_device_config_click_set_clickfinger_button_map(device,
-									options->clickfinger_map);
+	if (options->clickfinger_map !=
+	    (enum libinput_config_clickfinger_button_map) - 1)
+		libinput_device_config_click_set_clickfinger_button_map(
+			device,
+			options->clickfinger_map);
 
-	if (options->scroll_method != (enum libinput_config_scroll_method)-1)
+	if (options->scroll_method != (enum libinput_config_scroll_method) - 1)
 		libinput_device_config_scroll_set_method(device,
 							 options->scroll_method);
 	if (options->scroll_button != -1)
 		libinput_device_config_scroll_set_button(device,
 							 options->scroll_button);
 	if (options->scroll_button_lock != -1)
-		libinput_device_config_scroll_set_button_lock(device,
-							      options->scroll_button_lock);
+		libinput_device_config_scroll_set_button_lock(
+			device,
+			options->scroll_button_lock);
 
 	if (libinput_device_config_accel_is_available(device)) {
-		libinput_device_config_accel_set_speed(device,
-						       options->speed);
+		libinput_device_config_accel_set_speed(device, options->speed);
 		if (options->profile != LIBINPUT_CONFIG_ACCEL_PROFILE_NONE)
 			libinput_device_config_accel_set_profile(device,
 								 options->profile);
 	}
 
 	if (options->profile == LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM) {
-		struct libinput_config_accel *config =
-			libinput_config_accel_create(LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
+		_destroy_(libinput_config_accel) *config = libinput_config_accel_create(
+			LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM);
 		libinput_config_accel_set_points(config,
 						 options->custom_type,
 						 options->custom_step,
 						 options->custom_npoints,
 						 options->custom_points);
 		libinput_device_config_accel_apply(device, config);
-		libinput_config_accel_destroy(config);
 	}
 
 	if (options->angle != 0)
 		libinput_device_config_rotation_set_angle(device, options->angle % 360);
 
 	if (libinput_device_config_calibration_has_matrix(device))
-		libinput_device_config_calibration_set_matrix(device, options->calibration);
+		libinput_device_config_calibration_set_matrix(device,
+							      options->calibration);
 
 	if (libinput_device_config_area_has_rectangle(device))
 		libinput_device_config_area_set_rectangle(device, &options->area);
@@ -668,65 +755,60 @@ void
 tools_tablet_tool_apply_config(struct libinput_tablet_tool *tool,
 			       struct tools_options *options)
 {
-	libinput_tablet_tool_config_pressure_range_set(tool,
-						       options->pressure_range[0],
-						       options->pressure_range[1]);
+	if (options->pressure_range[0] != 0.0 || options->pressure_range[1] != 1.0)
+		libinput_tablet_tool_config_pressure_range_set(
+			tool,
+			options->pressure_range[0],
+			options->pressure_range[1]);
+	if (options->eraser_button_button)
+		libinput_tablet_tool_config_eraser_button_set_button(
+			tool,
+			options->eraser_button_button);
+	libinput_tablet_tool_config_eraser_button_set_mode(tool,
+							   options->eraser_button_mode);
 }
 
-static char*
+static char *
 find_device(const char *udev_tag)
 {
-	struct udev *udev;
-	struct udev_enumerate *e = NULL;
-	struct udev_list_entry *entry = NULL;
-	struct udev_device *device;
-	const char *path, *sysname;
-	char *device_node = NULL;
-
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	if (!udev)
-		goto out;
+		return NULL;
 
-	e = udev_enumerate_new(udev);
+	_unref_(udev_enumerate) *e = udev_enumerate_new(udev);
 	udev_enumerate_add_match_subsystem(e, "input");
 	udev_enumerate_scan_devices(e);
 
+	struct udev_list_entry *entry = NULL;
 	udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
-		path = udev_list_entry_get_name(entry);
-		device = udev_device_new_from_syspath(udev, path);
+		const char *path = udev_list_entry_get_name(entry);
+		_unref_(udev_device) *device = udev_device_new_from_syspath(udev, path);
 		if (!device)
 			continue;
 
-		sysname = udev_device_get_sysname(device);
+		const char *sysname = udev_device_get_sysname(device);
 		if (!strstartswith("event", sysname)) {
-			udev_device_unref(device);
 			continue;
 		}
 
-		if (udev_device_get_property_value(device, udev_tag))
-			device_node = safe_strdup(udev_device_get_devnode(device));
-
-		udev_device_unref(device);
-
-		if (device_node)
-			break;
+		if (udev_device_get_property_value(device, udev_tag)) {
+			char *device_node =
+				safe_strdup(udev_device_get_devnode(device));
+			if (device_node)
+				return device_node;
+		}
 	}
-out:
-	udev_enumerate_unref(e);
-	udev_unref(udev);
 
-	return device_node;
+	return NULL;
 }
 
 bool
 find_touchpad_device(char *path, size_t path_len)
 {
-	char *devnode = find_device("ID_INPUT_TOUCHPAD");
+	_autofree_ char *devnode = find_device("ID_INPUT_TOUCHPAD");
 
-	if (devnode) {
+	if (devnode)
 		snprintf(path, path_len, "%s", devnode);
-		free(devnode);
-	}
 
 	return devnode != NULL;
 }
@@ -734,29 +816,19 @@ find_touchpad_device(char *path, size_t
 bool
 is_touchpad_device(const char *devnode)
 {
-	struct udev *udev;
-	struct udev_device *dev = NULL;
 	struct stat st;
-	bool is_touchpad = false;
-
 	if (stat(devnode, &st) < 0)
 		return false;
 
-	udev = udev_new();
+	_unref_(udev) *udev = udev_new();
 	if (!udev)
-		goto out;
+		return false;
 
-	dev = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
+	_unref_(udev_device) *dev = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
 	if (!dev)
-		goto out;
-
-	is_touchpad = udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD");
-out:
-	if (dev)
-		udev_device_unref(dev);
-	udev_unref(udev);
+		return false;
 
-	return is_touchpad;
+	return udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD");
 }
 
 static inline void
@@ -765,21 +837,21 @@ setup_path(void)
 	const char *path = getenv("PATH");
 	char new_path[PATH_MAX];
 	const char *extra_path = LIBINPUT_TOOL_PATH;
-	char *builddir = builddir_lookup();
+	_autofree_ char *builddir = NULL;
 
+	builddir_lookup(&builddir);
 	snprintf(new_path,
 		 sizeof(new_path),
 		 "%s:%s",
 		 builddir ? builddir : extra_path,
 		 path ? path : "");
 	setenv("PATH", new_path, 1);
-	free(builddir);
 }
 
 int
 tools_exec_command(const char *prefix, int real_argc, char **real_argv)
 {
-	char *argv[64] = {NULL};
+	char *argv[64] = { NULL };
 	char executable[128];
 	const char *command;
 	int rc;
@@ -788,11 +860,7 @@ tools_exec_command(const char *prefix, i
 
 	command = real_argv[0];
 
-	rc = snprintf(executable,
-		      sizeof(executable),
-		      "%s-%s",
-		      prefix,
-		      command);
+	rc = snprintf(executable, sizeof(executable), "%s-%s", prefix, command);
 	if (rc >= (int)sizeof(executable)) {
 		fprintf(stderr, "Failed to assemble command.\n");
 		return EXIT_FAILURE;
@@ -807,9 +875,7 @@ tools_exec_command(const char *prefix, i
 	rc = execvp(executable, argv);
 	if (rc) {
 		if (errno == ENOENT) {
-			fprintf(stderr,
-				"libinput: %s is not installed\n",
-				command);
+			fprintf(stderr, "libinput: %s is not installed\n", command);
 			return EXIT_INVALID_USAGE;
 		}
 		fprintf(stderr,
@@ -842,7 +908,11 @@ sprintf_event_codes(char *buf, size_t sz
 
 		const char *name = libevdev_event_code_get_name(type, code);
 
-		printed = snprintf(buf + off, sz - off, "%c%s;", enable ? '+' : '-', name);
+		printed = snprintf(buf + off,
+				   sz - off,
+				   "%c%s;",
+				   enable ? '+' : '-',
+				   name);
 		assert(printed != -1);
 		off += printed;
 	}
@@ -868,7 +938,11 @@ sprintf_input_props(char *buf, size_t sz
 
 		const char *name = libevdev_property_get_name(prop);
 
-		printed = snprintf(buf + off, sz - off, "%c%s;", enable ? '+' : '-', name);
+		printed = snprintf(buf + off,
+				   sz - off,
+				   "%c%s;",
+				   enable ? '+' : '-',
+				   name);
 		assert(printed != -1);
 		off += printed;
 	}
@@ -882,10 +956,9 @@ tools_list_device_quirks(struct quirks_c
 {
 	char buf[256];
 
-	struct quirks *quirks;
 	enum quirk q;
 
-	quirks = quirks_fetch_for_device(ctx, device);
+	_unref_(quirks) *quirks = quirks_fetch_for_device(ctx, device);
 	if (!quirks)
 		return;
 
@@ -900,7 +973,7 @@ tools_list_device_quirks(struct quirks_c
 			snprintf(buf, sizeof(buf), "%s=%d", name, b ? 1 : 0);
 			callback(userdata, buf);
 		}
-	} while(++q < _QUIRK_LAST_MODEL_QUIRK_);
+	} while (++q < _QUIRK_LAST_MODEL_QUIRK_);
 
 	q = QUIRK_ATTR_SIZE_HINT;
 	do {
@@ -919,13 +992,23 @@ tools_list_device_quirks(struct quirks_c
 			case QUIRK_ATTR_SIZE_HINT:
 			case QUIRK_ATTR_RESOLUTION_HINT:
 				quirks_get_dimensions(quirks, q, &dim);
-				snprintf(buf, sizeof(buf), "%s=%zdx%zd", name, dim.x, dim.y);
+				snprintf(buf,
+					 sizeof(buf),
+					 "%s=%zdx%zd",
+					 name,
+					 dim.x,
+					 dim.y);
 				callback(userdata, buf);
 				break;
 			case QUIRK_ATTR_TOUCH_SIZE_RANGE:
 			case QUIRK_ATTR_PRESSURE_RANGE:
 				quirks_get_range(quirks, q, &r);
-				snprintf(buf, sizeof(buf), "%s=%d:%d", name, r.upper, r.lower);
+				snprintf(buf,
+					 sizeof(buf),
+					 "%s=%d:%d",
+					 name,
+					 r.upper,
+					 r.lower);
 				callback(userdata, buf);
 				break;
 			case QUIRK_ATTR_PALM_SIZE_THRESHOLD:
@@ -952,6 +1035,7 @@ tools_list_device_quirks(struct quirks_c
 				break;
 			case QUIRK_ATTR_USE_VELOCITY_AVERAGING:
 			case QUIRK_ATTR_TABLET_SMOOTHING:
+			case QUIRK_ATTR_IS_VIRTUAL:
 				quirks_get_bool(quirks, q, &b);
 				snprintf(buf, sizeof(buf), "%s=%d", name, b);
 				callback(userdata, buf);
@@ -969,7 +1053,5 @@ tools_list_device_quirks(struct quirks_c
 				break;
 			}
 		}
-	} while(++q < _QUIRK_LAST_ATTR_QUIRK_);
-
-	quirks_unref(quirks);
+	} while (++q < _QUIRK_LAST_ATTR_QUIRK_);
 }
diff -pruN 1.28.1-1/tools/shared.h 1.30.0-1/tools/shared.h
--- 1.28.1-1/tools/shared.h	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/tools/shared.h	2025-11-25 03:40:43.000000000 +0000
@@ -24,11 +24,13 @@
 #ifndef _SHARED_H_
 #define _SHARED_H_
 
-#include <stdbool.h>
-#include <limits.h>
+#include "config.h"
 
-#include <quirks.h>
+#include <getopt.h>
 #include <libinput.h>
+#include <limits.h>
+#include <quirks.h>
+#include <stdbool.h>
 
 #include "util-strings.h"
 
@@ -73,10 +75,17 @@ enum configuration_options {
 	OPT_AREA,
 	OPT_3FG_DRAG,
 	OPT_SENDEVENTS,
+	OPT_ERASER_BUTTON_MODE,
+	OPT_ERASER_BUTTON_BUTTON,
+	OPT_PLUGINS_DISABLE,
+	OPT_PLUGINS_ENABLE,
+	OPT_PLUGIN_PATH,
 };
 
 #define CONFIGURATION_OPTIONS \
 	{ "disable-sendevents",        required_argument, 0, OPT_DISABLE_SENDEVENTS }, \
+	{ "enable-plugins",            no_argument,       0, OPT_PLUGINS_ENABLE }, \
+	{ "disable-plugins",           no_argument,       0, OPT_PLUGINS_DISABLE }, \
 	{ "enable-tap",                no_argument,       0, OPT_TAP_ENABLE }, \
 	{ "disable-tap",               no_argument,       0, OPT_TAP_DISABLE }, \
 	{ "enable-drag",               no_argument,       0, OPT_DRAG_ENABLE }, \
@@ -111,7 +120,12 @@ enum configuration_options {
 	{ "set-rotation-angle",        required_argument, 0, OPT_ROTATION_ANGLE }, \
 	{ "set-pressure-range",        required_argument, 0, OPT_PRESSURE_RANGE }, \
 	{ "set-calibration",           required_argument, 0, OPT_CALIBRATION }, \
-	{ "set-area",                  required_argument, 0, OPT_AREA }
+	{ "set-area",                  required_argument, 0, OPT_AREA }, \
+	{ "set-eraser-button-mode",    required_argument, 0, OPT_ERASER_BUTTON_MODE }, \
+	{ "set-eraser-button-button",  required_argument, 0, OPT_ERASER_BUTTON_BUTTON },\
+	{ "set-plugin-path",	       required_argument, 0, OPT_PLUGIN_PATH }
+
+/* Note: New arguments should be added to shell completions */
 
 static inline void
 tools_print_usage_option_list(struct option *opts)
@@ -121,8 +135,8 @@ tools_print_usage_option_list(struct opt
 	struct option *o = opts;
 	while (o && o->name) {
 		if (strstartswith(o->name, "enable-") &&
-			strstartswith((o+1)->name, "disable-")) {
-			printf("   --%s/--%s\n", o->name, (o+1)->name);
+		    strstartswith((o + 1)->name, "disable-")) {
+			printf("   --%s/--%s\n", o->name, (o + 1)->name);
 			o++;
 		} else {
 			printf("   --%s\n", o->name);
@@ -131,15 +145,14 @@ tools_print_usage_option_list(struct opt
 	}
 }
 
-enum tools_backend {
-	BACKEND_NONE,
-	BACKEND_DEVICE,
-	BACKEND_UDEV
-};
+enum tools_backend { BACKEND_NONE, BACKEND_DEVICE, BACKEND_UDEV };
 
 struct tools_options {
 	char match[256];
 
+	int plugins;
+	char **plugin_paths;
+
 	int tapping;
 	int drag;
 	int drag_lock;
@@ -167,24 +180,34 @@ struct tools_options {
 	struct libinput_config_area_rectangle area;
 	enum libinput_config_3fg_drag_state drag_3fg;
 	enum libinput_config_send_events_mode sendevents;
+	enum libinput_config_eraser_button_mode eraser_button_mode;
+	unsigned int eraser_button_button;
 };
 
-void tools_init_options(struct tools_options *options);
-int tools_parse_option(int option,
-		       const char *optarg,
-		       struct tools_options *options);
-struct libinput* tools_open_backend(enum tools_backend which,
-				    const char **seat_or_devices,
-				    bool verbose,
-				    bool *grab);
-void tools_device_apply_config(struct libinput_device *device,
+void
+tools_init_options(struct tools_options *options);
+int
+tools_parse_option(int option, const char *optarg, struct tools_options *options);
+struct libinput *
+tools_open_backend(enum tools_backend which,
+		   const char **seat_or_devices,
+		   bool verbose,
+		   bool *grab,
+		   bool with_plugins,
+		   char **plugin_paths);
+void
+tools_device_apply_config(struct libinput_device *device,
+			  struct tools_options *options);
+void
+tools_tablet_tool_apply_config(struct libinput_tablet_tool *tool,
 			       struct tools_options *options);
-void tools_tablet_tool_apply_config(struct libinput_tablet_tool *tool,
-				    struct tools_options *options);
-int tools_exec_command(const char *prefix, int argc, char **argv);
+int
+tools_exec_command(const char *prefix, int argc, char **argv);
 
-bool find_touchpad_device(char *path, size_t path_len);
-bool is_touchpad_device(const char *devnode);
+bool
+find_touchpad_device(char *path, size_t path_len);
+bool
+is_touchpad_device(const char *devnode);
 
 void
 tools_list_device_quirks(struct quirks_context *ctx,
diff -pruN 1.28.1-1/udev/libinput-device-group.c 1.30.0-1/udev/libinput-device-group.c
--- 1.28.1-1/udev/libinput-device-group.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/udev/libinput-device-group.c	2025-11-25 03:40:43.000000000 +0000
@@ -23,20 +23,18 @@
 
 #include "config.h"
 
+#include <libudev.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <libudev.h>
 
 #include "libinput-util.h"
 
-#if HAVE_LIBWACOM
+#ifdef HAVE_LIBWACOM
 #include <libwacom/libwacom.h>
 
 static void
-wacom_handle_paired(struct udev_device *device,
-		    int *vendor_id,
-		    int *product_id)
+wacom_handle_paired(struct udev_device *device, int *vendor_id, int *product_id)
 {
 	WacomDeviceDatabase *db = NULL;
 	WacomDevice *tablet = NULL;
@@ -126,10 +124,8 @@ wacom_handle_ekr(struct udev_device *dev
 		pidstr = udev_device_get_property_value(d, "ID_MODEL_ID");
 		phys = udev_device_get_sysattr_value(d, "phys");
 
-		if (vidstr && pidstr && phys &&
-		    safe_atoi_base(vidstr, &vid, 16) &&
-		    safe_atoi_base(pidstr, &pid, 16) &&
-		    vid == VENDOR_ID_WACOM &&
+		if (vidstr && pidstr && phys && safe_atoi_base(vidstr, &vid, 16) &&
+		    safe_atoi_base(pidstr, &pid, 16) && vid == VENDOR_ID_WACOM &&
 		    pid != PRODUCT_ID_WACOM_EKR) {
 			dist = find_tree_distance(device, d);
 			if (dist > 0 && (dist < best_dist || best_dist < 0)) {
@@ -149,13 +145,13 @@ wacom_handle_ekr(struct udev_device *dev
 }
 #endif
 
-int main(int argc, char **argv)
+int
+main(int argc, char **argv)
 {
 	int rc = 1;
 	struct udev *udev = NULL;
 	struct udev_device *device = NULL;
-	const char *syspath,
-	           *phys = NULL;
+	const char *syspath, *phys = NULL;
 	const char *product;
 	int bustype, vendor_id, product_id, version;
 	char group[1024];
@@ -207,30 +203,28 @@ int main(int argc, char **argv)
 		   &version) != 4) {
 		snprintf(group, sizeof(group), "%s:%s", product, phys);
 	} else {
-	    char *physmatch = NULL;
+		char *physmatch = NULL;
 
-#if HAVE_LIBWACOM
-	    if (vendor_id == VENDOR_ID_WACOM) {
-		    if (product_id == PRODUCT_ID_WACOM_EKR)
-			    wacom_handle_ekr(device,
-					     &vendor_id,
-					     &product_id,
-					     &physmatch);
-		    /* This is called for the EKR as well */
-		    wacom_handle_paired(device,
-					&vendor_id,
-					&product_id);
-	    }
+#ifdef HAVE_LIBWACOM
+		if (vendor_id == VENDOR_ID_WACOM) {
+			if (product_id == PRODUCT_ID_WACOM_EKR)
+				wacom_handle_ekr(device,
+						 &vendor_id,
+						 &product_id,
+						 &physmatch);
+			/* This is called for the EKR as well */
+			wacom_handle_paired(device, &vendor_id, &product_id);
+		}
 #endif
-	    snprintf(group,
-		     sizeof(group),
-		     "%x/%x/%x:%s",
-		     bustype,
-		     vendor_id,
-		     product_id,
-		     physmatch ? physmatch : phys);
+		snprintf(group,
+			 sizeof(group),
+			 "%x/%x/%x:%s",
+			 bustype,
+			 vendor_id,
+			 product_id,
+			 physmatch ? physmatch : phys);
 
-	    free(physmatch);
+		free(physmatch);
 	}
 
 	str = strstr(group, "/input");
diff -pruN 1.28.1-1/udev/libinput-fuzz-extract.c 1.30.0-1/udev/libinput-fuzz-extract.c
--- 1.28.1-1/udev/libinput-fuzz-extract.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/udev/libinput-fuzz-extract.c	2025-11-25 03:40:43.000000000 +0000
@@ -24,15 +24,15 @@
 #include "config.h"
 
 #include <fcntl.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <unistd.h>
+#include <libevdev/libevdev.h>
 #include <libudev.h>
 #include <linux/input.h>
-#include <libevdev/libevdev.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
 
-#include "util-prop-parsers.h"
 #include "util-macros.h"
+#include "util-prop-parsers.h"
 
 /**
  * For a non-zero fuzz on the x/y axes, print that fuzz as property and
@@ -46,10 +46,12 @@ handle_absfuzz(struct udev_device *devic
 	struct libevdev *evdev = NULL;
 	int fd = -1;
 	int rc;
-	unsigned int axes[] = {ABS_X,
-			       ABS_Y,
-			       ABS_MT_POSITION_X,
-			       ABS_MT_POSITION_Y};
+	unsigned int axes[] = {
+		ABS_X,
+		ABS_Y,
+		ABS_MT_POSITION_X,
+		ABS_MT_POSITION_Y,
+	};
 
 	devnode = udev_device_get_devnode(device);
 	if (!devnode)
@@ -87,10 +89,12 @@ out:
 static void
 handle_evdev_abs(struct udev_device *device)
 {
-	unsigned int axes[] = {ABS_X,
-			       ABS_Y,
-			       ABS_MT_POSITION_X,
-			       ABS_MT_POSITION_Y};
+	unsigned int axes[] = {
+		ABS_X,
+		ABS_Y,
+		ABS_MT_POSITION_X,
+		ABS_MT_POSITION_Y,
+	};
 
 	ARRAY_FOR_EACH(axes, code) {
 		const char *prop;
@@ -109,7 +113,8 @@ handle_evdev_abs(struct udev_device *dev
 	}
 }
 
-int main(int argc, char **argv)
+int
+main(int argc, char **argv)
 {
 	int rc = 1;
 	struct udev *udev = NULL;
diff -pruN 1.28.1-1/udev/libinput-fuzz-to-zero.c 1.30.0-1/udev/libinput-fuzz-to-zero.c
--- 1.28.1-1/udev/libinput-fuzz-to-zero.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/udev/libinput-fuzz-to-zero.c	2025-11-25 03:40:43.000000000 +0000
@@ -24,11 +24,11 @@
 #include "config.h"
 
 #include <fcntl.h>
-#include <stdio.h>
-#include <unistd.h>
+#include <libevdev/libevdev.h>
 #include <libudev.h>
 #include <linux/input.h>
-#include <libevdev/libevdev.h>
+#include <stdio.h>
+#include <unistd.h>
 
 #include "util-macros.h"
 
@@ -39,10 +39,12 @@ reset_absfuzz_to_zero(struct udev_device
 	struct libevdev *evdev = NULL;
 	int fd = -1;
 	int rc;
-	unsigned int axes[] = {ABS_X,
-			       ABS_Y,
-			       ABS_MT_POSITION_X,
-			       ABS_MT_POSITION_Y};
+	unsigned int axes[] = {
+		ABS_X,
+		ABS_Y,
+		ABS_MT_POSITION_X,
+		ABS_MT_POSITION_Y,
+	};
 
 	devnode = udev_device_get_devnode(device);
 	if (!devnode)
@@ -77,7 +79,8 @@ out:
 	libevdev_free(evdev);
 }
 
-int main(int argc, char **argv)
+int
+main(int argc, char **argv)
 {
 	int rc = 1;
 	struct udev *udev = NULL;
diff -pruN 1.28.1-1/udev/test-libinput-fuzz-extract.c 1.30.0-1/udev/test-libinput-fuzz-extract.c
--- 1.28.1-1/udev/test-libinput-fuzz-extract.c	2025-04-01 02:46:07.000000000 +0000
+++ 1.30.0-1/udev/test-libinput-fuzz-extract.c	1970-01-01 00:00:00.000000000 +0000
@@ -1,126 +0,0 @@
-/*
- * Copyright © 2019 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:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-#include "config.h"
-
-#include <check.h>
-
-/* remove the main() from the included program so we can define our own */
-#define main __disabled
-int main(int argc, char **argv);
-#include "libinput-fuzz-extract.c"
-#undef main
-
-START_TEST(test_parse_ev_abs)
-{
-	struct test {
-		uint32_t which;
-		const char *prop;
-		int min, max, res, fuzz, flat;
-
-	} tests[] = {
-		{ .which = (MIN|MAX),
-		  .prop = "1:2",
-		  .min = 1, .max = 2 },
-		{ .which = (MIN|MAX),
-		  .prop = "1:2:",
-		  .min = 1, .max = 2 },
-		{ .which = (MIN|MAX|RES),
-		  .prop = "10:20:30",
-		  .min = 10, .max = 20, .res = 30 },
-		{ .which = (RES),
-		  .prop = "::100",
-		  .res = 100 },
-		{ .which = (MIN),
-		  .prop = "10:",
-		  .min = 10 },
-		{ .which = (MAX|RES),
-		  .prop = ":10:1001",
-		  .max = 10, .res = 1001 },
-		{ .which = (MIN|MAX|RES|FUZZ),
-		  .prop = "1:2:3:4",
-		  .min = 1, .max = 2, .res = 3, .fuzz = 4},
-		{ .which = (MIN|MAX|RES|FUZZ|FLAT),
-		  .prop = "1:2:3:4:5",
-		  .min = 1, .max = 2, .res = 3, .fuzz = 4, .flat = 5},
-		{ .which = (MIN|RES|FUZZ|FLAT),
-		  .prop = "1::3:4:50",
-		  .min = 1, .res = 3, .fuzz = 4, .flat = 50},
-		{ .which = FUZZ|FLAT,
-		  .prop = ":::5:60",
-		  .fuzz = 5, .flat = 60},
-		{ .which = FUZZ,
-		  .prop = ":::5:",
-		  .fuzz = 5 },
-		{ .which = RES, .prop = "::12::",
-		  .res = 12 },
-		/* Malformed property but parsing this one makes us more
-		 * future proof */
-		{ .which = (RES|FUZZ|FLAT), .prop = "::12:1:2:3:4:5:6",
-		  .res = 12, .fuzz = 1, .flat = 2 },
-		{ .which = 0, .prop = ":::::" },
-		{ .which = 0, .prop = ":" },
-		{ .which = 0, .prop = "" },
-		{ .which = 0, .prop = ":asb::::" },
-		{ .which = 0, .prop = "foo" },
-	};
-	struct test *t;
-
-	ARRAY_FOR_EACH(tests, t) {
-		struct input_absinfo abs;
-		uint32_t mask;
-
-		mask = parse_ev_abs_prop(t->prop, &abs);
-		ck_assert_int_eq(mask, t->which);
-
-		if (t->which & MIN)
-			ck_assert_int_eq(abs.minimum, t->min);
-		if (t->which & MAX)
-			ck_assert_int_eq(abs.maximum, t->max);
-		if (t->which & RES)
-			ck_assert_int_eq(abs.resolution, t->res);
-		if (t->which & FUZZ)
-			ck_assert_int_eq(abs.fuzz, t->fuzz);
-		if (t->which & FLAT)
-			ck_assert_int_eq(abs.flat, t->flat);
-	}
-}
-END_TEST
-
-int main(int argc, char **argv) {
-
-	SRunner *sr = srunner_create(NULL);
-	Suite *s = suite_create("fuzz-override");
-	TCase *tc = tcase_create("parser");
-	int nfailed;
-
-	tcase_add_test(tc, test_parse_ev_abs);
-	suite_add_tcase(s, tc);
-	srunner_add_suite(sr, s);
-
-	srunner_run_all(sr, CK_NORMAL);
-	nfailed = srunner_ntests_failed(sr);
-	srunner_free(sr);
-
-	return nfailed;
-}
